Merge pull request #14799 from variableundefined/tgui-atm

[TGUI] Convert ATM to TGUI
This commit is contained in:
AffectedArc07
2020-11-01 17:34:38 +00:00
committed by GitHub
6 changed files with 542 additions and 303 deletions
+153 -143
View File
@@ -7,11 +7,12 @@ log transactions
*/
#define NO_SCREEN 0
#define DEFAULT_SCREEN 0
#define CHANGE_SECURITY_LEVEL 1
#define TRANSFER_FUNDS 2
#define VIEW_TRANSACTION_LOGS 3
#define PRINT_DELAY 100
#define LOCKOUT_TIME 120
/obj/machinery/atm
name = "Nanotrasen automatic teller machine"
@@ -31,7 +32,7 @@ log transactions
var/machine_id = ""
var/obj/item/card/held_card
var/editing_security_level = 0
var/view_screen = NO_SCREEN
var/view_screen = DEFAULT_SCREEN
var/lastprint = 0 // Printer needs time to cooldown
/obj/machinery/atm/New()
@@ -97,7 +98,7 @@ log transactions
if(!powered())
return
var/obj/item/stack/spacecash/C = I
playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 50, 1)
playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 50, TRUE)
authenticated_account.credit(C.amount, "Credit deposit", machine_id, authenticated_account.owner_name)
@@ -115,21 +116,19 @@ log transactions
return
if(!linked_db)
reconnect_database()
ui_interact(user)
tgui_interact(user)
/obj/machinery/atm/attack_ghost(mob/user)
ui_interact(user)
tgui_interact(user)
/obj/machinery/atm/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
user.set_machine(src)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/atm/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, "atm.tmpl", name, 550, 650)
ui = new(user, src, ui_key, "ATM", name, 550, 650)
ui.open()
/obj/machinery/atm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
var/data[0]
data["src"] = UID()
/obj/machinery/atm/tgui_data(mob/user)
var/list/data = list()
data["view_screen"] = view_screen
data["machine_id"] = machine_id
data["held_card_name"] = held_card ? held_card.name : "------"
@@ -155,139 +154,150 @@ log transactions
return data
/obj/machinery/atm/Topic(href, list/href_list)
if(href_list["choice"])
switch(href_list["choice"])
if("transfer")
if(authenticated_account && linked_db)
var/transfer_amount = text2num(href_list["funds_amount"])
if(transfer_amount <= 0)
alert("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(linked_db.charge_to_account(target_account_number, authenticated_account, transfer_purpose, machine_id, transfer_amount))
to_chat(usr, "[bicon(src)]<span class='info'>Funds transfer successful.</span>")
else
to_chat(usr, "[bicon(src)]<span class='warning'>Funds transfer failed.</span>")
else
to_chat(usr, "[bicon(src)]<span class='warning'>You don't have enough funds to do that!</span>")
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(linked_db)
if(!ticks_left_locked_down)
var/tried_account_num = text2num(href_list["account_num"])
if(!tried_account_num && held_card)
tried_account_num = held_card.associated_account_number
var/tried_pin = text2num(href_list["account_pin"])
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 = linked_db.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 = GLOB.current_date_string
T.time = station_time_timestamp()
failed_account.transaction_log.Add(T)
else
to_chat(usr, "[bicon(src)]<span class='warning'>Incorrect pin/account combination entered, [max_pin_attempts - number_incorrect_tries] attempts remaining.</span>")
previous_account_number = tried_account_num
playsound(src, 'sound/machines/buzz-sigh.ogg', 50, 1)
else
to_chat(usr, "[bicon(src)]<span class='warning'>Incorrect pin/account combination entered.</span>")
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 = GLOB.current_date_string
T.time = station_time_timestamp()
authenticated_account.transaction_log.Add(T)
to_chat(usr, "[bicon(src)]<span class='notice'>Access granted. Welcome user '[authenticated_account.owner_name].'</span>")
previous_account_number = tried_account_num
if("withdrawal")
var/amount = max(text2num(href_list["funds_amount"]),0)
if(amount <= 0)
alert("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
if(amount > 100000) // prevent crashes
to_chat(usr, "<span class='notice'>The ATM's screen flashes, 'Maximum single withdrawl limit reached, defaulting to 100,000.'</span>")
amount = 100000
withdraw_arbitrary_sum(amount)
authenticated_account.charge(amount, null, "Credit withdrawal", machine_id, authenticated_account.owner_name)
else
to_chat(usr, "[bicon(src)]<span class='warning'>You don't have enough funds to do that!</span>")
if("balance_statement")
if(authenticated_account)
if(world.timeofday < lastprint + PRINT_DELAY)
to_chat(usr, "<span class='notice'>The [name] flashes an error on its display.</span>")
return
lastprint = world.timeofday
playsound(loc, 'sound/goonstation/machines/printer_thermal.ogg', 50, 1)
var/obj/item/paper/R = new(loc)
R.name = "Account balance: [authenticated_account.owner_name]"
R.info = {"<b>NT Automated Teller Account Statement</b><br><br>
<i>Account holder:</i> [authenticated_account.owner_name]<br>
<i>Account number:</i> [authenticated_account.account_number]<br>
<i>Balance:</i> $[authenticated_account.money]<br>
<i>Date and time:</i> [station_time_timestamp()], [GLOB.current_date_string]<br><br>
<i>Service terminal ID:</i> [machine_id]<br>"}
//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.overlays += stampoverlay
R.stamps += "<HR><i>This paper has been stamped by the Automatic Teller Machine.</i>"
playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 50, 1)
if("insert_card")
if(held_card)
held_card.forceMove(loc)
authenticated_account = null
if(ishuman(usr) && !usr.get_active_hand())
usr.put_in_hands(held_card)
held_card = null
/obj/machinery/atm/tgui_act(action, params)
switch(action)
if("transfer")
if(!authenticated_account || !linked_db)
return
var/transfer_amount = text2num(params["funds_amount"])
if(transfer_amount <= 0)
to_chat(usr, "[bicon(src)]<span class='warning'>That is not a valid amount.</span>")
else if(transfer_amount <= authenticated_account.money)
var/target_account_number = text2num(params["target_acc_number"])
var/transfer_purpose = params["purpose"]
if(linked_db.charge_to_account(target_account_number, authenticated_account, transfer_purpose, machine_id, transfer_amount))
to_chat(usr, "[bicon(src)]<span class='info'>Funds transfer successful.</span>")
else
var/obj/item/I = usr.get_active_hand()
if(istype(I, /obj/item/card/id))
usr.drop_item()
I.forceMove(src)
held_card = I
if("logout")
authenticated_account = null
to_chat(usr, "[bicon(src)]<span class='warning'>Funds transfer failed.</span>")
else
to_chat(usr, "[bicon(src)]<span class='warning'>You don't have enough funds to do that!</span>")
SSnanoui.update_uis(src)
return 1
if("view_screen")
var/list/valid_screen = list(DEFAULT_SCREEN, CHANGE_SECURITY_LEVEL, TRANSFER_FUNDS, VIEW_TRANSACTION_LOGS)
var/screen_proper = text2num(params["view_screen"])
if(screen_proper in valid_screen)
view_screen = screen_proper
else
message_admins("Warning: possible href exploit by [key_name(usr)] - Invalid screen number passed into an ATM")
log_debug("Warning: possible href exploit by [key_name(usr)] - Invalid screen number passed into an ATM")
if("change_security_level")
if(authenticated_account)
var/new_sec_level = max(min(text2num(params["new_security_level"]), 2), 0)
authenticated_account.security_level = new_sec_level
if("attempt_auth")
if(linked_db)
if(!ticks_left_locked_down)
var/tried_account_num = text2num(params["account_num"])
if(!tried_account_num && held_card)
tried_account_num = held_card.associated_account_number
var/tried_pin = text2num(params["account_pin"])
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, TRUE)
//create an entry in the account transaction log
var/datum/money_account/failed_account = linked_db.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 = GLOB.current_date_string
T.time = station_time_timestamp()
failed_account.transaction_log.Add(T)
else
to_chat(usr, "[bicon(src)]<span class='warning'>Incorrect pin/account combination entered, [max_pin_attempts - number_incorrect_tries] attempt\s remaining.</span>")
previous_account_number = tried_account_num
playsound(src, 'sound/machines/buzz-sigh.ogg', 50, TRUE)
else
to_chat(usr, "[bicon(src)]<span class='warning'>Incorrect pin/account combination entered.</span>")
number_incorrect_tries = 0
else
playsound(src, 'sound/machines/twobeep.ogg', 50, TRUE)
ticks_left_timeout = LOCKOUT_TIME
view_screen = DEFAULT_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 = GLOB.current_date_string
T.time = station_time_timestamp()
authenticated_account.transaction_log.Add(T)
to_chat(usr, "[bicon(src)]<span class='notice'>Access granted. Welcome user '[authenticated_account.owner_name].'</span>")
previous_account_number = tried_account_num
if("withdrawal")
var/amount = max(text2num(params["funds_amount"]),0)
if(amount <= 0)
to_chat(usr, "[bicon(src)]<span class='warning'>That is not a valid amount.</span>")
else if(authenticated_account && amount > 0)
if(amount <= authenticated_account.money)
playsound(src, 'sound/machines/chime.ogg', 50, TRUE)
//remove the money
if(amount > 100000) // prevent crashes
to_chat(usr, "<span class='notice'>The ATM's screen flashes, 'Maximum single withdrawl limit reached, defaulting to 100,000.'</span>")
amount = 100000
withdraw_arbitrary_sum(amount)
authenticated_account.charge(amount, null, "Credit withdrawal", machine_id, authenticated_account.owner_name)
else
to_chat(usr, "[bicon(src)]<span class='warning'>You don't have enough funds to do that!</span>")
if("balance_statement")
if(authenticated_account)
if(world.timeofday < lastprint + PRINT_DELAY)
to_chat(usr, "<span class='notice'>The [name] flashes an error on its display.</span>")
return
lastprint = world.timeofday
playsound(loc, 'sound/goonstation/machines/printer_thermal.ogg', 50, TRUE)
var/obj/item/paper/R = new(loc)
R.name = "Account balance: [authenticated_account.owner_name]"
R.info = {"<b>NT Automated Teller Account Statement</b><br><br>
<i>Account holder:</i> [authenticated_account.owner_name]<br>
<i>Account number:</i> [authenticated_account.account_number]<br>
<i>Balance:</i> $[authenticated_account.money]<br>
<i>Date and time:</i> [station_time_timestamp()], [GLOB.current_date_string]<br><br>
<i>Service terminal ID:</i> [machine_id]<br>"}
//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.overlays += stampoverlay
R.stamps += "<HR><i>This paper has been stamped by the Automatic Teller Machine.</i>"
playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 50, TRUE)
if("insert_card")
if(held_card)
held_card.forceMove(loc)
authenticated_account = null
if(ishuman(usr) && !usr.get_active_hand())
usr.put_in_hands(held_card)
held_card = null
else
var/obj/item/I = usr.get_active_hand()
if(istype(I, /obj/item/card/id))
usr.drop_item()
I.forceMove(src)
held_card = I
if("logout")
authenticated_account = null
. = TRUE
//create the most effective combination of notes to make up the requested amount
/obj/machinery/atm/proc/withdraw_arbitrary_sum(arbitrary_sum)
+3 -3
View File
@@ -19,7 +19,7 @@ GLOBAL_LIST_EMPTY(all_money_accounts)
GLOB.station_account = new()
GLOB.station_account.owner_name = "[station_name()] Station Account"
GLOB.station_account.account_number = rand(111111, 999999)
GLOB.station_account.remote_access_pin = rand(1111, 111111)
GLOB.station_account.remote_access_pin = rand(111111, 999999)
GLOB.station_account.money = STATION_START_CASH
//create an entry in the account transaction log for when it was created
@@ -35,7 +35,7 @@ GLOBAL_LIST_EMPTY(all_money_accounts)
var/datum/money_account/department_account = new()
department_account.owner_name = "[department] Account"
department_account.account_number = rand(111111, 999999)
department_account.remote_access_pin = rand(1111, 111111)
department_account.remote_access_pin = rand(111111, 999999)
department_account.money = DEPARTMENT_START_CASH
//create an entry in the account transaction log for when it was created
@@ -55,7 +55,7 @@ GLOBAL_LIST_EMPTY(all_money_accounts)
//create a new account
var/datum/money_account/M = new()
M.owner_name = new_owner_name
M.remote_access_pin = rand(1111, 111111)
M.remote_access_pin = rand(111111, 999999)
M.money = starting_funds
//create an entry in the account transaction log for when it was created
-149
View File
@@ -1,149 +0,0 @@
<!--
Title: ATM
Used In File(s): /code/modules/economy/ATM.dm
-->
<!--
#define NO_SCREEN 0
#define CHANGE_SECURITY_LEVEL 1
#define TRANSFER_FUNDS 2
#define VIEW_TRANSACTION_LOGS 3
-->
<style type="text/css">
input[type=text], input[type=submit] {
border: 1px solid #161616;
background-color: #40628a;
color: #FFFFFF;
padding: 4px;
}
input[type=submit]:hover {
background-color: #507aac;
}
</style>
<h1>Nanotrasen Automatic Teller Machine</h1>
For all your monetary needs!
<br>
<i>This terminal is</i> {{:data.machine_id}}. <i>Report this code when contacting Nanotrasen IT Support</i>
<br>
<div class="item">
<div class='itemLabel'>Card:</div>
<div class='itemContent'>{{:helper.link(data.held_card_name, 'eject', {'choice' : 'insert_card'})}}</div>
</div>
<hr>
{{if data.ticks_left_locked_down > 0}}
<span class='exclamation-triangle'>Maximum number of pin attempts exceeded! Access to this ATM has been temporarily disabled.</span>
{{else data.authenticated_account}}
{{if data.view_screen == 1}} <!-- CHANGE_SECURITY_LEVEL -->
<h4>Select a new security level for this account:</h4>
<div class="item">
<div class='itemLabel'>Level:</div>
<div class='itemContent'>{{:helper.link('Zero', 'unlock', {'choice' : 'change_security_level', 'new_security_level' : 0}, data.security_level == 0 ? 'selected' : null)}}</div>
</div>
<div class="item">
<div class='itemLabel'>Description:</div>
<div class='itemContent'>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.</div>
</div>
<hr>
<div class="item">
<div class='itemLabel'>Level:</div>
<div class='itemContent'>{{:helper.link('One', 'unlock-alt', {'choice' : 'change_security_level', 'new_security_level' : 1}, data.security_level == 1 ? 'selected' : null)}}</div>
</div>
<div class="item">
<div class='itemLabel'>Description:</div>
<div class='itemContent'>An account number and pin must be manually entered to access this account and process transactions.</div>
</div>
<hr>
<div class="item">
<div class='itemLabel'>Level:</div>
<div class='itemContent'>{{:helper.link('Two', 'lock', {'choice' : 'change_security_level', 'new_security_level' : 2}, data.security_level == 2 ? 'selected' : null)}}</div>
</div>
<div class="item">
<div class='itemLabel'>Description:</div>
<div class='itemContent'>In addition to account number and pin, a card is required to access this account and process transactions.</div>
</div>
<hr>
{{:helper.link('Back', 'arrow-left', {'choice' : 'view_screen', 'view_screen' : 0})}}
{{else data.view_screen == 2}} <!-- TRANSFER_FUNDS -->
<div class='line'><b>Account balance:</b> ${{:helper.smoothRound(data.money)}}</div>
<form name='transfer' id='transfer' action='?src={{:data.src}}' method='get'>
<input type='hidden' name='src' value='{{:data.src}}'>
<input type='hidden' name='choice' value='transfer'>
<div class='itemLabel'>Target account number:</div>
<div class='itemContent'><input type='text' name='target_acc_number' value='' style='width:200px;'></div>
<div class='itemLabel'>Funds to transfer:</div>
<div class='itemContent'><input type='text' name='funds_amount' value='' style='width:200px;'></div>
<div class='itemLabel'>Transaction purpose:</div>
<div class='itemContent'><input type='text' name='purpose' value='Funds transfer' style='width:200px;'></div>
<input type='submit' value='Transfer funds'>
</form>
<hr>
{{:helper.link('Back', 'arrow-left', {'choice' : 'view_screen', 'view_screen' : 0})}}
{{else data.view_screen == 3}} <!-- VIEW_TRANSACTION_LOGS -->
<h4>Transaction logs</h4>
<div class='item'>{{:helper.link('Back', 'arrow-left', {'choice' : 'view_screen', 'view_screen' : 0})}}</div>
<div class="statusDisplay" style="overflow: auto;">
{{if data.transaction_log}}
<table style='width: 100%'>
<thead>
<tr>
<th><b>Timestamp</b></th>
<th><b>Target</b></th>
<th><b>Reason</b></th>
<th><b>Value</b></th>
<th><b>Terminal</b></th>
</tr>
</thead>
<tbody>
{{for data.transaction_log}}
<tr>
<td>{{:value.date}} {{:value.time}}</td>
<td>{{:value.target_name}}</td>
<td>{{:value.purpose}}</td>
<td>${{:value.amount}}</td>
<td>{{:value.source_terminal}}</td>
</tr>
{{/for}}
</tbody>
</table>
{{else}}
<span class='exclamation-triangle'>Your account has no financial transactions on record for today.</span>
{{/if}}
</div>
{{else}} <!-- NO_SCREEN -->
Welcome, <b>{{:data.owner_name}}</b>.<br>
<b>Account balance:</b> ${{:helper.smoothRound(data.money)}}
<form name='withdrawal' action='?src={{:data.src}}' method='get'>
<input type='hidden' name='src' value='{{:data.src}}'>
<input type='hidden' name='choice' value='withdrawal'>
<input type='text' name='funds_amount' value=''> <input type='submit' value='Withdraw funds'><br>
</form>
<hr>
<h3>Menu</h3>
<div class="line">{{:helper.link('Change account security level', 'lock', {'choice' : 'view_screen', 'view_screen' : 1})}}</div>
<div class="line">{{:helper.link('Make transfer', 'exchange', {'choice' : 'view_screen', 'view_screen' : 2})}}</div>
<div class="line">{{:helper.link('View transaction log', 'list', {'choice' : 'view_screen', 'view_screen' : 3})}}</div>
<div class="line">{{:helper.link('Print balance statement', 'print', {'choice' : 'balance_statement'})}}</div>
<div class="line">{{:helper.link('Logout', 'sign-out', {'choice' : 'logout'})}}</div>
{{/if}}
{{else data.linked_db}}
<form name='atm_auth' action='?src={{:data.src}}' method='get'>
<input type='hidden' name='src' value='{{:data.src}}'>
<input type='hidden' name='choice' value='attempt_auth'>
<div class='itemLabel'>Account:</div>
<div class='itemContent'><input type='text' id='account_num' name='account_num' style='width:250px;'></div>
<div class='itemLabel'>PIN:</div>
<div class='itemContent'><input type='text' id='account_pin' name='account_pin' style='width:250px;'></div>
<br>
<input type='submit' value='Submit'>
</form>
{{else}}
<span class='exclamation-triangle'>Unable to connect to accounts database, please retry and if the issue persists contact Nanotrasen IT support.</span>
{{/if}}
+380
View File
@@ -0,0 +1,380 @@
import { Fragment } from 'inferno';
import { useBackend, useLocalState } from '../backend';
import { Button, Table, Icon, Input, Divider, Box, LabeledList, Section } from '../components';
import { Window } from '../layouts';
/*
#define NO_SCREEN 0
#define CHANGE_SECURITY_LEVEL 1
#define TRANSFER_FUNDS 2
#define VIEW_TRANSACTION_LOGS 3
*/
export const ATM = (props, context) => {
const { act, data } = useBackend(context);
const {
view_screen,
authenticated_account,
ticks_left_locked_down,
linked_db,
} = data;
let body;
if (ticks_left_locked_down > 0) {
body = (
<Box bold color="bad">
<Icon name="exclamation-triangle" />
Maximum number of pin attempts exceeded! Access to this ATM has been temporarily disabled.
</Box>
);
} else if (!linked_db) {
body = (
<Box bold color="bad">
<Icon name="exclamation-triangle" />
Unable to connect to accounts database, please retry and if the issue persists contact Nanotrasen IT support.
</Box>
);
} else if (authenticated_account) {
switch (view_screen) {
case 1: // CHANGE_SECURITY_LEVEL
body = <ChangeSecurityLevel />;
break;
case 2: // TRANSFER_FUNDS
body = <TransferFunds />;
break;
case 3: // VIEW_TRANSACTION_LOGS
body = <ViewTransactionLogs />;
break;
default:
body = <DefaultScreen />;
}
}
else {
body = <LoginScreen />;
}
return (
<Window resizable>
<Window.Content scrollable>
<IntroductionAndCard />
<Section>
{body}
</Section>
</Window.Content>
</Window>
);
};
const IntroductionAndCard = (props, context) => {
const { act, data } = useBackend(context);
const {
machine_id,
held_card_name,
} = data;
return (
<Section title="Nanotrasen Automatic Teller Machine" >
<Box>
For all your monetary need!
</Box>
<Divider />
<Box>
<Icon name="info-circle" /> This terminal is <i>{machine_id}</i>, report this code when contacting Nanotrasen IT Support.
</Box>
<Divider />
<LabeledList>
<LabeledList.Item label="Card">
<Button
content={held_card_name}
icon="eject"
onClick={
() => act('insert_card')
}
/>
</LabeledList.Item>
</LabeledList>
</Section>
);
};
const ChangeSecurityLevel = (props, context) => {
const { act, data } = useBackend(context);
const {
security_level,
} = data;
return (
<Section title="Select a new security level for this account" >
<LabeledList>
<Divider />
<LabeledList.Item label="Level">
<Button
content="Zero"
icon="unlock"
selected={security_level === 0}
onClick={
() => act('change_security_level', { new_security_level: 0 })
}
/>
</LabeledList.Item>
<LabeledList.Item label="Description">
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.
</LabeledList.Item>
<Divider />
<LabeledList.Item label="Level">
<Button
content="One"
icon="unlock"
selected={security_level === 1}
onClick={
() => act('change_security_level', { new_security_level: 1 })
}
/>
</LabeledList.Item>
<LabeledList.Item label="Description">
An account number and pin must be manually entered to access this account and process transactions.
</LabeledList.Item>
<Divider />
<LabeledList.Item label="Level">
<Button
content="Two"
selected={security_level === 2}
icon="unlock"
onClick={
() => act('change_security_level', { new_security_level: 2 })
}
/>
</LabeledList.Item>
<LabeledList.Item label="Description">
In addition to account number and pin, a card is required to access this account and process transactions.
</LabeledList.Item>
</LabeledList>
<Divider />
<BackButton />
</Section>
);
};
const TransferFunds = (props, context) => {
const { act, data } = useBackend(context);
const [targetAccNumber, setTargetAccNumber] = useLocalState(context, "targetAccNumber", 0);
const [fundsAmount, setFundsAmount] = useLocalState(context, "fundsAmount", 0);
const [purpose, setPurpose] = useLocalState(context, "purpose", 0);
const {
money,
} = data;
return (
<Section title="Transfer Fund" >
<LabeledList>
<LabeledList.Item label="Account Balance">
${money}
</LabeledList.Item>
<LabeledList.Item label="Target account number">
<Input
placeholder="6 Digit Number"
onInput={(e, value) => setTargetAccNumber(value)}
/>
</LabeledList.Item>
<LabeledList.Item label="Funds to transfer">
<Input
onInput={(e, value) => setFundsAmount(value)}
/>
</LabeledList.Item>
<LabeledList.Item label="Transaction Purpose">
<Input
fluid
onInput={(e, value) => setPurpose(value)}
/>
</LabeledList.Item>
</LabeledList>
<Divider />
<Button
content="Transfer"
icon="sign-out-alt"
onClick={
() => act('transfer', { target_acc_number: targetAccNumber, funds_amount: fundsAmount, purpose: purpose })
} />
<Divider />
<BackButton />
</Section>
);
};
const DefaultScreen = (props, context) => {
const { act, data } = useBackend(context);
const [fundsAmount, setFundsAmount] = useLocalState(context, "fundsAmount", 0);
const {
owner_name,
money,
} = data;
return (
<Fragment>
<Section
title={"Welcome, " + owner_name}
buttons={
<Button
content="Logout"
icon="sign-out-alt"
onClick={
() => act('logout')
} />
}>
<LabeledList>
<LabeledList.Item
label="Account Balance">
${money}
</LabeledList.Item>
<LabeledList.Item
label="Withdrawal Amount">
<Input
onInput={(e, value) => setFundsAmount(value)} />
</LabeledList.Item>
<LabeledList.Item>
<Button
content="Withdraw Fund"
icon="sign-out-alt"
onClick={
() => act('withdrawal', { funds_amount: fundsAmount })
} />
</LabeledList.Item>
</LabeledList>
</Section>
<Section title="Menu">
<Box>
<Button
content="Change account security level"
icon="lock"
onClick={
() => act('view_screen', { view_screen: 1 })
}
/>
</Box>
<Box>
<Button
content="Make transfer"
icon="exchange-alt"
onClick={
() => act('view_screen', { view_screen: 2 })
}
/>
</Box>
<Box>
<Button
content="View transaction log"
icon="list"
onClick={
() => act('view_screen', { view_screen: 3 })
}
/>
</Box>
<Box>
<Button
content="Print balance statement"
icon="print"
onClick={
() => act('balance_statement')
}
/>
</Box>
</Section>
</Fragment>
);
};
const LoginScreen = (props, context) => {
const { act, data } = useBackend(context);
const [accountID, setAccountID] = useLocalState(context, "accountID", null);
const [accountPin, setAccountPin] = useLocalState(context, "accountPin", null);
const {
machine_id,
held_card_name,
} = data;
return (
<Section
title="Insert card or enter ID and pin to login">
<LabeledList>
<LabeledList.Item label="Account ID">
<Input
placeholder="6 Digit Number"
onInput={(e, value) => setAccountID(value)}
/>
</LabeledList.Item>
<LabeledList.Item label="Pin">
<Input
placeholder="6 Digit Number"
onInput={(e, value) => setAccountPin(value)}
/>
</LabeledList.Item>
<LabeledList.Item>
<Button
content="Login"
icon="sign-in-alt"
onClick={
() => act('attempt_auth', { account_num: accountID, account_pin: accountPin })
}
/>
</LabeledList.Item>
</LabeledList>
</Section>
);
};
const ViewTransactionLogs = (props, context) => {
const { act, data } = useBackend(context);
const {
transaction_log,
} = data;
return (
<Section title="Transactions">
<Table>
<Table.Row header>
<Table.Cell>
Timestamp
</Table.Cell>
<Table.Cell>
Target
</Table.Cell>
<Table.Cell>
Reason
</Table.Cell>
<Table.Cell>
Value
</Table.Cell>
<Table.Cell>
Terminal
</Table.Cell>
</Table.Row>
{transaction_log.map(t => (
<Table.Row key={t}>
<Table.Cell p="1rem">
{t.date} {t.time}
</Table.Cell>
<Table.Cell>
{t.target_name}
</Table.Cell>
<Table.Cell>
{t.purpose}
</Table.Cell>
<Table.Cell>
${t.amount}
</Table.Cell>
<Table.Cell>
{t.source_terminal}
</Table.Cell>
</Table.Row>
))}
</Table>
<Divider />
<BackButton />
</Section>
);
};
const BackButton = (props, context) => {
const { act, data } = useBackend(context);
return (
<Button
content="Back"
icon="sign-out-alt"
onClick={
() => act('view_screen', { view_screen: 0 })
} />
);
};
@@ -1,13 +1,11 @@
import { createSearch, decodeHtmlEntities } from 'common/string';
import { createSearch } from 'common/string';
import { Fragment } from 'inferno';
import { useBackend, useLocalState } from "../backend";
import { Box, Button, Flex, Icon, Input, LabeledList, NumberInput, Section, Table, Tabs } from '../components';
import { Button, Flex, Icon, Input, LabeledList, Section, Table } 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';
export const AccountsUplinkTerminal = (properties, context) => {
const { act, data } = useBackend(context);
@@ -193,7 +191,7 @@ const DetailedAccountInfo = (properties, context) => {
<LabeledList.Item label="Account Holder">
{owner_name}
</LabeledList.Item>
<LabeledList.Item label="Account Balnce">
<LabeledList.Item label="Account Balance">
{money}
</LabeledList.Item>
<LabeledList.Item label="Account Status" color={suspended ? "red" : "green"}>
@@ -240,7 +238,7 @@ const DetailedAccountInfo = (properties, context) => {
{t.purpose}
</Table.Cell>
<Table.Cell>
{t.amount}
${t.amount}
</Table.Cell>
<Table.Cell>
{t.source_terminal}
File diff suppressed because one or more lines are too long