first commit
This commit is contained in:
commit
8b790b7601
86 changed files with 6348 additions and 0 deletions
72
ui_kits/mobile/MobileApp.jsx
Normal file
72
ui_kits/mobile/MobileApp.jsx
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
// MobileApp — stateful navigation between screens for ONE device frame
|
||||
|
||||
function MobileApp({ initial = "queue", initialTx = null, initialEntity = "all", initialPickerOpen = false }) {
|
||||
const [screen, setScreen] = React.useState(initial);
|
||||
const [entity, setEntity] = React.useState(initialEntity);
|
||||
const [transactions, setTransactions] = React.useState(window.AKEFIN_DATA.transactions);
|
||||
const [activeTxId, setActiveTxId] = React.useState(initialTx);
|
||||
const [pickerOpen, setPickerOpen] = React.useState(initialPickerOpen);
|
||||
const tx = activeTxId ? transactions.find(t => t.id === activeTxId) : null;
|
||||
const [account, setAccount] = React.useState(tx?.suggestedAccount || null);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (tx) setAccount(tx.suggestedAccount || null);
|
||||
}, [tx?.id]);
|
||||
|
||||
const openTx = (id) => { setActiveTxId(id); setScreen("detail"); };
|
||||
const back = () => { setScreen("queue"); setActiveTxId(null); };
|
||||
const approve = () => {
|
||||
setTransactions(prev => prev.filter(t => t.id !== activeTxId));
|
||||
back();
|
||||
};
|
||||
const skip = () => {
|
||||
setTransactions(prev => prev.filter(t => t.id !== activeTxId));
|
||||
back();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="m-app">
|
||||
<div className="m-app-body">
|
||||
{screen === "queue" && <MQueueScreen transactions={transactions} entity={entity} onTap={openTx} />}
|
||||
{screen === "detail" && <MDetailScreen tx={tx} onBack={back} onApprove={approve} onSkip={skip} account={account} onOpenPicker={() => setPickerOpen(true)} />}
|
||||
{screen === "rules" && <MRulesScreen entity={entity} />}
|
||||
{screen === "ledger" && <MLedgerScreen entity={entity} />}
|
||||
{screen === "import" && <MImportScreen entity={entity} />}
|
||||
</div>
|
||||
<MTabBar active={screen === "detail" ? "queue" : screen} onChange={(s) => { setActiveTxId(null); setScreen(s); }} />
|
||||
<MAccountPicker open={pickerOpen} onClose={() => setPickerOpen(false)}
|
||||
onPick={(a) => { setAccount(a); setPickerOpen(false); }}
|
||||
current={account} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// A tiny rules screen for mobile
|
||||
function MRulesScreen({ entity }) {
|
||||
const rules = window.AKEFIN_DATA.ruleSuggestions;
|
||||
return (
|
||||
<div className="m-screen">
|
||||
<MHeader eyebrow="AI SUGGESTIONS" title="Rules" />
|
||||
<div className="m-list">
|
||||
{rules.map(r => (
|
||||
<div key={r.id} className="m-rule-card">
|
||||
<div className="m-rule-top">
|
||||
<code className="m-rule-pattern ko">"{r.pattern}"</code>
|
||||
<MConfidenceChip score={r.score} tier="llm" />
|
||||
</div>
|
||||
<div className="m-rule-map">
|
||||
<span className="m-arrow">→</span>
|
||||
<span className="mono">{r.target}</span>
|
||||
</div>
|
||||
<div className="m-rule-foot">
|
||||
<span className="m-rule-occ"><span className="num">{r.occurrences}</span> matches · 30d</span>
|
||||
<button className="m-rule-promote">Promote ✓</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Object.assign(window, { MobileApp, MRulesScreen });
|
||||
Loading…
Add table
Add a link
Reference in a new issue