From 636b0323075225c584b62719ed51e75521bb7ffb Mon Sep 17 00:00:00 2001
From: aura <nw@moneybot.cc>
Date: Tue, 17 Feb 2026 22:39:42 +0100
Subject: push source

---
 moneyjsx/src/header.tsx | 98 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 98 insertions(+)
 create mode 100644 moneyjsx/src/header.tsx

(limited to 'moneyjsx/src/header.tsx')

diff --git a/moneyjsx/src/header.tsx b/moneyjsx/src/header.tsx
new file mode 100644
index 0000000..e79e256
--- /dev/null
+++ b/moneyjsx/src/header.tsx
@@ -0,0 +1,98 @@
+import $ from 'jquery';
+import * as JSX from './jsx';
+import * as user from './user';
+import * as settings from './settings';
+import { Dropdown, DropdownItem, Spinner, addPopup } from './components';
+
+
+async function logout() {
+  await user.logout();
+}
+
+/**
+  * NavItem component
+  * takes in title and route, optionally "disabled"
+**/
+function NavItem( props: any ) {
+  const url = new URL( window.location.href );
+  var style = `height: 45px;`;
+  var onclick = () => JSX.navigate( props.route );
+  if( props.disabled || url.pathname == props.route ) {
+    style += "color: #888;pointer-events: none;";
+    onclick = null;
+  }
+
+  return <DropdownItem onclick={ onclick } style={ style }>
+    <a>[ { props.title } ]</a>
+  </DropdownItem>
+}
+
+function SettingsButton( props: any ) {
+  let openSettings = settings.openPopup;
+  let style = `height: 45px;`;
+
+  if( props.disabled ) {
+    openSettings = null;
+    style += "color: #888; pointer-events: none;";
+  }
+
+  return <DropdownItem onclick={ openSettings } style={ style }>
+    <a>[ Settings ]</a>
+  </DropdownItem>
+}
+
+function EmailInput() {
+  const sendLink = async () => {
+    const email = $( "#login-email" ).val().toString();
+    $( "#login-btn-wrapper" ).append( <Spinner /> );
+
+    const res = await user.sendLoginLink( email );
+    if( res === true ) {
+      $( "#header-login-dropdown" ).replaceWith(
+        <div style="padding: 5px">Success! Check your inbox for a login link.</div>
+      );
+    } else {
+      $( "#header-login-dropdown" ).replaceWith(
+        <div style="padding: 5px">Error sending email: { res }</div>
+      );
+    }
+  };
+
+  return <div id="header-dropdown">
+    <div id="header-login-dropdown">
+      Log in
+      <input type="text" placeholder="email" id="login-email" style="margin-bottom: 5px" />
+      <div id="login-btn-wrapper" style="display: flex; justify-content: space-between; margin-top: 5px;">
+        <button onclick={ sendLink }>
+          [ get code ]
+        </button>
+      </div>
+    </div>
+  </div>
+}
+
+function showLoginPopup() {
+  addPopup( $( <EmailInput /> ) );
+}
+
+export function Header() {
+  return <header id="header">
+    <div id="header-user">
+      { user.is_loggedin && <><b>User: </b><b id="username">{ user.settings.nickname }</b>&nbsp;</> }
+      { user.is_loggedin && <button onclick={ logout }>[ Log out ]</button> }
+      { !user.is_loggedin && <button onclick={ showLoginPopup }>[ Log in ]</button> }
+    </div>
+    <div id="header-ctrls">
+      <a style="padding-right: 10px" onclick={ () => JSX.navigate( "/support" ) }>Site Map</a>
+      <Dropdown title="≡" inline innerStyle="top: 24px; margin-left: 0" style="width: 48px" id="header-dropdown-btn">
+        {
+          user.is_loggedin ? <NavItem title="Terminal" route="/terminal" /> : <NavItem title="Terminal" route="/" disabled />
+        }
+        {
+          user.is_loggedin ? <SettingsButton /> : <SettingsButton disabled />
+        }
+      <NavItem title="Home" route="/" />
+      </Dropdown>
+    </div>
+  </header>
+}
-- 
cgit v1.2.3

