cover/Elixir.DaProductAppWeb.OrganizationsLive.html

1
:-(
defmodule DaProductAppWeb.OrganizationsLive do
2 @moduledoc """
3 LiveView for Organizations management with CRUD operations and user management.
4
5 Features:
6 - List all organizations with search
7 - Create, edit, delete organizations
8 - View organization users
9 - Organization hierarchy management
10 - Modern UI with DaisyUI components
11 """
12 use DaProductAppWeb, :live_view
13
14 alias DaProductApp.Accounts
15 alias DaProductApp.Accounts.Organization
16
17 @impl true
18
:-(
def mount(_params, session, socket) do
19
:-(
case get_current_user(session) do
20
:-(
nil ->
21 {:ok, redirect(socket, to: ~p"/login")}
22
23 user ->
24
:-(
if has_access?(user) do
25
:-(
if connected?(socket) do
26 # Subscribe to organization events for real-time updates
27
:-(
Phoenix.PubSub.subscribe(DaProductApp.PubSub, "organizations")
28 end
29
30
:-(
socket =
31 socket
32 |> assign(:current_user, user)
33 |> assign(:page_title, "Organizations Management")
34 |> assign(:current_page, :organizations)
35 |> assign(:organizations, list_organizations())
36 |> assign(:search_term, "")
37 |> assign(:show_modal, false)
38 |> assign(:modal_action, :new)
39 |> assign(:selected_organization, nil)
40 |> assign(:form, to_form(Accounts.change_organization(%Organization{})))
41 |> assign(:show_users_modal, false)
42 |> assign(:selected_org_users, [])
43
44 {:ok, socket}
45 else
46 {:ok,
47 socket
48 |> put_flash(:error, "Access denied")
49 |> redirect(to: ~p"/login")}
50 end
51 end
52 end
53
54 @impl true
55
:-(
def handle_params(params, _url, socket) do
56
:-(
{:noreply, apply_action(socket, socket.assigns.live_action, params)}
57 end
58
59 defp apply_action(socket, :index, _params) do
60 socket
61 |> assign(:page_title, "Organizations Management")
62 |> assign(:show_modal, false)
63
:-(
|> assign(:show_users_modal, false)
64 end
65
66 defp apply_action(socket, :new, _params) do
67 socket
68 |> assign(:page_title, "New Organization")
69 |> assign(:show_modal, true)
70 |> assign(:modal_action, :new)
71 |> assign(:selected_organization, %Organization{})
72
:-(
|> assign(:form, to_form(Accounts.change_organization(%Organization{})))
73 end
74
75 defp apply_action(socket, :edit, %{"id" => id}) do
76
:-(
organization = Accounts.get_organization!(id)
77
78 socket
79 |> assign(:page_title, "Edit Organization")
80 |> assign(:show_modal, true)
81 |> assign(:modal_action, :edit)
82 |> assign(:selected_organization, organization)
83
:-(
|> assign(:form, to_form(Accounts.change_organization(organization)))
84 end
85
86 defp apply_action(socket, :users, %{"id" => id}) do
87
:-(
organization = Accounts.get_organization!(id)
88
:-(
users = get_organization_users(id)
89
90 socket
91
:-(
|> assign(:page_title, "#{organization.name} - Users")
92 |> assign(:show_users_modal, true)
93 |> assign(:selected_organization, organization)
94
:-(
|> assign(:selected_org_users, users)
95 end
96
97 @impl true
98
:-(
def handle_event("search", %{"search_term" => search_term}, socket) do
99
:-(
socket =
100 socket
101 |> assign(:search_term, search_term)
102 |> assign(:organizations, list_organizations(search_term))
103
104 {:noreply, socket}
105 end
106
107
:-(
def handle_event("close_modal", _params, socket) do
108 {:noreply, push_patch(socket, to: ~p"/organizations")}
109 end
110
111
:-(
def handle_event("close_users_modal", _params, socket) do
112 {:noreply, push_patch(socket, to: ~p"/organizations")}
113 end
114
115
:-(
def handle_event("delete_organization", %{"id" => id}, socket) do
116
:-(
organization = Accounts.get_organization!(id)
117
118 # Check if organization has users
119
:-(
users_count = get_organization_users_count(id)
120
121
:-(
if users_count > 0 do
122
:-(
{:noreply, put_flash(socket, :error, "Cannot delete organization with #{users_count} users. Please reassign users first.")}
123 else
124
:-(
case Accounts.delete_organization(organization) do
125 {:ok, _} ->
126 # Broadcast the deletion for real-time updates
127
:-(
Phoenix.PubSub.broadcast(DaProductApp.PubSub, "organizations", {:organization_deleted, organization})
128
129
:-(
socket =
130 socket
131 |> put_flash(:info, "Organization deleted successfully")
132 |> assign(:organizations, list_organizations())
133
134 {:noreply, socket}
135
136
:-(
{:error, _changeset} ->
137 {:noreply, put_flash(socket, :error, "Unable to delete organization")}
138 end
139 end
140 end
141
142
:-(
def handle_event("validate", %{"organization" => org_params}, socket) do
143
:-(
changeset =
144
:-(
socket.assigns.selected_organization
145 |> Accounts.change_organization(org_params)
146 |> Map.put(:action, :validate)
147
148 {:noreply, assign(socket, form: to_form(changeset))}
149 end
150
151 def handle_event("save", %{"organization" => org_params}, socket) do
152
:-(
save_organization(socket, socket.assigns.modal_action, org_params)
153 end
154
155 # Handle real-time updates from PubSub
156 @impl true
157
:-(
def handle_info({:organization_created, _organization}, socket) do
158 {:noreply, assign(socket, :organizations, list_organizations())}
159 end
160
161
:-(
def handle_info({:organization_updated, _organization}, socket) do
162 {:noreply, assign(socket, :organizations, list_organizations())}
163 end
164
165
:-(
def handle_info({:organization_deleted, _organization}, socket) do
166 {:noreply, assign(socket, :organizations, list_organizations())}
167 end
168
169
:-(
defp save_organization(socket, :new, org_params) do
170
:-(
case Accounts.create_organization(org_params) do
171 {:ok, organization} ->
172 # Broadcast the creation for real-time updates
173
:-(
Phoenix.PubSub.broadcast(DaProductApp.PubSub, "organizations", {:organization_created, organization})
174
175
:-(
socket =
176 socket
177 |> put_flash(:info, "Organization created successfully")
178 |> push_patch(to: ~p"/organizations")
179
180 {:noreply, socket}
181
182
:-(
{:error, %Ecto.Changeset{} = changeset} ->
183 {:noreply, assign(socket, form: to_form(changeset))}
184 end
185 end
186
187
:-(
defp save_organization(socket, :edit, org_params) do
188
:-(
case Accounts.update_organization(socket.assigns.selected_organization, org_params) do
189 {:ok, organization} ->
190 # Broadcast the update for real-time updates
191
:-(
Phoenix.PubSub.broadcast(DaProductApp.PubSub, "organizations", {:organization_updated, organization})
192
193
:-(
socket =
194 socket
195 |> put_flash(:info, "Organization updated successfully")
196 |> push_patch(to: ~p"/organizations")
197
198 {:noreply, socket}
199
200
:-(
{:error, %Ecto.Changeset{} = changeset} ->
201 {:noreply, assign(socket, form: to_form(changeset))}
202 end
203 end
204
205
:-(
defp list_organizations(search_term \\ "") do
206 import Ecto.Query
207
208
:-(
query = from o in Organization,
209 left_join: u in assoc(o, :users),
210 group_by: [o.id, o.name, o.inserted_at, o.updated_at],
211 select: %{
212 id: o.id,
213 name: o.name,
214 inserted_at: o.inserted_at,
215 updated_at: o.updated_at,
216 users_count: count(u.id)
217 },
218 order_by: [desc: o.updated_at]
219
220
:-(
query =
221 if search_term != "" do
222
:-(
search_pattern = "%#{search_term}%"
223
:-(
from o in query, where: ilike(o.name, ^search_pattern)
224 else
225
:-(
query
226 end
227
228
:-(
DaProductApp.Repo.all(query)
229 end
230
231
:-(
defp get_organization_users(org_id) do
232 import Ecto.Query
233
234 from(u in DaProductApp.Accounts.User,
235 where: u.organization_id == ^org_id,
236 preload: [:role],
237 order_by: [asc: u.name]
238 )
239
:-(
|> DaProductApp.Repo.all()
240 end
241
242
:-(
defp get_organization_users_count(org_id) do
243 import Ecto.Query
244
245 from(u in DaProductApp.Accounts.User,
246 where: u.organization_id == ^org_id,
247 select: count(u.id)
248 )
249
:-(
|> DaProductApp.Repo.one()
250 end
251
252 defp change_organization(%Organization{} = organization, attrs \\ %{}) do
253 Organization.changeset(organization, attrs)
254 end
255
256 # Helper functions for authentication and access control
257
:-(
defp get_current_user(session) do
258
:-(
case session do
259 %{"user_id" => user_id} when is_binary(user_id) or is_integer(user_id) ->
260
:-(
Accounts.get_user!(user_id)
261
:-(
_ ->
262 nil
263 end
264 rescue
265
:-(
_ -> nil
266 end
267
268
:-(
defp has_access?(_user), do: true
269 end
Line Hits Source