Skip to content

ui(ai): Rework the assistant's confirmation cards - #106

Merged
nfebe merged 5 commits into
devfrom
feat/agent-cards-currencies-and-admin
Jul 18, 2026
Merged

ui(ai): Rework the assistant's confirmation cards#106
nfebe merged 5 commits into
devfrom
feat/agent-cards-currencies-and-admin

Conversation

@nfebe

@nfebe nfebe commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

The counterpart to the webservice batch and currency work, plus two independent bug fixes that were in flight.

The assistant's confirmation cards named records by internal id, opened every edit field whether or not you wanted to edit, and stacked one full-height card per record. They now name what will change, stay compact until you choose to edit, and a set of changes proposed together arrives as one card confirmed or dismissed in one go.

Independent of that:

  • Three forms hardcoded their own short currency lists, so currencies the app already supported (the rupee, the real, the rupiah) could not be picked anywhere. Someone could set the rupee as their currency at onboarding and then find no wallet would take it. Every form now offers the shared list, grown to forty-eight for the places people are actually paid.
  • A transaction is limited to one category, matching the API.
  • Granting someone admin took effect on the server but not in front of them: the cached user was refreshed only when missing, never when stale, and any error while re-reading it signed them out. The admin route now re-reads on entry, and only a real rejection ends a session.
  • The readme leads with who Trakli is for.

nfebe added 2 commits July 15, 2026 22:43
Three forms carried their own hardcoded currency lists instead of the shared
one, so most of the currencies the app already knew about could not be picked
anywhere. Someone could choose the rupee as their currency during onboarding
and then find no way to open a wallet in it, because the wallet form offered
nine currencies and that was not among them. The transaction and transfer
forms build their lists from the wallets you hold, so a currency missing at
the wallet could never appear there either.

Every form now offers the shared list, which grows to cover the places people
are actually paid: India, Brazil, Indonesia, the Philippines, Vietnam,
Pakistan, Bangladesh, Turkey, Poland, Ukraine, Mexico and more. A wallet
already holding something outside the list keeps working.
A transaction could carry any number of categories, which made "what did I
spend this on" ambiguous and let one amount count under several headings at
once. Picking a category is now a single choice rather than a growing list of
chips.

Opening a transaction saved before the limit keeps the first category it had;
the rest fall away when it is saved.
@sourceant

sourceant Bot commented Jul 18, 2026

Copy link
Copy Markdown

Code Review Summary

This PR delivers a significant upgrade to the AI assistant UI, introduces batch processing for AI actions, and fixes several stability bugs related to authentication and currency handling.

🚀 Key Improvements

  • Reworked AI confirmation cards to be more compact and support batching of multiple changes.
  • Centralized the currency list to support 48 currencies across all wallet and transaction forms.
  • Fixed a bug where network errors would erroneously log out the user.
  • Ensured admin permissions are re-verified upon entering admin routes.
  • Refactored AI field editing into a dedicated component (ChatActionFields.vue).

💡 Minor Suggestions

  • Improve component reactivity by watching status props for external changes.
  • Convert initialization logic to watchers to support dynamic prop updates.

@sourceant sourceant Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review complete. See the overview comment for a summary.

// be recorded as the Unix epoch rather than defaulted server-side.
if (value === '' || value === null || value === undefined) return;
// The payload carries one category as a list, while the control is single.
out[f.key] = f.key === 'categories' ? [value] : value;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check for wrapping the category value in an array should rely on the field's type rather than the specific key name. The AI API might use various keys (e.g., category_id, categoryIds) for fields of type category, and all of them need to be wrapped as a single-item list for the current backend implementation.

Suggested change
out[f.key] = f.key === 'categories' ? [value] : value;
out[f.key] = f.type === 'category' ? [value] : value;

nfebe added 3 commits July 18, 2026 22:36
A card asked people to approve changes described by internal ids, opened every
field of an editing form whether or not anyone wanted to edit, and stacked one
full-height card per record, so a request touching a handful of transactions
scrolled for pages and cost a click each.

A card now names what it will change and shows the fields at rest, opening the
form only when someone chooses to edit. Changes proposed together arrive as a
single card carrying the count, confirmed or dismissed as one, with each entry
still reviewable on its own. Confirm and dismiss are quieter, the striped edge
is gone, and status reads as a chip rather than a coloured border.
Granting admin took effect on the server but not in front of the person: the
signed-in user is cached in a week-long cookie, and every path that refreshed
it only did so when it was missing entirely, never when it was merely out of
date. So a new admin saw no admin button and was turned away from the admin
area until the cookie expired or they signed out and back in. The admin route
now asks the server who they are on the way in.

A failed check also signed people out. Any error at all while re-reading the
user, a server hiccup or a dropped connection, cleared their session and left
them at the login screen with nothing explaining why. Only an actual
rejection ends a session now; everything else leaves them signed in and
surfaces the error.
The readme said only that this is the web UI for Trakli, so a reader learned
nothing about whether Trakli was for them before deciding to move on. The list
under it put the things nobody else does between the things everybody does.

It now opens with the person it is for, whose money is spread across cash,
mobile money and more than one currency, and whom every other app quietly
cannot serve. The list leads with what is ours, and the backend it needs is
named up front rather than discovered at the setup step.
@nfebe
nfebe force-pushed the feat/agent-cards-currencies-and-admin branch from 57ca992 to af8944a Compare July 18, 2026 21:37
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 18, 2026

Copy link
Copy Markdown

Deploying trakli-dev with  Cloudflare Pages  Cloudflare Pages

Latest commit: af8944a
Status: ✅  Deploy successful!
Preview URL: https://3c0206fc.trakli-dev.pages.dev
Branch Preview URL: https://feat-agent-cards-currencies.trakli-dev.pages.dev

View logs

@sourceant sourceant Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review complete. See the overview comment for a summary.

const props = withDefaults(
defineProps<{ block: ProposedActionBlock; sessionId: number; nested?: boolean }>(),
{ nested: false }
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the ChatResultRenderer now uses unique IDs as keys (action-${block.id}), the component instance is reused when the block data is updated. Because state is a ref initialized once, it won't reflect status changes from the prop (e.g., if the action was executed by another part of the system) unless we watch the prop.

Suggested change
);
const getStatus = (s: string) => {
if (s === 'executed') return 'executed';
if (s === 'rejected') return 'rejected';
if (s === 'failed') return 'failed';
return 'pending';
};
const state = ref<'pending' | 'executed' | 'rejected' | 'failed'>(getStatus(props.block.status));
watch(() => props.block.status, (newStatus) => {
state.value = getStatus(newStatus);
});

const open = ref(false);
const busy = ref(false);

const state = ref<'pending' | 'executed' | 'rejected' | 'failed'>(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like the single action block, the batch block should watch its status prop to remain reactive if the server-side status changes while the component is mounted.

Suggested change
const state = ref<'pending' | 'executed' | 'rejected' | 'failed'>(
const getStatus = (s: string) => {
if (s === 'executed') return 'executed';
if (s === 'rejected') return 'rejected';
if (s === 'failed') return 'failed';
return 'pending';
};
const state = ref<'pending' | 'executed' | 'rejected' | 'failed'>(getStatus(props.block.status));
watch(() => props.block.status, (newStatus) => {
state.value = getStatus(newStatus);
});

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying webui with  Cloudflare Pages  Cloudflare Pages

Latest commit: af8944a
Status: ✅  Deploy successful!
Preview URL: https://c063c003.webui-9fh.pages.dev
Branch Preview URL: https://feat-agent-cards-currencies.webui-9fh.pages.dev

View logs

@nfebe
nfebe merged commit 941c96d into dev Jul 18, 2026
6 checks passed
@nfebe
nfebe deleted the feat/agent-cards-currencies-and-admin branch July 18, 2026 21:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant