Persistent Settings Implementation - Complete Summary
✅ Implementation Complete
The contacts page now saves and restores user preferences automatically!
What Was Implemented
Settings That Persist
-
Column Count (2, 3, or 4 columns)
- User selects columns via button in top right
- Saved immediately when changed
- Restored on page load
-
Items Per Page (10, 20, 30, 40, or 50)
- User selects from dropdown in pagination area
- Saved immediately when changed
- Restored on page load
Storage Details
- Storage: localStorage (browser-based)
- Key Format:
"{userId}:{pathname}" - Scope: User-specific AND page-specific
- Persistence: Survives page refreshes and browser restarts
Files Changed
app/company/contacts/page.tsx
Imports Added:
import { usePathname } from "next/navigation"
import { usePageStore } from "@lib/pageStore"
State Added:
const pathname = usePathname() || ""
const { savePageSettings, getPageSettings } = usePageStore()
const userId = user?.id || "guest"
// Load columns from localStorage
const [columns, setColumns] = useState<2 | 3 | 4>(() => {
const saved = usePageStore.getState().getPageSettings(userId, pathname)
return (saved?.columns as 2 | 3 | 4) || 4
})
// Load items per page from localStorage
const [itemsPerPage, setItemsPerPage] = useState<number>(() => {
const saved = usePageStore.getState().getPageSettings(userId, pathname)
return (saved?.itemsPerPage as number) || 10
})
Handlers Added:
// Save columns when changed
const handleColumnsChange = (newColumns: number) => {
setColumns(newColumns as 2 | 3 | 4)
savePageSettings(userId, pathname, { columns: newColumns, itemsPerPage })
}
// Save items per page when changed
const handlePaginationChange = (state: any) => {
if (state.itemsPerPage !== itemsPerPage) {
setItemsPerPage(state.itemsPerPage)
savePageSettings(userId, pathname, { columns, itemsPerPage: state.itemsPerPage })
}
}
GridBlock Updated:
<GridBlock
columns={columns}
onColumnsChange={handleColumnsChange}
itemsPerPage={itemsPerPage}
onPaginationChange={handlePaginationChange}
// ... other props
/>
How It Works
Flow Diagram
┌─────────────────────────────────────────────────┐
│ 1. User Opens /company/contacts │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 2. Component Loads │
│ - Get userId from useUserAuth() │
│ - Get pathname from usePathname() │
│ - Call getPageSettings(userId, pathname) │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 3. Initialize State │
│ - columns = saved?.columns || 4 │
│ - itemsPerPage = saved?.itemsPerPage || 10 │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 4. Render GridBlock with Saved Settings │
│ - Show N columns │
│ - Show M items per page │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 5. User Changes Setting │
│ - Click column button (2, 3, or 4) │
│ - OR select items per page dropdown │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 6. Handler Called │
│ - handleColumnsChange(newColumns) │
│ - OR handlePaginationChange(state) │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 7. Save to localStorage │
│ savePageSettings(userId, pathname, { │
│ columns: N, │
│ itemsPerPage: M │
│ }) │
└─────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 8. Settings Persisted! │
│ localStorage["aiqlick-page-store"] = { │
│ pageSettings: { │
│ "userId:pathname": { columns, items } │
│ } │
│ } │
└─────────────────────────────────────────────────┘
localStorage Structure
{
"state": {
"pageSettings": {
"user-123:/company/contacts": {
"columns": 3,
"itemsPerPage": 20
},
"user-123:/company/jobs": {
"columns": 4,
"itemsPerPage": 10
},
"user-456:/company/contacts": {
"columns": 2,
"itemsPerPage": 50
}
}
},
"version": 0
}
Key Format: "{userId}:{pathname}"
This ensures:
- ✅ Different users have separate settings
- ✅ Different pages have separate settings
- ✅ Same user on same page always gets same settings
Test Scenarios
Scenario 1: Basic Persistence
- Set 3 columns
- Refresh page
- ✅ Still shows 3 columns
Scenario 2: Multiple Settings
- Set 2 columns and 30 items per page
- Refresh page
- ✅ Still shows 2 columns and 30 items
Scenario 3: Multiple Users
- User A sets 2 columns
- User B sets 4 columns
- ✅ Each user sees their own settings
Scenario 4: Multiple Pages
- Contacts: 3 columns, 20 items
- Jobs: 4 columns, 10 items
- ✅ Each page remembers its own settings
Scenario 5: Browser Restart
- Set 2 columns and 40 items
- Close browser
- Reopen browser
- ✅ Still shows 2 columns and 40 items
Adding to Other Pages
To add persistent settings to another page (e.g., Jobs, Candidates), follow this pattern:
Step 1: Import Dependencies
import { usePathname } from "next/navigation"
import { usePageStore } from "@lib/pageStore"
Step 2: Get User and Path
const { user } = useUserAuth()
const pathname = usePathname() || ""
const { savePageSettings } = usePageStore()
const userId = user?.id || "guest"
Step 3: Load Settings
const [columns, setColumns] = useState<2 | 3 | 4>(() => {
const saved = usePageStore.getState().getPageSettings(userId, pathname)
return (saved?.columns as 2 | 3 | 4) || 4
})
Step 4: Save on Change
const handleColumnsChange = (newColumns: number) => {
setColumns(newColumns as 2 | 3 | 4)
savePageSettings(userId, pathname, { columns: newColumns })
}
Step 5: Pass to GridBlock
<GridBlock
columns={columns}
onColumnsChange={handleColumnsChange}
/>
System Architecture
pageStore (Zustand with Persist)
- Zustand store with persist middleware
- Saves to localStorage automatically
- Only persists
pageSettings(not temporary page info)
Methods:
savePageSettings(userId, pathname, settings)- Save settingsgetPageSettings(userId, pathname)- Load settingsclearPageSettings(userId, pathname)- Clear settings
PageSettings Type
interface PageSettings {
columns?: 2 | 3 | 4
viewMode?: "grid" | "table"
itemsPerPage?: number
[key: string]: any // Extensible for future settings
}
Future Enhancements
Additional Settings to Save
- View Mode (grid vs table)
- Sort Order (name, date, etc.)
- Filter State (active filters)
- Search Term (current search)
- Expanded/Collapsed Sections
- Custom Display Options
Backend Sync (Optional)
Currently uses localStorage (client-only). For cross-device sync:
- Add GraphQL mutations to save settings to backend
- Replace
savePageSettings()with API call - Keep same interface - no page code changes needed!
Documentation
- Page Settings Guide - Complete usage guide
CONTACTS_COLUMN_SETTINGS_TEST.md- Test scenarios (legacy doc, removed in repo cleanup)- lib/pageStore.ts - Store implementation
- lib/types/pagestore.ts - TypeScript types
Benefits Achieved
✅ Better UX - Users don't have to reset preferences every time ✅ User-specific - Each user has their own preferences ✅ Page-specific - Different pages remember different settings ✅ Persistent - Survives refreshes and browser restarts ✅ Simple API - Easy to add to new pages ✅ Type-safe - Full TypeScript support ✅ Extensible - Easy to add new settings types
Production Ready ✅
The implementation is:
- ✅ Tested and working
- ✅ TypeScript error-free
- ✅ Documented
- ✅ Follows established patterns
- ✅ Ready for deployment
Users can now enjoy a personalized experience that remembers their preferences!