Documentation Index
Fetch the complete documentation index at: https://docs.bunship.com/llms.txt
Use this file to discover all available pages before exploring further.
Adding New Pages
BunShip Pro uses TanStack Router with file-based routing. Create a file and you have a route.
How Routing Works
Routes live in apps/web/src/routes/. The file name becomes the URL path:
| File | URL |
|---|
routes/index.tsx | / |
routes/_app/dashboard.tsx | /dashboard |
routes/_app/organizations_.$orgId/members.tsx | /organizations/:orgId/members |
routes/_marketing/pricing.tsx | /pricing |
Layout Groups
Files are organized into layout groups using the underscore prefix:
| Prefix | Layout | Purpose |
|---|
_app/ | Authenticated layout with sidebar | Dashboard, settings, org pages |
_auth/ | Minimal centered layout | Login, register, password reset |
_marketing/ | Marketing header/footer | Pricing, blog, contact, legal |
The layout file (e.g., _app.tsx) wraps all pages in that group with shared UI — sidebar, header, auth protection.
Adding a Dashboard Page
- Create a file in
routes/_app/:
// apps/web/src/routes/_app/analytics.tsx
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/_app/analytics")({
component: AnalyticsPage,
});
function AnalyticsPage() {
return (
<div className="space-y-6">
<div>
<h1 className="text-2xl font-semibold tracking-tight">Analytics</h1>
<p className="text-sm text-muted-foreground">View your usage analytics.</p>
</div>
{/* Your page content */}
</div>
);
}
- The page is now accessible at
/analytics (authentication required automatically via the _app layout).
Adding an Organization Page
Organization pages are scoped to a specific org and have access to the org ID from the URL:
// apps/web/src/routes/_app/organizations_.$orgId/reports.tsx
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/_app/organizations_/$orgId/reports")({
component: ReportsPage,
});
function ReportsPage() {
const { orgId } = Route.useParams();
return (
<div className="space-y-6">
<div>
<h1 className="text-2xl font-semibold tracking-tight">Reports</h1>
<p className="text-sm text-muted-foreground">Generate and view reports.</p>
</div>
{/* Use orgId to fetch org-specific data */}
</div>
);
}
This page is available at /organizations/:orgId/reports.
The sidebar navigation is defined in apps/web/src/components/sidebar.tsx. There are two navigation arrays:
Main Navigation
Add items to the DEFAULT_SECTIONS array:
const DEFAULT_SECTIONS: NavSection[] = [
{
label: "Workspace",
items: [
{ label: "Dashboard", href: "/dashboard", icon: LayoutDashboard },
{ label: "Organizations", href: "/organizations", icon: Building2 },
{ label: "Analytics", href: "/analytics", icon: BarChart3 }, // new
],
},
// ...
];
Organization Navigation
Add items to the ALL_ORG_ITEMS array. Each item belongs to either the org section (user-facing) or dev section (developer tools):
const ALL_ORG_ITEMS: Array<NavItem & { section: "org" | "dev" }> = [
// existing items...
{ section: "dev", label: "Reports", href: "/reports", icon: FileText }, // new
];
Import icons from lucide-react.
Protecting Pages by Role
The organization layout provides role context. Use the useOrgContext() hook to check the current user’s role:
import { useOrgContext } from '../organizations_.$orgId'
function ReportsPage() {
const { role } = useOrgContext()
// Hide admin-only sections
if (role === 'viewer') {
return <p className="text-sm text-muted-foreground">You don't have access to reports.</p>
}
return (
// Full page content
)
}
Role Hierarchy
Roles are hierarchical: owner > admin > member > viewer. Permissions are defined in packages/config/src/features.ts:
permissions: {
owner: ["*"],
admin: ["org:read", "org:update", "members:*", "billing:read", ...],
member: ["org:read", "members:read", "projects:*", "files:read", "files:upload"],
viewer: ["org:read", "members:read", "projects:read", "files:read"],
}
Adding a Marketing Page
Marketing pages use the _marketing layout with the public header and footer:
// apps/web/src/routes/_marketing/about.tsx
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/_marketing/about")({
component: AboutPage,
});
function AboutPage() {
return (
<div className="mx-auto max-w-4xl px-6 py-24">
<h1 className="text-4xl font-bold tracking-tight">About Us</h1>
<p className="mt-4 text-lg text-muted-foreground">Our story...</p>
</div>
);
}
To add it to the marketing header navigation, edit apps/web/src/components/marketing-header.tsx.
Next Steps