Code
user_profile.go222 lines
package pages
import (
"net/http"
"github.com/hypercodehq/hypercode/database/models"
"github.com/hypercodehq/libhtml"
"github.com/hypercodehq/libhtml/attr"
"github.com/hypercodehq/hypercode/views/components/layouts"
"github.com/hypercodehq/hypercode/views/components/ui"
)
type RepositoryWithOwner struct {
Repository *models.Repository
OwnerUsername string
StarCount int64
}
type UserProfileData struct {
User *models.User
ProfileUser *models.User
Repositories []*models.Repository
RepositoriesWithOwner []RepositoryWithOwner
StarCounts map[int64]int64
CurrentTab string
CanManage bool
}
func UserProfile(r *http.Request, data *UserProfileData) html.Node {
if data == nil || data.ProfileUser == nil {
data = &UserProfileData{}
}
// Default to overview tab if not specified
if data.CurrentTab == "" {
data.CurrentTab = "overview"
}
var tabContent html.Node
switch data.CurrentTab {
case "repositories":
tabContent = renderUserRepositoriesTab(data)
case "stars":
tabContent = renderUserStarsTab(data)
default: // overview
tabContent = renderUserOverviewTab(data)
}
return layouts.Profile(r,
data.ProfileUser.DisplayName+" - Hypercode",
layouts.ProfileLayoutOptions{
Username: data.ProfileUser.Username,
DisplayName: data.ProfileUser.DisplayName,
IsOrg: false,
CurrentTab: data.CurrentTab,
ShowSettings: data.CanManage,
},
html.Main(
attr.Class("w-full mx-auto max-w-7xl px-4 py-8"),
html.Div(
attr.Class("space-y-6"),
// User header with display name
html.Div(
attr.Class("flex items-center gap-4 pb-6 border-b"),
html.Div(
attr.Class("flex items-center gap-3"),
html.Div(
attr.Class("p-3 rounded-full bg-muted"),
ui.SVGIcon(ui.IconUser, "size-8"),
),
html.Div(
attr.Class("flex flex-col"),
html.H1(
attr.Class("text-2xl font-semibold"),
html.Text(data.ProfileUser.DisplayName),
),
html.P(
attr.Class("text-muted-foreground"),
html.Text("@"+data.ProfileUser.Username),
),
),
),
),
// Tab content
tabContent,
),
),
)
}
func renderUserOverviewTab(data *UserProfileData) html.Node {
if len(data.Repositories) == 0 {
return ui.EmptyState(ui.EmptyStateProps{
Icon: ui.SVGIcon(ui.IconRepository, "size-6"),
Title: "No repositories yet",
Description: "This user hasn't created any repositories yet.",
ShowAction: false,
})
}
// Show top 6 repositories for overview
repoCount := len(data.Repositories)
if repoCount > 6 {
repoCount = 6
}
repoCards := make([]html.Node, repoCount)
for i := 0; i < repoCount; i++ {
repo := data.Repositories[i]
starCount := int64(0)
if data.StarCounts != nil {
starCount = data.StarCounts[repo.ID]
}
repoCards[i] = ui.RepositoryCard(ui.RepositoryCardProps{
OwnerUsername: data.ProfileUser.Username,
Name: repo.Name,
IsPublic: repo.Visibility == "public",
StarCount: starCount,
})
}
return html.Div(
attr.Class("space-y-4"),
html.Div(
attr.Class("flex justify-between items-center"),
html.H2(
attr.Class("text-xl font-medium"),
html.Text("Popular repositories"),
),
html.If(
len(data.Repositories) > 6,
html.A(
attr.Href("/"+data.ProfileUser.Username+"/repositories"),
attr.Class("text-sm text-primary hover:underline"),
html.Text("View all repositories"),
),
),
),
html.Div(
attr.Class("grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"),
html.For(repoCards, func(card html.Node) html.Node {
return card
}),
),
)
}
func renderUserRepositoriesTab(data *UserProfileData) html.Node {
if len(data.Repositories) == 0 {
return ui.EmptyState(ui.EmptyStateProps{
Icon: ui.SVGIcon(ui.IconRepository, "size-6"),
Title: "No repositories yet",
Description: "This user hasn't created any repositories yet.",
ShowAction: false,
})
}
repoCards := make([]html.Node, len(data.Repositories))
for i, repo := range data.Repositories {
starCount := int64(0)
if data.StarCounts != nil {
starCount = data.StarCounts[repo.ID]
}
repoCards[i] = ui.RepositoryCard(ui.RepositoryCardProps{
OwnerUsername: data.ProfileUser.Username,
Name: repo.Name,
IsPublic: repo.Visibility == "public",
StarCount: starCount,
})
}
return html.Div(
attr.Class("space-y-4"),
html.H2(
attr.Class("text-xl font-medium"),
html.Text("Repositories"),
),
html.Div(
attr.Class("grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"),
html.For(repoCards, func(card html.Node) html.Node {
return card
}),
),
)
}
func renderUserStarsTab(data *UserProfileData) html.Node {
if len(data.RepositoriesWithOwner) == 0 {
return ui.EmptyState(ui.EmptyStateProps{
Icon: ui.SVGIcon(ui.IconStar, "size-6"),
Title: "No starred repositories yet",
Description: "This user hasn't starred any repositories yet.",
ShowAction: false,
})
}
repoCards := make([]html.Node, len(data.RepositoriesWithOwner))
for i, repoWithOwner := range data.RepositoriesWithOwner {
repoCards[i] = ui.RepositoryCard(ui.RepositoryCardProps{
OwnerUsername: repoWithOwner.OwnerUsername,
Name: repoWithOwner.Repository.Name,
IsPublic: repoWithOwner.Repository.Visibility == "public",
StarCount: repoWithOwner.StarCount,
})
}
return html.Div(
attr.Class("space-y-4"),
html.H2(
attr.Class("text-xl font-medium"),
html.Text("Starred repositories"),
),
html.Div(
attr.Class("grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"),
html.For(repoCards, func(card html.Node) html.Node {
return card
}),
),
)
}
