oficina / home Public

Show available and pending apps in Home directory

Paul Campbell pushed by paul@keymail.dev 32b33d11f4ab6152e7f155a3a3fe03e440fb1f85 parent 60faf05
4 files changed, +67 −1
  • internal/home/directory.go +1 −1
  • internal/home/directory_catalog.go +17 −0
  • internal/home/directory_catalog_test.go +36 −0
  • internal/home/templates/directory.html +13 −0

CI passed — run details

diff --git a/internal/home/directory.go b/internal/home/directory.go
index fe25f09..6f1a734 100644
--- a/internal/home/directory.go
+++ b/internal/home/directory.go
@@ -79,7 +79,7 @@ func (a *App) directoryRouter() http.Handler {
})
r.With(a.sessions.Require).Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "no-store")
- a.render(w, 200, "directory.html", map[string]any{})
+ a.render(w, 200, "directory.html", map[string]any{"Apps": directoryCatalog()})
})
r.Post("/signout", func(w http.ResponseWriter, r *http.Request) {
a.sessions.SignOut(w, r)
diff --git a/internal/home/directory_catalog.go b/internal/home/directory_catalog.go
new file mode 100644
index 0000000..282488f
--- /dev/null
+++ b/internal/home/directory_catalog.go
@@ -0,0 +1,17 @@
+package home
+
+type catalogApp struct {
+ Name, Href, Status string
+}
+
+// This display catalog grants no integration or provisioning capability.
+// Those remain governed by directoryApps and the destination checks.
+func directoryCatalog() []catalogApp {
+ return []catalogApp{
+ {Name: "Docs", Href: docsEntryPath, Status: "Choose a team"},
+ {Name: "Sheets", Status: "Not connected to Home yet"},
+ {Name: "Slides", Status: "Not connected to Home yet"},
+ {Name: "Calendar", Status: "Not connected to Home yet"},
+ {Name: "Meet", Status: "Coming soon"},
+ }
+}
diff --git a/internal/home/directory_catalog_test.go b/internal/home/directory_catalog_test.go
new file mode 100644
index 0000000..11b7c49
--- /dev/null
+++ b/internal/home/directory_catalog_test.go
@@ -0,0 +1,36 @@
+package home
+
+import (
+ "strings"
+ "testing"
+)
+
+func TestDirectoryCatalogRequiresSigninAndDoesNotInventLaunchDestinations(t *testing.T) {
+ f := newMountedChooser(t)
+ anonymous := f.request("GET", "/", "", "", nil)
+ if anonymous.Code != 303 || !strings.HasPrefix(anonymous.Header().Get("Location"), "/auth/start") || strings.Contains(anonymous.Body.String(), "Choose a team") {
+ t.Fatal("anonymous directory exposed catalog", anonymous.Code, anonymous.Header())
+ }
+ cookie := f.session(t, chooserPerson)
+ page := f.request("GET", "/", "", "", cookie)
+ if page.Code != 200 {
+ t.Fatal(page.Code)
+ }
+ noStore(t, page)
+ body := page.Body.String()
+ for _, name := range []string{"Docs", "Sheets", "Slides", "Calendar", "Meet"} {
+ if !strings.Contains(body, ">"+name+"<") {
+ t.Fatal("missing app", name)
+ }
+ }
+ if strings.Count(body, "<a ") != 1 || !strings.Contains(body, `href="/apps/docs"`) || !strings.Contains(body, "Choose a team") || strings.Contains(body, "<script") {
+ t.Fatal("catalog advertised unsupported launch", body)
+ }
+ if strings.Count(body, "Not connected to Home yet") != 3 || strings.Count(body, "Coming soon") != 1 {
+ t.Fatal("catalog readiness missing", body)
+ }
+ entry := f.request("GET", docsEntryPath, "", "", cookie)
+ if entry.Code != 200 || !strings.Contains(entry.Body.String(), "This account has no team with Docs available.") {
+ t.Fatal("catalog bypassed team selection", entry.Code)
+ }
+}
diff --git a/internal/home/templates/directory.html b/internal/home/templates/directory.html
index 7c27538..10bebfb 100644
--- a/internal/home/templates/directory.html
+++ b/internal/home/templates/directory.html
@@ -2,5 +2,18 @@
{{define "content"}}
<h1>Oficina</h1>
<p>You are signed in.</p>
+<section aria-labelledby="apps-heading">
+<h2 id="apps-heading">Apps</h2>
+<div rst-list>
+{{range .Apps}}
+<div rst-row>
+<div rst-row-main>
+{{if .Href}}<a href="{{.Href}}">{{.Name}}</a>{{else}}<strong>{{.Name}}</strong>{{end}}
+<span rst-row-sub>{{.Status}}</span>
+</div>
+</div>
+{{end}}
+</div>
+</section>
<form method="post" action="/signout" rst-form><button type="submit" rst-btn>Sign out</button></form>
{{end}}