cover/Elixir.DaProductAppWeb.QRValidationsLive.html

1
:-(
defmodule DaProductAppWeb.QRValidationsLive do
2 @moduledoc """
3 LiveView for QR Validation monitoring and analytics.
4
5 Features:
6 - Read-only QR validation monitoring
7 - Search and filter capabilities
8 - Real-time validation status updates
9 - QR validation details and history
10 - Validation analytics
11 """
12 use DaProductAppWeb, :live_view
13
14 alias DaProductApp.Accounts
15 alias DaProductApp.Monitoring
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 QR validation events for real-time updates
27
:-(
Phoenix.PubSub.subscribe(DaProductApp.PubSub, "qr_validations")
28 end
29
30
:-(
socket =
31 socket
32 |> assign(:current_user, user)
33 |> assign(:page_title, "QR Validation Monitoring")
34 |> assign(:current_page, :qr_validations)
35 |> assign(:qr_validations, [])
36 |> assign(:search_term, "")
37 |> assign(:status_filter, :all)
38 |> assign(:validation_type_filter, :all)
39 |> assign(:corridor_filter, :all)
40 |> assign(:date_from, nil)
41 |> assign(:date_to, nil)
42 |> assign(:page, 1)
43 |> assign(:page_size, 50)
44 |> assign(:has_more, false)
45 |> assign(:selected_qr_validation, nil)
46 |> assign(:show_details_modal, false)
47 |> assign(:stats, %{})
48 |> load_qr_validations()
49 |> load_stats()
50
51 {:ok, socket}
52 else
53 {:ok,
54 socket
55 |> put_flash(:error, "Access denied")
56 |> redirect(to: ~p"/login")}
57 end
58 end
59 end
60
61 @impl true
62
:-(
def handle_params(params, _url, socket) do
63
:-(
{:noreply, apply_action(socket, socket.assigns.live_action, params)}
64 end
65
66 defp apply_action(socket, :index, _params) do
67 socket
68 |> assign(:page_title, "QR Validation Monitoring")
69
:-(
|> assign(:show_details_modal, false)
70 end
71
72 defp apply_action(socket, :show, %{"id" => id}) do
73
:-(
qr_validation = Monitoring.get_qr_validation!(id)
74
75 socket
76
:-(
|> assign(:page_title, "QR Validation #{qr_validation.txn_id}")
77 |> assign(:show_details_modal, true)
78
:-(
|> assign(:selected_qr_validation, qr_validation)
79 end
80
81 @impl true
82
:-(
def handle_event("search", %{"search_term" => search_term}, socket) do
83
:-(
socket =
84 socket
85 |> assign(:search_term, search_term)
86 |> load_qr_validations()
87
88 {:noreply, socket}
89 end
90
91
:-(
def handle_event("filter_status", %{"status" => status}, socket) do
92
:-(
status_filter = if status == "all", do: :all, else: status
93
94
:-(
socket =
95 socket
96 |> assign(:status_filter, status_filter)
97 |> load_qr_validations()
98
99 {:noreply, socket}
100 end
101
102
:-(
def handle_event("filter_validation_type", %{"validation_type" => validation_type}, socket) do
103
:-(
validation_type_filter = if validation_type == "all", do: :all, else: validation_type
104
105
:-(
socket =
106 socket
107 |> assign(:validation_type_filter, validation_type_filter)
108 |> load_qr_validations()
109
110 {:noreply, socket}
111 end
112
113
:-(
def handle_event("filter_corridor", %{"corridor" => corridor}, socket) do
114
:-(
corridor_filter = if corridor == "all", do: :all, else: corridor
115
116
:-(
socket =
117 socket
118 |> assign(:corridor_filter, corridor_filter)
119 |> load_qr_validations()
120
121 {:noreply, socket}
122 end
123
124
:-(
def handle_event("filter_date_range", %{"date_from" => date_from, "date_to" => date_to}, socket) do
125
:-(
parsed_date_from = if date_from != "", do: Date.from_iso8601!(date_from), else: nil
126
:-(
parsed_date_to = if date_to != "", do: Date.from_iso8601!(date_to), else: nil
127
128
:-(
socket =
129 socket
130 |> assign(:date_from, parsed_date_from)
131 |> assign(:date_to, parsed_date_to)
132 |> load_qr_validations()
133 |> load_stats()
134
135 {:noreply, socket}
136 end
137
138
:-(
def handle_event("close_details", _params, socket) do
139 {:noreply, push_patch(socket, to: ~p"/qr-validations")}
140 end
141
142
:-(
def handle_event("refresh", _params, socket) do
143
:-(
socket =
144 socket
145 |> assign(:page, 1)
146 |> load_qr_validations()
147 |> load_stats()
148 |> put_flash(:info, "Data refreshed")
149
150 {:noreply, socket}
151 end
152
153
:-(
def handle_event("prev_page", _params, socket) do
154
:-(
page = max(socket.assigns.page - 1, 1)
155
156
:-(
socket =
157 socket
158 |> assign(:page, page)
159 |> load_qr_validations()
160
161 {:noreply, socket}
162 end
163
164
:-(
def handle_event("next_page", _params, socket) do
165
:-(
if socket.assigns.has_more do
166
:-(
page = socket.assigns.page + 1
167
168
:-(
socket =
169 socket
170 |> assign(:page, page)
171 |> load_qr_validations()
172
173 {:noreply, socket}
174 else
175 {:noreply, socket}
176 end
177 end
178
179
:-(
def handle_event("set_page_size", %{"page_size" => page_size_str}, socket) do
180
:-(
page_size = case Integer.parse(page_size_str) do
181
:-(
{v, _} when v > 0 -> v
182
:-(
_ -> socket.assigns.page_size || 50
183 end
184
185
:-(
socket =
186 socket
187 |> assign(:page_size, page_size)
188 |> assign(:page, 1)
189 |> load_qr_validations()
190
191 {:noreply, socket}
192 end
193
194 # Handle real-time updates from PubSub
195 @impl true
196
:-(
def handle_info({:qr_validation_created, _qr_validation}, socket) do
197 {:noreply, load_qr_validations(socket)}
198 end
199
200
:-(
def handle_info({:qr_validation_updated, _qr_validation}, socket) do
201 {:noreply, load_qr_validations(socket)}
202 end
203
204
:-(
def handle_info({:qr_validation_status_changed, _qr_validation}, socket) do
205 {:noreply, load_qr_validations(socket)}
206 end
207
208 defp load_qr_validations(socket) do
209
:-(
filters = [
210
:-(
search_term: socket.assigns.search_term,
211
:-(
status: socket.assigns.status_filter,
212
:-(
validation_type: socket.assigns.validation_type_filter,
213
:-(
corridor: socket.assigns.corridor_filter,
214
:-(
date_from: socket.assigns.date_from,
215
:-(
date_to: socket.assigns.date_to,
216
:-(
page: socket.assigns.page,
217
:-(
page_size: socket.assigns.page_size
218 ]
219
220
:-(
qr_validations = Monitoring.list_qr_validations(filters)
221
:-(
has_more = length(qr_validations) >= (socket.assigns.page_size || 50)
222
223 socket
224 |> assign(:qr_validations, qr_validations)
225
:-(
|> assign(:has_more, has_more)
226 end
227
228 defp load_stats(socket) do
229
:-(
stats = Monitoring.get_qr_validation_stats(socket.assigns.date_from, socket.assigns.date_to)
230
:-(
assign(socket, :stats, stats)
231 end
232
233 # Helper functions for the template
234
:-(
defp status_badge_class("validated"), do: "badge-success"
235
:-(
defp status_badge_class("failed"), do: "badge-error"
236
:-(
defp status_badge_class("pending"), do: "badge-warning"
237
:-(
defp status_badge_class("processing"), do: "badge-info"
238
:-(
defp status_badge_class("expired"), do: "badge-error"
239
:-(
defp status_badge_class(_), do: "badge-ghost"
240
241
:-(
defp validation_type_badge_class("INTERNATIONAL"), do: "badge-primary"
242
:-(
defp validation_type_badge_class("DOMESTIC"), do: "badge-secondary"
243
:-(
defp validation_type_badge_class(_), do: "badge-ghost"
244
245
:-(
defp corridor_badge_class("SINGAPORE"), do: "badge-info"
246
:-(
defp corridor_badge_class("UAE"), do: "badge-warning"
247
:-(
defp corridor_badge_class("USA"), do: "badge-accent"
248
:-(
defp corridor_badge_class(_), do: "badge-ghost"
249
250
:-(
defp format_amount(nil), do: "N/A"
251 defp format_amount(amount) when is_binary(amount) do
252
:-(
case Decimal.parse(amount) do
253
:-(
{decimal, _} -> format_decimal(decimal)
254
:-(
:error -> amount
255 end
256 end
257
:-(
defp format_amount(%Decimal{} = amount), do: format_decimal(amount)
258
:-(
defp format_amount(amount) when is_number(amount), do: :erlang.float_to_binary(amount, decimals: 2)
259
260 defp format_decimal(%Decimal{} = decimal) do
261 decimal
262 |> Decimal.round(2)
263
:-(
|> Decimal.to_string()
264 end
265
266 defp format_currency(amount, currency) when currency in ["USD", "SGD", "AED"] do
267
:-(
case currency do
268
:-(
"USD" -> "$#{format_amount(amount)}"
269
:-(
"SGD" -> "S$#{format_amount(amount)}"
270
:-(
"AED" -> "AED #{format_amount(amount)}"
271
:-(
_ -> "#{format_amount(amount)} #{currency}"
272 end
273 end
274
:-(
defp format_currency(amount, currency), do: "#{format_amount(amount)} #{currency || "INR"}"
275
276
:-(
defp qr_validation_status_options do
277 [
278 {"All Statuses", "all"},
279 {"Validated", "validated"},
280 {"Failed", "failed"},
281 {"Pending", "pending"},
282 {"Processing", "processing"},
283 {"Expired", "expired"}
284 ]
285 end
286
287
:-(
defp validation_type_options do
288 [
289 {"All Types", "all"},
290 {"International", "INTERNATIONAL"},
291 {"Domestic", "DOMESTIC"}
292 ]
293 end
294
295
:-(
defp corridor_options do
296 [
297 {"All Corridors", "all"},
298 {"Singapore", "SINGAPORE"},
299 {"UAE", "UAE"},
300 {"USA", "USA"}
301 ]
302 end
303
304 # Helper functions for authentication and access control
305
:-(
defp get_current_user(session) do
306
:-(
case session do
307 %{"user_id" => user_id} when is_binary(user_id) or is_integer(user_id) ->
308
:-(
Accounts.get_user!(user_id)
309
:-(
_ ->
310 nil
311 end
312 rescue
313
:-(
_ -> nil
314 end
315
316
:-(
defp has_access?(_user), do: true
317 end
Line Hits Source