Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 46 additions & 9 deletions backend/lang/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -260,11 +260,11 @@
"Most capacity": "Mayor capacidad",
"Sorry, these tickets are sold out": "Lo sentimos, estas entradas están agotadas",
"The maximum number of tickets available is :max": "El número máximo de entradas disponibles es :max",
"Ticket is hidden without promo code": "El boleto está oculto sin código promocional",
"Ticket is sold out": "El boleto está agotado",
"Ticket is before sale start date": "El boleto es antes de la fecha de inicio de la venta",
"Ticket is after sale end date": "El boleto es después de la fecha de finalización de la venta",
"Ticket is hidden": "El boleto está oculto",
"Ticket is hidden without promo code": "La entrada está oculta sin código promocional",
"Ticket is sold out": "La entrada está agotada",
"Ticket is before sale start date": "La entrada es antes de la fecha de inicio de la venta",
"Ticket is after sale end date": "La entrada es después de la fecha de finalización de la venta",
"Ticket is hidden": "La entrada está oculta",
"Price is before sale start date": "El precio es antes de la fecha de inicio de la venta",
"Price is after sale end date": "El precio es después de la fecha de finalización de la venta",
"Price is sold out": "El precio está agotado",
Expand Down Expand Up @@ -393,16 +393,16 @@
"Order is not awaiting offline payment": "El pedido no está en espera de pago offline",
"Refund already processed": "Reembolso ya procesado",
"Stripe refund successful": "Reembolso de Stripe exitoso",
"There are no tickets available for this event": "No hay boletos disponibles para este evento",
"There are no tickets available for this event": "No hay entradas disponibles para este evento",
"Address line 1 is required": "La dirección (línea 1) es obligatoria",
"City is required": "La ciudad es obligatoria",
"Zip or postal code is required": "El código postal es obligatorio",
"Country is required": "El país es obligatorio",
"If you did not request a password reset, please immediately reset your password.": "Si no solicitaste un restablecimiento de contraseña, por favor restablécela de inmediato.",
"ℹ️ Your order is pending payment. Tickets have been issued but will not be valid until payment is received.": "ℹ️ Tu pedido está pendiente de pago. Los boletos han sido emitidos, pero no serán válidos hasta que se reciba el pago.",
"ℹ️ Your order is pending payment. Tickets have been issued but will not be valid until payment is received.": "ℹ️ Tu pedido está pendiente de pago. Los entradas han sido emitidos, pero no serán válidos hasta que se reciba el pago.",
"ℹ️ This order is pending payment. Please mark the payment as received on the order management page once payment is received.": "ℹ️ Este pedido está pendiente de pago. Por favor, márcalo como pagado en la página de gestión de pedidos una vez recibido el pago.",
"Order Status:": "Estado del pedido:",
"Your order is pending payment. Tickets have been issued but will not be valid until payment is received.": "Tu pedido está pendiente de pago. Los boletos han sido emitidos, pero no serán válidos hasta que se reciba el pago.",
"Your order is pending payment. Tickets have been issued but will not be valid until payment is received.": "Tu pedido está pendiente de pago. Los entradas han sido emitidos, pero no serán válidos hasta que se reciba el pago.",
"Payment Instructions": "Instrucciones de pago",
"Please follow the instructions below to complete your payment.": "Por favor, sigue las instrucciones a continuación para completar tu pago.",
"Invoice Number": "Número de factura",
Expand Down Expand Up @@ -670,5 +670,42 @@
"View Event": "Ver evento",
"Your waitlist offer has expired": "Tu oferta de la lista de espera ha expirado",
"We'll notify you as soon as a spot becomes available.": "Te notificaremos en cuanto haya una plaza disponible.",
"You're on the waitlist!": "¡Estás en la lista de espera!"
"You're on the waitlist!": "¡Estás en la lista de espera!",
"Amount": "Importe",
"Amount Paid": "Importe pagado",
"Balance Due": "Saldo pendiente",
"Default": "Predeterminado",
"Description": "Descripción",
"Google Tag Manager is not available on hosted plans for security reasons.": "Google Tag Manager no está disponible en planes alojados por razones de seguridad.",
"Must be 6-30 alphanumeric characters": "Debe tener entre 6 y 30 caracteres alfanuméricos",
"Must be 9-20 digits (e.g., 1234567890)": "Debe tener entre 9 y 20 dígitos (p. ej., 1234567890)",
"Must start with G- followed by 6-20 characters (e.g., G-XXXXXXXXXX)": "Debe comenzar con G- seguido de 6 a 20 caracteres (p. ej., G-XXXXXXXXXX)",
"Must start with GTM- followed by 4-20 characters (e.g., GTM-XXXXXXX)": "Debe comenzar con GTM- seguido de 4 a 20 caracteres (p. ej., GTM-XXXXXXX)",
"Not enough products available. Please try again.": "No hay suficientes productos disponibles. Por favor, inténtelo de nuevo.",
"Only scheduled messages can be cancelled": "Solo se pueden cancelar los mensajes programados",
"Order Ascending": "Orden ascendente",
"Order Descending": "Orden descendente",
"Paid": "Pagado",
"Position ascending": "Posición ascendente",
"Position descending": "Posición descendente",
"Product not found for this event": "Producto no encontrado para este evento",
"Qty": "Cant.",
"Rate": "Tasa",
"The scheduled time must be in the future.": "La hora programada debe ser en el futuro.",
"The selected font is not supported.": "La fuente seleccionada no es compatible.",
"There are no waiting entries for this product": "No hay entradas en lista de espera para este producto",
"This event cannot be deleted because it has completed orders. Please cancel or refund all orders first.": "Este evento no se puede eliminar porque tiene pedidos completados. Por favor, cancele o reembolse todos los pedidos primero.",
"This event has completed orders. Please cancel or refund all orders before deleting.": "Este evento tiene pedidos completados. Por favor, cancele o reembolse todos los pedidos antes de eliminarlo.",
"This message can no longer be cancelled": "Este mensaje ya no se puede cancelar",
"This organizer has events with completed orders. Please cancel or refund all orders first.": "Este organizador tiene eventos con pedidos completados. Por favor, cancele o reembolse todos los pedidos primero.",
"This waitlist entry cannot be cancelled": "Esta entrada de la lista de espera no se puede cancelar",
"This waitlist entry cannot be offered in its current status": "Esta entrada de la lista de espera no se puede ofrecer en su estado actual",
"Unpaid": "No pagado",
"Void": "Anulado",
"Waitlist entry not found": "Entrada de lista de espera no encontrada",
"Waitlist is not enabled for this product": "La lista de espera no está habilitada para este producto",
"You are already on the waitlist for this product": "Ya estás en la lista de espera de este producto",
"You cannot archive the last active organizer on your account.": "No puedes archivar el último organizador activo de tu cuenta.",
"You cannot delete the last organizer on your account.": "No puedes eliminar el último organizador de tu cuenta.",
"You must acknowledge your data controller responsibilities before enabling tracking pixels.": "Debes reconocer tus responsabilidades como responsable del tratamiento de datos antes de habilitar los píxeles de seguimiento."
}
4 changes: 4 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {Notifications} from "@mantine/notifications";
import {i18n} from "@lingui/core";
import {I18nProvider} from "@lingui/react";
import {ModalsProvider} from "@mantine/modals";
import {DatesProvider} from "@mantine/dates";
import {HydrationBoundary, QueryClient, QueryClientProvider} from "@tanstack/react-query";
import {Helmet, HelmetProvider} from "react-helmet-async";
import {generateColors} from '@mantine/colors-generator';
Expand All @@ -22,6 +23,7 @@ import {ThirdPartyScripts} from "./components/common/ThirdPartyScripts";
import {getConfig} from "./utilites/config.ts";
import {CookieConsentBanner} from "./components/common/CookieConsentBanner";
import {isConsentPending, setConsentState, updateGoogleConsentMode} from "./utilites/trackingPixels/consent";
import "./utilites/dateLocales.ts";

declare global {
interface Window {
Expand Down Expand Up @@ -83,6 +85,7 @@ export const App: FC<
>
<HelmetProvider context={props.helmetContext}>
<I18nProvider i18n={i18n}>
<DatesProvider settings={{locale: props.locale}}>
<QueryClientProvider client={props.queryClient}>
<HydrationBoundary state={props.dehydratedState}>
<StartupChecks/>
Expand All @@ -103,6 +106,7 @@ export const App: FC<
)}
</HydrationBoundary>
</QueryClientProvider>
</DatesProvider>
</I18nProvider>
</HelmetProvider>
</MantineProvider>
Expand Down
9 changes: 8 additions & 1 deletion frontend/src/components/common/AttendeeStatusBadge/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {Attendee} from "../../../types.ts";
import {Badge} from "@mantine/core";
import {t} from "@lingui/macro";

interface AttendeeStatusBadgeProps {
attendee: Attendee;
Expand All @@ -22,7 +23,13 @@ export const AttendeeStatusBadge = ({attendee, noStyle = false}: AttendeeStatusB
break;
}

const status = attendee.status.replace('_', ' ');
const statusLabels: Record<string, string> = {
'ACTIVE': t`Active`,
'CANCELLED': t`Cancelled`,
'AWAITING_PAYMENT': t`Awaiting Payment`,
};

const status = statusLabels[attendee.status] || attendee.status.replace('_', ' ');

if (noStyle) {
return <span style={{color: color}}>{status}</span>;
Expand Down
28 changes: 22 additions & 6 deletions frontend/src/components/common/OrderStatusBadge/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,43 @@ import {Order} from "../../../types.ts";
import {getStatusColor} from "../../../utilites/helpers.ts";
import {t} from "@lingui/macro";

const getStatusLabel = (status: string): string => {
const labels: Record<string, string> = {
'COMPLETED': t`Completed`,
'CANCELLED': t`Cancelled`,
'AWAITING_PAYMENT': t`Awaiting Payment`,
'AWAITING_OFFLINE_PAYMENT': t`Awaiting offline payment`,
'PAYMENT_RECEIVED': t`Payment Received`,
'NO_PAYMENT_REQUIRED': t`No Payment Required`,
'PAYMENT_FAILED': t`Payment Failed`,
'REFUNDED': t`Refunded`,
'PARTIALLY_REFUNDED': t`Partially Refunded`,
'REFUND_PENDING': t`Refund Pending`,
};
return labels[status] || status.replace('_', ' ');
};

export const OrderStatusBadge = ({order, variant = 'outline'}: { order: Order, variant?: BadgeVariant }) => {
let color;
let title;

if (order.status === 'CANCELLED') {
color = getStatusColor(order.status);
title = t`Cancelled`;
title = getStatusLabel(order.status);
} else if (order.status === 'AWAITING_OFFLINE_PAYMENT') {
color = getStatusColor('AWAITING_PAYMENT');
title = t`Awaiting offline payment`;
title = getStatusLabel(order.status);
} else if (order.refund_status) {
color = getStatusColor(order.refund_status);
title = order.refund_status;
title = getStatusLabel(order.refund_status);
} else if (order.payment_status && order.payment_status !== 'PAYMENT_RECEIVED'
&& order.payment_status !== 'NO_PAYMENT_REQUIRED') {
color = getStatusColor(order.payment_status);
title = order.payment_status;
title = getStatusLabel(order.payment_status);
} else {
color = getStatusColor(order.status);
title = order.status;
title = getStatusLabel(order.status);
}

return <Badge color={color} variant={variant}>{title.replace('_', ' ')}</Badge>
return <Badge color={color} variant={variant}>{title}</Badge>
};
19 changes: 10 additions & 9 deletions frontend/src/components/forms/ProductCategoryForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {UseFormReturnType} from "@mantine/form";
import {ProductCategory} from "../../../types.ts";
import {Switch, TextInput} from "@mantine/core";
import {Editor} from "../../common/Editor";
import {t} from "@lingui/macro";

interface ProductCategoryFormProps {
form: UseFormReturnType<ProductCategory>,
Expand All @@ -11,30 +12,30 @@ export const ProductCategoryForm = ({form}: ProductCategoryFormProps) => {
return (
<>
<TextInput
label={"Name"}
placeholder={"Name"}
description={"This is the name of the category that will be displayed on the event page."}
label={t`Name`}
placeholder={t`Name`}
description={t`This is the name of the category that will be displayed on the event page.`}
required
{...form.getInputProps("name")}
/>

<TextInput
label={"No products message"}
description={"A message to display when there are no products in this category."}
label={t`No products message`}
description={t`A message to display when there are no products in this category.`}
{...form.getInputProps("no_products_message")}
/>

<Editor
label={"Description"}
description={"An optional description of this category to display on the event page."}
label={t`Description`}
description={t`An optional description of this category to display on the event page.`}
onChange={(value) => form.setFieldValue("description", value)}
value={form.values.description || ""}
maxLength={5000}
/>

<Switch
label={"Hide this category?"}
description={"If checked, this category will be hidden from the public."}
label={t`Hide this category?`}
description={t`If checked, this category will be hidden from the public.`}
{...form.getInputProps("is_hidden", {type: "checkbox"})}
/>
</>
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/components/modals/CreateEventModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {OrganizerCreateForm} from "../../forms/OrganizerForm";
import dayjs from "dayjs";
import {DateTimePicker} from "@mantine/dates";
import {EventCategories} from "../../../constants/eventCategories.ts";
import {getDateTimePickerFormat} from "../../../utilites/dates.ts";

interface CreateEventModalProps extends GenericModalProps {
organizerId?: IdParam;
Expand Down Expand Up @@ -212,7 +213,7 @@ export const CreateEventModal = ({onClose, organizerId}: CreateEventModalProps)
required
size="md"
placeholder={t`Select start date and time`}
valueFormat="MMM DD, YYYY [at] h:mm A"
valueFormat={getDateTimePickerFormat()}
clearable
dropdownType="modal"
timePickerProps={{
Expand All @@ -233,7 +234,7 @@ export const CreateEventModal = ({onClose, organizerId}: CreateEventModalProps)
{...form.getInputProps('end_date')}
size="md"
placeholder={t`Select end date and time`}
valueFormat="MMM DD, YYYY [at] h:mm A"
valueFormat={getDateTimePickerFormat()}
clearable
dropdownType="modal"
timePickerProps={{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ const Users = () => {
<Badge variant="outline">
<Group gap={5}>
{user.role === 'ORGANIZER' && <IconUser size={14}/>}
{user.role === 'ADMIN' && <IconUserShield size={14}/>} {user.role}
{user.role === 'ADMIN' && <IconUserShield size={14}/>} {user.role === 'ADMIN' ? t`Admin` : user.role === 'ORGANIZER' ? t`Organizer` : user.role}
</Group>
</Badge>
</Table.Td>
Expand All @@ -102,7 +102,7 @@ const Users = () => {
</Table.Td>
<Table.Td>
<Badge color={statusColor(user.status as string)} variant="light">
{user.status}
{user.status === 'ACTIVE' ? t`Active` : user.status === 'INVITED' ? t`Invited` : user.status === 'INACTIVE' ? t`Inactive` : user.status}
</Badge>
</Table.Td>
<Table.Td width={'60px'}>
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/components/routes/welcome/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import dayjs from "dayjs";
import {EventCategories} from "../../../constants/eventCategories.ts";
import {getConfig} from "../../../utilites/config.ts";
import {trackEvent, AnalyticsEvents} from "../../../utilites/analytics.ts";
import {getDateTimePickerFormat} from "../../../utilites/dates.ts";

export const CreateOrganizer = ({progressInfo}: {
progressInfo?: { currentStep: number, totalSteps: number, progressPercentage: number }
Expand Down Expand Up @@ -363,7 +364,7 @@ export const CreateEvent = ({progressInfo}: {
{...form.getInputProps('start_date')}
label={t`Start date & time`}
placeholder={t`Select start time`}
valueFormat="MMM DD, h:mm A"
valueFormat={getDateTimePickerFormat()}
size="lg"
required
dropdownType="modal"
Expand All @@ -384,7 +385,7 @@ export const CreateEvent = ({progressInfo}: {
{...form.getInputProps('end_date')}
label={t`End time (optional)`}
placeholder={t`Select end time`}
valueFormat="MMM DD, h:mm A"
valueFormat={getDateTimePickerFormat()}
size="lg"
dropdownType="modal"
timePickerProps={{
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/locales/es.js

Large diffs are not rendered by default.

Loading
Loading