Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 97 additions & 21 deletions src/internal/assets/templates/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,42 @@
<header class="mb-8">
<h1 class="text-3xl font-bold text-gray-800">Family Plan Manager</h1>
<nav class="mt-4">
<ul class="flex space-x-4 items-center">
<ul class="flex flex-wrap gap-4 items-center">
{{if .isAuthenticated}}
<li class="flex items-center">
<span class="text-gray-600 mr-4"
>Signed in as:
<span class="font-semibold"
>{{if .name}}{{.name}}{{else}}{{.username}}{{end}}</span
></span
>
<button
id="openProfileModalBtn"
class="text-blue-600 hover:text-blue-800 mr-4"
_="on click toggle .hidden on #profileModal"
>
Edit Profile
</button>
<a href="/logout" class="text-red-600 hover:text-red-800"
>Logout</a
<li class="flex items-center gap-3">
{{if .avatarURL}}
<img
src="{{.avatarURL}}"
alt=""
class="h-10 w-10 rounded-full object-cover border border-gray-200"
/>
{{else}}
<div
class="h-10 w-10 rounded-full bg-blue-100 text-blue-700 flex items-center justify-center font-semibold"
>
{{if .name}}{{slice .name 0 1 | upper}}{{else}}{{slice .username 0 1 | upper}}{{end}}
</div>
{{end}}
<div class="text-gray-600">
<div>
Signed in as
<span class="font-semibold text-gray-800"
>{{if .name}}{{.name}}{{else}}{{.username}}{{end}}</span
>
</div>
<div class="mt-1 flex items-center gap-3 text-sm">
<button
id="openProfileModalBtn"
class="text-blue-600 hover:text-blue-800"
_="on click toggle .hidden on #profileModal"
>
Edit Profile
</button>
<a href="/logout" class="text-red-600 hover:text-red-800"
>Logout</a
>
</div>
</div>
</li>
{{end}}
</ul>
Expand All @@ -70,10 +87,10 @@ <h1 class="text-3xl font-bold text-gray-800">Family Plan Manager</h1>
<!-- Profile Edit Modal -->
<div
id="profileModal"
class="fixed inset-0 bg-gray-500 bg-opacity-75 flex items-center justify-center z-50 hidden"
class="fixed inset-0 bg-gray-500 bg-opacity-75 flex items-center justify-center z-50 hidden p-4"
_="on click if event.target.id == 'profileModal' then add .hidden to me end"
>
<div class="bg-white rounded-lg p-8 max-w-md w-full">
<div class="bg-white rounded-lg p-8 max-w-md w-full max-h-[90vh] overflow-y-auto">
<div class="flex justify-between items-center mb-6">
<h3 class="text-xl font-bold">Edit Profile</h3>
<button
Expand Down Expand Up @@ -112,7 +129,9 @@ <h3 class="text-xl font-bold">Edit Profile</h3>
<form
id="profileForm"
hx-post="/profile"
hx-encoding="multipart/form-data"
hx-swap="none"
enctype="multipart/form-data"
_="on htmx:afterRequest
if event.detail.successful
add .hidden to #profileModal
Expand All @@ -121,15 +140,72 @@ <h3 class="text-xl font-bold">Edit Profile</h3>
end"
class="space-y-4"
>
<div class="flex items-center gap-4 rounded-md border border-gray-200 bg-gray-50 p-4">
{{if .avatarURL}}
<img
src="{{.avatarURL}}"
alt=""
class="h-16 w-16 rounded-full object-cover border border-gray-200"
/>
{{else}}
<div
class="h-16 w-16 rounded-full bg-blue-100 text-blue-700 flex items-center justify-center text-xl font-semibold"
>
{{if .name}}{{slice .name 0 1 | upper}}{{else}}{{slice .username 0 1 | upper}}{{end}}
</div>
{{end}}
<div>
<p class="font-semibold text-gray-800">
{{if .name}}{{.name}}{{else}}{{.username}}{{end}}
</p>
{{if .username}}
<p class="text-sm text-gray-500">@{{.username}}</p>
{{end}}
</div>
</div>

<div>
<label
for="profileModalAvatar"
class="block text-sm font-medium text-gray-700 mb-1"
>Profile Picture</label
>
<input
type="file"
id="profileModalAvatar"
name="avatar"
accept="image/jpeg,image/png,image/svg+xml,image/gif,image/webp"
class="w-full text-sm text-gray-700 border border-gray-300 rounded-md file:mr-4 file:border-0 file:bg-blue-50 file:text-blue-700 file:px-3 file:py-2 hover:file:bg-blue-100"
/>
<p class="text-sm text-gray-500 mt-1">
JPG, PNG, GIF, SVG, or WebP up to 5 MB.
</p>
</div>

<div>
<label
for="profileModalUsername"
class="block text-sm font-medium text-gray-700 mb-1"
>Username</label
>
<input
type="text"
id="profileModalUsername"
value="{{.username}}"
readonly
class="w-full px-3 py-2 border border-gray-300 rounded-md bg-gray-100 text-gray-600"
/>
</div>

<div>
<label
for="name"
for="profileModalName"
class="block text-sm font-medium text-gray-700 mb-1"
>Display Name</label
>
<input
type="text"
id="name"
id="profileModalName"
name="name"
value="{{.name}}"
required
Expand Down
8 changes: 8 additions & 0 deletions src/internal/assets/templates/plan_details.html
Original file line number Diff line number Diff line change
Expand Up @@ -256,12 +256,20 @@ <h3 class="text-lg font-semibold mb-4">Members</h3>
{{range .members}}
<div class="flex justify-between items-center p-3 border rounded-lg">
<div class="flex items-center flex-1">
{{if .AvatarURL}}
<img
src="{{.AvatarURL}}"
alt=""
class="w-10 h-10 rounded-full object-cover border border-gray-200"
/>
{{else}}
<div
class="w-10 h-10 bg-gray-200 rounded-full flex items-center justify-center text-gray-700"
>
{{if .Name}} {{slice .Name 0 1 | upper}} {{else}} {{slice
.Username 0 1 | upper}} {{end}}
</div>
{{end}}
<div class="ml-3 flex-1">
<div class="flex justify-between">
<div>
Expand Down
69 changes: 66 additions & 3 deletions src/internal/assets/templates/profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,84 @@ <h2 class="text-2xl font-bold text-gray-800 mb-6">Edit Profile</h2>
<form
id="profilePageForm"
hx-post="/profile"
hx-encoding="multipart/form-data"
hx-swap="none"
enctype="multipart/form-data"
_="on htmx:afterRequest
if event.detail.successful
reload() the location of the window
end
end"
end"
class="space-y-4"
>
<div
class="flex items-center gap-4 rounded-md border border-gray-200 bg-gray-50 p-4"
>
{{if .avatarURL}}
<img
src="{{.avatarURL}}"
alt=""
class="h-16 w-16 rounded-full object-cover border border-gray-200"
/>
{{else}}
<div
class="h-16 w-16 rounded-full bg-blue-100 text-blue-700 flex items-center justify-center text-xl font-semibold"
>
{{if .name}}{{slice .name 0 1 | upper}}{{else}}{{slice .username 0 1 | upper}}{{end}}
</div>
{{end}}
<div>
<p class="font-semibold text-gray-800">
{{if .name}}{{.name}}{{else}}{{.username}}{{end}}
</p>
{{if .username}}
<p class="text-sm text-gray-500">@{{.username}}</p>
{{end}}
</div>
</div>

<div>
<label
for="profilePageAvatar"
class="block text-sm font-medium text-gray-700 mb-1"
>Profile Picture</label
>
<input
type="file"
id="profilePageAvatar"
name="avatar"
accept="image/jpeg,image/png,image/svg+xml,image/gif,image/webp"
class="w-full text-sm text-gray-700 border border-gray-300 rounded-md file:mr-4 file:border-0 file:bg-blue-50 file:text-blue-700 file:px-3 file:py-2 hover:file:bg-blue-100"
/>
<p class="text-sm text-gray-500 mt-1">
JPG, PNG, GIF, SVG, or WebP up to 5 MB.
</p>
</div>

<div>
<label
for="profilePageUsername"
class="block text-sm font-medium text-gray-700 mb-1"
>Username</label
>
<input
type="text"
id="profilePageUsername"
value="{{.username}}"
readonly
class="w-full px-3 py-2 border border-gray-300 rounded-md bg-gray-100 text-gray-600"
/>
</div>

<div>
<label for="name" class="block text-sm font-medium text-gray-700 mb-1"
<label
for="profilePageName"
class="block text-sm font-medium text-gray-700 mb-1"
>Display Name</label
>
<input
type="text"
id="name"
id="profilePageName"
name="name"
value="{{.name}}"
required
Expand Down
2 changes: 2 additions & 0 deletions src/internal/domain/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ type SessionData struct {
UserID string
Username string
Name string
AvatarURL string
}

// FamilyPlan represents a subscription plan that can be shared among family/friends.
Expand All @@ -27,6 +28,7 @@ type Member struct {
ID string `json:"id"`
Username string `json:"username"`
Name string `json:"name"`
AvatarURL string `json:"avatar_url"`
Balance float64 `json:"balance"`
LeaveRequested bool `json:"leave_requested"`
DateEnded string `json:"date_ended"`
Expand Down
11 changes: 7 additions & 4 deletions src/internal/http/handlers/plans/details_members.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"familyplan/src/internal/billing"
"familyplan/src/internal/domain"
"familyplan/src/internal/planutil"
"familyplan/src/internal/userprofile"

"github.com/pocketbase/pocketbase"
)
Expand All @@ -20,10 +21,11 @@ func loadMembers(app *pocketbase.PocketBase, plan domain.FamilyPlan) ([]domain.M
ownerRecord, err := app.Dao().FindRecordById(usersCollection.Id, plan.Owner)
if err == nil && ownerRecord != nil {
members = append(members, domain.Member{
ID: ownerRecord.Id,
Username: ownerRecord.GetString("username"),
Name: ownerRecord.GetString("name"),
Balance: 0,
ID: ownerRecord.Id,
Username: ownerRecord.GetString("username"),
Name: ownerRecord.GetString("name"),
AvatarURL: userprofile.AvatarURL(ownerRecord),
Balance: 0,
})
uniqueMembers[ownerRecord.Id] = true
}
Expand Down Expand Up @@ -87,6 +89,7 @@ func loadMembers(app *pocketbase.PocketBase, plan domain.FamilyPlan) ([]domain.M
ID: userRecord.Id,
Username: userRecord.GetString("username"),
Name: userRecord.GetString("name"),
AvatarURL: userprofile.AvatarURL(userRecord),
Balance: balance,
LeaveRequested: membership.GetBool("leave_requested"),
DateEnded: membership.GetDateTime("date_ended").String(),
Expand Down
Loading
Loading