streaming-ui-script · CRM contacts ← Back to live examples
Live demo · directory

A working CRM directory, generated from one program

A polished directory with a SearchBar, segmented filter chips, four Tile quick stats, a grid of person cards, pagination, and a slide-in detail Sheet. Filtering and search drive a single $contacts array through @Filter.

Live preview

Tap a stat tile to filter, type into the search bar, or click "View profile" on any card to open the detail sheet.

$segment  = "all"
$query    = ""
$selected = ""

$contacts = [
  {id: "naomi",
   name: "Naomi Rivers",
   role: "VP Engineering · Looplog",
   avatar: "https://i.pravatar.cc/120?img=47",
   bio: "Built Looplog from 0 → 1.2M sessions/day. Owns the Q3 expansion deal.",
   segment: "champions",
   tags: ["champion", "enterprise"],
   company: "Looplog · 240 seats",
   owner: "Mei Tanaka",
   arr: "$84,000",
   renewal: "Q3 · Sep 14"},
  {id: "marc",
   name: "Marc Lee",
   role: "Head of Data · Northwind",
   avatar: "https://i.pravatar.cc/120?img=11",
   bio: "Renewing in 14 days. Wants a custom dashboard demo before signing.",
   segment: "customers",
   tags: ["renewal", "data"],
   company: "Northwind · 120 seats",
   owner: "Naomi Rivers",
   arr: "$56,000",
   renewal: "Q2 · Jun 02"},
  {id: "grace",
   name: "Grace Hopper",
   role: "Founder · Atlasworks",
   avatar: "https://i.pravatar.cc/120?img=32",
   bio: "Just upgraded to Scale. Asked about SSO + audit logs.",
   segment: "champions",
   tags: ["scale", "sso"],
   company: "Atlasworks · 80 seats",
   owner: "Naomi Rivers",
   arr: "$42,000",
   renewal: "Q1 · Mar 18"},
  {id: "linus",
   name: "Linus Torvalds",
   role: "Staff Eng · Kernelist",
   avatar: "https://i.pravatar.cc/120?img=12",
   bio: "Trial expires Friday. Hands-on with the JavaScript interactions feature.",
   segment: "prospects",
   tags: ["trial", "perf"],
   company: "Kernelist",
   owner: "Sam Reyes",
   arr: "$0 (trial)",
   renewal: "—"},
  {id: "ada",
   name: "Ada Lovelace",
   role: "CTO · Compute Lab",
   avatar: "https://i.pravatar.cc/120?img=20",
   bio: "Pilot signed. Needs onboarding for 12 engineers in two weeks.",
   segment: "customers",
   tags: ["onboarding", "enterprise"],
   company: "Compute Lab · 12 seats",
   owner: "Mei Tanaka",
   arr: "$24,000",
   renewal: "Q4 · Dec 08"},
  {id: "mei",
   name: "Mei Tanaka",
   role: "Eng lead · Atlasworks",
   avatar: "https://i.pravatar.cc/120?img=14",
   bio: "Open question on theming for a customer-facing portal.",
   segment: "champions",
   tags: ["theming", "champion"],
   company: "Atlasworks · 80 seats",
   owner: "Naomi Rivers",
   arr: "$42,000",
   renewal: "Q1 · Mar 18"},
  {id: "sam",
   name: "Sam Reyes",
   role: "Architect · Northwind",
   avatar: "https://i.pravatar.cc/120?img=15",
   bio: "Quiet for 30 days. Worth a check-in before the QBR.",
   segment: "at-risk",
   tags: ["at-risk", "qbr"],
   company: "Northwind · 120 seats",
   owner: "Naomi Rivers",
   arr: "$56,000",
   renewal: "Q2 · Jun 02"},
  {id: "jordan",
   name: "Jordan Patel",
   role: "Founder · Looplog",
   avatar: "https://i.pravatar.cc/120?img=22",
   bio: "References us in their public roadmap. Great expansion candidate.",
   segment: "champions",
   tags: ["champion", "advocacy"],
   company: "Looplog · 240 seats",
   owner: "Mei Tanaka",
   arr: "$84,000",
   renewal: "Q3 · Sep 14"}
]

segmentRows = $segment == "all" ? $contacts : @Filter($contacts, "segment", "==", $segment)
visibleRows = $query == "" ? segmentRows : @Filter(segmentRows, "name", "contains", $query)
visibleCount = @Count(visibleRows)
totalCount = @Count($contacts)

searchBar = SearchBar("crm-q", "Search contacts by name…", $query, "/")

segments = ToggleGroup("segment", [
  {value: "all",        label: "All",        icon: "users"},
  {value: "customers",  label: "Customers",  icon: "handshake"},
  {value: "prospects",  label: "Prospects",  icon: "seedling"},
  {value: "champions",  label: "Champions",  icon: "trophy"},
  {value: "at-risk",    label: "At-risk",    icon: "triangle-exclamation"}
], $segment)

statTiles = Grid([
  Tile("Total contacts",   "users", "2,481", "+128 this week",            "primary", Action([@Set($segment, "all")])),
  Tile("Active deals",     "briefcase", "47",    "$418k ARR pipeline",        "info",    Action([@Set($segment, "customers")])),
  Tile("At-risk accounts", "triangle-exclamation", "12",    "Needs follow-up this week", "warning", Action([@Set($segment, "at-risk")])),
  Tile("Champions",        "trophy", "63",    "NPS 9 or 10 in last 30d",   "success", Action([@Set($segment, "champions")]))
], 4, "m")

filterRow = Stack([
  searchBar,
  segments
], "column", "m")

emptyResults = EmptyState(
  "No contacts match",
  "Adjust the segment or clear the search to see more results.",
  "magnifying-glass",
  Button("Reset filters",
         Action([@Set($segment, "all"), @Reset($query)]),
         "secondary")
)

contactGrid = Grid(
  @Each(visibleRows, "c",
    ProfileCard(
      c.name,
      c.role,
      c.avatar,
      c.bio,
      c.tags,
      [Button("View profile",
              Action([@Set($selected, c.id)]),
              "secondary",
              "button",
              "small")]
    )
  ),
  4, "m"
)

cards = visibleCount == 0 ? emptyResults : contactGrid

selected = @First(@Filter($contacts, "id", "==", $selected))
selectedExists = @Count(@Filter($contacts, "id", "==", $selected))

detailGeneric = EmptyState("Profile not loaded",
                            "Pick a contact card on the left to see their details here.",
                            "user",
                            null)

detailLoaded = Stack([
  PersonChip(selected.name, selected.role, selected.avatar, "lg", "online"),
  DescriptionList([
    DescriptionItem("Company",  selected.company),
    DescriptionItem("Owner",    selected.owner),
    DescriptionItem("ARR",      selected.arr),
    DescriptionItem("Renewal",  selected.renewal)
  ]),
  Note(selected.bio, "info"),
  Quote("Streaming-ui-script took our recap email from 4k lines of glue to 40.",
        selected.name + " · last QBR",
        "primary"),
  Stack(
    @Each(selected.tags, "t", Tag(t, null, "sm", "info")),
    "row", "s"
  )
], "column", "m")

detailBody = selectedExists == 0 ? detailGeneric : detailLoaded

detailSheet = Sheet(
  "Contact detail",
  $selected != "",
  [detailBody],
  "right",
  [Buttons([
    Button("Close",
           Action([@Set($selected, "")]),
           "secondary"),
    Button("Open in CRM",
           Action([@ToAssistant("Open " + $selected + " in the CRM")]),
           "primary")
  ])]
)

resultSummary = TextContent(
  "Showing " + visibleCount + " of " + totalCount + " contacts" + ($query == "" ? "" : (" · matching \"" + $query + "\"")),
  "small",
  "muted"
)

root = Stack([
  PageHeader(
    "Contacts",
    "2,481 contacts · 12 at-risk",
    null,
    [Button("New contact",
            Action([@ToAssistant("Open the new-contact form")]),
            "primary")],
    Badge("CRM v3.1", "info")
  ),
  statTiles,
  filterRow,
  resultSummary,
  cards,
  detailSheet
], "column", "l")

What's powerful here

Cards, filter chips, the search bar, the result summary, and the detail sheet all read from the same $contacts array. Segmenting and searching are one @Filter chain; opening a contact is a single @Set on $selected. No host code runs for any of the interactions.