Skip to content

Conversation

@Lantum-Brendan
Copy link
Collaborator

@Lantum-Brendan Lantum-Brendan commented Nov 18, 2025

General summary

  • Align wallet creation with backend expectations and improve onboarding completion flow.

  • Auto-fill wallet & currency from the app’s default when not selected.

  • Minor UI/UX and styling tweaks across settings, tables, and forms pointing to the use of default wallet.

issue addressed: #24

Regarding issue/31: Concerning auth.token security => #31 (comment)

Screencast.From.2025-11-18.13-29-11.mp4

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Nov 18, 2025

Deploying webui with  Cloudflare Pages  Cloudflare Pages

Latest commit: a1286fe
Status: ✅  Deploy successful!
Preview URL: https://8c9302d9.webui-9fh.pages.dev
Branch Preview URL: https://issues.webui-9fh.pages.dev

View logs

@Lantum-Brendan Lantum-Brendan requested a review from nfebe November 18, 2025 12:51
@Lantum-Brendan Lantum-Brendan marked this pull request as ready for review November 18, 2025 13:38
@sourceant
Copy link

sourceant bot commented Nov 18, 2025

Code Review Summary

✨ This pull request introduces significant architectural improvements, especially in state management and configuration handling. The useSharedData composable now acts as a central hub for application-wide data, complete with caching and consistent API loading patterns. The onboarding flow has been completely revamped, providing a structured experience for new users to set up language, currency, and initial wallets/categories. Key changes also include transitioning pages to a dashboard layout, enhancing component reusability (e.g., ContentCard, ContentTable), and centralizing configuration keys and currency definitions. Overall, these changes greatly enhance the application's maintainability, scalability, and user experience.

🚀 Key Improvements

  • Refactored data fetching and caching with useSharedData and createDataLoader.
  • Implemented a comprehensive onboarding flow with explicit steps for language, wallet, and categories.
  • Centralized configuration management using configurationsApi and new utility files (types/configuration.ts, utils/configurationKeys.ts, utils/currencies.ts).
  • Improved consistency in layout definitions and component usage across the application.
  • Robust logic for resolving default wallet and currency settings.

💡 Minor Suggestions

  • Address premature success notifications in pages/onboarding.vue for language and wallet setup.
  • Refine the partyError validation in components/TransactionForm.vue to ensure selectedPartyId is properly set.
  • Consider extracting complex wallet creation/update logic in pages/onboarding.vue into helper functions or a dedicated composable.

🚨 Critical Issues

  • The 'Default Group' selection in components/settings/SettingsWallets.vue is hardcoded; it should dynamically load groups from useSharedData and persist its ID via configurationsApi.

@sourceant
Copy link

sourceant bot commented Nov 18, 2025

🔍 Code Review

💡 1. **components/WalletForm.vue** (Lines 160-165) - REFACTOR

The onMounted hook checks for isEditing.value and form.value.currency. The resetForm function already correctly sets the form.value.currency to defaultCurrency.value. To avoid redundant checks and simplify the logic, onMounted can simply call resetForm() if it's not in editing mode. This ensures consistent initialization.

Suggested Code:

onMounted(() => {
  if (!isEditing.value) {
    resetForm();
  }
});

Current Code:

onMounted(() => {
  if (!isEditing.value && (!form.value.currency || form.value.currency.trim() === '')) {
    form.value.currency = defaultCurrency.value;
  }
});
💡 2. **components/settings/SettingsWallets.vue** (Lines 25-29) - BUG

The 'Default Group' section uses hardcoded options ('Personal', 'Family', 'Work') and saves the selected group as a string value. However, the useSharedData composable has a groups ref and a getDefaultGroup computed property, suggesting groups are fetched from an API. For consistency and proper data management, these options should be dynamically fetched from sharedData.groups.value and the group ref should store an actual group ID, which can then be used with CONFIGURATION_KEYS.GROUP in handleSave to persist the setting.

Suggested Code:

        <select v-if="isEditMode" v-model="group" class="form-select">
          <option v-for="g in sharedData.groups.value" :key="g.id" :value="g.id">
            {{ g.name }}
          </option>
        </select>

Current Code:

        <select v-if="isEditMode" v-model="group" class="form-select">
          <option value="Personal">Personal</option>
          <option value="Family">Family</option>
          <option value="Work">Work</option>
        </select>
💡 3. **components/settings/SettingsWallets.vue** (Line 91) - IMPROVEMENT

Currently, only the CONFIGURATION_KEYS.WALLET is updated in handleSave. If the 'Default Group' functionality is to be fully integrated and saved, it also needs an update call using configurationsApi.update and a corresponding CONFIGURATION_KEYS.GROUP.

Suggested Code:

    if (walletId.value) {
      await configurationsApi.update(CONFIGURATION_KEYS.WALLET, {
        value: walletId.value,
        type: 'string'
      });
      // Refresh shared configurations so other parts of the app reflect the change immediately
      await sharedData.loadConfigurations(true);
    }
    if (group.value) { /* Assuming 'group' stores an ID now */
      await configurationsApi.update(CONFIGURATION_KEYS.GROUP, {
        value: group.value,
        type: 'string' /* or 'number' if storing ID */
      });
      await sharedData.loadConfigurations(true);
    }

Current Code:

    if (walletId.value) {
      await configurationsApi.update(CONFIGURATION_KEYS.WALLET, {
        value: walletId.value,
        type: 'string'
      });
      // Refresh shared configurations so other parts of the app reflect the change immediately
      await sharedData.loadConfigurations(true);
    }
💡 4. **composables/useSharedData.ts** (Lines 208-210) - BUG

The parseInt(groupId) operation can lead to unexpected behavior if groupId is not a valid number string (e.g., null, undefined, or an empty string). It's safer to add a check to ensure groupId is a valid string or number before attempting parseInt.

Suggested Code:

      const groupId = typeof configured === 'object' ? configured.id : configured;
      const parsedGroupId = typeof groupId === 'string' || typeof groupId === 'number' ? parseInt(String(groupId)) : null;
      const group = parsedGroupId !== null ? groups.value.find((g) => g.id === parsedGroupId) : null;
      if (group) return group;

Current Code:

      const groupId = typeof configured === 'object' ? configured.id : configured;
      const group = groups.value.find((g) => g.id === parseInt(groupId));
      if (group) return group;
💡 5. **composables/useSharedData.ts** (Lines 264-265) - CLARITY

The comment // REMOVED: Auto-initialization - now controlled by data manager is referring to code that has been removed. Such comments can be removed to improve code clarity and reduce noise.

Suggested Code:

  // View-scoped state

Current Code:

  // REMOVED: Auto-initialization - now controlled by data manager
  // ensureInit();
💡 6. **pages/onboarding.vue** (Lines 406-408) - BUG

The showSuccess notification is premature here. The language configuration is only saved during the final completOnboarding step. Giving a success message now might mislead the user into thinking the setting has been saved, when it has not yet been persisted.

Suggested Code:

  if (!selectedLanguage.value) return;
  nextStep();

Current Code:

  if (!selectedLanguage.value) return;

  showSuccess(
    'Language Set!',
    `Language changed to ${availableLanguages.find((l) => l.code === selectedLanguage.value)?.name}.`
  );
  nextStep();
💡 7. **pages/onboarding.vue** (Lines 411-420) - BUG

Similar to the language selection, this showSuccess notification for wallet and currency setup is premature. The actual wallet creation/update and currency configuration are only persisted in the final completOnboarding step. This notification should be removed or moved to the final completion step.

Suggested Code:

  if (!isWalletCurrencySetupValid.value) return;
  nextStep();

Current Code:

  if (!isWalletCurrencySetupValid.value) return;

  let message = 'Settings saved successfully!';

  if (walletChoice.value === 'rename-default') {
    message = `Default wallet renamed to "${newWalletName.value}" and currency set to ${selectedCurrency.value}.`;
  } else if (walletChoice.value === 'create-new') {
    message = `New wallet "${newWalletName.value}" created with ${newWalletCurrency.value} currency.`;
  } else {
    message = `Default wallet configured with ${selectedCurrency.value} currency.`;
  }

  showSuccess('Setup Complete!', message);
  nextStep();
💡 8. **pages/onboarding.vue** (Line 322) - BUG

The transactionCount computed property is currently hardcoded to 0. For a completion summary, it should ideally reflect the actual number of transactions if any were created or imported during onboarding, or at least be tied to the useTransactions composable's state. If no transactions are expected at this point, consider removing this statistic or dynamically displaying it based on actual data.

Suggested Code:

const transactionCount = computed(() => useTransactions().transactions.value.length || 0);

Current Code:

const transactionCount = computed(() => 0);
💡 9. **utils/transactionMapper.ts** (Lines 28-31) - CLARITY

The parameters _categories: Category[] = [] and _groups: GroupLite[] = [] are passed into the toFrontend function but are not actually used within its implementation. These unused parameters can be removed to simplify the function signature and improve clarity, as they don't contribute to the function's logic.

Suggested Code:

  toFrontend(
    api: ApiTransaction,
    parties: Party[] = [],
    wallets: Wallet[] = []
  ): FrontendTransaction {

Current Code:

  toFrontend(
    api: ApiTransaction,
    parties: Party[] = [],
    _categories: Category[] = [],
    wallets: Wallet[] = [],
    _groups: GroupLite[] = []
  ): FrontendTransaction {
💡 10. **utils/transactionMapper.ts** (Lines 194-196) - CLARITY

Similar to the single toFrontend method, the toFrontendBatch method also takes categories: Category[] and groups: GroupLite[] as parameters, but these are not used within the mapping logic as the toFrontend call itself only uses parties and wallets. Removing these unused parameters will make the function signature cleaner and more accurate.

Suggested Code:

  toFrontendBatch(
    apiTransactions: ApiTransaction[],
    parties: Party[],
    wallets: Wallet[]
  ): FrontendTransaction[] {
    return apiTransactions.map((api) => this.toFrontend(api, parties, wallets));
  },

Current Code:

  toFrontendBatch(
    apiTransactions: ApiTransaction[],
    parties: Party[],
    categories: Category[],
    wallets: Wallet[],
    groups: GroupLite[]
  ): FrontendTransaction[] {
    return apiTransactions.map((api) => this.toFrontend(api, parties, categories, wallets, groups));
  },

Verdict: APPROVE

Posted as a comment because posting a review failed.

Copy link
Contributor

@nfebe nfebe left a comment

Choose a reason for hiding this comment

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

Looks okay, minor nitpick.

I am yet to actually tests though.

Copy link
Contributor

@nfebe nfebe left a comment

Choose a reason for hiding this comment

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

So on-boarding starts after sign up. That must be the first the user should see. If the user is already signed up, you need to check if they completed on-boarding if they did not you have to show it.

Some of the post/put to configurations return 422 Unprocessable Content.

This flow does not show a user the onboarding form after register.

Test cases:

  • New users see onboarding form
  • Logged-in user with on-boarding complete from sees onboarding form
  • If user skips onboarding we should mark it as complete

@nfebe
Copy link
Contributor

nfebe commented Nov 28, 2025

Some guidelines...

  1. 422 Unprocessable content on configuration API

In services/api/configurationsApi.ts:32-42 - The update() method passes the full payload including key to the body, but key is already in the URL path. The API rejects the duplicate.

Fix needed: Strip key from payload body before sending PUT request.

  1. Onboarding flow not triggered
  • Check if onboarding-complete configuration exists
  • Redirect incomplete users to /onboarding

Fix needed: Update middleware to fetch onboarding status and redirect accordingly.

  1. Skipping onboarding does not mark it as complete

pages/onboarding.vue:321-323 - The skipStep() function just advances to the next step without marking onboarding as complete when the user skips all steps and leaves.

  const skipStep = () => {
    nextStep();
  };

Fix needed: If user skips to the final step or navigates away, mark onboarding-complete in the API.

Test using these

Test Case Status
New users see onboarding form ❌ Fails - no redirect after registration
Logged-in user with completed onboarding does NOT see form ❌ Not enforced - no check in middleware
Skipping onboarding marks it complete ❌ Fails - skip only advances step

Summary

The branch needs fixes for the configuration API payload and onboarding flow logic before merge.

Todos

  • Fix 422 errors on configuration API calls
  • Update auth middleware to check onboarding completion
  • Mark onboarding complete when user skips
  • Test the onboarding flow

@ubactmj
Copy link

ubactmj commented Dec 1, 2025

Some guidelines...

1. 422 Unprocessable content on configuration API

In services/api/configurationsApi.ts:32-42 - The update() method passes the full payload including key to the body, but key is already in the URL path. The API rejects the duplicate.

Fix needed: Strip key from payload body before sending PUT request.

2. Onboarding flow not triggered


* Check if onboarding-complete configuration exists

* Redirect incomplete users to /onboarding

Fix needed: Update middleware to fetch onboarding status and redirect accordingly.

3. Skipping onboarding does not mark it as complete

pages/onboarding.vue:321-323 - The skipStep() function just advances to the next step without marking onboarding as complete when the user skips all steps and leaves.

  const skipStep = () => {
    nextStep();
  };

Fix needed: If user skips to the final step or navigates away, mark onboarding-complete in the API.

Test using these
Test Case Status
New users see onboarding form ❌ Fails - no redirect after registration
Logged-in user with completed onboarding does NOT see form ❌ Not enforced - no check in middleware
Skipping onboarding marks it complete ❌ Fails - skip only advances step

Summary

The branch needs fixes for the configuration API payload and onboarding flow logic before merge.

Todos

* [ ]  Fix 422 errors on configuration API calls

* [ ]  Update auth middleware to check onboarding completion

* [ ]  Mark onboarding complete when user skips

* [ ]  Test the onboarding flow

Issues resolved:

  • on registration complete user is redirected to /onboarding.

  • onboarding marked as complete if skipped or if configurations set.

  • Redirect to dashboard after onboarding complete

@Lantum-Brendan Lantum-Brendan requested a review from nfebe December 6, 2025 18:41
Copy link
Contributor

@nfebe nfebe left a comment

Choose a reason for hiding this comment

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

Looks good so far. Please see inline comments for a few adjustments.

Comment on lines 10 to 38

// Check onboarding status
// Force reload configurations to avoid stale cache across sessions
const configurations = await sharedData.loadConfigurations(true);

// Explicitly check for the onboarding_complete key
// If it's missing or false, we consider onboarding incomplete
const isOnboardingComplete = !!configurations?.[CONFIGURATION_KEYS.ONBOARDING_COMPLETE];

// If onboarding is NOT complete
if (!isOnboardingComplete) {
// And we are NOT already on the onboarding page
if (to.path !== '/onboarding') {
return navigateTo('/onboarding');
}
// If we ARE on the onboarding page, allow access (do nothing)
return;
}

// If onboarding IS complete
if (isOnboardingComplete) {
// And we try to go to onboarding page
if (to.path === '/onboarding') {
// Redirect to dashboard
return navigateTo('/dashboard');
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks good but too many unnecessary comments, all of those are obvious right?

return;
}
// Login succeeded – now check onboarding status (errors here should not affect login error)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// Login succeeded – now check onboarding status (errors here should not affect login error)

const handleSubmit = async () => {
loading.value = true;
loginError.value = '';
// First attempt login
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// First attempt login

await login(form.value);
router.push('/dashboard');
} catch {
// Login failed – show error and stop further processing
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// Login failed – show error and stop further processing

Copy link

@sourceant sourceant bot left a comment

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.

Lantum-Brendan and others added 2 commits December 29, 2025 21:19
Signed-off-by: nfebe <fenn25.fn@gmail.com>
Add dynamic key to NuxtLayout based on route.meta.layout to ensure
proper DOM cleanup when switching between layouts (e.g., onboarding
to dashboard).

Signed-off-by: nfebe <fenn25.fn@gmail.com>
Copy link

@sourceant sourceant bot left a comment

Choose a reason for hiding this comment

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

Review complete. No specific code suggestions were generated. See the overview comment for a summary.

- Switch Reports, Settings, AI Insights to dashboard layout for consistency
- Remove auto-selection of default wallet in useStatistics composable
- Use currentStatistics from composable in reports page
- Change default item indicator color from primary to success

Signed-off-by: nfebe <fenn25.fn@gmail.com>
@nfebe nfebe merged commit 278ed2c into main Dec 30, 2025
4 checks passed
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.

4 participants