mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-26 22:50:26 +01:00
Fixes, Reworks, New Tabs and General Misery
This commit is contained in:
@@ -97,7 +97,7 @@
|
||||
if(!ui)
|
||||
var/datum/asset/spritesheet/simple/assets = get_asset_datum(/datum/asset/spritesheet/simple/achievements)
|
||||
assets.send(user)
|
||||
ui = new(user, src, ui_key, "Achievements", "Achievements Menu", 800, 1000, master_ui, state)
|
||||
ui = new(user, src, ui_key, "Achievements", "Achievements Menu", 540, 680, master_ui, state)
|
||||
ui.open()
|
||||
|
||||
/datum/achievement_data/ui_data(mob/user)
|
||||
|
||||
@@ -135,8 +135,7 @@
|
||||
var/list/data = list()
|
||||
data["telecrystals"] = telecrystals
|
||||
data["lockable"] = lockable
|
||||
data["compact_mode"] = compact_mode
|
||||
|
||||
data["compactMode"] = compact_mode
|
||||
return data
|
||||
|
||||
/datum/component/uplink/ui_static_data(mob/user)
|
||||
|
||||
@@ -96,7 +96,6 @@
|
||||
else
|
||||
data["patient"] = null
|
||||
return data
|
||||
|
||||
switch(patient.stat)
|
||||
if(CONSCIOUS)
|
||||
data["patient"]["stat"] = "Conscious"
|
||||
@@ -118,8 +117,8 @@
|
||||
data["patient"]["fireLoss"] = patient.getFireLoss()
|
||||
data["patient"]["toxLoss"] = patient.getToxLoss()
|
||||
data["patient"]["oxyLoss"] = patient.getOxyLoss()
|
||||
data["procedures"] = list()
|
||||
if(patient.surgeries.len)
|
||||
data["procedures"] = list()
|
||||
for(var/datum/surgery/procedure in patient.surgeries)
|
||||
var/datum/surgery_step/surgery_step = procedure.get_surgery_step()
|
||||
var/chems_needed = surgery_step.get_chem_list()
|
||||
|
||||
@@ -172,7 +172,7 @@
|
||||
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "MedicalKiosk", name, 625, 550, master_ui, state)
|
||||
ui = new(user, src, ui_key, "MedicalKiosk", name, 575, 420, master_ui, state)
|
||||
ui.open()
|
||||
icon_state = "kiosk_off"
|
||||
RefreshParts()
|
||||
@@ -236,29 +236,18 @@
|
||||
trauma_text += trauma_desc
|
||||
trauma_status = "Cerebral traumas detected: patient appears to be suffering from [english_list(trauma_text)]."
|
||||
|
||||
var/chem_status = FALSE
|
||||
var/chemical_list= list()
|
||||
var/overdose_status = FALSE
|
||||
var/chemical_list = list()
|
||||
var/overdose_list = list()
|
||||
var/addict_status = FALSE
|
||||
var/addict_list = list()
|
||||
var/hallucination_status = "Patient is not hallucinating."
|
||||
|
||||
for(var/datum/reagent/R in altPatient.reagents.reagent_list)
|
||||
if(R.overdosed)
|
||||
overdose_status = TRUE
|
||||
|
||||
if(altPatient.reagents.reagent_list.len) //Chemical Analysis details.
|
||||
chem_status = TRUE
|
||||
for(var/datum/reagent/R in altPatient.reagents.reagent_list)
|
||||
chemical_list += list(list("name" = R.name, "volume" = round(R.volume, 0.01)))
|
||||
if(R.overdosed == 1)
|
||||
overdose_list += list(list("name" = R.name))
|
||||
else
|
||||
chemical_list = "Patient contains no reagents"
|
||||
|
||||
if(altPatient.reagents.addiction_list.len)
|
||||
addict_status = TRUE
|
||||
for(var/datum/reagent/R in altPatient.reagents.addiction_list)
|
||||
addict_list += list(list("name" = R.name))
|
||||
if (altPatient.hallucinating())
|
||||
@@ -296,7 +285,6 @@
|
||||
else if (user.hallucinating())
|
||||
chaos_modifier = 0.3
|
||||
|
||||
|
||||
data["kiosk_cost"] = active_price + (chaos_modifier * (rand(1,25)))
|
||||
data["patient_name"] = patient_name
|
||||
data["patient_health"] = round(((total_health - (chaos_modifier * (rand(1,50)))) / max_health) * 100, 0.001)
|
||||
@@ -316,18 +304,15 @@
|
||||
data["bleed_status"] = bleed_status
|
||||
data["blood_levels"] = blood_percent - (chaos_modifier * (rand(1,35)))
|
||||
data["blood_status"] = blood_status
|
||||
data["are_chems_present"] = chem_status ? TRUE : FALSE
|
||||
data["chemical_list"] = chemical_list
|
||||
data["are_overdoses_present"] = overdose_status ? TRUE : FALSE
|
||||
data["overdose_status"] = overdose_list
|
||||
data["are_addictions_present"] = addict_status ? TRUE : FALSE
|
||||
data["addiction_status"] = addict_list
|
||||
data["overdose_list"] = overdose_list
|
||||
data["addict_list"] = addict_list
|
||||
data["hallucinating_status"] = hallucination_status
|
||||
|
||||
data["active_status_1"] = scan_active_1 ? FALSE : TRUE //General Scan Check
|
||||
data["active_status_2"] = scan_active_2 ? FALSE : TRUE //Symptom Scan Check
|
||||
data["active_status_3"] = scan_active_3 ? FALSE : TRUE //Radio-Neuro Scan Check
|
||||
data["active_status_4"] = scan_active_4 ? FALSE : TRUE //Radio-Neuro Scan Check
|
||||
data["active_status_1"] = scan_active_1 // General Scan Check
|
||||
data["active_status_2"] = scan_active_2 // Symptom Scan Check
|
||||
data["active_status_3"] = scan_active_3 // Radio-Neuro Scan Check
|
||||
data["active_status_4"] = scan_active_4 // Radio-Neuro Scan Check
|
||||
return data
|
||||
|
||||
/obj/machinery/medical_kiosk/ui_act(action,active)
|
||||
|
||||
@@ -39,8 +39,8 @@
|
||||
|
||||
/datum/module_picker/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["processing_time"] = processing_time
|
||||
data["compact_mode"] = compact_mode
|
||||
data["processingTime"] = processing_time
|
||||
data["compactMode"] = compact_mode
|
||||
return data
|
||||
|
||||
/datum/module_picker/ui_static_data(mob/user)
|
||||
|
||||
@@ -4,10 +4,6 @@
|
||||
assets = list(
|
||||
"tgui.bundle.js" = 'tgui/packages/tgui/public/tgui.bundle.js',
|
||||
"tgui.bundle.css" = 'tgui/packages/tgui/public/tgui.bundle.css',
|
||||
"shim-html5shiv.js" = 'tgui/packages/tgui/public/shim-html5shiv.js',
|
||||
"shim-ie8.js" = 'tgui/packages/tgui/public/shim-ie8.js',
|
||||
"shim-dom4.js" = 'tgui/packages/tgui/public/shim-dom4.js',
|
||||
"shim-css-om.js" = 'tgui/packages/tgui/public/shim-css-om.js',
|
||||
)
|
||||
|
||||
/datum/asset/group/tgui
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
icon_state = "uplink"
|
||||
|
||||
// UI variables.
|
||||
var/ui_x = 720
|
||||
var/ui_x = 600
|
||||
var/ui_y = 480
|
||||
var/viewing_category
|
||||
var/viewing_market
|
||||
@@ -58,7 +58,7 @@
|
||||
/obj/item/blackmarket_uplink/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "BlackmarketUplink", name, ui_x, ui_y, master_ui, state)
|
||||
ui = new(user, src, ui_key, "BlackMarketUplink", name, ui_x, ui_y, master_ui, state)
|
||||
ui.open()
|
||||
|
||||
/obj/item/blackmarket_uplink/ui_data(mob/user)
|
||||
|
||||
@@ -171,6 +171,13 @@ all available horizontal space.
|
||||
- `mb: number` - Bottom margin.
|
||||
- `ml: number` - Left margin.
|
||||
- `mr: number` - Right margin.
|
||||
- `p: number` - Padding on all sides.
|
||||
- `px: number` - Horizontal padding.
|
||||
- `py: number` - Vertical padding.
|
||||
- `pt: number` - Top padding.
|
||||
- `pb: number` - Bottom padding.
|
||||
- `pl: number` - Left padding.
|
||||
- `pr: number` - Right padding.
|
||||
- `opacity: number` - Opacity, from 0 to 1.
|
||||
- `bold: boolean` - Make text bold.
|
||||
- `italic: boolean` - Make text italic.
|
||||
@@ -848,55 +855,47 @@ Here is an example of how you would construct a simple tabbed view:
|
||||
|
||||
```jsx
|
||||
<Tabs>
|
||||
<Tabs.Tab label="Item one">
|
||||
Content for Item one.
|
||||
<Tabs.Tab
|
||||
selected={tabIndex === 1}
|
||||
onClick={() => setTabIndex(1)}>
|
||||
Tab one
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab label="Item two">
|
||||
Content for Item two.
|
||||
<Tabs.Tab
|
||||
selected={tabIndex === 2}
|
||||
onClick={() => setTabIndex(2)}>
|
||||
Tab two
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
<Box>
|
||||
Tab selected: {tabIndex}
|
||||
</Box>
|
||||
```
|
||||
|
||||
This is a rather simple example. In the real world, you might be
|
||||
constructing very complex tabbed views which can tax UI performance.
|
||||
This is because your tabs are being rendered regardless of their
|
||||
visibility status!
|
||||
Notice that tabs do not contain state. It is your job to track the selected
|
||||
tab, handle clicks and place tab content where you need it. In return, you get
|
||||
a lot of flexibility in regards to how you can layout your tabs.
|
||||
|
||||
There is a simple fix however. Tabs accept functions as children, which
|
||||
will be called to retrieve content only when the tab is visible:
|
||||
Tabs also support a vertical configuration. This is usually paired with a
|
||||
[Flex](#flex) component to render tab content to the right.
|
||||
|
||||
```jsx
|
||||
<Tabs>
|
||||
<Tabs.Tab key="tab_1" label="Item one">
|
||||
{() => (
|
||||
<Fragment>
|
||||
Content for Item one.
|
||||
</Fragment>
|
||||
)}
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab key="tab_2" label="Item two">
|
||||
{() => (
|
||||
<Fragment>
|
||||
Content for Item two.
|
||||
</Fragment>
|
||||
)}
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
<Flex>
|
||||
<Flex.Item>
|
||||
<Tabs vertical>
|
||||
...
|
||||
</Tabs>
|
||||
</Flex.Item>
|
||||
<Flex.Item grow={1} basis={0}>
|
||||
Tab content.
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
```
|
||||
|
||||
You might not always need this, but it is highly recommended to always
|
||||
use this method. Notice the `key` prop on tabs - it uniquely identifies
|
||||
the tab and is used for determining which tab is currently active. It can
|
||||
be either explicitly provided as a `key` prop, or if omitted, it will be
|
||||
implicitly derived from the tab's `label` prop.
|
||||
|
||||
Props:
|
||||
|
||||
- `vertical: boolean` - Use a vertical configuration, where tabs will appear
|
||||
stacked on the left side of the container.
|
||||
- `altSelection` - Whether the tab buttons select via standard select (color
|
||||
change) or by adding a white indicator to the selected tab.
|
||||
Intended for usage on interfaces where tab color has relevance.
|
||||
- See inherited props: [Box](#box)
|
||||
- `vertical: boolean` - Use a vertical configuration, where tabs will be
|
||||
stacked vertically.
|
||||
- `children: Tab[]` - This component only accepts tabs as its children.
|
||||
|
||||
### `Tabs.Tab`
|
||||
@@ -907,10 +906,11 @@ a lot of `Button` props.
|
||||
Props:
|
||||
|
||||
- See inherited props: [Button](#button)
|
||||
- `key: string` - A unique identifier for the tab.
|
||||
- `label: string` - Tab label.
|
||||
- `altSelection` - Whether the tab buttons select via standard select (color
|
||||
change) or by adding a white indicator to the selected tab.
|
||||
Intended for usage on interfaces where tab color has relevance.
|
||||
- `icon: string` - Tab icon.
|
||||
- `content/children: any` - Content to render inside the tab.
|
||||
- `children: any` - Tab text.
|
||||
- `onClick: function` - Called when element is clicked.
|
||||
|
||||
### `Tooltip`
|
||||
|
||||
@@ -82,13 +82,33 @@ const styleMapperByPropName = {
|
||||
italic: mapBooleanPropTo('font-style', 'italic'),
|
||||
nowrap: mapBooleanPropTo('white-space', 'nowrap'),
|
||||
// Margins
|
||||
m: mapDirectionalUnitPropTo('margin', ['top', 'bottom', 'left', 'right']),
|
||||
mx: mapDirectionalUnitPropTo('margin', ['left', 'right']),
|
||||
my: mapDirectionalUnitPropTo('margin', ['top', 'bottom']),
|
||||
m: mapDirectionalUnitPropTo('margin', [
|
||||
'top', 'bottom', 'left', 'right',
|
||||
]),
|
||||
mx: mapDirectionalUnitPropTo('margin', [
|
||||
'left', 'right',
|
||||
]),
|
||||
my: mapDirectionalUnitPropTo('margin', [
|
||||
'top', 'bottom',
|
||||
]),
|
||||
mt: mapUnitPropTo('margin-top'),
|
||||
mb: mapUnitPropTo('margin-bottom'),
|
||||
ml: mapUnitPropTo('margin-left'),
|
||||
mr: mapUnitPropTo('margin-right'),
|
||||
// Margins
|
||||
p: mapDirectionalUnitPropTo('padding', [
|
||||
'top', 'bottom', 'left', 'right',
|
||||
]),
|
||||
px: mapDirectionalUnitPropTo('padding', [
|
||||
'left', 'right',
|
||||
]),
|
||||
py: mapDirectionalUnitPropTo('padding', [
|
||||
'top', 'bottom',
|
||||
]),
|
||||
pt: mapUnitPropTo('padding-top'),
|
||||
pb: mapUnitPropTo('padding-bottom'),
|
||||
pl: mapUnitPropTo('padding-left'),
|
||||
pr: mapUnitPropTo('padding-right'),
|
||||
// Color props
|
||||
color: mapColorPropTo('color'),
|
||||
textColor: mapColorPropTo('color'),
|
||||
|
||||
@@ -1,19 +1,27 @@
|
||||
import { classes, pureComponentHooks } from 'common/react';
|
||||
import { Box } from './Box';
|
||||
import { computeBoxClassName, computeBoxProps } from './Box';
|
||||
|
||||
export const ColorBox = props => {
|
||||
const { color, content, className, ...rest } = props;
|
||||
const {
|
||||
content,
|
||||
children,
|
||||
className,
|
||||
color,
|
||||
backgroundColor,
|
||||
...rest
|
||||
} = props;
|
||||
rest.color = content ? null : 'transparent';
|
||||
rest.backgroundColor = color || backgroundColor;
|
||||
return (
|
||||
<Box
|
||||
<div
|
||||
className={classes([
|
||||
'ColorBox',
|
||||
className,
|
||||
computeBoxClassName(rest),
|
||||
])}
|
||||
color={content ? null : 'transparent'}
|
||||
backgroundColor={color}
|
||||
{...rest}>
|
||||
{...computeBoxProps(rest)}>
|
||||
{content || '.'}
|
||||
</Box>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { classes, pureComponentHooks } from 'common/react';
|
||||
import { Box, unit } from './Box';
|
||||
import { Divider } from './Divider';
|
||||
|
||||
export const LabeledList = props => {
|
||||
const { children } = props;
|
||||
@@ -62,12 +63,19 @@ export const LabeledListItem = props => {
|
||||
LabeledListItem.defaultHooks = pureComponentHooks;
|
||||
|
||||
export const LabeledListDivider = props => {
|
||||
const { size = 1 } = props;
|
||||
const padding = props.size
|
||||
? unit(Math.max(0, props.size - 1))
|
||||
: 0;
|
||||
return (
|
||||
<tr className="LabeledList__row">
|
||||
<td style={{
|
||||
'padding-bottom': unit(size),
|
||||
}} />
|
||||
<td
|
||||
colSpan={3}
|
||||
style={{
|
||||
'padding-top': padding,
|
||||
'padding-bottom': padding,
|
||||
}}>
|
||||
<Divider />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -56,10 +56,28 @@ export const formatPower = (value, minBase1000 = 0) => {
|
||||
};
|
||||
|
||||
export const formatMoney = (value, precision = 0) => {
|
||||
return round(value, precision)
|
||||
.toLocaleString('en', {
|
||||
minimumFractionDigits: precision,
|
||||
})
|
||||
// Thin space
|
||||
.replace(/,/g, '\u2009');
|
||||
if (!Number.isFinite(value)) {
|
||||
return value;
|
||||
}
|
||||
// Round the number and make it fixed precision
|
||||
let fixed = round(value, precision);
|
||||
if (precision > 0) {
|
||||
fixed = toFixed(value, precision);
|
||||
}
|
||||
fixed = String(fixed);
|
||||
// Place thousand separators
|
||||
const length = fixed.length;
|
||||
let indexOfPoint = fixed.indexOf('.');
|
||||
if (indexOfPoint === -1) {
|
||||
indexOfPoint = length;
|
||||
}
|
||||
let result = '';
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (i > 0 && i < indexOfPoint && (indexOfPoint - i) % 3 === 0) {
|
||||
// Thin space
|
||||
result += '\u2009';
|
||||
}
|
||||
result += fixed.charAt(i);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -1,13 +1,23 @@
|
||||
// Polyfills
|
||||
import 'core-js/es';
|
||||
import 'core-js/web/immediate';
|
||||
import 'core-js/web/queue-microtask';
|
||||
import 'core-js/web/timers';
|
||||
import 'regenerator-runtime/runtime';
|
||||
import './polyfills/html5shiv';
|
||||
import './polyfills/ie8';
|
||||
import './polyfills/dom4';
|
||||
import './polyfills/css-om';
|
||||
import './polyfills/inferno';
|
||||
|
||||
// This one is necessary for Inferno.
|
||||
if (!window.Int32Array) {
|
||||
window.Int32Array = Array;
|
||||
}
|
||||
// Themes
|
||||
import './styles/main.scss';
|
||||
import './styles/themes/cardtable.scss';
|
||||
import './styles/themes/malfunction.scss';
|
||||
import './styles/themes/ntos.scss';
|
||||
import './styles/themes/hackerman.scss';
|
||||
import './styles/themes/retro.scss';
|
||||
import './styles/themes/syndicate.scss';
|
||||
|
||||
import { loadCSS } from 'fg-loadcss';
|
||||
import { render } from 'inferno';
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Box, Icon, Table, Tabs } from '../components';
|
||||
import { Box, Flex, Icon, Table, Tabs } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
import { Fragment } from 'inferno';
|
||||
|
||||
export const Achievements = (props, context) => {
|
||||
const { data } = useBackend(context);
|
||||
@@ -63,11 +62,11 @@ const Achievement = props => {
|
||||
score,
|
||||
} = achievement;
|
||||
return (
|
||||
<tr key={name}>
|
||||
<td style={{ 'padding': '6px' }}>
|
||||
<Box className={icon_class} />
|
||||
</td>
|
||||
<td style={{ 'vertical-align': 'top' }}>
|
||||
<Table.Row key={name}>
|
||||
<Table.Cell collapsing>
|
||||
<Box m={1} className={icon_class} />
|
||||
</Table.Cell>
|
||||
<Table.Cell verticalAlign="top">
|
||||
<h1>{name}</h1>
|
||||
{desc}
|
||||
{score && (
|
||||
@@ -79,8 +78,8 @@ const Achievement = props => {
|
||||
{value ? 'Unlocked' : 'Locked'}
|
||||
</Box>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -105,54 +104,58 @@ const HighScoreTable = (props, context) => {
|
||||
value: highscore.scores[key],
|
||||
}));
|
||||
return (
|
||||
<Fragment>
|
||||
<Tabs vertical>
|
||||
{highscores.map((highscore, i) => (
|
||||
<Tabs.Tab
|
||||
key={highscore.name}
|
||||
selected={highScoreIndex === i}
|
||||
onClick={() => setHighScoreIndex(i)}>
|
||||
{highscore.name}
|
||||
</Tabs.Tab>
|
||||
))}
|
||||
</Tabs>
|
||||
<Table>
|
||||
<Table.Row className="candystripe">
|
||||
<Table.Cell color="label" textAlign="center">
|
||||
#
|
||||
</Table.Cell>
|
||||
<Table.Cell color="label" textAlign="center">
|
||||
Key
|
||||
</Table.Cell>
|
||||
<Table.Cell color="label" textAlign="center">
|
||||
Score
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{scores.map((score, i) => (
|
||||
<Table.Row
|
||||
key={score.ckey}
|
||||
className="candystripe"
|
||||
m={2}>
|
||||
<Table.Cell color="label" textAlign="center">
|
||||
{i + 1}
|
||||
</Table.Cell>
|
||||
<Table.Cell
|
||||
color={score.ckey === user_ckey && 'green'}
|
||||
textAlign="center">
|
||||
{i === 0 && (
|
||||
<Icon name="crown" color="gold" mr={2} />
|
||||
)}
|
||||
{score.ckey}
|
||||
{i === 0 && (
|
||||
<Icon name="crown" color="gold" ml={2} />
|
||||
)}
|
||||
<Flex>
|
||||
<Flex.Item>
|
||||
<Tabs vertical>
|
||||
{highscores.map((highscore, i) => (
|
||||
<Tabs.Tab
|
||||
key={highscore.name}
|
||||
selected={highScoreIndex === i}
|
||||
onClick={() => setHighScoreIndex(i)}>
|
||||
{highscore.name}
|
||||
</Tabs.Tab>
|
||||
))}
|
||||
</Tabs>
|
||||
</Flex.Item>
|
||||
<Flex.Item grow={1} basis={0}>
|
||||
<Table>
|
||||
<Table.Row header>
|
||||
<Table.Cell textAlign="center">
|
||||
#
|
||||
</Table.Cell>
|
||||
<Table.Cell textAlign="center">
|
||||
{score.value}
|
||||
Key
|
||||
</Table.Cell>
|
||||
<Table.Cell textAlign="center">
|
||||
Score
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
))}
|
||||
</Table>
|
||||
</Fragment>
|
||||
{scores.map((score, i) => (
|
||||
<Table.Row
|
||||
key={score.ckey}
|
||||
className="candystripe"
|
||||
m={2}>
|
||||
<Table.Cell color="label" textAlign="center">
|
||||
{i + 1}
|
||||
</Table.Cell>
|
||||
<Table.Cell
|
||||
color={score.ckey === user_ckey && 'green'}
|
||||
textAlign="center">
|
||||
{i === 0 && (
|
||||
<Icon name="crown" color="yellow" mr={2} />
|
||||
)}
|
||||
{score.ckey}
|
||||
{i === 0 && (
|
||||
<Icon name="crown" color="yellow" ml={2} />
|
||||
)}
|
||||
</Table.Cell>
|
||||
<Table.Cell textAlign="center">
|
||||
{score.value}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
))}
|
||||
</Table>
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,45 +1,145 @@
|
||||
import { map } from 'common/collections';
|
||||
import { useBackend } from '../backend';
|
||||
import { AnimatedNumber, Box, Button, Flex, Modal, Section, Table, Tabs } from '../components';
|
||||
import { formatMoney } from '../format';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
export const BlackmarketUplink = (props, context) => {
|
||||
export const BlackMarketUplink = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const categories = data.categories || [];
|
||||
const deliveryMethods = data.delivery_methods || [];
|
||||
const deliveryMethodDesc = data.delivery_method_description || [];
|
||||
const markets = data.markets || {};
|
||||
const items = data.items || {};
|
||||
const {
|
||||
categories = [],
|
||||
markets = [],
|
||||
items = [],
|
||||
money,
|
||||
viewing_market,
|
||||
viewing_category,
|
||||
} = data;
|
||||
return (
|
||||
<Window
|
||||
theme="hackerman"
|
||||
resizable>
|
||||
<ShipmentSelector />
|
||||
<Window.Content scrollable>
|
||||
<Section
|
||||
title="Black Market Uplink"
|
||||
buttons={(
|
||||
<Box inline bold>
|
||||
<AnimatedNumber
|
||||
value={money}
|
||||
format={value => formatMoney(value) + ' cr'} />
|
||||
</Box>
|
||||
)} />
|
||||
<Tabs>
|
||||
{markets.map(market => (
|
||||
<Tabs.Tab
|
||||
key={market.id}
|
||||
selected={market.id === viewing_market}
|
||||
onClick={() => act('set_market', {
|
||||
market: market.id,
|
||||
})}>
|
||||
{market.name}
|
||||
</Tabs.Tab>
|
||||
))}
|
||||
</Tabs>
|
||||
<Flex>
|
||||
<Flex.Item>
|
||||
<Tabs vertical>
|
||||
{categories.map(category => (
|
||||
<Tabs.Tab
|
||||
key={category}
|
||||
height={4}
|
||||
mt={0.5}
|
||||
selected={viewing_category === category}
|
||||
onClick={() => act('set_category', {
|
||||
category: category,
|
||||
})}>
|
||||
{category}
|
||||
</Tabs.Tab>
|
||||
))}
|
||||
</Tabs>
|
||||
</Flex.Item>
|
||||
<Flex.Item grow={1} basis={0}>
|
||||
{items.map(item => (
|
||||
<Box
|
||||
key={item.name}
|
||||
className="candystripe"
|
||||
p={1}
|
||||
pb={2}>
|
||||
<Flex spacing={1} align="baseline">
|
||||
<Flex.Item bold grow={1}>
|
||||
{item.name}
|
||||
</Flex.Item>
|
||||
<Flex.Item color="label">
|
||||
{item.amount
|
||||
? item.amount + " in stock"
|
||||
: "Out of stock"}
|
||||
</Flex.Item>
|
||||
<Flex.Item>
|
||||
{formatMoney(item.cost) + ' cr'}
|
||||
</Flex.Item>
|
||||
<Flex.Item>
|
||||
<Button
|
||||
content="Buy"
|
||||
disabled={!item.amount || item.cost > money}
|
||||
onClick={() => act('select', {
|
||||
item: item.id,
|
||||
})} />
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
{item.desc}
|
||||
</Box>
|
||||
))}
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const shipmentSelector = !!data.buying && (
|
||||
const ShipmentSelector = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
buying,
|
||||
ltsrbt_built,
|
||||
money,
|
||||
} = data;
|
||||
if (!buying) {
|
||||
return null;
|
||||
}
|
||||
const deliveryMethods = data.delivery_methods.map(method => {
|
||||
const description = data.delivery_method_description[method.name];
|
||||
return {
|
||||
...method,
|
||||
description,
|
||||
};
|
||||
});
|
||||
return (
|
||||
<Modal textAlign="center">
|
||||
<Flex mb={1}>
|
||||
{map(deliveryMethod => {
|
||||
const name = deliveryMethod.name;
|
||||
if (name === "LTSRBT" && !data.ltsrbt_built) {
|
||||
{deliveryMethods.map(method => {
|
||||
if (method.name === 'LTSRBT' && !ltsrbt_built) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Flex.Item
|
||||
key={name}
|
||||
key={method.name}
|
||||
mx={1}
|
||||
width="250px">
|
||||
<Box fontSize="30px">
|
||||
{name}
|
||||
{method.name}
|
||||
</Box>
|
||||
<Box mt={1}>
|
||||
{deliveryMethodDesc[name]}
|
||||
{method.description}
|
||||
</Box>
|
||||
<Button
|
||||
mt={2}
|
||||
content={deliveryMethod.price+' cr'}
|
||||
disabled={data.money < deliveryMethod.price}
|
||||
content={formatMoney(method.price) + ' cr'}
|
||||
disabled={money < method.price}
|
||||
onClick={() => act('buy', {
|
||||
method: name,
|
||||
method: method.name,
|
||||
})} />
|
||||
</Flex.Item>
|
||||
);
|
||||
})(deliveryMethods)}
|
||||
})}
|
||||
</Flex>
|
||||
<Button
|
||||
content="Cancel"
|
||||
@@ -47,83 +147,4 @@ export const BlackmarketUplink = (props, context) => {
|
||||
onClick={() => act('cancel')} />
|
||||
</Modal>
|
||||
);
|
||||
|
||||
return (
|
||||
<Window
|
||||
theme="hackerman"
|
||||
resizable>
|
||||
{shipmentSelector}
|
||||
<Window.Content scrollable>
|
||||
<Section
|
||||
title="Black Market Uplink"
|
||||
buttons={(
|
||||
<Box inline bold>
|
||||
<AnimatedNumber value={Math.round(data.money)} /> cr
|
||||
</Box>
|
||||
)} />
|
||||
<Tabs
|
||||
activeTab={data.viewing_market}>
|
||||
{map(market => {
|
||||
const id = market.id;
|
||||
const name = market.name;
|
||||
return (
|
||||
<Tabs.Tab
|
||||
key={id}
|
||||
label={name}
|
||||
onClick={() => act('set_market', {
|
||||
market: id,
|
||||
})} />
|
||||
);
|
||||
})(markets)}
|
||||
</Tabs>
|
||||
<Box>
|
||||
<Tabs vertical
|
||||
activeTab={data.viewing_category}>
|
||||
{categories.map(category => (
|
||||
<Tabs.Tab
|
||||
key={category}
|
||||
label={category}
|
||||
height={4}
|
||||
mt={0.5}
|
||||
onClick={() => act('set_category', {
|
||||
category: category,
|
||||
})}>
|
||||
{items.map(item => (
|
||||
<Table
|
||||
key={item.name}
|
||||
mt={1}
|
||||
className="candystripe">
|
||||
<Table.Row>
|
||||
<Table.Cell bold>
|
||||
{item.name}
|
||||
</Table.Cell>
|
||||
<Table.Cell collapsing textAlign="right">
|
||||
{item.amount ? item.amount+" in stock" : "Out of stock"}
|
||||
</Table.Cell>
|
||||
<Table.Cell collapsing textAlign="right">
|
||||
{item.cost+'cr'}
|
||||
</Table.Cell>
|
||||
<Table.Cell collapsing textAlign="right">
|
||||
<Button
|
||||
content="Buy"
|
||||
disabled={!item.amount || item.cost > data.money}
|
||||
onClick={() => act('select', {
|
||||
item: item.id,
|
||||
})} />
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
{item.desc}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table>
|
||||
))}
|
||||
</Tabs.Tab>
|
||||
))}
|
||||
</Tabs>
|
||||
</Box>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,167 +1,21 @@
|
||||
import { createSearch, decodeHtmlEntities } from 'common/string';
|
||||
import { Fragment } from 'inferno';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Box, Button, Input, Section, Table, Tabs } from '../components';
|
||||
import { useBackend } from '../backend';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
const MAX_SEARCH_RESULTS = 25;
|
||||
import { GenericUplink } from './Uplink';
|
||||
|
||||
export const MalfunctionModulePicker = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
compact_mode,
|
||||
processing_time,
|
||||
categories = [],
|
||||
processingTime,
|
||||
} = data;
|
||||
const [
|
||||
hoveredItem,
|
||||
setHoveredItem,
|
||||
] = useLocalState(context, 'hoveredItem', {});
|
||||
const [
|
||||
searchText,
|
||||
setSearchText,
|
||||
] = useLocalState(context, 'searchText', '');
|
||||
const testSearch = createSearch(searchText, item => {
|
||||
return item.name + item.desc;
|
||||
});
|
||||
return (
|
||||
<Window
|
||||
theme="malfunction"
|
||||
resizable>
|
||||
<Window.Content scrollable>
|
||||
<Section
|
||||
title={(
|
||||
<Box
|
||||
inline
|
||||
color={processing_time > 0 ? 'good' : 'bad'}>
|
||||
{processing_time} Processing Time
|
||||
</Box>
|
||||
)}
|
||||
buttons={(
|
||||
<Fragment>
|
||||
Search
|
||||
<Input
|
||||
value={searchText}
|
||||
onInput={(e, value) => setSearchText(value)}
|
||||
ml={1}
|
||||
mr={1} />
|
||||
<Button
|
||||
icon={compact_mode ? 'list' : 'info'}
|
||||
content={compact_mode ? 'Compact' : 'Detailed'}
|
||||
onClick={() => act('compact_toggle')} />
|
||||
</Fragment>
|
||||
)}>
|
||||
{searchText.length > 0 ? (
|
||||
<table className="Table">
|
||||
<ItemList
|
||||
compact
|
||||
items={categories
|
||||
.flatMap(category => category.items || [])
|
||||
.filter(testSearch)
|
||||
.filter((item, i) => i < MAX_SEARCH_RESULTS)}
|
||||
hoveredItem={hoveredItem}
|
||||
onBuyMouseOver={item => setHoveredItem(item)}
|
||||
onBuyMouseOut={item => setHoveredItem({})}
|
||||
onBuy={item => act('buy', {
|
||||
ref: item.ref,
|
||||
})} />
|
||||
</table>
|
||||
) : (
|
||||
<Tabs vertical>
|
||||
{categories.map(category => {
|
||||
const { name, items } = category;
|
||||
if (items === null) {
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<Tabs.Tab
|
||||
key={name}
|
||||
label={`${name} (${items.length})`}>
|
||||
{() => (
|
||||
<ItemList
|
||||
compact={compact_mode}
|
||||
items={items}
|
||||
hoveredItem={hoveredItem}
|
||||
processing_time={processing_time}
|
||||
onBuyMouseOver={item => setHoveredItem(item)}
|
||||
onBuyMouseOut={item => setHoveredItem({})}
|
||||
onBuy={item => act('buy', {
|
||||
ref: item.ref,
|
||||
})} />
|
||||
)}
|
||||
</Tabs.Tab>
|
||||
);
|
||||
})}
|
||||
</Tabs>
|
||||
)}
|
||||
</Section>
|
||||
<GenericUplink
|
||||
currencyAmount={processingTime}
|
||||
currencySymbol="PT" />
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const ItemList = props => {
|
||||
const {
|
||||
items,
|
||||
hoveredItem,
|
||||
processing_time,
|
||||
compact,
|
||||
onBuy,
|
||||
onBuyMouseOver,
|
||||
onBuyMouseOut,
|
||||
} = props;
|
||||
const hoveredCost = hoveredItem && hoveredItem.cost || 0;
|
||||
if (compact) {
|
||||
return (
|
||||
<Table>
|
||||
{items.map(item => {
|
||||
const notSameItem = hoveredItem && hoveredItem.name !== item.name;
|
||||
const notEnoughHovered = processing_time - hoveredCost < item.cost;
|
||||
const disabledDueToHovered = notSameItem && notEnoughHovered;
|
||||
return (
|
||||
<Table.Row
|
||||
key={item.name}
|
||||
className="candystripe">
|
||||
<Table.Cell bold>
|
||||
{decodeHtmlEntities(item.name)}
|
||||
</Table.Cell>
|
||||
<Table.Cell collapsing textAlign="right">
|
||||
<Button
|
||||
fluid
|
||||
content={item.cost + ' PT'}
|
||||
disabled={processing_time < item.cost
|
||||
|| disabledDueToHovered}
|
||||
tooltip={item.desc}
|
||||
tooltipPosition="left"
|
||||
onmouseover={() => onBuyMouseOver(item)}
|
||||
onmouseout={() => onBuyMouseOut(item)}
|
||||
onClick={() => onBuy(item)} />
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
);
|
||||
})}
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
return items.map(item => {
|
||||
const notSameItem = hoveredItem && hoveredItem.name !== item.name;
|
||||
const notEnoughHovered = processing_time - hoveredCost < item.cost;
|
||||
const disabledDueToHovered = notSameItem && notEnoughHovered;
|
||||
return (
|
||||
<Section
|
||||
key={item.name}
|
||||
title={item.name}
|
||||
level={2}
|
||||
buttons={(
|
||||
<Button
|
||||
content={item.cost + ' PT'}
|
||||
disabled={processing_time < item.cost || disabledDueToHovered}
|
||||
onmouseover={() => onBuyMouseOver(item)}
|
||||
onmouseout={() => onBuyMouseOut(item)}
|
||||
onClick={() => onBuy(item)} />
|
||||
)}>
|
||||
{decodeHtmlEntities(item.desc)}
|
||||
</Section>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,302 +1,358 @@
|
||||
import { multiline } from 'common/string';
|
||||
import { useBackend } from '../backend';
|
||||
import { AnimatedNumber, Box, Button, LabeledList, ProgressBar, Section, Tabs } from '../components';
|
||||
import { useBackend, useSharedState } from '../backend';
|
||||
import { AnimatedNumber, Box, Button, Flex, Icon, LabeledList, ProgressBar, Section } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
export const MedicalKiosk = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const [scanIndex] = useSharedState(context, 'scanIndex');
|
||||
const {
|
||||
active_status_1,
|
||||
active_status_2,
|
||||
active_status_3,
|
||||
active_status_4,
|
||||
} = data;
|
||||
return (
|
||||
<Window resizable>
|
||||
<Window.Content scrollable>
|
||||
<Section title="Health Kiosk"
|
||||
textAlign="center"
|
||||
icon="procedures">
|
||||
<Box my={1} textAlign="center">
|
||||
Greetings Valued Employee. Please select your desired diagnosis.
|
||||
Diagnosis costs {data.kiosk_cost} credits.
|
||||
<Box mt={1} />
|
||||
<Box textAlign="center">
|
||||
Current patient targeted for scanning: {data.patient_name} |
|
||||
</Box>
|
||||
<Box my={1} mx={4} />
|
||||
<Button
|
||||
icon="procedures"
|
||||
disabled={!data.active_status_1}
|
||||
tooltip={multiline`
|
||||
Reads back exact values of your general health scan.
|
||||
`}
|
||||
onClick={() => act('beginScan_1')}
|
||||
content="General Health Scan" />
|
||||
<Button
|
||||
icon="heartbeat"
|
||||
disabled={!data.active_status_2}
|
||||
tooltip={multiline`
|
||||
Provides information based on various non-obvious symptoms,
|
||||
like blood levels or disease status.
|
||||
`}
|
||||
onClick={() => act('beginScan_2')}
|
||||
content="Symptom Based Checkup" />
|
||||
<Button
|
||||
tooltip={multiline`
|
||||
Resets the current scanning target, cancelling current scans.
|
||||
`}
|
||||
icon="sync"
|
||||
color="average"
|
||||
onClick={() => act('clearTarget')}
|
||||
content="Reset Scanner" />
|
||||
</Box>
|
||||
<Box my={1} textAlign="center">
|
||||
<Button
|
||||
icon="radiation-alt"
|
||||
disabled={!data.active_status_3}
|
||||
tooltip={multiline`
|
||||
Provides information about brain trauma and radiation.
|
||||
`}
|
||||
onClick={() => act('beginScan_3')}
|
||||
content="Neurological/Radiological Scan" />
|
||||
<Button
|
||||
icon="mortar-pestle"
|
||||
disabled={!data.active_status_4}
|
||||
tooltip={multiline`
|
||||
Provides a list of consumed chemicals, as well as potential
|
||||
side effects.
|
||||
`}
|
||||
onClick={() => act('beginScan_4')}
|
||||
content="Chemical Analysis and Psychoactive Scan" />
|
||||
</Box>
|
||||
</Section>
|
||||
<Tabs>
|
||||
<Tabs.Tab
|
||||
key="tab_1"
|
||||
color="normal"
|
||||
label="General Health Scan">
|
||||
{() => (
|
||||
<Box>
|
||||
{data.active_status_1 === 0 && (
|
||||
<Section title="Patient Health"
|
||||
textAlign="center">
|
||||
<LabeledList>
|
||||
<LabeledList.Item
|
||||
label="Total Health">
|
||||
<ProgressBar
|
||||
value={(data.patient_health)/100}>
|
||||
<AnimatedNumber value={data.patient_health} />%
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Divider size={2} />
|
||||
<LabeledList.Item
|
||||
label="Brute Damage">
|
||||
<ProgressBar
|
||||
value={data.brute_health/100}
|
||||
color="bad">
|
||||
<AnimatedNumber value={data.brute_health} />
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Burn Damage">
|
||||
<ProgressBar
|
||||
value={data.burn_health/100}
|
||||
color="bad">
|
||||
<AnimatedNumber value={data.burn_health} />
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Oxygen Damage">
|
||||
<ProgressBar
|
||||
value={data.suffocation_health/100}
|
||||
color="bad">
|
||||
<AnimatedNumber value={data.suffocation_health} />
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Toxin Damage">
|
||||
<ProgressBar
|
||||
value={data.toxin_health/100}
|
||||
color="bad">
|
||||
<AnimatedNumber value={data.toxin_health} />
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
key="tab_2"
|
||||
color="normal"
|
||||
label="Symptom Based Checkup">
|
||||
{() => (
|
||||
<Box>
|
||||
{data.active_status_2 === 0 && (
|
||||
<Section title="Symptom Based Checkup"
|
||||
textAlign="center">
|
||||
<LabeledList>
|
||||
<LabeledList.Item
|
||||
label="Patient Status"
|
||||
color="good">
|
||||
{data.patient_status}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Divider size={1} />
|
||||
<LabeledList.Item
|
||||
label="Disease Status">
|
||||
{data.patient_illness}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Disease information">
|
||||
{data.illness_info}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Divider size={1} />
|
||||
<LabeledList.Item
|
||||
label="Blood Levels">
|
||||
{data.bleed_status}
|
||||
<LabeledList.Divider size={1} />
|
||||
<ProgressBar
|
||||
value={data.blood_levels/100}
|
||||
color="bad">
|
||||
<AnimatedNumber value={data.blood_levels} />
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Blood Information">
|
||||
{data.blood_status}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
key="tab_3"
|
||||
color="normal"
|
||||
label="Neurological/Radiological Scan">
|
||||
{() => (
|
||||
<Box>
|
||||
{data.active_status_3 === 0 && (
|
||||
<Section title="Patient Neurological and Radiological Health "
|
||||
textAlign="center">
|
||||
<LabeledList.Item
|
||||
label="Cellular Damage">
|
||||
<ProgressBar
|
||||
value={data.clone_health/100}
|
||||
color="good">
|
||||
<AnimatedNumber value={data.clone_health} />
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Brain Damage">
|
||||
<ProgressBar
|
||||
value={data.brain_damage/100}
|
||||
color="good">
|
||||
<AnimatedNumber value={data.brain_damage} />
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Brain Status"
|
||||
color="health-0">
|
||||
{data.brain_health}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Radiation Status">
|
||||
{data.rad_status}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Irradiation Percentage">
|
||||
{data.rad_value}%
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Brain Trauma Status">
|
||||
{data.trauma_status}
|
||||
</LabeledList.Item>
|
||||
</Section>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
key="tab_4"
|
||||
color="normal"
|
||||
label="Chemical Analysis and Psychoactive Scan">
|
||||
{() => (
|
||||
<Box>
|
||||
{data.active_status_4 === 0 && (
|
||||
<Section title="Chemical and Psychoactive Analysis"
|
||||
textAlign="center">
|
||||
<LabeledList.Item label="Chemical Contents">
|
||||
{data.are_chems_present ? (
|
||||
data.chemical_list.length ? (
|
||||
data.chemical_list.map(specificChem => (
|
||||
<Box
|
||||
key={specificChem.id}
|
||||
color="good" >
|
||||
{specificChem.volume} units of {specificChem.name}
|
||||
</Box>
|
||||
))
|
||||
) : (
|
||||
<Box>
|
||||
No reagents detected.
|
||||
</Box>
|
||||
)
|
||||
) : (
|
||||
<Box color="average">
|
||||
No reagents detected.
|
||||
</Box>
|
||||
)}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Overdose Status"
|
||||
color="bad">
|
||||
{data.are_overdoses_present ? (
|
||||
data.overdose_status.length ? (
|
||||
data.overdose_status.map(specificOD => (
|
||||
<Box key={specificOD.id}>
|
||||
Overdosing on {specificOD.name}
|
||||
</Box>
|
||||
))
|
||||
) : (
|
||||
<Box>
|
||||
No reagents detected.
|
||||
</Box>
|
||||
)
|
||||
) : (
|
||||
<Box color="good">
|
||||
Patient is not overdosing.
|
||||
</Box>
|
||||
)}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Addiction Status"
|
||||
color="bad">
|
||||
{data.are_addictions_present ? (
|
||||
data.addiction_status.length ? (
|
||||
data.addiction_status.map(specificAddict => (
|
||||
<Box key={specificAddict.id}>
|
||||
Addicted to {specificAddict.name}
|
||||
</Box>
|
||||
))
|
||||
) : (
|
||||
<Box>
|
||||
Patient has no addictions.
|
||||
</Box>
|
||||
)
|
||||
) : (
|
||||
<Box color="good">
|
||||
Patient has no addictions detected.
|
||||
</Box>
|
||||
)}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Psychoactive Status">
|
||||
{data.hallucinating_status}
|
||||
</LabeledList.Item>
|
||||
</Section>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
<Flex mb={1}>
|
||||
<Flex.Item mr={1}>
|
||||
<Section minHeight="100%">
|
||||
<MedicalKioskScanButton
|
||||
index={1}
|
||||
icon="procedures"
|
||||
name="General Health Scan"
|
||||
description={multiline`
|
||||
Reads back exact values of your general health scan.
|
||||
`} />
|
||||
<MedicalKioskScanButton
|
||||
index={2}
|
||||
icon="heartbeat"
|
||||
name="Symptom Based Checkup"
|
||||
description={multiline`
|
||||
Provides information based on various non-obvious symptoms,
|
||||
like blood levels or disease status.
|
||||
`} />
|
||||
<MedicalKioskScanButton
|
||||
index={3}
|
||||
icon="radiation-alt"
|
||||
name="Neurological/Radiological Scan"
|
||||
description={multiline`
|
||||
Provides information about brain trauma and radiation.
|
||||
`} />
|
||||
<MedicalKioskScanButton
|
||||
index={4}
|
||||
icon="mortar-pestle"
|
||||
name="Chemical and Psychoactive Scan"
|
||||
description={multiline`
|
||||
Provides a list of consumed chemicals, as well as potential
|
||||
side effects.
|
||||
`} />
|
||||
</Section>
|
||||
</Flex.Item>
|
||||
<Flex.Item grow={1} basis={0}>
|
||||
<MedicalKioskInstructions />
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
{!!active_status_1 && scanIndex === 1 && (
|
||||
<MedicalKioskScanResults1 />
|
||||
)}
|
||||
{!!active_status_2 && scanIndex === 2 && (
|
||||
<MedicalKioskScanResults2 />
|
||||
)}
|
||||
{!!active_status_3 && scanIndex === 3 && (
|
||||
<MedicalKioskScanResults3 />
|
||||
)}
|
||||
{!!active_status_4 && scanIndex === 4 && (
|
||||
<MedicalKioskScanResults4 />
|
||||
)}
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const MedicalKioskScanButton = (props, context) => {
|
||||
const {
|
||||
index,
|
||||
name,
|
||||
description,
|
||||
icon,
|
||||
} = props;
|
||||
const { act, data } = useBackend(context);
|
||||
const [scanIndex, setScanIndex] = useSharedState(context, 'scanIndex');
|
||||
const paid = data[`active_status_${index}`];
|
||||
return (
|
||||
<Flex spacing={1} align="baseline">
|
||||
<Flex.Item grow={1}>
|
||||
<Button
|
||||
fluid
|
||||
icon={icon}
|
||||
selected={paid && scanIndex === index}
|
||||
tooltip={description}
|
||||
tooltipPosition="right"
|
||||
content={name}
|
||||
onClick={() => {
|
||||
act(`beginScan_${index}`);
|
||||
setScanIndex(index);
|
||||
}} />
|
||||
</Flex.Item>
|
||||
<Flex.Item width="16px" textAlign="center">
|
||||
<Icon
|
||||
name={paid ? 'check' : 'dollar-sign'}
|
||||
color={paid ? 'green' : 'grey'} />
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const MedicalKioskInstructions = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
kiosk_cost,
|
||||
patient_name,
|
||||
} = data;
|
||||
return (
|
||||
<Section minHeight="100%">
|
||||
<Box italic>
|
||||
Greetings Valued Employee! Please select a desired automatic
|
||||
health check procedure. Diagnosis costs <b>{kiosk_cost} credits.</b>
|
||||
</Box>
|
||||
<Box mt={1}>
|
||||
<Box inline color="label" mr={1}>
|
||||
Patient:
|
||||
</Box>
|
||||
{patient_name}
|
||||
</Box>
|
||||
<Button
|
||||
mt={1}
|
||||
tooltip={multiline`
|
||||
Resets the current scanning target, cancelling current scans.
|
||||
`}
|
||||
icon="sync"
|
||||
color="average"
|
||||
onClick={() => act('clearTarget')}
|
||||
content="Reset Scanner" />
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const MedicalKioskScanResults1 = (props, context) => {
|
||||
const { data } = useBackend(context);
|
||||
const {
|
||||
patient_health,
|
||||
brute_health,
|
||||
burn_health,
|
||||
suffocation_health,
|
||||
toxin_health,
|
||||
} = data;
|
||||
return (
|
||||
<Section title="Patient Health">
|
||||
<LabeledList>
|
||||
<LabeledList.Item
|
||||
label="Total Health">
|
||||
<ProgressBar
|
||||
value={patient_health / 100}>
|
||||
<AnimatedNumber value={patient_health} />%
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Divider />
|
||||
<LabeledList.Item
|
||||
label="Brute Damage">
|
||||
<ProgressBar
|
||||
value={brute_health / 100}
|
||||
color="bad">
|
||||
<AnimatedNumber value={brute_health} />
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Burn Damage">
|
||||
<ProgressBar
|
||||
value={burn_health / 100}
|
||||
color="bad">
|
||||
<AnimatedNumber value={burn_health} />
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Oxygen Damage">
|
||||
<ProgressBar
|
||||
value={suffocation_health / 100}
|
||||
color="bad">
|
||||
<AnimatedNumber value={suffocation_health} />
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Toxin Damage">
|
||||
<ProgressBar
|
||||
value={toxin_health / 100}
|
||||
color="bad">
|
||||
<AnimatedNumber value={toxin_health} />
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const MedicalKioskScanResults2 = (props, context) => {
|
||||
const { data } = useBackend(context);
|
||||
const {
|
||||
patient_status,
|
||||
patient_illness,
|
||||
illness_info,
|
||||
bleed_status,
|
||||
blood_levels,
|
||||
blood_status,
|
||||
} = data;
|
||||
return (
|
||||
<Section title="Symptom Based Checkup">
|
||||
<LabeledList>
|
||||
<LabeledList.Item
|
||||
label="Patient Status"
|
||||
color="good">
|
||||
{patient_status}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Divider />
|
||||
<LabeledList.Item
|
||||
label="Disease Status">
|
||||
{patient_illness}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Disease information">
|
||||
{illness_info}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Divider />
|
||||
<LabeledList.Item
|
||||
label="Blood Levels">
|
||||
<ProgressBar
|
||||
value={blood_levels / 100}
|
||||
color="bad">
|
||||
<AnimatedNumber value={blood_levels} />
|
||||
</ProgressBar>
|
||||
<Box mt={1} color="label">
|
||||
{bleed_status}
|
||||
</Box>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Blood Information">
|
||||
{blood_status}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const MedicalKioskScanResults3 = (props, context) => {
|
||||
const { data } = useBackend(context);
|
||||
const {
|
||||
clone_health,
|
||||
brain_damage,
|
||||
brain_health,
|
||||
rad_status,
|
||||
rad_value,
|
||||
trauma_status,
|
||||
} = data;
|
||||
return (
|
||||
<Section title="Patient Neurological and Radiological Health">
|
||||
<LabeledList>
|
||||
<LabeledList.Item
|
||||
label="Cellular Damage">
|
||||
<ProgressBar
|
||||
value={clone_health / 100}
|
||||
color="good">
|
||||
<AnimatedNumber value={clone_health} />
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Divider />
|
||||
<LabeledList.Item
|
||||
label="Brain Damage">
|
||||
<ProgressBar
|
||||
value={brain_damage / 100}
|
||||
color="good">
|
||||
<AnimatedNumber value={brain_damage} />
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Brain Status"
|
||||
color="health-0">
|
||||
{brain_health}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Brain Trauma Status">
|
||||
{trauma_status}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Divider />
|
||||
<LabeledList.Item
|
||||
label="Radiation Status">
|
||||
{rad_status}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Irradiation Percentage">
|
||||
{rad_value}%
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const MedicalKioskScanResults4 = (props, context) => {
|
||||
const { data } = useBackend(context);
|
||||
const {
|
||||
chemical_list = [],
|
||||
overdose_list = [],
|
||||
addict_list = [],
|
||||
hallucinating_status,
|
||||
} = data;
|
||||
return (
|
||||
<Section title="Chemical and Psychoactive Analysis">
|
||||
<LabeledList>
|
||||
<LabeledList.Item
|
||||
label="Chemical Contents">
|
||||
{chemical_list.length === 0 && (
|
||||
<Box color="average">
|
||||
No reagents detected.
|
||||
</Box>
|
||||
)}
|
||||
{chemical_list.map(chem => (
|
||||
<Box
|
||||
key={chem.id}
|
||||
color="good">
|
||||
{chem.volume} units of {chem.name}
|
||||
</Box>
|
||||
))}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Overdose Status"
|
||||
color="bad">
|
||||
{overdose_list.length === 0 && (
|
||||
<Box color="good">
|
||||
Patient is not overdosing.
|
||||
</Box>
|
||||
)}
|
||||
{overdose_list.map(chem => (
|
||||
<Box key={chem.id}>
|
||||
Overdosing on {chem.name}
|
||||
</Box>
|
||||
))}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Addiction Status"
|
||||
color="bad">
|
||||
{addict_list.length === 0 && (
|
||||
<Box color="good">
|
||||
Patient has no addictions.
|
||||
</Box>
|
||||
)}
|
||||
{addict_list.map(chem => (
|
||||
<Box key={chem.id}>
|
||||
Addicted to {chem.name}
|
||||
</Box>
|
||||
))}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Psychoactive Status">
|
||||
{hallucinating_status}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { map } from 'common/collections';
|
||||
import { Fragment } from 'inferno';
|
||||
import { useBackend } from '../backend';
|
||||
import { Button, LabeledList, NoticeBox, Section, Tabs } from '../components';
|
||||
import { useBackend, useSharedState } from '../backend';
|
||||
import { Button, Flex, LabeledList, NoticeBox, Section, Tabs } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
export const NaniteProgramHub = (props, context) => {
|
||||
@@ -13,6 +13,11 @@ export const NaniteProgramHub = (props, context) => {
|
||||
has_program,
|
||||
programs = {},
|
||||
} = data;
|
||||
const [
|
||||
selectedCategory,
|
||||
setSelectedCategory,
|
||||
] = useSharedState(context, 'category', Object.keys(programs)[0]);
|
||||
const programsInCategory = programs[selectedCategory] || [];
|
||||
return (
|
||||
<Window resizable>
|
||||
<Window.Content scrollable>
|
||||
@@ -66,56 +71,65 @@ export const NaniteProgramHub = (props, context) => {
|
||||
</Fragment>
|
||||
)}>
|
||||
{programs !== null ? (
|
||||
<Tabs vertical>
|
||||
{map((cat_contents, category) => {
|
||||
const progs = cat_contents || [];
|
||||
// Backend was sending stupid data that would have been
|
||||
// annoying to fix
|
||||
const tabLabel = category.substring(0, category.length - 8);
|
||||
return (
|
||||
<Tabs.Tab
|
||||
key={category}
|
||||
label={tabLabel}>
|
||||
{detail_view ? (
|
||||
progs.map(program => (
|
||||
<Section
|
||||
key={program.id}
|
||||
title={program.name}
|
||||
level={2}
|
||||
buttons={(
|
||||
<Button
|
||||
icon="download"
|
||||
content="Download"
|
||||
disabled={!has_disk}
|
||||
onClick={() => act('download', {
|
||||
program_id: program.id,
|
||||
})} />
|
||||
)}>
|
||||
{program.desc}
|
||||
</Section>
|
||||
))
|
||||
) : (
|
||||
<LabeledList>
|
||||
{progs.map(program => (
|
||||
<LabeledList.Item
|
||||
key={program.id}
|
||||
label={program.name}
|
||||
buttons={(
|
||||
<Button
|
||||
icon="download"
|
||||
content="Download"
|
||||
disabled={!has_disk}
|
||||
onClick={() => act('download', {
|
||||
program_id: program.id,
|
||||
})} />
|
||||
)} />
|
||||
))}
|
||||
</LabeledList>
|
||||
)}
|
||||
</Tabs.Tab>
|
||||
);
|
||||
})(programs)}
|
||||
</Tabs>
|
||||
<Flex>
|
||||
<Flex.Item minWidth="110px">
|
||||
<Tabs vertical>
|
||||
{map((cat_contents, category) => {
|
||||
const progs = cat_contents || [];
|
||||
// Backend was sending stupid data that would have been
|
||||
// annoying to fix
|
||||
const tabLabel = category
|
||||
.substring(0, category.length - 8);
|
||||
return (
|
||||
<Tabs.Tab
|
||||
key={category}
|
||||
selected={category === selectedCategory}
|
||||
onClick={() => setSelectedCategory(category)}>
|
||||
{tabLabel}
|
||||
</Tabs.Tab>
|
||||
);
|
||||
})(programs)}
|
||||
</Tabs>
|
||||
</Flex.Item>
|
||||
<Flex.Item grow={1} basis={0}>
|
||||
{detail_view ? (
|
||||
programsInCategory.map(program => (
|
||||
<Section
|
||||
key={program.id}
|
||||
title={program.name}
|
||||
level={2}
|
||||
buttons={(
|
||||
<Button
|
||||
icon="download"
|
||||
content="Download"
|
||||
disabled={!has_disk}
|
||||
onClick={() => act('download', {
|
||||
program_id: program.id,
|
||||
})} />
|
||||
)}>
|
||||
{program.desc}
|
||||
</Section>
|
||||
))
|
||||
) : (
|
||||
<LabeledList>
|
||||
{programsInCategory.map(program => (
|
||||
<LabeledList.Item
|
||||
key={program.id}
|
||||
label={program.name}
|
||||
buttons={(
|
||||
<Button
|
||||
icon="download"
|
||||
content="Download"
|
||||
disabled={!has_disk}
|
||||
onClick={() => act('download', {
|
||||
program_id: program.id,
|
||||
})} />
|
||||
)} />
|
||||
))}
|
||||
</LabeledList>
|
||||
)}
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
) : (
|
||||
<NoticeBox>
|
||||
No nanite programs are currently researched.
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { Fragment } from 'inferno';
|
||||
import { useBackend } from '../backend';
|
||||
import { Button, NoticeBox, Section, Tabs, Input } from '../components';
|
||||
import { AccessList } from './common/AccessList';
|
||||
import { map } from 'common/collections';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Box, Button, Flex, Input, NoticeBox, Section, Tabs } from '../components';
|
||||
import { NtosWindow } from '../layouts';
|
||||
import { AccessList } from './common/AccessList';
|
||||
|
||||
export const NtosCard = (props, context) => {
|
||||
return (
|
||||
@@ -17,7 +16,7 @@ export const NtosCard = (props, context) => {
|
||||
|
||||
export const NtosCardContent = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
|
||||
const [tab, setTab] = useLocalState(context, 'tab', 1);
|
||||
const {
|
||||
authenticated,
|
||||
regions = [],
|
||||
@@ -30,7 +29,10 @@ export const NtosCardContent = (props, context) => {
|
||||
have_id_slot,
|
||||
id_name,
|
||||
} = data;
|
||||
|
||||
const [
|
||||
selectedDepartment,
|
||||
setSelectedDepartment,
|
||||
] = useLocalState(context, 'department', Object.keys(jobs)[0]);
|
||||
if (!have_id_slot) {
|
||||
return (
|
||||
<NoticeBox>
|
||||
@@ -38,7 +40,7 @@ export const NtosCardContent = (props, context) => {
|
||||
</NoticeBox>
|
||||
);
|
||||
}
|
||||
|
||||
const departmentJobs = jobs[selectedDepartment] || [];
|
||||
return (
|
||||
<Fragment>
|
||||
<Section
|
||||
@@ -75,8 +77,20 @@ export const NtosCardContent = (props, context) => {
|
||||
onClick={() => act('PRG_eject')} />
|
||||
</Section>
|
||||
{(!!has_id && !!authenticated) && (
|
||||
<Tabs>
|
||||
<Tabs.Tab label="Access">
|
||||
<Box>
|
||||
<Tabs>
|
||||
<Tabs.Tab
|
||||
selected={tab === 1}
|
||||
onClick={() => setTab(1)}>
|
||||
Access
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
selected={tab === 2}
|
||||
onClick={() => setTab(2)}>
|
||||
Jobs
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
{tab === 1 && (
|
||||
<AccessList
|
||||
accesses={regions}
|
||||
selectedList={access_on_card}
|
||||
@@ -91,8 +105,8 @@ export const NtosCardContent = (props, context) => {
|
||||
denyDep={dep => act('PRG_denyregion', {
|
||||
region: dep,
|
||||
})} />
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab label="Jobs">
|
||||
)}
|
||||
{tab === 2 && (
|
||||
<Section
|
||||
title={id_rank}
|
||||
buttons={(
|
||||
@@ -109,29 +123,34 @@ export const NtosCardContent = (props, context) => {
|
||||
assign_target: 'Custom',
|
||||
custom_name: value,
|
||||
})} />
|
||||
<Tabs vertical>
|
||||
{map((jobs_param, department) => {
|
||||
const jobs = jobs_param || [];
|
||||
return (
|
||||
<Tabs.Tab
|
||||
key={department}
|
||||
label={department}>
|
||||
{jobs.map(job => (
|
||||
<Button
|
||||
fluid
|
||||
key={job.job}
|
||||
content={job.display_name}
|
||||
onClick={() => act('PRG_assign', {
|
||||
assign_target: job.job,
|
||||
})} />
|
||||
))}
|
||||
</Tabs.Tab>
|
||||
);
|
||||
})(jobs)}
|
||||
</Tabs>
|
||||
<Flex>
|
||||
<Flex.Item>
|
||||
<Tabs vertical>
|
||||
{Object.keys(jobs).map(department => (
|
||||
<Tabs.Tab
|
||||
key={department}
|
||||
selected={department === selectedDepartment}
|
||||
onClick={() => setSelectedDepartment(department)}>
|
||||
{department}
|
||||
</Tabs.Tab>
|
||||
))}
|
||||
</Tabs>
|
||||
</Flex.Item>
|
||||
<Flex.Item grow={1}>
|
||||
{departmentJobs.map(job => (
|
||||
<Button
|
||||
fluid
|
||||
key={job.job}
|
||||
content={job.display_name}
|
||||
onClick={() => act('PRG_assign', {
|
||||
assign_target: job.job,
|
||||
})} />
|
||||
))}
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</Section>
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
@@ -1,148 +1,163 @@
|
||||
import { Fragment } from 'inferno';
|
||||
import { useBackend } from '../backend';
|
||||
import { useBackend, useSharedState } from '../backend';
|
||||
import { AnimatedNumber, Button, LabeledList, NoticeBox, ProgressBar, Section, Tabs } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
const damageTypes = [
|
||||
{
|
||||
label: 'Brute',
|
||||
type: 'bruteLoss',
|
||||
},
|
||||
{
|
||||
label: 'Burn',
|
||||
type: 'fireLoss',
|
||||
},
|
||||
{
|
||||
label: 'Toxin',
|
||||
type: 'toxLoss',
|
||||
},
|
||||
{
|
||||
label: 'Respiratory',
|
||||
type: 'oxyLoss',
|
||||
},
|
||||
];
|
||||
|
||||
export const OperatingComputer = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const damageTypes = [
|
||||
{
|
||||
label: 'Brute',
|
||||
type: 'bruteLoss',
|
||||
},
|
||||
{
|
||||
label: 'Burn',
|
||||
type: 'fireLoss',
|
||||
},
|
||||
{
|
||||
label: 'Toxin',
|
||||
type: 'toxLoss',
|
||||
},
|
||||
{
|
||||
label: 'Respiratory',
|
||||
type: 'oxyLoss',
|
||||
},
|
||||
];
|
||||
const {
|
||||
table,
|
||||
surgeries = [],
|
||||
procedures = [],
|
||||
patient = {},
|
||||
} = data;
|
||||
const [tab, setTab] = useSharedState(context, 'tab', 1);
|
||||
return (
|
||||
<Window resizable>
|
||||
<Window.Content scrollable>
|
||||
<Tabs>
|
||||
<Tabs.Tab
|
||||
key="state"
|
||||
label="Patient State">
|
||||
{!table && (
|
||||
<NoticeBox>
|
||||
No Table Detected
|
||||
</NoticeBox>
|
||||
)}
|
||||
<Section>
|
||||
<Section
|
||||
title="Patient State"
|
||||
level={2}>
|
||||
{patient ? (
|
||||
<LabeledList>
|
||||
<LabeledList.Item
|
||||
label="State"
|
||||
color={patient.statstate}>
|
||||
{patient.stat}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Blood Type">
|
||||
{patient.blood_type}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Health">
|
||||
<ProgressBar
|
||||
value={patient.health}
|
||||
minValue={patient.minHealth}
|
||||
maxValue={patient.maxHealth}
|
||||
color={patient.health >= 0 ? 'good' : 'average'}>
|
||||
<AnimatedNumber value={patient.health} />
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
{damageTypes.map(type => (
|
||||
<LabeledList.Item key={type.type} label={type.label}>
|
||||
<ProgressBar
|
||||
value={patient[type.type] / patient.maxHealth}
|
||||
color="bad">
|
||||
<AnimatedNumber value={patient[type.type]} />
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
))}
|
||||
</LabeledList>
|
||||
) : (
|
||||
'No Patient Detected'
|
||||
)}
|
||||
</Section>
|
||||
<Section
|
||||
title="Initiated Procedures"
|
||||
level={2}>
|
||||
{procedures.length ? (
|
||||
procedures.map(procedure => (
|
||||
<Section
|
||||
key={procedure.name}
|
||||
title={procedure.name}
|
||||
level={3}>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Next Step">
|
||||
{procedure.next_step}
|
||||
{procedure.chems_needed && (
|
||||
<Fragment>
|
||||
<b>
|
||||
Required Chemicals:
|
||||
</b>
|
||||
<br />
|
||||
{procedure.chems_needed}
|
||||
</Fragment>
|
||||
)}
|
||||
</LabeledList.Item>
|
||||
{!!data.alternative_step && (
|
||||
<LabeledList.Item label="Alternative Step">
|
||||
{procedure.alternative_step}
|
||||
{procedure.alt_chems_needed && (
|
||||
<Fragment>
|
||||
<b>
|
||||
Required Chemicals:
|
||||
</b>
|
||||
<br />
|
||||
{procedure.alt_chems_needed}
|
||||
</Fragment>
|
||||
)}
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
</LabeledList>
|
||||
</Section>
|
||||
))
|
||||
) : (
|
||||
'No Active Procedures'
|
||||
)}
|
||||
</Section>
|
||||
</Section>
|
||||
selected={tab === 1}
|
||||
onClick={() => setTab(1)}>
|
||||
Patient State
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
key="procedures"
|
||||
label="Surgery Procedures">
|
||||
<Section title="Advanced Surgery Procedures">
|
||||
<Button
|
||||
icon="download"
|
||||
content="Sync Research Database"
|
||||
onClick={() => act('sync')} />
|
||||
{surgeries.map(surgery => (
|
||||
<Section
|
||||
title={surgery.name}
|
||||
key={surgery.name}
|
||||
level={2}>
|
||||
{surgery.desc}
|
||||
</Section>
|
||||
))}
|
||||
</Section>
|
||||
selected={tab === 2}
|
||||
onClick={() => setTab(2)}>
|
||||
Surgery Procedures
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
{tab === 1 && (
|
||||
<PatientStateView />
|
||||
)}
|
||||
{tab === 2 && (
|
||||
<SurgeryProceduresView />
|
||||
)}
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const PatientStateView = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
table,
|
||||
procedures = [],
|
||||
patient = {},
|
||||
} = data;
|
||||
if (!table) {
|
||||
return (
|
||||
<NoticeBox>
|
||||
No Table Detected
|
||||
</NoticeBox>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Fragment>
|
||||
<Section title="Patient State">
|
||||
{patient && (
|
||||
<LabeledList>
|
||||
<LabeledList.Item
|
||||
label="State"
|
||||
color={patient.statstate}>
|
||||
{patient.stat}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Blood Type">
|
||||
{patient.blood_type}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Health">
|
||||
<ProgressBar
|
||||
value={patient.health}
|
||||
minValue={patient.minHealth}
|
||||
maxValue={patient.maxHealth}
|
||||
color={patient.health >= 0 ? 'good' : 'average'}>
|
||||
<AnimatedNumber value={patient.health} />
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
{damageTypes.map(type => (
|
||||
<LabeledList.Item key={type.type} label={type.label}>
|
||||
<ProgressBar
|
||||
value={patient[type.type] / patient.maxHealth}
|
||||
color="bad">
|
||||
<AnimatedNumber value={patient[type.type]} />
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
))}
|
||||
</LabeledList>
|
||||
) || (
|
||||
'No Patient Detected'
|
||||
)}
|
||||
</Section>
|
||||
{procedures.length === 0 && (
|
||||
<Section>
|
||||
No Active Procedures
|
||||
</Section>
|
||||
)}
|
||||
{procedures.map(procedure => (
|
||||
<Section
|
||||
key={procedure.name}
|
||||
title={procedure.name}>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Next Step">
|
||||
{procedure.next_step}
|
||||
{procedure.chems_needed && (
|
||||
<Fragment>
|
||||
<b>Required Chemicals:</b>
|
||||
<br />
|
||||
{procedure.chems_needed}
|
||||
</Fragment>
|
||||
)}
|
||||
</LabeledList.Item>
|
||||
{!!data.alternative_step && (
|
||||
<LabeledList.Item label="Alternative Step">
|
||||
{procedure.alternative_step}
|
||||
{procedure.alt_chems_needed && (
|
||||
<Fragment>
|
||||
<b>Required Chemicals:</b>
|
||||
<br />
|
||||
{procedure.alt_chems_needed}
|
||||
</Fragment>
|
||||
)}
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
</LabeledList>
|
||||
</Section>
|
||||
))}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const SurgeryProceduresView = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
surgeries = [],
|
||||
} = data;
|
||||
return (
|
||||
<Section title="Advanced Surgery Procedures">
|
||||
<Button
|
||||
icon="download"
|
||||
content="Sync Research Database"
|
||||
onClick={() => act('sync')} />
|
||||
{surgeries.map(surgery => (
|
||||
<Section
|
||||
title={surgery.name}
|
||||
key={surgery.name}
|
||||
level={2}>
|
||||
{surgery.desc}
|
||||
</Section>
|
||||
))}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,9 +1,100 @@
|
||||
import { map } from 'common/collections';
|
||||
import { Fragment } from 'inferno';
|
||||
import { useBackend } from '../backend';
|
||||
import { Button, Dimmer, Icon, LabeledList, Section, Tabs } from '../components';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Button, Dimmer, Flex, Icon, LabeledList, Section, Tabs } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
export const PersonalCrafting = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
busy,
|
||||
display_craftable_only,
|
||||
display_compact,
|
||||
} = data;
|
||||
const crafting_recipes = data.crafting_recipes || {};
|
||||
// Sort everything into flat categories
|
||||
const categories = [];
|
||||
const recipes = [];
|
||||
for (let category of Object.keys(crafting_recipes)) {
|
||||
const subcategories = crafting_recipes[category];
|
||||
if ('has_subcats' in subcategories) {
|
||||
for (let subcategory of Object.keys(subcategories)) {
|
||||
if (subcategory === 'has_subcats') {
|
||||
continue;
|
||||
}
|
||||
// Push category
|
||||
categories.push(subcategory);
|
||||
// Push recipes
|
||||
const _recipes = subcategories[subcategory];
|
||||
for (let recipe of _recipes) {
|
||||
recipes.push({
|
||||
...recipe,
|
||||
category: subcategory,
|
||||
});
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Push category
|
||||
categories.push(category);
|
||||
// Push recipes
|
||||
const _recipes = crafting_recipes[category];
|
||||
for (let recipe of _recipes) {
|
||||
recipes.push({
|
||||
...recipe,
|
||||
category,
|
||||
});
|
||||
}
|
||||
}
|
||||
// Sort out the tab state
|
||||
const [tab, setTab] = useLocalState(context, 'tab', categories[0]);
|
||||
const shownRecipes = recipes
|
||||
.filter(recipe => recipe.category === tab);
|
||||
return (
|
||||
<Window resizable>
|
||||
<Window.Content scrollable>
|
||||
{!!busy && (
|
||||
<Dimmer fontSize="32px">
|
||||
<Icon name="cog" spin={1} />
|
||||
{' Crafting...'}
|
||||
</Dimmer>
|
||||
)}
|
||||
<Section
|
||||
title="Personal Crafting"
|
||||
buttons={(
|
||||
<Fragment>
|
||||
<Button.Checkbox
|
||||
content="Compact"
|
||||
checked={display_compact}
|
||||
onClick={() => act('toggle_compact')} />
|
||||
<Button.Checkbox
|
||||
content="Craftable Only"
|
||||
checked={display_craftable_only}
|
||||
onClick={() => act('toggle_recipes')} />
|
||||
</Fragment>
|
||||
)}>
|
||||
<Flex>
|
||||
<Flex.Item>
|
||||
<Tabs vertical>
|
||||
{categories.map(category => (
|
||||
<Tabs.Tab
|
||||
key={category}
|
||||
selected={category === tab}
|
||||
onClick={() => setTab(category)}>
|
||||
{category}
|
||||
</Tabs.Tab>
|
||||
))}
|
||||
</Tabs>
|
||||
</Flex.Item>
|
||||
<Flex.Item grow={1} basis={0}>
|
||||
<CraftingList craftables={shownRecipes} />
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const CraftingList = (props, context) => {
|
||||
const {
|
||||
craftables = [],
|
||||
@@ -78,94 +169,3 @@ const CraftingList = (props, context) => {
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export const PersonalCrafting = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
busy,
|
||||
display_craftable_only,
|
||||
display_compact,
|
||||
} = data;
|
||||
|
||||
const craftingRecipes = map((subcategory, category) => {
|
||||
const hasSubcats = ('has_subcats' in subcategory);
|
||||
const firstSubcatName = Object.keys(subcategory)
|
||||
.find(name => name !== 'has_subcats');
|
||||
return {
|
||||
category,
|
||||
subcategory,
|
||||
hasSubcats,
|
||||
firstSubcatName,
|
||||
};
|
||||
})(data.crafting_recipes || {});
|
||||
|
||||
// Shows an overlay when crafting an item
|
||||
const busyBox = !!busy && (
|
||||
<Dimmer fontSize="32px">
|
||||
<Icon name="cog" spin={1} />
|
||||
{' Crafting...'}
|
||||
</Dimmer>
|
||||
);
|
||||
|
||||
return (
|
||||
<Window resizable>
|
||||
<Window.Content scrollable>
|
||||
{busyBox}
|
||||
<Section
|
||||
title="Personal Crafting"
|
||||
buttons={(
|
||||
<Fragment>
|
||||
<Button
|
||||
icon={display_compact ? "check-square-o" : "square-o"}
|
||||
content="Compact"
|
||||
selected={display_compact}
|
||||
onClick={() => act('toggle_compact')} />
|
||||
<Button
|
||||
icon={display_craftable_only ? "check-square-o" : "square-o"}
|
||||
content="Craftable Only"
|
||||
selected={display_craftable_only}
|
||||
onClick={() => act('toggle_recipes')} />
|
||||
</Fragment>
|
||||
)}>
|
||||
<Tabs>
|
||||
{craftingRecipes.map(recipe => (
|
||||
<Tabs.Tab
|
||||
key={recipe.category}
|
||||
label={recipe.category}
|
||||
onClick={() => act('set_category', {
|
||||
category: recipe.category,
|
||||
// Backend expects "0" or "" to indicate no subcategory
|
||||
subcategory: recipe.firstSubcatName,
|
||||
})}>
|
||||
{() => !recipe.hasSubcats && (
|
||||
<CraftingList
|
||||
craftables={recipe.subcategory} />
|
||||
) || (
|
||||
<Tabs vertical>
|
||||
{map((value, name) => {
|
||||
if (name === "has_subcats") {
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<Tabs.Tab
|
||||
label={name}
|
||||
onClick={() => act('set_category', {
|
||||
subcategory: name,
|
||||
})}>
|
||||
{() => (
|
||||
<CraftingList
|
||||
craftables={value} />
|
||||
)}
|
||||
</Tabs.Tab>
|
||||
);
|
||||
})(recipe.subcategory)}
|
||||
</Tabs>
|
||||
)}
|
||||
</Tabs.Tab>
|
||||
))}
|
||||
</Tabs>
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { classes } from 'common/react';
|
||||
import { useBackend } from '../backend';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Box, Button, ColorBox, Flex, LabeledList, Section, Tabs } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
@@ -64,6 +64,13 @@ export const RapidPipeDispenser = (props, context) => {
|
||||
mode,
|
||||
} = data;
|
||||
const previews = data.preview_rows.flatMap(row => row.previews);
|
||||
const [
|
||||
categoryName,
|
||||
setCategoryName,
|
||||
] = useLocalState(context, 'categoryName');
|
||||
const shownCategory = categories
|
||||
.find(category => category.cat_name === categoryName)
|
||||
|| categories[0];
|
||||
return (
|
||||
<Window resizable>
|
||||
<Window.Content scrollable>
|
||||
@@ -161,28 +168,30 @@ export const RapidPipeDispenser = (props, context) => {
|
||||
<Flex.Item m={0.5} grow={1}>
|
||||
<Section>
|
||||
<Tabs>
|
||||
{categories.map(category => (
|
||||
{categories.map((category, i) => (
|
||||
<Tabs.Tab
|
||||
fluid
|
||||
key={category.cat_name}
|
||||
icon={ICON_BY_CATEGORY_NAME[category.cat_name]}
|
||||
label={category.cat_name}>
|
||||
{() => category.recipes.map(recipe => (
|
||||
<Button.Checkbox
|
||||
key={recipe.pipe_index}
|
||||
fluid
|
||||
ellipsis
|
||||
checked={recipe.selected}
|
||||
content={recipe.pipe_name}
|
||||
title={recipe.pipe_name}
|
||||
onClick={() => act('pipe_type', {
|
||||
pipe_type: recipe.pipe_index,
|
||||
category: category.cat_name,
|
||||
})} />
|
||||
))}
|
||||
selected={category.cat_name === shownCategory.cat_name}
|
||||
onClick={() => setCategoryName(category.cat_name)}>
|
||||
{category.cat_name}
|
||||
</Tabs.Tab>
|
||||
))}
|
||||
</Tabs>
|
||||
{shownCategory?.recipes.map(recipe => (
|
||||
<Button.Checkbox
|
||||
key={recipe.pipe_index}
|
||||
fluid
|
||||
ellipsis
|
||||
checked={recipe.selected}
|
||||
content={recipe.pipe_name}
|
||||
title={recipe.pipe_name}
|
||||
onClick={() => act('pipe_type', {
|
||||
pipe_type: recipe.pipe_index,
|
||||
category: shownCategory.cat_name,
|
||||
})} />
|
||||
))}
|
||||
</Section>
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Fragment } from 'inferno';
|
||||
import { useBackend } from '../backend';
|
||||
import { Box, Button, NoticeBox, Section, Tabs, LabeledList } from '../components';
|
||||
import { useBackend, useSharedState } from '../backend';
|
||||
import { Box, Button, LabeledList, NoticeBox, Section, Tabs } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
export const RoboticsControlConsole = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const [tab, setTab] = useSharedState(context, 'tab', 1);
|
||||
const {
|
||||
can_hack,
|
||||
cyborgs = [],
|
||||
@@ -15,24 +16,26 @@ export const RoboticsControlConsole = (props, context) => {
|
||||
<Window.Content scrollable>
|
||||
<Tabs>
|
||||
<Tabs.Tab
|
||||
key="cyborgs"
|
||||
label={`Cyborgs (${cyborgs.length})`}
|
||||
icon="list"
|
||||
lineHeight="23px">
|
||||
{() => (
|
||||
<Cyborgs cyborgs={cyborgs} can_hack={can_hack} />
|
||||
)}
|
||||
lineHeight="23px"
|
||||
selected={tab === 1}
|
||||
onClick={() => setTab(1)}>
|
||||
Cyborgs ({cyborgs.length})
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
key="drones"
|
||||
label={`Drones (${drones.length})`}
|
||||
icon="list"
|
||||
lineHeight="23px">
|
||||
{() => (
|
||||
<Drones drones={drones} />
|
||||
)}
|
||||
lineHeight="23px"
|
||||
selected={tab === 2}
|
||||
onClick={() => setTab(2)}>
|
||||
Drones ({drones.length})
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
{tab === 1 && (
|
||||
<Cyborgs cyborgs={cyborgs} can_hack={can_hack} />
|
||||
)}
|
||||
{tab === 2 && (
|
||||
<Drones drones={drones} />
|
||||
)}
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
@@ -43,11 +46,9 @@ const Cyborgs = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
if (!cyborgs.length) {
|
||||
return (
|
||||
<Section>
|
||||
<NoticeBox textAlign="center">
|
||||
No cyborg units detected within access parameters
|
||||
</NoticeBox>
|
||||
</Section>
|
||||
<NoticeBox>
|
||||
No cyborg units detected within access parameters
|
||||
</NoticeBox>
|
||||
);
|
||||
}
|
||||
return cyborgs.map(cyborg => {
|
||||
@@ -127,11 +128,9 @@ const Drones = (props, context) => {
|
||||
|
||||
if (!drones.length) {
|
||||
return (
|
||||
<Section>
|
||||
<NoticeBox textAlign="center">
|
||||
No drone units detected within access parameters
|
||||
</NoticeBox>
|
||||
</Section>
|
||||
<NoticeBox>
|
||||
No drone units detected within access parameters
|
||||
</NoticeBox>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,48 @@
|
||||
import { map } from 'common/collections';
|
||||
import { Fragment } from 'inferno';
|
||||
import { useBackend } from '../backend';
|
||||
import { Button, LabeledList, Section, Table, Tabs } from '../components';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Button, Flex, LabeledList, Section, Table, Tabs } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
export const ShuttleManipulator = (props, context) => {
|
||||
const [tab, setTab] = useLocalState(context, 'tab', 1);
|
||||
return (
|
||||
<Window resizable>
|
||||
<Window.Content scrollable>
|
||||
<Tabs>
|
||||
<Tabs.Tab
|
||||
selected={tab === 1}
|
||||
onClick={() => setTab(1)}>
|
||||
Status
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
selected={tab === 2}
|
||||
onClick={() => setTab(2)}>
|
||||
Templates
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
selected={tab === 3}
|
||||
onClick={() => setTab(3)}>
|
||||
Modification
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
{tab === 1 && (
|
||||
<ShuttleManipulatorStatus />
|
||||
)}
|
||||
{tab === 2 && (
|
||||
<ShuttleManipulatorTemplates />
|
||||
)}
|
||||
{tab === 3 && (
|
||||
<ShuttleManipulatorModification />
|
||||
)}
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
export const ShuttleManipulatorStatus = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
|
||||
const shuttles = data.shuttles || [];
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<Table>
|
||||
@@ -65,70 +99,76 @@ export const ShuttleManipulatorStatus = (props, context) => {
|
||||
|
||||
export const ShuttleManipulatorTemplates = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
|
||||
const templateObject = data.templates || {};
|
||||
const selected = data.selected || {};
|
||||
|
||||
const [
|
||||
selectedTemplateId,
|
||||
setSelectedTemplateId,
|
||||
] = useLocalState(context, 'templateId', Object.keys(templateObject)[0]);
|
||||
const actualTemplates = templateObject[selectedTemplateId]?.templates;
|
||||
return (
|
||||
<Section>
|
||||
<Tabs vertical>
|
||||
{map((template, templateId) => {
|
||||
const templates = template.templates || [];
|
||||
return (
|
||||
<Tabs.Tab
|
||||
key={templateId}
|
||||
label={template.port_id}>
|
||||
{templates.map(actualTemplate => {
|
||||
const isSelected = (
|
||||
actualTemplate.shuttle_id === selected.shuttle_id
|
||||
);
|
||||
// Whoever made the structure being sent is an asshole
|
||||
return (
|
||||
<Section
|
||||
title={actualTemplate.name}
|
||||
level={2}
|
||||
key={actualTemplate.shuttle_id}
|
||||
buttons={(
|
||||
<Button
|
||||
content={isSelected ? 'Selected' : 'Select'}
|
||||
selected={isSelected}
|
||||
onClick={() => act('select_template', {
|
||||
shuttle_id: actualTemplate.shuttle_id,
|
||||
})} />
|
||||
)}>
|
||||
{(!!actualTemplate.description
|
||||
|| !!actualTemplate.admin_notes
|
||||
) && (
|
||||
<LabeledList>
|
||||
{!!actualTemplate.description && (
|
||||
<LabeledList.Item label="Description">
|
||||
{actualTemplate.description}
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
{!!actualTemplate.admin_notes && (
|
||||
<LabeledList.Item label="Admin Notes">
|
||||
{actualTemplate.admin_notes}
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
</LabeledList>
|
||||
<Flex>
|
||||
<Flex.Item>
|
||||
<Tabs vertical>
|
||||
{map((template, templateId) => (
|
||||
<Tabs.Tab
|
||||
key={templateId}
|
||||
selected={selectedTemplateId === templateId}
|
||||
onClick={() => setSelectedTemplateId(templateId)}>
|
||||
{template.port_id}
|
||||
</Tabs.Tab>
|
||||
))(templateObject)}
|
||||
</Tabs>
|
||||
</Flex.Item>
|
||||
<Flex.Item grow={1} basis={0}>
|
||||
{actualTemplates.map(actualTemplate => {
|
||||
const isSelected = (
|
||||
actualTemplate.shuttle_id === selected.shuttle_id
|
||||
);
|
||||
// Whoever made the structure being sent is an asshole
|
||||
return (
|
||||
<Section
|
||||
title={actualTemplate.name}
|
||||
level={2}
|
||||
key={actualTemplate.shuttle_id}
|
||||
buttons={(
|
||||
<Button
|
||||
content={isSelected ? 'Selected' : 'Select'}
|
||||
selected={isSelected}
|
||||
onClick={() => act('select_template', {
|
||||
shuttle_id: actualTemplate.shuttle_id,
|
||||
})} />
|
||||
)}>
|
||||
{(!!actualTemplate.description
|
||||
|| !!actualTemplate.admin_notes
|
||||
) && (
|
||||
<LabeledList>
|
||||
{!!actualTemplate.description && (
|
||||
<LabeledList.Item label="Description">
|
||||
{actualTemplate.description}
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
</Section>
|
||||
);
|
||||
})}
|
||||
</Tabs.Tab>
|
||||
);
|
||||
})(templateObject)}
|
||||
</Tabs>
|
||||
{!!actualTemplate.admin_notes && (
|
||||
<LabeledList.Item label="Admin Notes">
|
||||
{actualTemplate.admin_notes}
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
</LabeledList>
|
||||
)}
|
||||
</Section>
|
||||
);
|
||||
})}
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
export const ShuttleManipulatorModification = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
|
||||
const selected = data.selected || {};
|
||||
const existingShuttle = data.existing_shuttle || {};
|
||||
|
||||
return (
|
||||
<Section>
|
||||
{selected ? (
|
||||
@@ -200,29 +240,3 @@ export const ShuttleManipulatorModification = (props, context) => {
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
export const ShuttleManipulator = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const selected = data.selected || {};
|
||||
const existingShuttle = data.existing_shuttle || {};
|
||||
return (
|
||||
<Window resizable>
|
||||
<Window.Content scrollable>
|
||||
<Tabs>
|
||||
<Tabs.Tab
|
||||
label="Status">
|
||||
<ShuttleManipulatorStatus />
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
label="Templates">
|
||||
<ShuttleManipulatorTemplates />
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
label="Modification">
|
||||
<ShuttleManipulatorModification />
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
import { Component, Fragment } from 'inferno';
|
||||
import { useBackend } from '../backend';
|
||||
import { Box, Button, Dimmer, Grid, Icon, LabeledList, NoticeBox, Section, Table, Tabs } from '../components';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Box, Button, Flex, Grid, Icon, LabeledList, Modal, NoticeBox, Section, Table, Tabs } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
const CONTRACT_STATUS_INACTIVE = 1;
|
||||
const CONTRACT_STATUS_ACTIVE = 2;
|
||||
const CONTRACT_STATUS_BOUNTY_CONSOLE_ACTIVE = 3;
|
||||
const CONTRACT_STATUS_EXTRACTING = 4;
|
||||
const CONTRACT_STATUS_COMPLETE = 5;
|
||||
const CONTRACT_STATUS_ABORTED = 6;
|
||||
|
||||
export class FakeTerminal extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -123,36 +130,29 @@ export const SyndContractorContent = (props, context) => {
|
||||
];
|
||||
|
||||
const errorPane = !!data.error && (
|
||||
<Dimmer>
|
||||
<Box
|
||||
backgroundColor="red"
|
||||
minHeight="150px">
|
||||
<Table m={1}>
|
||||
<Table.Row>
|
||||
<Table.Cell collapsing fontSize="100px">
|
||||
<Icon
|
||||
name="exclamation-triangle"
|
||||
mt={4}
|
||||
ml={2} />
|
||||
</Table.Cell>
|
||||
<Table.Cell
|
||||
verticalAlign="top"
|
||||
textAlign="center">
|
||||
<Box
|
||||
m={1}
|
||||
textAlign="left"
|
||||
width="100%"
|
||||
minHeight="110px">
|
||||
{data.error}
|
||||
</Box>
|
||||
<Button
|
||||
content="Dismiss"
|
||||
onClick={() => act('PRG_clear_error')} />
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table>
|
||||
</Box>
|
||||
</Dimmer>
|
||||
<Modal backgroundColor="red">
|
||||
<Flex align="center">
|
||||
<Flex.Item mr={2}>
|
||||
<Icon
|
||||
size={4}
|
||||
name="exclamation-triangle" />
|
||||
</Flex.Item>
|
||||
<Flex.Item
|
||||
mr={2}
|
||||
grow={1}
|
||||
textAlign="center">
|
||||
<Box
|
||||
width="260px"
|
||||
textAlign="left"
|
||||
minHeight="80px">
|
||||
{data.error}
|
||||
</Box>
|
||||
<Button
|
||||
content="Dismiss"
|
||||
onClick={() => act('PRG_clear_error')} />
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
if (!data.logged_in) {
|
||||
@@ -271,144 +271,160 @@ export const StatusPane = (props, context) => {
|
||||
};
|
||||
|
||||
export const SyndPane = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
|
||||
const contractor_hub_items = data.contractor_hub_items || [];
|
||||
const contracts = data.contracts || [];
|
||||
|
||||
const CONTRACT_STATUS_INACTIVE = 1;
|
||||
const CONTRACT_STATUS_ACTIVE = 2;
|
||||
const CONTRACT_STATUS_BOUNTY_CONSOLE_ACTIVE = 3;
|
||||
const CONTRACT_STATUS_EXTRACTING = 4;
|
||||
const CONTRACT_STATUS_COMPLETE = 5;
|
||||
const CONTRACT_STATUS_ABORTED = 6;
|
||||
|
||||
const [tab, setTab] = useLocalState(context, 'tab', 1);
|
||||
return (
|
||||
<Fragment>
|
||||
<StatusPane state={props.state} />
|
||||
<Tabs>
|
||||
<Tabs.Tab label="Contracts">
|
||||
<Section
|
||||
title="Availible Contracts"
|
||||
buttons={(
|
||||
<Button
|
||||
content="Call Extraction"
|
||||
disabled={!data.ongoing_contract || data.extraction_enroute}
|
||||
onClick={() => act('PRG_call_extraction')} />
|
||||
)}>
|
||||
{contracts.map(contract => {
|
||||
if (data.ongoing_contract
|
||||
&& contract.status !== CONTRACT_STATUS_ACTIVE) {
|
||||
return;
|
||||
}
|
||||
|
||||
const active = (contract.status > CONTRACT_STATUS_INACTIVE);
|
||||
if (contract.status >= CONTRACT_STATUS_COMPLETE) {
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<Section
|
||||
key={contract.target}
|
||||
title={contract.target
|
||||
? `${contract.target} (${contract.target_rank})`
|
||||
: "Invalid Target"}
|
||||
level={active ? 1 : 2}
|
||||
buttons={(
|
||||
<Fragment>
|
||||
<Box
|
||||
inline
|
||||
bold
|
||||
mr={1}>
|
||||
{contract.payout} (+{contract.payout_bonus}) TC
|
||||
</Box>
|
||||
<Button
|
||||
content={active ? "Abort" : "Accept"}
|
||||
disabled={contract.extraction_enroute}
|
||||
color={active && "bad"}
|
||||
onClick={() => act(
|
||||
'PRG_contract' + (active ? '_abort' : '-accept'),
|
||||
{
|
||||
contract_id: contract.id,
|
||||
})} />
|
||||
</Fragment>
|
||||
)}>
|
||||
<Grid>
|
||||
<Grid.Column>
|
||||
{contract.message}
|
||||
</Grid.Column>
|
||||
<Grid.Column size={0.5}>
|
||||
<Box
|
||||
bold
|
||||
mb={1}>
|
||||
Dropoff Location:
|
||||
</Box>
|
||||
<Box>
|
||||
{contract.dropoff}
|
||||
</Box>
|
||||
</Grid.Column>
|
||||
</Grid>
|
||||
</Section>
|
||||
);
|
||||
})}
|
||||
</Section>
|
||||
<Section
|
||||
title="Dropoff Locator"
|
||||
textAlign="center"
|
||||
opacity={data.ongoing_contract ? 100 : 0}>
|
||||
<Box bold>
|
||||
{data.dropoff_direction}
|
||||
</Box>
|
||||
</Section>
|
||||
<Tabs.Tab
|
||||
selected={tab === 1}
|
||||
onClick={() => setTab(1)}>
|
||||
Contracts
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab label="Hub">
|
||||
<Section>
|
||||
{contractor_hub_items.map(item => {
|
||||
const repInfo = item.cost ? (item.cost + ' Rep') : 'FREE';
|
||||
const limited = (item.limited !== -1);
|
||||
return (
|
||||
<Section
|
||||
key={item.name}
|
||||
title={item.name + ' - ' + repInfo}
|
||||
level={2}
|
||||
buttons={(
|
||||
<Fragment>
|
||||
{limited && (
|
||||
<Box
|
||||
inline
|
||||
bold
|
||||
mr={1}>
|
||||
{item.limited} remaining
|
||||
</Box>
|
||||
)}
|
||||
<Button
|
||||
content="Purchase"
|
||||
disabled={data.contract_rep < item.cost
|
||||
|| (limited && item.limited <= 0)}
|
||||
onClick={() => act('buy_hub', {
|
||||
item: item.name,
|
||||
cost: item.cost,
|
||||
})} />
|
||||
</Fragment>
|
||||
)}>
|
||||
<Table>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Icon
|
||||
fontSize="60px"
|
||||
name={item.item_icon} />
|
||||
</Table.Cell>
|
||||
<Table.Cell
|
||||
verticalAlign="top">
|
||||
{item.desc}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table>
|
||||
</Section>
|
||||
);
|
||||
})}
|
||||
</Section>
|
||||
<Tabs.Tab
|
||||
selected={tab === 2}
|
||||
onClick={() => setTab(2)}>
|
||||
Hub
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
{tab === 1 && (
|
||||
<ContractsTab />
|
||||
)}
|
||||
{tab === 2 && (
|
||||
<HubTab />
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const ContractsTab = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const contracts = data.contracts || [];
|
||||
return (
|
||||
<Fragment>
|
||||
<Section
|
||||
title="Availible Contracts"
|
||||
buttons={(
|
||||
<Button
|
||||
content="Call Extraction"
|
||||
disabled={!data.ongoing_contract || data.extraction_enroute}
|
||||
onClick={() => act('PRG_call_extraction')} />
|
||||
)}>
|
||||
{contracts.map(contract => {
|
||||
if (data.ongoing_contract
|
||||
&& contract.status !== CONTRACT_STATUS_ACTIVE) {
|
||||
return;
|
||||
}
|
||||
const active = (contract.status > CONTRACT_STATUS_INACTIVE);
|
||||
if (contract.status >= CONTRACT_STATUS_COMPLETE) {
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<Section
|
||||
key={contract.target}
|
||||
title={contract.target
|
||||
? `${contract.target} (${contract.target_rank})`
|
||||
: "Invalid Target"}
|
||||
level={active ? 1 : 2}
|
||||
buttons={(
|
||||
<Fragment>
|
||||
<Box
|
||||
inline
|
||||
bold
|
||||
mr={1}>
|
||||
{contract.payout} (+{contract.payout_bonus}) TC
|
||||
</Box>
|
||||
<Button
|
||||
content={active ? "Abort" : "Accept"}
|
||||
disabled={contract.extraction_enroute}
|
||||
color={active && "bad"}
|
||||
onClick={() => act(
|
||||
'PRG_contract' + (active ? '_abort' : '-accept'),
|
||||
{
|
||||
contract_id: contract.id,
|
||||
})} />
|
||||
</Fragment>
|
||||
)}>
|
||||
<Grid>
|
||||
<Grid.Column>
|
||||
{contract.message}
|
||||
</Grid.Column>
|
||||
<Grid.Column size={0.5}>
|
||||
<Box
|
||||
bold
|
||||
mb={1}>
|
||||
Dropoff Location:
|
||||
</Box>
|
||||
<Box>
|
||||
{contract.dropoff}
|
||||
</Box>
|
||||
</Grid.Column>
|
||||
</Grid>
|
||||
</Section>
|
||||
);
|
||||
})}
|
||||
</Section>
|
||||
<Section
|
||||
title="Dropoff Locator"
|
||||
textAlign="center"
|
||||
opacity={data.ongoing_contract ? 100 : 0}>
|
||||
<Box bold>
|
||||
{data.dropoff_direction}
|
||||
</Box>
|
||||
</Section>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const HubTab = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const contractor_hub_items = data.contractor_hub_items || [];
|
||||
return (
|
||||
<Section>
|
||||
{contractor_hub_items.map(item => {
|
||||
const repInfo = item.cost ? (item.cost + ' Rep') : 'FREE';
|
||||
const limited = (item.limited !== -1);
|
||||
return (
|
||||
<Section
|
||||
key={item.name}
|
||||
title={item.name + ' - ' + repInfo}
|
||||
level={2}
|
||||
buttons={(
|
||||
<Fragment>
|
||||
{limited && (
|
||||
<Box
|
||||
inline
|
||||
bold
|
||||
mr={1}>
|
||||
{item.limited} remaining
|
||||
</Box>
|
||||
)}
|
||||
<Button
|
||||
content="Purchase"
|
||||
disabled={data.contract_rep < item.cost
|
||||
|| (limited && item.limited <= 0)}
|
||||
onClick={() => act('buy_hub', {
|
||||
item: item.name,
|
||||
cost: item.cost,
|
||||
})} />
|
||||
</Fragment>
|
||||
)}>
|
||||
<Table>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Icon
|
||||
fontSize="60px"
|
||||
name={item.item_icon} />
|
||||
</Table.Cell>
|
||||
<Table.Cell
|
||||
verticalAlign="top">
|
||||
{item.desc}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table>
|
||||
</Section>
|
||||
);
|
||||
})}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,171 +1,182 @@
|
||||
import { createSearch, decodeHtmlEntities } from 'common/string';
|
||||
import { Fragment } from 'inferno';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Box, Button, Input, Section, Table, Tabs } from '../components';
|
||||
import { Box, Button, Flex, Input, Section, Table, Tabs } from '../components';
|
||||
import { formatMoney } from '../format';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
const MAX_SEARCH_RESULTS = 25;
|
||||
|
||||
export const Uplink = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
compact_mode,
|
||||
lockable,
|
||||
telecrystals,
|
||||
categories = [],
|
||||
} = data;
|
||||
const [
|
||||
hoveredItem,
|
||||
setHoveredItem,
|
||||
] = useLocalState(context, 'hoveredItem', {});
|
||||
const [
|
||||
searchText,
|
||||
setSearchText,
|
||||
] = useLocalState(context, 'searchText', '');
|
||||
const testSearch = createSearch(searchText, item => {
|
||||
return item.name + item.desc;
|
||||
});
|
||||
const { data } = useBackend(context);
|
||||
const { telecrystals } = data;
|
||||
return (
|
||||
<Window
|
||||
theme="syndicate"
|
||||
resizable>
|
||||
<Window.Content scrollable>
|
||||
<Section
|
||||
title={(
|
||||
<Box
|
||||
inline
|
||||
color={telecrystals > 0 ? 'good' : 'bad'}>
|
||||
{telecrystals} TC
|
||||
</Box>
|
||||
)}
|
||||
buttons={(
|
||||
<Fragment>
|
||||
Search
|
||||
<Input
|
||||
value={searchText}
|
||||
onInput={(e, value) => setSearchText(value)}
|
||||
ml={1}
|
||||
mr={1} />
|
||||
<Button
|
||||
icon={compact_mode ? 'list' : 'info'}
|
||||
content={compact_mode ? 'Compact' : 'Detailed'}
|
||||
onClick={() => act('compact_toggle')} />
|
||||
{!!lockable && (
|
||||
<Button
|
||||
icon="lock"
|
||||
content="Lock"
|
||||
onClick={() => act('lock')} />
|
||||
)}
|
||||
</Fragment>
|
||||
)}>
|
||||
{searchText.length > 0 ? (
|
||||
<ItemList
|
||||
compact
|
||||
items={categories
|
||||
.flatMap(category => category.items || [])
|
||||
.filter(testSearch)
|
||||
.filter((item, i) => i < MAX_SEARCH_RESULTS)}
|
||||
hoveredItem={hoveredItem}
|
||||
onBuyMouseOver={item => setHoveredItem(item)}
|
||||
onBuyMouseOut={item => setHoveredItem({})}
|
||||
onBuy={item => act('buy', {
|
||||
item: item.name,
|
||||
})} />
|
||||
) : (
|
||||
<Tabs vertical>
|
||||
{categories.map(category => {
|
||||
const { name, items } = category;
|
||||
if (items === null) {
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<Tabs.Tab
|
||||
key={name}
|
||||
label={`${name} (${items.length})`}>
|
||||
{() => (
|
||||
<ItemList
|
||||
compact={compact_mode}
|
||||
items={items}
|
||||
hoveredItem={hoveredItem}
|
||||
telecrystals={telecrystals}
|
||||
onBuyMouseOver={item => setHoveredItem(item)}
|
||||
onBuyMouseOut={item => setHoveredItem({})}
|
||||
onBuy={item => act('buy', {
|
||||
item: item.name,
|
||||
})} />
|
||||
)}
|
||||
</Tabs.Tab>
|
||||
);
|
||||
})}
|
||||
</Tabs>
|
||||
)}
|
||||
</Section>
|
||||
<GenericUplink
|
||||
currencyAmount={telecrystals}
|
||||
currencySymbol="TC" />
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const ItemList = props => {
|
||||
export const GenericUplink = (props, context) => {
|
||||
const {
|
||||
items,
|
||||
hoveredItem,
|
||||
telecrystals,
|
||||
compact,
|
||||
onBuy,
|
||||
onBuyMouseOver,
|
||||
onBuyMouseOut,
|
||||
currencyAmount = 0,
|
||||
currencySymbol = 'cr',
|
||||
} = props;
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
compactMode,
|
||||
lockable,
|
||||
categories = [],
|
||||
} = data;
|
||||
const [
|
||||
searchText,
|
||||
setSearchText,
|
||||
] = useLocalState(context, 'searchText', '');
|
||||
const [
|
||||
selectedCategory,
|
||||
setSelectedCategory,
|
||||
] = useLocalState(context, 'category', categories[0]?.name);
|
||||
const testSearch = createSearch(searchText, item => {
|
||||
return item.name + item.desc;
|
||||
});
|
||||
const items = searchText.length > 0
|
||||
// Flatten all categories and apply search to it
|
||||
&& categories
|
||||
.flatMap(category => category.items || [])
|
||||
.filter(testSearch)
|
||||
.filter((item, i) => i < MAX_SEARCH_RESULTS)
|
||||
// Select a category and show all items in it
|
||||
|| categories
|
||||
.find(category => category.name === selectedCategory)
|
||||
?.items;
|
||||
return (
|
||||
<Section
|
||||
title={(
|
||||
<Box
|
||||
inline
|
||||
color={currencyAmount > 0 ? 'good' : 'bad'}>
|
||||
{formatMoney(currencyAmount)} {currencySymbol}
|
||||
</Box>
|
||||
)}
|
||||
buttons={(
|
||||
<Fragment>
|
||||
Search
|
||||
<Input
|
||||
value={searchText}
|
||||
onInput={(e, value) => setSearchText(value)}
|
||||
mx={1} />
|
||||
<Button
|
||||
icon={compactMode ? 'list' : 'info'}
|
||||
content={compactMode ? 'Compact' : 'Detailed'}
|
||||
onClick={() => act('compact_toggle')} />
|
||||
{!!lockable && (
|
||||
<Button
|
||||
icon="lock"
|
||||
content="Lock"
|
||||
onClick={() => act('lock')} />
|
||||
)}
|
||||
</Fragment>
|
||||
)}>
|
||||
<Flex>
|
||||
{searchText.length === 0 && (
|
||||
<Flex.Item>
|
||||
<Tabs vertical>
|
||||
{categories.map(category => (
|
||||
<Tabs.Tab
|
||||
key={category.name}
|
||||
selected={category.name === selectedCategory}
|
||||
onClick={() => setSelectedCategory(category.name)}>
|
||||
{category.name} ({category.items?.length || 0})
|
||||
</Tabs.Tab>
|
||||
))}
|
||||
</Tabs>
|
||||
</Flex.Item>
|
||||
)}
|
||||
<Flex.Item grow={1} basis={0}>
|
||||
<ItemList
|
||||
compactMode={searchText.length > 0 || compactMode}
|
||||
currencyAmount={currencyAmount}
|
||||
currencySymbol={currencySymbol}
|
||||
items={items} />
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const ItemList = (props, context) => {
|
||||
const {
|
||||
compactMode,
|
||||
currencyAmount,
|
||||
currencySymbol,
|
||||
} = props;
|
||||
const { act } = useBackend(context);
|
||||
const [
|
||||
hoveredItem,
|
||||
setHoveredItem,
|
||||
] = useLocalState(context, 'hoveredItem', {});
|
||||
const hoveredCost = hoveredItem && hoveredItem.cost || 0;
|
||||
if (compact) {
|
||||
// Append extra hover data to items
|
||||
const items = props.items.map(item => {
|
||||
const notSameItem = hoveredItem && hoveredItem.name !== item.name;
|
||||
const notEnoughHovered = currencyAmount - hoveredCost < item.cost;
|
||||
const disabledDueToHovered = notSameItem && notEnoughHovered;
|
||||
const disabled = currencyAmount < item.cost || disabledDueToHovered;
|
||||
return {
|
||||
...item,
|
||||
disabled,
|
||||
};
|
||||
});
|
||||
if (compactMode) {
|
||||
return (
|
||||
<Table>
|
||||
{items.map(item => {
|
||||
const notSameItem = hoveredItem && hoveredItem.name !== item.name;
|
||||
const notEnoughHovered = telecrystals - hoveredCost < item.cost;
|
||||
const disabledDueToHovered = notSameItem && notEnoughHovered;
|
||||
return (
|
||||
<Table.Row
|
||||
key={item.name}
|
||||
className="candystripe">
|
||||
<Table.Cell bold>
|
||||
{decodeHtmlEntities(item.name)}
|
||||
</Table.Cell>
|
||||
<Table.Cell collapsing textAlign="right">
|
||||
<Button
|
||||
fluid
|
||||
content={item.cost + " TC"}
|
||||
disabled={telecrystals < item.cost || disabledDueToHovered}
|
||||
tooltip={item.desc}
|
||||
tooltipPosition="left"
|
||||
onmouseover={() => onBuyMouseOver(item)}
|
||||
onmouseout={() => onBuyMouseOut(item)}
|
||||
onClick={() => onBuy(item)} />
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
);
|
||||
})}
|
||||
{items.map(item => (
|
||||
<Table.Row
|
||||
key={item.name}
|
||||
className="candystripe">
|
||||
<Table.Cell bold>
|
||||
{decodeHtmlEntities(item.name)}
|
||||
</Table.Cell>
|
||||
<Table.Cell collapsing textAlign="right">
|
||||
<Button
|
||||
fluid
|
||||
content={formatMoney(item.cost) + ' ' + currencySymbol}
|
||||
disabled={item.disabled}
|
||||
tooltip={item.desc}
|
||||
tooltipPosition="left"
|
||||
onmouseover={() => setHoveredItem(item)}
|
||||
onmouseout={() => setHoveredItem({})}
|
||||
onClick={() => act('buy', {
|
||||
item: item.name,
|
||||
})} />
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
))}
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
return items.map(item => {
|
||||
const notSameItem = hoveredItem && hoveredItem.name !== item.name;
|
||||
const notEnoughHovered = telecrystals - hoveredCost < item.cost;
|
||||
const disabledDueToHovered = notSameItem && notEnoughHovered;
|
||||
return (
|
||||
<Section
|
||||
key={item.name}
|
||||
title={item.name}
|
||||
level={2}
|
||||
buttons={(
|
||||
<Button
|
||||
content={item.cost + ' TC'}
|
||||
disabled={telecrystals < item.cost || disabledDueToHovered}
|
||||
onmouseover={() => onBuyMouseOver(item)}
|
||||
onmouseout={() => onBuyMouseOut(item)}
|
||||
onClick={() => onBuy(item)} />
|
||||
)}>
|
||||
{decodeHtmlEntities(item.desc)}
|
||||
</Section>
|
||||
);
|
||||
});
|
||||
return items.map(item => (
|
||||
<Section
|
||||
key={item.name}
|
||||
title={item.name}
|
||||
level={2}
|
||||
buttons={(
|
||||
<Button
|
||||
content={item.cost + ' ' + currencySymbol}
|
||||
disabled={item.disabled}
|
||||
onmouseover={() => setHoveredItem(item)}
|
||||
onmouseout={() => setHoveredItem({})}
|
||||
onClick={() => act('buy', {
|
||||
item: item.name,
|
||||
})} />
|
||||
)}>
|
||||
{decodeHtmlEntities(item.desc)}
|
||||
</Section>
|
||||
));
|
||||
};
|
||||
|
||||
@@ -1,8 +1,24 @@
|
||||
import { Tabs, Section, Button, Grid } from "../../components";
|
||||
import { Fragment } from "inferno";
|
||||
import { sortBy } from "common/collections";
|
||||
import { sortBy } from 'common/collections';
|
||||
import { Fragment } from 'inferno';
|
||||
import { useLocalState } from '../../backend';
|
||||
import { Button, Flex, Grid, Section, Tabs } from '../../components';
|
||||
|
||||
export const AccessList = props => {
|
||||
const diffMap = {
|
||||
0: {
|
||||
icon: 'times-circle',
|
||||
color: 'bad',
|
||||
},
|
||||
1: {
|
||||
icon: 'stop-circle',
|
||||
color: null,
|
||||
},
|
||||
2: {
|
||||
icon: 'check-circle',
|
||||
color: 'good',
|
||||
},
|
||||
};
|
||||
|
||||
export const AccessList = (props, context) => {
|
||||
const {
|
||||
accesses = [],
|
||||
selectedList = [],
|
||||
@@ -12,35 +28,27 @@ export const AccessList = props => {
|
||||
grantDep,
|
||||
denyDep,
|
||||
} = props;
|
||||
|
||||
const diffMap = {
|
||||
0: {
|
||||
icon: 'times-circle',
|
||||
color: 'bad',
|
||||
},
|
||||
1: {
|
||||
icon: 'stop-circle',
|
||||
color: null,
|
||||
},
|
||||
2: {
|
||||
icon: 'check-circle',
|
||||
color: 'good',
|
||||
},
|
||||
};
|
||||
const [
|
||||
selectedAccessName,
|
||||
setSelectedAccessName,
|
||||
] = useLocalState(context, 'accessName', accesses[0]?.name);
|
||||
const selectedAccess = accesses
|
||||
.find(access => access.name === selectedAccessName);
|
||||
const selectedAccessEntries = sortBy(
|
||||
entry => entry.desc,
|
||||
)(selectedAccess?.accesses || []);
|
||||
|
||||
const checkAccessIcon = accesses => {
|
||||
let oneAccess = false;
|
||||
let oneInaccess = false;
|
||||
|
||||
accesses.forEach(element => {
|
||||
for (let element of accesses) {
|
||||
if (selectedList.includes(element.ref)) {
|
||||
oneAccess = true;
|
||||
}
|
||||
else {
|
||||
oneInaccess = true;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
if (!oneAccess && oneInaccess) {
|
||||
return 0;
|
||||
}
|
||||
@@ -69,49 +77,56 @@ export const AccessList = props => {
|
||||
onClick={() => denyAll()} />
|
||||
</Fragment>
|
||||
)}>
|
||||
<Tabs vertical altSelection>
|
||||
{accesses.map(access => {
|
||||
const accessEntries = sortBy(
|
||||
entry => entry.desc,
|
||||
)(access.accesses || []);
|
||||
const icon = diffMap[checkAccessIcon(accessEntries)].icon;
|
||||
const color = diffMap[checkAccessIcon(accessEntries)].color;
|
||||
return (
|
||||
<Tabs.Tab
|
||||
key={access.name}
|
||||
label={access.name}
|
||||
color={color}
|
||||
icon={icon}>
|
||||
<Grid>
|
||||
<Grid.Column mr={0}>
|
||||
<Button
|
||||
fluid
|
||||
icon="check"
|
||||
content="Grant Region"
|
||||
color="good"
|
||||
onClick={() => grantDep(access.regid)} />
|
||||
</Grid.Column>
|
||||
<Grid.Column ml={0}>
|
||||
<Button
|
||||
fluid
|
||||
icon="times"
|
||||
content="Deny Region"
|
||||
color="bad"
|
||||
onClick={() => denyDep(access.regid)} />
|
||||
</Grid.Column>
|
||||
</Grid>
|
||||
{accessEntries.map(entry => (
|
||||
<Button.Checkbox
|
||||
fluid
|
||||
key={entry.desc}
|
||||
content={entry.desc}
|
||||
checked={selectedList.includes(entry.ref)}
|
||||
onClick={() => accessMod(entry.ref)} />
|
||||
))}
|
||||
</Tabs.Tab>
|
||||
);
|
||||
})}
|
||||
</Tabs>
|
||||
<Flex>
|
||||
<Flex.Item>
|
||||
<Tabs vertical>
|
||||
{accesses.map(access => {
|
||||
const entries = access.accesses || [];
|
||||
const icon = diffMap[checkAccessIcon(entries)].icon;
|
||||
const color = diffMap[checkAccessIcon(entries)].color;
|
||||
return (
|
||||
<Tabs.Tab
|
||||
key={access.name}
|
||||
altSelection
|
||||
color={color}
|
||||
icon={icon}
|
||||
selected={access.name === selectedAccessName}
|
||||
onClick={() => setSelectedAccessName(access.name)}>
|
||||
{access.name}
|
||||
</Tabs.Tab>
|
||||
);
|
||||
})}
|
||||
</Tabs>
|
||||
</Flex.Item>
|
||||
<Flex.Item grow={1}>
|
||||
<Grid>
|
||||
<Grid.Column mr={0}>
|
||||
<Button
|
||||
fluid
|
||||
icon="check"
|
||||
content="Grant Region"
|
||||
color="good"
|
||||
onClick={() => grantDep(selectedAccess.regid)} />
|
||||
</Grid.Column>
|
||||
<Grid.Column ml={0}>
|
||||
<Button
|
||||
fluid
|
||||
icon="times"
|
||||
content="Deny Region"
|
||||
color="bad"
|
||||
onClick={() => denyDep(selectedAccess.regid)} />
|
||||
</Grid.Column>
|
||||
</Grid>
|
||||
{selectedAccessEntries.map(entry => (
|
||||
<Button.Checkbox
|
||||
fluid
|
||||
key={entry.desc}
|
||||
content={entry.desc}
|
||||
checked={selectedList.includes(entry.ref)}
|
||||
onClick={() => accessMod(entry.ref)} />
|
||||
))}
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
+7
-7
@@ -1,8 +1,8 @@
|
||||
import { Component } from 'inferno';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { BlockQuote, Box, Button, ByondUi, Collapsible, Icon, Input, Knob, LabeledList, NumberInput, ProgressBar, Section, Slider, Tabs, Tooltip, Flex } from '../components';
|
||||
import { DraggableControl } from '../components/DraggableControl';
|
||||
import { Window } from '../layouts';
|
||||
import { Component, Fragment } from 'inferno';
|
||||
import { useBackend, useLocalState } from '../../backend';
|
||||
import { BlockQuote, Box, Button, ByondUi, Collapsible, Flex, Icon, Input, Knob, LabeledList, NumberInput, ProgressBar, Section, Slider, Tabs, Tooltip } from '../../components';
|
||||
import { DraggableControl } from '../../components/DraggableControl';
|
||||
import { Window } from '../../layouts';
|
||||
|
||||
const COLORS_ARBITRARY = [
|
||||
'red',
|
||||
@@ -265,7 +265,7 @@ const KitchenSinkTooltip = props => {
|
||||
'bottom-right',
|
||||
];
|
||||
return (
|
||||
<Box>
|
||||
<Fragment>
|
||||
<Box>
|
||||
<Box inline position="relative" mr={1}>
|
||||
Box (hover me).
|
||||
@@ -285,7 +285,7 @@ const KitchenSinkTooltip = props => {
|
||||
content={position} />
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,14 +5,15 @@
|
||||
|
||||
/* eslint-disable */
|
||||
(function(Proto) {
|
||||
|
||||
function toAttr(prop) {
|
||||
return prop.replace(/-[a-z]/g, function (bit) {
|
||||
return bit[1].toUpperCase();
|
||||
});
|
||||
}
|
||||
'use strict';
|
||||
|
||||
if (typeof Proto.setAttribute !== 'undefined') {
|
||||
function toAttr(prop) {
|
||||
return prop.replace(/-[a-z]/g, function (bit) {
|
||||
return bit[1].toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
Proto.setProperty = function (prop, value) {
|
||||
var attr = toAttr(prop);
|
||||
if (!value) {
|
||||
@@ -966,4 +966,4 @@
|
||||
}());
|
||||
}
|
||||
|
||||
}(self));
|
||||
}(window));
|
||||
+2
-2
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
/* eslint-disable */
|
||||
;(function(window, document) {
|
||||
(function(window, document) {
|
||||
/*jshint evil:true */
|
||||
/** version */
|
||||
var version = '3.7.3';
|
||||
@@ -326,4 +326,4 @@
|
||||
module.exports = html5;
|
||||
}
|
||||
|
||||
}(typeof window !== "undefined" ? window : this, document));
|
||||
}(window, document));
|
||||
@@ -832,4 +832,4 @@
|
||||
this._start = node;
|
||||
};
|
||||
}());
|
||||
}(this.window || global));
|
||||
}(window));
|
||||
@@ -0,0 +1,4 @@
|
||||
// Inferno needs Int32Array, and it is not covered by core-js.
|
||||
if (!window.Int32Array) {
|
||||
window.Int32Array = Array;
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -123,10 +123,6 @@ Byond.call('', {
|
||||
<!--customheadhtml-->
|
||||
|
||||
<!-- Scripts -->
|
||||
<script type="text/javascript" defer src="shim-html5shiv.js"></script>
|
||||
<script type="text/javascript" defer src="shim-ie8.js"></script>
|
||||
<script type="text/javascript" defer src="shim-dom4.js"></script>
|
||||
<script type="text/javascript" defer src="shim-css-om.js"></script>
|
||||
<script type="text/javascript" defer src="tgui.bundle.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Window } from './layouts';
|
||||
|
||||
const requireInterface = require.context('./interfaces', false, /\.js$/);
|
||||
|
||||
const routingError = (type, name) => () => {
|
||||
return (
|
||||
<Window resizable>
|
||||
@@ -19,14 +21,13 @@ export const getRoutedComponent = state => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
// Show a kitchen sink
|
||||
if (state.showKitchenSink) {
|
||||
const { KitchenSink } = require('./interfaces/KitchenSink');
|
||||
return KitchenSink;
|
||||
return require('./interfaces/manually-routed/KitchenSink').KitchenSink;
|
||||
}
|
||||
}
|
||||
const name = state.config?.interface;
|
||||
let esModule;
|
||||
try {
|
||||
esModule = require(`./interfaces/${name}.js`);
|
||||
esModule = requireInterface(`./${name}.js`);
|
||||
}
|
||||
catch (err) {
|
||||
if (err.code === 'MODULE_NOT_FOUND') {
|
||||
|
||||
@@ -7,6 +7,14 @@
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.Flex--ie8 {
|
||||
display: table !important;
|
||||
}
|
||||
|
||||
.Flex__item--ie8 {
|
||||
display: table-cell !important;
|
||||
}
|
||||
|
||||
@for $i from 1 through 2 {
|
||||
.Flex--spacing--#{$i} {
|
||||
margin: 0 -3px * $i;
|
||||
|
||||
@@ -32,7 +32,8 @@ $border-radius: base.$border-radius !default;
|
||||
.Tabs__tab {
|
||||
// Force display block because Button theme overrides it via cascading.
|
||||
display: block !important;
|
||||
margin-right: 0;
|
||||
// Override to stop themed buttons from taking priority over it.
|
||||
margin-right: 0 !important;
|
||||
margin-bottom: 0;
|
||||
padding: 1px 9px 0px 6px;
|
||||
border-bottom: 2px solid rgba(255, 255, 255, 0.1);
|
||||
|
||||
@@ -3,19 +3,25 @@ const path = require('path');
|
||||
const BuildNotifierPlugin = require('webpack-build-notifier');
|
||||
const ExtractCssChunks = require('extract-css-chunks-webpack-plugin');
|
||||
|
||||
const createStats = verbose => ({
|
||||
assets: verbose,
|
||||
builtAt: verbose,
|
||||
cached: false,
|
||||
children: false,
|
||||
chunks: false,
|
||||
colors: true,
|
||||
hash: false,
|
||||
timings: verbose,
|
||||
version: verbose,
|
||||
modules: false,
|
||||
});
|
||||
|
||||
module.exports = (env = {}, argv) => {
|
||||
const config = {
|
||||
mode: argv.mode === 'production' ? 'production' : 'development',
|
||||
context: __dirname,
|
||||
entry: {
|
||||
tgui: [
|
||||
path.resolve(__dirname, './styles/main.scss'),
|
||||
path.resolve(__dirname, './styles/themes/cardtable.scss'),
|
||||
path.resolve(__dirname, './styles/themes/malfunction.scss'),
|
||||
path.resolve(__dirname, './styles/themes/ntos.scss'),
|
||||
path.resolve(__dirname, './styles/themes/hackerman.scss'),
|
||||
path.resolve(__dirname, './styles/themes/retro.scss'),
|
||||
path.resolve(__dirname, './styles/themes/syndicate.scss'),
|
||||
path.resolve(__dirname, './index.js'),
|
||||
],
|
||||
},
|
||||
@@ -97,6 +103,7 @@ module.exports = (env = {}, argv) => {
|
||||
hints: false,
|
||||
},
|
||||
devtool: false,
|
||||
stats: createStats(true),
|
||||
plugins: [
|
||||
new webpack.EnvironmentPlugin({
|
||||
NODE_ENV: env.NODE_ENV || argv.mode || 'development',
|
||||
@@ -129,11 +136,8 @@ module.exports = (env = {}, argv) => {
|
||||
extractComments: false,
|
||||
terserOptions: {
|
||||
ie8: true,
|
||||
// mangle: false,
|
||||
output: {
|
||||
ascii_only: true,
|
||||
// beautify: true,
|
||||
// indent_level: 2,
|
||||
},
|
||||
},
|
||||
}),
|
||||
@@ -168,24 +172,11 @@ module.exports = (env = {}, argv) => {
|
||||
}
|
||||
config.devtool = 'cheap-module-source-map';
|
||||
config.devServer = {
|
||||
// Informational flags
|
||||
progress: false,
|
||||
quiet: false,
|
||||
noInfo: false,
|
||||
// Fine-grained logging control
|
||||
clientLogLevel: 'silent',
|
||||
stats: {
|
||||
assets: false,
|
||||
builtAt: false,
|
||||
cached: false,
|
||||
children: false,
|
||||
chunks: false,
|
||||
colors: true,
|
||||
hash: false,
|
||||
timings: false,
|
||||
version: false,
|
||||
modules: false,
|
||||
},
|
||||
stats: createStats(false),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user