Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions app/business/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import { ArrowRight, Briefcase, Users, PiggyBank, CreditCard, Settings, Zap } fr
import { useApiOpts } from '@/hooks/use-api';
import * as businessApi from '@/lib/api/business';
import type { BusinessStatsResponse } from '@/lib/api/business';
import { featureFlags } from '@/lib/features';
import { logger } from '@/lib/logger';

const businessServices = [
{ id: 'sme', title: 'SME Services', description: 'Business accounts, transfers & statements', icon: Briefcase, badge: 'Pro', href: '/business/sme' },
{ id: 'sme', title: 'SME Services', description: 'Business accounts, transfers & statements', icon: Briefcase, badge: 'Pro', href: '/sme' },
{ id: 'salary', title: 'Payroll', description: 'Disburse salaries and manage batches', icon: Users, badge: 'New', href: '/salary' },
{ id: 'campaigns', title: 'Crowdfunding', description: 'Raise funds for projects via Trivela', icon: Zap, badge: 'Alpha', href: '/campaigns/1' },
{ id: 'enterprise', title: 'Enterprise', description: 'Bulk transfers and treasury management', icon: PiggyBank, href: '/enterprise' },
Expand All @@ -29,10 +29,6 @@ export default function BusinessPage() {
const opts = useApiOpts();
const [stats, setStats] = useState<BusinessStatsResponse | null>(null);
const [loading, setLoading] = useState(true);
const visibleBusinessServices = businessServices.filter((service) => {
if (service.id === 'gateway') return featureFlags.businessGateway;
return true;
});

useEffect(() => {
const fetchStats = async () => {
Expand All @@ -42,7 +38,7 @@ export default function BusinessPage() {
setStats(data);
} catch (err) {
// Fallback: show loading state instead of error UI
console.error('Business stats error:', err);
logger.error('Business stats error:', err);
} finally {
Comment on lines 39 to 42
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don’t escalate fallback stats failures to production errors.

This path intentionally falls back without user-facing error UI, but logger.error emits the raw caught error in production. Use a debug-gated warning unless there is sanitized telemetry behind it.

🔧 Proposed fix
       } catch (err) {
         // Fallback: show loading state instead of error UI
-        logger.error('Business stats error:', err);
+        logger.warn('Business stats error', err);
       } finally {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (err) {
// Fallback: show loading state instead of error UI
console.error('Business stats error:', err);
logger.error('Business stats error:', err);
} finally {
} catch (err) {
// Fallback: show loading state instead of error UI
logger.warn('Business stats error', err);
} finally {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/business/page.tsx` around lines 39 - 42, The catch block that logs
business stats failures currently calls logger.error('Business stats error:',
err) and thus emits raw errors in production; change it so that raw error
details are only logged at debug level in non-production (e.g., guard with
NODE_ENV !== 'production' and call logger.debug(...err...)), and in production
replace the error call with a sanitized warning (e.g., logger.warn('Business
stats fallback - details omitted')) or send sanitized telemetry instead; update
the catch in app/business/page.tsx (the catch around the business stats fetch
where logger.error is called) accordingly so production logs do not contain raw
error objects.

setLoading(false);
}
Expand Down Expand Up @@ -77,7 +73,7 @@ export default function BusinessPage() {
</div>

<div className="space-y-3 mb-6">
{visibleBusinessServices.map((service) => {
{businessServices.map((service) => {
const Icon = service.icon;
return (
<button key={service.id} onClick={() => router.push(service.href)} className="w-full text-left">
Expand Down
72 changes: 14 additions & 58 deletions app/error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

import { useEffect } from 'react';
import { Button } from '@/components/ui/button';
import { AlertTriangle, RefreshCw, Home } from 'lucide-react';
import { errorReporter } from '@/lib/error-reporting';
import { logger } from '@/lib/logger';

export default function Error({
error,
Expand All @@ -13,67 +12,24 @@ export default function Error({
reset: () => void;
}) {
useEffect(() => {
errorReporter.reportError(error, {
level: 'page',
context: {
digest: error.digest,
type: 'route-error'
}
});
logger.error('Application error:', error);
}, [error]);
Comment on lines 14 to 16
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Only log sanitized error identifiers in production.

Passing the full error object to logger.error can expose arbitrary error details in the production console. Log the digest/generic signal at error level and keep raw details debug-gated.

🛡️ Proposed fix
   useEffect(() => {
-    logger.error('Application error:', error);
+    logger.error('Application error', error.digest ? { digest: error.digest } : undefined);
+    logger.debug('Application error details', error);
   }, [error]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
console.error('Application error:', error);
logger.error('Application error:', error);
}, [error]);
useEffect(() => {
logger.error('Application error', error.digest ? { digest: error.digest } : undefined);
logger.debug('Application error details', error);
}, [error]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/error.tsx` around lines 14 - 16, The current useEffect calls
logger.error('Application error:', error) which may leak raw error details;
change the effect (useEffect) to compute a sanitized identifier (e.g. an
error.code or a short digest derived from error.name/message or error.id) and
pass only that identifier to logger.error, and then emit the full error object
to logger.debug (or logger.warn) only when not in production (guarded by
NODE_ENV or an isDevelopment flag); update the useEffect to use the new
sanitizedErrorId and conditional debug logging while keeping the original
logger.error call signature limited to non-sensitive data.


const handleGoHome = () => {
if (typeof window !== 'undefined') {
window.location.href = '/';
}
};

return (
<div className="flex min-h-[400px] flex-col items-center justify-center gap-4 p-6 text-center">
<div className="rounded-full bg-red-100 dark:bg-red-900/30 p-3">
<AlertTriangle className="h-8 w-8 text-red-600 dark:text-red-400" />
</div>

<div className="space-y-2">
<h2 className="text-xl font-semibold text-foreground">Page Error</h2>
<p className="text-sm text-muted-foreground max-w-md">
This page encountered an unexpected error. You can try again or return to the home page.
</p>
{error.digest && (
<p className="text-xs text-muted-foreground mt-2">
Error ID: {error.digest}
</p>
)}

{process.env.NODE_ENV === 'development' && (
<details className="mt-4 text-left">
<summary className="cursor-pointer text-xs text-muted-foreground hover:text-foreground">
Error Details (Development)
</summary>
<div className="mt-2 p-3 bg-muted rounded-md text-xs font-mono text-left overflow-auto max-h-32">
<div className="text-red-600 dark:text-red-400 font-semibold">
{error.name}: {error.message}
</div>
{error.stack && (
<pre className="mt-2 text-muted-foreground whitespace-pre-wrap">
{error.stack}
</pre>
)}
</div>
</details>
)}
<div className="flex min-h-[400px] flex-col items-center justify-center gap-4 p-4 text-center">
<div className="rounded-full bg-red-100 p-3">
<svg className="h-6 w-6 text-red-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
</div>

<div className="flex gap-2">
<Button onClick={reset} variant="outline" size="sm">
<RefreshCw className="w-4 h-4 mr-2" />
Try again
</Button>
<Button onClick={handleGoHome} variant="default" size="sm">
<Home className="w-4 h-4 mr-2" />
Go home
</Button>
<div>
<h2 className="text-lg font-semibold text-foreground">Something went wrong</h2>
<p className="text-sm text-muted-foreground mt-1">An unexpected error occurred</p>
{error.digest && <p className="text-xs text-muted-foreground mt-2">Error ID: {error.digest}</p>}
</div>
<Button onClick={reset} variant="outline">
Try again
</Button>
</div>
);
}
Loading