mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-28 15:47:15 +01:00
Converts the APC console from html to TGUI (#51973)
* Updates APC control panel to tgui * Finishes things up, adds emag behavior fully * Commented code bad * Removes tooltips * That shound just be name * Rebuild tgui * removes header and adds a class
This commit is contained in:
@@ -3,19 +3,21 @@
|
||||
desc = "Used to remotely control the flow of power to different parts of the station."
|
||||
icon_screen = "solar"
|
||||
icon_keyboard = "power_key"
|
||||
req_access = list(ACCESS_ENGINE)
|
||||
req_access = list(ACCESS_CE)
|
||||
circuit = /obj/item/circuitboard/computer/apc_control
|
||||
light_color = LIGHT_COLOR_YELLOW
|
||||
var/mob/living/operator //Who's operating the computer right now
|
||||
var/obj/machinery/power/apc/active_apc //The APC we're using right now
|
||||
var/list/result_filters //For sorting the results
|
||||
var/checking_logs = 0
|
||||
var/should_log = TRUE
|
||||
var/restoring = FALSE
|
||||
var/list/logs
|
||||
var/auth_id = "\[NULL\]"
|
||||
var/auth_id = "\[NULL\]:"
|
||||
ui_x = 550
|
||||
ui_y = 500
|
||||
|
||||
/obj/machinery/computer/apc_control/Initialize()
|
||||
/obj/machinery/computer/apc_control/Initialize(mapload, obj/item/circuitboard/C)
|
||||
. = ..()
|
||||
result_filters = list("Name" = null, "Charge Above" = null, "Charge Below" = null, "Responsive" = null)
|
||||
logs = list()
|
||||
|
||||
/obj/machinery/computer/apc_control/process()
|
||||
if(operator && (!operator.Adjacent(src) || machine_stat))
|
||||
@@ -34,170 +36,164 @@
|
||||
if(!IsAdminGhost(user))
|
||||
to_chat(user,"<span class='warning'>[src] does not support AI control.</span>") //You already have APC access, cheater!
|
||||
return
|
||||
..(user)
|
||||
..()
|
||||
|
||||
/obj/machinery/computer/apc_control/proc/check_apc(obj/machinery/power/apc/APC)
|
||||
return APC.z == z && !APC.malfhack && !APC.aidisabled && !(APC.obj_flags & EMAGGED) && !APC.machine_stat && !istype(APC.area, /area/ai_monitored) && !APC.area.outdoors
|
||||
|
||||
/obj/machinery/computer/apc_control/ui_interact(mob/living/user)
|
||||
. = ..()
|
||||
var/dat
|
||||
if(authenticated)
|
||||
if(!checking_logs)
|
||||
dat += "Logged in as [auth_id].<br><br>"
|
||||
dat += "<i>Filters</i><br>"
|
||||
dat += "<b>Name:</b> <a href='?src=[REF(src)];name_filter=1'>[result_filters["Name"] ? result_filters["Name"] : "None set"]</a><br>"
|
||||
dat += "<b>Charge:</b> <a href='?src=[REF(src)];above_filter=1'>\>[result_filters["Charge Above"] ? result_filters["Charge Above"] : "NaN"]%</a> and <a href='?src=[REF(src)];below_filter=1'>\<[result_filters["Charge Below"] ? result_filters["Charge Below"] : "NaN"]%</a><br>"
|
||||
dat += "<b>Accessible:</b> <a href='?src=[REF(src)];access_filter=1'>[result_filters["Responsive"] ? "Non-Responsive Only" : "All"]</a><br><br>"
|
||||
for(var/A in GLOB.apcs_list)
|
||||
if(check_apc(A))
|
||||
var/obj/machinery/power/apc/APC = A
|
||||
if(result_filters["Name"] && !findtext(APC.name, result_filters["Name"]) && !findtext(APC.area.name, result_filters["Name"]))
|
||||
continue
|
||||
if(result_filters["Charge Above"] && (!APC.cell || (APC.cell && (APC.cell.charge / APC.cell.maxcharge) < result_filters["Charge Above"] / 100)))
|
||||
continue
|
||||
if(result_filters["Charge Below"] && APC.cell && (APC.cell.charge / APC.cell.maxcharge) > result_filters["Charge Below"] / 100)
|
||||
continue
|
||||
if(result_filters["Responsive"] && !APC.aidisabled)
|
||||
continue
|
||||
dat += "<a href='?src=[REF(src)];access_apc=[REF(APC)]'>[A]</a><br>\
|
||||
<b>Charge:</b> [APC.cell ? "[DisplayEnergy(APC.cell.charge)] / [DisplayEnergy(APC.cell.maxcharge)] ([round((APC.cell.charge / APC.cell.maxcharge) * 100)]%)" : "No Power cell Installed"]<br>\
|
||||
<b>Area:</b> [APC.area]<br>\
|
||||
[APC.aidisabled || APC.panel_open ? "<font color='#FF0000'>APC does not respond to interface query.</font>" : "<font color='#00FF00'>APC responds to interface query.</font>"]<br><br>"
|
||||
dat += "<a href='?src=[REF(src)];check_logs=1'>Check Logs</a><br>"
|
||||
dat += "<a href='?src=[REF(src)];log_out=1'>Log Out</a><br>"
|
||||
if(obj_flags & EMAGGED)
|
||||
dat += "<font color='#FF0000'>WARNING: Logging functionality partially disabled from outside source.</font><br>"
|
||||
dat += "<a href='?src=[REF(src)];restore_logging=1'>Restore logging functionality?</a><br>"
|
||||
else
|
||||
if(logs.len)
|
||||
for(var/entry in logs)
|
||||
dat += "[entry]<br>"
|
||||
else
|
||||
dat += "<i>No activity has been recorded at this time.</i><br>"
|
||||
if(obj_flags & EMAGGED)
|
||||
dat += "<a href='?src=[REF(src)];clear_logs=1'><font color='#FF0000'>@#%! CLEAR LOGS</a>"
|
||||
dat += "<a href='?src=[REF(src)];check_apcs=1'>Return</a>"
|
||||
operator = user
|
||||
else
|
||||
dat = "<a href='?src=[REF(src)];authenticate=1'>Please swipe a valid ID to log in...</a>"
|
||||
var/datum/browser/popup = new(user, "apc_control", name, 600, 400)
|
||||
popup.set_content(dat)
|
||||
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
|
||||
popup.open()
|
||||
/obj/machinery/computer/apc_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) // Remember to use the appropriate state.
|
||||
operator = user
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "ApcControl", "APC Controller", ui_x, ui_y, master_ui, state)
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/computer/apc_control/Topic(href, href_list)
|
||||
|
||||
/obj/machinery/computer/apc_control/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["auth_id"] = auth_id
|
||||
data["authenticated"] = authenticated
|
||||
data["emagged"] = obj_flags & EMAGGED
|
||||
data["logging"] = should_log
|
||||
data["restoring"] = restoring
|
||||
data["logs"] = list()
|
||||
data["apcs"] = list()
|
||||
|
||||
for(var/entry in logs)
|
||||
data["logs"] += list(list("entry" = entry))
|
||||
|
||||
for(var/apc in GLOB.apcs_list)
|
||||
if(check_apc(apc))
|
||||
var/obj/machinery/power/apc/A = apc
|
||||
var/has_cell = (A.cell) ? TRUE : FALSE
|
||||
data["apcs"] += list(list(
|
||||
"name" = A.area.name,
|
||||
"operating" = A.operating,
|
||||
"charge" = (has_cell) ? A.cell.percent() : "NOCELL",
|
||||
"load" = DisplayPower(A.lastused_total),
|
||||
"charging" = A.charging,
|
||||
"chargeMode" = A.chargemode,
|
||||
"eqp" = A.equipment,
|
||||
"lgt" = A.lighting,
|
||||
"env" = A.environ,
|
||||
"responds" = A.aidisabled || A.panel_open,
|
||||
"ref" = REF(A)
|
||||
)
|
||||
)
|
||||
return data
|
||||
|
||||
/obj/machinery/computer/apc_control/ui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
if(!usr || !usr.canUseTopic(src, !issilicon(usr)) || machine_stat || QDELETED(src))
|
||||
return
|
||||
if(href_list["authenticate"])
|
||||
var/obj/item/card/id/ID = usr.get_idcard(TRUE)
|
||||
if(ID && istype(ID))
|
||||
if(check_access(ID))
|
||||
switch(action)
|
||||
if("log-in")
|
||||
if(obj_flags & EMAGGED)
|
||||
authenticated = TRUE
|
||||
auth_id = "[ID.registered_name] ([ID.assignment])"
|
||||
log_activity("logged in")
|
||||
playsound(src, 'sound/machines/terminal_on.ogg', 50, FALSE)
|
||||
if(href_list["log_out"])
|
||||
log_activity("logged out")
|
||||
playsound(src, 'sound/machines/terminal_off.ogg', 50, FALSE)
|
||||
authenticated = FALSE
|
||||
auth_id = "\[NULL\]"
|
||||
if(href_list["restore_logging"])
|
||||
to_chat(usr, "<span class='robot notice'>[icon2html(src, usr)] Logging functionality restored from backup data.</span>")
|
||||
obj_flags &= ~EMAGGED
|
||||
LAZYADD(logs, "<b>-=- Logging restored to full functionality at this point -=-</b>")
|
||||
if(href_list["access_apc"])
|
||||
playsound(src, "terminal_type", 50, FALSE)
|
||||
var/obj/machinery/power/apc/APC = locate(href_list["access_apc"]) in GLOB.apcs_list
|
||||
if(!APC || APC.aidisabled || APC.panel_open || QDELETED(APC))
|
||||
to_chat(usr, "<span class='robot danger'>[icon2html(src, usr)] APC does not return interface request. Remote access may be disabled.</span>")
|
||||
return
|
||||
if(active_apc)
|
||||
to_chat(usr, "<span class='robot danger'>[icon2html(src, usr)] Disconnected from [active_apc].</span>")
|
||||
active_apc.say("Remote access canceled. Interface locked.")
|
||||
playsound(active_apc, 'sound/machines/boltsdown.ogg', 25, FALSE)
|
||||
playsound(active_apc, 'sound/machines/terminal_alert.ogg', 50, FALSE)
|
||||
active_apc.locked = TRUE
|
||||
active_apc.update_icon()
|
||||
active_apc.remote_control = null
|
||||
active_apc = null
|
||||
to_chat(usr, "<span class='robot notice'>[icon2html(src, usr)] Connected to APC in [get_area_name(APC.area, TRUE)]. Interface request sent.</span>")
|
||||
log_activity("remotely accessed APC in [get_area_name(APC.area, TRUE)]")
|
||||
APC.remote_control = src
|
||||
APC.ui_interact(usr)
|
||||
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
|
||||
message_admins("[ADMIN_LOOKUPFLW(usr)] remotely accessed [APC] from [src] at [AREACOORD(src)].")
|
||||
log_game("[key_name(usr)] remotely accessed [APC] from [src] at [AREACOORD(src)].")
|
||||
if(APC.locked)
|
||||
APC.say("Remote access detected. Interface unlocked.")
|
||||
playsound(APC, 'sound/machines/boltsup.ogg', 25, FALSE)
|
||||
playsound(APC, 'sound/machines/terminal_alert.ogg', 50, FALSE)
|
||||
APC.locked = FALSE
|
||||
APC.update_icon()
|
||||
active_apc = APC
|
||||
if(href_list["name_filter"])
|
||||
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE)
|
||||
var/new_filter = stripped_input(usr, "What name are you looking for?", name)
|
||||
if(!src || !usr || !usr.canUseTopic(src, !issilicon(usr)) || machine_stat || QDELETED(src))
|
||||
return
|
||||
log_activity("changed name filter to \"[new_filter]\"")
|
||||
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
|
||||
result_filters["Name"] = new_filter
|
||||
if(href_list["above_filter"])
|
||||
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE)
|
||||
var/new_filter = input(usr, "Enter a percentage from 1-100 to sort by (greater than).", name) as null|num
|
||||
if(!src || !usr || !usr.canUseTopic(src, !issilicon(usr)) || machine_stat || QDELETED(src))
|
||||
return
|
||||
log_activity("changed greater than charge filter to \"[new_filter]\"")
|
||||
if(new_filter)
|
||||
new_filter = clamp(new_filter, 0, 100)
|
||||
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
|
||||
result_filters["Charge Above"] = new_filter
|
||||
if(href_list["below_filter"])
|
||||
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE)
|
||||
var/new_filter = input(usr, "Enter a percentage from 1-100 to sort by (lesser than).", name) as null|num
|
||||
if(!src || !usr || !usr.canUseTopic(src, !issilicon(usr)) || machine_stat || QDELETED(src))
|
||||
return
|
||||
log_activity("changed lesser than charge filter to \"[new_filter]\"")
|
||||
if(new_filter)
|
||||
new_filter = clamp(new_filter, 0, 100)
|
||||
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
|
||||
result_filters["Charge Below"] = new_filter
|
||||
if(href_list["access_filter"])
|
||||
if(isnull(result_filters["Responsive"]))
|
||||
result_filters["Responsive"] = 1
|
||||
log_activity("sorted by non-responsive APCs only")
|
||||
else
|
||||
result_filters["Responsive"] = !result_filters["Responsive"]
|
||||
log_activity("sorted by all APCs")
|
||||
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
|
||||
if(href_list["check_logs"])
|
||||
checking_logs = TRUE
|
||||
log_activity("checked logs")
|
||||
if(href_list["check_apcs"])
|
||||
checking_logs = FALSE
|
||||
log_activity("checked APCs")
|
||||
if(href_list["clear_logs"])
|
||||
logs = list()
|
||||
ui_interact(usr) //Refresh the UI after a filter changes
|
||||
auth_id = "Unknown (Unknown):"
|
||||
log_activity("[auth_id] logged in to the terminal")
|
||||
return
|
||||
var/obj/item/card/id/ID = operator.get_idcard(TRUE)
|
||||
if(ID && istype(ID))
|
||||
if(check_access(ID))
|
||||
authenticated = TRUE
|
||||
auth_id = "[ID.registered_name] ([ID.assignment]):"
|
||||
log_activity("[auth_id] logged in to the terminal")
|
||||
playsound(src, 'sound/machines/terminal_on.ogg', 50, FALSE)
|
||||
else
|
||||
auth_id = "[ID.registered_name] ([ID.assignment]):"
|
||||
log_activity("[auth_id] attempted to log into the terminal")
|
||||
return
|
||||
auth_id = "Unknown (Unknown):"
|
||||
log_activity("[auth_id] attempted to log into the terminal")
|
||||
if("log-out")
|
||||
log_activity("[auth_id] logged out of the terminal")
|
||||
playsound(src, 'sound/machines/terminal_off.ogg', 50, FALSE)
|
||||
authenticated = FALSE
|
||||
auth_id = "\[NULL\]"
|
||||
if("toggle-logs")
|
||||
should_log = !should_log
|
||||
log_game("[key_name(operator)] set the logs of [src] in [AREACOORD(src)] [should_log ? "On" : "Off"]")
|
||||
if("restore-console")
|
||||
restoring = TRUE
|
||||
addtimer(CALLBACK(src, .proc/restore_comp), rand(3,5) * 9)
|
||||
if("access-apc")
|
||||
var/ref = params["ref"]
|
||||
playsound(src, "terminal_type", 50, FALSE)
|
||||
var/obj/machinery/power/apc/APC = locate(ref) in GLOB.apcs_list
|
||||
if(!APC)
|
||||
return
|
||||
if(active_apc)
|
||||
to_chat(operator, "<span class='robot danger'>[icon2html(src, auth_id)] Disconnected from [active_apc].</span>")
|
||||
active_apc.say("Remote access canceled. Interface locked.")
|
||||
playsound(active_apc, 'sound/machines/boltsdown.ogg', 25, FALSE)
|
||||
playsound(active_apc, 'sound/machines/terminal_alert.ogg', 50, FALSE)
|
||||
active_apc.locked = TRUE
|
||||
active_apc.update_icon()
|
||||
active_apc.remote_control = null
|
||||
active_apc = null
|
||||
APC.remote_control = src
|
||||
APC.ui_interact(operator)
|
||||
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
|
||||
log_game("[key_name(operator)] remotely accessed [APC] from [src] at [AREACOORD(src)].")
|
||||
log_activity("[auth_id] remotely accessed APC in [get_area_name(APC.area, TRUE)]")
|
||||
if(APC.locked)
|
||||
APC.say("Remote access detected. Interface unlocked.")
|
||||
playsound(APC, 'sound/machines/boltsup.ogg', 25, FALSE)
|
||||
playsound(APC, 'sound/machines/terminal_alert.ogg', 50, FALSE)
|
||||
APC.locked = FALSE
|
||||
APC.update_icon()
|
||||
active_apc = APC
|
||||
if("check-logs")
|
||||
log_activity("Checked Logs")
|
||||
if("check-apcs")
|
||||
log_activity("Checked APCs")
|
||||
if("toggle-minor")
|
||||
var/ref = params["ref"]
|
||||
var/type = params["type"]
|
||||
var/value = params["value"]
|
||||
var/obj/machinery/power/apc/target = locate(ref) in GLOB.apcs_list
|
||||
if(!target)
|
||||
return
|
||||
target.vars[type] = target.setsubsystem(text2num(value))
|
||||
target.update_icon()
|
||||
target.update()
|
||||
var/setTo = ""
|
||||
switch(target.vars[type])
|
||||
if(0)
|
||||
setTo = "Off"
|
||||
if(1)
|
||||
setTo = "Auto Off"
|
||||
if(2)
|
||||
setTo = "On"
|
||||
if(3)
|
||||
setTo = "Auto On"
|
||||
log_activity("Set APC [target.area.name] [type] to [setTo]")
|
||||
log_game("[key_name(operator)] Set APC [target.area.name] [type] to [setTo]]")
|
||||
if("breaker")
|
||||
var/ref = params["ref"]
|
||||
var/obj/machinery/power/apc/target = locate(ref) in GLOB.apcs_list
|
||||
target.toggle_breaker()
|
||||
var/setTo = target.operating ? "On" : "Off"
|
||||
log_activity("Turned APC [target.area.name]'s breaker [setTo]")
|
||||
|
||||
/obj/machinery/computer/apc_control/emag_act(mob/user)
|
||||
if(!authenticated)
|
||||
to_chat(user, "<span class='warning'>You bypass [src]'s access requirements using your emag.</span>")
|
||||
authenticated = TRUE
|
||||
log_activity("logged in")
|
||||
else if(!(obj_flags & EMAGGED))
|
||||
if(user)
|
||||
user.visible_message("<span class='warning'>You emag [src], disabling precise logging and allowing you to clear logs.</span>")
|
||||
log_game("[key_name(user)] emagged [src] at [AREACOORD(src)], disabling operator tracking.")
|
||||
obj_flags |= EMAGGED
|
||||
if(obj_flags & EMAGGED)
|
||||
return
|
||||
obj_flags |= EMAGGED
|
||||
log_game("[key_name(user)] emagged [src] at [AREACOORD(src)]")
|
||||
playsound(src, "sparks", 50, TRUE)
|
||||
|
||||
/obj/machinery/computer/apc_control/proc/log_activity(log_text)
|
||||
var/op_string = operator && !(obj_flags & EMAGGED) ? operator : "\[NULL OPERATOR\]"
|
||||
LAZYADD(logs, "<b>([station_time_timestamp()])</b> [op_string] [log_text]")
|
||||
if(!should_log)
|
||||
return
|
||||
LAZYADD(logs, "([station_time_timestamp()]): [auth_id] [log_text]")
|
||||
|
||||
/obj/machinery/computer/apc_control/proc/restore_comp()
|
||||
obj_flags &= ~EMAGGED
|
||||
should_log = TRUE
|
||||
log_game("[key_name(operator)] restored the logs of [src] in [AREACOORD(src)]")
|
||||
log_activity("-=- Logging restored to full functionality at this point -=-")
|
||||
restoring = FALSE
|
||||
|
||||
/mob/proc/using_power_flow_console()
|
||||
for(var/obj/machinery/computer/apc_control/A in range(1, src))
|
||||
|
||||
@@ -0,0 +1,300 @@
|
||||
import { map, sortBy } from 'common/collections';
|
||||
import { flow } from 'common/fp';
|
||||
import { pureComponentHooks } from 'common/react';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Box, Button, Dimmer, Flex, Icon, Table, Tabs } from '../components';
|
||||
import { Fragment, Window } from '../layouts';
|
||||
import { AreaCharge, powerRank } from './PowerMonitor';
|
||||
|
||||
export const ApcControl = (props, context) => {
|
||||
const { data } = useBackend(context);
|
||||
return (
|
||||
<Window resizable>
|
||||
{data.authenticated === 1
|
||||
&& <ApcLoggedIn />}
|
||||
{data.authenticated === 0
|
||||
&& <ApcLoggedOut />}
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const ApcLoggedOut = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { emagged } = data;
|
||||
const text = emagged === 1 ? 'Open' : 'Log In';
|
||||
return (
|
||||
<Window.Content>
|
||||
<Button
|
||||
fluid
|
||||
color={emagged === 1 ? '' : 'good'}
|
||||
content={text}
|
||||
onClick={() => act('log-in')} />
|
||||
</Window.Content>
|
||||
);
|
||||
};
|
||||
|
||||
const ApcLoggedIn = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { restoring } = data;
|
||||
const [
|
||||
tabIndex,
|
||||
setTabIndex,
|
||||
] = useLocalState(context, 'tab-index', 1);
|
||||
return (
|
||||
<Fragment>
|
||||
<Tabs>
|
||||
<Tabs.Tab
|
||||
selected={tabIndex === 1}
|
||||
onClick={() => {
|
||||
setTabIndex(1);
|
||||
act('check-apcs');
|
||||
}}>
|
||||
APC Control Panel
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
selected={tabIndex === 2}
|
||||
onClick={() => {
|
||||
setTabIndex(2);
|
||||
act('check-logs');
|
||||
}}>
|
||||
Log View Panel
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
{restoring === 1 && (
|
||||
<Dimmer fontSize="32px">
|
||||
<Icon name="cog" spin />
|
||||
{' Resetting...'}
|
||||
</Dimmer>
|
||||
)}
|
||||
{tabIndex === 1 && (
|
||||
<Fragment>
|
||||
<ControlPanel />
|
||||
<Box fillPositionedParent top="53px">
|
||||
<Window.Content scrollable>
|
||||
<ApcControlScene />
|
||||
</Window.Content>
|
||||
</Box>
|
||||
</Fragment>
|
||||
)}
|
||||
{tabIndex === 2 && (
|
||||
<Box fillPositionedParent top="20px">
|
||||
<Window.Content scrollable>
|
||||
<LogPanel />
|
||||
</Window.Content>
|
||||
</Box>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const ControlPanel = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
emagged,
|
||||
logging,
|
||||
} = data;
|
||||
const [
|
||||
sortByField,
|
||||
setSortByField,
|
||||
] = useLocalState(context, 'sortByField', null);
|
||||
return (
|
||||
<Flex>
|
||||
<Flex.Item>
|
||||
<Box inline mr={2} color="label">
|
||||
Sort by:
|
||||
</Box>
|
||||
<Button.Checkbox
|
||||
checked={sortByField === 'name'}
|
||||
content="Name"
|
||||
onClick={() => setSortByField(sortByField !== 'name' && 'name')} />
|
||||
<Button.Checkbox
|
||||
checked={sortByField === 'charge'}
|
||||
content="Charge"
|
||||
onClick={() => setSortByField(
|
||||
sortByField !== 'charge' && 'charge'
|
||||
)} />
|
||||
<Button.Checkbox
|
||||
checked={sortByField === 'draw'}
|
||||
content="Draw"
|
||||
onClick={() => setSortByField(sortByField !== 'draw' && 'draw')} />
|
||||
</Flex.Item>
|
||||
<Flex.Item grow={1} />
|
||||
<Flex.Item>
|
||||
{emagged === 1 && (
|
||||
<Fragment>
|
||||
<Button
|
||||
color={logging === 1 ? 'bad' : 'good'}
|
||||
content={logging === 1 ? 'Stop Logging' : 'Restore Logging'}
|
||||
onClick={() => act('toggle-logs')}
|
||||
/>
|
||||
<Button
|
||||
content="Reset Console"
|
||||
onClick={() => act('restore-console')}
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
<Button
|
||||
color="bad"
|
||||
content="Log Out"
|
||||
onClick={() => act('log-out')}
|
||||
/>
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const ApcControlScene = (props, context) => {
|
||||
const { data, act } = useBackend(context);
|
||||
|
||||
const [
|
||||
sortByField,
|
||||
] = useLocalState(context, 'sortByField', null);
|
||||
|
||||
const apcs = flow([
|
||||
map((apc, i) => ({
|
||||
...apc,
|
||||
// Generate a unique id
|
||||
id: apc.name + i,
|
||||
})),
|
||||
sortByField === 'name' && sortBy(apc => apc.name),
|
||||
sortByField === 'charge' && sortBy(apc => -apc.charge),
|
||||
sortByField === 'draw' && sortBy(
|
||||
apc => -powerRank(apc.load),
|
||||
apc => -parseFloat(apc.load)),
|
||||
])(data.apcs);
|
||||
return (
|
||||
<Table>
|
||||
<Table.Row header>
|
||||
<Table.Cell>
|
||||
On/Off
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
Area
|
||||
</Table.Cell>
|
||||
<Table.Cell collapsing>
|
||||
Charge
|
||||
</Table.Cell>
|
||||
<Table.Cell collapsing textAlign="right">
|
||||
Draw
|
||||
</Table.Cell>
|
||||
<Table.Cell collapsing title="Equipment">
|
||||
Eqp
|
||||
</Table.Cell>
|
||||
<Table.Cell collapsing title="Lighting">
|
||||
Lgt
|
||||
</Table.Cell>
|
||||
<Table.Cell collapsing title="Environment">
|
||||
Env
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{apcs.map((apc, i) => (
|
||||
<tr
|
||||
key={apc.id}
|
||||
className="Table__row candystripe">
|
||||
<td>
|
||||
<Button
|
||||
icon={apc.operating ? 'power-off' : 'times'}
|
||||
color={apc.operating ? 'good' : 'bad'}
|
||||
onClick={() => act('breaker', {
|
||||
ref: apc.ref,
|
||||
})}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<Button
|
||||
onClick={() => act('access-apc', {
|
||||
ref: apc.ref,
|
||||
})}>
|
||||
{apc.name}
|
||||
</Button>
|
||||
</td>
|
||||
<td className="Table__cell text-right text-nowrap">
|
||||
<AreaCharge
|
||||
charging={apc.charging}
|
||||
charge={apc.charge}
|
||||
/>
|
||||
</td>
|
||||
<td className="Table__cell text-right text-nowrap">
|
||||
{apc.load}
|
||||
</td>
|
||||
<td className="Table__cell text-center text-nowrap">
|
||||
<AreaStatusColorButton
|
||||
target="equipment"
|
||||
status={apc.eqp}
|
||||
apc={apc}
|
||||
act={act}
|
||||
/>
|
||||
</td>
|
||||
<td className="Table__cell text-center text-nowrap">
|
||||
<AreaStatusColorButton
|
||||
target="lighting"
|
||||
status={apc.lgt}
|
||||
apc={apc}
|
||||
act={act}
|
||||
/>
|
||||
</td>
|
||||
<td className="Table__cell text-center text-nowrap">
|
||||
<AreaStatusColorButton
|
||||
target="environ"
|
||||
status={apc.env}
|
||||
apc={apc}
|
||||
act={act}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</Table>
|
||||
);
|
||||
};
|
||||
|
||||
const LogPanel = (props, context) => {
|
||||
const { data } = useBackend(context);
|
||||
|
||||
const logs = flow([
|
||||
map((line, i) => ({
|
||||
...line,
|
||||
// Generate a unique id
|
||||
id: line.entry + i,
|
||||
})),
|
||||
logs => logs.reverse(),
|
||||
])(data.logs);
|
||||
return (
|
||||
<Box m={-0.5}>
|
||||
{logs.map(line => (
|
||||
<Box
|
||||
p={0.5}
|
||||
key={line.id}
|
||||
className="candystripe"
|
||||
bold>
|
||||
{line.entry}
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const AreaStatusColorButton = props => {
|
||||
const { target, status, apc, act } = props;
|
||||
const power = Boolean(status & 2);
|
||||
const mode = Boolean(status & 1);
|
||||
return (
|
||||
<Button
|
||||
icon={mode ? 'sync' : 'power-off'}
|
||||
color={power ? 'good' : 'bad'}
|
||||
onClick={() => act('toggle-minor', {
|
||||
type: target,
|
||||
value: statusChange(status),
|
||||
ref: apc.ref,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const statusChange = status => {
|
||||
// mode flip power flip both flip
|
||||
// 0, 2, 3
|
||||
return status === 0 ? 2 : status === 2 ? 3 : 0;
|
||||
};
|
||||
|
||||
AreaStatusColorButton.defaultHooks = pureComponentHooks;
|
||||
|
||||
@@ -6,10 +6,9 @@ import { Component, Fragment } from 'inferno';
|
||||
import { Box, Button, Chart, ColorBox, Flex, Icon, LabeledList, ProgressBar, Section, Table } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
|
||||
const PEAK_DRAW = 500000;
|
||||
|
||||
const powerRank = str => {
|
||||
export const powerRank = str => {
|
||||
const unit = String(str.split(' ')[1]).toLowerCase();
|
||||
return ['w', 'kw', 'mw', 'gw'].indexOf(unit);
|
||||
};
|
||||
@@ -172,7 +171,7 @@ export const PowerMonitorContent = (props, context) => {
|
||||
);
|
||||
};
|
||||
|
||||
const AreaCharge = props => {
|
||||
export const AreaCharge = props => {
|
||||
const { charging, charge } = props;
|
||||
return (
|
||||
<Fragment>
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user