Skip to main content

Settings

Settings

Component File: src/features/admin/components/Settings.tsx Route / navigation: Browser path `/settings` (PageView settings; PageRouter renders Settings — no separate *Tool enum). Access Level: All authenticated users Last Updated: March 22, 2026

> Implementation Note: The live page is a standalone settings route in the current router. The implementation uses Supabase clients plus store/auth hooks rather than AppContext.

Overview

The Settings component provides a comprehensive user settings interface with five tabs: Profile, Notifications, Security, Preferences, and Organization. Users can manage their personal information, notification preferences, security settings, application preferences, and organization details (if authorized). The interface adapts based on user role and permissions.

UI Features

Main Features

  • 5-Tab Interface:
  • Profile
  • Notifications
  • Security
  • Preferences
  • Organization
  • Back Button: Returns to Administration Hub (if accessed from there)
  • Responsive Design: Mobile-friendly tab layout

Tab 1: Profile

  • Avatar Section:
  • Current profile picture (initials fallback)
  • Change Photo button
  • File upload (JPG, PNG, GIF, max 5MB)
  • Personal Information:
  • First Name
  • Last Name
  • Email Address
  • Phone Number
  • Job Title
  • Role (dropdown)
  • Save Changes button

Tab 2: Notifications

Implemented by `NotificationPreferences` (src/components/shared/NotificationPreferences.tsx), loaded from `user_preferences.notification_settings` via saveNotificationSettings / fetchUserPreferences.

  • Channels: Email on/off, in-app on/off. (Browser push is not implemented; no push toggle is shown.)
  • Role-based categories: Expandable sections depend on position (director, bookkeeper, assistant, custom, etc.) — e.g. donation thresholds, financial report emails, expenses, messaging alerts. Bookkeepers include a Financial Reports block (weekly donation summary, monthly statement, quarterly, year-end) aligned with edge functions. Assistants include Weekly Summaries (weekly_reports) for weekly email eligibility.
  • Digest / frequency: There is no user-facing “real-time vs daily digest” control. Stored digest.frequency is normalized to `realtime`; donation alert emails send immediately when email is on and amount/category rules pass (see send-donation-alert).
  • Save: “Save Notification Preferences” persists the full JSON blob to user_preferences.notification_settings.

Tab 3: Security

  • Password Management:
  • Current password field
  • New password field
  • Confirm password field
  • Change Password button
  • Reauthentication required for password changes
  • Two-Factor Authentication:
  • Enable/disable 2FA
  • QR code for setup
  • Requires current password to disable 2FA
  • Active Sessions:
  • Current device session details only
  • Multi-device history planned

Tab 4: Preferences

  • Language & Region:
  • Language selector (English, Spanish, etc.)
  • Timezone selector
  • Date format selector (MM/DD/YYYY, DD/MM/YYYY, YYYY-MM-DD)
  • Currency format
  • Display Preferences:
  • Theme toggle (Light/Dark) - syncs with global theme
  • Compact mode
  • Default view settings
  • Save Preferences button

Tab 5: Organization

  • Organization Details Card:
  • Organization Name (editable)
  • EIN (Tax ID) (editable)
  • Address (editable)
  • Phone Number (editable)
  • Description (editable)
  • Logo + Social media links (editable)
  • Save Organization Details button
  • Access Control:
  • Parent org admins can edit master organization details.
  • Enterprise fund users can edit their fund's overrides only if they are directors with read/write access.
  • Read-only users see a read-only notice.
  • Inheritance: Fund organizations can leave fields blank to inherit from the parent org (all fields).
  • Scope: Parent org edits apply to all funds; fund edits are overrides for that fund only.
  • Billing & Subscription Card:
  • Current Plan badge (Beta Access)
  • Coming Soon placeholder for:
  • Subscription management
  • Invoice history
  • Payment method updates
  • *Note: Stripe billing integration planned for future release*

Settings Layout

┌─────────────────────────────────────────────────┐
│ [Profile] [Notifications] [Security] [Preferences] [Organization] │
├─────────────────────────────────────────────────┤
│                                                 │
│  Profile Information                            │
│  ┌─────────────────────────────────────┐       │
│  │ [Avatar]  Change Photo              │       │
│  └─────────────────────────────────────┘       │
│                                                 │
│  First Name: [John        ]                    │
│  Last Name:  [Doe         ]                    │
│  Email:      [john@...    ]                    │
│  Phone:      [+1 555...   ]                    │
│                                                 │
│  [Save Changes]                                 │
└─────────────────────────────────────────────────┘

Data Requirements

User Profile

  • user_id (uuid)
  • first_name (string)
  • last_name (string)
  • email (string)
  • phone (string, nullable)
  • job_title (string, nullable)
  • role (enum) - 'parent_org', 'fund_user', 'donor', 'volunteer'
  • position (enum) - 'director', 'bookkeeper', 'assistant', 'custom'
  • access_level (enum) - 'read_write', 'read_only'
  • avatar_url (string, nullable)

Notification Preferences

  • notification_settings (jsonb on user_preferences)
  • channels: email, in_app (push may exist in legacy JSON but is unused)
  • categories: position-specific keys (donations, fund_donations, financial_reports, weekly_reports, messaging, etc.) with enabled flags and thresholds
  • digest: { frequency, quiet_hours_start, quiet_hours_end }frequency is always treated as `realtime` in product behavior (UI does not offer daily batching for donation alerts)

Security Settings

  • two_factor_enabled (boolean)
  • active_session (object) - Current device, browser, OS
  • session_history (planned)

User Preferences

  • language (string) - 'en', 'es', 'fr', etc.
  • timezone (string) - IANA timezone
  • date_format (string) - 'MM/DD/YYYY', 'DD/MM/YYYY', 'YYYY-MM-DD'
  • theme (enum) - 'light', 'dark', 'system'

Organization Settings

  • organization_id (uuid) - Parent organization ID or selected fund ID
  • organization_name (string) - Editable by parent_org admins; fund users edit overrides when authorized
  • ein (string) - Tax ID, editable by parent_org admins; fund overrides allowed when authorized
  • address (string) - Physical address, editable by parent_org admins; fund overrides allowed when authorized
  • phone (string) - Contact phone, editable by parent_org admins; fund overrides allowed when authorized
  • description (string) - Organization description, editable by parent_org admins; fund overrides allowed when authorized
  • Billing fields (planned):
  • subscription_plan (string)
  • subscription_cost (decimal)
  • subscription_status (enum) - 'active', 'past_due', 'canceled'
  • next_billing_date (date)

Request/Response Schemas

interface UserProfile {
  user_id: string;
  first_name: string;
  last_name: string;
  email: string;
  phone?: string;
  job_title?: string;
  role: 'admin' | 'manager' | 'staff' | 'donor' | 'volunteer';
  avatar_url?: string;
}

interface NotificationPreferences {
  notification_settings: {
    channels: {
      email: boolean;
      push: boolean;
      in_app: boolean;
    };
    categories: {
      donations?: { enabled: boolean; threshold?: number; include_recurring?: boolean };
      fund_donations?: { enabled: boolean; threshold?: number; include_recurring?: boolean };
    };
    digest?: { frequency: 'realtime' | 'daily' };
  };
}

interface UserPreferences {
  language: string;
  timezone: string;
  date_format: string;
  theme: 'light' | 'dark' | 'system';
}

interface OrganizationSettings {
  organization_id: string;
  organization_name: string;
  ein: string;
  address: string;
  phone: string;
  subscription: {
    plan: string;
    cost: number;
    status: 'active' | 'past_due' | 'canceled';
    next_billing_date: string;
  };
  api_key: string;
}

Authentication & Authorization

Required Permissions

  • profile:read - View own profile
  • profile:update - Update own profile
  • organization:read - View organization settings (admin only)
  • organization:update - Update organization settings (admin only)

Role-Based Access

  • All Users: Can access Profile, Notifications, Security, Preferences tabs
  • Parent Org Admin (role='parent_org'): Can access Organization tab and edit parent org details
  • Fund User (role='fund_user'): Can access Organization tab for their fund only; edit requires director + read_write + eligible tier
  • Donor/Volunteer: Organization tab hidden

Business Logic & Validations

Frontend Validations

  • Email format validation
  • Phone format validation
  • Password strength requirements (min 8 chars, uppercase, lowercase, number, special char)
  • Password confirmation match
  • File size limit for avatar (5MB)
  • Accepted file types for avatar (JPG, PNG, GIF)
  • File size limit for organization logo (10MB)
  • Accepted file types for logo (JPG, PNG, SVG, WebP)

Backend Validations (Supabase)

  • Unique email check
  • Valid timezone
  • Valid language code
  • Valid date format
  • Password complexity requirements (Supabase Auth)
  • Current password verification for password change (reauthentication)
  • Organization access authorization

Business Rules

  • Theme preference syncs with global theme state
  • Notification preferences affect email/push delivery
  • 2FA required for admin users (future)
  • API key regeneration invalidates old key
  • Organization settings only visible to authorized users
  • Profile changes logged in audit trail

State Management

Local State

  • emailNotifications - Email toggle
  • pushNotifications - Push toggle
  • donationAlerts - Donation alerts toggle
  • weeklyReports - Weekly reports toggle
  • monthlyReports - Monthly reports toggle
  • language - Selected language
  • timezone - Selected timezone
  • dateFormat - Selected date format

Global State (Zustand Store)

  • selectedEntity - Current organization (from useAppStore)
  • administrationTool - Current admin tool (from useAppStore)
  • setAdministrationTool - Navigation function (from useAppStore)

Theme Context

  • theme - Global theme setting (from useTheme)
  • toggleTheme - Theme toggle function (from useTheme)

Dependencies

Internal Dependencies

  • useAppStore - Zustand global state
  • useAuth - Current user and refreshUser function
  • useTheme - Theme context
  • fetchUserPreferences, upsertUserPreferences - Preferences from Supabase
  • updateUser, uploadProfilePhoto, updateUserPassword - User management
  • fetchParentOrganization, updateOrganization - Organization management (db.ts)
  • UI components (Card, Button, Input, Switch, Tabs, etc.)

External Libraries

  • lucide-react - Icons
  • sonner - Toast notifications

Error Handling

Error Scenarios

1. Network Error: Show toast "Unable to save settings" 2. Invalid Email: Show error "Invalid email format" 3. Weak Password: Show error "Password too weak" 4. Password Mismatch: Show error "Passwords don't match" 5. Wrong Current Password: Show toast "Current password incorrect" 6. Avatar Too Large: Show error "Image file is too large. Please use an image under 5MB." 7. Logo Too Large: Show error "Logo file is too large. Please use an image under 10MB." 8. Logo RLS Denied: Show error "Permission denied. Only organization directors can upload logos." 9. Logo Invalid Type: Show error "Invalid file type. Please upload a JPG, PNG, SVG, or WebP image." 10. Unauthorized: Show toast "You don't have permission"

Loading States

  • Initial load: Skeleton form fields
  • Save actions: Button loading state
  • Avatar upload: Progress indicator
  • Tab switching: Instant (no loading)

Migration Notes

Phase 1: Profile/Data Integration

1. Create/expand Supabase user/profile helpers and edge-function wiring where needed 2. Implement profile endpoints 3. Test CRUD operations 4. Add avatar upload

Phase 2: Preferences

1. Implement notification preferences 2. Implement user preferences 3. Test theme synchronization 4. Add validation

Phase 3: Security

1. Implement password change 2. Add 2FA setup 3. Add session management 4. Implement security log

Phase 4: Organization ✅ COMPLETE (January 2025)

1. ✅ Implement organization settings (name, EIN, address, phone, description) 2. 🔲 Add billing integration (Stripe - planned) 3. 🔲 Implement API key management (planned) 4. 🔲 Add usage analytics (planned)

Related Documentation

Additional Notes

Theme Synchronization

The theme toggle in Settings syncs with the global theme state in AppContext. Changes apply immediately across the entire application.

Notification Delivery

  • Email notifications sent via configured email service
  • Push notifications require browser permission
  • Preferences stored per user
  • Can be overridden by organization-level settings

Security Best Practices

  • Passwords hashed with bcrypt
  • 2FA using TOTP (Time-based One-Time Password)
  • Session tokens with expiration
  • Security events logged
  • Failed login attempts tracked

Organization Tab Visibility & Permissions

The Organization tab is visible to parent_org and fund_user user types, but with different access levels:

  • Parent Org Admin (parent_org + director): Full edit access to parent organization details (name, EIN, address, phone, description, logo, social links). Changes apply to all sub-funds.
  • Fund User (director + read_write on enterprise/pro): Can edit their fund's overrides only, including uploading a fund-specific logo. Leaving fields blank inherits from the parent org.
  • Fund User (non-director or read_only): Can view the Organization tab but sees a read-only notice.
  • Donor/Volunteer: Organization tab is hidden from navigation.

API Key Usage

  • Used for programmatic API access
  • Should be kept secret
  • Regeneration invalidates old key
  • Rate limited per key
  • Logged for audit purposes

Organization Logo Upload

  • Storage bucket: org-logos (public read)
  • File path: {organization_id}/logo.{ext}
  • Max size: 10MB (bucket-enforced)
  • Accepted types: JPG, PNG, SVG, WebP
  • RLS policies:
  • INSERT/UPDATE/DELETE: is_parent_org_admin() OR fund director (role=fund_user, position=director, access_level=read_write) for their own org folder
  • SELECT: Public (anyone can view logos)
  • Upsert: Uses upsert: true so re-uploads overwrite the existing file
  • Inheritance: Fund orgs with no logo display the parent org's logo

Changelog

Feb 10, 2026 — Logo Upload Fix

  • P0 Fix: Added storage RLS INSERT/UPDATE/DELETE policies for fund directors on org-logos bucket (was parent_org-only, blocking all fund user uploads)
  • P1 Fix: Increased bucket file size limit from 2MB to 10MB
  • P2 Fix: Added WebP to accepted MIME types (bucket + frontend validation + file input)
  • P2 Fix: Improved error messaging — specific toast messages for RLS denial, file too large, invalid type (was generic "Failed to upload logo")

Synced from IFMmvp-Frontend documentation: pages/administration/02-SETTINGS.md

Ready to Get Started?

See how Alignmint can simplify your nonprofit's operations. Schedule a free demo with our team and we'll walk you through everything.

Questions? Email us at steven@getalignmint.org

Ready to get started?Start Plus Trial