✨ Personalize
This commit is contained in:
parent
6b26cad796
commit
8e315642a4
43
.idea/workspace.xml
generated
43
.idea/workspace.xml
generated
@ -4,30 +4,17 @@
|
|||||||
<option name="autoReloadType" value="ALL" />
|
<option name="autoReloadType" value="ALL" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="3fefb2c4-b6f9-466b-a523-53352e8d6f95" name="更改" comment=":sparkles: An entire complete sign in user flow">
|
<list default="true" id="3fefb2c4-b6f9-466b-a523-53352e8d6f95" name="更改" comment=":sparkles: User center page">
|
||||||
<change afterPath="$PROJECT_DIR$/pkg/views/layouts/user-center.gohtml" afterDir="false" />
|
<change afterPath="$PROJECT_DIR$/pkg/server/ui/personalize.go" afterDir="false" />
|
||||||
<change afterPath="$PROJECT_DIR$/pkg/views/users/me.gohtml" afterDir="false" />
|
<change afterPath="$PROJECT_DIR$/pkg/views/users/personalize.gohtml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/Passport.iml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/Passport.iml" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/jsLibraryMappings.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/jsLibraryMappings.xml" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/go.mod" beforeDir="false" afterPath="$PROJECT_DIR$/go.mod" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/pkg/i18n/locale.en.json" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/i18n/locale.en.json" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/go.sum" beforeDir="false" afterPath="$PROJECT_DIR$/go.sum" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/pkg/i18n/locale.zh.json" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/i18n/locale.zh.json" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/pkg/models/accounts.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/models/accounts.go" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/pkg/models/auth.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/models/auth.go" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/pkg/models/clients.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/models/clients.go" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/pkg/server/accounts_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/server/accounts_api.go" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/pkg/server/oauth_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/server/oauth_api.go" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/pkg/server/security_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/server/security_api.go" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/pkg/server/startup.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/server/startup.go" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/pkg/server/ui/accounts.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/server/ui/accounts.go" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/pkg/server/ui/index.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/server/ui/index.go" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/pkg/server/ui/index.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/server/ui/index.go" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/pkg/server/userinfo_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/server/userinfo_api.go" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/pkg/services/auth.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/services/auth.go" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/pkg/services/ticker_maintainer.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/services/ticker_maintainer.go" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/pkg/services/ticket.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/services/ticket.go" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/pkg/views/layouts/auth.gohtml" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/views/layouts/auth.gohtml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/pkg/views/layouts/auth.gohtml" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/views/layouts/auth.gohtml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/pkg/views/partials/header.gohtml" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/views/partials/header.gohtml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/pkg/views/layouts/user-center.gohtml" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/views/layouts/user-center.gohtml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/pkg/views/users/me/index.gohtml" beforeDir="false" />
|
<change beforePath="$PROJECT_DIR$/pkg/views/signup.gohtml" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/views/signup.gohtml" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/pkg/views/users/me.gohtml" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/views/users/me.gohtml" afterDir="false" />
|
||||||
</list>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
@ -100,6 +87,17 @@
|
|||||||
<recent name="$PROJECT_DIR$/pkg/services" />
|
<recent name="$PROJECT_DIR$/pkg/services" />
|
||||||
</key>
|
</key>
|
||||||
</component>
|
</component>
|
||||||
|
<component name="RunAnythingCache">
|
||||||
|
<myKeys>
|
||||||
|
<visibility group="Grunt" flag="true" />
|
||||||
|
<visibility group="Gulp" flag="true" />
|
||||||
|
<visibility group="HTTP 请求" flag="true" />
|
||||||
|
<visibility group="Recent projects" flag="true" />
|
||||||
|
<visibility group="Run configurations" flag="true" />
|
||||||
|
<visibility group="npm" flag="true" />
|
||||||
|
<visibility group="yarn" flag="true" />
|
||||||
|
</myKeys>
|
||||||
|
</component>
|
||||||
<component name="RunManager">
|
<component name="RunManager">
|
||||||
<configuration name="Backend" type="GoApplicationRunConfiguration" factoryName="Go Application">
|
<configuration name="Backend" type="GoApplicationRunConfiguration" factoryName="Go Application">
|
||||||
<module name="Passport" />
|
<module name="Passport" />
|
||||||
@ -139,7 +137,8 @@
|
|||||||
<MESSAGE value=":sparkles: New ticket ways" />
|
<MESSAGE value=":sparkles: New ticket ways" />
|
||||||
<MESSAGE value=":sparkles: Sign up & Sign in" />
|
<MESSAGE value=":sparkles: Sign up & Sign in" />
|
||||||
<MESSAGE value=":sparkles: An entire complete sign in user flow" />
|
<MESSAGE value=":sparkles: An entire complete sign in user flow" />
|
||||||
<option name="LAST_COMMIT_MESSAGE" value=":sparkles: An entire complete sign in user flow" />
|
<MESSAGE value=":sparkles: User center page" />
|
||||||
|
<option name="LAST_COMMIT_MESSAGE" value=":sparkles: User center page" />
|
||||||
</component>
|
</component>
|
||||||
<component name="VgoProject">
|
<component name="VgoProject">
|
||||||
<settings-migrated>true</settings-migrated>
|
<settings-migrated>true</settings-migrated>
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
"nickname": "Nickname",
|
"nickname": "Nickname",
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"unknown": "Unknown",
|
"unknown": "Unknown",
|
||||||
|
"apply": "Apply",
|
||||||
|
"back": "Back",
|
||||||
"magicToken": "Magic Token",
|
"magicToken": "Magic Token",
|
||||||
"signinTitle": "Sign In",
|
"signinTitle": "Sign In",
|
||||||
"signinCaption": "Sign in to Solarpass to explore entire Solar Network. Explore posts, discover communities, talk with your best friends. All these things in the Solar Network!",
|
"signinCaption": "Sign in to Solarpass to explore entire Solar Network. Explore posts, discover communities, talk with your best friends. All these things in the Solar Network!",
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
"nickname": "昵称",
|
"nickname": "昵称",
|
||||||
"password": "密码",
|
"password": "密码",
|
||||||
"unknown": "未知",
|
"unknown": "未知",
|
||||||
|
"apply": "应用",
|
||||||
|
"back": "返回",
|
||||||
"magicToken": "魔法令牌",
|
"magicToken": "魔法令牌",
|
||||||
"signinTitle": "登陆",
|
"signinTitle": "登陆",
|
||||||
"signinCaption": "登陆 Solarpass 以探索整个 Solar Network,浏览帖子、探索社区、和你的好朋友聊八卦,一切尽在 Solar Network!",
|
"signinCaption": "登陆 Solarpass 以探索整个 Solar Network,浏览帖子、探索社区、和你的好朋友聊八卦,一切尽在 Solar Network!",
|
||||||
|
@ -36,4 +36,7 @@ func MapUserInterface(A *fiber.App, authFunc func(c *fiber.Ctx, overrides ...str
|
|||||||
pages.Post("/mfa/apply", mfaApplyAction)
|
pages.Post("/mfa/apply", mfaApplyAction)
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
98
pkg/server/ui/personalize.go
Normal file
98
pkg/server/ui/personalize.go
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"git.solsynth.dev/hydrogen/passport/pkg/database"
|
||||||
|
"git.solsynth.dev/hydrogen/passport/pkg/models"
|
||||||
|
"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")
|
||||||
|
}
|
||||||
|
|
||||||
|
return flash.WithInfo(c, fiber.Map{
|
||||||
|
"message": "your account has been personalized",
|
||||||
|
}).Redirect("/users/me")
|
||||||
|
}
|
@ -107,6 +107,12 @@
|
|||||||
display: unset;
|
display: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.columns-two {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.wrapper-card {
|
.wrapper-card {
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
|
@ -78,10 +78,66 @@
|
|||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
margin-left: -8px;
|
||||||
|
margin-bottom: -8px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin-block-start: 0.33em;
|
||||||
|
margin-block-end: 0.33em;
|
||||||
|
font-size: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.caption {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.8rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-form-buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: end;
|
||||||
|
margin-top: 8px;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.responsive-hidden {
|
||||||
|
display: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.columns-two {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
margin: 1rem 0.2rem;
|
||||||
|
border-top: 1px solid var(--md-sys-color-on-surface);
|
||||||
|
border-left: none !important;
|
||||||
|
border-right: none !important;
|
||||||
|
border-bottom: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.wrapper-card {
|
.wrapper-card {
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.responsive-title-gap {
|
||||||
|
height: calc(56px + 0.44rem);
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</html>
|
</html>
|
||||||
|
@ -64,12 +64,4 @@
|
|||||||
<md-filled-button type="submit">{{.i18n.next}}</md-filled-button>
|
<md-filled-button type="submit">{{.i18n.next}}</md-filled-button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
|
||||||
.columns-two {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -3,11 +3,14 @@
|
|||||||
<link rel="stylesheet" href="https://unpkg.com/@tailwindcss/typography@0.1.2/dist/typography.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">
|
<link rel="stylesheet" href="https://unpkg.com/tailwindcss@1.4.6/dist/utilities.min.css">
|
||||||
|
|
||||||
<div class="left-part name-card">
|
<div class="banner-container">
|
||||||
{{$bannerLength := len .userinfo.Banner}} {{if gt $bannerLength 0}}
|
{{if gt (len .userinfo.Banner) 0}}
|
||||||
|
<img src="/api/avatar/{{.userinfo.Banner}}" alt="Banner" class="banner">
|
||||||
{{end}}
|
{{end}}
|
||||||
|
</div>
|
||||||
|
|
||||||
{{$avatarLength := len .userinfo.Avatar}} {{if gt $avatarLength 0}}
|
<div class="left-part name-card">
|
||||||
|
{{if gt (len .userinfo.Avatar) 0}}
|
||||||
<img src="/api/avatar/{{.userinfo.Avatar}}" alt="Avatar" class="avatar">
|
<img src="/api/avatar/{{.userinfo.Avatar}}" alt="Avatar" class="avatar">
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="avatar empty">
|
<div class="avatar empty">
|
||||||
@ -19,7 +22,7 @@
|
|||||||
<h2 class="username">{{.userinfo.Nick}}</h2>
|
<h2 class="username">{{.userinfo.Nick}}</h2>
|
||||||
<h6 class="nickname">@{{.userinfo.Name}}</h6>
|
<h6 class="nickname">@{{.userinfo.Name}}</h6>
|
||||||
</div>
|
</div>
|
||||||
{{$descriptionLength := len .userinfo.Description}} {{if gt $descriptionLength 0}}
|
{{if gt (len .userinfo.Description) 0}}
|
||||||
<div class="description">{{.userinfo.Description}}</div>
|
<div class="description">{{.userinfo.Description}}</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="description empty">No description yet.</div>
|
<div class="description empty">No description yet.</div>
|
||||||
@ -36,7 +39,7 @@
|
|||||||
Personalize
|
Personalize
|
||||||
<span slot="icon" class="material-symbols-outlined">palette</span>
|
<span slot="icon" class="material-symbols-outlined">palette</span>
|
||||||
</md-filled-tonal-button>
|
</md-filled-tonal-button>
|
||||||
<md-filled-tonal-button class="action" href="/users/me/security">
|
<md-filled-tonal-button disabled class="action" href="/users/me/security">
|
||||||
Security
|
Security
|
||||||
<span slot="icon" class="material-symbols-outlined">security</span>
|
<span slot="icon" class="material-symbols-outlined">security</span>
|
||||||
</md-filled-tonal-button>
|
</md-filled-tonal-button>
|
||||||
@ -66,6 +69,18 @@
|
|||||||
color: var(--md-sys-color-on-secondary);
|
color: var(--md-sys-color-on-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.banner-container {
|
||||||
|
grid-column: span 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner {
|
||||||
|
display: block;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 28px;
|
||||||
|
height: 180px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.name-card {
|
.name-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
138
pkg/views/users/personalize.gohtml
Normal file
138
pkg/views/users/personalize.gohtml
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
<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="action-form">
|
||||||
|
<div class="input-label">Avatar</div>
|
||||||
|
<input
|
||||||
|
id="avatar-input"
|
||||||
|
class="block-field"
|
||||||
|
name="avatar"
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
placeholder="Avatar"
|
||||||
|
>
|
||||||
|
|
||||||
|
<div class="input-label">Banner</div>
|
||||||
|
<input
|
||||||
|
id="banner-input"
|
||||||
|
class="block-field"
|
||||||
|
name="banner"
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
placeholder="Banner"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="divider"/>
|
||||||
|
|
||||||
|
<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;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.getElementById("avatar-input").addEventListener("input", (evt) => {
|
||||||
|
if (!evt.target.files) return
|
||||||
|
const data = new FormData();
|
||||||
|
data.set("avatar", evt.target.files[0])
|
||||||
|
fetch("/api/users/me/avatar", {
|
||||||
|
method: "PUT",
|
||||||
|
body: data,
|
||||||
|
}).then(() => {
|
||||||
|
location.href = "/users/me"
|
||||||
|
})
|
||||||
|
})
|
||||||
|
document.getElementById("banner-input").addEventListener("input", (evt) => {
|
||||||
|
if (!evt.target.files) return
|
||||||
|
const data = new FormData();
|
||||||
|
data.set("banner", evt.target.files[0])
|
||||||
|
fetch("/api/users/me/banner", {
|
||||||
|
method: "PUT",
|
||||||
|
body: data,
|
||||||
|
}).then(() => {
|
||||||
|
location.href = "/users/me"
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
Loading…
Reference in New Issue
Block a user