more strings extractions (#963)

This commit is contained in:
Mihai C 2021-10-15 13:53:42 +03:00 committed by GitHub
parent e1f4ba06d8
commit c4e2b6b458
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 201 additions and 112 deletions

View file

@ -1,7 +1,10 @@
import { XIcon } from "@heroicons/react/outline";
import { useState } from "react";
import { useLocale } from "@lib/hooks/useLocale";
export default function AddToHomescreen() {
const { t } = useLocale();
const [closeBanner, setCloseBanner] = useState(false);
if (typeof window !== "undefined") {
@ -27,9 +30,7 @@ export default function AddToHomescreen() {
</svg>
</span>
<p className="ml-3 text-xs font-medium text-white">
<span className="inline">
Add this app to your home screen for faster access and improved experience.
</span>
<span className="inline">{t("add_to_homescreen")}</span>
</p>
</div>
@ -38,7 +39,7 @@ export default function AddToHomescreen() {
onClick={() => setCloseBanner(true)}
type="button"
className="-mr-1 flex p-2 rounded-md hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-white">
<span className="sr-only">Dismiss</span>
<span className="sr-only">{t("dismiss")}</span>
<XIcon className="h-6 w-6 text-white" aria-hidden="true" />
</button>
</div>

View file

@ -1,19 +1,22 @@
import React from "react";
import { useLocale } from "@lib/hooks/useLocale";
import NavTabs from "./NavTabs";
export default function BookingsShell({ children }: { children: React.ReactNode }) {
const { t } = useLocale();
const tabs = [
{
name: "Upcoming",
name: t("upcoming"),
href: "/bookings/upcoming",
},
{
name: "Past",
name: t("past"),
href: "/bookings/past",
},
{
name: "Cancelled",
name: t("cancelled"),
href: "/bookings/cancelled",
},
];

View file

@ -3,6 +3,7 @@ import Cropper from "react-easy-crop";
import { Area, getCroppedImg } from "@lib/cropImage";
import { useFileReader } from "@lib/hooks/useFileReader";
import { useLocale } from "@lib/hooks/useLocale";
import { DialogClose, DialogTrigger, Dialog, DialogContent } from "@components/Dialog";
import Slider from "@components/Slider";
@ -24,6 +25,7 @@ function CropContainer({
imageSrc: string;
onCropComplete: (croppedAreaPixels: Area) => void;
}) {
const { t } = useLocale();
const [crop, setCrop] = useState({ x: 0, y: 0 });
const [zoom, setZoom] = useState(1);
@ -49,7 +51,7 @@ function CropContainer({
min={1}
max={3}
step={0.1}
label="Slide to zoom, drag to reposition"
label={t("slide_zoom_drag_instructions")}
changeHandler={handleZoomSliderChange}
/>
</div>
@ -63,6 +65,7 @@ export default function ImageUploader({
handleAvatarChange,
...props
}: ImageUploaderProps) {
const { t } = useLocale();
const [imageSrc, setImageSrc] = useState<string | null>();
const [croppedAreaPixels, setCroppedAreaPixels] = useState<Area | null>();
@ -110,7 +113,7 @@ export default function ImageUploader({
<div className="sm:flex sm:items-start mb-4">
<div className="mt-3 text-center sm:mt-0 sm:text-left">
<h3 className="font-cal text-lg leading-6 font-bold text-gray-900" id="modal-title">
Upload {target}
{t("upload_target", { target })}
</h3>
</div>
</div>
@ -118,7 +121,11 @@ export default function ImageUploader({
<div className="cropper mt-6 flex flex-col justify-center items-center p-8 bg-gray-50">
{!result && (
<div className="flex justify-start items-center bg-gray-500 max-h-20 h-20 w-20 rounded-full">
{!imageSrc && <p className="sm:text-xs text-sm text-white w-full text-center">No {target}</p>}
{!imageSrc && (
<p className="sm:text-xs text-sm text-white w-full text-center">
{t("no_target", { target })}
</p>
)}
{imageSrc && <img className="h-20 w-20 rounded-full" src={imageSrc} alt={target} />}
</div>
)}
@ -128,20 +135,20 @@ export default function ImageUploader({
onInput={onInputFile}
type="file"
name={id}
placeholder="Upload image"
placeholder={t("upload_image")}
className="mt-4 pointer-events-none opacity-0 absolute"
accept="image/*"
/>
Choose a file...
{t("choose_a_file")}
</label>
</div>
</div>
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse gap-x-2">
<DialogClose asChild>
<Button onClick={() => showCroppedImage(croppedAreaPixels)}>Save</Button>
<Button onClick={() => showCroppedImage(croppedAreaPixels)}>{t("save")}</Button>
</DialogClose>
<DialogClose asChild>
<Button color="secondary">Cancel</Button>
<Button color="secondary">{t("cancel")}</Button>
</DialogClose>
</div>
</DialogContent>

View file

@ -232,7 +232,7 @@ export default function Shell(props: {
</Link>
<div className="flex items-center self-center gap-3">
<button className="p-2 text-gray-400 bg-white rounded-full hover:text-gray-500 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black">
<span className="sr-only">View notifications</span>
<span className="sr-only">{t("view_notifications")}</span>
<Link href="/settings/profile">
<a>
<CogIcon className="w-6 h-6" aria-hidden="true" />
@ -294,6 +294,7 @@ export default function Shell(props: {
}
function UserDropdown({ small }: { small?: boolean }) {
const { t } = useLocale();
const query = useMeQuery();
const user = query.data;
@ -331,7 +332,7 @@ function UserDropdown({ small }: { small?: boolean }) {
rel="noopener noreferrer"
href={`${process.env.NEXT_PUBLIC_APP_URL}/${user?.username || ""}`}
className="flex px-4 py-2 text-sm text-neutral-500">
View public page <ExternalLinkIcon className="w-3 h-3 mt-1 ml-1 text-neutral-400" />
{t("view_public_page")} <ExternalLinkIcon className="w-3 h-3 mt-1 ml-1 text-neutral-400" />
</a>
</DropdownMenuItem>
<DropdownMenuSeparator className="h-px bg-gray-200" />
@ -363,7 +364,7 @@ function UserDropdown({ small }: { small?: boolean }) {
fill="#9BA6B6"></path>
</g>
</svg>
Join our Slack
{t("join_our_slack")}
</a>
</DropdownMenuItem>
<HelpMenuItemDynamic />
@ -379,7 +380,7 @@ function UserDropdown({ small }: { small?: boolean }) {
)}
aria-hidden="true"
/>
Sign out
{t("sign_out")}
</a>
</DropdownMenuItem>
</DropdownMenuContent>

View file

@ -4,6 +4,7 @@ import dayjs from "dayjs";
import { useMutation } from "react-query";
import { HttpError } from "@lib/core/http/error";
import { useLocale } from "@lib/hooks/useLocale";
import { inferQueryOutput, trpc } from "@lib/trpc";
import TableActions, { ActionType } from "@components/ui/TableActions";
@ -11,6 +12,7 @@ import TableActions, { ActionType } from "@components/ui/TableActions";
type BookingItem = inferQueryOutput<"viewer.bookings">[number];
function BookingListItem(booking: BookingItem) {
const { t } = useLocale();
const utils = trpc.useContext();
const mutation = useMutation(
async (confirm: boolean) => {
@ -37,14 +39,14 @@ function BookingListItem(booking: BookingItem) {
const pendingActions: ActionType[] = [
{
id: "reject",
label: "Reject",
label: t("reject"),
onClick: () => mutation.mutate(false),
icon: BanIcon,
disabled: mutation.isLoading,
},
{
id: "confirm",
label: "Confirm",
label: t("confirm"),
onClick: () => mutation.mutate(true),
icon: CheckIcon,
disabled: mutation.isLoading,
@ -55,13 +57,13 @@ function BookingListItem(booking: BookingItem) {
const bookedActions: ActionType[] = [
{
id: "cancel",
label: "Cancel",
label: t("cancel"),
href: `/cancel/${booking.uid}`,
icon: XIcon,
},
{
id: "reschedule",
label: "Reschedule",
label: t("reschedule"),
href: `/reschedule/${booking.uid}`,
icon: ClockIcon,
},
@ -78,7 +80,7 @@ function BookingListItem(booking: BookingItem) {
</div>
{!booking.confirmed && !booking.rejected && (
<span className="mb-2 inline-flex items-center px-1.5 py-0.5 rounded-sm text-xs font-medium bg-yellow-100 text-yellow-800">
Unconfirmed
{t("unconfirmed")}
</span>
)}
</td>
@ -86,7 +88,7 @@ function BookingListItem(booking: BookingItem) {
<div className="sm:hidden">
{!booking.confirmed && !booking.rejected && (
<span className="mb-2 inline-flex items-center px-1.5 py-0.5 rounded-sm text-xs font-medium bg-yellow-100 text-yellow-800">
Unconfirmed
{t("unconfirmed")}
</span>
)}
<div className="text-sm text-gray-900 font-medium">
@ -117,7 +119,9 @@ function BookingListItem(booking: BookingItem) {
<>
{!booking.confirmed && !booking.rejected && <TableActions actions={pendingActions} />}
{booking.confirmed && !booking.rejected && <TableActions actions={bookedActions} />}
{!booking.confirmed && booking.rejected && <div className="text-sm text-gray-500">Rejected</div>}
{!booking.confirmed && booking.rejected && (
<div className="text-sm text-gray-500">{t("rejected")}</div>
)}
</>
) : null}
</td>

View file

@ -151,7 +151,7 @@ const BookingPage = (props: BookingPageProps) => {
if (payload["location"]) {
if (payload["location"].includes("integration")) {
params.location = "Web conferencing details to follow.";
params.location = t("web_conferencing_details_to_follow");
} else {
params.location = payload["location"];
}
@ -398,7 +398,7 @@ const BookingPage = (props: BookingPageProps) => {
<label
htmlFor="guests"
className="block mb-1 text-sm font-medium text-gray-700 dark:text-white">
Guests
{t("guests")}
</label>
<ReactMultiEmail
className="relative"

View file

@ -3,12 +3,7 @@ import React, { FC } from "react";
import { Controller, SubmitHandler, useForm, useWatch } from "react-hook-form";
import Select, { OptionTypeBase } from "react-select";
const inputOptions: OptionTypeBase[] = [
{ value: EventTypeCustomInputType.TEXT, label: "Text" },
{ value: EventTypeCustomInputType.TEXTLONG, label: "Multiline Text" },
{ value: EventTypeCustomInputType.NUMBER, label: "Number" },
{ value: EventTypeCustomInputType.BOOL, label: "Checkbox" },
];
import { useLocale } from "@lib/hooks/useLocale";
interface Props {
onSubmit: SubmitHandler<IFormInput>;
@ -19,6 +14,13 @@ interface Props {
type IFormInput = EventTypeCustomInput;
const CustomInputTypeForm: FC<Props> = (props) => {
const { t } = useLocale();
const inputOptions: OptionTypeBase[] = [
{ value: EventTypeCustomInputType.TEXT, label: t("text") },
{ value: EventTypeCustomInputType.TEXTLONG, label: t("multiline_text") },
{ value: EventTypeCustomInputType.NUMBER, label: t("number") },
{ value: EventTypeCustomInputType.BOOL, label: t("checkbox") },
];
const { selectedCustomInput } = props;
const defaultValues = selectedCustomInput || { type: inputOptions[0].value };
const { register, control, handleSubmit } = useForm<IFormInput>({
@ -35,7 +37,7 @@ const CustomInputTypeForm: FC<Props> = (props) => {
<form onSubmit={handleSubmit(props.onSubmit)}>
<div className="mb-2">
<label htmlFor="type" className="block text-sm font-medium text-gray-700">
Input type
{t("input_type")}
</label>
<Controller
name="type"
@ -57,7 +59,7 @@ const CustomInputTypeForm: FC<Props> = (props) => {
</div>
<div className="mb-2">
<label htmlFor="label" className="block text-sm font-medium text-gray-700">
Label
{t("label")}
</label>
<div className="mt-1">
<input
@ -74,7 +76,7 @@ const CustomInputTypeForm: FC<Props> = (props) => {
selectedInputType === EventTypeCustomInputType.TEXTLONG) && (
<div className="mb-2">
<label htmlFor="placeholder" className="block text-sm font-medium text-gray-700">
Placeholder
{t("placeholder")}
</label>
<div className="mt-1">
<input
@ -96,7 +98,7 @@ const CustomInputTypeForm: FC<Props> = (props) => {
{...register("required")}
/>
<label htmlFor="required" className="block text-sm font-medium text-gray-700">
Is required
{t("is_required")}
</label>
</div>
<input
@ -113,10 +115,10 @@ const CustomInputTypeForm: FC<Props> = (props) => {
/>
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<button type="submit" className="btn btn-primary">
Save
{t("save")}
</button>
<button onClick={onCancel} type="button" className="mr-2 btn btn-white">
Cancel
{t("cancel")}
</button>
</div>
</form>

View file

@ -79,7 +79,7 @@ const EventTypeList = ({ readOnly, types, profile }: Props): JSX.Element => {
}))}
/>
)}
<Tooltip content="Preview">
<Tooltip content={t("preview")}>
<a
href={`${process.env.NEXT_PUBLIC_APP_URL}/${profile.slug}/${type.slug}`}
target="_blank"
@ -89,10 +89,10 @@ const EventTypeList = ({ readOnly, types, profile }: Props): JSX.Element => {
</a>
</Tooltip>
<Tooltip content="Copy link">
<Tooltip content={t("copy_link")}>
<button
onClick={() => {
showToast("Link copied!", "success");
showToast(t("link_copied"), "success");
navigator.clipboard.writeText(
`${process.env.NEXT_PUBLIC_APP_URL}/${profile.slug}/${type.slug}`
);

View file

@ -27,13 +27,6 @@ enum SetupStep {
EnterTotpCode,
}
const setupDescriptions = {
[SetupStep.ConfirmPassword]: "Confirm your current password to get started.",
[SetupStep.DisplayQrCode]:
"Scan the image below with the authenticator app on your phone or manually enter the text code instead.",
[SetupStep.EnterTotpCode]: "Enter the six-digit code from your authenticator app below.",
};
const WithStep = ({
step,
current,
@ -47,6 +40,12 @@ const WithStep = ({
};
const EnableTwoFactorModal = ({ onEnable, onCancel }: EnableTwoFactorModalProps) => {
const { t } = useLocale();
const setupDescriptions = {
[SetupStep.ConfirmPassword]: t("2fa_confirm_current_password"),
[SetupStep.DisplayQrCode]: t("2fa_scan_image_or_use_code"),
[SetupStep.EnterTotpCode]: t("2fa_enter_six_digit_code"),
};
const [step, setStep] = useState(SetupStep.ConfirmPassword);
const [password, setPassword] = useState("");
const [totpCode, setTotpCode] = useState("");
@ -54,7 +53,6 @@ const EnableTwoFactorModal = ({ onEnable, onCancel }: EnableTwoFactorModalProps)
const [secret, setSecret] = useState("");
const [isSubmitting, setIsSubmitting] = useState(false);
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const { t } = useLocale();
async function handleSetup(e: SyntheticEvent) {
e.preventDefault();

View file

@ -100,7 +100,7 @@ export default function TeamListItem(props: {
<span className="self-center h-6 px-3 py-1 text-xs text-gray-700 capitalize rounded-md bg-gray-50">
{t("owner")}
</span>
<Tooltip content="Copy link">
<Tooltip content={t("copy_link")}>
<Button
onClick={() => {
navigator.clipboard.writeText(
@ -155,7 +155,7 @@ export default function TeamListItem(props: {
</DialogTrigger>
<ConfirmationDialogContent
variety="danger"
title="Disband Team"
title={t("disband_team")}
confirmBtnText={t("confirm_disband_team")}
onConfirm={() => props.onActionSelect("disband")}>
{t("disband_team_confirmation_message")}

View file

@ -1,23 +1,28 @@
import Link from "next/link";
const PoweredByCal = () => (
<div className="text-xs text-center sm:text-right p-1">
<Link href={`https://cal.com?utm_source=embed&utm_medium=powered-by-button`}>
<a target="_blank" className="dark:text-white text-gray-500 opacity-50 hover:opacity-100">
powered by{" "}
<img
className="dark:hidden w-auto inline h-[10px] relative -mt-px"
src="https://cal.com/logo.svg"
alt="Cal.com Logo"
/>
<img
className="hidden dark:inline w-auto h-[10px] relativ -mt-px"
src="https://cal.com/logo-white.svg"
alt="Cal.com Logo"
/>
</a>
</Link>
</div>
);
import { useLocale } from "@lib/hooks/useLocale";
const PoweredByCal = () => {
const { t } = useLocale();
return (
<div className="text-xs text-center sm:text-right p-1">
<Link href={`https://cal.com?utm_source=embed&utm_medium=powered-by-button`}>
<a target="_blank" className="dark:text-white text-gray-500 opacity-50 hover:opacity-100">
{t("powered_by")}{" "}
<img
className="dark:hidden w-auto inline h-[10px] relative -mt-px"
src="https://cal.com/logo.svg"
alt="Cal.com Logo"
/>
<img
className="hidden dark:inline w-auto h-[10px] relativ -mt-px"
src="https://cal.com/logo-white.svg"
alt="Cal.com Logo"
/>
</a>
</Link>
</div>
);
};
export default PoweredByCal;

View file

@ -3,6 +3,8 @@ import classnames from "classnames";
import dayjs, { Dayjs } from "dayjs";
import React from "react";
import { useLocale } from "@lib/hooks/useLocale";
import Text from "@components/ui/Text";
export const SCHEDULE_FORM_ID = "SCHEDULE_FORM_ID";
@ -79,6 +81,7 @@ type Props = {
};
const SchedulerForm = ({ schedule = DEFAULT_SCHEDULE, onSubmit }: Props) => {
const { t } = useLocale();
const ref = React.useRef<HTMLFormElement>(null);
const transformElementsToSchedule = (elements: HTMLFormControlsCollection): Schedule => {
@ -299,7 +302,7 @@ const SchedulerForm = ({ schedule = DEFAULT_SCHEDULE, onSubmit }: Props) => {
))
) : (
<Text key={`${day}`} variant="caption">
Unavailable
{t("unavailable")}
</Text>
)}
</div>

View file

@ -6,6 +6,8 @@ import utc from "dayjs/plugin/utc";
import React, { useEffect, useState } from "react";
import TimezoneSelect from "react-timezone-select";
import { useLocale } from "@lib/hooks/useLocale";
import { WeekdaySelect } from "./WeekdaySelect";
import SetTimesModal from "./modal/SetTimesModal";
@ -24,6 +26,7 @@ export const Scheduler = ({
timeZone: selectedTimeZone,
setTimeZone,
}: Props) => {
const { t } = useLocale();
const [editSchedule, setEditSchedule] = useState(-1);
const [dateOverrides, setDateOverrides] = useState([]);
const [openingHours, setOpeningHours] = useState([]);
@ -81,7 +84,7 @@ export const Scheduler = ({
.startOf("day")
.add(item.startTime, "minutes")
.format(item.startTime % 60 === 0 ? "ha" : "h:mma")}
&nbsp;until&nbsp;
&nbsp;{t("until")}&nbsp;
{dayjs()
.startOf("day")
.add(item.endTime, "minutes")
@ -103,7 +106,7 @@ export const Scheduler = ({
<div className="w-full">
<div>
<label htmlFor="timeZone" className="block text-sm font-medium text-gray-700">
Timezone
{t("timezone")}
</label>
<div className="mt-1">
<TimezoneSelect
@ -120,7 +123,7 @@ export const Scheduler = ({
))}
</ul>
<button type="button" onClick={addNewSchedule} className="btn-white btn-sm mt-2">
Add another
{t("add_another")}
</button>
</div>
</div>

View file

@ -3,6 +3,7 @@ import { DotsHorizontalIcon } from "@heroicons/react/solid";
import React, { FC, Fragment } from "react";
import classNames from "@lib/classNames";
import { useLocale } from "@lib/hooks/useLocale";
import { SVGComponent } from "@lib/types/SVGComponent";
import Button from "./Button";
@ -20,6 +21,7 @@ interface Props {
}
const TableActions: FC<Props> = ({ actions }) => {
const { t } = useLocale();
return (
<>
<div className="space-x-2 hidden lg:block">
@ -41,7 +43,7 @@ const TableActions: FC<Props> = ({ actions }) => {
<>
<div>
<Menu.Button className="text-neutral-400 mt-1 p-2 border border-transparent hover:border-gray-200">
<span className="sr-only">Open options</span>
<span className="sr-only">{t("open_options")}</span>
<DotsHorizontalIcon className="h-5 w-5" aria-hidden="true" />
</Menu.Button>
</div>

View file

@ -1,6 +1,8 @@
import { CheckIcon, XIcon } from "@heroicons/react/outline";
import React, { ForwardedRef, useEffect, useState } from "react";
import { useLocale } from "@lib/hooks/useLocale";
import Avatar from "@components/ui/Avatar";
import Select from "@components/ui/form/Select";
@ -21,6 +23,7 @@ export type CheckedSelectProps = {
export const CheckedSelect = React.forwardRef((props: CheckedSelectProps, ref: ForwardedRef<unknown>) => {
const [selectedOptions, setSelectedOptions] = useState<CheckedSelectValue>(props.defaultValue || []);
const { t } = useLocale();
useEffect(() => {
props.onChange(selectedOptions);
@ -66,12 +69,12 @@ export const CheckedSelect = React.forwardRef((props: CheckedSelectProps, ref: F
}),
}}
name={props.name}
placeholder={props.placeholder || "Select..."}
placeholder={props.placeholder || t("select")}
isSearchable={false}
formatOptionLabel={formatOptionLabel}
options={options}
isMulti
value={props.placeholder || "Select..."}
value={props.placeholder || t("select")}
onChange={changeHandler}
/>
{selectedOptions.map((option) => (

View file

@ -3,6 +3,7 @@ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/r
import React from "react";
import classNames from "@lib/classNames";
import { useLocale } from "@lib/hooks/useLocale";
import { RadioArea, RadioAreaGroup } from "@components/ui/form/radio-area/RadioAreaGroup";
@ -15,10 +16,11 @@ type RadioAreaSelectProps = React.SelectHTMLAttributes<HTMLSelectElement> & {
};
export const Select = function RadioAreaSelect(props: RadioAreaSelectProps) {
const { t } = useLocale();
const {
options,
disabled = !options.length, // if not explicitly disabled and the options length is empty, disable anyway
placeholder = "Select...",
placeholder = t("select"),
} = props;
const getLabel = (value: string | ReadonlyArray<string> | number) =>

View file

@ -1,7 +1,10 @@
import { ClockIcon } from "@heroicons/react/outline";
import { useRef } from "react";
import { useLocale } from "@lib/hooks/useLocale";
export default function SetTimesModal(props) {
const { t } = useLocale();
const [startHours, startMinutes] = [Math.floor(props.startTime / 60), props.startTime % 60];
const [endHours, endMinutes] = [Math.floor(props.endTime / 60), props.endTime % 60];
@ -48,18 +51,18 @@ export default function SetTimesModal(props) {
</div>
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 className="text-lg leading-6 font-medium text-gray-900" id="modal-title">
Change when you are available for bookings
{t("change_bookings_availability")}
</h3>
<div>
<p className="text-sm text-gray-500">Set your work schedule</p>
<p className="text-sm text-gray-500">{t("set_work_schedule")}</p>
</div>
</div>
</div>
<div className="flex mb-4">
<label className="w-1/4 pt-2 block text-sm font-medium text-gray-700">Start time</label>
<label className="w-1/4 pt-2 block text-sm font-medium text-gray-700">{t("start_time")}</label>
<div>
<label htmlFor="startHours" className="sr-only">
Hours
{t("hours")}
</label>
<input
ref={startHoursRef}
@ -77,7 +80,7 @@ export default function SetTimesModal(props) {
<span className="mx-2 pt-1">:</span>
<div>
<label htmlFor="startMinutes" className="sr-only">
Minutes
{t("minutes")}
</label>
<input
ref={startMinsRef}
@ -95,10 +98,10 @@ export default function SetTimesModal(props) {
</div>
</div>
<div className="flex">
<label className="w-1/4 pt-2 block text-sm font-medium text-gray-700">End time</label>
<label className="w-1/4 pt-2 block text-sm font-medium text-gray-700">{t("end_time")}</label>
<div>
<label htmlFor="endHours" className="sr-only">
Hours
{t("hours")}
</label>
<input
ref={endHoursRef}
@ -116,7 +119,7 @@ export default function SetTimesModal(props) {
<span className="mx-2 pt-1">:</span>
<div>
<label htmlFor="endMinutes" className="sr-only">
Minutes
{t("minutes")}
</label>
<input
ref={endMinsRef}
@ -135,10 +138,10 @@ export default function SetTimesModal(props) {
</div>
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<button onClick={updateStartEndTimesHandler} type="submit" className="btn btn-primary">
Save
{t("save")}
</button>
<button onClick={props.onExit} type="button" className="btn btn-white mr-2">
Cancel
{t("cancel")}
</button>
</div>
</div>

View file

@ -1,6 +1,7 @@
import { ArrowLeftIcon } from "@heroicons/react/solid";
import { useEffect, useRef, useState } from "react";
import { useLocale } from "@lib/hooks/useLocale";
import showToast from "@lib/notification";
import { Webhook } from "@lib/webhook";
@ -8,6 +9,7 @@ import Button from "@components/ui/Button";
import Switch from "@components/ui/Switch";
export default function EditTeam(props: { webhook: Webhook; onCloseEdit: () => void }) {
const { t } = useLocale();
const [bookingCreated, setBookingCreated] = useState(
props.webhook.eventTriggers.includes("booking_created")
);
@ -58,7 +60,7 @@ export default function EditTeam(props: { webhook: Webhook; onCloseEdit: () => v
})
.then(handleErrors)
.then(() => {
showToast("Webhook updated successfully!", "success");
showToast(t("webhook_updated_successfully"), "success");
setBtnLoading(false);
});
};
@ -74,12 +76,12 @@ export default function EditTeam(props: { webhook: Webhook; onCloseEdit: () => v
loading={btnLoading}
StartIcon={ArrowLeftIcon}
onClick={() => props.onCloseEdit()}>
Back
{t("back")}
</Button>
</div>
<div>
<div className="pb-5 pr-4 sm:pb-6">
<h3 className="text-lg font-bold leading-6 text-gray-900">Manage your webhook</h3>
<h3 className="text-lg font-bold leading-6 text-gray-900">{t("manage_your_webhook")}</h3>
</div>
</div>
<hr className="mt-2" />
@ -87,7 +89,7 @@ export default function EditTeam(props: { webhook: Webhook; onCloseEdit: () => v
<div className="my-4">
<div className="mb-4">
<label htmlFor="subUrl" className="block text-sm font-medium text-gray-700">
Subscriber Url
{t("subscriber_url")}
</label>
<input
ref={subUrlRef}
@ -99,11 +101,14 @@ export default function EditTeam(props: { webhook: Webhook; onCloseEdit: () => v
required
className="block w-full px-3 py-2 mt-1 border border-gray-300 rounded-sm shadow-sm focus:outline-none focus:ring-neutral-500 focus:border-neutral-500 sm:text-sm"
/>
<legend className="block pt-4 mb-2 text-sm font-medium text-gray-700"> Event Triggers </legend>
<legend className="block pt-4 mb-2 text-sm font-medium text-gray-700">
{" "}
{t("event_triggers")}{" "}
</legend>
<div className="p-2 bg-white border border-gray-300 rounded-sm">
<div className="flex p-2">
<div className="w-10/12">
<h2 className="text-sm text-gray-800">Booking Created</h2>
<h2 className="text-sm text-gray-800">{t("booking_created")}</h2>
</div>
<div className="flex items-center justify-end w-2/12 text-right">
<Switch
@ -116,7 +121,7 @@ export default function EditTeam(props: { webhook: Webhook; onCloseEdit: () => v
</div>
<div className="flex px-2 py-1">
<div className="w-10/12">
<h2 className="text-sm text-gray-800">Booking Rescheduled</h2>
<h2 className="text-sm text-gray-800">{t("booking_rescheduled")}</h2>
</div>
<div className="flex items-center justify-end w-2/12 text-right">
<Switch
@ -129,7 +134,7 @@ export default function EditTeam(props: { webhook: Webhook; onCloseEdit: () => v
</div>
<div className="flex p-2">
<div className="w-10/12">
<h2 className="text-sm text-gray-800">Booking Cancelled</h2>
<h2 className="text-sm text-gray-800">{t("booking_cancelled")}</h2>
</div>
<div className="flex items-center justify-end w-2/12 text-right">
<Switch
@ -141,11 +146,14 @@ export default function EditTeam(props: { webhook: Webhook; onCloseEdit: () => v
</div>
</div>
</div>
<legend className="block pt-4 mb-2 text-sm font-medium text-gray-700"> Webhook Status </legend>
<legend className="block pt-4 mb-2 text-sm font-medium text-gray-700">
{" "}
{t("webhook_status")}{" "}
</legend>
<div className="p-2 bg-white border border-gray-300 rounded-sm">
<div className="flex p-2">
<div className="w-10/12">
<h2 className="text-sm text-gray-800">Webhook Enabled</h2>
<h2 className="text-sm text-gray-800">{t("webhook_enabled")}</h2>
</div>
<div className="flex items-center justify-end w-2/12 text-right">
<Switch
@ -160,7 +168,7 @@ export default function EditTeam(props: { webhook: Webhook; onCloseEdit: () => v
</div>
<div className="gap-2 mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<Button type="submit" color="primary" className="ml-2" loading={btnLoading}>
Save
{t("save")}
</Button>
</div>
</div>

View file

@ -1,5 +1,6 @@
import { TrashIcon, PencilAltIcon } from "@heroicons/react/outline";
import { useLocale } from "@lib/hooks/useLocale";
import showToast from "@lib/notification";
import { Webhook } from "@lib/webhook";
@ -14,6 +15,7 @@ export default function WebhookListItem(props: {
webhook: Webhook;
onEditWebhook: () => void;
}) {
const { t } = useLocale();
const handleErrors = async (resp: Response) => {
if (!resp.ok) {
const err = await resp.json();
@ -31,7 +33,7 @@ export default function WebhookListItem(props: {
})
.then(handleErrors)
.then(() => {
showToast("Webhook removed successfully!", "success");
showToast(t("webhook_removed_successfully"), "success");
props.onChange();
});
};
@ -43,7 +45,7 @@ export default function WebhookListItem(props: {
<span className="flex flex-col space-y-2 text-xs">
{props.webhook.eventTriggers.map((eventTrigger, ind) => (
<span key={ind} className="px-1 text-xs text-blue-700 rounded-md w-max bg-blue-50">
{eventTrigger}
{t(`${eventTrigger}`)}
</span>
))}
</span>
@ -56,16 +58,16 @@ export default function WebhookListItem(props: {
<div className="flex">
{!props.webhook.active && (
<span className="self-center h-6 px-3 py-1 text-xs text-red-700 capitalize rounded-md bg-red-50">
Disabled
{t("disabled")}
</span>
)}
{!!props.webhook.active && (
<span className="self-center h-6 px-3 py-1 text-xs text-green-700 capitalize rounded-md bg-green-50">
Enabled
{t("enabled")}
</span>
)}
<Tooltip content="Edit Webhook">
<Tooltip content={t("edit_webhook")}>
<Button
onClick={() => props.onEditWebhook()}
color="minimal"
@ -74,7 +76,7 @@ export default function WebhookListItem(props: {
className="self-center w-full p-2 ml-4"></Button>
</Tooltip>
<Dialog>
<Tooltip content="Delete Webhook">
<Tooltip content={t("delete_webhook")}>
<DialogTrigger asChild>
<Button
onClick={(e) => {
@ -88,14 +90,13 @@ export default function WebhookListItem(props: {
</Tooltip>
<ConfirmationDialogContent
variety="danger"
title="Delete Webhook"
confirmBtnText="Yes, delete webhook"
cancelBtnText="Cancel"
title={t("delete_webhook")}
confirmBtnText={t("confirm_delete_webhook")}
cancelBtnText={t("cancel")}
onConfirm={() => {
deleteWebhook(props.webhook.id);
}}>
Are you sure you want to delete this webhook? You will no longer receive Cal.com meeting data at
a specified URL, in real-time, when an event is scheduled or canceled .
{t("delete_webhook_confirmation_message")}
</ConfirmationDialogContent>
</Dialog>
</div>

View file

@ -1,5 +1,46 @@
{
"delete_webhook_confirmation_message": "Are you sure you want to delete this webhook? You will no longer receive Cal.com meeting data at a specified URL, in real-time, when an event is scheduled or canceled.",
"confirm_delete_webhook": "Yes, delete webhook",
"edit_webhook": "Edit Webhook",
"delete_webhook": "Delete Webhook",
"webhook_status": "Webhook Status",
"webhook_enabled": "Webhook Enabled",
"manage_your_webhook": "Manage your webhook",
"webhook_updated_successfully": "Webhook updated successfully!",
"webhook_removed_successfully": "Webhook removed successfully!",
"dismiss": "Dismiss",
"add_to_homescreen": "Add this app to your home screen for faster access and improved experience.",
"upcoming": "Upcoming",
"past": "Past",
"choose_a_file": "Choose a file...",
"upload_image": "Upload image",
"upload_target": "Upload {{target}}",
"no_target": "No {{target}}",
"slide_zoom_drag_instructions": "Slide to zoom, drag to reposition",
"view_notifications": "View notifications",
"view_public_page": "View public page",
"sign_out": "Sign out",
"add_another": "Add another",
"until": "until",
"powered_by": "powered by",
"unavailable": "Unavailable",
"set_work_schedule": "Set your work schedule",
"change_bookings_availability": "Change when you are available for bookings",
"select": "Select...",
"2fa_confirm_current_password": "Confirm your current password to get started.",
"2fa_scan_image_or_use_code": "Scan the image below with the authenticator app on your phone or manually enter the text code instead.",
"text": "Text",
"multiline_text": "Multiline Text",
"number": "Number",
"checkbox": "Checkbox",
"is_required": "Is required",
"input_type": "Input type",
"rejected": "Rejected",
"unconfirmed": "Unconfirmed",
"guests": "Guests",
"web_conferencing_details_to_follow": "Web conferencing details to follow.",
"the_username": "The username",
"username": "Username",
"is_still_available": "is still available.",
"documentation": "Documentation",
"documentation_description": "Learn how to integrate our tools with your app",
@ -8,6 +49,7 @@
"blog": "Blog",
"blog_description": "Read our latest news and articles",
"join_our_community": "Join our community",
"join_our_slack": "Join our Slack",
"claim_username_and_schedule_events": "Claim your username and schedule events",
"popular_pages": "Popular pages",
"register_now": "Register now",
@ -31,6 +73,7 @@
"incorrect_2fa_code": "Two-factor code is incorrect.",
"no_account_exists": "No account exists matching that email address.",
"2fa_enabled_instructions": "Two-factor authentication enabled. Please enter the six-digit code from your authenticator app.",
"2fa_enter_six_digit_code": "Enter the six-digit code from your authenticator app below.",
"create_an_account": "Create an account",
"dont_have_an_account": "Don't have an account?",
"2fa_code": "Two-Factor Code",
@ -139,7 +182,7 @@
"booking_cancelled": "Booking Cancelled",
"booking_rescheduled": "Booking Rescheduled",
"booking_created": "Booking Created",
"event_triggers": "Event triggers",
"event_triggers": "Event Triggers",
"subscriber_url": "Subscriber Url",
"create_new_webhook": "Create a new webhook",
"create_new_webhook_to_account": "Create a new webhook to your account",