@@ -6,6 +6,8 @@ defmodule AniminaWeb.Admin.AdDetailLive do
66 alias Animina.Ads.QrCode
77 alias AniminaWeb.Layouts
88
9+ require Logger
10+
911 import AniminaWeb.Helpers.AdminHelpers , only: [ parse_int: 2 , format_datetime: 1 ]
1012
1113 use AniminaWeb.Helpers.PaginationHelpers , filter_events: [ ]
@@ -22,6 +24,11 @@ defmodule AniminaWeb.Admin.AdDetailLive do
2224 |> push_navigate ( to: ~p" /admin/ads" ) }
2325
2426 ad ->
27+ ad = maybe_regenerate_qr_code ( ad )
28+
29+ default_cm = 5.0
30+ default_pixels = cm_to_pixels ( default_cm )
31+
2532 { :ok ,
2633 socket
2734 |> assign (
@@ -34,7 +41,9 @@ defmodule AniminaWeb.Admin.AdDetailLive do
3441 daily_counts: Ads . daily_visit_counts ( ad . id ) ,
3542 os_breakdown: Ads . visit_breakdown ( ad . id , :os ) ,
3643 browser_breakdown: Ads . visit_breakdown ( ad . id , :browser ) ,
37- device_breakdown: Ads . visit_breakdown ( ad . id , :device_type )
44+ device_breakdown: Ads . visit_breakdown ( ad . id , :device_type ) ,
45+ custom_cm: default_cm ,
46+ custom_pixels: default_pixels
3847 )
3948 |> stream ( :visits , [ ] ) }
4049 end
@@ -81,6 +90,46 @@ defmodule AniminaWeb.Admin.AdDetailLive do
8190 end
8291 end
8392
93+ @ impl true
94+ def handle_event ( "update-qr-size" , % { "cm" => cm_str } , socket ) do
95+ case Float . parse ( cm_str ) do
96+ { cm , _ } when cm >= 1.0 and cm <= 80.0 ->
97+ { :noreply , assign ( socket , custom_cm: cm , custom_pixels: cm_to_pixels ( cm ) ) }
98+
99+ _ ->
100+ { :noreply , socket }
101+ end
102+ end
103+
104+ defp cm_to_pixels ( cm ) , do: round ( cm * 300 / 2.54 )
105+
106+ defp maybe_regenerate_qr_code ( % { qr_code_path: path } = ad )
107+ when is_binary ( path ) and path != "" do
108+ if File . exists? ( path ) , do: ad , else: regenerate_qr_code ( ad , path )
109+ end
110+
111+ defp maybe_regenerate_qr_code ( ad ) , do: ad
112+
113+ defp regenerate_qr_code ( ad , path ) do
114+ Logger . warning ( "QR code file missing for ad ##{ ad . number } , regenerating: #{ path } " )
115+
116+ with { :ok , new_path } <- QrCode . generate ( ad ) ,
117+ { :ok , updated_ad } <- Ads . update_qr_code_path ( ad , new_path ) do
118+ updated_ad
119+ else
120+ { :error , reason } ->
121+ Logger . error ( "Failed to regenerate QR code for ad ##{ ad . number } : #{ inspect ( reason ) } " )
122+ clear_stale_qr_path ( ad )
123+ end
124+ end
125+
126+ defp clear_stale_qr_path ( ad ) do
127+ case Ads . update_qr_code_path ( ad , nil ) do
128+ { :ok , cleared_ad } -> cleared_ad
129+ _ -> ad
130+ end
131+ end
132+
84133 defp build_path ( socket , overrides ) do
85134 ad = socket . assigns . ad
86135
@@ -155,6 +204,29 @@ defmodule AniminaWeb.Admin.AdDetailLive do
155204 < . icon name = "hero-arrow-down-tray-mini " class = "w-3 h-3 " />
156205 { gettext ( "Download QR" ) }
157206 </ a >
207+ < form phx-change = "update-qr-size " class = "mt-2 flex flex-col items-center gap-1 " >
208+ < label class = "text-xs text-base-content/50 " > { gettext ( "Width (cm)" ) } </ label >
209+ < input
210+ type = "number "
211+ name = "cm "
212+ value = { @ custom_cm }
213+ step = "0.1 "
214+ min = "1 "
215+ max = "80 "
216+ class = "input input-xs input-bordered w-20 text-center "
217+ />
218+ < span class = "text-xs text-base-content/50 " >
219+ { @ custom_pixels } × { @ custom_pixels } px
220+ </ span >
221+ < a
222+ href = { ~p" /admin/ads/#{ @ ad . id } /qr-code/sized?size=#{ @ custom_pixels } " }
223+ class = "btn btn-outline btn-xs "
224+ download
225+ >
226+ < . icon name = "hero-arrow-down-tray-mini " class = "w-3 h-3 " />
227+ { gettext ( "Download custom size" ) }
228+ </ a >
229+ </ form >
158230 </ div >
159231 < div :if = { ! @ ad . qr_code_path } class = "flex flex-col items-center gap-2 " >
160232 < div class = "w-32 h-32 bg-base-300 rounded flex items-center justify-center " >
0 commit comments