Compare commits

...

14 Commits

Author SHA1 Message Date
Alan a722836b70 fix undefined currency 2023-09-29 13:07:21 -07:00
Alan ade7f30f31 Fix didMount form initialize on paypal 2023-09-29 12:05:04 -07:00
alannnc d3cddd5dee
Merge branch 'main' into paypal-app-refactor 2023-09-29 12:00:13 -07:00
Alan 3f172e1c33 undo zod changes that give type error 2023-09-28 18:55:11 -07:00
Alan 50985aba14 type fixes 2023-09-28 18:33:21 -07:00
Alan 19aef4f2d5 apply same styles for stripe app 2023-09-28 18:13:16 -07:00
Alan b1c467574b undo 2023-09-28 17:32:45 -07:00
Alan 0337e4b35e merge with remote main 2023-09-28 17:14:39 -07:00
Lauris Skraucis dfb227586d
Merge branch 'main' into paypal-app-refactor 2023-09-27 11:33:56 +02:00
supalarry f1b4cb9c41 fix: dynamic currency symbols 2023-09-26 17:00:52 +02:00
supalarry 29c79fde59 fix: Use USD as the default currency 2023-09-26 15:43:39 +02:00
supalarry 6de58154a5 refactor(ui): inputs spacing 2023-09-26 12:20:06 +02:00
supalarry 99f130b107 refactor(ui): price input border 2023-09-26 12:07:22 +02:00
supalarry 22490ac578 refactor(ui): price input margin 2023-09-26 11:55:36 +02:00
3 changed files with 92 additions and 36 deletions

View File

@ -1,9 +1,13 @@
import { useRouter } from "next/router";
import { useState } from "react";
import { useState, useEffect } from "react";
import { useAppContextWithSchema } from "@calcom/app-store/EventTypeAppContext";
import AppCard from "@calcom/app-store/_components/AppCard";
import { currencyOptions } from "@calcom/app-store/paypal/lib/currencyOptions";
import {
currencyOptions,
currencySymbols,
isAcceptedCurrencyCode,
} from "@calcom/app-store/paypal/lib/currencyOptions";
import type { EventTypeAppCardComponent } from "@calcom/app-store/types";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
@ -18,12 +22,11 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
const { asPath } = useRouter();
const { getAppData, setAppData } = useAppContextWithSchema<typeof appDataSchema>();
const price = getAppData("price");
const currency = getAppData("currency");
const [selectedCurrency, setSelectedCurrency] = useState(
currencyOptions.find((c) => c.value === currency) || {
label: currencyOptions[0].label,
value: currencyOptions[0].value,
}
const [selectedCurrency, setSelectedCurrency] = useState(currencyOptions.find((c) => c.value === currency));
const [currencySymbol, setCurrencySymbol] = useState(
isAcceptedCurrencyCode(currency) ? currencySymbols[currency] : ""
);
const paymentOption = getAppData("paymentOption");
@ -36,6 +39,17 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
const { t } = useLocale();
const recurringEventDefined = eventType.recurringEvent?.count !== undefined;
useEffect(() => {
if (requirePayment) {
if (!getAppData("currency")) {
setAppData("currency", currencyOptions[0].value);
}
if (!getAppData("paymentOption")) {
setAppData("paymentOption", paymentOptions[0].value);
}
}
}, []);
return (
<AppCard
returnTo={WEBAPP_URL + asPath}
@ -55,13 +69,13 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
<TextField
label="Price"
labelSrOnly
addOnLeading="$"
addOnSuffix={selectedCurrency.value || "No selected currency"}
addOnLeading={currencySymbol}
addOnSuffix={currency}
step="0.01"
min="0.5"
type="number"
required
className="block w-full rounded-sm border-gray-300 pl-2 pr-12 text-sm"
className="block w-full rounded-sm pl-2 text-sm"
placeholder="Price"
onChange={(e) => {
setAppData("price", Number(e.target.value) * 100);
@ -73,7 +87,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
/>
</div>
<div className="mt-5 w-60">
<label className="text-default block text-sm font-medium" htmlFor="currency">
<label className="text-default mb-1 block text-sm font-medium" htmlFor="currency">
{t("currency")}
</label>
<Select
@ -85,14 +99,15 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
onChange={(e) => {
if (e) {
setSelectedCurrency(e);
setCurrencySymbol(currencySymbols[e.value]);
setAppData("currency", e.value);
}
}}
/>
</div>
<div className="mt-2 w-60">
<label className="text-default block text-sm font-medium" htmlFor="currency">
<div className="mt-4 w-60">
<label className="text-default mb-1 block text-sm font-medium" htmlFor="currency">
Payment option
</label>
<Select<Option>

View File

@ -24,4 +24,38 @@ export const currencyOptions = [
{ label: "Swedish krona", value: "SEK" },
{ label: "Swiss franc", value: "CHF" },
{ label: "Thai baht", value: "THB" },
];
] as const;
type CurrencyCode = (typeof currencyOptions)[number]["value"];
export const currencySymbols: Record<CurrencyCode, string> = {
USD: "$",
AUD: "$",
BRL: "R$",
CAD: "$",
CNY: "¥",
CZK: "Kč",
DKK: "kr",
EUR: "€",
HKD: "$",
HUF: "Ft",
ILS: "₪",
JPY: "¥",
MYR: "RM",
MXN: "$",
TWD: "$",
NZD: "$",
NOK: "kr",
PHP: "₱",
PLN: "zł",
GBP: "£",
RUB: "₽",
SGD: "$",
SEK: "kr",
CHF: "Fr",
THB: "฿",
};
export function isAcceptedCurrencyCode(currencyCode: string): currencyCode is CurrencyCode {
return Object.keys(currencySymbols).includes(currencyCode);
}

View File

@ -22,7 +22,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
const pathname = usePathname();
const { getAppData, setAppData, disabled } = useAppContextWithSchema<typeof appDataSchema>();
const price = getAppData("price");
const currency = getAppData("currency");
const currency = getAppData("currency") || currencyOptions[0].value;
const [selectedCurrency, setSelectedCurrency] = useState(
currencyOptions.find((c) => c.value === currency) || {
label: currencyOptions[0].label,
@ -73,8 +73,30 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
)}
{!recurringEventDefined && requirePayment && (
<>
<div className="mt-4 block items-center justify-start sm:flex sm:space-x-2">
<TextField
data-testid="price-input-stripe"
label={t("price")}
className="h-[38px]"
addOnLeading={
<>{selectedCurrency.value ? getCurrencySymbol("en", selectedCurrency.value) : ""}</>
}
addOnSuffix={currency.toUpperCase()}
addOnClassname="h-[38px]"
step="0.01"
min="0.5"
type="number"
required
placeholder="Price"
disabled={disabled}
onChange={(e) => {
setAppData("price", convertToSmallestCurrencyUnit(Number(e.target.value), currency));
}}
value={price > 0 ? convertFromSmallestToPresentableCurrencyUnit(price, currency) : undefined}
/>
</div>
<div className="mt-5 w-60">
<label className="text-default block text-sm font-medium" htmlFor="currency">
<label className="text-default mb-1 block text-sm font-medium" htmlFor="currency">
{t("currency")}
</label>
<Select
@ -92,26 +114,10 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
}}
/>
</div>
<div className="mt-4 block items-center justify-start sm:flex sm:space-x-2">
<TextField
data-testid="price-input-stripe"
label={t("price")}
className="h-[38px]"
addOnLeading={
<>{selectedCurrency.value ? getCurrencySymbol("en", selectedCurrency.value) : ""}</>
}
addOnClassname="h-[38px]"
step="0.01"
min="0.5"
type="number"
required
placeholder="Price"
disabled={disabled}
onChange={(e) => {
setAppData("price", convertToSmallestCurrencyUnit(Number(e.target.value), currency));
}}
value={price > 0 ? convertFromSmallestToPresentableCurrencyUnit(price, currency) : undefined}
/>
<div className="mt-4 w-60">
<label className="text-default mb-1 block text-sm font-medium" htmlFor="currency">
Payment option
</label>
<Select<Option>
defaultValue={
paymentOptionSelectValue
@ -128,6 +134,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
isDisabled={seatsEnabled || disabled}
/>
</div>
{seatsEnabled && paymentOption === "HOLD" && (
<Alert className="mt-2" severity="warning" title={t("seats_and_no_show_fee_error")} />
)}