-
-
- Construction Project Management
+ {/* hero */}
+
+ {/* compass rose */}
+
+
+ {Array.from({ length: 72 }).map((_, i) => (
+
+ ))}
+
+
+
+
+ {(["N", "E", "S", "W"] as const).map(
+ (d, i) => {
+ const positions = [
+ {
+ top: "4%",
+ left: "50%",
+ tx: "-50%",
+ },
+ {
+ top: "50%",
+ right: "4%",
+ ty: "-50%",
+ },
+ {
+ bottom: "4%",
+ left: "50%",
+ tx: "-50%",
+ },
+ {
+ top: "50%",
+ left: "4%",
+ ty: "-50%",
+ },
+ ];
+ const pos = positions[i];
+ return (
+
+ {d}
+
+ );
+ },
+ )}
+
+
+
+
+
+ Open Source Platform
-
+
Build With
-
- Clarity
+
+ Direction
-
- Project scheduling, file management, and team coordination
- — purpose-built for construction professionals.
+
+ An AI-native workspace platform that
+ handles auth, deployment, and real-time
+ collaboration — so you can focus on
+ building what actually matters.
-
-
-
+
+
+ {/* features */}
+
+
+
+ What You Get
+
+
+ Everything a workspace needs.
+
+
+ Nothing it doesn’t.
+
+
+
+
+
+ {features.map((f, i) => (
+
+
+
+
+
+ {f.num}
+
+
+ {f.title}
+
+
+ {f.description}
+
+
+
+ ))}
+
+
+
+ {/* modules ticker */}
+
+
+ {[...modules, ...modules].map((m, i) => (
+
+
+ {m}
+
+ ))}
+
+
+
+ {/* showcase section */}
+
+
+
+
+ Built With Compass
+
+
+ HPS Compass
+
+
+ Construction project management
+ — the first product built
+ entirely on this platform.
+ Scheduling, financials, NetSuite
+ integration, and AI-powered
+ workflows.
+
+
+ Try the Demo
+
+
+
+
+ {/* gantt visualization */}
+
+ {/* ambient glow behind */}
+
+
+ {/* gantt header */}
+
+
+
+ {["Jan", "Feb", "Mar", "Apr", "May", "Jun"].map((m) => (
+
+ {m}
+
+ ))}
+
+
+ {/* gantt rows */}
+ {[
+ { label: "Foundation", start: 0, width: 22, color: "#00C896", done: true },
+ { label: "Structural", start: 12, width: 30, color: "#00C896", done: true },
+ { label: "Electrical", start: 25, width: 20, color: "#F59E0B", done: false },
+ { label: "Plumbing", start: 28, width: 25, color: "#3B82F6", done: false },
+ { label: "HVAC", start: 38, width: 22, color: "#F59E0B", done: false },
+ { label: "Interior", start: 48, width: 30, color: "#00C896", done: false },
+ { label: "Exterior", start: 42, width: 35, color: "#8B5CF6", done: false },
+ { label: "Landscaping", start: 65, width: 20, color: "#00C896", done: false },
+ { label: "Punch List", start: 78, width: 15, color: "#F59E0B", done: false },
+ ].map((row, i) => (
+
+
+ {row.label}
+
+
+ {/* grid lines */}
+
+ {Array.from({ length: 6 }).map((_, j) => (
+
+ ))}
+
+ {/* bar */}
+
+ {/* progress fill for done items */}
+ {row.done && (
+
+ )}
+
+ {/* dependency arrow for some rows */}
+ {i === 1 && (
+
+ )}
+
+
+ ))}
+ {/* today line */}
+
+
+ Today
+
+
+
+
+
+
+
+ {/* cta closer */}
+
+
+
+
+
+
+
+ Get Started
+
+
+ Your next product
+
+
+ starts here.
+
+
+
+ Clone the repo, drop in your
+ modules, deploy. Open source and
+ free forever.
+
+
+ {/* code snippet */}
+
+
+ $
+
+ git clone compass && bun dev
+
+
+
+
+
+
+
+ {/* footer */}
+
-
-
);
}
diff --git a/src/components/app-sidebar.tsx b/src/components/app-sidebar.tsx
index 2e250c1..02a21ab 100755
--- a/src/components/app-sidebar.tsx
+++ b/src/components/app-sidebar.tsx
@@ -9,8 +9,6 @@ import {
IconMessageCircle,
IconReceipt,
IconSettings,
- IconTruck,
- IconUsers,
} from "@tabler/icons-react"
import { usePathname } from "next/navigation"
@@ -21,7 +19,7 @@ import { NavFiles } from "@/components/nav-files"
import { NavProjects } from "@/components/nav-projects"
import { NavConversations } from "@/components/nav-conversations"
import { NavUser } from "@/components/nav-user"
-import { useSettings } from "@/components/settings-provider"
+// settings is now a page at /dashboard/settings
import { openFeedbackDialog } from "@/components/feedback-widget"
import type { SidebarUser } from "@/lib/auth"
import {
@@ -42,11 +40,6 @@ const data = {
url: "/dashboard/projects",
icon: IconFolder,
},
- {
- title: "Team",
- url: "/dashboard/people",
- icon: IconUsers,
- },
{
title: "Schedule",
url: "/dashboard/projects/demo-project-1/schedule",
@@ -63,15 +56,10 @@ const data = {
icon: IconFiles,
},
{
- title: "Customers",
- url: "/dashboard/customers",
+ title: "Contacts",
+ url: "/dashboard/contacts",
icon: IconAddressBook,
},
- {
- title: "Vendors",
- url: "/dashboard/vendors",
- icon: IconTruck,
- },
{
title: "Financials",
url: "/dashboard/financials",
@@ -81,7 +69,7 @@ const data = {
navSecondary: [
{
title: "Settings",
- url: "#",
+ url: "/dashboard/settings",
icon: IconSettings,
},
],
@@ -99,7 +87,6 @@ function SidebarNav({
}) {
const pathname = usePathname()
const { state, setOpen } = useSidebar()
- const { open: openSettings } = useSettings()
const isExpanded = state === "expanded"
const isFilesMode = pathname?.startsWith("/dashboard/files")
const isConversationsMode = pathname?.startsWith("/dashboard/conversations")
@@ -124,13 +111,7 @@ function SidebarNav({
? "projects"
: "main"
- const secondaryItems = [
- ...data.navSecondary.map((item) =>
- item.title === "Settings"
- ? { ...item, onClick: openSettings }
- : item
- ),
- ]
+ const secondaryItems = [...data.navSecondary]
return (
diff --git a/src/components/dashboard-context-menu.tsx b/src/components/dashboard-context-menu.tsx
index d5f8688..31dd1d7 100755
--- a/src/components/dashboard-context-menu.tsx
+++ b/src/components/dashboard-context-menu.tsx
@@ -14,10 +14,9 @@ import {
MessageCircle,
LayoutDashboard,
FolderKanban,
- Users,
+ Settings,
FolderOpen,
UserRound,
- Building2,
DollarSign,
} from "lucide-react"
@@ -49,9 +48,9 @@ const NAV_ITEMS = [
icon: FolderKanban,
},
{
- label: "People",
- href: "/dashboard/people",
- icon: Users,
+ label: "Settings",
+ href: "/dashboard/settings",
+ icon: Settings,
},
{
label: "Files",
@@ -59,15 +58,10 @@ const NAV_ITEMS = [
icon: FolderOpen,
},
{
- label: "Customers",
- href: "/dashboard/customers",
+ label: "Contacts",
+ href: "/dashboard/contacts",
icon: UserRound,
},
- {
- label: "Vendors",
- href: "/dashboard/vendors",
- icon: Building2,
- },
{
label: "Financials",
href: "/dashboard/financials",
diff --git a/src/components/financials/customer-dialog.tsx b/src/components/financials/customer-dialog.tsx
index ee98ced..74bde54 100755
--- a/src/components/financials/customer-dialog.tsx
+++ b/src/components/financials/customer-dialog.tsx
@@ -85,9 +85,11 @@ export function CustomerDialog({
setName(e.target.value)}
required
+ autoFocus
/>
@@ -97,43 +99,48 @@ export function CustomerDialog({
setCompany(e.target.value)}
/>
-
-
- setEmail(e.target.value)}
- />
-
-
-
-
setPhone(e.target.value)}
- />
+
-
@@ -145,7 +152,8 @@ export function CustomerDialog({
value={notes}
onChange={(e) => setNotes(e.target.value)}
rows={2}
- className="text-sm"
+ placeholder="Additional notes..."
+ className="text-sm resize-none"
/>
diff --git a/src/components/financials/customers-table.tsx b/src/components/financials/customers-table.tsx
index fd42198..fb28841 100755
--- a/src/components/financials/customers-table.tsx
+++ b/src/components/financials/customers-table.tsx
@@ -16,7 +16,7 @@ import {
import type { Customer } from "@/db/schema"
import { useIsMobile } from "@/hooks/use-mobile"
-import { Badge } from "@/components/ui/badge"
+import { Avatar, AvatarFallback } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import {
@@ -110,49 +110,90 @@ export function CustomersTable({
{
accessorKey: "name",
header: "Name",
- cell: ({ row }) => (
-
{row.getValue("name")}
- ),
- },
- {
- accessorKey: "company",
- header: "Company",
- cell: ({ row }) =>
- row.getValue("company") || (
-
-
- ),
+ cell: ({ row }) => {
+ const c = row.original
+ const initials = c.name
+ .split(/\s+/)
+ .map((w) => w[0])
+ .join("")
+ .slice(0, 2)
+ .toUpperCase()
+ return (
+
+
+
+ {initials}
+
+
+
+
+ {c.name}
+
+ {c.company ? (
+
+ {c.company}
+
+ ) : null}
+
+
+ )
+ },
},
{
accessorKey: "email",
header: "Email",
- cell: ({ row }) =>
- row.getValue("email") || (
-
-
- ),
+ cell: ({ row }) => {
+ const email = row.getValue("email") as string | null
+ if (!email) {
+ return (
+
—
+ )
+ }
+ return (
+
e.stopPropagation()}
+ >
+ {email}
+
+ )
+ },
},
{
accessorKey: "phone",
header: "Phone",
- cell: ({ row }) =>
- row.getValue("phone") || (
-
-
- ),
- },
- {
- accessorKey: "netsuiteId",
- header: "NetSuite ID",
cell: ({ row }) => {
- const nsId = row.getValue("netsuiteId") as string | null
- if (!nsId) return
-
- return
{nsId}
+ const phone = row.getValue("phone") as string | null
+ if (!phone) {
+ return (
+
—
+ )
+ }
+ return (
+
e.stopPropagation()}
+ >
+ {phone}
+
+ )
},
},
{
accessorKey: "createdAt",
- header: "Created",
+ header: "Added",
cell: ({ row }) => {
const d = row.getValue("createdAt") as string
- return new Date(d).toLocaleDateString()
+ return (
+
+ {new Date(d).toLocaleDateString("en-US", {
+ month: "short",
+ year: "numeric",
+ })}
+
+ )
},
},
{
@@ -195,6 +236,7 @@ export function CustomersTable({
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onRowSelectionChange: setRowSelection,
+ initialState: { pagination: { pageSize: 100 } },
state: { sorting, columnFilters, rowSelection },
})
@@ -241,6 +283,11 @@ export function CustomersTable({
key={row.id}
className="flex items-center gap-3 px-3 py-2.5"
>
+
+
+ {c.name.split(/\s+/).map((w) => w[0]).join("").slice(0, 2).toUpperCase()}
+
+
{c.name}
@@ -286,19 +333,19 @@ export function CustomersTable({
}
return (
-
+
table.getColumn("name")?.setFilterValue(e.target.value)
}
- className="w-full sm:max-w-sm"
+ className="h-8 w-full sm:max-w-sm shrink-0"
/>
-
-
+
+
-
+
{table.getHeaderGroups().map((headerGroup) => (
{headerGroup.headers.map((header) => (
@@ -345,30 +392,36 @@ export function CustomersTable({
-
-
- {table.getFilteredSelectedRowModel().rows.length} of{" "}
- {table.getFilteredRowModel().rows.length} row(s) selected
+ {(table.getPageCount() > 1 ||
+ table.getFilteredSelectedRowModel().rows.length > 0) && (
+
+
+ {table.getFilteredSelectedRowModel().rows.length > 0
+ ? `${table.getFilteredSelectedRowModel().rows.length} of ${table.getFilteredRowModel().rows.length} selected`
+ : `${table.getFilteredRowModel().rows.length} contacts`}
+
+ {table.getPageCount() > 1 && (
+
+
+
+
+ )}
-
-
-
-
-
+ )}
)
}
diff --git a/src/components/financials/vendor-dialog.tsx b/src/components/financials/vendor-dialog.tsx
index 2b77221..3d2b687 100755
--- a/src/components/financials/vendor-dialog.tsx
+++ b/src/components/financials/vendor-dialog.tsx
@@ -16,7 +16,6 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
-import { Textarea } from "@/components/ui/textarea"
import type { Vendor } from "@/db/schema"
const VENDOR_CATEGORIES = [
@@ -89,68 +88,79 @@ export function VendorDialog({
>