fix: align accessible UI and browser coverage
This commit is contained in:
parent
3224d7d697
commit
1db67b1048
|
|
@ -63,12 +63,16 @@ test("activity approval, privacy controls, reporting, and moderation work end to
|
|||
.getByLabel("Plan and expectations")
|
||||
.fill("Meet at a public café for an hour of safe conversation.");
|
||||
|
||||
const startsAt = new Date(Date.now() + 4 * 60 * 60 * 1000).toISOString().slice(0, 16);
|
||||
const joinDeadline = new Date(Date.now() + 2 * 60 * 60 * 1000)
|
||||
.toISOString()
|
||||
.slice(0, 16);
|
||||
await organizer.page.getByLabel("Starts at (UTC)").fill(startsAt);
|
||||
await organizer.page.getByLabel("Join requests close (UTC)").fill(joinDeadline);
|
||||
const startsAt = new Date(Date.now() + 4 * 60 * 60 * 1000).toISOString();
|
||||
const joinDeadline = new Date(Date.now() + 2 * 60 * 60 * 1000).toISOString();
|
||||
const startsAtPicker = organizer.page.getByRole("group", { name: "Starts at" });
|
||||
const joinDeadlinePicker = organizer.page.getByRole("group", {
|
||||
name: "Join requests close",
|
||||
});
|
||||
await startsAtPicker.getByLabel("Date").fill(startsAt.slice(0, 10));
|
||||
await startsAtPicker.getByLabel("Time").fill(startsAt.slice(11, 16));
|
||||
await joinDeadlinePicker.getByLabel("Date").fill(joinDeadline.slice(0, 10));
|
||||
await joinDeadlinePicker.getByLabel("Time").fill(joinDeadline.slice(11, 16));
|
||||
await organizer.page.getByLabel("Total group capacity").fill("3");
|
||||
await organizer.page
|
||||
.getByLabel("Approximate public meeting area")
|
||||
|
|
@ -173,9 +177,13 @@ test("activity approval, privacy controls, reporting, and moderation work end to
|
|||
await expect(socialSection.getByText("No social links added.")).toBeVisible();
|
||||
|
||||
await gotoLiveView(participant.page, "/requests/new");
|
||||
await expect(participant.page.getByLabel("Location visibility")).toHaveValue(
|
||||
"exact_for_active_match",
|
||||
);
|
||||
const requestVisibility = participant.page.getByRole("group", {
|
||||
name: "What should people see?",
|
||||
});
|
||||
await expect(
|
||||
requestVisibility.getByRole("radio", { name: /Approximate area/ }),
|
||||
).toBeChecked();
|
||||
await expect(participant.page.getByText("After a helper accepts")).toBeVisible();
|
||||
|
||||
const adminEmail = process.env.E2E_ADMIN_EMAIL ?? "e2e-admin@example.invalid";
|
||||
const admin =
|
||||
|
|
|
|||
|
|
@ -12,14 +12,18 @@ test("public language selection persists across pages", async ({ browser }) => {
|
|||
|
||||
await page.goto("/?locale=ru");
|
||||
await expect(page.locator("html")).toHaveAttribute("lang", "ru");
|
||||
await expect(page.getByRole("heading", { name: "Помощь может быть ближе, чем кажется." })).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "Нужна помощь рядом? Обратитесь к сообществу." }),
|
||||
).toBeVisible();
|
||||
|
||||
await page.goto("/safety");
|
||||
await expect(page.getByRole("heading", { name: "Правила безопасности" })).toBeVisible();
|
||||
|
||||
await page.goto("/?locale=uk");
|
||||
await expect(page.locator("html")).toHaveAttribute("lang", "uk");
|
||||
await expect(page.getByRole("heading", { name: "Допомога може бути ближче, ніж здається." })).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "Потрібна допомога поруч? Зверніться до спільноти." }),
|
||||
).toBeVisible();
|
||||
|
||||
await context.close();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ test("public safety pages load and private discovery requires authentication", a
|
|||
|
||||
await page.goto("/");
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "Help can be closer than you think." }),
|
||||
page.getByRole("heading", { name: "Need help nearby? Ask the community." }),
|
||||
).toBeVisible();
|
||||
await expect(page.getByText("Not an emergency or medical service.")).toBeVisible();
|
||||
|
||||
await page.getByRole("link", { name: "Safety rules" }).click();
|
||||
await page.getByRole("link", { name: "Safety rules", exact: true }).click();
|
||||
await expect(page.getByRole("heading", { name: "Safety rules" })).toBeVisible();
|
||||
await expect(page.getByRole("heading", { name: "Medicine requests" })).toBeVisible();
|
||||
|
||||
|
|
|
|||
|
|
@ -399,8 +399,8 @@ defmodule WhoNeedHelpWeb.CoreComponents do
|
|||
|> assign(:errors, Enum.map(errors, &translate_error(&1)))
|
||||
|
||||
~H"""
|
||||
<div class="fieldset mb-2">
|
||||
<span class="label mb-1">{@label}</span>
|
||||
<fieldset class="fieldset mb-2">
|
||||
<legend class="label mb-1">{@label}</legend>
|
||||
<div
|
||||
id={"#{@id}-picker"}
|
||||
phx-hook="DateTimePicker"
|
||||
|
|
@ -457,12 +457,12 @@ defmodule WhoNeedHelpWeb.CoreComponents do
|
|||
{label}
|
||||
</button>
|
||||
</div>
|
||||
<p class="mt-2 text-xs text-base-content/55">
|
||||
<p class="mt-2 text-xs text-base-content/65">
|
||||
{gettext("Your time zone:")} <span data-datetime-timezone></span>
|
||||
</p>
|
||||
</div>
|
||||
<.error :for={msg <- @errors}>{msg}</.error>
|
||||
</div>
|
||||
</fieldset>
|
||||
"""
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
]}>
|
||||
{status_label(notice.status)}
|
||||
</span>
|
||||
<span class="text-xs text-base-content/55">{category_label(notice.category)}</span>
|
||||
<span class="text-xs text-base-content/65">{category_label(notice.category)}</span>
|
||||
</div>
|
||||
<p class="mt-3 line-clamp-2 text-sm text-base-content/70">{notice.explanation}</p>
|
||||
<time class="mt-2 block text-xs text-base-content/50">{notice.inserted_at}</time>
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@
|
|||
value={@contact_email || @form[:contact_email].value}
|
||||
readonly={not is_nil(@contact_email)}
|
||||
/>
|
||||
<p :if={@contact_email} class="text-xs text-base-content/55">
|
||||
<p :if={@contact_email} class="text-xs text-base-content/65">
|
||||
{gettext("Status updates go to the email address of your signed-in account.")}
|
||||
</p>
|
||||
<.input
|
||||
|
|
@ -99,7 +99,7 @@
|
|||
)
|
||||
}
|
||||
/>
|
||||
<p class="text-xs text-base-content/55">
|
||||
<p class="text-xs text-base-content/65">
|
||||
{gettext(
|
||||
"Identity and contact fields may be omitted for reports of sexual material involving a minor. Do not include or upload the material itself."
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
</span>
|
||||
</div>
|
||||
<h1 class="mt-4 text-3xl font-black">{gettext("Content-removal notice")}</h1>
|
||||
<p class="mt-2 text-sm text-base-content/55">{category_label(@notice.category)}</p>
|
||||
<p class="mt-2 text-sm text-base-content/65">{category_label(@notice.category)}</p>
|
||||
<section class="mt-6 rounded-2xl bg-base-200 p-5">
|
||||
<h2 class="font-bold">{gettext("Reported locations")}</h2>
|
||||
<ul class="mt-3 list-disc space-y-1 pl-5 text-sm">
|
||||
|
|
@ -37,7 +37,7 @@
|
|||
<h2 class="font-bold">{gettext("Decision or request for information")}</h2>
|
||||
<p class="mt-2 whitespace-pre-wrap">{@notice.resolution_note}</p>
|
||||
</section>
|
||||
<p class="mt-5 text-xs text-base-content/55">
|
||||
<p class="mt-5 text-xs text-base-content/65">
|
||||
{gettext(
|
||||
"Keep this status link private. The submitter identity is not shown publicly or automatically disclosed to the person who posted the reported content."
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@
|
|||
readonly={not is_nil(@contact_email)}
|
||||
required
|
||||
/>
|
||||
<p :if={@contact_email} class="text-xs text-base-content/55">
|
||||
<p :if={@contact_email} class="text-xs text-base-content/65">
|
||||
{gettext("Status updates go to the email address of your signed-in account.")}
|
||||
</p>
|
||||
<.input
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@
|
|||
<div class="mt-7 flex items-center justify-between gap-4 border-t border-base-300 pt-5">
|
||||
<div>
|
||||
<div class="text-sm font-semibold">{gettext("Approximate area")}</div>
|
||||
<div class="text-xs text-base-content/55">
|
||||
<div class="text-xs text-base-content/65">
|
||||
{gettext("Exact address after matching")}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
readonly={not is_nil(@contact_email)}
|
||||
required
|
||||
/>
|
||||
<p :if={@contact_email} class="text-xs text-base-content/55">
|
||||
<p :if={@contact_email} class="text-xs text-base-content/65">
|
||||
{gettext("This request is linked to your signed-in account and its email address.")}
|
||||
</p>
|
||||
<.input
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
<div class="flex flex-wrap items-center gap-2">
|
||||
<span class="badge badge-outline">{request.reference}</span>
|
||||
<span class="badge badge-primary">{status_label(request.status)}</span>
|
||||
<span class="text-xs text-base-content/55">{kind_label(request.kind)}</span>
|
||||
<span class="text-xs text-base-content/65">{kind_label(request.kind)}</span>
|
||||
</div>
|
||||
<h2 class="mt-3 font-bold">{request.subject}</h2>
|
||||
<time class="mt-1 block text-xs text-base-content/50">{request.inserted_at}</time>
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
readonly={not is_nil(@contact_email)}
|
||||
required
|
||||
/>
|
||||
<p :if={@contact_email} class="text-xs text-base-content/55">
|
||||
<p :if={@contact_email} class="text-xs text-base-content/65">
|
||||
{gettext(
|
||||
"This request is linked to your signed-in account. Replies go to its email address."
|
||||
)}
|
||||
|
|
@ -60,7 +60,7 @@
|
|||
label={gettext("Describe the problem")}
|
||||
required
|
||||
/>
|
||||
<p class="text-xs text-base-content/55">
|
||||
<p class="text-xs text-base-content/65">
|
||||
{gettext(
|
||||
"Do not include passwords, payment card details, access codes, prescriptions, or unnecessary medical information."
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<span class="badge badge-primary">{status_label(@request.status)}</span>
|
||||
</div>
|
||||
<h1 class="mt-4 text-3xl font-black">{@request.subject}</h1>
|
||||
<p class="mt-2 text-sm text-base-content/55">{kind_label(@request.kind)}</p>
|
||||
<p class="mt-2 text-sm text-base-content/65">{kind_label(@request.kind)}</p>
|
||||
<div class="mt-6 rounded-2xl bg-base-200 p-5">
|
||||
<p class="whitespace-pre-wrap">{@request.details}</p>
|
||||
</div>
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
<h2 class="font-bold">{gettext("Support response")}</h2>
|
||||
<p class="mt-2 whitespace-pre-wrap">{@request.resolution_note}</p>
|
||||
</section>
|
||||
<p class="mt-5 text-xs text-base-content/55">
|
||||
<p class="mt-5 text-xs text-base-content/65">
|
||||
{gettext(
|
||||
"Keep this status link private. A copy is sent to the contact email when email delivery is configured."
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@
|
|||
label={gettext("Sign up with Google")}
|
||||
disabled={!@google_auth_enabled}
|
||||
/>
|
||||
<p :if={!@google_auth_enabled} class="text-center text-xs text-base-content/55">
|
||||
<p :if={!@google_auth_enabled} class="text-center text-xs text-base-content/65">
|
||||
{gettext("Google sign-in will be available after the operator configures it.")}
|
||||
</p>
|
||||
</.form>
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@
|
|||
label={gettext("Continue with Google")}
|
||||
disabled={!@google_auth_enabled}
|
||||
/>
|
||||
<p :if={!@google_auth_enabled} class="mt-2 text-center text-xs text-base-content/55">
|
||||
<p :if={!@google_auth_enabled} class="mt-2 text-center text-xs text-base-content/65">
|
||||
{gettext("Google sign-in will be available after the operator configures it.")}
|
||||
</p>
|
||||
</.form>
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@
|
|||
label={gettext("Connect Google account")}
|
||||
disabled={!@google_auth_enabled}
|
||||
/>
|
||||
<p :if={!@google_auth_enabled} class="mt-2 text-center text-xs text-base-content/55">
|
||||
<p :if={!@google_auth_enabled} class="mt-2 text-center text-xs text-base-content/65">
|
||||
{gettext("Google sign-in will be available after the operator configures it.")}
|
||||
</p>
|
||||
</.form>
|
||||
|
|
|
|||
|
|
@ -254,7 +254,7 @@ defmodule WhoNeedHelpWeb.ActivityLive.Index do
|
|||
</div>
|
||||
<.icon name="hero-chevron-right" class="mt-2 size-5 shrink-0" />
|
||||
</div>
|
||||
<div class="mt-5 flex flex-wrap gap-x-5 gap-y-2 border-t border-base-300 pt-4 text-xs text-base-content/55">
|
||||
<div class="mt-5 flex flex-wrap gap-x-5 gap-y-2 border-t border-base-300 pt-4 text-xs text-base-content/65">
|
||||
<span>📍 {activity.location_label}</span>
|
||||
<span>🕒 {Calendar.strftime(activity.starts_at, "%d.%m.%Y, %H:%M UTC")}</span>
|
||||
<span>
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ defmodule WhoNeedHelpWeb.CategoryProposalLive do
|
|||
<div class="mt-5 space-y-3">
|
||||
<div
|
||||
:if={@proposals == []}
|
||||
class="rounded-2xl border border-dashed p-8 text-center text-base-content/55"
|
||||
class="rounded-2xl border border-dashed p-8 text-center text-base-content/65"
|
||||
>
|
||||
{gettext("No proposals yet.")}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ defmodule WhoNeedHelpWeb.LeaderboardLive do
|
|||
</thead>
|
||||
<tbody>
|
||||
<tr :if={@leaders == []}>
|
||||
<td colspan="7" class="py-10 text-center text-base-content/55">
|
||||
<td colspan="7" class="py-10 text-center text-base-content/65">
|
||||
{gettext("No completed help yet.")}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
|||
|
|
@ -434,7 +434,7 @@ defmodule WhoNeedHelpWeb.ModerationLive do
|
|||
<div class="font-semibold">
|
||||
{signal_label(signal.kind)} · {signal.subject.display_name}
|
||||
</div>
|
||||
<p class="mt-1 text-xs text-base-content/55">
|
||||
<p class="mt-1 text-xs text-base-content/65">
|
||||
{gettext(
|
||||
"A signal requests human review; optional tracking absence is not proof of abuse."
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ defmodule WhoNeedHelpWeb.ProfileLive do
|
|||
label={gettext("Optional external thank-you link")}
|
||||
placeholder="https://…"
|
||||
/>
|
||||
<p class="text-xs text-base-content/55">
|
||||
<p class="text-xs text-base-content/65">
|
||||
{gettext("The platform never handles, requires, splits, or guarantees a payment.")}
|
||||
</p>
|
||||
<.button class="btn btn-primary w-full">{gettext("Save profile")}</.button>
|
||||
|
|
@ -208,7 +208,7 @@ defmodule WhoNeedHelpWeb.ProfileLive do
|
|||
</div>
|
||||
<div class="rounded-3xl bg-base-200 p-5">
|
||||
<h3 class="font-bold">{gettext("Visible reviews")}</h3>
|
||||
<p :if={@reviews == []} class="mt-3 text-sm text-base-content/55">
|
||||
<p :if={@reviews == []} class="mt-3 text-sm text-base-content/65">
|
||||
{gettext("None revealed yet.")}
|
||||
</p>
|
||||
<div :for={review <- @reviews} class="mt-3 border-t border-base-300 pt-3 text-sm">
|
||||
|
|
@ -239,7 +239,7 @@ defmodule WhoNeedHelpWeb.ProfileLive do
|
|||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<h2 class="text-xl font-bold">{gettext("Social links")}</h2>
|
||||
<p class="mt-1 text-sm text-base-content/55">
|
||||
<p class="mt-1 text-sm text-base-content/65">
|
||||
{gettext(
|
||||
"Manually added links are public and marked unverified. A verified badge is only granted after completing a provider authorization flow."
|
||||
)}
|
||||
|
|
@ -265,7 +265,7 @@ defmodule WhoNeedHelpWeb.ProfileLive do
|
|||
|
||||
<div class="mt-5 grid gap-4 md:grid-cols-[1fr_.9fr]">
|
||||
<div class="space-y-3">
|
||||
<p :if={@social_identities == []} class="text-sm text-base-content/55">
|
||||
<p :if={@social_identities == []} class="text-sm text-base-content/65">
|
||||
{gettext("No social links added.")}
|
||||
</p>
|
||||
<div
|
||||
|
|
@ -337,7 +337,7 @@ defmodule WhoNeedHelpWeb.ProfileLive do
|
|||
|
||||
<section class="mt-6 rounded-3xl border border-base-300 p-6">
|
||||
<h2 class="text-xl font-bold">{gettext("Blocked users")}</h2>
|
||||
<p :if={@blocks == []} class="mt-3 text-sm text-base-content/55">
|
||||
<p :if={@blocks == []} class="mt-3 text-sm text-base-content/65">
|
||||
{gettext("You have not blocked anyone.")}
|
||||
</p>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -67,18 +67,18 @@ defmodule WhoNeedHelpWeb.PublicProfileLive do
|
|||
<p :if={@user.bio not in [nil, ""]} class="mt-4 whitespace-pre-wrap text-base-content/70">
|
||||
{@user.bio}
|
||||
</p>
|
||||
<p :if={@user.bio in [nil, ""]} class="mt-4 text-sm text-base-content/55">
|
||||
<p :if={@user.bio in [nil, ""]} class="mt-4 text-sm text-base-content/65">
|
||||
{gettext("This person has not added a public bio yet.")}
|
||||
</p>
|
||||
|
||||
<div class="mt-7">
|
||||
<h2 class="text-xl font-bold">{gettext("Public social links")}</h2>
|
||||
<p class="mt-1 text-sm text-base-content/55">
|
||||
<p class="mt-1 text-sm text-base-content/65">
|
||||
{gettext(
|
||||
"A verified badge means the account completed that provider's authorization flow. Manually added links remain unverified."
|
||||
)}
|
||||
</p>
|
||||
<p :if={@user.social_identities == []} class="mt-4 text-sm text-base-content/55">
|
||||
<p :if={@user.social_identities == []} class="mt-4 text-sm text-base-content/65">
|
||||
{gettext("No public social links added.")}
|
||||
</p>
|
||||
<div class="mt-4 flex flex-wrap gap-3">
|
||||
|
|
@ -144,10 +144,10 @@ defmodule WhoNeedHelpWeb.PublicProfileLive do
|
|||
|
||||
<section class="rounded-3xl border border-base-300 p-6">
|
||||
<h2 class="text-xl font-bold">{gettext("Revealed reviews")}</h2>
|
||||
<p class="mt-1 text-sm text-base-content/55">
|
||||
<p class="mt-1 text-sm text-base-content/65">
|
||||
{gettext("A review becomes public only after both people submit for that match.")}
|
||||
</p>
|
||||
<p :if={@reviews == []} class="mt-4 text-sm text-base-content/55">
|
||||
<p :if={@reviews == []} class="mt-4 text-sm text-base-content/65">
|
||||
{gettext("None revealed yet.")}
|
||||
</p>
|
||||
<article :for={review <- @reviews} class="mt-4 border-t border-base-300 pt-4 text-sm">
|
||||
|
|
|
|||
|
|
@ -465,7 +465,7 @@ defmodule WhoNeedHelpWeb.RequestLive.Index do
|
|||
</div>
|
||||
<.icon name="hero-chevron-right" class="mt-2 size-5 shrink-0" />
|
||||
</div>
|
||||
<div class="mt-4 flex flex-wrap items-center gap-x-5 gap-y-2 border-t border-base-300 pt-4 text-xs text-base-content/55">
|
||||
<div class="mt-4 flex flex-wrap items-center gap-x-5 gap-y-2 border-t border-base-300 pt-4 text-xs text-base-content/65">
|
||||
<span class="inline-flex items-center gap-1.5">
|
||||
<.icon name="hero-map-pin" class="size-4" /> {request.location_label}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -1309,7 +1309,7 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do
|
|||
class="rounded-3xl border border-base-300 p-5"
|
||||
>
|
||||
<h3 class="font-bold">{gettext("Live location")}</h3>
|
||||
<p class="mt-2 text-xs text-base-content/55">
|
||||
<p class="mt-2 text-xs text-base-content/65">
|
||||
{gettext(
|
||||
"In a browser this works only while the page remains open. The Android app shows a persistent notification and can continue after it is minimized. Raw coordinates are removed when sharing stops."
|
||||
)}
|
||||
|
|
@ -1335,7 +1335,7 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do
|
|||
|
||||
<div :if={@other_user_id} class="rounded-3xl border border-base-300 p-5">
|
||||
<h3 class="font-bold">{gettext("Safety controls")}</h3>
|
||||
<p class="mt-2 text-xs text-base-content/55">
|
||||
<p class="mt-2 text-xs text-base-content/65">
|
||||
{gettext(
|
||||
"Reports go to moderators. Blocking hides requests in both directions and prevents new chat messages."
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ defmodule WhoNeedHelpWeb.SupportOperationsLive do
|
|||
<span :if={is_nil(notice.contact_verified_at)} class="badge badge-warning">
|
||||
{gettext("contact unverified")}
|
||||
</span>
|
||||
<span class="text-xs text-base-content/55">{removal_category(notice.category)}</span>
|
||||
<span class="text-xs text-base-content/65">{removal_category(notice.category)}</span>
|
||||
<span :if={notice.response_due_at} class="badge badge-error badge-outline">
|
||||
{gettext("review by %{time}", time: notice.response_due_at)}
|
||||
</span>
|
||||
|
|
@ -234,10 +234,10 @@ defmodule WhoNeedHelpWeb.SupportOperationsLive do
|
|||
<span :if={is_nil(request.contact_verified_at)} class="badge badge-warning">
|
||||
{gettext("contact unverified")}
|
||||
</span>
|
||||
<span class="text-xs text-base-content/55">{support_kind(request.kind)}</span>
|
||||
<span class="text-xs text-base-content/65">{support_kind(request.kind)}</span>
|
||||
</div>
|
||||
<h3 class="mt-3 font-bold">{request.subject}</h3>
|
||||
<p class="mt-1 text-xs text-base-content/55">{request.contact_email}</p>
|
||||
<p class="mt-1 text-xs text-base-content/65">{request.contact_email}</p>
|
||||
<p class="mt-3 whitespace-pre-wrap text-sm">{request.details}</p>
|
||||
<.form
|
||||
for={form}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user