Skip to content
Open
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
39 changes: 25 additions & 14 deletions client/app/error.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { useEffect } from "react";
import { ErrorBoundary } from "@/components/ui/error-boundary";
import * as Sentry from "@sentry/nextjs";
import { Button } from "@/components/ui/button";

export default function Error({
Expand All @@ -12,31 +12,42 @@ export default function Error({
reset: () => void;
}) {
useEffect(() => {
// Log error to error reporting service
// Report to Sentry
Sentry.captureException(error);
console.error("Application error:", error);
}, [error]);

return (
<div className="min-h-screen bg-[#F9F6F2] dark:bg-[#1E2A35] flex items-center justify-center p-4">
<div className="max-w-md w-full text-center space-y-4">
<h2 className="text-2xl font-bold text-[#1E2A35] dark:text-[#F9F6F2]">
Something went wrong!
</h2>
<p className="text-[#1E2A35]/70 dark:text-[#F9F6F2]/70">
{error.message || "An unexpected error occurred"}
</p>
{error.digest && (
<p className="text-sm text-[#1E2A35]/50 dark:text-[#F9F6F2]/50">
Error ID: {error.digest}
<div className="min-h-screen bg-[#F9F6F2] dark:bg-[#1E2A35] flex flex-col items-center justify-center p-4">
<div className="max-w-md w-full text-center space-y-6">
<div className="space-y-2">
<h2 className="text-3xl font-bold text-[#1E2A35] dark:text-[#F9F6F2]">
Something went wrong
</h2>
<p className="text-[#1E2A35]/70 dark:text-[#F9F6F2]/70">
{error.message || "An unexpected error occurred. Our team has been notified."}
</p>
</div>

{error.digest && (
<div className="bg-black/5 dark:bg-white/5 py-2 px-4 rounded-md inline-block">
<p className="text-xs font-mono text-[#1E2A35]/50 dark:text-[#F9F6F2]/50">
Error ID: {error.digest}
</p>
</div>
)}

<div className="flex gap-4 justify-center">
<Button onClick={reset} variant="default">
<Button
onClick={reset}
className="bg-[#E86A33] hover:bg-[#E86A33]/90 text-white px-8"
>
Try again
</Button>
<Button
onClick={() => (window.location.href = "/")}
variant="outline"
className="border-[#1E2A35]/20 dark:border-[#F9F6F2]/20"
>
Go home
</Button>
Expand Down
38 changes: 22 additions & 16 deletions client/app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,35 @@
"use client";

import Link from "next/link";
import { Button } from "@/components/ui/button";

export default function NotFound() {
return (
<div className="min-h-screen bg-[#F9F6F2] dark:bg-[#1E2A35] flex items-center justify-center p-4">
<div className="max-w-md w-full text-center space-y-4">
<h1 className="text-6xl font-bold text-[#1E2A35] dark:text-[#F9F6F2]">
404
</h1>
<h2 className="text-2xl font-semibold text-[#1E2A35] dark:text-[#F9F6F2]">
Page Not Found
</h2>
<p className="text-[#1E2A35]/70 dark:text-[#F9F6F2]/70">
The page you're looking for doesn't exist or has been moved.
</p>
<div className="flex gap-4 justify-center pt-4">
<Button asChild>
<div className="min-h-screen bg-[#F9F6F2] dark:bg-[#1E2A35] flex flex-col items-center justify-center p-4">
<div className="max-w-md w-full text-center space-y-6">
<div className="space-y-2">
<div className="text-6xl font-bold text-[#E86A33]">404</div>
<h2 className="text-3xl font-bold text-[#1E2A35] dark:text-[#F9F6F2]">
Page Not Found
</h2>
<p className="text-[#1E2A35]/70 dark:text-[#F9F6F2]/70">
The page you are looking for doesn't exist or has been moved.
</p>
</div>

<div className="flex gap-4 justify-center">
<Button asChild className="bg-[#E86A33] hover:bg-[#E86A33]/90 text-white px-8">
<Link href="/">Go home</Link>
</Button>
<Button asChild variant="outline">
<Link href="/dashboard">Go to dashboard</Link>
<Button
asChild
variant="outline"
className="border-[#1E2A35]/20 dark:border-[#F9F6F2]/20"
>
<Link href="/dashboard">Dashboard</Link>
</Button>
</div>
</div>
</div>
);
}

76 changes: 55 additions & 21 deletions client/components/pages/subscriptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Edit2, Trash2, Mail, Clock, Copy, Lock, Users, Calendar, Check } from "
import { useDebounce } from "@/hooks/use-debounce"
import { VirtualizedList } from "@/components/ui/virtualized-list"
import { EmptyState } from "@/components/ui/empty-state"
import { ErrorBoundary } from "@/components/ui/error-boundary"

interface SubscriptionsPageProps {
subscriptions?: any[]
Expand Down Expand Up @@ -328,33 +329,41 @@ export default function SubscriptionsPage({
itemHeight={80}
containerHeight={600}
renderItem={(sub: any, index: number) => (
<SubscriptionCard
key={sub.id}
subscription={sub}
onDelete={onDelete}
onManage={onManage}
selectedSubscriptions={selectedSubscriptions}
onToggleSelect={onToggleSelect}
darkMode={darkMode}
isDuplicate={duplicates.some((dup: any) => dup.subscriptions.some((s: any) => s.id === sub.id))}
unusedInfo={unusedSubscriptions.find((unused: any) => unused.id === sub.id)}
/>
<ErrorBoundary
fallback={<BrokenCardPlaceholder name={sub?.name} darkMode={darkMode} />}
>
<SubscriptionCard
key={sub.id}
subscription={sub}
onDelete={onDelete}
onManage={onManage}
selectedSubscriptions={selectedSubscriptions}
onToggleSelect={onToggleSelect}
darkMode={darkMode}
isDuplicate={duplicates.some((dup: any) => dup.subscriptions.some((s: any) => s.id === sub.id))}
unusedInfo={unusedSubscriptions.find((unused: any) => unused.id === sub.id)}
/>
</ErrorBoundary>
)}
/>
) : (
<div className="space-y-3">
{filtered.map((sub: any) => (
<SubscriptionCard
<ErrorBoundary
key={sub.id}
subscription={sub}
onDelete={onDelete}
onManage={onManage}
selectedSubscriptions={selectedSubscriptions}
onToggleSelect={onToggleSelect}
darkMode={darkMode}
isDuplicate={duplicates.some((dup: any) => dup.subscriptions.some((s: any) => s.id === sub.id))}
unusedInfo={unusedSubscriptions.find((unused: any) => unused.id === sub.id)}
/>
fallback={<BrokenCardPlaceholder name={sub?.name} darkMode={darkMode} />}
>
<SubscriptionCard
subscription={sub}
onDelete={onDelete}
onManage={onManage}
selectedSubscriptions={selectedSubscriptions}
onToggleSelect={onToggleSelect}
darkMode={darkMode}
isDuplicate={duplicates.some((dup: any) => dup.subscriptions.some((s: any) => s.id === sub.id))}
unusedInfo={unusedSubscriptions.find((unused: any) => unused.id === sub.id)}
/>
</ErrorBoundary>
))}
</div>
)}
Expand Down Expand Up @@ -580,3 +589,28 @@ function SubscriptionCard({
</div>
)
}

function BrokenCardPlaceholder({ name, darkMode }: { name?: string; darkMode?: boolean }) {
return (
<div
className={`${darkMode ? "bg-[#2D3748] border-[#374151]" : "bg-white border-gray-200"} border rounded-xl p-5 flex items-center justify-between opacity-70`}
>
<div className="flex items-center gap-4 flex-1">
<div className={`w-12 h-12 ${darkMode ? "bg-[#1E2A35]" : "bg-gray-100"} rounded-lg flex items-center justify-center`}>
<AlertCircle className="w-6 h-6 text-destructive" />
</div>
<div>
<h4 className={`font-semibold ${darkMode ? "text-white" : "text-gray-900"}`}>
{name || "Subscription"} (Error)
</h4>
<p className={`text-xs ${darkMode ? "text-gray-400" : "text-gray-500"}`}>
This component failed to load.
</p>
</div>
</div>
<div className="text-right">
<p className={`text-xs ${darkMode ? "text-gray-400" : "text-gray-500"}`}>Unavailable</p>
</div>
</div>
)
}
Loading