Profile Personalization
Tailor home pages, default list views, and insight strips per role and profile type — no user-side config required
Overview
Profile Personalization lets you tailor what different user profiles see in OptiCRM without making every user configure their own workspace. Three independently-useful features compose into a full experience:
- Profile home pages — what a user lands on after logging in
- Profile object default views — which list view loads first when a user opens an entity page
- Insight strips — 2-4 tile "at-a-glance" panels above entity list grids
All three are configured by subject — role, profile type, or custom assignment. If you've never touched any of this, nothing changes — existing behavior is the default.
Navigate to Settings → Presentation — Design → Home Pages / Insight Strips for design, and Settings → Presentation — Assignments → List Views & Insight Strips for the per-subject assignment tables.
Feature 1 — Home Pages
Configure the landing screen each role or profile sees after login.
What you can land a user on
- A saved dashboard (from Administration → Dashboards)
- A saved report (from Analytics → Reports Explorer)
- A record list (any configured list view on any entity)
Configuring a home page
- Go to Settings → Presentation — Design → Home Pages
- Click + New Home Page
- Pick the subject — which users this applies to:
- Role (TEACHER, ADMISSIONS_COUNSELLOR, PRINCIPAL, etc.) — applies to every user with that role
- Profile type (PROFILE_STAFF, PROFILE_STUDENT, etc.) — applies to every user with that profile type assigned
- Custom assignment — applies to a specific role definition or user group
- Pick the target — dashboard, report, or record list
- Save. Users matching the subject land on this screen on their next login.
Resolution priority
When a user logs in and matches multiple subjects (e.g., they have both a role AND a profile type), the resolver picks the most specific match first:
custom assignment > profile type > built-in role > system defaultSo a user with profileType=COUNSELLOR, role=TEACHER gets the counsellor home page if one exists, else the teacher one.
When a configured target gets deleted
If an admin deletes a dashboard or report that's currently set as someone's home page, the user gracefully falls back to the system default landing page on their next login. A warning banner explains "your home page is unavailable" — they're not stuck on a broken screen.
Feature 2 — Profile Object Default Views
Control which list view loads first when a user opens /o/Student, /o/Invoice, etc.
Configuring a default view
- Go to Settings → Presentation — Assignments → List Views & Insight Strips
- Pick a subject in the sidebar (e.g., "TEACHER" role)
- For each entity row in the table, use the Default view dropdown to pick which list view loads first for users matching that subject
- Click Save
What users see
- A user matching the subject opens
/o/Student→ the configured list view loads automatically - The user can still switch to other list views via the selector — personalization only changes the initial load
- If no subject matches, the tenant's
isDefault=truelist view loads (the pre-personalization behavior)
Copy defaults from another profile
Click Copy from Profile on any subject row → pick a source subject → review the per-entity diff → confirm. Saves re-configuring every entity manually when bootstrapping a new profile type.
Private list views
Private list views (created by a specific user) appear in the dropdown with a (private) badge. If you assign a private view to a role, users other than the creator may not have permission to see it — a yellow warning flags this mismatch.
Feature 3 — Insight Strips
Add a row of 2-4 data tiles above any entity list grid. Great for "at-a-glance" context — counts, charts, recent records — before users start scrolling.
Creating a strip
- Go to Settings → Customisation → Insight Strips
- Click + New Strip
- Name the strip (e.g., "Student at-risk overview")
- Pick the target entity — this locks the strip to one entity type
- Add 2-4 tiles (see below)
- Save
Tile types
| Tile type | Shows | Good for |
|---|---|---|
| METRIC | A single number (COUNT, SUM, AVG, PERCENT aggregation) | "Outstanding invoices: 137", "Avg enquiry age: 4 days" |
| CHART | Donut / bar chart grouping rows by a field | "Enquiries by stage", "Students by grade" |
| LIST_PREVIEW | Top 5 most-recent rows as clickable previews | "Recent enrolments", "Most recent invoices" |
Every tile has:
- Title — shown above the tile
- Width —
sm(1 column),md(2),lg(3). Four small tiles fit in one row. - Color hint —
default,success,warn,danger. Applied as a subtle left-border accent. Useful for "overdue count" = danger, "active count" = success. - Source report — every tile pulls from a saved report (from Analytics → Reports Explorer). Pick the report first, then configure what the tile shows from it.
Optional tile filters
Narrow what a tile shows by adding a filter. At launch, filters are equality-only:
{status: "ACTIVE"}→ rows where status equals ACTIVE{overdue: true}→ rows where overdue is true
Filters are on the tile, not the report — one report can feed several differently-filtered tiles without creating a report per tile. Time-windowed filters like "this week" or "overdue > 30 days" aren't expressible yet; workaround is to bake the time window into the tile's source report.
Assigning a strip to users
- Go to Settings → Presentation — Assignments → List Views & Insight Strips
- Pick the subject (role or profile type)
- Find the entity row for the strip's target (e.g., Student)
- Use the Strip dropdown to pick your strip. "None" means no strip
- If you pick an archived strip, an amber warning shows:
"{name}" is archived — users of this profile won't see it until it's re-activated
What users see
When a matching user opens /o/Student (or whichever entity), the strip renders above the grid. Users can collapse the strip — their choice is remembered in their browser.
If no strip is assigned, the page renders exactly as it did before — no empty region, no placeholder.
Editing, archiving, duplicating
- Edit — users see updated tiles on their next page load (cache auto-invalidates)
- Archive — sets
isActive=false. Users stop seeing the strip; any profile assignment pointing at it keeps the reference but doesn't render. Re-activating restores the display. - Duplicate — creates a copy named
{original} (Copy)with its own tile IDs. Useful for trying variations.
What if a source report gets deleted
If an admin deletes a report that a strip's tile references, only that tile breaks — it shows "Data unavailable". The rest of the strip still renders. Sibling tiles on different reports are unaffected. To fix: restore the report, or edit the strip to point the broken tile at a different report.
Feature flags — when might personalization be off?
Personalization is enabled by default for every institution (existing and new signups). No ops configuration required.
In rare cases, an operator can disable a specific feature via Coolify environment variables as an emergency kill-switch:
FEATURE_PROFILEPERSONALIZATION_HOMEPAGE=0 # disable home pages globally
FEATURE_PROFILEPERSONALIZATION_OBJECTDEFAULTS=0 # disable object defaults globally
FEATURE_PROFILEPERSONALIZATION_INSIGHTSTRIPS=0 # disable insight strips globallyThese are kill-switches only — use them if a feature misbehaves and you need a quick disable before the next code fix. Under normal operation, no env config is needed.
FAQ
Why are tile filters equality-only?
Launch scope enforces primitive equality to keep the surface predictable. Time-windowed filters, numeric ranges, and NOT-EQUALS are on the roadmap. For now, bake time windows into the tile's source report (e.g., create an "Enquiries — This Week" report with a createdAt > now() - 7d filter, then reference it from a METRIC tile).
Can I share a strip across entities?
No — one strip, one entity. This is a deliberate scope decision. Cross-entity dashboards belong in the Dashboards area; strips are a focused "at-a-glance context for THIS entity's list page".
Two subjects match the same user — which wins?
The resolver picks most-specific first: custom assignment → profile type → built-in role → tenant default. So a user with profileType=COUNSELLOR, role=TEACHER gets the counsellor config if one exists, else teacher.
Is there an audit trail?
Yes. Every create, update, archive, duplicate, set, unset, and copy writes an audit log entry under the action names INSIGHT_STRIP_*, PROFILE_OBJECT_DEFAULT_*, and PROFILE_HOME_PAGE_*. Check the Audit Log admin surface or query the audit_logs table.
How fast are strips?
Fast. Benchmark numbers at launch: full strip p95 of 34 ms cold, 1.3 ms warm at demo data scale. Per-tile caching is tiered (METRIC 60s, CHART 300s, LIST_PREVIEW 30s) with stale-while-revalidate so real-world response is near-instant.
Before enabling strips for a tenant with > ~1000 rows in a strip's source entity, consider re-running the benchmark against that tenant's data to confirm performance under real load.
What if Redis is down?
Everything still works. The cache is fail-soft — cache misses fall through to direct DB reads. Strips and defaults render a little slower but nothing breaks. Users don't see errors.