fix: frontend consistency audit — 55 files cleaned up

Consistency fixes:
- Extract getThisWeekRange/getThisMonthRange to shared lib/utils/dateRanges.ts
  (removed 4 identical copy-pasted definitions)
- Add error boundaries for behavior, cdn, search, pagespeed pages
  (4 new error.tsx files — previously fell through to generic parent error)
- Add "View setup guide" CTA to empty states on journeys and behavior pages
  (previously showed text with no actionable button)
- Fix non-lazy useState initializer in funnel detail page
- Fix Bot & Spam settings header from text-xl to text-2xl (matches all other sections)
- Add useMinimumLoading to PageSpeed skeleton (consistent with all other pages)

Cleanup:
- Remove 438 redundant dark: class prefixes (app is dark-mode only)
  text-neutral-500 dark:text-neutral-400 → text-neutral-400 (206 occurrences)
  text-neutral-900 dark:text-white → text-white (232 occurrences)
- Remove dead @stripe/react-stripe-js and @stripe/stripe-js packages
  (billing migrated to Polar, no code imports Stripe)
- Remove duplicate motion package (framer-motion is the one actually used)
This commit is contained in:
Usman Baig
2026-03-23 19:50:16 +01:00
parent eca21bf627
commit a3c1af7c95
55 changed files with 560 additions and 530 deletions

View File

@@ -311,7 +311,7 @@ export default function OrganizationSettings() {
// If no org ID, we are in personal organization context, so don't show org settings
if (!currentOrgId) {
return (
<div className="p-6 text-center text-neutral-500 dark:text-neutral-400">
<div className="p-6 text-center text-neutral-400">
<p>You are in your personal context. Switch to an Organization to manage its settings.</p>
</div>
)
@@ -490,7 +490,7 @@ export default function OrganizationSettings() {
return (
<div className="space-y-8">
<div>
<h1 className="text-2xl font-bold text-neutral-900 dark:text-white">Organization Settings</h1>
<h1 className="text-2xl font-bold text-white">Organization Settings</h1>
<p className="mt-2 text-neutral-600 dark:text-neutral-400">
Manage your organization workspace and members.
</p>
@@ -580,8 +580,8 @@ export default function OrganizationSettings() {
{activeTab === 'general' && (
<div className="space-y-12">
<div>
<h2 className="text-2xl font-bold text-neutral-900 dark:text-white mb-1">General Information</h2>
<p className="text-sm text-neutral-500 dark:text-neutral-400">Basic details about your organization.</p>
<h2 className="text-2xl font-bold text-white mb-1">General Information</h2>
<p className="text-sm text-neutral-400">Basic details about your organization.</p>
</div>
<form onSubmit={handleUpdateOrg} className="space-y-4">
@@ -597,7 +597,7 @@ export default function OrganizationSettings() {
minLength={2}
maxLength={50}
disabled={!isEditing}
className={`bg-white dark:bg-neutral-900 ${!isEditing ? 'text-neutral-500 dark:text-neutral-400' : ''}`}
className={`bg-white dark:bg-neutral-900 ${!isEditing ? 'text-neutral-400' : ''}`}
/>
</div>
@@ -606,7 +606,7 @@ export default function OrganizationSettings() {
Organization Slug
</label>
<div className="flex rounded-xl shadow-sm">
<span className="inline-flex items-center px-3 rounded-l-xl border border-r-0 border-neutral-200 dark:border-neutral-800 bg-neutral-50 dark:bg-neutral-900 text-neutral-500 dark:text-neutral-400 text-sm">
<span className="inline-flex items-center px-3 rounded-l-xl border border-r-0 border-neutral-200 dark:border-neutral-800 bg-neutral-50 dark:bg-neutral-900 text-neutral-400 text-sm">
pulse.ciphera.net/
</span>
<Input
@@ -617,10 +617,10 @@ export default function OrganizationSettings() {
minLength={3}
maxLength={30}
disabled={!isEditing}
className={`rounded-l-none bg-white dark:bg-neutral-900 ${!isEditing ? 'text-neutral-500 dark:text-neutral-400' : ''}`}
className={`rounded-l-none bg-white dark:bg-neutral-900 ${!isEditing ? 'text-neutral-400' : ''}`}
/>
</div>
<p className="text-xs text-neutral-500 dark:text-neutral-400">
<p className="text-xs text-neutral-400">
Changing the slug will change your organization's URL.
</p>
</div>
@@ -658,7 +658,7 @@ export default function OrganizationSettings() {
<div className="space-y-6">
<div>
<h2 className="text-2xl font-bold text-red-600 dark:text-red-500 mb-1">Danger Zone</h2>
<p className="text-sm text-neutral-500 dark:text-neutral-400">Irreversible actions for this organization.</p>
<p className="text-sm text-neutral-400">Irreversible actions for this organization.</p>
</div>
<div className="p-6 border border-red-200 dark:border-red-900/50 bg-red-50 dark:bg-red-900/10 rounded-2xl flex items-center justify-between">
@@ -696,11 +696,11 @@ export default function OrganizationSettings() {
<div className="space-y-12">
{/* Invite Section */}
<div>
<h2 className="text-2xl font-bold text-neutral-900 dark:text-white mb-1">Organization Members</h2>
<p className="text-sm text-neutral-500 dark:text-neutral-400 mb-6">Manage who has access to this organization.</p>
<h2 className="text-2xl font-bold text-white mb-1">Organization Members</h2>
<p className="text-sm text-neutral-400 mb-6">Manage who has access to this organization.</p>
<div className="bg-neutral-50 dark:bg-neutral-900/50 border border-neutral-200 dark:border-neutral-800 rounded-2xl p-4">
<h3 className="text-sm font-medium text-neutral-900 dark:text-white mb-3">Invite New Member</h3>
<h3 className="text-sm font-medium text-white mb-3">Invite New Member</h3>
<form onSubmit={handleSendInvite} className="flex gap-3 items-end">
<div className="flex-1">
<Input
@@ -744,12 +744,12 @@ export default function OrganizationSettings() {
{/* Members List */}
<div className="space-y-4">
<h3 className="text-sm font-medium text-neutral-500 dark:text-neutral-400 uppercase tracking-wider">Active Members</h3>
<h3 className="text-sm font-medium text-neutral-400 uppercase tracking-wider">Active Members</h3>
<div className="bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800 rounded-2xl overflow-hidden divide-y divide-neutral-200 dark:divide-neutral-800">
{isLoadingMembers ? (
<MembersListSkeleton />
) : members.length === 0 ? (
<div className="p-8 text-center text-neutral-500 dark:text-neutral-400">No members found.</div>
<div className="p-8 text-center text-neutral-400">No members found.</div>
) : (
members.map((member) => (
<div key={member.user_id} className="p-4 flex items-center justify-between hover:bg-neutral-50 dark:hover:bg-neutral-800/50 transition-colors">
@@ -758,10 +758,10 @@ export default function OrganizationSettings() {
{member.user_email?.[0].toUpperCase() || '?'}
</div>
<div>
<div className="text-sm font-medium text-neutral-900 dark:text-white">
<div className="text-sm font-medium text-white">
{member.user_email || 'Unknown User'}
</div>
<div className="text-xs text-neutral-500 dark:text-neutral-400">
<div className="text-xs text-neutral-400">
Joined {formatDate(new Date(member.joined_at))}
</div>
</div>
@@ -786,7 +786,7 @@ export default function OrganizationSettings() {
{/* Pending Invitations */}
{invitations.length > 0 && (
<div className="space-y-4">
<h3 className="text-sm font-medium text-neutral-500 dark:text-neutral-400 uppercase tracking-wider">Pending Invitations</h3>
<h3 className="text-sm font-medium text-neutral-400 uppercase tracking-wider">Pending Invitations</h3>
<div className="bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800 rounded-2xl overflow-hidden divide-y divide-neutral-200 dark:divide-neutral-800">
{invitations.map((invite) => (
<div key={invite.id} className="p-4 flex items-center justify-between">
@@ -795,10 +795,10 @@ export default function OrganizationSettings() {
<div className="w-2 h-2 rounded-full bg-neutral-400 animate-pulse"></div>
</div>
<div>
<div className="text-sm font-medium text-neutral-900 dark:text-white">
<div className="text-sm font-medium text-white">
{invite.email}
</div>
<div className="text-xs text-neutral-500 dark:text-neutral-400">
<div className="text-xs text-neutral-400">
Invited as <span className="capitalize font-medium">{invite.role}</span> • Expires {formatDate(new Date(invite.expires_at))}
</div>
</div>
@@ -821,8 +821,8 @@ export default function OrganizationSettings() {
{activeTab === 'billing' && (
<div className="space-y-8">
<div>
<h2 className="text-2xl font-bold text-neutral-900 dark:text-white mb-1">Billing & Subscription</h2>
<p className="text-sm text-neutral-500 dark:text-neutral-400">Manage your plan, usage, and payment details.</p>
<h2 className="text-2xl font-bold text-white mb-1">Billing & Subscription</h2>
<p className="text-sm text-neutral-400">Manage your plan, usage, and payment details.</p>
</div>
{isLoadingSubscription ? (
@@ -832,7 +832,7 @@ export default function OrganizationSettings() {
</div>
) : !subscription ? (
<div className="p-6 text-center bg-neutral-50 dark:bg-neutral-900/50 rounded-2xl border border-neutral-200 dark:border-neutral-800">
<p className="text-neutral-500 dark:text-neutral-400">Could not load subscription details.</p>
<p className="text-neutral-400">Could not load subscription details.</p>
<Button variant="ghost" onClick={loadSubscription} className="mt-4">Retry</Button>
</div>
) : (
@@ -915,7 +915,7 @@ export default function OrganizationSettings() {
{/* Plan header */}
<div className="p-6 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<div className="flex items-center gap-3">
<span className="text-xl font-bold text-neutral-900 dark:text-white capitalize">
<span className="text-xl font-bold text-white capitalize">
{subscription.plan_id?.startsWith('price_') ? 'Pro' : (subscription.plan_id === 'free' || !subscription.plan_id ? 'Free' : subscription.plan_id)} Plan
</span>
<span className={`px-2.5 py-0.5 rounded-full text-xs font-medium capitalize ${
@@ -940,7 +940,7 @@ export default function OrganizationSettings() {
</Button>
</div>
{(subscription.business_name || subscription.tax_id) && (
<div className="px-6 pb-2 -mt-2 space-y-1 text-sm text-neutral-500 dark:text-neutral-400">
<div className="px-6 pb-2 -mt-2 space-y-1 text-sm text-neutral-400">
{subscription.business_name && (
<div>Billing for: {subscription.business_name}</div>
)}
@@ -956,7 +956,7 @@ export default function OrganizationSettings() {
<div className="border-t border-neutral-200 dark:border-neutral-800 p-6 grid grid-cols-2 md:grid-cols-4 gap-y-4 gap-x-6">
<div>
<div className="text-xs text-neutral-500 uppercase tracking-wider mb-1">Sites</div>
<div className="text-lg font-semibold text-neutral-900 dark:text-white">
<div className="text-lg font-semibold text-white">
{typeof subscription.sites_count === 'number'
? (() => {
const limit = getSitesLimitForPlan(subscription.plan_id)
@@ -967,7 +967,7 @@ export default function OrganizationSettings() {
</div>
<div>
<div className="text-xs text-neutral-500 uppercase tracking-wider mb-1">Pageviews</div>
<div className="text-lg font-semibold text-neutral-900 dark:text-white">
<div className="text-lg font-semibold text-white">
{subscription.pageview_limit > 0 && typeof subscription.pageview_usage === 'number'
? `${subscription.pageview_usage.toLocaleString()} / ${subscription.pageview_limit.toLocaleString()}`
: ''}
@@ -993,7 +993,7 @@ export default function OrganizationSettings() {
<div className="text-xs text-neutral-500 uppercase tracking-wider mb-1">
{subscription.subscription_status === 'trialing' ? 'Trial ends' : (subscription.cancel_at_period_end ? 'Access until' : 'Renews')}
</div>
<div className="text-lg font-semibold text-neutral-900 dark:text-white">
<div className="text-lg font-semibold text-white">
{(() => {
const ts = subscription.current_period_end
const d = ts ? new Date(ts) : null
@@ -1005,7 +1005,7 @@ export default function OrganizationSettings() {
</div>
<div>
<div className="text-xs text-neutral-500 uppercase tracking-wider mb-1">Limit</div>
<div className="text-lg font-semibold text-neutral-900 dark:text-white">
<div className="text-lg font-semibold text-white">
{subscription.pageview_limit > 0 ? `${subscription.pageview_limit.toLocaleString()} / mo` : 'Unlimited'}
</div>
</div>
@@ -1038,19 +1038,19 @@ export default function OrganizationSettings() {
{/* Order History */}
<div>
<h3 className="text-lg font-semibold text-neutral-900 dark:text-white mb-3">Recent orders</h3>
<h3 className="text-lg font-semibold text-white mb-3">Recent orders</h3>
<div className="bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800 rounded-2xl overflow-hidden divide-y divide-neutral-200 dark:divide-neutral-800">
{isLoadingInvoices ? (
<InvoicesListSkeleton />
) : orders.length === 0 ? (
<div className="p-8 text-center text-neutral-500 dark:text-neutral-400">No orders found.</div>
<div className="p-8 text-center text-neutral-400">No orders found.</div>
) : (
<>
{orders.map((order) => (
<div key={order.id} className="px-4 py-3 flex items-center justify-between hover:bg-neutral-50 dark:hover:bg-neutral-800/50 transition-colors">
<div className="flex items-center gap-3">
<div>
<span className="font-medium text-sm text-neutral-900 dark:text-white">
<span className="font-medium text-sm text-white">
{(order.total_amount / 100).toLocaleString('en-US', { style: 'currency', currency: order.currency.toUpperCase() })}
</span>
<span className="text-xs text-neutral-500 ml-2">
@@ -1084,8 +1084,8 @@ export default function OrganizationSettings() {
{activeTab === 'notifications' && (
<div className="space-y-8">
<div>
<h2 className="text-2xl font-bold text-neutral-900 dark:text-white mb-1">Notification Settings</h2>
<p className="text-sm text-neutral-500 dark:text-neutral-400 mb-6">
<h2 className="text-2xl font-bold text-white mb-1">Notification Settings</h2>
<p className="text-sm text-neutral-400 mb-6">
Choose which notification types you want to receive. These apply to the notification center for owners and admins.
</p>
</div>
@@ -1094,7 +1094,7 @@ export default function OrganizationSettings() {
<SettingsFormSkeleton />
) : (
<div className="space-y-4">
<h3 className="text-sm font-medium text-neutral-500 dark:text-neutral-400 uppercase tracking-wider">Notification categories</h3>
<h3 className="text-sm font-medium text-neutral-400 uppercase tracking-wider">Notification categories</h3>
<div className="bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800 rounded-2xl overflow-hidden divide-y divide-neutral-200 dark:divide-neutral-800">
{notificationCategories.map((cat) => (
<div
@@ -1102,8 +1102,8 @@ export default function OrganizationSettings() {
className="p-4 flex flex-col sm:flex-row sm:items-center justify-between gap-4 hover:bg-neutral-50 dark:hover:bg-neutral-800/50 transition-colors"
>
<div className="flex-1">
<p className="text-sm font-medium text-neutral-900 dark:text-white">{cat.label}</p>
<p className="text-sm text-neutral-500 dark:text-neutral-400 mt-0.5">{cat.description}</p>
<p className="text-sm font-medium text-white">{cat.label}</p>
<p className="text-sm text-neutral-400 mt-0.5">{cat.description}</p>
</div>
<div className="flex items-center shrink-0">
<button
@@ -1149,8 +1149,8 @@ export default function OrganizationSettings() {
{activeTab === 'audit' && (
<div className="space-y-12">
<div>
<h2 className="text-2xl font-bold text-neutral-900 dark:text-white mb-1">Audit log</h2>
<p className="text-sm text-neutral-500 dark:text-neutral-400">Who did what and when for this organization.</p>
<h2 className="text-2xl font-bold text-white mb-1">Audit log</h2>
<p className="text-sm text-neutral-400">Who did what and when for this organization.</p>
</div>
{/* Advanced Filters */}
@@ -1163,7 +1163,7 @@ export default function OrganizationSettings() {
placeholder="e.g. 8a2b3c"
value={auditLogIdFilter}
onChange={(e) => setAuditLogIdFilter(e.target.value)}
className="w-full px-3 py-2 rounded-lg border border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 text-sm text-neutral-900 dark:text-white focus:ring-2 focus:ring-brand-orange outline-none transition-all"
className="w-full px-3 py-2 rounded-lg border border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 text-sm text-white focus:ring-2 focus:ring-brand-orange outline-none transition-all"
/>
</div>
<div className="space-y-1">
@@ -1173,7 +1173,7 @@ export default function OrganizationSettings() {
placeholder="e.g. site_created"
value={auditActionFilter}
onChange={(e) => setAuditActionFilter(e.target.value)}
className="w-full px-3 py-2 rounded-lg border border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 text-sm text-neutral-900 dark:text-white focus:ring-2 focus:ring-brand-orange outline-none transition-all"
className="w-full px-3 py-2 rounded-lg border border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 text-sm text-white focus:ring-2 focus:ring-brand-orange outline-none transition-all"
/>
</div>
<div className="space-y-1">
@@ -1182,7 +1182,7 @@ export default function OrganizationSettings() {
type="date"
value={auditStartDate}
onChange={(e) => setAuditStartDate(e.target.value)}
className="w-full px-3 py-2 rounded-lg border border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 text-sm text-neutral-900 dark:text-white focus:ring-2 focus:ring-brand-orange outline-none transition-all"
className="w-full px-3 py-2 rounded-lg border border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 text-sm text-white focus:ring-2 focus:ring-brand-orange outline-none transition-all"
/>
</div>
<div className="space-y-1">
@@ -1191,7 +1191,7 @@ export default function OrganizationSettings() {
type="date"
value={auditEndDate}
onChange={(e) => setAuditEndDate(e.target.value)}
className="w-full px-3 py-2 rounded-lg border border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 text-sm text-neutral-900 dark:text-white focus:ring-2 focus:ring-brand-orange outline-none transition-all"
className="w-full px-3 py-2 rounded-lg border border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 text-sm text-white focus:ring-2 focus:ring-brand-orange outline-none transition-all"
/>
</div>
</div>
@@ -1240,10 +1240,10 @@ export default function OrganizationSettings() {
<td className="px-4 py-3 text-neutral-600 dark:text-neutral-400 whitespace-nowrap">
{formatDateTime(new Date(entry.occurred_at))}
</td>
<td className="px-4 py-3 text-neutral-900 dark:text-white whitespace-nowrap" title={entry.actor_email || entry.actor_id || 'System'}>
<td className="px-4 py-3 text-white whitespace-nowrap" title={entry.actor_email || entry.actor_id || 'System'}>
{entry.actor_email || entry.actor_id || 'System'}
</td>
<td className="px-4 py-3 font-medium text-neutral-900 dark:text-white whitespace-nowrap" title={entry.action}>{entry.action}</td>
<td className="px-4 py-3 font-medium text-white whitespace-nowrap" title={entry.action}>{entry.action}</td>
<td className="px-4 py-3 text-neutral-600 dark:text-neutral-400">{entry.resource_type}</td>
</tr>
))}
@@ -1255,7 +1255,7 @@ export default function OrganizationSettings() {
{/* Pagination */}
{auditTotal > auditPageSize && (
<div className="flex items-center justify-between px-4 py-3 border-t border-neutral-200 dark:border-neutral-800">
<span className="text-sm text-neutral-500 dark:text-neutral-400">
<span className="text-sm text-neutral-400">
{auditPage * auditPageSize + 1}{Math.min((auditPage + 1) * auditPageSize, auditTotal)} of {auditTotal}
</span>
<div className="flex gap-2">
@@ -1361,7 +1361,7 @@ export default function OrganizationSettings() {
value={deleteConfirm}
onChange={(e) => setDeleteConfirm(e.target.value)}
autoComplete="off"
className="w-full px-3 py-2 text-sm border border-neutral-300 dark:border-neutral-700 rounded-lg bg-white dark:bg-neutral-800 text-neutral-900 dark:text-white placeholder-neutral-400 focus:outline-none focus:ring-2 focus:ring-red-500 dark:focus:ring-red-400"
className="w-full px-3 py-2 text-sm border border-neutral-300 dark:border-neutral-700 rounded-lg bg-white dark:bg-neutral-800 text-white placeholder-neutral-400 focus:outline-none focus:ring-2 focus:ring-red-500 dark:focus:ring-red-400"
placeholder="DELETE"
/>
</div>
@@ -1410,7 +1410,7 @@ export default function OrganizationSettings() {
className="bg-white dark:bg-neutral-900 rounded-2xl shadow-2xl max-w-md w-full p-6 border border-neutral-200 dark:border-neutral-800"
>
<div className="flex justify-between items-center mb-4">
<h3 className="text-lg font-semibold text-neutral-900 dark:text-white">Cancel subscription?</h3>
<h3 className="text-lg font-semibold text-white">Cancel subscription?</h3>
<button
onClick={() => setShowCancelPrompt(false)}
className="text-neutral-500 hover:text-neutral-700 dark:hover:text-neutral-400"
@@ -1464,7 +1464,7 @@ export default function OrganizationSettings() {
className="bg-white dark:bg-neutral-900 rounded-2xl shadow-2xl max-w-md w-full p-6 border border-neutral-200 dark:border-neutral-800"
>
<div className="flex justify-between items-center mb-4">
<h3 className="text-lg font-semibold text-neutral-900 dark:text-white">Change plan</h3>
<h3 className="text-lg font-semibold text-white">Change plan</h3>
<button
type="button"
onClick={() => setShowChangePlanModal(false)}
@@ -1500,7 +1500,7 @@ export default function OrganizationSettings() {
: 'border-neutral-200 dark:border-neutral-700 hover:border-neutral-300 dark:hover:border-neutral-600'
}`}
>
<span className={`block text-sm font-semibold ${isSelected ? 'text-brand-orange' : 'text-neutral-900 dark:text-white'}`}>
<span className={`block text-sm font-semibold ${isSelected ? 'text-brand-orange' : 'text-white'}`}>
{plan.name}
</span>
<span className="block text-xs text-neutral-500 mt-0.5">{plan.sites}</span>
@@ -1519,7 +1519,7 @@ export default function OrganizationSettings() {
<select
value={changePlanTierIndex}
onChange={(e) => setChangePlanTierIndex(Number(e.target.value))}
className="w-full px-3 py-2 rounded-lg border border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 text-neutral-900 dark:text-white focus:ring-2 focus:ring-brand-orange outline-none"
className="w-full px-3 py-2 rounded-lg border border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 text-white focus:ring-2 focus:ring-brand-orange outline-none"
>
{TRAFFIC_TIERS.map((tier, idx) => (
<option key={tier.value} value={idx}>

View File

@@ -38,7 +38,7 @@ function getEventColor(eventType: string, outcome: string): string {
if (eventType === '2fa_disabled') return 'text-amber-500 dark:text-amber-400 bg-amber-50 dark:bg-amber-950/30'
if (eventType === 'account_deleted') return 'text-red-500 dark:text-red-400 bg-red-50 dark:bg-red-950/30'
if (eventType === 'recovery_codes_regenerated') return 'text-amber-500 dark:text-amber-400 bg-amber-50 dark:bg-amber-950/30'
return 'text-neutral-500 dark:text-neutral-400 bg-neutral-100 dark:bg-neutral-800'
return 'text-neutral-400 bg-neutral-100 dark:bg-neutral-800'
}
function getMethodLabel(entry: AuditLogEntry): string | null {
@@ -120,8 +120,8 @@ export default function SecurityActivityCard() {
return (
<div>
<h2 className="text-xl font-semibold text-neutral-900 dark:text-white mb-1">Security Activity</h2>
<p className="text-neutral-500 dark:text-neutral-400 text-sm mb-6">
<h2 className="text-xl font-semibold text-white mb-1">Security Activity</h2>
<p className="text-neutral-400 text-sm mb-6">
Recent security events on your account{totalCount > 0 ? ` (${totalCount})` : ''}
</p>
@@ -138,7 +138,7 @@ export default function SecurityActivityCard() {
<svg className="w-12 h-12 mx-auto mb-3 text-neutral-300 dark:text-neutral-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M9 12.75L11.25 15 15 9.75m-3-7.036A11.959 11.959 0 013.598 6 11.99 11.99 0 003 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285z" />
</svg>
<p className="text-neutral-500 dark:text-neutral-400">No activity recorded yet.</p>
<p className="text-neutral-400">No activity recorded yet.</p>
</div>
) : (
<div className="space-y-2">
@@ -165,11 +165,11 @@ export default function SecurityActivityCard() {
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 flex-wrap">
<span className="font-medium text-neutral-900 dark:text-white text-sm">
<span className="font-medium text-white text-sm">
{label}
</span>
{method && (
<span className="text-xs px-1.5 py-0.5 rounded bg-neutral-100 dark:bg-neutral-800 text-neutral-500 dark:text-neutral-400">
<span className="text-xs px-1.5 py-0.5 rounded bg-neutral-100 dark:bg-neutral-800 text-neutral-400">
{method}
</span>
)}
@@ -179,7 +179,7 @@ export default function SecurityActivityCard() {
</span>
)}
</div>
<div className="flex items-center gap-2 mt-0.5 text-xs text-neutral-500 dark:text-neutral-400 flex-wrap">
<div className="flex items-center gap-2 mt-0.5 text-xs text-neutral-400 flex-wrap">
{reason && <span>{reason}</span>}
{reason && (deviceStr || entry.ip_address) && <span>&middot;</span>}
{deviceStr && <span>{deviceStr}</span>}
@@ -189,7 +189,7 @@ export default function SecurityActivityCard() {
</div>
<div className="flex-shrink-0 text-right">
<span className="text-xs text-neutral-500 dark:text-neutral-400" title={formatDateTimeFull(new Date(entry.created_at))}>
<span className="text-xs text-neutral-400" title={formatDateTimeFull(new Date(entry.created_at))}>
{formatRelativeTime(entry.created_at)}
</span>
</div>

View File

@@ -72,7 +72,7 @@ function NotificationCenterPlaceholder() {
return (
<div className="text-center max-w-md mx-auto py-8">
<BellIcon className="w-12 h-12 text-neutral-300 mx-auto mb-4" />
<h3 className="text-lg font-medium text-neutral-900 dark:text-white mb-2">Notification Center</h3>
<h3 className="text-lg font-medium text-white mb-2">Notification Center</h3>
<p className="text-sm text-neutral-500 mb-4">View and manage all your notifications in one place.</p>
<Link href="/notifications" className="inline-flex items-center gap-2 px-4 py-2 bg-brand-orange text-white rounded-lg hover:bg-brand-orange/90 transition-colors">
Open Notification Center

View File

@@ -56,8 +56,8 @@ export default function TrustedDevicesCard() {
return (
<div>
<h2 className="text-xl font-semibold text-neutral-900 dark:text-white mb-1">Trusted Devices</h2>
<p className="text-neutral-500 dark:text-neutral-400 text-sm mb-6">
<h2 className="text-xl font-semibold text-white mb-1">Trusted Devices</h2>
<p className="text-neutral-400 text-sm mb-6">
Devices that have signed in to your account. Removing a device means the next sign-in from it will trigger a new device alert.
</p>
@@ -74,7 +74,7 @@ export default function TrustedDevicesCard() {
<svg className="w-12 h-12 mx-auto mb-3 text-neutral-300 dark:text-neutral-600" fill="none" stroke="currentColor" strokeWidth={1.5} viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M9 17.25v1.007a3 3 0 01-.879 2.122L7.5 21h9l-.621-.621A3 3 0 0115 18.257V17.25m6-12V15a2.25 2.25 0 01-2.25 2.25H5.25A2.25 2.25 0 013 15V5.25A2.25 2.25 0 015.25 3h13.5A2.25 2.25 0 0121 5.25z" />
</svg>
<p className="text-neutral-500 dark:text-neutral-400">No trusted devices yet. They appear after you sign in.</p>
<p className="text-neutral-400">No trusted devices yet. They appear after you sign in.</p>
</div>
) : (
<div className="space-y-2">
@@ -83,7 +83,7 @@ export default function TrustedDevicesCard() {
key={device.id}
className="flex items-center gap-3 rounded-xl border border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 px-4 py-3"
>
<div className="flex-shrink-0 w-9 h-9 rounded-lg flex items-center justify-center bg-neutral-100 dark:bg-neutral-800 text-neutral-500 dark:text-neutral-400">
<div className="flex-shrink-0 w-9 h-9 rounded-lg flex items-center justify-center bg-neutral-100 dark:bg-neutral-800 text-neutral-400">
<svg className="w-5 h-5" fill="none" stroke="currentColor" strokeWidth={1.5} viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d={getDeviceIcon(device.display_hint)} />
</svg>
@@ -91,7 +91,7 @@ export default function TrustedDevicesCard() {
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
<span className="font-medium text-neutral-900 dark:text-white text-sm truncate">
<span className="font-medium text-white text-sm truncate">
{device.display_hint || 'Unknown device'}
</span>
{device.is_current && (
@@ -100,7 +100,7 @@ export default function TrustedDevicesCard() {
</span>
)}
</div>
<div className="flex items-center gap-2 mt-0.5 text-xs text-neutral-500 dark:text-neutral-400">
<div className="flex items-center gap-2 mt-0.5 text-xs text-neutral-400">
<span title={formatDateTimeFull(new Date(device.first_seen_at))}>
First seen {formatRelativeTime(device.first_seen_at)}
</span>