From ffb65bbc3e7cf89903899e03925bf7149337b85c Mon Sep 17 00:00:00 2001 From: CHOMPStation2 <58959929+CHOMPStation2@users.noreply.github.com> Date: Sat, 12 Oct 2024 06:03:24 -0700 Subject: [PATCH] [MIRROR] Convert ATM to TGUI (#9212) Co-authored-by: ShadowLarkens Co-authored-by: CHOMPStation2 --- code/modules/economy/ATM.dm | 635 ++++++++-------- .../interfaces/AutomatedTellerMachine.tsx | 702 ++++++++++++++++++ .../interfaces/AutomatedTellerMachine.scss | 42 ++ tgui/packages/tgui/styles/main.scss | 1 + 4 files changed, 1061 insertions(+), 319 deletions(-) create mode 100644 tgui/packages/tgui/interfaces/AutomatedTellerMachine.tsx create mode 100644 tgui/packages/tgui/styles/interfaces/AutomatedTellerMachine.scss diff --git a/code/modules/economy/ATM.dm b/code/modules/economy/ATM.dm index f5690ccdfa..704700139f 100644 --- a/code/modules/economy/ATM.dm +++ b/code/modules/economy/ATM.dm @@ -123,330 +123,327 @@ log transactions else ..() +/obj/machinery/atm/tgui_status(mob/user) + . = ..() + if(issilicon(user)) + return STATUS_CLOSE + +/obj/machinery/atm/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui, custom_state) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AutomatedTellerMachine", name) + ui.open() + +/obj/machinery/atm/tgui_static_data(mob/user) + var/list/data = ..() + data["machine_id"] = machine_id + return data + +/obj/machinery/atm/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + data["emagged"] = emagged + if(emagged > 0) + return data + + data["held_card"] = held_card + data["locked_down"] = ticks_left_locked_down + if(ticks_left_locked_down > 0) + return data + + data["authenticated_account"] = null + data["suspended"] = FALSE + if(authenticated_account) + if(authenticated_account.suspended) + data["suspended"] = TRUE + return data + + var/list/transactions = list() + for(var/datum/transaction/T as anything in authenticated_account.transaction_log) + UNTYPED_LIST_ADD(transactions, list( + "date" = T.date, + "time" = T.time, + "target_name" = T.target_name, + "purpose" = T.purpose, + "amount" = T.amount, + "source_terminal" = T.source_terminal + )) + + data["authenticated_account"] = list( + "owner_name" = authenticated_account.owner_name, + "money" = authenticated_account.money, + "security_level" = authenticated_account.security_level, + "transactions" = transactions, + ) + + return data + +/obj/machinery/atm/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + . = ..() + if(.) + return + + switch(action) + // This is also a logout + if("insert_card") + if(held_card) + release_held_id(usr) + else + if(emagged > 0) + to_chat(usr, span_red("[icon2html(src, usr.client)] The ATM card reader rejected your ID because this machine has been sabotaged!")) + else + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/card/id)) + usr.drop_item(src) + held_card = I + . = TRUE + + if("logout") + if(held_card) + release_held_id(usr) + authenticated_account = null + . = TRUE + + // Balance statement + if("balance_statement") + if(!authenticated_account) + return + + var/obj/item/paper/R = new(loc) + R.name = "Account balance: [authenticated_account.owner_name]" + R.info = "NT Automated Teller Account Statement

" + R.info += "Account holder: [authenticated_account.owner_name]
" + R.info += "Account number: [authenticated_account.account_number]
" + R.info += "Balance: $[authenticated_account.money]
" + R.info += "Date and time: [stationtime2text()], [current_date_string]

" + R.info += "Service terminal ID: [machine_id]
" + + //stamp the paper + var/image/stampoverlay = image('icons/obj/bureaucracy.dmi') + stampoverlay.icon_state = "paper_stamp-cent" + if(!R.stamped) + R.stamped = new + R.stamped += /obj/item/stamp + R.add_overlay(stampoverlay) + R.stamps += "
This paper has been stamped by the Automatic Teller Machine." + + if(prob(50)) + playsound(src, 'sound/items/polaroid1.ogg', 50, 1) + else + playsound(src, 'sound/items/polaroid2.ogg', 50, 1) + . = TRUE + + // Transaction logs + if("print_transaction") + if(!authenticated_account) + return + + var/obj/item/paper/R = new(loc) + R.name = "Transaction logs: [authenticated_account.owner_name]" + R.info = "Transaction logs
" + R.info += "Account holder: [authenticated_account.owner_name]
" + R.info += "Account number: [authenticated_account.account_number]
" + R.info += "Date and time: [stationtime2text()], [current_date_string]

" + R.info += "Service terminal ID: [machine_id]
" + R.info += "" + R.info += "" + R.info += "" + R.info += "" + R.info += "" + R.info += "" + R.info += "" + R.info += "" + R.info += "" + for(var/datum/transaction/T in authenticated_account.transaction_log) + R.info += "" + R.info += "" + R.info += "" + R.info += "" + R.info += "" + R.info += "" + R.info += "" + R.info += "" + R.info += "
DateTimeTargetPurposeValueSource terminal ID
[T.date][T.time][T.target_name][T.purpose]$[T.amount][T.source_terminal]
" + + //stamp the paper + var/image/stampoverlay = image('icons/obj/bureaucracy.dmi') + stampoverlay.icon_state = "paper_stamp-cent" + if(!R.stamped) + R.stamped = new + R.stamped += /obj/item/stamp + R.add_overlay(stampoverlay) + R.stamps += "
This paper has been stamped by the Automatic Teller Machine." + + if(prob(50)) + playsound(src, 'sound/items/polaroid1.ogg', 50, 1) + else + playsound(src, 'sound/items/polaroid2.ogg', 50, 1) + . = TRUE + + if("change_security_level") + if(authenticated_account) + var/new_sec_level = clamp(text2num(params["new_security_level"]), 0, 2) + authenticated_account.security_level = new_sec_level + . = TRUE + + if("attempt_auth") + if(ticks_left_locked_down) + return + var/tried_account_num = held_card ? held_card.associated_account_number : text2num(params["account_num"]) + var/tried_pin = text2num(params["account_pin"]) + + // check if they have low security enabled + if(!tried_account_num) + scan_user(usr) + else + authenticated_account = attempt_account_access(tried_account_num, tried_pin, held_card && held_card.associated_account_number == tried_account_num ? 2 : 1) + + if(!authenticated_account) + number_incorrect_tries++ + if(previous_account_number == tried_account_num) + if(number_incorrect_tries > max_pin_attempts) + //lock down the atm + ticks_left_locked_down = 30 + playsound(src, 'sound/machines/buzz-two.ogg', 50, 1) + + //create an entry in the account transaction log + var/datum/money_account/failed_account = get_account(tried_account_num) + if(failed_account) + var/datum/transaction/T = new() + T.target_name = failed_account.owner_name + T.purpose = "Unauthorised login attempt" + T.source_terminal = machine_id + T.date = current_date_string + T.time = stationtime2text() + failed_account.transaction_log.Add(T) + else + to_chat(usr, span_red("[icon2html(src, usr.client)] Incorrect pin/account combination entered, [max_pin_attempts - number_incorrect_tries] attempts remaining.")) + previous_account_number = tried_account_num + playsound(src, 'sound/machines/buzz-sigh.ogg', 50, 1) + else + to_chat(usr, span_red("[icon2html(src, usr.client)] incorrect pin/account combination entered.")) + number_incorrect_tries = 0 + else + playsound(src, 'sound/machines/twobeep.ogg', 50, 1) + ticks_left_timeout = 120 + view_screen = NO_SCREEN + + //create a transaction log entry + var/datum/transaction/T = new() + T.target_name = authenticated_account.owner_name + T.purpose = "Remote terminal access" + T.source_terminal = machine_id + T.date = current_date_string + T.time = stationtime2text() + authenticated_account.transaction_log.Add(T) + + to_chat(usr, span_blue("[icon2html(src, usr.client)] Access granted. Welcome user '[authenticated_account.owner_name].'")) + + previous_account_number = tried_account_num + . = TRUE + + if("transfer") + if(!authenticated_account) + return + var/transfer_amount = text2num(params["funds_amount"]) + transfer_amount = round(transfer_amount, 0.01) + if(transfer_amount <= 0) + tgui_alert_async(usr, "That is not a valid amount.") + else if(transfer_amount <= authenticated_account.money) + var/target_account_number = text2num(params["target_acc_number"]) + var/transfer_purpose = params["purpose"] + if(charge_to_account(target_account_number, authenticated_account.owner_name, transfer_purpose, machine_id, transfer_amount)) + to_chat(usr, "[icon2html(src, usr.client)]Funds transfer successful.") + authenticated_account.money -= transfer_amount + + //create an entry in the account transaction log + var/datum/transaction/T = new() + T.target_name = "Account #[target_account_number]" + T.purpose = transfer_purpose + T.source_terminal = machine_id + T.date = current_date_string + T.time = stationtime2text() + T.amount = "([transfer_amount])" + authenticated_account.transaction_log.Add(T) + else + to_chat(usr, "[icon2html(src, usr.client)]Funds transfer failed.") + + else + to_chat(usr, "[icon2html(src, usr.client)]You don't have enough funds to do that!") + . = TRUE + + if("e_withdrawal") + var/amount = max(text2num(params["funds_amount"]),0) + amount = round(amount, 0.01) + if(amount <= 0) + tgui_alert_async(usr, "That is not a valid amount.") + return + + if(!authenticated_account) + return + + if(amount <= authenticated_account.money) + playsound(src, 'sound/machines/chime.ogg', 50, 1) + + //remove the money + authenticated_account.money -= amount + + // spawn_money(amount,src.loc) + spawn_ewallet(amount,src.loc,usr) + + //create an entry in the account transaction log + var/datum/transaction/T = new() + T.target_name = authenticated_account.owner_name + T.purpose = "Credit withdrawal" + T.amount = "([amount])" + T.source_terminal = machine_id + T.date = current_date_string + T.time = stationtime2text() + authenticated_account.transaction_log.Add(T) + else + to_chat(usr, "[icon2html(src, usr.client)]You don't have enough funds to do that!") + . = TRUE + + if("withdrawal") + var/amount = max(text2num(params["funds_amount"]),0) + amount = round(amount, 0.01) + if(amount <= 0) + tgui_alert_async(usr, "That is not a valid amount.") + return + + if(!authenticated_account) + return + + if(amount <= authenticated_account.money) + playsound(src, 'sound/machines/chime.ogg', 50, 1) + + //remove the money + authenticated_account.money -= amount + + spawn_money(amount,src.loc,usr) + + //create an entry in the account transaction log + var/datum/transaction/T = new() + T.target_name = authenticated_account.owner_name + T.purpose = "Credit withdrawal" + T.amount = "([amount])" + T.source_terminal = machine_id + T.date = current_date_string + T.time = stationtime2text() + authenticated_account.transaction_log.Add(T) + else + to_chat(usr, "[icon2html(src, usr.client)]You don't have enough funds to do that!") + . = TRUE + + if(.) + playsound(src, "keyboard", 50, TRUE) + /obj/machinery/atm/attack_hand(mob/user as mob) if(istype(user, /mob/living/silicon)) to_chat (user, span_warning("A firewall prevents you from interfacing with this device!")) return if(get_dist(src,user) <= 1) - - //js replicated from obj/machinery/computer/card - var/dat = "

Automatic Teller Machine

" - dat += "For all your monetary needs!
" - dat += "This terminal is [machine_id]. Report this code when contacting IT Support
" - - if(emagged > 0) - dat += "Card: LOCKED

Unauthorized terminal access detected! This ATM has been locked. Please contact IT Support." - else - dat += "Card: [held_card ? held_card.name : "------"]

" - - if(ticks_left_locked_down > 0) - dat += span_warning("Maximum number of pin attempts exceeded! Access to this ATM has been temporarily disabled.") - else if(authenticated_account) - if(authenticated_account.suspended) - dat += span_red(span_bold("Access to this account has been suspended, and the funds within frozen.")) - else - switch(view_screen) - if(CHANGE_SECURITY_LEVEL) - dat += "Select a new security level for this account:

" - var/text = "Zero - Either the account number or card is required to access this account. EFTPOS transactions will require a card and ask for a pin, but not verify the pin is correct." - if(authenticated_account.security_level != 0) - text = "[text]" - dat += "[text]
" - text = "One - An account number and pin must be manually entered to access this account and process transactions." - if(authenticated_account.security_level != 1) - text = "[text]" - dat += "[text]
" - text = "Two - In addition to account number and pin, a card is required to access this account and process transactions." - if(authenticated_account.security_level != 2) - text = "[text]" - dat += "[text]

" - dat += "Back" - if(VIEW_TRANSACTION_LOGS) - dat += "Transaction logs
" - dat += "Back" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - for(var/datum/transaction/T in authenticated_account.transaction_log) - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "
DateTimeTargetPurposeValueSource terminal ID
[T.date][T.time][T.target_name][T.purpose]$[T.amount][T.source_terminal]
" - dat += "Print
" - if(TRANSFER_FUNDS) - dat += "Account balance: $[authenticated_account.money]
" - dat += "Back

" - dat += "
" - dat += "" - dat += "" - dat += "Target account number:
" - dat += "Funds to transfer:
" - dat += "Transaction purpose:
" - dat += "
" - dat += "
" - else - dat += "Welcome, [authenticated_account.owner_name].
" - dat += "Account balance: $[authenticated_account.money]" - dat += "
" - dat += "" - dat += " Cash Chargecard
" - dat += "" - dat += "
" - dat += "Change account security level
" - dat += "Make transfer
" - dat += "View transaction log
" - dat += "Print balance statement
" - dat += "Logout
" - else - dat += "
" - dat += "" - dat += "" - dat += "Account:
" - dat += "PIN:
" - dat += "
" - dat += "
" - - user << browse(dat,"window=atm;size=550x650") - else - user << browse(null,"window=atm") - -/obj/machinery/atm/Topic(var/href, var/href_list) - if(href_list["choice"]) - switch(href_list["choice"]) - if("transfer") - if(authenticated_account) - var/transfer_amount = text2num(href_list["funds_amount"]) - transfer_amount = round(transfer_amount, 0.01) - if(transfer_amount <= 0) - tgui_alert_async(usr, "That is not a valid amount.") - else if(transfer_amount <= authenticated_account.money) - var/target_account_number = text2num(href_list["target_acc_number"]) - var/transfer_purpose = href_list["purpose"] - if(charge_to_account(target_account_number, authenticated_account.owner_name, transfer_purpose, machine_id, transfer_amount)) - to_chat(usr, "[icon2html(src, usr.client)]Funds transfer successful.") - authenticated_account.money -= transfer_amount - - //create an entry in the account transaction log - var/datum/transaction/T = new() - T.target_name = "Account #[target_account_number]" - T.purpose = transfer_purpose - T.source_terminal = machine_id - T.date = current_date_string - T.time = stationtime2text() - T.amount = "([transfer_amount])" - authenticated_account.transaction_log.Add(T) - else - to_chat(usr, "[icon2html(src, usr.client)]Funds transfer failed.") - - else - to_chat(usr, "[icon2html(src, usr.client)]You don't have enough funds to do that!") - if("view_screen") - view_screen = text2num(href_list["view_screen"]) - if("change_security_level") - if(authenticated_account) - var/new_sec_level = max( min(text2num(href_list["new_security_level"]), 2), 0) - authenticated_account.security_level = new_sec_level - if("attempt_auth") - if(!ticks_left_locked_down) - var/tried_account_num = held_card ? held_card.associated_account_number : text2num(href_list["account_num"]) - var/tried_pin = text2num(href_list["account_pin"]) - - // check if they have low security enabled - - if (!tried_account_num) - scan_user(usr) - else - authenticated_account = attempt_account_access(tried_account_num, tried_pin, held_card && held_card.associated_account_number == tried_account_num ? 2 : 1) - - if(!authenticated_account) - number_incorrect_tries++ - if(previous_account_number == tried_account_num) - if(number_incorrect_tries > max_pin_attempts) - //lock down the atm - ticks_left_locked_down = 30 - playsound(src, 'sound/machines/buzz-two.ogg', 50, 1) - - //create an entry in the account transaction log - var/datum/money_account/failed_account = get_account(tried_account_num) - if(failed_account) - var/datum/transaction/T = new() - T.target_name = failed_account.owner_name - T.purpose = "Unauthorised login attempt" - T.source_terminal = machine_id - T.date = current_date_string - T.time = stationtime2text() - failed_account.transaction_log.Add(T) - else - to_chat(usr, span_red("[icon2html(src, usr.client)] Incorrect pin/account combination entered, [max_pin_attempts - number_incorrect_tries] attempts remaining.")) - previous_account_number = tried_account_num - playsound(src, 'sound/machines/buzz-sigh.ogg', 50, 1) - else - to_chat(usr, span_red("[icon2html(src, usr.client)] incorrect pin/account combination entered.")) - number_incorrect_tries = 0 - else - playsound(src, 'sound/machines/twobeep.ogg', 50, 1) - ticks_left_timeout = 120 - view_screen = NO_SCREEN - - //create a transaction log entry - var/datum/transaction/T = new() - T.target_name = authenticated_account.owner_name - T.purpose = "Remote terminal access" - T.source_terminal = machine_id - T.date = current_date_string - T.time = stationtime2text() - authenticated_account.transaction_log.Add(T) - - to_chat(usr, span_blue("[icon2html(src, usr.client)] Access granted. Welcome user '[authenticated_account.owner_name].'")) - - previous_account_number = tried_account_num - if("e_withdrawal") - var/amount = max(text2num(href_list["funds_amount"]),0) - amount = round(amount, 0.01) - if(amount <= 0) - tgui_alert_async(usr, "That is not a valid amount.") - else if(authenticated_account && amount > 0) - if(amount <= authenticated_account.money) - playsound(src, 'sound/machines/chime.ogg', 50, 1) - - //remove the money - authenticated_account.money -= amount - - // spawn_money(amount,src.loc) - spawn_ewallet(amount,src.loc,usr) - - //create an entry in the account transaction log - var/datum/transaction/T = new() - T.target_name = authenticated_account.owner_name - T.purpose = "Credit withdrawal" - T.amount = "([amount])" - T.source_terminal = machine_id - T.date = current_date_string - T.time = stationtime2text() - authenticated_account.transaction_log.Add(T) - else - to_chat(usr, "[icon2html(src, usr.client)]You don't have enough funds to do that!") - if("withdrawal") - var/amount = max(text2num(href_list["funds_amount"]),0) - amount = round(amount, 0.01) - if(amount <= 0) - tgui_alert_async(usr, "That is not a valid amount.") - else if(authenticated_account && amount > 0) - if(amount <= authenticated_account.money) - playsound(src, 'sound/machines/chime.ogg', 50, 1) - - //remove the money - authenticated_account.money -= amount - - spawn_money(amount,src.loc,usr) - - //create an entry in the account transaction log - var/datum/transaction/T = new() - T.target_name = authenticated_account.owner_name - T.purpose = "Credit withdrawal" - T.amount = "([amount])" - T.source_terminal = machine_id - T.date = current_date_string - T.time = stationtime2text() - authenticated_account.transaction_log.Add(T) - else - to_chat(usr, "[icon2html(src, usr.client)]You don't have enough funds to do that!") - if("balance_statement") - if(authenticated_account) - var/obj/item/paper/R = new(src.loc) - R.name = "Account balance: [authenticated_account.owner_name]" - R.info = "NT Automated Teller Account Statement

" - R.info += "Account holder: [authenticated_account.owner_name]
" - R.info += "Account number: [authenticated_account.account_number]
" - R.info += "Balance: $[authenticated_account.money]
" - R.info += "Date and time: [stationtime2text()], [current_date_string]

" - R.info += "Service terminal ID: [machine_id]
" - - //stamp the paper - var/image/stampoverlay = image('icons/obj/bureaucracy.dmi') - stampoverlay.icon_state = "paper_stamp-cent" - if(!R.stamped) - R.stamped = new - R.stamped += /obj/item/stamp - R.add_overlay(stampoverlay) - R.stamps += "
This paper has been stamped by the Automatic Teller Machine." - - if(prob(50)) - playsound(src, 'sound/items/polaroid1.ogg', 50, 1) - else - playsound(src, 'sound/items/polaroid2.ogg', 50, 1) - if ("print_transaction") - if(authenticated_account) - var/obj/item/paper/R = new(src.loc) - R.name = "Transaction logs: [authenticated_account.owner_name]" - R.info = "Transaction logs
" - R.info += "Account holder: [authenticated_account.owner_name]
" - R.info += "Account number: [authenticated_account.account_number]
" - R.info += "Date and time: [stationtime2text()], [current_date_string]

" - R.info += "Service terminal ID: [machine_id]
" - R.info += "" - R.info += "" - R.info += "" - R.info += "" - R.info += "" - R.info += "" - R.info += "" - R.info += "" - R.info += "" - for(var/datum/transaction/T in authenticated_account.transaction_log) - R.info += "" - R.info += "" - R.info += "" - R.info += "" - R.info += "" - R.info += "" - R.info += "" - R.info += "" - R.info += "
DateTimeTargetPurposeValueSource terminal ID
[T.date][T.time][T.target_name][T.purpose]$[T.amount][T.source_terminal]
" - - //stamp the paper - var/image/stampoverlay = image('icons/obj/bureaucracy.dmi') - stampoverlay.icon_state = "paper_stamp-cent" - if(!R.stamped) - R.stamped = new - R.stamped += /obj/item/stamp - R.add_overlay(stampoverlay) - R.stamps += "
This paper has been stamped by the Automatic Teller Machine." - - if(prob(50)) - playsound(src, 'sound/items/polaroid1.ogg', 50, 1) - else - playsound(src, 'sound/items/polaroid2.ogg', 50, 1) - - if("insert_card") - if(!held_card) - //this might happen if the user had the browser window open when somebody emagged it - if(emagged > 0) - to_chat(usr, span_red("[icon2html(src, usr.client)] The ATM card reader rejected your ID because this machine has been sabotaged!")) - else - var/obj/item/I = usr.get_active_hand() - if (istype(I, /obj/item/card/id)) - usr.drop_item() - I.loc = src - held_card = I - else - release_held_id(usr) - if("logout") - authenticated_account = null - //usr << browse(null,"window=atm") - - src.attack_hand(usr) + tgui_interact(user) //stolen wholesale and then edited a bit from newscasters, which are awesome and by Agouri /obj/machinery/atm/proc/scan_user(mob/living/carbon/human/human_user as mob) diff --git a/tgui/packages/tgui/interfaces/AutomatedTellerMachine.tsx b/tgui/packages/tgui/interfaces/AutomatedTellerMachine.tsx new file mode 100644 index 0000000000..ef1b517299 --- /dev/null +++ b/tgui/packages/tgui/interfaces/AutomatedTellerMachine.tsx @@ -0,0 +1,702 @@ +import { useEffect, useState } from 'react'; +import { useBackend } from 'tgui/backend'; +import { Window } from 'tgui/layouts'; +import { + Box, + Button, + DmIcon, + Icon, + Input, + LabeledList, + Section, + Stack, + Table, +} from 'tgui-core/components'; + +export type Transaction = { + date: string; + time: number; + target_name: string; + purpose: string; + amount: number; + source_terminal: string; +}; + +export type Account = { + owner_name: string; + money: number; + security_level: number; + transactions: Transaction[]; +}; + +export type AutomatedTellerMachineData = { + machine_id: string; + emagged: number; + held_card: string | null; + locked_down: number; + suspended: boolean; + authenticated_account: Account; +}; + +export const AutomatedTellerMachine = (props) => { + const { act, data } = useBackend(); + + return ( + + +
+ {data.authenticated_account ? ( + + ) : ( + + )} +
+
+
+ ); +}; + +enum Menu { + Main = 'main', + Withdraw = 'withdraw', + Balance = 'balance', + Transfer = 'transfer', + More = 'more', +} + +const AuthenticatedScreen = (props) => { + const [menu, setMenu] = useState(Menu.Main); + + switch (menu) { + case Menu.Main: { + return ; + } + case Menu.Withdraw: { + return ; + } + case Menu.Balance: { + return ; + } + case Menu.Transfer: { + return ; + } + case Menu.More: { + return ; + } + } +}; + +const MainMenu = (props: { + setMenu: React.Dispatch>; +}) => { + const { setMenu } = props; + const { act, data } = useBackend(); + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +const WithdrawMenu = (props: { + setMenu: React.Dispatch>; +}) => { + const { act, data } = useBackend(); + const { setMenu } = props; + const [custom, setCustom] = useState(false); + const [useCard, setUseCard] = useState(false); + + const withdrawFunction = (val) => { + if (useCard) { + act('e_withdrawal', { funds_amount: val }); + } else { + act('withdrawal', { funds_amount: val }); + } + setMenu(Menu.Main); + }; + + if (custom) { + return ( + + ); + } + + return ( +
setMenu(Menu.Main)}> + Back To Main Menu + + } + > + + Account Balance: ${data.authenticated_account.money} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ); +}; + +const CustomWithdrawal = (props: { + withdrawFunction: (val: number) => void; + useCard: boolean; + setCustom: React.Dispatch>; + setMenu: React.Dispatch>; +}) => { + const { act, data } = useBackend(); + const { withdrawFunction, useCard, setCustom, setMenu } = props; + const [money, setMoney] = useState(1000); + return ( +
setCustom(false)}> + Back To Withdrawals + + } + fill + > + + + + + Available: ${data.authenticated_account.money} + + + { + let value = parseInt(val, 10); + if (isNaN(value)) { + setMoney(0); + } else { + setMoney(value); + } + }} + onChange={(e, val) => { + let value = parseInt(val, 10); + if (isNaN(value)) { + setMoney(0); + } else { + setMoney(value); + } + }} + /> + + + + + + + +
+ ); +}; + +const BalanceMenu = (props: { + setMenu: React.Dispatch>; +}) => { + const { act, data } = useBackend(); + const { setMenu } = props; + return ( +
setMenu(Menu.Main)}> + Back To Main Menu + + } + fill + > + + + + Current Funds: ${data.authenticated_account.money} + + + + + + + + +
+ ); +}; + +const TransactionLog = (props: { transactions: Transaction[] }) => { + const { transactions } = props; + + return ( +
+ + + Date + Time + Target + Purpose + Value + Source Terminal ID + + {transactions.map((transaction, index) => ( + + {transaction.date} + {transaction.time} + {transaction.target_name} + {transaction.purpose} + ${transaction.amount} + {transaction.source_terminal} + + ))} +
+
+ ); +}; + +const MoreMenu = (props: { + setMenu: React.Dispatch>; +}) => { + const { act, data } = useBackend(); + const { setMenu } = props; + return ( +
setMenu(Menu.Main)}> + Back To Main Menu + + } + > + + Account Security Settings + + + + +
+ ); +}; + +const TransferMenu = (props: { + setMenu: React.Dispatch>; +}) => { + const { act, data } = useBackend(); + const [accountNum, setAccountNum] = useState(100000); + const updateAccountNum = (val) => { + let newVal = parseInt(val, 10); + if (isNaN(newVal)) { + setAccountNum(100000); + } else { + setAccountNum(newVal); + } + }; + + const [money, setMoney] = useState(0); + const updateMoney = (val) => { + let newVal = parseInt(val, 10); + if (isNaN(newVal)) { + setMoney(0); + } else { + setMoney(newVal); + } + }; + + const [purpose, setPurpose] = useState('Funds transfer'); + + const { setMenu } = props; + return ( +
setMenu(Menu.Main)}> + Back To Main Menu + + } + fill + textAlign="center" + > + + + Available: ${data.authenticated_account.money} + + + + + updateAccountNum(val)} + onInput={(e, val) => updateAccountNum(val)} + /> + + + updateMoney(val)} + onInput={(e, val) => updateMoney(val)} + /> + + + setPurpose(val)} + onInput={(e, val) => setPurpose(val)} + /> + + + + +
+ ); +}; + +const LoginScreen = (props: { machine_id: string; card: string | null }) => { + const { act } = useBackend(); + const { machine_id, card } = props; + + const [account, setAccount] = useState(''); + const [pin, setPin] = useState(''); + + return ( + + + + + + + + + + setAccount(val)} + onInput={(e, val) => setAccount(val)} + /> + + + setPin(val)} + onInput={(e, val) => setPin(val)} + /> + + + + + + + act('insert_card')} + style={{ cursor: 'pointer' }} + > + + + + + + + + + + + + + ); +}; + +export const CardSlot = (props) => { + return ( + + {/* Top */} + + {/* Faceplate */} + + {/* Slot */} + + {/* Arrows */} + + + {/* Card */} + + {/* Slot */} + + {/* Card Outline */} + + {/* Chip */} + + + + ); +}; + +export const AnimatedIDCard = (props) => { + const [glitch, setGlitch] = useState(false); + + useEffect(() => { + if (Math.random() * 100 < 1) { + // 1% chance + setGlitch(true); + } + }, []); + + return ( + + + + ); +}; diff --git a/tgui/packages/tgui/styles/interfaces/AutomatedTellerMachine.scss b/tgui/packages/tgui/styles/interfaces/AutomatedTellerMachine.scss new file mode 100644 index 0000000000..0ca797eab4 --- /dev/null +++ b/tgui/packages/tgui/styles/interfaces/AutomatedTellerMachine.scss @@ -0,0 +1,42 @@ +.AutomatedTellerMachine__Card { + animation: cardAnim 0.75s linear 0s infinite normal both; +} + +@keyframes cardAnim { + 0% { + transform: translate(0, 40px); + } + + 25% { + transform: translate(0, -32px); + } + + 100% { + transform: translate(0, 40px); + } +} + +.AutomatedTellerMachine__Card--glitch { + animation: cardAnimGlitch 0.2s linear 0s infinite normal both; + transform-origin: 'center'; +} + +@keyframes cardAnimGlitch { + 0% { + transform: translate(0, 40px) rotate(-45deg); + } + + 25% { + transform: translate(0, -32px) rotate(45deg); + } + + 100% { + transform: translate(0, 40px) rotate(-45deg); + } +} + +.AutomatedTellerMachine__Table, +.AutomatedTellerMachine__Table td { + border: 4px solid #aaa; + border-style: ridge; +} diff --git a/tgui/packages/tgui/styles/main.scss b/tgui/packages/tgui/styles/main.scss index effa918048..7945c9ca7e 100644 --- a/tgui/packages/tgui/styles/main.scss +++ b/tgui/packages/tgui/styles/main.scss @@ -45,6 +45,7 @@ @include meta.load-css('./components/Tooltip.scss'); // Interfaces +@include meta.load-css('./interfaces/AutomatedTellerMachine.scss'); @include meta.load-css('./interfaces/ListInput.scss'); @include meta.load-css('./interfaces/InputModal.scss'); @include meta.load-css('./interfaces/IntegratedCircuit.scss');