Skip to content

Commit

Permalink
feat: initial profile page
Browse files Browse the repository at this point in the history
  • Loading branch information
AfonsoMartins26 committed Sep 23, 2024
1 parent 783805f commit 99b28b4
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 114 deletions.
5 changes: 4 additions & 1 deletion lib/atomic/accounts/user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ defmodule Atomic.Accounts.User do
alias Atomic.Accounts.Course
alias Atomic.Activities.Enrollment
alias Atomic.Organizations.{Collaborator, Membership, Organization}
alias Atomic.Socials

@required_fields ~w(email password)a
@optional_fields ~w(name slug role confirmed_at phone_number course_id current_organization_id)a
Expand Down Expand Up @@ -39,6 +40,8 @@ defmodule Atomic.Accounts.User do
has_many :enrollments, Enrollment
has_many :collaborators, Collaborator

embeds_one :socials, Socials, on_replace: :update

many_to_many :organizations, Organization, join_through: Membership

timestamps()
Expand Down Expand Up @@ -70,7 +73,6 @@ defmodule Atomic.Accounts.User do

def picture_changeset(user, attrs) do
user
|> cast(attrs, @required_fields ++ @optional_fields)
|> cast_attachments(attrs, [:profile_picture])
end

Expand All @@ -83,6 +85,7 @@ defmodule Atomic.Accounts.User do
|> validate_email()
|> validate_slug()
|> validate_phone_number()
|> cast_embed(:socials, with: &Socials.changeset/2)
end

defp validate_email(changeset) do
Expand Down
47 changes: 43 additions & 4 deletions lib/atomic_web/live/profile_live/form_component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,52 @@ defmodule AtomicWeb.ProfileLive.FormComponent do
use AtomicWeb, :live_component

alias Atomic.Accounts
alias AtomicWeb.Components.ImageUploader
import AtomicWeb.Components.Forms

@extensions_whitelist ~w(.jpg .jpeg .gif .png)
@impl true
def render(assigns) do
~H"""
<div class="px-4 pt-4">
<.form :let={f} for={@changeset} id="profile-form" phx-target={@myself} phx-change="validate" phx-submit="save">
<!-- Section for profile picture upload -->
<div class="mb-6 flex justify-center">
<div class="flex flex-col items-center pr-4">
<%= label(f, :name, "Profile Picture", class: "mt-3 mb-1 text-sm font-medium text-gray-700") %>
<.live_component module={ImageUploader} id="uploader-profile-picture" uploads={@uploads} target={@myself} />
</div>
<!-- Grid layout for social media and contact fields -->
<div class="flex flex-col gap-y-8">
<div class="grid w-full gap-x-4 sm:grid-cols-1 md:grid-cols-4 lg:grid-cols-4">
<.inputs_for :let={socials_form} field={f[:socials]}>
<.field field={socials_form[:instagram]} type="text" class="w-full" />
<.field field={socials_form[:facebook]} type="text" class="w-full" />
<.field field={socials_form[:x]} type="text" class="w-full" />
<.field field={f[:tiktok]} type="text" placeholder="TikTok" class="w-full" />
</.inputs_for>
</div>
</div>
<.field field={f[:name]} type="text" placeholder="Name" class="w-full" />
<.field field={f[:phone_number]} type="text" placeholder="Phone Number" class="w-full" />
<.field field={f[:email]} type="email" placeholder="Email" class="w-full" />
</div>
<!-- Submit button -->
<div class="mt-8 flex w-full justify-end">
<.button size={:md} color={:white} icon="hero-cube" phx-click="submit">Save</.button>
</div>
</.form>
</div>
"""
end

@impl true
def mount(socket) do
{:ok,
socket
|> allow_upload(:picture, accept: @extensions_whitelist, max_entries: 1)}
|> allow_upload(:image,
accept: Uploaders.ProfilePicture.extension_whitelist(),
max_entries: 1
)}
end

@impl true
Expand All @@ -32,6 +70,7 @@ defmodule AtomicWeb.ProfileLive.FormComponent do
{:noreply, assign(socket, :changeset, changeset)}
end

@impl true
def handle_event("save", %{"user" => user_params}, socket) do
user = socket.assigns.user

Expand Down Expand Up @@ -69,8 +108,8 @@ defmodule AtomicWeb.ProfileLive.FormComponent do

defp consume_image_data(socket, user) do
consume_uploaded_entries(socket, :image, fn %{path: path}, entry ->
Accounts.update_user(user, %{
"image" => %Plug.Upload{
Accounts.update_user_picture(user, %{
"profile_picture" => %Plug.Upload{
content_type: entry.client_type,
filename: entry.client_name,
path: path
Expand Down
86 changes: 0 additions & 86 deletions lib/atomic_web/live/profile_live/form_component.html.heex

This file was deleted.

3 changes: 1 addition & 2 deletions lib/atomic_web/live/profile_live/show.ex
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
defmodule AtomicWeb.ProfileLive.Show do
use AtomicWeb, :live_view

import AtomicWeb.Components.Button
import AtomicWeb.Components.Avatar
import AtomicWeb.Components.{Button, Avatar, Gradient}

alias Atomic.Accounts
alias Atomic.Organizations
Expand Down
81 changes: 60 additions & 21 deletions lib/atomic_web/live/profile_live/show.html.heex
Original file line number Diff line number Diff line change
@@ -1,14 +1,56 @@
<div>
<div class="pt-4 px-4">
<div class="flex items-center justify-between">
<div class="min-w-0 flex-1 space-y-2">
<div class="flex flex-row">
<h2 class="text-xl font-bold leading-7 text-zinc-900 sm:text-4xl">
<%= @user.name %>
</h2>
<div class="relative">
<div class="h-64 w-full border-b-2 bg-cover">
<.gradient class="h-64 w-full bg-cover bg-center" seed={@user.id} />
</div>
<!-- Profile Info Container -->
<div class="relative px-4 pt-4">
<div class="flex items-start">
<!-- Profile Picture -->
<div class="relative -mt-16 flex-shrink-0">
<div class="relative">
<.avatar name={@user.name} color={:light_gray} class="h-36 w-36 text-4xl rounded-full border-4 border-white" type={:user} src={Uploaders.ProfilePicture.url({@user.profile_picture, @user}, :original)} />
</div>
</div>
<div class="flex-1 pl-6">
<!-- User Info -->
<h2 class="text-xl font-bold leading-7 text-zinc-900 sm:text-4xl">
<%= @user.name %>
</h2>

<div class="mt-2">
<%= if length(@organizations) > 0 do %>
<div class="mt-2">
<%= for organization <- @organizations do %>
<p class="text-lg font-semibold text-zinc-600 md:text-md lg:text-sm">
<%= organization.name %> - <%= Atomic.Organizations.get_role(@user.id, organization.id) %>
</p>
<% end %>
</div>
<% else %>
<p class="py-2">No organizations found.</p>
<% end %>
</div>
<!-- Social Media Links -->
<div class="mt-4 flex gap-4">
<div class="flex flex-row items-center gap-x-1">
<img src="/images/tiktok.svg" class="h-5 w-5" alt="TikTok" />
<.link class="text-blue-500">Tik Tok</.link>
</div>
<div class="flex flex-row items-center gap-x-1">
<img src="/images/instagram.svg" class="h-5 w-5" alt="Instagram" />
<.link class="text-blue-500">Instagram</.link>
</div>
<div class="flex flex-row items-center gap-x-1">
<img src="/images/facebook.svg" class="h-5 w-5" alt="Facebook" />
<.link class="text-blue-500">Facebook</.link>
</div>
<div class="flex flex-row items-center gap-x-1">
<img src="/images/x.svg" class="h-5 w-5" alt="X" />
<.link class="text-blue-500">X</.link>
</div>
</div>
<p class="text-zinc-500">@<%= @user.slug %></p>
<div class="grid grid-cols-1 gap-4 py-6 mb-2 sm:grid-cols-2 lg:grid-cols-3">

<div class="fllex-row mt-4 flex gap-8">
<%= if @user.email do %>
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Email</dt>
Expand All @@ -31,18 +73,15 @@
</div>
<% end %>
</div>
<!-- Edit Button for Current User -->
<%= if @is_current_user do %>
<div class="mt-4 flex justify-start">
<.button patch={Routes.profile_edit_path(@socket, :edit, @user)}>
<%= gettext("Edit") %>
</.button>
</div>
<% end %>
</div>
<.avatar class="sm:w-44 sm:h-44 sm:text-6xl" name={@user.name} size={:xl} color={:light_gray} />
</div>
<!-- Divider -->
<div class="py-6 mb-2 border-b border-zinc-200"></div>

<%= if @is_current_user do %>
<div class="w-24 flex justify-end">
<.button patch={Routes.profile_edit_path(@socket, :edit, @user)}>
<%= gettext("Edit") %>
</.button>
</div>
<% end %>
</div>
</div>

0 comments on commit 99b28b4

Please sign in to comment.