feat: add Payment page styles

This commit is contained in:
jpiere-vailos
2025-04-08 14:31:48 -05:00
parent 8318ac9500
commit 226baf99a6
10 changed files with 321 additions and 101 deletions
+54 -19
View File
@@ -1,30 +1,65 @@
<script lang="ts">
import type { ApiGatewaysNameGetResponse } from "../../openapi/client/index";
import CreditCardIcon from "../../svgs/CreditCardIcon.svelte";
import PaypalIcon from "../../svgs/PaypalIcon.svelte";
import StripeIcon from "../../svgs/StripeIcon.svelte";
import { t } from "../../i18n/store";
export let paymentGateways: ApiGatewaysNameGetResponse[] = [];
let selectedPaymentMethod = ""; // 👈 variable normal
let selectedPaymentMethod = "";
</script>
<section>
<h1>Indícanos tus datos de pago</h1>
<p>Ahora necesitamos que nos digas cómo quieres pagar.</p>
<p>Selecciona método de pago:</p>
<div class="flex flex-col gap-6 sm:w-full sm:max-w-md">
<p class="text-2xl font-bold">
{$t("payment.page.selectMethod")}
</p>
<div>
{#each paymentGateways as gateway}
<label>
<input
type="radio"
name="paymentMethod"
value={gateway.name}
bind:group={selectedPaymentMethod}
/>
<span
>{(gateway.name ?? "").charAt(0).toUpperCase() +
(gateway.name ?? "").slice(1)}</span
>
</label>
{/each}
<form id="register" method="POST" class="flex flex-col gap-4">
<div class="grid grid-cols-2 gap-4">
{#each paymentGateways as gateway}
<label
class="flex cursor-pointer items-center justify-between gap-4 rounded-2xl border px-6 py-4 shadow-sm transition-all hover:shadow-md"
class:selected={selectedPaymentMethod === gateway.name}
class:bg-teal-300={selectedPaymentMethod === gateway.name}
class:text-[#2f1e2e]={selectedPaymentMethod === gateway.name}
class:border-teal-400={selectedPaymentMethod === gateway.name}
class:border-[#F3F3EF]={selectedPaymentMethod !== gateway.name}
>
<div class="flex items-center gap-2">
<input
type="radio"
name="paymentMethod"
value={gateway.name}
bind:group={selectedPaymentMethod}
class="after:bg-primary relative h-6 w-6 appearance-none rounded-full border border-[#462949] after:absolute after:top-1/2 after:left-1/2 after:hidden after:h-2 after:w-2 after:-translate-x-1/2 after:-translate-y-1/2 after:transform after:rounded-full after:content-[''] checked:border-[#462949] checked:bg-[#462949] checked:after:block"
/>
<div class="flex flex-col leading-tight">
<span class="font-semibold capitalize">
{gateway.name}
</span>
</div>
</div>
<div>
{#if gateway.name === "paypal"}
<PaypalIcon />
{:else if gateway.name === "stripe"}
<StripeIcon />
{:else if gateway.name === "wallet"}
<CreditCardIcon />
{/if}
</div>
</label>
{/each}
</div>
<button
class="bg-primary cursor-pointer rounded-3xl px-6 py-4 font-bold text-[#462949]"
type="submit"
>
Enviar
</button>
</form>
</div>
</section>
+1
View File
@@ -40,6 +40,7 @@ const { t } = Astro.locals;
</label>
</div>
<!-- TODO: Merge with Btn "Continuar" -->
<button
class="bg-primary cursor-pointer rounded-3xl px-6 py-4 font-bold text-[#462949]"
type="submit"
+137 -58
View File
@@ -5,18 +5,67 @@ const { t } = Astro.locals;
<div>
<div class="mt-8 sm:w-full sm:max-w-md">
<form id="register" method="POST" class="flex flex-col gap-4">
<fieldset>
<legend>Tipo de usuario</legend>
<label>
<input type="radio" name="type" value="individual" checked />
{t("register.form.userType.individual")}
<fieldset class="flex gap-6">
<label class="flex cursor-pointer items-center gap-2">
<input
type="radio"
name="type"
value="individual"
checked
class="relative h-6 w-6 appearance-none rounded-full border border-purple-600 after:absolute after:top-1/2 after:left-1/2 after:hidden after:h-2 after:w-2 after:-translate-x-1/2 after:-translate-y-1/2 after:transform after:rounded-full after:bg-purple-900 after:content-[''] checked:border-teal-400 checked:bg-teal-400 checked:after:block"
/>
<span class="text-gray-700"> {t("register.form.userType.individual")}</span>
</label>
<label>
<input type="radio" name="type" value="organization" />
{t("register.form.userType.organization")}
<label class="flex cursor-pointer items-center gap-2">
<input
type="radio"
name="type"
value="organization"
class="relative h-6 w-6 appearance-none rounded-full border border-[#855a96] after:absolute after:top-1/2 after:left-1/2 after:hidden after:h-2 after:w-2 after:-translate-x-1/2 after:-translate-y-1/2 after:transform after:rounded-full after:bg-purple-900 after:content-[''] checked:border-teal-400 checked:bg-teal-400 checked:after:block"
/>
<span class="text-gray-700">{t("register.form.userType.organization")}</span>
</label>
</fieldset>
<div class="flex flex-row gap-4">
<div class="relative w-full">
<input
class="peer w-full rounded-md border border-[#855a96] px-4 pt-6 pb-2 text-base text-gray-700 placeholder-gray-400 focus:ring-1 focus:ring-[#855a96] focus:outline-none"
required
type="text"
id="firstname"
name="firstname"
placeholder=""
required
/>
<label
for="firstname"
class="absolute top-1 left-4 text-[10px] font-medium text-gray-500 transition-all peer-placeholder-shown:top-4 peer-placeholder-shown:text-base peer-placeholder-shown:text-gray-400 peer-focus:top-1 peer-focus:text-[10px] peer-focus:text-gray-500"
>
{t("register.individual.firstName")}
</label>
</div>
<div class="relative w-full">
<input
class="peer w-full rounded-md border border-[#855a96] px-4 pt-6 pb-2 text-base text-gray-700 placeholder-gray-400 focus:ring-1 focus:ring-[#855a96] focus:outline-none"
required
type="text"
id="lastname"
name="lastname"
placeholder=""
required
/>
<label
for="lastname"
class="absolute top-1 left-4 text-[10px] font-medium text-gray-500 transition-all peer-placeholder-shown:top-4 peer-placeholder-shown:text-base peer-placeholder-shown:text-gray-400 peer-focus:top-1 peer-focus:text-[10px] peer-focus:text-gray-500"
>
{t("register.individual.lastName")}
</label>
</div>
</div>
<div class="relative w-full">
<input
type="email"
@@ -52,58 +101,40 @@ const { t } = Astro.locals;
</label>
</div>
<div class="relative w-full">
<input
class="peer w-full rounded-md border border-[#855a96] px-4 pt-6 pb-2 text-base text-gray-700 placeholder-gray-400 focus:ring-1 focus:ring-[#855a96] focus:outline-none"
required
type="text"
id="firstname"
name="firstname"
placeholder=""
required
/>
<label
for="firstname"
class="absolute top-1 left-4 text-[10px] font-medium text-gray-500 transition-all peer-placeholder-shown:top-4 peer-placeholder-shown:text-base peer-placeholder-shown:text-gray-400 peer-focus:top-1 peer-focus:text-[10px] peer-focus:text-gray-500"
>
{t("register.individual.firstName")}
</label>
<div class="relative w-full text-xs font-medium text-[#575757]">
<span> {t("register.form.validation.password.minLength")}</span>
</div>
<div class="relative w-full">
<input
class="peer w-full rounded-md border border-[#855a96] px-4 pt-6 pb-2 text-base text-gray-700 placeholder-gray-400 focus:ring-1 focus:ring-[#855a96] focus:outline-none"
required
type="text"
id="lastname"
name="lastname"
placeholder=""
required
/>
<label
for="lastname"
class="absolute top-1 left-4 text-[10px] font-medium text-gray-500 transition-all peer-placeholder-shown:top-4 peer-placeholder-shown:text-base peer-placeholder-shown:text-gray-400 peer-focus:top-1 peer-focus:text-[10px] peer-focus:text-gray-500"
>
{t("register.individual.lastName")}
</label>
</div>
<div class="relative flex w-full flex-col gap-4" id="fields-individual">
<div>
<label class="flex flex-row items-start gap-2">
<input
type="checkbox"
id="toggle-dni"
class="accent-primary mt-1 h-5 w-5 shrink-0 border border-gray-400 text-[#855a96] focus:ring-[#855a96]"
/>
<span class="text-sm text-gray-700"
>{t("register.individual.taxId.msgDeduction")}</span
>
</label>
</div>
<div class="relative w-full" id="fields-individual">
<input
class="peer w-full rounded-md border border-[#855a96] px-4 pt-6 pb-2 text-base text-gray-700 placeholder-gray-400 focus:ring-1 focus:ring-[#855a96] focus:outline-none"
required
type="text"
id="dni"
name="dni"
placeholder=""
required
/>
<label
for="dni"
class="absolute top-1 left-4 text-[10px] font-medium text-gray-500 transition-all peer-placeholder-shown:top-4 peer-placeholder-shown:text-base peer-placeholder-shown:text-gray-400 peer-focus:top-1 peer-focus:text-[10px] peer-focus:text-gray-500"
>
{t("register.individual.taxId")}
</label>
<div class="relative w-full" id="fields-individual">
<input
class="peer w-full rounded-md border px-4 pt-6 pb-2 text-base text-gray-700 placeholder-gray-400 focus:ring-1 focus:outline-none disabled:cursor-not-allowed"
type="text"
id="dni"
name="dni"
placeholder=""
disabled
/>
<label
for="dni"
class="absolute top-1 left-4 text-[10px] font-medium text-gray-500 transition-all peer-placeholder-shown:top-4 peer-placeholder-shown:text-base peer-placeholder-shown:text-gray-400 peer-focus:top-1 peer-focus:text-[10px] peer-focus:text-gray-500"
>
{t("register.individual.taxId.label")}
</label>
</div>
</div>
<div id="fields-organization" style="display: none;" class="flex flex-col gap-4">
@@ -144,6 +175,30 @@ const { t } = Astro.locals;
</div>
</div>
<div class="space-y-2">
<label class="flex items-start gap-2">
<input
type="checkbox"
id="terms"
class="accent-primary mt-1 h-5 w-5 shrink-0 border border-gray-400 text-[#855a96] focus:ring-[#855a96]"
/>
<span class="text-sm text-gray-700">
{t("register.form.termsCheckbox")}
</span>
</label>
<label class="flex items-start gap-2">
<input
type="checkbox"
id="cookies"
class="accent-primary mt-1 h-5 w-5 shrink-0 border border-gray-400 text-[#855a96] focus:ring-[#855a96]"
/>
<span class="text-sm text-gray-700">
{t("register.form.policiesCheckbox")}
</span>
</label>
</div>
<!-- TODO: Merge with Btn "Continuar" -->
<button
class="bg-primary cursor-pointer rounded-3xl px-6 py-4 font-bold text-[#462949]"
type="submit"
@@ -202,7 +257,7 @@ const { t } = Astro.locals;
const fieldsOrganization = document.getElementById("fields-organization");
if (fieldsIndividual && fieldsOrganization) {
fieldsIndividual.style.display = isIndividual ? "block" : "none";
fieldsIndividual.style.display = isIndividual ? "flex" : "none";
fieldsOrganization.style.display = isIndividual ? "none" : "flex";
}
}
@@ -213,5 +268,29 @@ const { t } = Astro.locals;
document.querySelectorAll('input[name="type"]').forEach((radio) => {
radio.addEventListener("change", toggleFields);
});
const toggleDni = document.getElementById("toggle-dni");
if (toggleDni) {
toggleDni.addEventListener("change", function () {
const dniInput = document.getElementById("dni") as HTMLInputElement;
const labelInput = document.querySelector('label[for="dni"]');
if (dniInput && labelInput) {
const isChecked = (this as HTMLInputElement).checked;
dniInput.disabled = !isChecked;
dniInput.classList.remove("border-[#e6e6e6]", "focus:ring-transparent");
if (isChecked) {
dniInput.classList.add("border-[#855a96]", "focus:ring-[#855a96]");
dniInput.classList.remove("cursor-not-allowed");
labelInput.classList.remove("cursor-not-allowed");
}
}
});
toggleDni.dispatchEvent(new Event("change"));
}
});
</script>
+3
View File
@@ -97,6 +97,9 @@
"zod": {
"taxId.custom": "Tax ID must be at least 8 characters long",
"legalName.custom": "Legal name is required for organizations"
},
"password": {
"minLength": "*Requisitos de la contraseña: debe tener 8 caracteres"
}
},
"individual": {
+16 -1
View File
@@ -98,6 +98,11 @@
"zod": {
"taxId.custom": "El CIF/DNI debe tener al menos 8 caracteres",
"legalName.custom": "La razón social es obligatoria para empresas"
},
"validation": {
"password": {
"minLength": "*Requisitos de la contraseña: debe tener 8 caracteres"
}
}
},
"individual": {
@@ -106,7 +111,10 @@
"email": "Correo electrónico",
"password": "Contraseña",
"taxIdCheckbox": "Deseo desgravar esta donación. Es necesario indicar el número de DNI para comunicárselo a Hacienda.",
"taxId": "DNI*"
"taxId": {
"label": "DNI*",
"msgDeduction": "Deseo desgravar esta donación. Es necesario indicar el número de DNI para comunicárselo a Hacienda."
}
},
"organization": {
"legalName": "Razón social",
@@ -229,5 +237,12 @@
"btnContinue": {
"label": "Continuar"
}
},
"payment": {
"page": {
"title": "Indícanos tus datos de pago",
"description": "Ahora necesitamos que nos digas cómo quieres pagar.",
"selectMethod": "Selecciona método de pago:"
}
}
}
+32 -4
View File
@@ -11,9 +11,37 @@ const { t } = Astro.locals;
<Layout title={t("metadata.title")} description={t("metadata.description")}>
<main class="wrapper">
<h1 class="text-body">payment page</h1>
<PaymentMethod client:load {paymentGateways} />
<Summary client:load />
<Steps step={3} client:load />
<div class="flex flex-row gap-8 py-10">
<div class="flex w-1/2 flex-col gap-10">
<div class="flex flex-col gap-4">
<div class="flex flex-col gap-10">
<div class="flex flex-col gap-4">
<h2 class="text-[40px]/[48px] font-bold tracking-[0%] text-[#3D3D3D]">
{t("payment.page.title")}
</h2>
<p class="text-[#575757]">
{t("payment.page.description")}
</p>
</div>
<PaymentMethod client:load {paymentGateways} />
</div>
</div>
</div>
<div
class="border-tertiary flex h-fit w-1/2 flex-col gap-6 rounded-[40px] border bg-white"
>
<Summary client:load />
<Steps step={3} client:load />
<div class="flex flex-col gap-4 px-6 pt-0 pb-6">
<p class="text-sm text-[#575757]">{t("checkout.summary.deduct")}</p>
<a
class="bg-primary w-fit cursor-pointer rounded-3xl px-6 py-4 font-bold text-[#462949]"
>
Continuar
</a>
</div>
</div>
</div>
</main>
</Layout>
+20 -19
View File
@@ -19,27 +19,28 @@ const { t } = Astro.locals;
{t("register.page.title")}
</h2>
<p class="text-[#575757]">{t("register.page.description")}</p>
<div>
<div class="flex flex-col gap-4">
<div class="flex flex-row items-center gap-2">
<h2 class="text-secondary text-2xl font-bold">
{t("register.page.registerOr")}
</h2>
<a
href="#"
class="text-tertiary rounded-2xl bg-[#E6E5F7] px-4 py-2 font-bold"
>{t("register.page.loginBtnLabel")}
</a>
</div>
</div>
</div>
<p>{t("register.page.goteoUserInfo")}</p>
</div>
<RegisterForm />
<div>
<p>{t("login.page.otherAccessMethods")}</p>
<Oauth />
<div>
<div class="flex flex-col gap-4">
<div class="flex flex-row items-center gap-2">
<h2 class="text-secondary text-2xl font-bold">
{t("register.page.registerOr")}
</h2>
<a
href="#"
class="text-tertiary rounded-2xl bg-[#E6E5F7] px-4 py-2 font-bold"
>{t("register.page.loginBtnLabel")}
</a>
</div>
<p class="text-[#575757]">{t("register.page.goteoUserInfo")}</p>
</div>
<RegisterForm />
</div>
<div>
<p>{t("login.page.otherAccessMethods")}</p>
<Oauth />
</div>
</div>
</div>
+7
View File
@@ -0,0 +1,7 @@
<svg width="26" height="22" viewBox="0 0 26 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M3 2C2.44772 2 2 2.44772 2 3V6H24V3C24 2.44772 23.5523 2 23 2H3ZM26 3C26 1.34315 24.6569 0 23 0H3C1.34315 0 0 1.34315 0 3V19C0 20.6569 1.34315 22 3 22H23C24.6569 22 26 20.6569 26 19V3ZM24 10V19C24 19.5523 23.5523 20 23 20H3C2.44772 20 2 19.5523 2 19V10H24ZM4 17C4 16.4477 4.44772 16 5 16H13C13.5523 16 14 16.4477 14 17C14 17.5523 13.5523 18 13 18H5C4.44772 18 4 17.5523 4 17Z"
fill="#462949"></path>
</svg>

After

Width:  |  Height:  |  Size: 588 B

+11
View File
@@ -0,0 +1,11 @@
<svg width="26" height="32" viewBox="0 0 26 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M10.4128 6.39921C10.1964 6.39939 9.98724 6.4779 9.82278 6.62064C9.65833 6.76337 9.54938 6.96098 9.51554 7.17794L8.02472 16.7779C8.09422 16.3296 8.47473 15.9992 8.92195 15.9992H13.2906C17.6873 15.9992 21.4181 12.7428 22.0998 8.32982C22.1505 8.00036 22.1793 7.6678 22.1858 7.3344C21.0685 6.73957 19.7559 6.39921 18.3177 6.39921H10.4128Z"
fill="#001C64"></path>
<path
d="M22.1858 7.3344C22.1793 7.6678 22.1505 8.00036 22.0998 8.32982C21.4181 12.7428 17.6873 15.9992 13.2906 15.9992H8.92195C8.47473 15.9992 8.09422 16.3296 8.02472 16.7779L5.79524 31.134C5.77848 31.2409 5.78476 31.3502 5.81367 31.4544C5.84257 31.5586 5.89341 31.6552 5.96266 31.7376C6.03191 31.8199 6.11792 31.886 6.21477 31.9313C6.31162 31.9766 6.417 32.0001 6.52364 32H11.2654C11.4818 31.9998 11.691 31.9213 11.8554 31.7786C12.0199 31.6358 12.1288 31.4382 12.1626 31.2213L13.4116 23.1784C13.4455 22.9613 13.5545 22.7636 13.7191 22.6209C13.8837 22.4781 14.0931 22.3997 14.3096 22.3997H17.1014C21.4981 22.3997 25.2288 19.1433 25.9106 14.7303C26.3946 11.5983 24.841 8.74788 22.1858 7.3344Z"
fill="#0070E0"></path>
<path
d="M5.02581 0C4.57894 0 4.19808 0.330026 4.12857 0.77802L0.407595 24.7338C0.337037 25.1885 0.683167 25.5998 1.1367 25.5998H6.65461L8.02472 16.7779L9.51554 7.17794C9.54938 6.96098 9.65833 6.76337 9.82278 6.62064C9.98724 6.4779 10.1964 6.39939 10.4128 6.39921H18.3177C19.7559 6.39921 21.0685 6.73957 22.1858 7.3344C22.2623 3.31671 18.997 0 14.5076 0H5.02581Z"
fill="#003087"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

+40
View File
@@ -0,0 +1,40 @@
<svg width="77" height="32" viewBox="0 0 77 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_957_1465)">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M76.9965 16.5291C76.9965 11.062 74.3483 6.74805 69.2869 6.74805C64.2042 6.74805 61.1289 11.062 61.1289 16.4864C61.1289 22.9146 64.7594 26.1608 69.9703 26.1608C72.5117 26.1608 74.4338 25.5841 75.886 24.7726V20.5014C74.4338 21.2275 72.768 21.676 70.6537 21.676C68.5822 21.676 66.7456 20.9499 66.5106 18.4298H76.9538C76.9538 18.1522 76.9965 17.0417 76.9965 16.5291ZM66.4466 14.5003C66.4466 12.0871 67.9201 11.0833 69.2656 11.0833C70.5683 11.0833 71.9565 12.0871 71.9565 14.5003H66.4466Z"
fill="#635BFF"></path>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M52.8844 6.74805C50.7915 6.74805 49.4461 7.73043 48.6986 8.41382L48.421 7.08974H43.7227V31.991L49.0617 30.8591L49.083 24.8153C49.8519 25.3706 50.9837 26.1608 52.8631 26.1608C56.6858 26.1608 60.1669 23.0855 60.1669 16.3156C60.1455 10.1223 56.6217 6.74805 52.8844 6.74805ZM51.6031 21.4624C50.3431 21.4624 49.5956 21.0139 49.083 20.4587L49.0617 12.5356C49.6169 11.9162 50.3858 11.4891 51.6031 11.4891C53.5465 11.4891 54.8919 13.6674 54.8919 16.4651C54.8919 19.3268 53.5678 21.4624 51.6031 21.4624Z"
fill="#635BFF"></path>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M36.375 5.48852L41.7354 4.33529V0L36.375 1.13187V5.48852Z"
fill="#635BFF"></path>
<path d="M41.7354 7.11133H36.375V25.7979H41.7354V7.11133Z" fill="#635BFF"></path>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M30.6304 8.69173L30.2887 7.11137H25.6758V25.798H31.0148V13.1338C32.2748 11.4894 34.4104 11.7884 35.0725 12.0233V7.11137C34.3891 6.8551 31.8904 6.38526 30.6304 8.69173Z"
fill="#635BFF"></path>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M19.9549 2.47754L14.744 3.58806L14.7227 20.6943C14.7227 23.855 17.0932 26.1828 20.2539 26.1828C22.0051 26.1828 23.2865 25.8625 23.9912 25.4781V21.1428C23.3078 21.4204 19.9335 22.4028 19.9335 19.2421V11.6607H23.9912V7.11182H19.9335L19.9549 2.47754Z"
fill="#635BFF"></path>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M5.51638 12.5356C5.51638 11.7027 6.19978 11.3823 7.33165 11.3823C8.95471 11.3823 11.0049 11.8735 12.628 12.7491V7.73043C10.8554 7.02568 9.10421 6.74805 7.33165 6.74805C2.99636 6.74805 0.113281 9.0118 0.113281 12.7918C0.113281 18.6861 8.22861 17.7464 8.22861 20.2878C8.22861 21.2702 7.37436 21.5905 6.17842 21.5905C4.40586 21.5905 2.14211 20.8644 0.348198 19.8821V24.9648C2.33432 25.8191 4.34179 26.1821 6.17842 26.1821C10.6205 26.1821 13.6744 23.9824 13.6744 20.1597C13.6531 13.7956 5.51638 14.9274 5.51638 12.5356Z"
fill="#635BFF"></path>
</g>
<defs>
<clipPath id="clip0_957_1465">
<rect width="76.8863" height="32" fill="white" transform="translate(0.113281)"></rect>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB