1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
require "pro_tacts/admin/phlex"
require "pro_tacts/admin/contact_dialog"
require "pro_tacts/admin/layout"
require "pro_tacts/admin/recent_row"
require "pro_tacts/admin/upcoming_birthdays"
module ProTacts
module Admin
# GET / — the dashboard (docs/DESIGN.md): recency in the primary
# column, the birthdays the year is about to bring in the ambient
# one beside it. It answers no query; the search is the header's
# dialog, on this screen as on every other
# (docs/plans/2026-09-30-live-search.md). The create dialog rides
# along hidden on every render — a popover opens where it sits, so
# the add button needs nothing from the server but the page it is
# already on.
class Dashboard < Phlex::HTML
# How many rows recently updated lists, here and on the search
# page before a query (Web#search_results).
RECENT_LIMIT = 10
# @rbs @login: String
# @rbs @upcoming: Array[Store::UpcomingBirthday]
# @rbs @rows: Array[Store::RecentContact]
# @rbs @notice: String?
#: (recent: Array[Store::RecentContact], upcoming: Array[Store::UpcomingBirthday], login: String, ?notice: String?) -> void
def initialize(recent:, upcoming:, login:, notice: nil)
@login = login
@upcoming = upcoming
@notice = notice
@rows = recent.first(RECENT_LIMIT)
end
def view_template
render Layout.new(title: "Home", login: @login, wide: true, notice: @notice) do
div(class: "dashboard") do
section do
div(class: "section-head") do
h2(class: "type-label") { "recently updated" }
# A button, not a link: it changes the page's state
# rather than navigating, and the Popover API is what
# it invokes (see ContactDialog).
button(data_size: "sm", popovertarget: "new-contact") { "add contact" }
end
if @rows.empty?
p(class: "type-body-sm") { "No contacts yet." }
else
ul(class: "card") do
@rows.each { render RecentRow.new(row: it) }
end
end
end
render UpcomingBirthdays.new(upcoming: @upcoming)
end
render ContactDialog.new
end
end
end
end
end