diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm
index 0e98179e2f9..f44edc6029c 100644
--- a/code/datums/datacore.dm
+++ b/code/datums/datacore.dm
@@ -281,8 +281,10 @@ GLOBAL_VAR_INIT(record_id_num, 1001)
G.fields["photo-south"] = "'data:image/png;base64,[icon2base64(icon(G.fields["photo"], dir = SOUTH))]'"
G.fields["photo-west"] = "'data:image/png;base64,[icon2base64(icon(G.fields["photo"], dir = WEST))]'"
if(H.gen_record && !jobban_isbanned(H, "Records"))
+ G.fields["record"] = H.gen_record
G.fields["notes"] = H.gen_record
else
+ G.fields["record"] = "No notes found."
G.fields["notes"] = "No notes found."
general += G
@@ -301,8 +303,10 @@ GLOBAL_VAR_INIT(record_id_num, 1001)
M.fields["cdi"] = "None"
M.fields["cdi_d"] = "No diseases have been diagnosed at the moment."
if(H.med_record && !jobban_isbanned(H, "Records"))
+ M.fields["record"] = H.med_record
M.fields["notes"] = H.med_record
else
+ M.fields["record"] = "No notes found."
M.fields["notes"] = "No notes found."
medical += M
@@ -317,9 +321,11 @@ GLOBAL_VAR_INIT(record_id_num, 1001)
S.fields["ma_crim_d"] = "No major crime convictions."
S.fields["notes"] = "No notes."
if(H.sec_record && !jobban_isbanned(H, "Records"))
+ S.fields["record"] = H.sec_record
S.fields["notes"] = H.sec_record
else
- S.fields["notes"] = "No notes."
+ S.fields["record"] = "No notes found."
+ S.fields["notes"] = "No notes found."
LAZYINITLIST(S.fields["comments"])
security += S
diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm
index 8c815198d31..6cb11f8ffb2 100644
--- a/code/game/machinery/computer/security.dm
+++ b/code/game/machinery/computer/security.dm
@@ -2,43 +2,74 @@
#define SEC_DATA_MAINT 2 // Records maintenance
#define SEC_DATA_RECORD 3 // Record
-/obj/machinery/computer/secure_data//TODO:SANITY
+#define FIELD(N, V, E) list(field = N, value = V, edit = E)
+#define SEC_FIELD(N, V, E, LB) list(field = N, value = V, edit = E, line_break = LB)
+
+/obj/machinery/computer/secure_data
name = "security records"
desc = "Used to view and edit personnel's security records."
icon_keyboard = "security_key"
icon_screen = "security"
- req_one_access = list(ACCESS_SECURITY, ACCESS_FORENSICS_LOCKERS)
circuit = /obj/item/circuitboard/secure_data
- var/obj/item/card/id/scan = null
- var/authenticated = null
- var/rank = null
- var/list/authcard_access = list()
- var/screen = null
- var/datum/data/record/active1 = null
- var/datum/data/record/active2 = null
- var/temp = null
- var/printing = null
- //Sorting Variables
- var/sortBy = "name"
- var/order = 1 // -1 = Descending - 1 = Ascending
+ /// The inserted ID card's access list.
+ var/list/inserted_id_access = null
+ /// The current page being viewed.
+ var/current_page = SEC_DATA_R_LIST
+ /// The current general record being viewed.
+ var/datum/data/record/record_general = null
+ /// The current security record being viewed.
+ var/datum/data/record/record_security = null
+ /// Whether the computer is currently printing a paper or not.
+ var/is_printing = FALSE
+ /// The editable fields and their associated question to display to the user.
+ var/static/list/field_edit_questions
+ /// The editable fields and their associated choices to display to the user.
+ var/static/list/field_edit_choices
+ /// The current temporary notice.
+ var/temp_notice
light_color = LIGHT_COLOR_RED
+/obj/machinery/computer/secure_data/Initialize(mapload)
+ . = ..()
+ req_one_access = list(ACCESS_SECURITY, ACCESS_FORENSICS_LOCKERS)
+ field_edit_questions = list(
+ // General
+ "name" = "Please input new name:",
+ "id" = "Please input new ID:",
+ "sex" = "Please select new sex:",
+ "age" = "Please input new age:",
+ "rank" = "Please input new assignment:",
+ "fingerprint" = "Please input new fingerprint hash:",
+ "p_stat" = "Please select new physical status:",
+ "m_stat" = "Please select new mental status:",
+ // Security
+ "criminal" = "Please select new criminal status:",
+ "mi_crim" = "Please input new minor crimes:",
+ "mi_crim_d" = "Please summarize minor crimes:",
+ "ma_crim" = "Please input new major crimes:",
+ "ma_crim_d" = "Please summarize major crimes:",
+ "notes" = "Please input new important notes:",
+ )
+ field_edit_choices = list(
+ // General
+ "sex" = list("Male", "Female"),
+ "p_stat" = list("*Deceased*", "*SSD*", "Active", "Physically Unfit", "Disabled"),
+ "m_stat" = list("*Insane*", "*Unstable*", "*Watch*", "Stable"),
+ // Security
+ "criminal" = list(SEC_RECORD_STATUS_NONE, SEC_RECORD_STATUS_ARREST, SEC_RECORD_STATUS_EXECUTE, SEC_RECORD_STATUS_INCARCERATED, SEC_RECORD_STATUS_RELEASED, SEC_RECORD_STATUS_PAROLLED, SEC_RECORD_STATUS_DEMOTE, SEC_RECORD_STATUS_SEARCH, SEC_RECORD_STATUS_MONITOR),
+ )
+
/obj/machinery/computer/secure_data/Destroy()
- active1 = null
- active2 = null
+ record_general = null
+ record_security = null
return ..()
/obj/machinery/computer/secure_data/attackby(obj/item/O, mob/user, params)
- if(istype(O, /obj/item/card/id) && !scan)
- user.drop_item()
- O.forceMove(src)
- scan = O
- ui_interact(user)
+ if(tgui_login_attackby(O, user))
return
return ..()
-//Someone needs to break down the dat += into chunks instead of long ass lines.
/obj/machinery/computer/secure_data/attack_hand(mob/user)
if(..())
return
@@ -46,267 +77,117 @@
to_chat(user, "Unable to establish a connection : You're too far away from the station!")
return
add_fingerprint(user)
- ui_interact(user)
+ tgui_interact(user)
-/obj/machinery/computer/secure_data/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+/obj/machinery/computer/secure_data/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "secure_data.tmpl", name, 800, 800)
+ ui = new(user, src, ui_key, "SecurityRecords", name, 800, 800)
ui.open()
+ ui.set_autoupdate(FALSE)
-/obj/machinery/computer/secure_data/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
+/obj/machinery/computer/secure_data/tgui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
var/data[0]
- data["temp"] = temp
- data["scan"] = scan ? scan.name : null
- data["authenticated"] = authenticated
- data["screen"] = screen
- if(authenticated)
- switch(screen)
+ data["currentPage"] = current_page
+ data["isPrinting"] = is_printing
+ tgui_login_data(data, user)
+ data["modal"] = tgui_modal_data()
+ data["temp"] = temp_notice
+ if(data["loginState"]["logged_in"])
+ switch(current_page)
if(SEC_DATA_R_LIST)
- if(!isnull(GLOB.data_core.general))
- for(var/datum/data/record/R in sortRecord(GLOB.data_core.general, sortBy, order))
- var/crimstat = "null"
- for(var/datum/data/record/E in GLOB.data_core.security)
- if(E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"])
- crimstat = E.fields["criminal"]
- break
- var/background = "''"
- switch(crimstat)
- if(SEC_RECORD_STATUS_EXECUTE)
- background = "'background-color:#5E0A1A'"
- if(SEC_RECORD_STATUS_ARREST)
- background = "'background-color:#890E26'"
- if(SEC_RECORD_STATUS_SEARCH)
- background = "'background-color:#999900'"
- if(SEC_RECORD_STATUS_MONITOR)
- background = "'background-color:#004C99'"
- if(SEC_RECORD_STATUS_DEMOTE)
- background = "'background-color:#C2A111'"
- if(SEC_RECORD_STATUS_INCARCERATED)
- background = "'background-color:#743B03'"
- if(SEC_RECORD_STATUS_PAROLLED)
- background = "'background-color:#743B03'"
- if(SEC_RECORD_STATUS_RELEASED)
- background = "'background-color:#216489'"
- if(SEC_RECORD_STATUS_NONE)
- background = "'background-color:#007f47'"
- if("null")
- crimstat = "No record."
- data["records"] += list(list("ref" = "\ref[R]", "id" = R.fields["id"], "name" = R.fields["name"], "rank" = R.fields["rank"], "fingerprint" = R.fields["fingerprint"], "background" = background, "crimstat" = crimstat))
+ // Prepare the list of security records to associate with the general ones.
+ // This is not ideal but datacore code sucks and needs to be rewritten.
+ var/list/sec_records_assoc[0]
+ for(var/datum/data/record/S in (GLOB.data_core.security || list()))
+ sec_records_assoc["[S.fields["name"]]|[S.fields["id"]]"] = S
+ // List the general records
+ var/list/records = list()
+ data["records"] = records
+ for(var/datum/data/record/G in (GLOB.data_core.general || list()))
+ var/datum/data/record/S = sec_records_assoc["[G.fields["name"]]|[G.fields["id"]]"]
+ var/list/record_line = list("uid_gen" = G.UID(), "id" = G.fields["id"], "name" = G.fields["name"], "rank" = G.fields["rank"], "fingerprint" = G.fields["fingerprint"])
+ record_line["status"] = S?.fields["criminal"] || "No record"
+ record_line["uid_sec"] = S && S.UID() // So we don't have to perform the search through a for loop again later
+ records[++records.len] = record_line
if(SEC_DATA_RECORD)
var/list/general = list()
data["general"] = general
- if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1))
- var/list/fields = list()
- general["fields"] = fields
- fields[++fields.len] = list("field" = "Name:", "value" = active1.fields["name"], "edit" = "name")
- fields[++fields.len] = list("field" = "ID:", "value" = active1.fields["id"], "edit" = "id")
- fields[++fields.len] = list("field" = "Sex:", "value" = active1.fields["sex"], "edit" = "sex")
- fields[++fields.len] = list("field" = "Age:", "value" = active1.fields["age"], "edit" = "age")
- fields[++fields.len] = list("field" = "Rank:", "value" = active1.fields["rank"], "edit" = "rank")
- fields[++fields.len] = list("field" = "Fingerprint:", "value" = active1.fields["fingerprint"], "edit" = "fingerprint")
- fields[++fields.len] = list("field" = "Physical Status:", "value" = active1.fields["p_stat"], "edit" = null)
- fields[++fields.len] = list("field" = "Mental Status:", "value" = active1.fields["m_stat"], "edit" = null)
- var/list/photos = list()
- general["photos"] = photos
- photos[++photos.len] = list("photo" = active1.fields["photo-south"])
- photos[++photos.len] = list("photo" = active1.fields["photo-west"])
- general["has_photos"] += (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0)
- general["empty"] = 0
+ if(record_general && GLOB.data_core.general.Find(record_general))
+ var/list/gen_fields = record_general.fields
+ general["fields"] = list(
+ FIELD("Name", gen_fields["name"], "name"),
+ FIELD("ID", gen_fields["id"], "id"),
+ FIELD("Sex", gen_fields["sex"], "sex"),
+ FIELD("Age", gen_fields["age"], "age"),
+ FIELD("Assignment", gen_fields["rank"], "rank"),
+ FIELD("Fingerprint", gen_fields["fingerprint"], "fingerprint"),
+ FIELD("Physical Status", gen_fields["p_stat"], "p_stat"),
+ FIELD("Mental Status", gen_fields["m_stat"], "m_stat"),
+ FIELD("Important Notes", gen_fields["record"], null),
+ )
+ general["photos"] = list(
+ gen_fields["photo-south"],
+ gen_fields["photo-west"],
+ )
+ general["has_photos"] = (gen_fields["photo-south"] || gen_fields["photo-west"]) ? TRUE : FALSE
+ general["empty"] = FALSE
else
- general["empty"] = 1
+ general["empty"] = TRUE
var/list/security = list()
data["security"] = security
- if(istype(active2, /datum/data/record) && GLOB.data_core.security.Find(active2))
- var/list/fields = list()
- security["fields"] = fields
- fields[++fields.len] = list("field" = "Criminal Status:", "value" = active2.fields["criminal"], "edit" = "criminal", "line_break" = 1)
- fields[++fields.len] = list("field" = "Minor Crimes:", "value" = active2.fields["mi_crim"], "edit" = "mi_crim", "line_break" = 0)
- fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["mi_crim_d"], "edit" = "mi_crim_d", "line_break" = 1)
- fields[++fields.len] = list("field" = "Major Crimes:", "value" = active2.fields["ma_crim"], "edit" = "ma_crim", "line_break" = 0)
- fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["ma_crim_d"], "edit" = "ma_crim_d", "line_break" = 1)
- fields[++fields.len] = list("field" = "Important Notes:", "value" = active2.fields["notes"], "edit" = "notes", "line_break" = 0)
- if(!active2.fields["comments"] || !islist(active2.fields["comments"]))
- active2.fields["comments"] = list()
- security["comments"] = active2.fields["comments"]
- security["empty"] = 0
+ if(record_security && GLOB.data_core.security.Find(record_security))
+ var/list/sec_fields = record_security.fields
+ security["fields"] = list(
+ SEC_FIELD("Criminal Status", sec_fields["criminal"], "criminal", TRUE),
+ SEC_FIELD("Minor Crimes", sec_fields["mi_crim"], "mi_crim", FALSE),
+ SEC_FIELD("Details", sec_fields["mi_crim_d"], "mi_crim_d", TRUE),
+ SEC_FIELD("Major Crimes", sec_fields["ma_crim"], "ma_crim", FALSE),
+ SEC_FIELD("Details", sec_fields["ma_crim_d"], "ma_crim_d", TRUE),
+ SEC_FIELD("Important Notes", sec_fields["notes"], null, FALSE),
+ )
+ if(!islist(sec_fields["comments"]))
+ sec_fields["comments"] = list()
+ security["comments"] = sec_fields["comments"]
+ security["empty"] = FALSE
else
- security["empty"] = 1
+ security["empty"] = TRUE
+
return data
-/obj/machinery/computer/secure_data/Topic(href, href_list)
+/obj/machinery/computer/secure_data/tgui_act(action, list/params)
if(..())
- return 1
+ return
- if(!GLOB.data_core.general.Find(active1))
- active1 = null
- if(!GLOB.data_core.security.Find(active2))
- active2 = null
+ . = TRUE
+ if(tgui_act_modal(action, params))
+ return
+ if(tgui_login_act(action, params))
+ return
- if(href_list["temp"])
- temp = null
-
- if(href_list["temp_action"])
- var/temp_href = splittext(href_list["temp_action"], "=")
- switch(temp_href[1])
- if("del_all2")
- for(var/datum/data/record/R in GLOB.data_core.security)
- qdel(R)
- update_all_mob_security_hud()
- setTemp("
All records deleted. ")
- if("del_alllogs2")
- if(GLOB.cell_logs.len)
- setTemp("All cell logs deleted. ")
- GLOB.cell_logs.Cut()
- else
- to_chat(usr, "Error; No cell logs to delete. ")
- if("del_r2")
- if(active2)
- qdel(active2)
- update_all_mob_security_hud()
- if("del_rg2")
- if(active1)
- for(var/datum/data/record/R in GLOB.data_core.medical)
- if(R.fields["name"] == active1.fields["name"] && R.fields["id"] == active1.fields["id"])
- qdel(R)
- QDEL_NULL(active1)
- QDEL_NULL(active2)
- update_all_mob_security_hud()
- screen = SEC_DATA_R_LIST
- if("criminal")
- if(active2)
- var/t1
- if(temp_href[2] == "execute")
- t1 = copytext(trim(sanitize(input("Explain why they are being executed. Include a list of their crimes, and victims.", "EXECUTION ORDER", null, null) as text)), 1, MAX_MESSAGE_LEN)
- else
- t1 = copytext(trim(sanitize(input("Enter Reason:", "Secure. records", null, null) as text)), 1, MAX_MESSAGE_LEN)
- if(!t1)
- t1 = "(none)"
- if(!set_criminal_status(usr, active2, temp_href[2], t1, rank, authcard_access))
- setTemp("Error: permission denied. ")
- return 1
- if("rank")
- if(active1)
- active1.fields["rank"] = temp_href[2]
- if(temp_href[2] in GLOB.joblist)
- active1.fields["real_rank"] = temp_href[2]
-
- if(href_list["scan"])
- if(scan)
- scan.forceMove(loc)
- if(ishuman(usr) && !usr.get_active_hand())
- usr.put_in_hands(scan)
- scan = null
- else
- var/obj/item/I = usr.get_active_hand()
- if(istype(I, /obj/item/card/id))
- usr.drop_item()
- I.forceMove(src)
- scan = I
-
- if(href_list["login"])
- if(isAI(usr))
- authenticated = usr.name
- rank = "AI"
- else if(isrobot(usr))
- authenticated = usr.name
- var/mob/living/silicon/robot/R = usr
- rank = "[R.modtype] [R.braintype]"
- else if(istype(scan, /obj/item/card/id))
- if(check_access(scan))
- authenticated = scan.registered_name
- rank = scan.assignment
- authcard_access = scan.access
-
- if(authenticated)
- active1 = null
- active2 = null
- screen = SEC_DATA_R_LIST
-
- if(authenticated)
- if(href_list["logout"])
- authenticated = null
- screen = null
- active1 = null
- active2 = null
- authcard_access = list()
-
- else if(href_list["sort"])
- // Reverse the order if clicked twice
- if(sortBy == href_list["sort"])
- if(order == 1)
- order = -1
- else
- order = 1
- else
- sortBy = href_list["sort"]
- order = initial(order)
-
- else if(href_list["screen"])
- screen = text2num(href_list["screen"])
- if(screen < 1)
- screen = SEC_DATA_R_LIST
-
- active1 = null
- active2 = null
-
- else if(href_list["d_rec"])
- var/datum/data/record/R = locate(href_list["d_rec"])
- var/datum/data/record/M = locate(href_list["d_rec"])
- if(!GLOB.data_core.general.Find(R))
- setTemp("Record not found! ")
- return 1
- for(var/datum/data/record/E in GLOB.data_core.security)
- if(E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"])
- M = E
- active1 = R
- active2 = M
- screen = SEC_DATA_RECORD
-
- else if(href_list["del_all"])
- var/list/buttons = list()
- buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_all2=1", "status" = null)
- buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null)
- setTemp("Are you sure you wish to delete all records? ", buttons)
-
- else if(href_list["del_alllogs"])
- var/list/buttons = list()
- buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_alllogs2=1", "status" = null)
- buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null)
- setTemp("Are you sure you wish to delete all cell logs? ", buttons)
-
- else if(href_list["del_rg"])
- if(active1)
- var/list/buttons = list()
- buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_rg2=1", "status" = null)
- buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null)
- setTemp("Are you sure you wish to delete the record (ALL)? ", buttons)
-
- else if(href_list["del_r"])
- if(active1)
- var/list/buttons = list()
- buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_r2=1", "status" = null)
- buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null)
- setTemp("Are you sure you wish to delete the record (Security Portion Only)? ", buttons)
-
- else if(href_list["new_s"])
- if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record))
- var/datum/data/record/R = new /datum/data/record()
- R.fields["name"] = active1.fields["name"]
- R.fields["id"] = active1.fields["id"]
- R.name = "Security Record #[R.fields["id"]]"
- R.fields["criminal"] = "None"
- R.fields["mi_crim"] = "None"
- R.fields["mi_crim_d"] = "No minor crime convictions."
- R.fields["ma_crim"] = "None"
- R.fields["ma_crim_d"] = "No major crime convictions."
- R.fields["notes"] = "No notes."
- GLOB.data_core.security += R
- active2 = R
- screen = SEC_DATA_RECORD
-
- else if(href_list["new_g"])
+ switch(action)
+ if("cleartemp")
+ temp_notice = null
+ if("page") // Select Page
+ var/page_num = clamp(text2num(params["page"]) || SEC_DATA_R_LIST, SEC_DATA_R_LIST, SEC_DATA_MAINT) // SEC_DATA_RECORD cannot be accessed through this act
+ current_page = page_num
+ record_general = null
+ record_security = null
+ if("view") // View Record
+ var/datum/data/record/G = locateUID(params["uid_gen"])
+ var/datum/data/record/S = locateUID(params["uid_sec"])
+ if(!istype(G)) // No general record!
+ set_temp("General record not found!", "danger")
+ return
+ if(istype(S) && !(G.fields["name"] == S.fields["name"] && G.fields["id"] == S.fields["id"])) // General and security records don't match!
+ S = null
+ record_general = G
+ record_security = S
+ current_page = SEC_DATA_RECORD
+ if("new_general") // New General Record
+ if(record_general)
+ return
var/datum/data/record/G = new /datum/data/record()
G.fields["name"] = "New Record"
G.fields["id"] = "[add_zero(num2hex(rand(1, 1.6777215E7)), 6)]"
@@ -319,228 +200,219 @@
G.fields["m_stat"] = "Stable"
G.fields["species"] = "Human"
GLOB.data_core.general += G
- active1 = G
- active2 = null
+ record_general = G
+ record_security = null
+ current_page = SEC_DATA_RECORD
+ if("new_security") // New Security Record
+ if(!record_general || record_security)
+ return
+ var/datum/data/record/S = new /datum/data/record()
+ S.fields["name"] = record_general.fields["name"]
+ S.fields["id"] = record_general.fields["id"]
+ S.name = "Security Record #[S.fields["id"]]"
+ S.fields["criminal"] = SEC_RECORD_STATUS_NONE
+ S.fields["mi_crim"] = "None"
+ S.fields["mi_crim_d"] = "No minor crime convictions."
+ S.fields["ma_crim"] = "None"
+ S.fields["ma_crim_d"] = "No major crime convictions."
+ S.fields["notes"] = "No notes."
+ GLOB.data_core.security += S
+ record_security = S
+ update_all_mob_security_hud()
+ if("delete_general") // Delete General, Security and Medical Records
+ if(!record_general)
+ return
+ for(var/datum/data/record/M in GLOB.data_core.medical)
+ if(M.fields["name"] == record_general.fields["name"] && M.fields["id"] == record_general.fields["id"])
+ qdel(M)
+ QDEL_NULL(record_general)
+ QDEL_NULL(record_security)
+ update_all_mob_security_hud()
+ current_page = SEC_DATA_R_LIST
+ set_temp("General, Security and Medical records deleted.")
+ if("delete_security") // Delete Security Record
+ if(!record_security)
+ return
+ QDEL_NULL(record_security)
+ update_all_mob_security_hud()
+ set_temp("Security record deleted.")
+ if("delete_security_all") // Delete All Security Records
+ for(var/datum/data/record/S in GLOB.data_core.security)
+ qdel(S)
+ update_all_mob_security_hud()
+ set_temp("All security records deleted.")
+ if("delete_cell_logs") // Delete All Cell Logs
+ GLOB?.cell_logs.Cut()
+ set_temp("All cell logs deleted.")
+ if("comment_delete") // Delete Comment
+ var/index = text2num(params["id"] || "")
+ if(!index || !record_security)
+ return
- else if(href_list["print_r"])
- if(!printing)
- printing = 1
- playsound(loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1)
- sleep(50)
- var/obj/item/paper/P = new /obj/item/paper(loc)
- P.info = "Security Record "
- if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1))
- P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]]
- \nSex: [active1.fields["sex"]]
- \nAge: [active1.fields["age"]]
- \nFingerprint: [active1.fields["fingerprint"]]
- \nPhysical Status: [active1.fields["p_stat"]]
- \nMental Status: [active1.fields["m_stat"]] "}
- else
- P.info += "General Record Lost! "
- if(istype(active2, /datum/data/record) && GLOB.data_core.security.Find(active2))
- P.info += {" \nSecurity Data
- \nCriminal Status: [active2.fields["criminal"]] \n
- \nMinor Crimes: [active2.fields["mi_crim"]]
- \nDetails: [active2.fields["mi_crim_d"]] \n
- \nMajor Crimes: [active2.fields["ma_crim"]]
- \nDetails: [active2.fields["ma_crim_d"]] \n
- \nImportant Notes:
- \n\t[active2.fields["notes"]] \n \nComments/Log "}
- for(var/c in active2.fields["comments"])
- P.info += "[c] "
- else
- P.info += "Security Record Lost! "
- P.info += ""
- P.name = "paper - 'Security Record: [active1.fields["name"]]'"
- printing = 0
+ var/list/comments = record_security.fields["comments"]
+ index = clamp(index, 1, length(comments))
+ if(comments[index])
+ comments.Cut(index, index + 1)
+ if("print_record")
+ if(is_printing)
+ return
+ is_printing = TRUE
+ playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE)
+ addtimer(CALLBACK(src, .proc/print_record_finish), 5 SECONDS)
+ else
+ return FALSE
-/* Removed due to BYOND issue
- else if(href_list["print_p"])
- if(!printing)
- printing = 1
- playsound(loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1)
- sleep(50)
- if(istype(active1, /datum/data/record) && data_core.general.Find(active1))
- create_record_photo(active1)
- printing = 0
-*/
+ add_fingerprint(usr)
- else if(href_list["printlogs"])
- if(GLOB.cell_logs.len && !printing)
- var/obj/item/paper/P = input(usr, "Select log to print", "Available Cell Logs") as null|anything in GLOB.cell_logs
- if(!P)
- return 0
- printing = 1
- playsound(loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1)
- to_chat(usr, "Printing file [P.name]. ")
- sleep(50)
- var/obj/item/paper/log = new /obj/item/paper(loc)
- log.name = P.name
- log.info = P.info
- printing = 0
- return 1
- else
- to_chat(usr, "[src] has no logs stored or is already printing. ")
-
-
- else if(href_list["add_c"])
- if(istype(active2, /datum/data/record))
- var/a2 = active2
- var/t1 = copytext(trim(sanitize(input("Add Comment:", "Secure. records", null, null) as message)), 1, MAX_MESSAGE_LEN)
- if(!t1 || ..() || active2 != a2)
- return 1
- active2.fields["comments"] += "Made by [authenticated] ([rank]) on [GLOB.current_date_string] [station_time_timestamp()] [t1]"
-
- else if(href_list["del_c"])
- var/index = min(max(text2num(href_list["del_c"]) + 1, 1), length(active2.fields["comments"]))
- if(istype(active2, /datum/data/record) && active2.fields["comments"][index])
- active2.fields["comments"] -= active2.fields["comments"][index]
-
- if(href_list["field"])
- if(..())
- return 1
- var/a1 = active1
- var/a2 = active2
- switch(href_list["field"])
- if("name")
- if(istype(active1, /datum/data/record))
- var/t1 = reject_bad_name(clean_input("Please input name:", "Secure. records", active1.fields["name"], null))
- if(!t1 || !length(trim(t1)) || ..() || active1 != a1)
- return 1
- active1.fields["name"] = t1
- if(istype(active2, /datum/data/record))
- active2.fields["name"] = t1
- if("id")
- if(istype(active1, /datum/data/record))
- var/t1 = copytext(trim(sanitize(input("Please input id:", "Secure. records", active1.fields["id"], null) as text)), 1, MAX_MESSAGE_LEN)
- if(!t1 || ..() || active1 != a1)
- return 1
- active1.fields["id"] = t1
- if(istype(active2, /datum/data/record))
- active2.fields["id"] = t1
- if("fingerprint")
- if(istype(active1, /datum/data/record))
- var/t1 = copytext(trim(sanitize(input("Please input fingerprint hash:", "Secure. records", active1.fields["fingerprint"], null) as text)), 1, MAX_MESSAGE_LEN)
- if(!t1 || ..() || active1 != a1)
- return 1
- active1.fields["fingerprint"] = t1
- if("sex")
- if(istype(active1, /datum/data/record))
- if(active1.fields["sex"] == "Male")
- active1.fields["sex"] = "Female"
- else
- active1.fields["sex"] = "Male"
- if("age")
- if(istype(active1, /datum/data/record))
- var/t1 = input("Please input age:", "Secure. records", active1.fields["age"], null) as num
- if(!t1 || ..() || active1 != a1)
- return 1
- active1.fields["age"] = t1
- if("mi_crim")
- if(istype(active2, /datum/data/record))
- var/t1 = copytext(trim(sanitize(input("Please input minor crimes list:", "Secure. records", active2.fields["mi_crim"], null) as text)), 1, MAX_MESSAGE_LEN)
- if(!t1 || ..() || active2 != a2)
- return 1
- active2.fields["mi_crim"] = t1
- if("mi_crim_d")
- if(istype(active2, /datum/data/record))
- var/t1 = copytext(trim(sanitize(input("Please summarize minor crimes:", "Secure. records", active2.fields["mi_crim_d"], null) as message)), 1, MAX_MESSAGE_LEN)
- if(!t1 || ..() || active2 != a2)
- return 1
- active2.fields["mi_crim_d"] = t1
- if("ma_crim")
- if(istype(active2, /datum/data/record))
- var/t1 = copytext(trim(sanitize(input("Please input major crimes list:", "Secure. records", active2.fields["ma_crim"], null) as text)), 1, MAX_MESSAGE_LEN)
- if(!t1 || ..() || active2 != a2)
- return 1
- active2.fields["ma_crim"] = t1
- if("ma_crim_d")
- if(istype(active2, /datum/data/record))
- var/t1 = copytext(trim(sanitize(input("Please summarize major crimes:", "Secure. records", active2.fields["ma_crim_d"], null) as message)), 1, MAX_MESSAGE_LEN)
- if(!t1 || ..() || active2 != a2)
- return 1
- active2.fields["ma_crim_d"] = t1
- if("notes")
- if(istype(active2, /datum/data/record))
- var/t1 = copytext(html_encode(trim(input("Please summarize notes:", "Secure. records", html_decode(active2.fields["notes"]), null) as message)), 1, MAX_MESSAGE_LEN)
- if(!t1 || ..() || active2 != a2)
- return 1
- active2.fields["notes"] = t1
- if("criminal")
- if(istype(active2, /datum/data/record))
- var/list/buttons = list()
- buttons[++buttons.len] = list("name" = "None", "icon" = "unlock", "href" = "criminal=none", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_NONE ? "selected" : null))
- buttons[++buttons.len] = list("name" = "*Arrest*", "icon" = "lock", "href" = "criminal=arrest", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_ARREST ? "selected" : null))
- buttons[++buttons.len] = list("name" = "Search", "icon" = "lock", "href" = "criminal=search", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_SEARCH ? "selected" : null))
- buttons[++buttons.len] = list("name" = "Monitor", "icon" = "unlock", "href" = "criminal=monitor", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_MONITOR ? "selected" : null))
- buttons[++buttons.len] = list("name" = "Demote", "icon" = "lock", "href" = "criminal=demote", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_DEMOTE ? "selected" : null))
- buttons[++buttons.len] = list("name" = "Incarcerated", "icon" = "lock", "href" = "criminal=incarcerated", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_INCARCERATED ? "selected" : null))
- buttons[++buttons.len] = list("name" = "*Execute*", "icon" = "lock", "href" = "criminal=execute", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_EXECUTE ? "selected" : null))
- buttons[++buttons.len] = list("name" = "Parolled", "icon" = "unlock-alt", "href" = "criminal=parolled", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_PAROLLED ? "selected" : null))
- buttons[++buttons.len] = list("name" = "Released", "icon" = "unlock", "href" = "criminal=released", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_RELEASED ? "selected" : null))
- setTemp("Criminal Status ", buttons)
- if("rank")
- var/list/L = list("Head of Personnel", "Captain", "AI")
- //This was so silly before the change. Now it actually works without beating your head against the keyboard. /N
- if(istype(active1, /datum/data/record) && L.Find(rank))
- var/list/buttons = list()
- for(var/rank in GLOB.joblist)
- buttons[++buttons.len] = list("name" = rank, "icon" = null, "href" = "rank=[rank]", "status" = (active1.fields["rank"] == rank ? "selected" : null))
- setTemp("Rank ", buttons)
+/**
+ * Called in tgui_act() to process modal actions
+ *
+ * Arguments:
+ * * action - The action passed by tgui
+ * * params - The params passed by tgui
+ */
+/obj/machinery/computer/secure_data/proc/tgui_act_modal(action, list/params)
+ . = TRUE
+ var/id = params["id"]
+ var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"]
+ switch(tgui_modal_act(src, action, params))
+ if(TGUI_MODAL_OPEN)
+ switch(id)
+ if("edit")
+ var/field = arguments["field"]
+ if(!length(field) || !field_edit_questions[field])
+ return
+ var/question = field_edit_questions[field]
+ var/choices = field_edit_choices[field]
+ if(length(choices))
+ tgui_modal_choice(src, id, question, arguments = arguments, value = arguments["value"], choices = choices)
else
- setTemp("You do not have the required rank to do this! ")
- if("species")
- if(istype(active1, /datum/data/record))
- var/t1 = copytext(trim(sanitize(input("Please enter race:", "General records", active1.fields["species"], null) as message)), 1, MAX_MESSAGE_LEN)
- if(!t1 || ..() || active1 != a1)
- return 1
- active1.fields["species"] = t1
- return 1
+ tgui_modal_input(src, id, question, arguments = arguments, value = arguments["value"])
+ if("comment_add")
+ tgui_modal_input(src, id, "Please enter your message:")
+ if("print_cell_log")
+ if(is_printing)
+ return
+ if(!length(GLOB?.cell_logs))
+ set_temp("There are no cell logs available to print.")
+ return
+ var/list/choices = list()
+ var/list/already_in[0]
+ for(var/obj/item/paper/P in GLOB.cell_logs)
+ if(already_in[P.name])
+ continue
+ choices += P.name
+ already_in[P.name] = TRUE
+ tgui_modal_choice(src, id, "Please select the cell log you would like printed:", choices = choices)
+ else
+ return FALSE
+ if(TGUI_MODAL_ANSWER)
+ var/answer = params["answer"]
+ switch(id)
+ if("edit")
+ var/field = arguments["field"]
+ if(!length(field) || !field_edit_questions[field])
+ return
+ var/list/choices = field_edit_choices[field]
+ if(length(choices) && !(answer in choices))
+ return
-/obj/machinery/computer/secure_data/proc/setTemp(text, list/buttons = list())
- temp = list("text" = text, "buttons" = buttons, "has_buttons" = buttons.len > 0)
+ if(field == "age")
+ var/new_age = text2num(answer)
+ if(new_age < AGE_MIN || new_age > AGE_MAX)
+ set_temp("Invalid age. It must be between [AGE_MIN] and [AGE_MAX].", "danger")
+ return
+ answer = new_age
+ else if(field == "criminal")
+ var/text = "Please enter a reason for the status change to [answer]:"
+ if(answer == SEC_RECORD_STATUS_EXECUTE)
+ text = "Please explain why they are being executed. Include a list of their crimes, and victims."
+ tgui_modal_input(src, "criminal_reason", text, arguments = list("status" = answer))
+ return
-/* Proc disabled due to BYOND Issue
+ if(record_security && (field in record_security.fields))
+ record_security.fields[field] = answer
+ else if(record_general && (field in record_general.fields))
+ record_general.fields[field] = answer
+ if("criminal_reason")
+ if(!record_security || !(arguments["status"] in field_edit_choices["criminal"]))
+ return
+ if(!set_criminal_status(usr, record_security, arguments["status"], answer, tgui_login_get().rank, inserted_id_access))
+ set_temp("Required permissions to set this criminal status not found!", "danger")
+ if("comment_add")
+ var/datum/tgui_login/state = tgui_login_get()
+ if(!length(answer) || !record_security || !length(state.name))
+ return
+ record_security.fields["comments"] += list(list(
+ header = "Made by [state.name] ([state.rank]) on [GLOB.current_date_string] [station_time_timestamp()]",
+ text = answer
+ ))
+ if("print_cell_log")
+ if(is_printing)
+ return
+ var/obj/item/paper/T
+ for(var/obj/item/paper/P in GLOB?.cell_logs)
+ if(P.name == answer)
+ T = P
+ break
+ if(!T)
+ set_temp("Cell log not found!", "danger")
+ return
+ is_printing = TRUE
+ playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE)
+ addtimer(CALLBACK(src, .proc/print_cell_log_finish, T.name, T.info), 5 SECONDS)
+ else
+ return FALSE
+ else
+ return FALSE
-/obj/machinery/computer/secure_data/proc/create_record_photo(datum/data/record/R)
- // basically copy-pasted from the camera code but different enough that it has to be redone
- var/icon/photoimage = get_record_photo(R)
- var/icon/small_img = icon(photoimage)
- var/icon/tiny_img = icon(photoimage)
- var/icon/ic = icon('icons/obj/items.dmi',"photo")
- var/icon/pc = icon('icons/obj/bureaucracy.dmi', "photo")
- small_img.Scale(8, 8)
- tiny_img.Scale(4, 4)
- ic.Blend(small_img, ICON_OVERLAY, 10, 13)
- pc.Blend(tiny_img, ICON_OVERLAY, 12, 19)
+/**
+ * Called when the print record timer finishes
+ */
+/obj/machinery/computer/secure_data/proc/print_record_finish()
+ var/obj/item/paper/P = new(loc)
+ P.info = "Security Record "
+ if(record_general && GLOB.data_core.general.Find(record_general))
+ P.info += {"Name: [record_general.fields["name"]] ID: [record_general.fields["id"]]
+ \nSex: [record_general.fields["sex"]]
+ \nAge: [record_general.fields["age"]]
+ \nFingerprint: [record_general.fields["fingerprint"]]
+ \nPhysical Status: [record_general.fields["p_stat"]]
+ \nMental Status: [record_general.fields["m_stat"]] "}
+ P.name = "paper - 'Security Record: [record_general.fields["name"]]'"
+ else
+ P.info += "General Record Lost! "
+ if(record_security && GLOB.data_core.security.Find(record_security))
+ P.info += {" \nSecurity Data
+ \nCriminal Status: [record_security.fields["criminal"]] \n
+ \nMinor Crimes: [record_security.fields["mi_crim"]]
+ \nDetails: [record_security.fields["mi_crim_d"]] \n
+ \nMajor Crimes: [record_security.fields["ma_crim"]]
+ \nDetails: [record_security.fields["ma_crim_d"]] \n
+ \nImportant Notes:
+ \n\t[record_security.fields["notes"]] \n \nComments/Log "}
+ for(var/c in record_security.fields["comments"])
+ P.info += "[c] "
+ else
+ P.info += "Security Record Lost! "
+ is_printing = FALSE
+ SStgui.update_uis(src)
- var/datum/picture/P = new()
- P.fields["name"] = "File Photo - [R.fields["name"]]"
- P.fields["author"] = "Central Command"
- P.fields["icon"] = ic
- P.fields["tiny"] = pc
- P.fields["img"] = photoimage
- P.fields["desc"] = "You can see [R.fields["name"]] on the photo."
- P.fields["pixel_x"] = rand(-10, 10)
- P.fields["pixel_y"] = rand(-10, 10)
- P.fields["size"] = 2
+/**
+ * Called when the print cell log timer finishes
+ */
+/obj/machinery/computer/secure_data/proc/print_cell_log_finish(name, info)
+ var/obj/item/paper/P = new(loc)
+ P.name = name
+ P.info = info
+ is_printing = FALSE
+ SStgui.update_uis(src)
- var/obj/item/photo/PH = new/obj/item/photo(loc)
- PH.construct(P)
+/obj/machinery/computer/secure_data/tgui_login_on_login(datum/tgui_login/state)
+ inserted_id_access = state?.id.access
-*/
-
-/obj/machinery/computer/secure_data/proc/get_record_photo(datum/data/record/R)
- // similar to the code to make a photo, but of course the actual rendering is completely different
- var/icon/res = icon('icons/effects/96x96.dmi', "")
- // will be 2x2 to fit the 2 directions
- res.Scale(2 * 32, 2 * 32)
- // transparent background (it's a plastic transparency, you see) with the front and side icons
- res.Blend(icon(R.fields["photo"], dir = SOUTH), ICON_OVERLAY, 1, 17)
- res.Blend(icon(R.fields["photo"], dir = WEST), ICON_OVERLAY, 33, 17)
-
- return res
+/obj/machinery/computer/secure_data/tgui_login_on_logout(datum/tgui_login/state)
+ inserted_id_access = null
/obj/machinery/computer/secure_data/emp_act(severity)
if(stat & (BROKEN|NOPOWER))
@@ -548,8 +420,8 @@
return
for(var/datum/data/record/R in GLOB.data_core.security)
- if(prob(10/severity))
- switch(rand(1,6))
+ if(prob(10 / severity))
+ switch(rand(1, 6))
if(1)
R.fields["name"] = "[pick(pick(GLOB.first_names_male), pick(GLOB.first_names_female))] [pick(GLOB.last_names)]"
if(2)
@@ -570,6 +442,18 @@
..(severity)
+/**
+ * Sets a temporary message to display to the user
+ *
+ * Arguments:
+ * * text - Text to display, null/empty to clear the message from the UI
+ * * style - The style of the message: (color name), info, success, warning, danger
+ */
+/obj/machinery/computer/secure_data/proc/set_temp(text = "", style = "info", update_now = FALSE)
+ temp_notice = list(text = text, style = style)
+ if(update_now)
+ SStgui.update_uis(src)
+
/obj/machinery/computer/secure_data/detective_computer
icon = 'icons/obj/computer.dmi'
icon_state = "messyfiles"
@@ -580,8 +464,10 @@
icon_state = "laptop"
icon_keyboard = "seclaptop_key"
icon_screen = "seclaptop"
- density = 0
+ density = FALSE
#undef SEC_DATA_R_LIST
#undef SEC_DATA_MAINT
#undef SEC_DATA_RECORD
+#undef FIELD
+#undef SEC_FIELD
diff --git a/tgui/packages/tgui/index.js b/tgui/packages/tgui/index.js
index 45012a7e956..af9d6c6d7be 100644
--- a/tgui/packages/tgui/index.js
+++ b/tgui/packages/tgui/index.js
@@ -24,6 +24,7 @@ import './styles/themes/hackerman.scss';
import './styles/themes/malfunction.scss';
import './styles/themes/ntos.scss';
import './styles/themes/retro.scss';
+import './styles/themes/security.scss';
import './styles/themes/syndicate.scss';
diff --git a/tgui/packages/tgui/interfaces/SecurityRecords.js b/tgui/packages/tgui/interfaces/SecurityRecords.js
new file mode 100644
index 00000000000..7ce69f82c4c
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/SecurityRecords.js
@@ -0,0 +1,437 @@
+import { createSearch } from 'common/string';
+import { Fragment } from 'inferno';
+import { useBackend, useLocalState } from "../backend";
+import { Box, Button, Flex, Icon, Input, LabeledList, Section, Table, Tabs } from '../components';
+import { FlexItem } from '../components/Flex';
+import { Window } from "../layouts";
+import { ComplexModal, modalOpen } from './common/ComplexModal';
+import { LoginInfo } from './common/LoginInfo';
+import { LoginScreen } from './common/LoginScreen';
+import { TemporaryNotice } from './common/TemporaryNotice';
+
+const statusStyles = {
+ "*Execute*": "execute",
+ "*Arrest*": "arrest",
+ "Incarcerated": "incarcerated",
+ "Parolled": "parolled",
+ "Released": "released",
+};
+
+const doEdit = (context, field) => {
+ modalOpen(context, 'edit', {
+ field: field.edit,
+ value: field.value,
+ });
+};
+
+export const SecurityRecords = (properties, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ loginState,
+ currentPage,
+ } = data;
+
+ let body;
+ if (!loginState.logged_in) {
+ return (
+
+
+
+
+
+ );
+ } else {
+ if (currentPage === 1) {
+ body = ;
+ } else if (currentPage === 2) {
+ body = ;
+ } else if (currentPage === 3) {
+ body = ;
+ }
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const SecurityRecordsNavigation = (properties, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ currentPage,
+ general,
+ } = data;
+ return (
+
+ act('page', { page: 1 })}>
+
+ List Records
+
+ act('page', { page: 2 })}>
+
+ Record Maintenance
+
+ {(currentPage === 3 && general && !general.empty) && (
+
+
+ Record: {general.fields[0].value}
+
+ )}
+
+ );
+};
+
+const SecurityRecordsPageList = (properties, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ records,
+ } = data;
+ const [searchText, setSearchText] = useLocalState(context, "searchText", "");
+ const [sortId, _setSortId] = useLocalState(context, "sortId", "name");
+ const [sortOrder, _setSortOrder] = useLocalState(context, "sortOrder", true);
+ return (
+
+
+
+
+
+ Name
+ ID
+ Assignment
+ Fingerprint
+ Criminal Status
+
+ {records
+ .filter(createSearch(searchText, record => {
+ return record.name + "|"
+ + record.id + "|"
+ + record.rank + "|"
+ + record.fingerprint + "|"
+ + record.status;
+ }))
+ .sort((a, b) => {
+ const i = sortOrder ? 1 : -1;
+ return a[sortId].localeCompare(b[sortId]) * i;
+ })
+ .map(record => (
+ act('view', {
+ uid_gen: record.uid_gen,
+ uid_sec: record.uid_sec,
+ })}>
+ {record.name}
+ {record.id}
+ {record.rank}
+ {record.fingerprint}
+ {record.status}
+
+ ))}
+
+
+
+ );
+};
+
+const SortButton = (properties, context) => {
+ const [sortId, setSortId] = useLocalState(context, "sortId", "name");
+ const [sortOrder, setSortOrder] = useLocalState(context, "sortOrder", true);
+ const {
+ id,
+ children,
+ } = properties;
+ return (
+
+ {
+ if (sortId === id) {
+ setSortOrder(!sortOrder);
+ } else {
+ setSortId(id);
+ setSortOrder(true);
+ }
+ }}>
+ {children}
+ {sortId === id && (
+
+ )}
+
+
+ );
+};
+
+const SecurityRecordsActions = (properties, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ isPrinting,
+ } = data;
+ const [searchText, setSearchText] = useLocalState(context, "searchText", "");
+ return (
+
+
+ act('new_general')}
+ />
+ modalOpen(context, "print_cell_log")}
+ />
+
+
+ setSearchText(value)}
+ />
+
+
+ );
+};
+
+const SecurityRecordsPageMaintenance = (properties, context) => {
+ const { act } = useBackend(context);
+ return (
+
+
+
+ act('delete_security_all')}
+ mb="0.5rem"
+ />
+ act('delete_cell_logs')}
+ />
+
+ );
+};
+
+const SecurityRecordsPageView = (properties, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ isPrinting,
+ general,
+ security,
+ } = data;
+ if (!general || !general.fields) {
+ return (
+
+ General records lost!
+
+ );
+ }
+ return (
+
+
+ act('print_record')}
+ />
+ act('delete_general')}
+ />
+
+ }>
+
+
+ act('delete_security')}
+ />
+ }>
+
+
+
+ );
+};
+
+const SecurityRecordsViewGeneral = (_properties, context) => {
+ const { data } = useBackend(context);
+ const {
+ general,
+ } = data;
+ if (!general || !general.fields) {
+ return (
+
+ General records lost!
+
+ );
+ }
+ return (
+
+
+
+ {general.fields.map((field, i) => (
+
+
+ {field.value}
+
+ {!!field.edit && (
+ doEdit(context, field)}
+ />
+ )}
+
+ ))}
+
+
+
+ {!!general.has_photos && (
+ general.photos.map((p, i) => (
+
+
+ Photo #{i + 1}
+
+ ))
+ )}
+
+
+ );
+};
+
+const SecurityRecordsViewSecurity = (_properties, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ security,
+ } = data;
+ if (!security || !security.fields) {
+ return (
+
+ Security records lost!
+ act('new_security')}
+ />
+
+ );
+ }
+ return (
+
+
+ {security.fields.map((field, i) => (
+
+ {field.value}
+ {!!field.edit && (
+ doEdit(context, field)}
+ />
+ )}
+
+ ))}
+
+ modalOpen(context, 'comment_add')}
+ />
+ }>
+ {security.comments.length === 0 ? (
+
+ No comments found.
+
+ ) : security.comments.map((comment, i) => (
+
+
+ {comment.header || "Auto-generated"}
+
+ {comment.text || comment}
+ act('comment_delete', { id: i + 1 })}
+ />
+
+ ))}
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/common/ComplexModal.js b/tgui/packages/tgui/interfaces/common/ComplexModal.js
index 0bc6804752c..1454c2fbd95 100644
--- a/tgui/packages/tgui/interfaces/common/ComplexModal.js
+++ b/tgui/packages/tgui/interfaces/common/ComplexModal.js
@@ -89,6 +89,7 @@ export const ComplexModal = (props, context) => {
onClick={() => modalClose(context)}
/>
);
+ let overflowY = "auto";
// Different contents depending on the type
if (bodyOverrides[id]) {
@@ -140,6 +141,7 @@ export const ComplexModal = (props, context) => {
onSelected={val => modalAnswer(context, id, val)}
/>
);
+ overflowY = "initial";
} else if (type === "bento") {
modalBody = (
{
maxHeight={props.maxHeight || (window.innerHeight / 2 + "px")}
onEnter={modalOnEnter}
mx="auto"
- overflowY="auto">
+ overflowY={overflowY}>
{text}
diff --git a/tgui/packages/tgui/styles/interfaces/SecurityRecords.scss b/tgui/packages/tgui/styles/interfaces/SecurityRecords.scss
new file mode 100644
index 00000000000..f1f30b19965
--- /dev/null
+++ b/tgui/packages/tgui/styles/interfaces/SecurityRecords.scss
@@ -0,0 +1,36 @@
+@use '../base.scss';
+@use '../colors.scss';
+@use '../functions.scss' as *;
+
+.SecurityRecords__list {
+ tr > td {
+ text-align: center;
+ }
+
+ tr:not(:first-child) {
+ height: 24px;
+ line-height: 24px;
+ cursor: pointer;
+
+ transition: background-color 50ms;
+ &:hover, &:focus {
+ background-color: rgba(base.$color-bg, 1);
+ }
+ }
+}
+
+.SecurityRecords__listRow--execute {
+ background-color: colors.bg(#5E0A1A);
+}
+
+.SecurityRecords__listRow--arrest {
+ background-color: colors.bg(#890E26);
+}
+
+.SecurityRecords__listRow--incarcerated, .SecurityRecords__listRow--parolled {
+ background-color: colors.bg(#743B03);
+}
+
+.SecurityRecords__listRow--released {
+ background-color: colors.bg(#216489);
+}
diff --git a/tgui/packages/tgui/styles/main.scss b/tgui/packages/tgui/styles/main.scss
index 490073e6854..bab57ee89d0 100644
--- a/tgui/packages/tgui/styles/main.scss
+++ b/tgui/packages/tgui/styles/main.scss
@@ -42,6 +42,7 @@
@include meta.load-css('./interfaces/CameraConsole.scss');
@include meta.load-css('./interfaces/NuclearBomb.scss');
@include meta.load-css('./interfaces/Roulette.scss');
+@include meta.load-css('./interfaces/SecurityRecords.scss');
// Layouts
@include meta.load-css('./layouts/Layout.scss');
diff --git a/tgui/packages/tgui/styles/themes/security.scss b/tgui/packages/tgui/styles/themes/security.scss
new file mode 100644
index 00000000000..12ccc0de3f6
--- /dev/null
+++ b/tgui/packages/tgui/styles/themes/security.scss
@@ -0,0 +1,26 @@
+@use 'sass:color';
+@use 'sass:meta';
+
+@use '../colors.scss' with (
+ $primary: #a14c49,
+ $blue: #d0212d,
+ $label: #a7817e,
+ $fg-map-keys: ('label'),
+ $bg-map-keys: ('good')
+);
+@use '../base.scss';
+
+.theme-security {
+ // Atomic classes
+ @include meta.load-css('../atomic/color.scss');
+
+ // Components
+ @include meta.load-css('../components/Button.scss', $with: (
+ 'color-default': colors.$primary,
+ ));
+ @include meta.load-css('../components/Input.scss', $with: (
+ 'border-color': #ff8d88,
+ ));
+ @include meta.load-css('../components/NoticeBox.scss');
+ @include meta.load-css('../components/Section.scss');
+}