| 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">→</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 |