♻️ Refactored web ui with bootstrap and jQuery
This commit is contained in:
parent
1c36b429ea
commit
f1ab0f203f
@ -1,12 +1,11 @@
|
|||||||
package admin
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.solsynth.dev/hydrogen/passport/pkg/utils"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MapAdminEndpoints(A *fiber.App, authFunc utils.AuthFunc) {
|
func MapAdminEndpoints(A *fiber.App, authMiddleware fiber.Handler) {
|
||||||
admin := A.Group("/api/admin").Use(authFunc)
|
admin := A.Group("/api/admin").Use(authMiddleware)
|
||||||
{
|
{
|
||||||
admin.Post("/badges", grantBadge)
|
admin.Post("/badges", grantBadge)
|
||||||
admin.Delete("/badges/:badgeId", revokeBadge)
|
admin.Delete("/badges/:badgeId", revokeBadge)
|
||||||
|
@ -144,7 +144,7 @@ func NewServer() {
|
|||||||
URL: "/favicon.png",
|
URL: "/favicon.png",
|
||||||
}))
|
}))
|
||||||
|
|
||||||
admin.MapAdminEndpoints(A, authFunc)
|
admin.MapAdminEndpoints(A, authMiddleware)
|
||||||
ui.MapUserInterface(A, authFunc)
|
ui.MapUserInterface(A, authFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
package ui
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"html/template"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.solsynth.dev/hydrogen/passport/pkg/database"
|
|
||||||
"git.solsynth.dev/hydrogen/passport/pkg/models"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/gomarkdown/markdown"
|
|
||||||
"github.com/gomarkdown/markdown/html"
|
|
||||||
"github.com/gomarkdown/markdown/parser"
|
|
||||||
"github.com/sujit-baniya/flash"
|
|
||||||
)
|
|
||||||
|
|
||||||
func otherUserinfoPage(c *fiber.Ctx) error {
|
|
||||||
name := c.Params("account")
|
|
||||||
|
|
||||||
var data models.Account
|
|
||||||
if err := database.C.
|
|
||||||
Where(&models.Account{Name: name}).
|
|
||||||
Preload("Profile").
|
|
||||||
Preload("PersonalPage").
|
|
||||||
Preload("Contacts").
|
|
||||||
First(&data).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
birthday := "Unknown"
|
|
||||||
if data.Profile.Birthday != nil {
|
|
||||||
birthday = data.Profile.Birthday.Format(time.RFC822)
|
|
||||||
}
|
|
||||||
|
|
||||||
doc := parser.
|
|
||||||
NewWithExtensions(parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock).
|
|
||||||
Parse([]byte(data.PersonalPage.Content))
|
|
||||||
|
|
||||||
renderer := html.NewRenderer(html.RendererOptions{Flags: html.CommonFlags | html.HrefTargetBlank})
|
|
||||||
|
|
||||||
return c.Render("views/users/directory/userinfo", fiber.Map{
|
|
||||||
"info": flash.Get(c)["message"],
|
|
||||||
"uid": fmt.Sprintf("%08d", data.ID),
|
|
||||||
"joined_at": data.CreatedAt.Format(time.RFC822),
|
|
||||||
"birthday_at": birthday,
|
|
||||||
"personal_page": template.HTML(markdown.Render(doc, renderer)),
|
|
||||||
"userinfo": data,
|
|
||||||
"avatar": data.GetAvatar(),
|
|
||||||
"banner": data.GetBanner(),
|
|
||||||
}, "views/layouts/user-center")
|
|
||||||
}
|
|
@ -43,10 +43,5 @@ func MapUserInterface(A *fiber.App, authFunc utils.AuthFunc) {
|
|||||||
pages.Post("/mfa/apply", mfaApplyAction)
|
pages.Post("/mfa/apply", mfaApplyAction)
|
||||||
pages.Post("/authorize", authCheckWare, authorizeAction)
|
pages.Post("/authorize", authCheckWare, authorizeAction)
|
||||||
|
|
||||||
pages.Get("/@:account", otherUserinfoPage)
|
|
||||||
|
|
||||||
pages.Get("/users/me", authCheckWare, selfUserinfoPage)
|
pages.Get("/users/me", authCheckWare, selfUserinfoPage)
|
||||||
pages.Get("/users/me/personalize", authCheckWare, personalizePage)
|
|
||||||
|
|
||||||
pages.Post("/users/me/personalize", authCheckWare, personalizeAction)
|
|
||||||
}
|
}
|
||||||
|
@ -1,101 +0,0 @@
|
|||||||
package ui
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"git.solsynth.dev/hydrogen/passport/pkg/database"
|
|
||||||
"git.solsynth.dev/hydrogen/passport/pkg/models"
|
|
||||||
"git.solsynth.dev/hydrogen/passport/pkg/services"
|
|
||||||
"git.solsynth.dev/hydrogen/passport/pkg/utils"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
|
||||||
"github.com/samber/lo"
|
|
||||||
"github.com/sujit-baniya/flash"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func personalizePage(c *fiber.Ctx) error {
|
|
||||||
user := c.Locals("principal").(models.Account)
|
|
||||||
localizer := c.Locals("localizer").(*i18n.Localizer)
|
|
||||||
|
|
||||||
var data models.Account
|
|
||||||
if err := database.C.
|
|
||||||
Where(&models.Account{BaseModel: models.BaseModel{ID: user.ID}}).
|
|
||||||
Preload("Profile").
|
|
||||||
Preload("PersonalPage").
|
|
||||||
Preload("Contacts").
|
|
||||||
First(&data).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
var birthday any
|
|
||||||
if data.Profile.Birthday != nil {
|
|
||||||
birthday = strings.SplitN(data.Profile.Birthday.Format(time.RFC3339), "T", 1)[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
apply, _ := localizer.LocalizeMessage(&i18n.Message{ID: "apply"})
|
|
||||||
back, _ := localizer.LocalizeMessage(&i18n.Message{ID: "back"})
|
|
||||||
|
|
||||||
return c.Render("views/users/personalize", fiber.Map{
|
|
||||||
"info": flash.Get(c)["message"],
|
|
||||||
"birthday_at": birthday,
|
|
||||||
"userinfo": data,
|
|
||||||
"i18n": fiber.Map{
|
|
||||||
"apply": apply,
|
|
||||||
"back": back,
|
|
||||||
},
|
|
||||||
}, "views/layouts/user-center")
|
|
||||||
}
|
|
||||||
|
|
||||||
func personalizeAction(c *fiber.Ctx) error {
|
|
||||||
user := c.Locals("principal").(models.Account)
|
|
||||||
|
|
||||||
var data struct {
|
|
||||||
Nick string `form:"nick" validate:"required,min=4,max=24"`
|
|
||||||
Description string `form:"description"`
|
|
||||||
FirstName string `form:"first_name"`
|
|
||||||
LastName string `form:"last_name"`
|
|
||||||
Birthday string `form:"birthday"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := utils.BindAndValidate(c, &data); err != nil {
|
|
||||||
return flash.WithInfo(c, fiber.Map{
|
|
||||||
"message": err.Error(),
|
|
||||||
}).Redirect("/users/me/personalize")
|
|
||||||
}
|
|
||||||
|
|
||||||
var account models.Account
|
|
||||||
if err := database.C.
|
|
||||||
Where(&models.Account{BaseModel: models.BaseModel{ID: user.ID}}).
|
|
||||||
Preload("Profile").
|
|
||||||
First(&account).Error; err != nil {
|
|
||||||
return flash.WithInfo(c, fiber.Map{
|
|
||||||
"message": fmt.Sprintf("unable to get your userinfo: %v", err),
|
|
||||||
}).Redirect("/users/me/personalize")
|
|
||||||
}
|
|
||||||
|
|
||||||
account.Nick = data.Nick
|
|
||||||
account.Description = data.Description
|
|
||||||
account.Profile.FirstName = data.FirstName
|
|
||||||
account.Profile.LastName = data.LastName
|
|
||||||
|
|
||||||
if birthday, err := time.Parse(time.DateOnly, data.Birthday); err == nil {
|
|
||||||
account.Profile.Birthday = lo.ToPtr(birthday)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := database.C.Save(&account).Error; err != nil {
|
|
||||||
return flash.WithInfo(c, fiber.Map{
|
|
||||||
"message": fmt.Sprintf("unable to personalize your account: %v", err),
|
|
||||||
}).Redirect("/users/me/personalize")
|
|
||||||
} else if err := database.C.Save(&account.Profile).Error; err != nil {
|
|
||||||
return flash.WithInfo(c, fiber.Map{
|
|
||||||
"message": fmt.Sprintf("unable to personalize your profile: %v", err),
|
|
||||||
}).Redirect("/users/me/personalize")
|
|
||||||
}
|
|
||||||
|
|
||||||
services.InvalidAuthCacheWithUser(account.ID)
|
|
||||||
|
|
||||||
return flash.WithInfo(c, fiber.Map{
|
|
||||||
"message": "your account has been personalized",
|
|
||||||
}).Redirect("/users/me")
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
<div class="left-part">
|
<div class="left-part">
|
||||||
<img class="logo" alt="Logo" src="/favicon.png" width="64" height="64"/>
|
<img class="logo" alt="Logo" src="/favicon.png" width="64" height="64" />
|
||||||
|
|
||||||
<h1 class="title">{{.i18n.title}} {{.client.Name}}</h1>
|
<h1 class="title">{{.i18n.title}} {{.client.Name}}</h1>
|
||||||
<p class="caption">{{.i18n.caption}}</p>
|
<p class="caption">{{.i18n.caption}}</p>
|
||||||
@ -16,18 +16,18 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="section-title">Requested scopes</div>
|
<div class="section-title">Requested scopes</div>
|
||||||
<ul class="section-scope">
|
<ul class="section-scope list-group">
|
||||||
{{range $_, $element := .scopes}}
|
{{range $_, $element := .scopes}}
|
||||||
<li>
|
<li class="monospace list-group-item">
|
||||||
<span class="section-mono">{{$element}}</span>
|
{{$element}}
|
||||||
</li>
|
</li>
|
||||||
{{end}}
|
{{end}}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="action-form-buttons">
|
<div class="action-form-buttons">
|
||||||
<md-text-button type="button" id="decline-button">{{.i18n.decline}}</md-text-button>
|
<button class="btn btn-secondary" type="button" id="decline-button">{{.i18n.decline}}</button>
|
||||||
<md-filled-button type="submit">{{.i18n.approve}}</md-filled-button>
|
<button class="btn btn-primary" type="submit">{{.i18n.approve}}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@ -37,13 +37,19 @@
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-mono {
|
.section-scope {
|
||||||
|
margin-top: 4px;
|
||||||
|
margin-left: -8px;
|
||||||
|
margin-right: -8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.monospace {
|
||||||
font-family: "Roboto Mono", monospace;
|
font-family: "Roboto Mono", monospace;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.getElementById("decline-button").addEventListener("click", () => {
|
$("#decline-button").on("click", () => {
|
||||||
history.back()
|
history.back()
|
||||||
window.close()
|
window.close()
|
||||||
})
|
})
|
||||||
|
@ -4,33 +4,34 @@
|
|||||||
{{template "views/partials/header"}}
|
{{template "views/partials/header"}}
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="wrapper-container">
|
<div class="outer-container">
|
||||||
<div class="wrapper-middleware">
|
<div class="inner-container">
|
||||||
{{if ne .info nil}}
|
{{if ne .info nil}}
|
||||||
<div class="alert">
|
<div class="alert alert-primary" role="alert">
|
||||||
|
<svg class="bi me-2" role="img" aria-label="Info:">
|
||||||
|
<use xlink:href="#info-fill" />
|
||||||
|
</svg>
|
||||||
<div class="content">{{.info}}</div>
|
<div class="content">{{.info}}</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<div class="wrapper-card">
|
<div class="card card-container">
|
||||||
{{embed}}
|
{{embed}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.wrapper-container {
|
.outer-container {
|
||||||
width: 100dvw;
|
width: 100dvw;
|
||||||
height: 100dvh;
|
height: 100dvh;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
background-color: var(--md-sys-color-surface-container);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapper-middleware {
|
.inner-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
max-width: min(800px, 100dvw);
|
max-width: min(800px, 100dvw);
|
||||||
@ -42,32 +43,15 @@
|
|||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapper-card {
|
.card-container {
|
||||||
transition: all .3s;
|
transition: all .3s;
|
||||||
height: auto;
|
height: auto;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border-radius: 28px;
|
padding: 48px;
|
||||||
padding: 56px;
|
|
||||||
gap: 0 2rem;
|
gap: 0 2rem;
|
||||||
background-color: var(--md-sys-color-surface);
|
|
||||||
color: var(--md-sys-color-on-surface)
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert {
|
|
||||||
padding: 16px;
|
|
||||||
border-radius: 16px;
|
|
||||||
background-color: var(--md-sys-color-secondary-container);
|
|
||||||
color: var(--md-sys-color-on-secondary-container);
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert .content {
|
|
||||||
flex-grow: 1;
|
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
@ -94,9 +78,12 @@
|
|||||||
|
|
||||||
.action-form-buttons {
|
.action-form-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: end;
|
|
||||||
margin-top: 8px;
|
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-form-buttons * {
|
||||||
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.block-field {
|
.block-field {
|
||||||
@ -114,7 +101,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.wrapper-card {
|
.card-container {
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,4 +111,5 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
</html>
|
</html>
|
@ -4,41 +4,46 @@
|
|||||||
{{template "views/partials/header"}}
|
{{template "views/partials/header"}}
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="wrapper-container">
|
<div class="outer-container">
|
||||||
<div class="wrapper-middleware">
|
<div class="inner-container">
|
||||||
{{if ne .info nil}}
|
{{if ne .info nil}}
|
||||||
<div class="alert">
|
<div class="alert alert-primary" role="alert">
|
||||||
|
<svg class="bi me-2" role="img" aria-label="Info:">
|
||||||
|
<use xlink:href="#info-fill" />
|
||||||
|
</svg>
|
||||||
<div class="content">{{.info}}</div>
|
<div class="content">{{.info}}</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<div class="wrapper-card">
|
<div class="card card-container">
|
||||||
{{embed}}
|
{{embed}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.wrapper-container {
|
body,
|
||||||
|
.outer-container {
|
||||||
|
scrollbar-width: none;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outer-container {
|
||||||
width: 100dvw;
|
width: 100dvw;
|
||||||
min-height: 100dvh;
|
min-height: 100dvh;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
scrollbar-width: none;
|
|
||||||
|
|
||||||
background-color: var(--md-sys-color-surface-container);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapper-container::-webkit-scrollbar, body::-webkit-scrollbar {
|
.outer-container::-webkit-scrollbar,
|
||||||
|
body::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
width: 0;
|
width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapper-middleware {
|
.inner-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
max-width: min(800px, 100dvw);
|
max-width: min(800px, 100dvw);
|
||||||
@ -48,35 +53,22 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
|
||||||
|
padding-top: 1rem;
|
||||||
|
padding-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapper-card {
|
.card-container {
|
||||||
transition: all .3s;
|
transition: all .3s;
|
||||||
height: auto;
|
height: auto;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border-radius: 28px;
|
padding: 48px;
|
||||||
padding: 56px;
|
gap: 0 2rem;
|
||||||
gap: 2rem;
|
|
||||||
background-color: var(--md-sys-color-surface);
|
|
||||||
color: var(--md-sys-color-on-surface)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.alert {
|
|
||||||
padding: 16px;
|
|
||||||
border-radius: 16px;
|
|
||||||
background-color: var(--md-sys-color-secondary-container);
|
|
||||||
color: var(--md-sys-color-on-secondary-container);
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert .content {
|
|
||||||
flex-grow: 1;
|
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
margin-left: -8px;
|
margin-left: -8px;
|
||||||
@ -122,7 +114,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.wrapper-card {
|
.card-container {
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,4 +124,5 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div class="left-part">
|
<div class="left-part">
|
||||||
<img class="logo" alt="Logo" src="/favicon.png" width="64" height="64"/>
|
<img class="logo" alt="Logo" src="/favicon.png" width="64" height="64" />
|
||||||
|
|
||||||
<h1 class="title">{{.i18n.title}}</h1>
|
<h1 class="title">{{.i18n.title}}</h1>
|
||||||
<p class="caption">{{.i18n.caption}}</p>
|
<p class="caption">{{.i18n.caption}}</p>
|
||||||
@ -18,17 +18,13 @@
|
|||||||
|
|
||||||
<div class="factor-label">{{.label}}</div>
|
<div class="factor-label">{{.label}}</div>
|
||||||
|
|
||||||
<md-outlined-text-field
|
<div class="mb-1 block-field">
|
||||||
class="block-field"
|
<label for="code" class="form-label">{{.i18n.password}}</label>
|
||||||
name="code"
|
<input type="password" class="form-control" id="code" name="password" autocomplete="off">
|
||||||
type="password"
|
</div>
|
||||||
autocomplete="off"
|
|
||||||
label={{.i18n.password}}
|
|
||||||
>
|
|
||||||
</md-outlined-text-field>
|
|
||||||
|
|
||||||
<div class="action-form-buttons">
|
<div class="action-form-buttons">
|
||||||
<md-filled-button type="submit">{{.i18n.next}}</md-filled-button>
|
<button class="btn btn-primary" type="submit">{{.i18n.next}}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div class="left-part">
|
<div class="left-part">
|
||||||
<img class="logo" alt="Logo" src="/favicon.png" width="64" height="64"/>
|
<img class="logo" alt="Logo" src="/favicon.png" width="64" height="64" />
|
||||||
|
|
||||||
<h1 class="title">{{.i18n.title}}</h1>
|
<h1 class="title">{{.i18n.title}}</h1>
|
||||||
<p class="caption">{{.i18n.caption}}</p>
|
<p class="caption">{{.i18n.caption}}</p>
|
||||||
@ -21,21 +21,19 @@
|
|||||||
<div class="block-field factor-list" role="radiogroup">
|
<div class="block-field factor-list" role="radiogroup">
|
||||||
{{range $_, $element := .factors}}
|
{{range $_, $element := .factors}}
|
||||||
<div class="factor-label">
|
<div class="factor-label">
|
||||||
<md-radio
|
<div class="form-check">
|
||||||
aria-label="{{$element.name}}"
|
<input class="form-check-input" type="radio" name="factor_id" id="factor-{{$element.id}}"
|
||||||
id="factor-{{$element.id}}"
|
value="{{$element.id}}">
|
||||||
value="{{$element.id}}"
|
<label class="form-check-label" for="factor-{{$element.id}}">
|
||||||
touch-target="wrapper"
|
{{$element.name}}
|
||||||
name="factor_id"
|
</label>
|
||||||
>
|
</div>
|
||||||
</md-radio>
|
|
||||||
<label for="factor-{{$element.id}}">{{$element.name}}</label>
|
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="action-form-buttons">
|
<div class="action-form-buttons">
|
||||||
<md-filled-button type="submit">{{.i18n.next}}</md-filled-button>
|
<button class="btn btn-primary" type="submit">{{.i18n.next}}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,84 +6,51 @@
|
|||||||
|
|
||||||
<link rel="icon" type="image/png" href="/favicon.png">
|
<link rel="icon" type="image/png" href="/favicon.png">
|
||||||
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
|
|
||||||
<link
|
|
||||||
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200"
|
|
||||||
rel="stylesheet"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<script type="importmap">
|
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"
|
||||||
{
|
integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r"
|
||||||
"imports": {
|
crossorigin="anonymous"></script>
|
||||||
"@material/web/": "https://esm.run/@material/web/"
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js"
|
||||||
}
|
integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy"
|
||||||
}
|
crossorigin="anonymous"></script>
|
||||||
</script>
|
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"
|
||||||
|
integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
<script type="module">
|
|
||||||
import "@material/web/all.js";
|
|
||||||
import {styles as typescaleStyles} from "@material/web/typography/md-typescale-styles.js";
|
|
||||||
|
|
||||||
document.adoptedStyleSheets.push(typescaleStyles.styleSheet);
|
<svg xmlns="http://www.w3.org/2000/svg" class="d-none">
|
||||||
</script>
|
<symbol id="info-fill" viewBox="0 0 16 16">
|
||||||
|
<path
|
||||||
|
d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm.93-9.412-1 4.705c-.07.34.029.533.304.533.194 0 .487-.07.686-.246l-.088.416c-.287.346-.92.598-1.465.598-.703 0-1.002-.422-.808-1.319l.738-3.468c.064-.293.006-.399-.287-.47l-.451-.081.082-.381 2.29-.287zM8 5.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2z" />
|
||||||
|
</symbol>
|
||||||
|
</svg>
|
||||||
|
|
||||||
<title>Solarpass</title>
|
<title>Solarpass</title>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
:root, :host {
|
html,
|
||||||
--md-sys-color-background: #fbf8ff;
|
body {
|
||||||
--md-sys-color-on-background: #1b1b20;
|
|
||||||
--md-sys-color-surface: #fbf8ff;
|
|
||||||
--md-sys-color-surface-dim: #dcd9df;
|
|
||||||
--md-sys-color-surface-bright: #fbf8ff;
|
|
||||||
--md-sys-color-surface-container-lowest: #ffffff;
|
|
||||||
--md-sys-color-surface-container-low: #f6f2f9;
|
|
||||||
--md-sys-color-surface-container: #f0edf3;
|
|
||||||
--md-sys-color-surface-container-high: #eae7ed;
|
|
||||||
--md-sys-color-surface-container-highest: #e4e1e8;
|
|
||||||
--md-sys-color-on-surface: #1b1b20;
|
|
||||||
--md-sys-color-surface-variant: #e3e1ee;
|
|
||||||
--md-sys-color-on-surface-variant: #464650;
|
|
||||||
--md-sys-color-inverse-surface: #303035;
|
|
||||||
--md-sys-color-inverse-on-surface: #f3eff6;
|
|
||||||
--md-sys-color-outline: #777681;
|
|
||||||
--md-sys-color-outline-variant: #c7c5d2;
|
|
||||||
--md-sys-color-shadow: #000000;
|
|
||||||
--md-sys-color-scrim: #000000;
|
|
||||||
--md-sys-color-surface-tint: #53589d;
|
|
||||||
--md-sys-color-primary: #373c7e;
|
|
||||||
--md-sys-color-on-primary: #ffffff;
|
|
||||||
--md-sys-color-primary-container: #5b60a5;
|
|
||||||
--md-sys-color-on-primary-container: #ffffff;
|
|
||||||
--md-sys-color-inverse-primary: #bec2ff;
|
|
||||||
--md-sys-color-secondary: #5b5c79;
|
|
||||||
--md-sys-color-on-secondary: #ffffff;
|
|
||||||
--md-sys-color-secondary-container: #e2e1ff;
|
|
||||||
--md-sys-color-on-secondary-container: #454662;
|
|
||||||
--md-sys-color-tertiary: #662d5e;
|
|
||||||
--md-sys-color-on-tertiary: #ffffff;
|
|
||||||
--md-sys-color-tertiary-container: #8e5084;
|
|
||||||
--md-sys-color-on-tertiary-container: #ffffff;
|
|
||||||
--md-sys-color-error: #ba1a1a;
|
|
||||||
--md-sys-color-on-error: #ffffff;
|
|
||||||
--md-sys-color-error-container: #ffdad6;
|
|
||||||
--md-sys-color-on-error-container: #410002;
|
|
||||||
}
|
|
||||||
|
|
||||||
.material-symbols-outlined {
|
|
||||||
font-variation-settings:
|
|
||||||
'FILL' 0,
|
|
||||||
'wght' 400,
|
|
||||||
'GRAD' 0,
|
|
||||||
'opsz' 24
|
|
||||||
}
|
|
||||||
|
|
||||||
html, body {
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background-color: var(--md-sys-color-surface-container);
|
}
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
padding: 16px 48px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert .bi {
|
||||||
|
aspect-ratio: 1;
|
||||||
|
width: 16px;
|
||||||
|
fill: var(--bs-alert-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert .content {
|
||||||
|
flex-grow: 1;
|
||||||
|
text-transform: capitalize;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div class="left-part">
|
<div class="left-part">
|
||||||
<img class="logo" alt="Logo" src="/favicon.png" width="64" height="64"/>
|
<img class="logo" alt="Logo" src="/favicon.png" width="64" height="64" />
|
||||||
|
|
||||||
<h1 class="title">{{.i18n.title}}</h1>
|
<h1 class="title">{{.i18n.title}}</h1>
|
||||||
<p class="caption">{{.i18n.caption}}</p>
|
<p class="caption">{{.i18n.caption}}</p>
|
||||||
@ -9,27 +9,19 @@
|
|||||||
<div class="responsive-title-gap"></div>
|
<div class="responsive-title-gap"></div>
|
||||||
|
|
||||||
<form class="action-form" action="/sign-in" method="POST">
|
<form class="action-form" action="/sign-in" method="POST">
|
||||||
<md-outlined-text-field
|
<div class="mb-1 block-field">
|
||||||
class="block-field"
|
<label for="username" class="form-label">{{.i18n.username}}</label>
|
||||||
name="username"
|
<input type="text" class="form-control" id="username" name="username">
|
||||||
type="text"
|
</div>
|
||||||
autocomplete="username"
|
|
||||||
label={{.i18n.username}}
|
|
||||||
>
|
|
||||||
</md-outlined-text-field>
|
|
||||||
|
|
||||||
<md-outlined-text-field
|
<div class="mb-1 block-field">
|
||||||
class="block-field"
|
<label for="password" class="form-label">{{.i18n.password}}</label>
|
||||||
name="password"
|
<input type="password" class="form-control" id="password" name="password">
|
||||||
type="password"
|
</div>
|
||||||
autocomplete="password"
|
|
||||||
label={{.i18n.password}}
|
|
||||||
>
|
|
||||||
</md-outlined-text-field>
|
|
||||||
|
|
||||||
<div class="action-form-buttons">
|
<div class="action-form-buttons">
|
||||||
<md-text-button type="button" href="/sign-up">{{.i18n.signup}}</md-text-button>
|
<a class="btn btn-secondary" href="/sign-up">{{.i18n.signup}}</a>
|
||||||
<md-filled-button type="submit">{{.i18n.next}}</md-filled-button>
|
<button class="btn btn-primary" type="submit">{{.i18n.next}}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
@ -1,5 +1,5 @@
|
|||||||
<div class="left-part">
|
<div class="left-part">
|
||||||
<img class="logo" alt="Logo" src="/favicon.png" width="64" height="64"/>
|
<img class="logo" alt="Logo" src="/favicon.png" width="64" height="64" />
|
||||||
|
|
||||||
<h1 class="title">{{.i18n.title}}</h1>
|
<h1 class="title">{{.i18n.title}}</h1>
|
||||||
<p class="caption">{{.i18n.caption}}</p>
|
<p class="caption">{{.i18n.caption}}</p>
|
||||||
@ -10,58 +10,38 @@
|
|||||||
|
|
||||||
<form class="action-form" action="/sign-up" method="POST">
|
<form class="action-form" action="/sign-up" method="POST">
|
||||||
<div class="columns-two">
|
<div class="columns-two">
|
||||||
<md-outlined-text-field
|
<div class="mb-1">
|
||||||
class="block-field"
|
<label for="name" class="form-label">{{.i18n.username}}</label>
|
||||||
name="name"
|
<input type="text" class="form-control" id="name" name="name">
|
||||||
type="text"
|
</div>
|
||||||
autocomplete="username"
|
|
||||||
label={{.i18n.username}}
|
|
||||||
>
|
|
||||||
</md-outlined-text-field>
|
|
||||||
|
|
||||||
<md-outlined-text-field
|
<div class="mb-1">
|
||||||
class="block-field"
|
<label for="nick" class="form-label">{{.i18n.nickname}}</label>
|
||||||
name="nick"
|
<input type="text" class="form-control" id="nick" name="nick">
|
||||||
type="text"
|
</div>
|
||||||
autocomplete="nickname"
|
|
||||||
label={{.i18n.nickname}}
|
|
||||||
>
|
|
||||||
</md-outlined-text-field>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<md-outlined-text-field
|
<div class="mb-1 block-field">
|
||||||
class="block-field"
|
<label for="email" class="form-label">{{.i18n.email}}</label>
|
||||||
name="email"
|
<input type="email" class="form-control" id="email" name="email">
|
||||||
type="email"
|
</div>
|
||||||
autocomplete="email"
|
|
||||||
label={{.i18n.email}}
|
|
||||||
>
|
|
||||||
</md-outlined-text-field>
|
|
||||||
|
|
||||||
<md-outlined-text-field
|
<div class="mb-1">
|
||||||
class="block-field"
|
<label for="password" class="form-label">{{.i18n.password}}</label>
|
||||||
name="password"
|
<input type="password" class="form-control" id="password" name="password" autocomplete="new-password">
|
||||||
type="password"
|
</div>
|
||||||
autocomplete="new-password"
|
|
||||||
label={{.i18n.password}}
|
|
||||||
>
|
|
||||||
</md-outlined-text-field>
|
|
||||||
|
|
||||||
{{if eq .use_magic_token true}}
|
{{if eq .use_magic_token true}}
|
||||||
<md-outlined-text-field
|
<div class="mb-1">
|
||||||
class="block-field"
|
<label for="token" class="form-label">{{.i18n.password}}</label>
|
||||||
name="magic_token"
|
<input type="password" class="form-control" id="token" name="magic_token" autocomplete="new-password">
|
||||||
type="password"
|
</div>
|
||||||
autocomplete="off"
|
|
||||||
label={{.i18n.magic_token}}
|
|
||||||
>
|
|
||||||
</md-outlined-text-field>
|
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<div class="action-form-buttons">
|
<div class="action-form-buttons">
|
||||||
<md-text-button type="button" href="/sign-in">{{.i18n.signin}}</md-text-button>
|
<a class="btn btn-secondary" href="/sign-in">{{.i18n.signin}}</a>
|
||||||
<md-filled-button type="submit">{{.i18n.next}}</md-filled-button>
|
<button class="btn btn-primary" type="submit">{{.i18n.next}}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
@ -1,165 +0,0 @@
|
|||||||
<link rel="stylesheet" href="https://unpkg.com/tailwindcss@1.4.6/dist/base.min.css">
|
|
||||||
<link rel="stylesheet" href="https://unpkg.com/tailwindcss@1.4.6/dist/components.min.css">
|
|
||||||
<link rel="stylesheet" href="https://unpkg.com/@tailwindcss/typography@0.1.2/dist/typography.min.css">
|
|
||||||
<link rel="stylesheet" href="https://unpkg.com/tailwindcss@1.4.6/dist/utilities.min.css">
|
|
||||||
|
|
||||||
<div class="banner-container">
|
|
||||||
{{if ne .userinfo.Banner nil}}
|
|
||||||
<img src="{{.banner}}" alt="Banner" class="banner">
|
|
||||||
{{end}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="left-part name-card">
|
|
||||||
{{if ne .userinfo.Avatar nil}}
|
|
||||||
<img src="{{.avatar}}" alt="Avatar" class="avatar">
|
|
||||||
{{else}}
|
|
||||||
<div class="avatar empty">
|
|
||||||
<span class="material-symbols-outlined">account_circle</span>
|
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
<div class="name">
|
|
||||||
<h2 class="username">{{.userinfo.Nick}}</h2>
|
|
||||||
<h6 class="nickname">@{{.userinfo.Name}}</h6>
|
|
||||||
</div>
|
|
||||||
{{if gt (len .userinfo.Description) 0}}
|
|
||||||
<div class="description">{{.userinfo.Description}}</div>
|
|
||||||
{{else}}
|
|
||||||
<div class="description empty">No description yet.</div>
|
|
||||||
{{end}}
|
|
||||||
<div class="uid">#{{.uid}}</div>
|
|
||||||
|
|
||||||
<div class="metadata">
|
|
||||||
<div><span class="material-symbols-outlined">calendar_month</span> {{.joined_at}}</div>
|
|
||||||
<div><span class="material-symbols-outlined">cake</span> {{.birthday_at}}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="actions">
|
|
||||||
<md-filled-tonal-button disabled class="action">
|
|
||||||
Add as friend
|
|
||||||
<span slot="icon" class="material-symbols-outlined">group_add</span>
|
|
||||||
</md-filled-tonal-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="right-part">
|
|
||||||
<article class="personal-page prose">
|
|
||||||
{{.personal_page}}
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.avatar {
|
|
||||||
display: block;
|
|
||||||
width: 64px;
|
|
||||||
height: 64px;
|
|
||||||
object-fit: cover;
|
|
||||||
|
|
||||||
clip-path: circle();
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar.empty {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
background-color: var(--md-sys-color-secondary);
|
|
||||||
color: var(--md-sys-color-on-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.banner-container {
|
|
||||||
grid-column: span 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.banner {
|
|
||||||
display: block;
|
|
||||||
object-fit: cover;
|
|
||||||
border-radius: 28px;
|
|
||||||
aspect-ratio: 3 / 1;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name-card {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name-card .name {
|
|
||||||
display: flex;
|
|
||||||
align-items: baseline;
|
|
||||||
gap: 0.3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name-card .username {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name-card .nickname {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name-card .uid {
|
|
||||||
margin-top: -0.8rem;
|
|
||||||
font-size: 0.7rem;
|
|
||||||
font-weight: 400;
|
|
||||||
font-family: Roboto Mono, monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name-card .description {
|
|
||||||
margin-top: -1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description.empty {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name-card .metadata {
|
|
||||||
font-size: 0.85rem;
|
|
||||||
font-weight: 500;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.metadata > div {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.metadata .material-symbols-outlined {
|
|
||||||
font-size: 1rem;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 0.5rem;
|
|
||||||
margin: 0 -0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.actions {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions .action {
|
|
||||||
width: fit-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions .material-symbols-outlined {
|
|
||||||
font-size: 20px;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.left-part .prose {
|
|
||||||
min-width: 0;
|
|
||||||
max-width: unset;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -28,22 +28,6 @@
|
|||||||
<div class="description empty">No description yet.</div>
|
<div class="description empty">No description yet.</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="uid">#{{.uid}}</div>
|
<div class="uid">#{{.uid}}</div>
|
||||||
|
|
||||||
<div class="metadata">
|
|
||||||
<div><span class="material-symbols-outlined">calendar_month</span> {{.joined_at}}</div>
|
|
||||||
<div><span class="material-symbols-outlined">cake</span> {{.birthday_at}}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="actions">
|
|
||||||
<md-filled-tonal-button class="action" href="/users/me/personalize">
|
|
||||||
Personalize
|
|
||||||
<span slot="icon" class="material-symbols-outlined">palette</span>
|
|
||||||
</md-filled-tonal-button>
|
|
||||||
<md-filled-tonal-button disabled class="action" href="/users/me/security">
|
|
||||||
Security
|
|
||||||
<span slot="icon" class="material-symbols-outlined">security</span>
|
|
||||||
</md-filled-tonal-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="right-part">
|
<div class="right-part">
|
||||||
@ -130,7 +114,7 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.metadata > div {
|
.metadata>div {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.25rem;
|
gap: 0.25rem;
|
||||||
|
@ -1,110 +0,0 @@
|
|||||||
<div class="left-part">
|
|
||||||
<img class="logo" alt="Logo" src="/favicon.png" width="64" height="64"/>
|
|
||||||
|
|
||||||
<h1 class="title">Personalize</h1>
|
|
||||||
<p class="caption">Personalize your account, and make us provide better service for you.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="right-part">
|
|
||||||
<div class="responsive-title-gap"></div>
|
|
||||||
|
|
||||||
<div class="personalize-actions">
|
|
||||||
<span>We doesn't support edit avatar / banner through Hydrogen.Passport web yet. Go try our Solian App!</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form class="action-form" action="/users/me/personalize" method="POST">
|
|
||||||
<div class="columns-two">
|
|
||||||
<md-outlined-text-field
|
|
||||||
class="block-field"
|
|
||||||
name="name"
|
|
||||||
type="text"
|
|
||||||
autocomplete="username"
|
|
||||||
label="Username"
|
|
||||||
disabled
|
|
||||||
value="{{.userinfo.Name}}"
|
|
||||||
>
|
|
||||||
</md-outlined-text-field>
|
|
||||||
|
|
||||||
<md-outlined-text-field
|
|
||||||
class="block-field"
|
|
||||||
name="nick"
|
|
||||||
type="text"
|
|
||||||
autocomplete="nickname"
|
|
||||||
label="Nickname"
|
|
||||||
value="{{.userinfo.Nick}}"
|
|
||||||
>
|
|
||||||
</md-outlined-text-field>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="columns-two">
|
|
||||||
<md-outlined-text-field
|
|
||||||
class="block-field"
|
|
||||||
name="first_name"
|
|
||||||
type="text"
|
|
||||||
autocomplete="given_name"
|
|
||||||
label="First Name"
|
|
||||||
value="{{if gt (len .userinfo.Profile.FirstName) 0}}{{.userinfo.Profile.FirstName}}{{end}}"
|
|
||||||
>
|
|
||||||
</md-outlined-text-field>
|
|
||||||
|
|
||||||
<md-outlined-text-field
|
|
||||||
class="block-field"
|
|
||||||
name="last_name"
|
|
||||||
type="text"
|
|
||||||
autocomplete="family_name"
|
|
||||||
label="Last Name"
|
|
||||||
value="{{if gt (len .userinfo.Profile.LastName) 0}}{{.userinfo.Profile.LastName}}{{end}}"
|
|
||||||
>
|
|
||||||
</md-outlined-text-field>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<md-outlined-text-field
|
|
||||||
class="block-field"
|
|
||||||
name="birthday"
|
|
||||||
type="date"
|
|
||||||
label="Birthday"
|
|
||||||
value="{{if ne .birthday nil}}{{.birthday}}{{end}}"
|
|
||||||
>
|
|
||||||
</md-outlined-text-field>
|
|
||||||
|
|
||||||
<md-outlined-text-field
|
|
||||||
class="block-field"
|
|
||||||
name="description"
|
|
||||||
type="textarea"
|
|
||||||
autocomplete="off"
|
|
||||||
label="Description"
|
|
||||||
value="{{if gt (len .userinfo.Description) 0}}{{.userinfo.Description}}{{end}}"
|
|
||||||
style="resize: vertical"
|
|
||||||
>
|
|
||||||
</md-outlined-text-field>
|
|
||||||
|
|
||||||
<div class="action-form-buttons">
|
|
||||||
<md-text-button type="button" href="/users/me">{{.i18n.back}}</md-text-button>
|
|
||||||
<md-filled-button type="submit">{{.i18n.apply}}</md-filled-button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.input-label {
|
|
||||||
font-size: 14px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.personalize-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 0.5rem;
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
margin-left: -0.2rem;
|
|
||||||
margin-right: -0.2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.personalize-actions .personalize-action {
|
|
||||||
width: fit-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.personalize-action .material-symbols-outlined {
|
|
||||||
font-size: 20px;
|
|
||||||
margin-bottom: 2px;
|
|
||||||
}
|
|
||||||
</style>
|
|
Loading…
Reference in New Issue
Block a user