Portfolio — the home
The Portfolio is what you land on after signing in (/home). It's the example's "home
screen": a ranked view of every building you're part of, most-urgent first, with the AI
assistant (Carola) front and centre. Click a building to enter its
workspace at /project/$projectKey, where the project-scoped apps —
ProcessN, ProtokollN, NyhetN,
WikiN — live.
Coming from Django? This is your
indexview — a dashboard that aggregates across several apps. The twist: it composes data from multiple server functions in one loader, and the primary surface is a chat box, not a table.
Where it lives
| Layer | Files |
|---|---|
| Routes | src/routes/_authed.home.tsx (the landing) · src/routes/index.tsx (the public, signed-out home) |
| Server functions | src/lib/project-server.ts (getPortfolio) · src/lib/briefing-server.ts (deadlines) · src/lib/agent-server.ts (notifications, approvals) |
| Components | src/components/portfolio/Portfolio.tsx · CarolaInline · NeedsYou · ProjectMapPanel |
What you see
The Portfolio is deliberately chat-first and minimal — not a wall of widgets:
- a one-line greeting and Carola's briefing (a single sentence: clear, or "something's overdue"),
- the single most urgent thing that needs you (an overdue task, a pending approval),
- a prominent composer — ask Carola anything; the full overview comes back in the conversation, on demand,
- a map of your buildings (
ProjectMapPanel, client-only / lazy-loaded), rendered only when you're not mid-conversation.
The design rule worth knowing if you extend it: Carola's UI is one inline canvas that expands and reflows — never in-page overlays, modals, popovers, or floating widgets; everything happens in the page flow. Once a conversation opens it gets its own header (rename · star · add-to-project · files · pop-out) and the right rail becomes Carola's companion. The one exception to "no floating widgets" is floating Carola — the live conversation popped into a real, separate OS window via Document Picture-in-Picture (Carola).
How it loads
/home is a single loader that fans out to
a few server functions and composes them:
export const Route = createFileRoute("/_authed/home")({
// `?scope=` carries Carola's scope so switching it re-lenses the conversation in place
// (pathname stays `/home`) and a reload / deep-link restores it. The provider resolves the
// value (a project key, or "portfolio" / "general") against the viewer's projects.
validateSearch: (search: Record<string, unknown>): { scope?: string } => ({
scope: typeof search.scope === "string" ? search.scope : undefined,
}),
loader: async () => {
const [buildings, findings, approvals, deadlines] = await Promise.all([
getPortfolio(),
listMyNotifications(),
listMyApprovals(),
listMyDeadlines(),
]);
return { buildings, findings, approvals, deadlines };
},
component: Home,
});The loader fires the reads in parallel (Promise.all) and hands the component
{ buildings, findings, approvals, deadlines }. getPortfolio() returns each building
the viewer can see (the same permission filters the apps use) with
its urgency signal — overdue / due-soon ProcessN tasks and the next ProtokollN
meeting — so the list can sort "what needs me most" to the top. The "needs you" rollup
(findings + approvals) comes from the proactive agent.
The seed
bun run db:seed fills the Portfolio (and every app) with realistic data so the rules are
visible, not theoretical. It models Tikab's live portfolio — "Tikab – aktiva uppdrag" —
as nine real construction projects (Slussen, Slakthusområdet, Hisingsbron,
Sturekvarteret, …), each with map coordinates.
- Ten Tikab logins sign in (password
password123); the site admin isandre.holmstrom.tikab@p4o.se(André Holmström). The other nine are his colleagues — samefornamn.efternamn.tikab@p4o.sepattern. Each login has real assignments (at least one overdue and one due-soon), so the urgency ranking is genuine. - ~665 register people are members of the projects, and ~1000 background users give the admin table realistic volume — but only the ten Tikab logins can actually sign in (everyone else has no password).
- 100+ news posts and 100+ wiki articles carry concrete, askable facts, so "ask the project a question" has real answers (NyhetN · WikiN).
Sign in as different people and the same pages change — André (site admin, owns four projects) sees everything; a viewer-role member sees the project but can change nothing. Authorization explains why.
Extending it
- A new urgency signal → add it to
getPortfolio()inproject-server.tsand fold it into the ranking; the building cards read whatever the server returns. - A new landing surface → keep it inline (no modal/widget) and gate it on the
conversation state, like
ProjectMapPaneldoes. - A whole new page → not everything belongs on
/home; add a route and follow Building a feature.