Add a profile dropdown menu

This commit is contained in:
Ben Visness 2024-07-02 22:58:33 -05:00
parent a646dddec0
commit dffd1c94b5
9 changed files with 58 additions and 53 deletions

View File

@ -8298,7 +8298,7 @@ header .header-nav > .root-item > a {
display: block; display: block;
padding: var(--spacing-3); padding: var(--spacing-3);
} }
header .header-nav .submenu { header .submenu {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
position: absolute; position: absolute;
@ -8309,16 +8309,16 @@ header .header-nav .submenu {
border-width: 1px; border-width: 1px;
border-top-width: 0; border-top-width: 0;
} }
header .header-nav .submenu > a { header .submenu > a {
padding: var(--spacing-2) var(--spacing-3); padding: var(--spacing-2) var(--spacing-3);
display: block; display: block;
white-space: nowrap; white-space: nowrap;
z-index: 1; z-index: 1;
} }
header .header-nav .root-item:not(:hover):not(.clicked) > .submenu { header .root-item:not(:hover):not(.clicked) > .submenu {
display: none; display: none;
} }
header .header-nav .root-item.clicked .svgicon { header .root-item.clicked .svgicon {
transform: rotate(180deg); transform: rotate(180deg);
} }
header:not(.clicked) .root-item:not(:hover) > .submenu, header:not(.clicked) .root-item:not(:hover) > .submenu,

View File

@ -67,7 +67,7 @@ func TestLoginWithDiscord(t *testing.T) {
} }
func TestLogoutAction(t *testing.T) { func TestLogoutAction(t *testing.T) {
AssertRegexMatch(t, BuildLogoutAction(""), RegexLogoutAction, nil) AssertRegexMatch(t, BuildLogoutAction(""), RegexLogout, nil)
} }
func TestRegister(t *testing.T) { func TestRegister(t *testing.T) {

View File

@ -219,7 +219,7 @@ func BuildLoginWithDiscord(redirectTo string) string {
return Url("/login-with-discord", []Q{{Name: "redirect", Value: redirectTo}}) return Url("/login-with-discord", []Q{{Name: "redirect", Value: redirectTo}})
} }
var RegexLogoutAction = regexp.MustCompile("^/logout$") var RegexLogout = regexp.MustCompile("^/logout$")
func BuildLogoutAction(redir string) string { func BuildLogoutAction(redir string) string {
defer CatchPanic() defer CatchPanic()

View File

@ -144,6 +144,7 @@ header {
display: block; display: block;
padding: var(--spacing-3); padding: var(--spacing-3);
} }
}
.submenu { .submenu {
display: flex; display: flex;
@ -173,7 +174,6 @@ header {
transform: rotate(180deg); transform: rotate(180deg);
} }
} }
}
&:not(.clicked) .root-item:not(:hover), &:not(.clicked) .root-item:not(:hover),
&.clicked .root-item:not(.clicked) { &.clicked .root-item:not(.clicked) {

View File

@ -39,6 +39,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="root-item f6">
<a class="db {{ if .User }}pv2 ph3{{ else }}pa3{{ end }} lh-solid flex f6 {{ if not .User }}bl{{ end }}" href="{{ or .Header.UserProfileUrl .LoginPageUrl }}"> <a class="db {{ if .User }}pv2 ph3{{ else }}pa3{{ end }} lh-solid flex f6 {{ if not .User }}bl{{ end }}" href="{{ or .Header.UserProfileUrl .LoginPageUrl }}">
{{ with .User }} {{ with .User }}
<img class="avatar avatar-user" src="{{ .AvatarUrl }}"> <img class="avatar avatar-user" src="{{ .AvatarUrl }}">
@ -46,6 +47,14 @@
Log In Log In
{{ end }} {{ end }}
</a> </a>
{{ if .User }}
<div class="submenu right-0" id="profile-submenu">
<a href="{{ .Header.UserProfileUrl }}">Profile</a>
<a href="{{ .Header.UserSettingsUrl }}">Settings</a>
<a href="{{ .Header.LogoutUrl }}">Log Out</a>
</div>
{{ end }}
</div>
</header> </header>
<script type="text/javascript"> <script type="text/javascript">
document.addEventListener("DOMContentLoaded", function() { document.addEventListener("DOMContentLoaded", function() {

View File

@ -9,7 +9,7 @@
<a class="dib pv2 pl2" href="{{ .Header.UserProfileUrl }}">{{ .User.Username }}</a> <a class="dib pv2 pl2" href="{{ .Header.UserProfileUrl }}">{{ .User.Username }}</a>
<a class="dib pv2 pr2" href="{{ .Header.UserSettingsUrl }}">(settings)</a> <a class="dib pv2 pr2" href="{{ .Header.UserSettingsUrl }}">(settings)</a>
</div> </div>
<a class="pa2" href="{{ .Header.LogoutActionUrl }}"><span class="icon-logout"></span> Log Out</a> <a class="pa2" href="{{ .Header.LogoutUrl }}"><span class="icon-logout"></span> Log Out</a>
{{ else }} {{ else }}
<a class="pa2" id="register-link" href="{{ .Header.RegisterUrl }}">Register</a> <a class="pa2" id="register-link" href="{{ .Header.RegisterUrl }}">Register</a>
<a class="pa2" id="login-link" href="{{ .LoginPageUrl }}">Log in</a> <a class="pa2" id="login-link" href="{{ .LoginPageUrl }}">Log in</a>

View File

@ -43,11 +43,9 @@ type Header struct {
AdminUrl string AdminUrl string
UserProfileUrl string UserProfileUrl string
UserSettingsUrl string UserSettingsUrl string
LoginActionUrl string LogoutUrl string
LogoutActionUrl string
ForgotPasswordUrl string ForgotPasswordUrl string
RegisterUrl string RegisterUrl string
LoginWithDiscordUrl string
HMNHomepageUrl string HMNHomepageUrl string
ProjectIndexUrl string ProjectIndexUrl string

View File

@ -66,11 +66,9 @@ func getBaseData(c *RequestContext, title string, breadcrumbs []templates.Breadc
Header: templates.Header{ Header: templates.Header{
AdminUrl: hmnurl.BuildAdminApprovalQueue(), // TODO(asaf): Replace with general-purpose admin page AdminUrl: hmnurl.BuildAdminApprovalQueue(), // TODO(asaf): Replace with general-purpose admin page
UserSettingsUrl: hmnurl.BuildUserSettings(""), UserSettingsUrl: hmnurl.BuildUserSettings(""),
LoginActionUrl: hmnurl.BuildLoginAction(c.FullUrl()), LogoutUrl: hmnurl.BuildLogoutAction(c.FullUrl()),
LogoutActionUrl: hmnurl.BuildLogoutAction(c.FullUrl()),
ForgotPasswordUrl: hmnurl.BuildRequestPasswordReset(), ForgotPasswordUrl: hmnurl.BuildRequestPasswordReset(),
RegisterUrl: hmnurl.BuildRegister(""), RegisterUrl: hmnurl.BuildRegister(""),
LoginWithDiscordUrl: hmnurl.BuildLoginWithDiscord(c.FullUrl()),
HMNHomepageUrl: hmnurl.BuildHomepage(), HMNHomepageUrl: hmnurl.BuildHomepage(),
ProjectIndexUrl: hmnurl.BuildProjectIndex(), ProjectIndexUrl: hmnurl.BuildProjectIndex(),

View File

@ -137,7 +137,7 @@ func NewWebsiteRoutes(conn *pgxpool.Pool) http.Handler {
hmnOnly.GET(hmnurl.RegexOldHome, Index) hmnOnly.GET(hmnurl.RegexOldHome, Index)
hmnOnly.POST(hmnurl.RegexLoginAction, securityTimerMiddleware(time.Millisecond*100, Login)) hmnOnly.POST(hmnurl.RegexLoginAction, securityTimerMiddleware(time.Millisecond*100, Login))
hmnOnly.GET(hmnurl.RegexLogoutAction, Logout) hmnOnly.GET(hmnurl.RegexLogout, Logout)
hmnOnly.GET(hmnurl.RegexLoginPage, LoginPage) hmnOnly.GET(hmnurl.RegexLoginPage, LoginPage)
hmnOnly.GET(hmnurl.RegexLoginWithDiscord, LoginWithDiscord) hmnOnly.GET(hmnurl.RegexLoginWithDiscord, LoginWithDiscord)