forked from tangled.org/core
Monorepo for Tangled

appview/pages: rework knots pages to support new flow

Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.sh>

anirudh.fi 2c2b5320 fe10e193

verified
-16
appview/pages/pages.go
··· 337 337 return p.executePlain("knots/fragments/knotListing", w, params) 338 338 } 339 339 340 - type KnotListingFullParams struct { 341 - Registrations []db.Registration 342 - } 343 - 344 - func (p *Pages) KnotListingFull(w io.Writer, params KnotListingFullParams) error { 345 - return p.executePlain("knots/fragments/knotListingFull", w, params) 346 - } 347 - 348 - type KnotSecretParams struct { 349 - Secret string 350 - } 351 - 352 - func (p *Pages) KnotSecret(w io.Writer, params KnotSecretParams) error { 353 - return p.executePlain("knots/fragments/secret", w, params) 354 - } 355 - 356 340 type SpindlesParams struct { 357 341 LoggedInUser *oauth.User 358 342 Spindles []db.Spindle
+86 -29
appview/pages/templates/knots/dashboard.html
··· 1 - {{ define "title" }}{{ .Registration.Domain }}{{ end }} 1 + {{ define "title" }}{{ .Registration.Domain }} &middot; knots{{ end }} 2 2 3 3 {{ define "content" }} 4 - <div class="px-6 py-4"> 5 - <div class="flex justify-between items-center"> 6 - <div id="left-side" class="flex gap-2 items-center"> 7 - <h1 class="text-xl font-bold dark:text-white"> 8 - {{ .Registration.Domain }} 9 - </h1> 10 - <span class="text-gray-500 text-base"> 11 - {{ template "repo/fragments/shortTimeAgo" .Registration.Created }} 12 - </span> 13 - </div> 14 - <div id="right-side" class="flex gap-2"> 15 - {{ $style := "px-2 py-1 rounded flex items-center flex-shrink-0 gap-2" }} 16 - {{ if .Registration.Registered }} 17 - <span class="bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200 {{$style}}">{{ i "shield-check" "w-4 h-4" }} verified</span> 4 + <div class="px-6 py-4"> 5 + <div class="flex justify-between items-center"> 6 + <h1 class="text-xl font-bold dark:text-white">{{ .Registration.Domain }}</h1> 7 + <div id="right-side" class="flex gap-2"> 8 + {{ $style := "px-2 py-1 rounded flex items-center flex-shrink-0 gap-2" }} 9 + {{ $isOwner := and .LoggedInUser (eq .LoggedInUser.Did .Registration.ByDid) }} 10 + {{ if .Registration.Registered }} 11 + <span class="bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200 {{$style}}">{{ i "shield-check" "w-4 h-4" }} verified</span> 12 + {{ if $isOwner }} 18 13 {{ template "knots/fragments/addMemberModal" .Registration }} 19 - {{ else }} 20 - <span class="bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200 {{$style}}">{{ i "shield-off" "w-4 h-4" }} pending</span> 21 14 {{ end }} 22 - </div> 15 + {{ else }} 16 + <span class="bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200 {{$style}}">{{ i "shield-off" "w-4 h-4" }} unverified</span> 17 + {{ if $isOwner }} 18 + {{ block "retryButton" .Registration }} {{ end }} 19 + {{ end }} 20 + {{ end }} 21 + 22 + {{ if $isOwner }} 23 + {{ block "deleteButton" .Registration }} {{ end }} 24 + {{ end }} 23 25 </div> 24 - <div id="operation-error" class="dark:text-red-400"></div> 25 26 </div> 27 + <div id="operation-error" class="dark:text-red-400"></div> 28 + </div> 26 29 27 - {{ if .Members }} 28 - <section class="bg-white dark:bg-gray-800 p-6 rounded relative w-full mx-auto drop-shadow-sm dark:text-white"> 29 - <div class="flex flex-col gap-2"> 30 - {{ block "knotMember" . }} {{ end }} 31 - </div> 32 - </section> 33 - {{ end }} 30 + {{ if .Members }} 31 + <section class="bg-white dark:bg-gray-800 p-6 rounded relative w-full mx-auto drop-shadow-sm dark:text-white"> 32 + <div class="flex flex-col gap-2"> 33 + {{ block "member" . }} {{ end }} 34 + </div> 35 + </section> 36 + {{ end }} 34 37 {{ end }} 35 38 36 - {{ define "knotMember" }} 39 + 40 + {{ define "member" }} 37 41 {{ range .Members }} 38 42 <div> 39 43 <div class="flex justify-between items-center"> 40 44 <div class="flex items-center gap-2"> 41 45 {{ i "user" "size-4" }} 42 46 {{ $user := index $.DidHandleMap . }} 43 - <a href="/{{ $user }}">{{ $user }} <span class="ml-2 font-mono text-gray-500">{{.}}</span></a> 47 + <a href="/{{ $user }}">{{ $user }}</a> 44 48 </div> 49 + {{ if ne $.LoggedInUser.Did . }} 50 + {{ block "removeMemberButton" (list $ . ) }} {{ end }} 51 + {{ end }} 45 52 </div> 46 53 <div class="ml-2 pl-2 pt-2 border-l border-gray-200 dark:border-gray-700"> 47 54 {{ $repos := index $.Repos . }} ··· 54 61 </div> 55 62 {{ else }} 56 63 <div class="text-gray-500 dark:text-gray-400"> 57 - No repositories created yet. 64 + No repositories configured yet. 58 65 </div> 59 66 {{ end }} 60 67 </div> 61 68 </div> 62 69 {{ end }} 63 70 {{ end }} 71 + 72 + {{ define "deleteButton" }} 73 + <button 74 + class="btn text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 gap-2 group" 75 + title="Delete knot" 76 + hx-delete="/knots/{{ .Domain }}" 77 + hx-swap="outerHTML" 78 + hx-confirm="Are you sure you want to delete the knot '{{ .Domain }}'?" 79 + hx-headers='{"shouldRedirect": "true"}' 80 + > 81 + {{ i "trash-2" "w-5 h-5" }} 82 + <span class="hidden md:inline">delete</span> 83 + {{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 84 + </button> 85 + {{ end }} 86 + 87 + 88 + {{ define "retryButton" }} 89 + <button 90 + class="btn gap-2 group" 91 + title="Retry knot verification" 92 + hx-post="/knots/{{ .Domain }}/retry" 93 + hx-swap="none" 94 + hx-headers='{"shouldRefresh": "true"}' 95 + > 96 + {{ i "rotate-ccw" "w-5 h-5" }} 97 + <span class="hidden md:inline">retry</span> 98 + {{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 99 + </button> 100 + {{ end }} 101 + 102 + 103 + {{ define "removeMemberButton" }} 104 + {{ $root := index . 0 }} 105 + {{ $member := index . 1 }} 106 + <button 107 + class="btn text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 gap-2 group" 108 + title="Remove member" 109 + hx-post="/knots/{{ $root.Registration.Domain }}/remove" 110 + hx-swap="none" 111 + hx-vals='{"member": "{{$member}}" }' 112 + hx-confirm="Are you sure you want to remove {{ index $root.DidHandleMap $member }} from this knot?" 113 + > 114 + {{ i "user-minus" "w-4 h-4" }} 115 + remove 116 + {{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 117 + </button> 118 + {{ end }} 119 + 120 +
+6 -7
appview/pages/templates/knots/fragments/addMemberModal.html
··· 1 1 {{ define "knots/fragments/addMemberModal" }} 2 2 <button 3 3 class="btn gap-2 group" 4 - title="Add member to this spindle" 4 + title="Add member to this knot" 5 5 popovertarget="add-member-{{ .Id }}" 6 6 popovertargetaction="toggle" 7 7 > ··· 20 20 21 21 {{ define "addKnotMemberPopover" }} 22 22 <form 23 - hx-put="/knots/{{ .Domain }}/member" 23 + hx-post="/knots/{{ .Domain }}/add" 24 24 hx-indicator="#spinner" 25 25 hx-swap="none" 26 26 class="flex flex-col gap-2" ··· 28 28 <label for="member-did-{{ .Id }}" class="uppercase p-0"> 29 29 ADD MEMBER 30 30 </label> 31 - <p class="text-sm text-gray-500 dark:text-gray-400">Members can create repositories on this knot.</p> 31 + <p class="text-sm text-gray-500 dark:text-gray-400">Members can create repositories and run workflows on this knot.</p> 32 32 <input 33 33 type="text" 34 34 id="member-did-{{ .Id }}" 35 - name="subject" 35 + name="member" 36 36 required 37 37 placeholder="@foo.bsky.social" 38 38 /> 39 39 <div class="flex gap-2 pt-2"> 40 - <button 40 + <button 41 41 type="button" 42 42 popovertarget="add-member-{{ .Id }}" 43 43 popovertargetaction="hide" ··· 54 54 </div> 55 55 <div id="add-member-error-{{ .Id }}" class="text-red-500 dark:text-red-400"></div> 56 56 </form> 57 - {{ end }} 58 - 57 + {{ end }}
+42 -23
appview/pages/templates/knots/fragments/knotListing.html
··· 1 1 {{ define "knots/fragments/knotListing" }} 2 - <div 3 - id="knot-{{.Id}}" 4 - hx-swap-oob="true" 5 - class="flex items-center justify-between p-2 border-b border-gray-200 dark:border-gray-700"> 6 - {{ block "listLeftSide" . }} {{ end }} 7 - {{ block "listRightSide" . }} {{ end }} 2 + <div id="knot-{{.Id}}" class="flex items-center justify-between p-2 border-b border-gray-200 dark:border-gray-700"> 3 + {{ block "knotLeftSide" . }} {{ end }} 4 + {{ block "knotRightSide" . }} {{ end }} 8 5 </div> 9 6 {{ end }} 10 7 11 - {{ define "listLeftSide" }} 8 + {{ define "knotLeftSide" }} 9 + {{ if .Registered }} 10 + <a href="/knots/{{ .Domain }}" class="hover:no-underline flex items-center gap-2 min-w-0 max-w-[60%]"> 11 + {{ i "hard-drive" "w-4 h-4" }} 12 + {{ .Domain }} 13 + <span class="text-gray-500"> 14 + {{ template "repo/fragments/shortTimeAgo" .Created }} 15 + </span> 16 + </a> 17 + {{ else }} 12 18 <div class="hover:no-underline flex items-center gap-2 min-w-0 max-w-[60%]"> 13 19 {{ i "hard-drive" "w-4 h-4" }} 14 - {{ if .Registered }} 15 - <a href="/knots/{{ .Domain }}"> 16 - {{ .Domain }} 17 - </a> 18 - {{ else }} 19 - {{ .Domain }} 20 - {{ end }} 20 + {{ .Domain }} 21 21 <span class="text-gray-500"> 22 22 {{ template "repo/fragments/shortTimeAgo" .Created }} 23 23 </span> 24 24 </div> 25 + {{ end }} 25 26 {{ end }} 26 27 27 - {{ define "listRightSide" }} 28 + {{ define "knotRightSide" }} 28 29 <div id="right-side" class="flex gap-2"> 29 30 {{ $style := "px-2 py-1 rounded flex items-center flex-shrink-0 gap-2 text-sm" }} 30 31 {{ if .Registered }} 31 32 <span class="bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200 {{$style}}">{{ i "shield-check" "w-4 h-4" }} verified</span> 32 33 {{ template "knots/fragments/addMemberModal" . }} 33 34 {{ else }} 34 - <span class="bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200 {{$style}}">{{ i "shield-off" "w-4 h-4" }} pending</span> 35 - {{ block "initializeButton" . }} {{ end }} 35 + <span class="bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200 {{$style}}">{{ i "shield-off" "w-4 h-4" }} unverified</span> 36 + {{ block "knotRetryButton" . }} {{ end }} 36 37 {{ end }} 38 + {{ block "knotDeleteButton" . }} {{ end }} 37 39 </div> 38 40 {{ end }} 39 41 40 - {{ define "initializeButton" }} 42 + {{ define "knotDeleteButton" }} 41 43 <button 42 - class="btn dark:bg-gray-700 dark:text-white dark:hover:bg-gray-600 flex gap-2 items-center group" 43 - hx-post="/knots/{{ .Domain }}/init" 44 - hx-swap="none" 44 + class="btn text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 gap-2 group" 45 + title="Delete knot" 46 + hx-delete="/knots/{{ .Domain }}" 47 + hx-swap="outerHTML" 48 + hx-target="#knot-{{.Id}}" 49 + hx-confirm="Are you sure you want to delete the knot '{{ .Domain }}'?" 45 50 > 46 - {{ i "square-play" "w-5 h-5" }} 47 - <span class="hidden md:inline">initialize</span> 51 + {{ i "trash-2" "w-5 h-5" }} 52 + <span class="hidden md:inline">delete</span> 48 53 {{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 49 54 </button> 50 55 {{ end }} 51 56 57 + 58 + {{ define "knotRetryButton" }} 59 + <button 60 + class="btn gap-2 group" 61 + title="Retry knot verification" 62 + hx-post="/knots/{{ .Domain }}/retry" 63 + hx-swap="none" 64 + hx-target="#knot-{{.Id}}" 65 + > 66 + {{ i "rotate-ccw" "w-5 h-5" }} 67 + <span class="hidden md:inline">retry</span> 68 + {{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 69 + </button> 70 + {{ end }}
-18
appview/pages/templates/knots/fragments/knotListingFull.html
··· 1 - {{ define "knots/fragments/knotListingFull" }} 2 - <section 3 - id="knot-listing-full" 4 - hx-swap-oob="true" 5 - class="rounded w-full flex flex-col gap-2"> 6 - <h2 class="text-sm font-bold py-2 uppercase dark:text-gray-300">your knots</h2> 7 - <div class="flex flex-col rounded border border-gray-200 dark:border-gray-700 w-full"> 8 - {{ range $knot := .Registrations }} 9 - {{ template "knots/fragments/knotListing" . }} 10 - {{ else }} 11 - <div class="flex items-center justify-center p-2 border-b border-gray-200 dark:border-gray-700 text-gray-500"> 12 - no knots registered yet 13 - </div> 14 - {{ end }} 15 - </div> 16 - <div id="operation-error" class="text-red-500 dark:text-red-400"></div> 17 - </section> 18 - {{ end }}
-10
appview/pages/templates/knots/fragments/secret.html
··· 1 - {{ define "knots/fragments/secret" }} 2 - <div 3 - id="secret" 4 - hx-swap-oob="true" 5 - class="bg-gray-50 dark:bg-gray-700 border border-black dark:border-gray-500 rounded px-6 py-2 w-full lg:w-3xl"> 6 - <h2 class="text-sm font-bold py-2 uppercase dark:text-gray-300">generated secret</h2> 7 - <p class="pb-2">Configure your knot to use this secret, and then hit initialize.</p> 8 - <span class="font-mono overflow-x">{{ .Secret }}</span> 9 - </div> 10 - {{ end }}
+22 -7
appview/pages/templates/knots/index.html
··· 8 8 <section class="bg-white dark:bg-gray-800 p-6 rounded relative w-full mx-auto drop-shadow-sm dark:text-white"> 9 9 <div class="flex flex-col gap-6"> 10 10 {{ block "about" . }} {{ end }} 11 - {{ template "knots/fragments/knotListingFull" . }} 11 + {{ block "list" . }} {{ end }} 12 12 {{ block "register" . }} {{ end }} 13 13 </div> 14 14 </section> ··· 27 27 </section> 28 28 {{ end }} 29 29 30 + {{ define "list" }} 31 + <section class="rounded w-full flex flex-col gap-2"> 32 + <h2 class="text-sm font-bold py-2 uppercase dark:text-gray-300">your knots</h2> 33 + <div class="flex flex-col rounded border border-gray-200 dark:border-gray-700 w-full"> 34 + {{ range $registration := .Registrations }} 35 + {{ template "knots/fragments/knotListing" . }} 36 + {{ else }} 37 + <div class="flex items-center justify-center p-2 border-b border-gray-200 dark:border-gray-700 text-gray-500"> 38 + no knots registered yet 39 + </div> 40 + {{ end }} 41 + </div> 42 + <div id="operation-error" class="text-red-500 dark:text-red-400"></div> 43 + </section> 44 + {{ end }} 45 + 30 46 {{ define "register" }} 31 - <section class="rounded max-w-2xl flex flex-col gap-2"> 47 + <section class="rounded w-full lg:w-fit flex flex-col gap-2"> 32 48 <h2 class="text-sm font-bold py-2 uppercase dark:text-gray-300">register a knot</h2> 33 - <p class="mb-2 dark:text-gray-300">Enter the hostname of your knot to generate a key.</p> 49 + <p class="mb-2 dark:text-gray-300">Enter the hostname of your knot to get started.</p> 34 50 <form 35 - hx-post="/knots/key" 36 - class="space-y-4" 51 + hx-post="/knots/register" 52 + class="max-w-2xl mb-2 space-y-4" 37 53 hx-indicator="#register-button" 38 54 hx-swap="none" 39 55 > ··· 53 69 > 54 70 <span class="inline-flex items-center gap-2"> 55 71 {{ i "plus" "w-4 h-4" }} 56 - generate 72 + register 57 73 </span> 58 74 <span class="pl-2 hidden group-[.htmx-request]:inline"> 59 75 {{ i "loader-circle" "w-4 h-4 animate-spin" }} ··· 64 80 <div id="registration-error" class="error dark:text-red-400"></div> 65 81 </form> 66 82 67 - <div id="secret"></div> 68 83 </section> 69 84 {{ end }}
+2 -2
appview/pages/templates/spindles/fragments/addMemberModal.html
··· 14 14 id="add-member-{{ .Instance }}" 15 15 popover 16 16 class="bg-white w-full md:w-96 dark:bg-gray-800 p-4 rounded border border-gray-200 dark:border-gray-700 drop-shadow dark:text-white backdrop:bg-gray-400/50 dark:backdrop:bg-gray-800/50"> 17 - {{ block "addMemberPopover" . }} {{ end }} 17 + {{ block "addSpindleMemberPopover" . }} {{ end }} 18 18 </div> 19 19 {{ end }} 20 20 21 - {{ define "addMemberPopover" }} 21 + {{ define "addSpindleMemberPopover" }} 22 22 <form 23 23 hx-post="/spindles/{{ .Instance }}/add" 24 24 hx-indicator="#spinner"
+8 -8
appview/pages/templates/spindles/fragments/spindleListing.html
··· 1 1 {{ define "spindles/fragments/spindleListing" }} 2 2 <div id="spindle-{{.Id}}" class="flex items-center justify-between p-2 border-b border-gray-200 dark:border-gray-700"> 3 - {{ block "leftSide" . }} {{ end }} 4 - {{ block "rightSide" . }} {{ end }} 3 + {{ block "spindleLeftSide" . }} {{ end }} 4 + {{ block "spindleRightSide" . }} {{ end }} 5 5 </div> 6 6 {{ end }} 7 7 8 - {{ define "leftSide" }} 8 + {{ define "spindleLeftSide" }} 9 9 {{ if .Verified }} 10 10 <a href="/spindles/{{ .Instance }}" class="hover:no-underline flex items-center gap-2 min-w-0 max-w-[60%]"> 11 11 {{ i "hard-drive" "w-4 h-4" }} ··· 25 25 {{ end }} 26 26 {{ end }} 27 27 28 - {{ define "rightSide" }} 28 + {{ define "spindleRightSide" }} 29 29 <div id="right-side" class="flex gap-2"> 30 30 {{ $style := "px-2 py-1 rounded flex items-center flex-shrink-0 gap-2 text-sm" }} 31 31 {{ if .Verified }} ··· 33 33 {{ template "spindles/fragments/addMemberModal" . }} 34 34 {{ else }} 35 35 <span class="bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200 {{$style}}">{{ i "shield-off" "w-4 h-4" }} unverified</span> 36 - {{ block "retryButton" . }} {{ end }} 36 + {{ block "spindleRetryButton" . }} {{ end }} 37 37 {{ end }} 38 - {{ block "deleteButton" . }} {{ end }} 38 + {{ block "spindleDeleteButton" . }} {{ end }} 39 39 </div> 40 40 {{ end }} 41 41 42 - {{ define "deleteButton" }} 42 + {{ define "spindleDeleteButton" }} 43 43 <button 44 44 class="btn text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 gap-2 group" 45 45 title="Delete spindle" ··· 55 55 {{ end }} 56 56 57 57 58 - {{ define "retryButton" }} 58 + {{ define "spindleRetryButton" }} 59 59 <button 60 60 class="btn gap-2 group" 61 61 title="Retry spindle verification"