Monorepo for Tangled tangled.org

appview/pages: rework profile overview page to use tabs model

Signed-off-by: oppiliappan <me@oppi.li>

oppi.li 24822eb5 d2e5b880

verified
Changed files
+84 -113
appview
+14
appview/db/profile.go
··· 22 22 ByMonth []ByMonth 23 23 } 24 24 25 + func (p *ProfileTimeline) IsEmpty() bool { 26 + if p == nil { 27 + return true 28 + } 29 + 30 + for _, m := range p.ByMonth { 31 + if !m.IsEmpty() { 32 + return false 33 + } 34 + } 35 + 36 + return true 37 + } 38 + 25 39 type ByMonth struct { 26 40 RepoEvents []RepoEvent 27 41 IssueEvents IssueEvents
+4 -4
appview/db/punchcard.go
··· 29 29 Punches []Punch 30 30 } 31 31 32 - func MakePunchcard(e Execer, filters ...filter) (Punchcard, error) { 33 - punchcard := Punchcard{} 32 + func MakePunchcard(e Execer, filters ...filter) (*Punchcard, error) { 33 + punchcard := &Punchcard{} 34 34 now := time.Now() 35 35 startOfYear := time.Date(now.Year(), 1, 1, 0, 0, 0, 0, time.UTC) 36 36 endOfYear := time.Date(now.Year(), 12, 31, 0, 0, 0, 0, time.UTC) ··· 63 63 64 64 rows, err := e.Query(query, args...) 65 65 if err != nil { 66 - return punchcard, err 66 + return nil, err 67 67 } 68 68 defer rows.Close() 69 69 ··· 72 72 var date string 73 73 var count sql.NullInt64 74 74 if err := rows.Scan(&date, &count); err != nil { 75 - return punchcard, err 75 + return nil, err 76 76 } 77 77 78 78 punch.Date, err = time.Parse(time.DateOnly, date)
+28 -9
appview/pages/pages.go
··· 409 409 return p.execute("repo/fork", w, params) 410 410 } 411 411 412 - type ProfileHomePageParams struct { 412 + type ProfileCard struct { 413 + UserDid string 414 + UserHandle string 415 + FollowStatus db.FollowStatus 416 + FollowersCount int 417 + FollowingCount int 418 + Punchcard *db.Punchcard 419 + Profile *db.Profile 420 + Active string 421 + } 422 + 423 + func (p *ProfileCard) GetTabs() [][]string { 424 + tabs := [][]string{ 425 + {"overview", "overview", "square-chart-gantt"}, 426 + {"repos", "repos", "book-marked"}, 427 + {"starred", "starred", "star"}, 428 + } 429 + 430 + return tabs 431 + } 432 + 433 + type ProfileOverviewParams struct { 413 434 LoggedInUser *oauth.User 414 435 Repos []db.Repo 415 436 CollaboratingRepos []db.Repo 416 437 ProfileTimeline *db.ProfileTimeline 417 - Card ProfileCard 418 - Punchcard db.Punchcard 438 + Card *ProfileCard 439 + Active string 419 440 } 420 441 421 - type ProfileCard struct { 422 - UserDid string 423 - UserHandle string 424 - FollowStatus db.FollowStatus 425 - FollowersCount int 426 - FollowingCount int 442 + func (p *Pages) ProfileOverview(w io.Writer, params ProfileOverviewParams) error { 443 + params.Active = "overview" 444 + return p.executeProfile("user/overview", w, params) 445 + } 427 446 428 447 Profile *db.Profile 429 448 }
+1 -1
appview/pages/templates/user/fragments/editBio.html
··· 13 13 <label class="m-0 p-0" for="description">bio</label> 14 14 <textarea 15 15 type="text" 16 - class="py-1 px-1 w-full" 16 + class="p-2 w-full" 17 17 name="description" 18 18 rows="3" 19 19 placeholder="write a bio">{{ $description }}</textarea>
-2
appview/pages/templates/user/fragments/profileCard.html
··· 1 1 {{ define "user/fragments/profileCard" }} 2 2 {{ $userIdent := didOrHandle .UserDid .UserHandle }} 3 - <div class="bg-white dark:bg-gray-800 px-6 py-4 rounded drop-shadow-sm max-h-fit"> 4 3 <div class="grid grid-cols-3 md:grid-cols-1 gap-1 items-center"> 5 4 <div id="avatar" class="col-span-1 flex justify-center items-center"> 6 5 <div class="w-3/4 aspect-square relative"> ··· 85 84 <div id="update-profile" class="text-red-400 dark:text-red-500"></div> 86 85 </div> 87 86 </div> 88 - </div> 89 87 {{ end }} 90 88 91 89 {{ define "followerFollowing" }}
+37 -97
appview/pages/templates/user/profile.html appview/pages/templates/user/overview.html
··· 1 1 {{ define "title" }}{{ or .Card.UserHandle .Card.UserDid }}{{ end }} 2 2 3 - {{ define "extrameta" }} 4 - <meta property="og:title" content="{{ or .Card.UserHandle .Card.UserDid }}" /> 5 - <meta property="og:type" content="profile" /> 6 - <meta property="og:url" content="https://tangled.sh/{{ or .Card.UserHandle .Card.UserDid }}" /> 7 - <meta property="og:description" content="{{ or .Card.Profile.Description .Card.UserHandle .Card.UserDid }}" /> 8 - {{ end }} 9 - 10 - {{ define "content" }} 11 - <div class="grid grid-cols-1 md:grid-cols-11 gap-4"> 12 - <div class="md:col-span-3 order-1 md:order-1"> 13 - <div class="grid grid-cols-1 gap-4"> 14 - {{ template "user/fragments/profileCard" .Card }} 15 - {{ block "punchcard" .Punchcard }} {{ end }} 16 - </div> 17 - </div> 18 - <div id="all-repos" class="md:col-span-4 order-2 md:order-2"> 19 - <div class="grid grid-cols-1 gap-4"> 20 - {{ block "ownRepos" . }}{{ end }} 21 - {{ block "collaboratingRepos" . }}{{ end }} 22 - </div> 3 + {{ define "profileContent" }} 4 + <div id="all-repos" class="md:col-span-4 order-2 md:order-2"> 5 + <div class="grid grid-cols-1 gap-4"> 6 + {{ block "ownRepos" . }}{{ end }} 7 + {{ block "collaboratingRepos" . }}{{ end }} 23 8 </div> 24 - <div class="md:col-span-4 order-3 md:order-3"> 25 - {{ block "profileTimeline" . }}{{ end }} 26 - </div> 27 - </div> 9 + </div> 10 + <div class="md:col-span-4 order-3 md:order-3"> 11 + {{ block "profileTimeline" . }}{{ end }} 12 + </div> 28 13 {{ end }} 29 14 30 15 {{ define "profileTimeline" }} 31 - <p class="text-sm font-bold p-2 dark:text-white">ACTIVITY</p> 16 + <p class="text-sm font-bold px-2 pb-4 dark:text-white">ACTIVITY</p> 32 17 <div class="flex flex-col gap-4 relative"> 18 + {{ if .ProfileTimeline.IsEmpty }} 19 + <p class="dark:text-white">This user does not have any activity yet.</p> 20 + {{ end }} 21 + 33 22 {{ with .ProfileTimeline }} 34 23 {{ range $idx, $byMonth := .ByMonth }} 35 24 {{ with $byMonth }} 36 - <div class="bg-white dark:bg-gray-800 px-6 py-4 rounded drop-shadow-sm"> 37 - {{ if eq $idx 0 }} 38 - 39 - {{ else }} 40 - {{ $s := "s" }} 41 - {{ if eq $idx 1 }} 42 - {{ $s = "" }} 43 - {{ end }} 44 - <p class="text-sm font-bold dark:text-white mb-2">{{$idx}} month{{$s}} ago</p> 45 - {{ end }} 25 + {{ if not .IsEmpty }} 26 + <div class="border border-gray-200 dark:border-gray-700 rounded-sm py-4 px-6"> 27 + <p class="text-sm font-mono mb-2 text-gray-500 dark:text-gray-400"> 28 + {{ if eq $idx 0 }} 29 + this month 30 + {{ else }} 31 + {{$idx}} month{{if ne $idx 1}}s{{end}} ago 32 + {{ end }} 33 + </p> 46 34 47 - {{ if .IsEmpty }} 48 - <div class="text-gray-500 dark:text-gray-400"> 49 - No activity for this month 50 - </div> 51 - {{ else }} 52 - <div class="flex flex-col gap-1"> 53 - {{ block "repoEvents" .RepoEvents }} {{ end }} 54 - {{ block "issueEvents" .IssueEvents }} {{ end }} 55 - {{ block "pullEvents" .PullEvents }} {{ end }} 35 + <div class="flex flex-col gap-1"> 36 + {{ block "repoEvents" .RepoEvents }} {{ end }} 37 + {{ block "issueEvents" .IssueEvents }} {{ end }} 38 + {{ block "pullEvents" .PullEvents }} {{ end }} 39 + </div> 56 40 </div> 57 41 {{ end }} 58 - </div> 59 - 60 42 {{ end }} 61 - {{ else }} 62 - <p class="dark:text-white">This user does not have any activity yet.</p> 63 43 {{ end }} 64 44 {{ end }} 65 45 </div> ··· 232 212 233 213 {{ define "ownRepos" }} 234 214 <div> 235 - <div class="text-sm font-bold p-2 pr-0 dark:text-white flex items-center justify-between gap-2"> 215 + <div class="text-sm font-bold px-2 pb-4 dark:text-white flex items-center gap-2"> 236 216 <a href="/@{{ or $.Card.UserHandle $.Card.UserDid }}?tab=repos" 237 217 class="flex text-black dark:text-white items-center gap-2 no-underline hover:no-underline group"> 238 218 <span>PINNED REPOS</span> 239 - <span class="flex gap-1 items-center font-normal text-sm text-gray-500 dark:text-gray-400 "> 240 - view all {{ i "chevron-right" "w-4 h-4" }} 241 - </span> 242 219 </a> 243 220 {{ if and .LoggedInUser (eq .LoggedInUser.Did .Card.UserDid) }} 244 - <button 221 + <button 245 222 hx-get="profile/edit-pins" 246 223 hx-target="#all-repos" 247 - class="btn py-0 font-normal text-sm flex gap-2 items-center group"> 224 + class="py-0 font-normal text-sm flex gap-2 items-center group"> 248 225 {{ i "pencil" "w-3 h-3" }} 249 - edit 250 226 {{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 251 227 </button> 252 228 {{ end }} 253 229 </div> 254 230 <div id="repos" class="grid grid-cols-1 gap-4 items-stretch"> 255 231 {{ range .Repos }} 232 + <div class="border border-gray-200 dark:border-gray-700 rounded-sm"> 256 233 {{ template "user/fragments/repoCard" (list $ . false) }} 234 + </div> 257 235 {{ else }} 258 - <p class="px-6 dark:text-white">This user does not have any repos yet.</p> 236 + <p class="dark:text-white">This user does not have any pinned repos.</p> 259 237 {{ end }} 260 238 </div> 261 239 </div> ··· 264 242 {{ define "collaboratingRepos" }} 265 243 {{ if gt (len .CollaboratingRepos) 0 }} 266 244 <div> 267 - <p class="text-sm font-bold p-2 dark:text-white">COLLABORATING ON</p> 245 + <p class="text-sm font-bold px-2 pb-4 dark:text-white">COLLABORATING ON</p> 268 246 <div id="collaborating" class="grid grid-cols-1 gap-4"> 269 247 {{ range .CollaboratingRepos }} 248 + <div class="border border-gray-200 dark:border-gray-700 rounded-sm"> 270 249 {{ template "user/fragments/repoCard" (list $ . true) }} 250 + </div> 271 251 {{ else }} 272 252 <p class="px-6 dark:text-white">This user is not collaborating.</p> 273 253 {{ end }} ··· 276 256 {{ end }} 277 257 {{ end }} 278 258 279 - {{ define "punchcard" }} 280 - {{ $now := now }} 281 - <div> 282 - <p class="p-2 flex gap-2 text-sm font-bold dark:text-white"> 283 - PUNCHCARD 284 - <span class="font-normal text-sm text-gray-500 dark:text-gray-400 "> 285 - {{ .Total | int64 | commaFmt }} commits 286 - </span> 287 - </p> 288 - <div class="bg-white dark:bg-gray-800 px-6 py-4 rounded drop-shadow-sm"> 289 - <div class="grid grid-cols-28 md:grid-cols-14 gap-y-2 w-full h-full"> 290 - {{ range .Punches }} 291 - {{ $count := .Count }} 292 - {{ $theme := "bg-gray-200 dark:bg-gray-700 size-[4px]" }} 293 - {{ if lt $count 1 }} 294 - {{ $theme = "bg-gray-200 dark:bg-gray-700 size-[4px]" }} 295 - {{ else if lt $count 2 }} 296 - {{ $theme = "bg-green-200 dark:bg-green-900 size-[5px]" }} 297 - {{ else if lt $count 4 }} 298 - {{ $theme = "bg-green-300 dark:bg-green-800 size-[5px]" }} 299 - {{ else if lt $count 8 }} 300 - {{ $theme = "bg-green-400 dark:bg-green-700 size-[6px]" }} 301 - {{ else }} 302 - {{ $theme = "bg-green-500 dark:bg-green-600 size-[7px]" }} 303 - {{ end }} 304 - 305 - {{ if .Date.After $now }} 306 - {{ $theme = "border border-gray-200 dark:border-gray-700 size-[4px]" }} 307 - {{ end }} 308 - <div class="w-full h-full flex justify-center items-center"> 309 - <div 310 - class="aspect-square rounded-full transition-all duration-300 {{ $theme }} max-w-full max-h-full" 311 - title="{{ .Date.Format "2006-01-02" }}: {{ .Count }} commits"> 312 - </div> 313 - </div> 314 - {{ end }} 315 - </div> 316 - </div> 317 - </div> 318 - {{ end }}