mirror of
https://github.com/MeshTender/MeshTender.git
synced 2026-10-23 13:32:53 +00:00
Get started checklist
This commit is contained in:
1 parent
cfc236e365
commit
f2a34cf03c
2 files changed
+79
-36
No files matched your search
@@ -66,36 +66,25 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Getting started: shown only while a step is still pending -->
|
||||
{{if or (not .OwnedCount) (not .OrgCount)}}
|
||||
<div class="row row-cards mt-1">
|
||||
{{if not .OwnedCount}}
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-body text-center">
|
||||
<span class="avatar avatar-lg bg-primary-lt mb-3">{{template "icon-antenna" ""}}</span>
|
||||
<h3 class="card-title">Add your first repeater</h3>
|
||||
<p class="text-secondary">Connect a repeater to MeshTender and test it with a modem.</p>
|
||||
<a href="/repeaters/add" class="btn btn-primary">{{template "icon-plus" "me-1"}}Add repeater</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Getting-started checklist: shown only while a step is still pending -->
|
||||
{{if .Onboarding}}
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Get started</h3>
|
||||
<div class="card-actions text-secondary small">{{.OnboardingDone}} of {{.OnboardingTotal}} done</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if not .OrgCount}}
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-body text-center">
|
||||
<span class="avatar avatar-lg bg-azure-lt mb-3">{{template "icon-world" ""}}</span>
|
||||
<h3 class="card-title">Join an organization</h3>
|
||||
<p class="text-secondary">Find a local mesh community to share repeaters and coordinate.</p>
|
||||
<div class="btn-list justify-content-center">
|
||||
<a href="{{.RootURL}}/orgs" class="btn btn-primary">Browse organizations</a>
|
||||
<a href="/orgs/new" class="btn">Create one</a>
|
||||
</div>
|
||||
<div class="list-group list-group-flush">
|
||||
{{range .Onboarding}}
|
||||
<div class="list-group-item d-flex align-items-center gap-3">
|
||||
<input class="form-check-input m-0" type="checkbox" {{if .Done}}checked{{end}} disabled aria-label="{{.Title}}">
|
||||
<div class="flex-fill" style="min-width:0">
|
||||
<div class="fw-bold{{if .Done}} text-secondary text-decoration-line-through{{end}}">{{.Title}}</div>
|
||||
{{if not .Done}}<div class="text-secondary small">{{.Desc}}</div>{{end}}
|
||||
</div>
|
||||
{{if not .Done}}<a href="{{.Href}}" class="btn btn-sm btn-primary">{{.Action}}</a>{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
|
||||
+64
-10
@@ -223,8 +223,19 @@ func (s *Handlers) pageRepeaters(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
// pageDashboard renders the signed-in overview: summary stats, short lists of
|
||||
// repeaters and organizations, a map of owned repeaters, and recent activity.
|
||||
// onboardingStep is one item in the dashboard getting-started checklist. Steps
|
||||
// are data-driven so adding a new one (e.g. future profile fields) is a single
|
||||
// entry in buildOnboarding rather than template surgery.
|
||||
type onboardingStep struct {
|
||||
Title string
|
||||
Desc string
|
||||
Action string // CTA label, shown only while the step is pending
|
||||
Href string // where the CTA leads (absolute when it crosses hosts)
|
||||
Done bool
|
||||
}
|
||||
|
||||
// pageDashboard renders the signed-in overview: summary stats, a getting-started
|
||||
// checklist, a map of owned repeaters, and recent activity.
|
||||
func (s *Handlers) pageDashboard(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
uid := s.Auth.CurrentUserID(ctx)
|
||||
@@ -246,6 +257,11 @@ func (s *Handlers) pageDashboard(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "could not load activity", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
user, err := s.Store.GetUserByID(ctx, uid)
|
||||
if err != nil {
|
||||
http.Error(w, "could not load account", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
confirmed, unconfirmed := 0, 0
|
||||
var mapped []*store.Repeater
|
||||
@@ -260,15 +276,53 @@ func (s *Handlers) pageDashboard(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
steps := []onboardingStep{
|
||||
{
|
||||
Title: "Set up your profile",
|
||||
Desc: "Add a display name so teammates recognize you.",
|
||||
Action: "Edit profile",
|
||||
Href: s.Origin(r, s.Cfg.AuthHost) + "/account",
|
||||
Done: user.DisplayName != nil && *user.DisplayName != "",
|
||||
},
|
||||
{
|
||||
Title: "Add a repeater",
|
||||
Desc: "Connect a repeater to MeshTender and test it with a modem.",
|
||||
Action: "Add repeater",
|
||||
Href: "/repeaters/add",
|
||||
Done: len(owned) > 0,
|
||||
},
|
||||
{
|
||||
Title: "Join an organization",
|
||||
Desc: "Find a local mesh community to share repeaters and coordinate.",
|
||||
Action: "Browse organizations",
|
||||
Href: s.Origin(r, s.Cfg.RootHost) + "/orgs",
|
||||
Done: len(orgs) > 0,
|
||||
},
|
||||
}
|
||||
total := len(steps)
|
||||
doneCount := 0
|
||||
for _, st := range steps {
|
||||
if st.Done {
|
||||
doneCount++
|
||||
}
|
||||
}
|
||||
// Hide the checklist entirely once everything's done.
|
||||
if doneCount == total {
|
||||
steps = nil
|
||||
}
|
||||
|
||||
s.Render(w, r, "dashboard.html", map[string]any{
|
||||
"OwnedCount": len(owned),
|
||||
"SharedCount": len(shared),
|
||||
"OrgCount": len(orgs),
|
||||
"Confirmed": confirmed,
|
||||
"Unconfirmed": unconfirmed,
|
||||
"Mapped": mapped,
|
||||
"Recent": recent,
|
||||
"Error": r.URL.Query().Get("error"),
|
||||
"OwnedCount": len(owned),
|
||||
"SharedCount": len(shared),
|
||||
"OrgCount": len(orgs),
|
||||
"Confirmed": confirmed,
|
||||
"Unconfirmed": unconfirmed,
|
||||
"Mapped": mapped,
|
||||
"Recent": recent,
|
||||
"Onboarding": steps,
|
||||
"OnboardingDone": doneCount,
|
||||
"OnboardingTotal": total,
|
||||
"Error": r.URL.Query().Get("error"),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user