diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index cf03f0d..4bdf07e 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,30 +4,17 @@
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
+
+
+
@@ -100,6 +87,17 @@
+
+
+
+
+
+
+
+
+
+
+
@@ -139,7 +137,8 @@
-
+
+
true
diff --git a/pkg/i18n/locale.en.json b/pkg/i18n/locale.en.json
index bfe530c..867ce39 100644
--- a/pkg/i18n/locale.en.json
+++ b/pkg/i18n/locale.en.json
@@ -5,6 +5,8 @@
"nickname": "Nickname",
"password": "Password",
"unknown": "Unknown",
+ "apply": "Apply",
+ "back": "Back",
"magicToken": "Magic Token",
"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!",
diff --git a/pkg/i18n/locale.zh.json b/pkg/i18n/locale.zh.json
index f0000d3..18e89fe 100644
--- a/pkg/i18n/locale.zh.json
+++ b/pkg/i18n/locale.zh.json
@@ -5,6 +5,8 @@
"nickname": "昵称",
"password": "密码",
"unknown": "未知",
+ "apply": "应用",
+ "back": "返回",
"magicToken": "魔法令牌",
"signinTitle": "登陆",
"signinCaption": "登陆 Solarpass 以探索整个 Solar Network,浏览帖子、探索社区、和你的好朋友聊八卦,一切尽在 Solar Network!",
diff --git a/pkg/server/ui/index.go b/pkg/server/ui/index.go
index a49fe78..fce0b19 100644
--- a/pkg/server/ui/index.go
+++ b/pkg/server/ui/index.go
@@ -36,4 +36,7 @@ func MapUserInterface(A *fiber.App, authFunc func(c *fiber.Ctx, overrides ...str
pages.Post("/mfa/apply", mfaApplyAction)
pages.Get("/users/me", authCheckWare, selfUserinfoPage)
+ pages.Get("/users/me/personalize", authCheckWare, personalizePage)
+
+ pages.Post("/users/me/personalize", authCheckWare, personalizeAction)
}
diff --git a/pkg/server/ui/personalize.go b/pkg/server/ui/personalize.go
new file mode 100644
index 0000000..b2b0687
--- /dev/null
+++ b/pkg/server/ui/personalize.go
@@ -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")
+}
diff --git a/pkg/views/layouts/auth.gohtml b/pkg/views/layouts/auth.gohtml
index 7187ed7..b993624 100644
--- a/pkg/views/layouts/auth.gohtml
+++ b/pkg/views/layouts/auth.gohtml
@@ -107,6 +107,12 @@
display: unset;
}
+ .columns-two {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 1rem;
+ }
+
@media (min-width: 768px) {
.wrapper-card {
grid-template-columns: 1fr 1fr;
diff --git a/pkg/views/layouts/user-center.gohtml b/pkg/views/layouts/user-center.gohtml
index 27c78e3..9bf2759 100644
--- a/pkg/views/layouts/user-center.gohtml
+++ b/pkg/views/layouts/user-center.gohtml
@@ -78,10 +78,66 @@
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) {
.wrapper-card {
grid-template-columns: 1fr 1fr;
}
+
+ .responsive-title-gap {
+ height: calc(56px + 0.44rem);
+ display: block;
+ }
}