cover/Elixir.DaProductAppWeb.LayoutComponents.html

1
:-(
defmodule DaProductAppWeb.LayoutComponents do
2 @moduledoc """
3 Layout components for admin interface with sidebar navigation.
4 """
5 use Phoenix.Component
6 use DaProductAppWeb, :verified_routes
7
8 attr :current_user, :map, required: true
9 attr :page_title, :string, default: "Dashboard"
10 attr :menu_items, :list, default: []
11 slot :inner_block, required: true
12
13 def admin_layout(assigns) do
14
:-(
~H"""
15 <div class="min-h-screen bg-gray-50">
16 <!-- Sidebar -->
17
:-(
<.sidebar current_user={@current_user} menu_items={@menu_items} />
18
19 <!-- Main content -->
20 <div class="lg:pl-72">
21 <!-- Top navigation -->
22
:-(
<.navbar current_user={@current_user} page_title={@page_title} />
23
24 <!-- Page content -->
25 <main class="py-10">
26 <div class="px-4 sm:px-6 lg:px-8">
27
:-(
<%= render_slot(@inner_block) %>
28 </div>
29 </main>
30
31 <!-- Footer for admin layout -->
32
:-(
<.admin_footer />
33 </div>
34 </div>
35 """
36 end
37
38 attr :current_user, :map, default: nil
39 attr :show_header, :boolean, default: true
40 attr :show_footer, :boolean, default: true
41 slot :inner_block, required: true
42
43 def public_layout(assigns) do
44
:-(
~H"""
45 <div class="min-h-screen bg-white">
46
:-(
<%= if @show_header do %>
47
:-(
<.public_header current_user={@current_user} />
48 <% end %>
49
50 <main class="px-4 py-20 sm:px-6 lg:px-8">
51 <div class="mx-auto max-w-2xl">
52
:-(
<%= render_slot(@inner_block) %>
53 </div>
54 </main>
55
56
:-(
<%= if @show_footer do %>
57
:-(
<.public_footer />
58 <% end %>
59 </div>
60 """
61 end
62
63 attr :current_user, :map, default: nil
64
65 def public_header(assigns) do
66
:-(
~H"""
67 <header class="px-4 sm:px-6 lg:px-8">
68 <div class="flex items-center justify-between border-b border-zinc-100 py-3 text-sm">
69 <div class="flex items-center gap-4">
70 <a href="/">
71
:-(
<img src={~p"/images/logo.svg"} width="36" />
72 </a>
73 <p class="bg-brand/5 text-brand rounded-full px-2 font-medium leading-6">
74
:-(
v{Application.spec(:phoenix, :vsn)}
75 </p>
76 </div>
77 <div class="flex items-center gap-4 font-semibold leading-6 text-zinc-900">
78 <a href="https://twitter.com/elixirphoenix" class="hover:text-zinc-700">
79 @elixirphoenix
80 </a>
81 <a href="https://github.com/phoenixframework/phoenix" class="hover:text-zinc-700">
82 GitHub
83 </a>
84 <a
85 href="https://hexdocs.pm/phoenix/overview.html"
86 class="rounded-lg bg-zinc-100 px-2 py-1 hover:bg-zinc-200/80"
87 >
88 Get Started <span aria-hidden="true">&rarr;</span>
89 </a>
90
91 <!-- Login / Logout button -->
92 <div>
93
:-(
<%= cond do %>
94
:-(
<% @current_user != nil -> %>
95
:-(
<form action={~p"/logout"} method="post" class="inline">
96 <input type="hidden" name="_method" value="delete" />
97 <button type="submit" class="rounded-md bg-red-600 text-white px-3 py-1 hover:bg-red-700">Logout</button>
98 </form>
99
:-(
<% true -> %>
100
:-(
<a href={~p"/login"} class="rounded-md bg-blue-600 text-white px-3 py-1 hover:bg-blue-700">Login</a>
101 <% end %>
102 </div>
103 </div>
104 </div>
105 </header>
106 """
107 end
108
109 def public_footer(assigns) do
110
:-(
~H"""
111 <footer class="bg-gray-50 border-t border-gray-200">
112 <div class="mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8">
113 <div class="flex flex-col md:flex-row justify-between items-center">
114 <div class="flex items-center gap-2 mb-4 md:mb-0">
115
:-(
<img src={~p"/images/logo.svg"} width="24" class="h-6 w-6" />
116 <span class="text-sm text-gray-600">© 2024 Mercury UPI PSP. All rights reserved.</span>
117 </div>
118 <div class="flex space-x-6">
119 <a href="#" class="text-sm text-gray-600 hover:text-gray-900">Privacy Policy</a>
120 <a href="#" class="text-sm text-gray-600 hover:text-gray-900">Terms of Service</a>
121 <a href="#" class="text-sm text-gray-600 hover:text-gray-900">Support</a>
122 </div>
123 </div>
124 </div>
125 </footer>
126 """
127 end
128
129 def admin_footer(assigns) do
130
:-(
~H"""
131 <footer class="bg-white border-t border-gray-200 mt-8">
132 <div class="mx-auto max-w-7xl px-4 py-4 sm:px-6 lg:px-8">
133 <div class="flex justify-between items-center">
134 <div class="text-sm text-gray-500">
135 Mercury UPI PSP Admin Dashboard
136 </div>
137 <div class="text-sm text-gray-500">
138 Version 1.0.0
139 </div>
140 </div>
141 </div>
142 </footer>
143 """
144 end
145
146 attr :breadcrumbs, :list, default: []
147
148 def breadcrumb(assigns) do
149
:-(
~H"""
150
:-(
<%= if @breadcrumbs != [] do %>
151 <nav class="flex mb-6" aria-label="Breadcrumb">
152 <ol class="flex items-center space-x-4">
153 <li>
154 <div>
155 <a href="/" class="text-gray-400 hover:text-gray-500">
156
:-(
<.hero_icon name="home" class="h-5 w-5 flex-shrink-0" />
157 <span class="sr-only">Home</span>
158 </a>
159 </div>
160 </li>
161
:-(
<%= for {breadcrumb, index} <- Enum.with_index(@breadcrumbs) do %>
162 <li>
163 <div class="flex items-center">
164 <svg class="h-5 w-5 flex-shrink-0 text-gray-300" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
165 <path d="m5.555 17.776 4-16 .894.448-4 16-.894-.448z" />
166 </svg>
167
:-(
<%= if index == length(@breadcrumbs) - 1 do %>
168 <span class="ml-4 text-sm font-medium text-gray-500" aria-current="page">
169
:-(
<%= breadcrumb.label %>
170 </span>
171 <% else %>
172
:-(
<a href={breadcrumb.path} class="ml-4 text-sm font-medium text-gray-500 hover:text-gray-700">
173
:-(
<%= breadcrumb.label %>
174 </a>
175 <% end %>
176 </div>
177 </li>
178 <% end %>
179 </ol>
180 </nav>
181 <% end %>
182 """
183 end
184
185 attr :title, :string, required: true
186 attr :description, :string, default: nil
187 slot :actions, doc: "Action buttons for the page header"
188
189 def page_header(assigns) do
190
:-(
~H"""
191 <div class="border-b border-gray-200 pb-5 mb-6">
192 <div class="flex items-center justify-between">
193 <div>
194
:-(
<h3 class="text-base font-semibold leading-6 text-gray-900"><%= @title %></h3>
195
:-(
<%= if @description do %>
196
:-(
<p class="mt-2 max-w-4xl text-sm text-gray-500"><%= @description %></p>
197 <% end %>
198 </div>
199
:-(
<%= if assigns.actions do %>
200 <div class="flex space-x-3">
201
:-(
<%= render_slot(@actions) %>
202 </div>
203 <% end %>
204 </div>
205 </div>
206 """
207 end
208
209 attr :current_user, :map, required: true
210 attr :menu_items, :list, default: []
211
212 def sidebar(assigns) do
213
:-(
~H"""
214 <!-- Static sidebar for desktop -->
215 <div class="hidden lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:w-72 lg:flex-col">
216 <div class="flex grow flex-col gap-y-5 overflow-y-auto bg-white px-6 pb-4 shadow-lg">
217 <div class="flex h-16 shrink-0 items-center">
218 <img class="h-8 w-auto" src="/images/logo.svg" alt="Mercury UPI PSP" />
219 </div>
220
:-(
<.sidebar_content current_user={@current_user} menu_items={@menu_items} />
221 </div>
222 </div>
223 """
224 end
225
226 attr :current_user, :map, required: true
227 attr :menu_items, :list, default: []
228
229 def sidebar_content(assigns) do
230
:-(
~H"""
231 <!-- Navigation -->
232 <nav class="flex flex-1 flex-col">
233 <ul role="list" class="flex flex-1 flex-col gap-y-7">
234 <li>
235 <ul role="list" class="-mx-2 space-y-1">
236
:-(
<%= for menu_item <- @menu_items do %>
237
:-(
<.nav_item
238
:-(
href={menu_item.path}
239
:-(
icon={menu_item.icon}
240 active={false}
241 >
242
:-(
<%= menu_item.label %>
243 </.nav_item>
244 <% end %>
245 </ul>
246 </li>
247
248 <!-- User info at bottom -->
249 <li class="-mx-6 mt-auto">
250 <div class="flex items-center gap-x-4 px-6 py-3 text-sm font-semibold leading-6 text-gray-900 hover:bg-gray-50">
251 <div class="h-8 w-8 rounded-full bg-gray-50 flex items-center justify-center">
252 <span class="text-sm font-medium text-gray-600">
253
:-(
<%= String.first(@current_user.name || "U") %>
254 </span>
255 </div>
256 <span class="sr-only">Your profile</span>
257
:-(
<span aria-hidden="true"><%= @current_user.name || "User" %></span>
258 </div>
259 </li>
260 </ul>
261 </nav>
262 """
263 end
264
265 attr :current_user, :map, required: true
266 attr :page_title, :string, default: "Dashboard"
267
268 def navbar(assigns) do
269
:-(
~H"""
270 <div class="sticky top-0 z-40 flex h-16 shrink-0 items-center gap-x-4 border-b border-gray-200 bg-white px-4 shadow-sm sm:gap-x-6 sm:px-6 lg:px-8">
271 <button type="button" class="-m-2.5 p-2.5 text-gray-700 lg:hidden">
272 <span class="sr-only">Open sidebar</span>
273 <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
274 <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
275 </svg>
276 </button>
277
278 <!-- Separator -->
279 <div class="h-6 w-px bg-gray-900/10 lg:hidden" aria-hidden="true"></div>
280
281 <div class="flex flex-1 gap-x-4 self-stretch lg:gap-x-6">
282 <div class="flex items-center gap-x-4 lg:gap-x-6">
283
:-(
<h1 class="text-xl font-semibold leading-7 text-gray-900"><%= @page_title %></h1>
284 </div>
285
286 <div class="ml-auto flex items-center gap-x-4 lg:gap-x-6">
287 <!-- Notifications -->
288 <button type="button" class="-m-2.5 p-2.5 text-gray-400 hover:text-gray-500">
289 <span class="sr-only">View notifications</span>
290 <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
291 <path stroke-linecap="round" stroke-linejoin="round" d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0" />
292 </svg>
293 </button>
294
295 <!-- Separator -->
296 <div class="hidden lg:block lg:h-6 lg:w-px lg:bg-gray-900/10" aria-hidden="true"></div>
297
298 <!-- Profile dropdown -->
299 <div class="relative">
300 <button type="button" class="-m-1.5 flex items-center p-1.5" id="user-menu-button" aria-expanded="false" aria-haspopup="true">
301 <span class="sr-only">Open user menu</span>
302 <div class="h-8 w-8 rounded-full bg-gray-50 flex items-center justify-center">
303 <span class="text-sm font-medium text-gray-600">
304
:-(
<%= String.first(@current_user.name || "U") %>
305 </span>
306 </div>
307 <span class="hidden lg:flex lg:items-center">
308 <span class="ml-4 text-sm font-semibold leading-6 text-gray-900" aria-hidden="true">
309
:-(
<%= @current_user.name || "User" %>
310 </span>
311 <svg class="ml-2 h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
312 <path fill-rule="evenodd" d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z" clip-rule="evenodd" />
313 </svg>
314 </span>
315 </button>
316 </div>
317 </div>
318 </div>
319 </div>
320 """
321 end
322
323 attr :href, :string, required: true
324 attr :icon, :string, required: true
325 attr :active, :boolean, default: false
326 slot :inner_block, required: true
327
328 def nav_item(assigns) do
329
:-(
~H"""
330 <li>
331
:-(
<a href={@href} class={[
332 "group flex gap-x-3 rounded-md p-2 text-sm leading-6 font-semibold",
333
:-(
if(@active, do: "bg-gray-50 text-blue-600", else: "text-gray-700 hover:text-blue-600 hover:bg-gray-50")
334 ]}>
335
:-(
<.hero_icon name={@icon} class={[
336 "h-6 w-6 shrink-0",
337
:-(
if(@active, do: "text-blue-600", else: "text-gray-400 group-hover:text-blue-600")
338 ]} />
339
:-(
<%= render_slot(@inner_block) %>
340 </a>
341 </li>
342 """
343 end
344
345 attr :name, :string, required: true
346 attr :class, :any, default: ""
347
348 def hero_icon(assigns) do
349
:-(
~H"""
350
:-(
<svg class={@class} fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
351
:-(
<%= case @name do %>
352 <% "home" -> %>
353 <path stroke-linecap="round" stroke-linejoin="round" d="m2.25 12 8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
354 <% "users" -> %>
355 <path stroke-linecap="round" stroke-linejoin="round" d="M15 19.128a9.38 9.38 0 0 0 2.625.372 9.337 9.337 0 0 0 4.121-.952 4.125 4.125 0 0 0-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 0 1 8.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0 1 11.964-3.07M12 6.375a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0Zm8.25 2.25a2.625 2.625 0 1 1-5.25 0 2.625 2.625 0 0 1 5.25 0Z" />
356 <% "building-office" -> %>
357 <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 21h16.5M4.5 3h15l-.75 18h-13.5L4.5 3ZM12.75 8.25h.008v.008h-.008V8.25Zm0 3h.008v.008h-.008v-.008Zm0 3h.008v.008h-.008V14.25Zm-3.75-6h.008v.008H9V8.25Zm0 3h.008v.008H9v-.008Zm0 3h.008v.008H9V14.25Zm7.5-6h.008v.008h-.008V8.25Zm0 3h.008v.008h-.008v-.008Zm0 3h.008v.008h-.008V14.25Z" />
358 <% "credit-card" -> %>
359 <path stroke-linecap="round" stroke-linejoin="round" d="M2.25 8.25h19.5M2.25 9h19.5m-16.5 5.25h6m-6 2.25h3m-3.75 3h15a2.25 2.25 0 0 0 2.25-2.25V6.75A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25v10.5A2.25 2.25 0 0 0 4.5 19.5Z" />
360 <% "chart-bar" -> %>
361 <path stroke-linecap="round" stroke-linejoin="round" d="M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 0 1 3 19.875v-6.75ZM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V8.625ZM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V4.125Z" />
362 <% "cog-6-tooth" -> %>
363 <path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a6.759 6.759 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z" />
364 <path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
365 <% _ -> %>
366 <path stroke-linecap="round" stroke-linejoin="round" d="M9 12h3.75M9 15h3.75M9 18h3.75m3-12h.75M15 6h.75M18 6h.75" />
367 <% end %>
368 </svg>
369 """
370 end
371 end
Line Hits Source