Update own parking space

This commit is contained in:
Adrian Woźniak 2023-09-14 14:25:29 +02:00
parent a5954e6966
commit 6a3378a6b5
4 changed files with 266 additions and 19 deletions

View File

@ -809,6 +809,10 @@ select {
margin-right: 0.25rem; margin-right: 0.25rem;
} }
.mr-2 {
margin-right: 0.5rem;
}
.mr-3 { .mr-3 {
margin-right: 0.75rem; margin-right: 0.75rem;
} }
@ -992,6 +996,10 @@ select {
border-radius: 0.5rem; border-radius: 0.5rem;
} }
.rounded-sm {
border-radius: 0.125rem;
}
.border { .border {
border-width: 1px; border-width: 1px;
} }
@ -1010,6 +1018,11 @@ select {
background-color: rgb(0 0 0 / var(--tw-bg-opacity)); background-color: rgb(0 0 0 / var(--tw-bg-opacity));
} }
.bg-blue-100 {
--tw-bg-opacity: 1;
background-color: rgb(219 234 254 / var(--tw-bg-opacity));
}
.bg-blue-700 { .bg-blue-700 {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(29 78 216 / var(--tw-bg-opacity)); background-color: rgb(29 78 216 / var(--tw-bg-opacity));
@ -1035,6 +1048,11 @@ select {
background-color: rgb(254 226 226 / var(--tw-bg-opacity)); background-color: rgb(254 226 226 / var(--tw-bg-opacity));
} }
.bg-red-700 {
--tw-bg-opacity: 1;
background-color: rgb(185 28 28 / var(--tw-bg-opacity));
}
.bg-transparent { .bg-transparent {
background-color: transparent; background-color: transparent;
} }
@ -1044,6 +1062,11 @@ select {
background-color: rgb(255 255 255 / var(--tw-bg-opacity)); background-color: rgb(255 255 255 / var(--tw-bg-opacity));
} }
.bg-yellow-100 {
--tw-bg-opacity: 1;
background-color: rgb(254 249 195 / var(--tw-bg-opacity));
}
.bg-cover { .bg-cover {
background-size: cover; background-size: cover;
} }
@ -1052,6 +1075,10 @@ select {
background-position: center; background-position: center;
} }
.p-1 {
padding: 0.25rem;
}
.p-2 { .p-2 {
padding: 0.5rem; padding: 0.5rem;
} }
@ -1074,16 +1101,31 @@ select {
padding-right: 1rem; padding-right: 1rem;
} }
.px-5 {
padding-left: 1.25rem;
padding-right: 1.25rem;
}
.px-6 { .px-6 {
padding-left: 1.5rem; padding-left: 1.5rem;
padding-right: 1.5rem; padding-right: 1.5rem;
} }
.py-1 {
padding-top: 0.25rem;
padding-bottom: 0.25rem;
}
.py-2 { .py-2 {
padding-top: 0.5rem; padding-top: 0.5rem;
padding-bottom: 0.5rem; padding-bottom: 0.5rem;
} }
.py-2\.5 {
padding-top: 0.625rem;
padding-bottom: 0.625rem;
}
.py-3 { .py-3 {
padding-top: 0.75rem; padding-top: 0.75rem;
padding-bottom: 0.75rem; padding-bottom: 0.75rem;
@ -1109,6 +1151,10 @@ select {
padding-top: 4rem; padding-top: 4rem;
} }
.text-center {
text-align: center;
}
.text-2xl { .text-2xl {
font-size: 1.5rem; font-size: 1.5rem;
line-height: 2rem; line-height: 2rem;
@ -1154,6 +1200,16 @@ select {
line-height: 1; line-height: 1;
} }
.text-blue-400 {
--tw-text-opacity: 1;
color: rgb(96 165 250 / var(--tw-text-opacity));
}
.text-blue-800 {
--tw-text-opacity: 1;
color: rgb(30 64 175 / var(--tw-text-opacity));
}
.text-emerald-500 { .text-emerald-500 {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(16 185 129 / var(--tw-text-opacity)); color: rgb(16 185 129 / var(--tw-text-opacity));
@ -1179,24 +1235,24 @@ select {
color: rgb(17 24 39 / var(--tw-text-opacity)); color: rgb(17 24 39 / var(--tw-text-opacity));
} }
.text-green-900 {
--tw-text-opacity: 1;
color: rgb(20 83 45 / var(--tw-text-opacity));
}
.text-neutral-500 { .text-neutral-500 {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(115 115 115 / var(--tw-text-opacity)); color: rgb(115 115 115 / var(--tw-text-opacity));
} }
.text-red-400 {
--tw-text-opacity: 1;
color: rgb(248 113 113 / var(--tw-text-opacity));
}
.text-red-700 { .text-red-700 {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(185 28 28 / var(--tw-text-opacity)); color: rgb(185 28 28 / var(--tw-text-opacity));
} }
.text-red-900 { .text-red-800 {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(127 29 29 / var(--tw-text-opacity)); color: rgb(153 27 27 / var(--tw-text-opacity));
} }
.text-white { .text-white {
@ -1204,9 +1260,14 @@ select {
color: rgb(255 255 255 / var(--tw-text-opacity)); color: rgb(255 255 255 / var(--tw-text-opacity));
} }
.text-yellow-900 { .text-yellow-400 {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(113 63 18 / var(--tw-text-opacity)); color: rgb(250 204 21 / var(--tw-text-opacity));
}
.text-yellow-800 {
--tw-text-opacity: 1;
color: rgb(133 77 14 / var(--tw-text-opacity));
} }
.opacity-75 { .opacity-75 {
@ -1256,16 +1317,56 @@ select {
transition-timing-function: linear; transition-timing-function: linear;
} }
.hover\:bg-blue-200:hover {
--tw-bg-opacity: 1;
background-color: rgb(191 219 254 / var(--tw-bg-opacity));
}
.hover\:bg-blue-800:hover {
--tw-bg-opacity: 1;
background-color: rgb(30 64 175 / var(--tw-bg-opacity));
}
.hover\:bg-gray-100:hover { .hover\:bg-gray-100:hover {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(243 244 246 / var(--tw-bg-opacity)); background-color: rgb(243 244 246 / var(--tw-bg-opacity));
} }
.hover\:bg-red-200:hover {
--tw-bg-opacity: 1;
background-color: rgb(254 202 202 / var(--tw-bg-opacity));
}
.hover\:bg-red-800:hover {
--tw-bg-opacity: 1;
background-color: rgb(153 27 27 / var(--tw-bg-opacity));
}
.hover\:bg-yellow-200:hover {
--tw-bg-opacity: 1;
background-color: rgb(254 240 138 / var(--tw-bg-opacity));
}
.hover\:text-blue-900:hover {
--tw-text-opacity: 1;
color: rgb(30 58 138 / var(--tw-text-opacity));
}
.hover\:text-neutral-700:hover { .hover\:text-neutral-700:hover {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(64 64 64 / var(--tw-text-opacity)); color: rgb(64 64 64 / var(--tw-text-opacity));
} }
.hover\:text-red-900:hover {
--tw-text-opacity: 1;
color: rgb(127 29 29 / var(--tw-text-opacity));
}
.hover\:text-yellow-900:hover {
--tw-text-opacity: 1;
color: rgb(113 63 18 / var(--tw-text-opacity));
}
.hover\:ease-in-out:hover { .hover\:ease-in-out:hover {
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
} }
@ -1297,6 +1398,11 @@ select {
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
} }
.focus\:ring-blue-300:focus {
--tw-ring-opacity: 1;
--tw-ring-color: rgb(147 197 253 / var(--tw-ring-opacity));
}
.focus\:ring-blue-500:focus { .focus\:ring-blue-500:focus {
--tw-ring-opacity: 1; --tw-ring-opacity: 1;
--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity)); --tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity));
@ -1317,6 +1423,11 @@ select {
--tw-ring-color: rgb(209 213 219 / var(--tw-ring-opacity)); --tw-ring-color: rgb(209 213 219 / var(--tw-ring-opacity));
} }
.focus\:ring-red-300:focus {
--tw-ring-opacity: 1;
--tw-ring-color: rgb(252 165 165 / var(--tw-ring-opacity));
}
.focus\:ring-offset-2:focus { .focus\:ring-offset-2:focus {
--tw-ring-offset-width: 2px; --tw-ring-offset-width: 2px;
} }
@ -1347,16 +1458,46 @@ select {
border-color: rgb(55 65 81 / var(--tw-border-opacity)); border-color: rgb(55 65 81 / var(--tw-border-opacity));
} }
.dark\:bg-blue-600 {
--tw-bg-opacity: 1;
background-color: rgb(37 99 235 / var(--tw-bg-opacity));
}
.dark\:bg-blue-900 {
--tw-bg-opacity: 1;
background-color: rgb(30 58 138 / var(--tw-bg-opacity));
}
.dark\:bg-gray-700 { .dark\:bg-gray-700 {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(55 65 81 / var(--tw-bg-opacity)); background-color: rgb(55 65 81 / var(--tw-bg-opacity));
} }
.dark\:bg-red-600 {
--tw-bg-opacity: 1;
background-color: rgb(220 38 38 / var(--tw-bg-opacity));
}
.dark\:bg-red-900 {
--tw-bg-opacity: 1;
background-color: rgb(127 29 29 / var(--tw-bg-opacity));
}
.dark\:bg-yellow-900 {
--tw-bg-opacity: 1;
background-color: rgb(113 63 18 / var(--tw-bg-opacity));
}
.dark\:text-black { .dark\:text-black {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(0 0 0 / var(--tw-text-opacity)); color: rgb(0 0 0 / var(--tw-text-opacity));
} }
.dark\:text-blue-300 {
--tw-text-opacity: 1;
color: rgb(147 197 253 / var(--tw-text-opacity));
}
.dark\:text-gray-200 { .dark\:text-gray-200 {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(229 231 235 / var(--tw-text-opacity)); color: rgb(229 231 235 / var(--tw-text-opacity));
@ -1372,11 +1513,21 @@ select {
color: rgb(229 229 229 / var(--tw-text-opacity)); color: rgb(229 229 229 / var(--tw-text-opacity));
} }
.dark\:text-red-300 {
--tw-text-opacity: 1;
color: rgb(252 165 165 / var(--tw-text-opacity));
}
.dark\:text-white { .dark\:text-white {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity)); color: rgb(255 255 255 / var(--tw-text-opacity));
} }
.dark\:text-yellow-300 {
--tw-text-opacity: 1;
color: rgb(253 224 71 / var(--tw-text-opacity));
}
.dark\:placeholder-gray-400::-moz-placeholder { .dark\:placeholder-gray-400::-moz-placeholder {
--tw-placeholder-opacity: 1; --tw-placeholder-opacity: 1;
color: rgb(156 163 175 / var(--tw-placeholder-opacity)); color: rgb(156 163 175 / var(--tw-placeholder-opacity));
@ -1387,6 +1538,16 @@ select {
color: rgb(156 163 175 / var(--tw-placeholder-opacity)); color: rgb(156 163 175 / var(--tw-placeholder-opacity));
} }
.dark\:hover\:bg-blue-700:hover {
--tw-bg-opacity: 1;
background-color: rgb(29 78 216 / var(--tw-bg-opacity));
}
.dark\:hover\:bg-blue-800:hover {
--tw-bg-opacity: 1;
background-color: rgb(30 64 175 / var(--tw-bg-opacity));
}
.dark\:hover\:bg-gray-600:hover { .dark\:hover\:bg-gray-600:hover {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(75 85 99 / var(--tw-bg-opacity)); background-color: rgb(75 85 99 / var(--tw-bg-opacity));
@ -1397,16 +1558,46 @@ select {
background-color: rgb(55 65 81 / var(--tw-bg-opacity)); background-color: rgb(55 65 81 / var(--tw-bg-opacity));
} }
.dark\:hover\:bg-red-700:hover {
--tw-bg-opacity: 1;
background-color: rgb(185 28 28 / var(--tw-bg-opacity));
}
.dark\:hover\:bg-red-800:hover {
--tw-bg-opacity: 1;
background-color: rgb(153 27 27 / var(--tw-bg-opacity));
}
.dark\:hover\:bg-yellow-800:hover {
--tw-bg-opacity: 1;
background-color: rgb(133 77 14 / var(--tw-bg-opacity));
}
.dark\:hover\:text-blue-300:hover {
--tw-text-opacity: 1;
color: rgb(147 197 253 / var(--tw-text-opacity));
}
.dark\:hover\:text-neutral-400:hover { .dark\:hover\:text-neutral-400:hover {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(163 163 163 / var(--tw-text-opacity)); color: rgb(163 163 163 / var(--tw-text-opacity));
} }
.dark\:hover\:text-red-300:hover {
--tw-text-opacity: 1;
color: rgb(252 165 165 / var(--tw-text-opacity));
}
.dark\:hover\:text-white:hover { .dark\:hover\:text-white:hover {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity)); color: rgb(255 255 255 / var(--tw-text-opacity));
} }
.dark\:hover\:text-yellow-300:hover {
--tw-text-opacity: 1;
color: rgb(253 224 71 / var(--tw-text-opacity));
}
.dark\:focus\:border-blue-500:focus { .dark\:focus\:border-blue-500:focus {
--tw-border-opacity: 1; --tw-border-opacity: 1;
border-color: rgb(59 130 246 / var(--tw-border-opacity)); border-color: rgb(59 130 246 / var(--tw-border-opacity));
@ -1422,10 +1613,20 @@ select {
--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity)); --tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity));
} }
.dark\:focus\:ring-blue-800:focus {
--tw-ring-opacity: 1;
--tw-ring-color: rgb(30 64 175 / var(--tw-ring-opacity));
}
.dark\:focus\:ring-gray-600:focus { .dark\:focus\:ring-gray-600:focus {
--tw-ring-opacity: 1; --tw-ring-opacity: 1;
--tw-ring-color: rgb(75 85 99 / var(--tw-ring-opacity)); --tw-ring-color: rgb(75 85 99 / var(--tw-ring-opacity));
} }
.dark\:focus\:ring-red-800:focus {
--tw-ring-opacity: 1;
--tw-ring-color: rgb(153 27 27 / var(--tw-ring-opacity));
}
} }
@media (min-width: 640px) { @media (min-width: 640px) {

View File

@ -63,6 +63,7 @@ struct AllPartialParkingSpace {
locations: Vec<Rc<parking_space_locations::Model>>, locations: Vec<Rc<parking_space_locations::Model>>,
location_by_id: BTreeMap<i32, Rc<parking_space_locations::Model>>, location_by_id: BTreeMap<i32, Rc<parking_space_locations::Model>>,
session: Option<SessionOpts>, session: Option<SessionOpts>,
account_id: Option<i32>,
} }
#[autometrics] #[autometrics]
@ -167,6 +168,7 @@ async fn load_parking_spaces(
}); });
AllPartialParkingSpace { AllPartialParkingSpace {
account_id,
account_by_id, account_by_id,
parking_space_rents: rents, parking_space_rents: rents,
parking_spaces, parking_spaces,
@ -420,12 +422,17 @@ async fn update(
location_id: Set(Some(location_id as i32)), location_id: Set(Some(location_id as i32)),
spot: Set(spot.map(|n| n as i32)), spot: Set(spot.map(|n| n as i32)),
account_id: Set(session.account_id()), account_id: Set(session.account_id()),
state: Set(ParkingSpaceState::Pending),
..Default::default() ..Default::default()
}; };
if let Err(_e) = model.update(&*db).await { if let Err(_e) = model.update(&*db).await {
return HttpResponse::BadRequest().body( return HttpResponse::BadRequest().body(
ParkingSpaceFormPartial { ParkingSpaceFormPartial {
form: CreateParkingSpace { location_id, spot, id: Some(id) }, form: CreateParkingSpace {
location_id,
spot,
id: Some(id),
},
t: t.into_inner(), t: t.into_inner(),
lang, lang,
errors: Default::default(), errors: Default::default(),

View File

@ -59,11 +59,23 @@
{% match parking_space.state %} {% match parking_space.state %}
{% when ParkingSpaceState::Pending %} {% when ParkingSpaceState::Pending %}
<div class="inline-flex items-center text-base font-semibold text-yellow-900 dark:text-yellow">Pending</div> <div class="inline-flex items-center px-2 py-1 mr-2 text-sm font-medium text-yellow-800 bg-yellow-100 rounded dark:bg-yellow-900 dark:text-yellow-300">
<div class="inline-flex items-center p-1 text-sm text-yellow-400 bg-transparent rounded-sm hover:bg-yellow-200 hover:text-yellow-900 dark:hover:bg-yellow-800 dark:hover:text-yellow-300">
Pending
</div>
</div>
{% when ParkingSpaceState::Verified %} {% when ParkingSpaceState::Verified %}
<div class="inline-flex items-center text-base font-semibold text-green-900 dark:text-green">Accepted</div> <div class="inline-flex items-center px-2 py-1 mr-2 text-sm font-medium text-blue-800 bg-blue-100 rounded dark:bg-blue-900 dark:text-blue-300">
<div class="inline-flex items-center p-1 text-sm text-blue-400 bg-transparent rounded-sm hover:bg-blue-200 hover:text-blue-900 dark:hover:bg-blue-800 dark:hover:text-blue-300">
Accepted
</div>
</div>
{% when ParkingSpaceState::Banned %} {% when ParkingSpaceState::Banned %}
<div class="inline-flex items-center text-base font-semibold text-red-900 dark:text-red">Rejected</div> <div class="inline-flex items-center px-2 py-1 mr-2 text-sm font-medium text-red-800 bg-red-100 rounded dark:bg-red-900 dark:text-red-300">
<div class="inline-flex items-center p-1 text-sm text-red-400 bg-transparent rounded-sm hover:bg-red-200 hover:text-red-900 dark:hover:bg-red-800 dark:hover:text-red-300">
Rejected
</div>
</div>
{% endmatch %} {% endmatch %}
<div> <div>
<a <a
@ -71,9 +83,19 @@
hx-target="main" hx-target="main"
hx-get="/parking-spaces/edit/{{parking_space.id}}" hx-get="/parking-spaces/edit/{{parking_space.id}}"
hx-headers='{"Accept":"text/html-partial"}' hx-headers='{"Accept":"text/html-partial"}'
class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
> >
Edit Edit
</a> </a>
<a
href="/parking-spaces/delete/{{parking_space.id}}"
hx-target="main"
hx-delete="/parking-spaces/edit/{{parking_space.id}}"
hx-headers='{"Accept":"text/html-partial"}'
class="text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-800"
>
Delete
</a>
</div> </div>
</div> </div>
@ -92,6 +114,7 @@
<oswilno-parking-space-rents> <oswilno-parking-space-rents>
{% for parking_space_rent in parking_space_rents -%} {% for parking_space_rent in parking_space_rents -%}
{% if let Some(parking_space) = parking_space_by_id.get(parking_space_rent.parking_space_id) %} {% if let Some(parking_space) = parking_space_by_id.get(parking_space_rent.parking_space_id) %}
{% if parking_space.state == ParkingSpaceState::Verified || Some(parking_space.account_id.clone()) == account_id %}
{% if let Some(account) = account_by_id.get(parking_space.account_id) %} {% if let Some(account) = account_by_id.get(parking_space.account_id) %}
<oswilno-parking-space-rent id="parking-space-rent-{{ parking_space_rent.id }}"> <oswilno-parking-space-rent id="parking-space-rent-{{ parking_space_rent.id }}">
<oswilno-parking-space id="parking-space-{{ parking_space.id }}"> <oswilno-parking-space id="parking-space-{{ parking_space.id }}">
@ -105,6 +128,7 @@
</oswilno-parking-space-rent> </oswilno-parking-space-rent>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% endif %}
{% endfor -%} {% endfor -%}
</oswilno-parking-space-rents> </oswilno-parking-space-rents>
</section> </section>

View File

@ -4,12 +4,17 @@
{% for error in errors.global() %} {% for error in errors.global() %}
<oswilno-error>{{error|t(lang,t)}}</oswilno-error> <oswilno-error>{{error|t(lang,t)}}</oswilno-error>
{% endfor %} {% endfor %}
<form hx-post="/parking-spaces/create" hx-target="main"> <form
{% match form.id %} {% if let Some(id) = form.id %}
{% when Some with (id) %} hx-post="/parking-spaces/update"
{% else %}
hx-post="/parking-spaces/create"
{% endif %}
hx-target="main"
>
{% if let Some(id) = form.id %}
<input type="hidden" name="id" value="{{id}}" /> <input type="hidden" name="id" value="{{id}}" />
{% when _ %} {% endif %}
{% endmatch %}
<div class="mb-4"> <div class="mb-4">
<label <label
for="location_id" for="location_id"
@ -24,7 +29,10 @@
class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500" class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
> >
{% for loc in locations %} {% for loc in locations %}
<option value="{{loc.id}}"> <option
value="{{loc.id}}"
{% if form.location_id == loc.id %} selected {% endif %}
>
{{ loc.name }} {{ loc.name }}
{{ loc.number }} {{ loc.number }}
Etap {{ loc.stage }} Etap {{ loc.stage }}
@ -48,12 +56,19 @@
class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500" class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
type="number" type="number"
required required
{% if let Some(v) = form.spot %}
value="{{v}}"
{% endif %}
/> />
</div> </div>
<div class="mb-6"> <div class="mb-6">
<input <input
type="submit" type="submit"
{% if let Some(id) = form.id %}
value="{{"Update parking space"|t(lang,t)}}"
{% else %}
value="{{"Register parking space"|t(lang,t)}}" value="{{"Register parking space"|t(lang,t)}}"
{% endif %}
class="w-64 bg-cyan-600 text-white py-2 rounded-lg mx-auto block focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-cyan-500 mb-2" class="w-64 bg-cyan-600 text-white py-2 rounded-lg mx-auto block focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-cyan-500 mb-2"
/> />
</div> </div>