chore: i18n/extract strings (#934)
This commit is contained in:
parent
bee41b242b
commit
9e2f8de313
7 changed files with 139 additions and 72 deletions
|
@ -4,6 +4,7 @@ import { useRouter } from "next/router";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
|
||||||
|
import { useLocale } from "@lib/hooks/useLocale";
|
||||||
import { useToggleQuery } from "@lib/hooks/useToggleQuery";
|
import { useToggleQuery } from "@lib/hooks/useToggleQuery";
|
||||||
import showToast from "@lib/notification";
|
import showToast from "@lib/notification";
|
||||||
import { trpc } from "@lib/trpc";
|
import { trpc } from "@lib/trpc";
|
||||||
|
@ -22,6 +23,7 @@ function convertMinsToHrsMins(mins: number) {
|
||||||
return `${hours}:${minutes}`;
|
return `${hours}:${minutes}`;
|
||||||
}
|
}
|
||||||
export default function Availability() {
|
export default function Availability() {
|
||||||
|
const { t } = useLocale();
|
||||||
const queryMe = trpc.useQuery(["viewer.me"]);
|
const queryMe = trpc.useQuery(["viewer.me"]);
|
||||||
const formModal = useToggleQuery("edit");
|
const formModal = useToggleQuery("edit");
|
||||||
|
|
||||||
|
@ -57,27 +59,25 @@ export default function Availability() {
|
||||||
return <Loader />;
|
return <Loader />;
|
||||||
}
|
}
|
||||||
if (queryMe.status !== "success") {
|
if (queryMe.status !== "success") {
|
||||||
return <Alert severity="error" title="Something went wrong" />;
|
return <Alert severity="error" title={t("something_went_wrong")} />;
|
||||||
}
|
}
|
||||||
const user = queryMe.data;
|
const user = queryMe.data;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Shell heading="Availability" subtitle="Configure times when you are available for bookings.">
|
<Shell heading={t("availability")} subtitle={t("configure_availability")}>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="w-1/2 mr-2 bg-white border border-gray-200 rounded-sm">
|
<div className="w-1/2 mr-2 bg-white border border-gray-200 rounded-sm">
|
||||||
<div className="px-4 py-5 sm:p-6">
|
<div className="px-4 py-5 sm:p-6">
|
||||||
<h3 className="text-lg leading-6 font-medium text-gray-900">
|
<h3 className="text-lg leading-6 font-medium text-gray-900">{t("change_start_end")}</h3>
|
||||||
Change the start and end times of your day
|
|
||||||
</h3>
|
|
||||||
<div className="mt-2 max-w-xl text-sm text-gray-500">
|
<div className="mt-2 max-w-xl text-sm text-gray-500">
|
||||||
<p>
|
<p>
|
||||||
Currently, your day is set to start at {convertMinsToHrsMins(user.startTime)} and end at{" "}
|
{t("current_start_date")} {convertMinsToHrsMins(user.startTime)} {t("and_end_at")}{" "}
|
||||||
{convertMinsToHrsMins(user.endTime)}.
|
{convertMinsToHrsMins(user.endTime)}.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<Button href={formModal.hrefOn}>Change available times</Button>
|
<Button href={formModal.hrefOn}>{t("change_available_times")}</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -85,14 +85,14 @@ export default function Availability() {
|
||||||
<div className="w-1/2 ml-2 border border-gray-200 rounded-sm">
|
<div className="w-1/2 ml-2 border border-gray-200 rounded-sm">
|
||||||
<div className="px-4 py-5 sm:p-6">
|
<div className="px-4 py-5 sm:p-6">
|
||||||
<h3 className="text-lg leading-6 font-medium text-gray-900">
|
<h3 className="text-lg leading-6 font-medium text-gray-900">
|
||||||
Something doesn't look right?
|
{t("something_doesnt_look_right")}
|
||||||
</h3>
|
</h3>
|
||||||
<div className="mt-2 max-w-xl text-sm text-gray-500">
|
<div className="mt-2 max-w-xl text-sm text-gray-500">
|
||||||
<p>Troubleshoot your availability to explore why your times are showing as they are.</p>
|
<p>{t("troubleshoot_availability")}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<Link href="/availability/troubleshoot">
|
<Link href="/availability/troubleshoot">
|
||||||
<a className="btn btn-white">Launch troubleshooter</a>
|
<a className="btn btn-white">{t("launch_troubleshooter")}</a>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -111,12 +111,10 @@ export default function Availability() {
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
<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">
|
<h3 className="text-lg leading-6 font-medium text-gray-900" id="modal-title">
|
||||||
Change your available times
|
{t("change_your_available_times")}
|
||||||
</h3>
|
</h3>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-gray-500">
|
<p className="text-sm text-gray-500">{t("change_start_end_buffer")}</p>
|
||||||
Set the start and end time of your day and a minimum buffer between your meetings.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -136,19 +134,21 @@ export default function Availability() {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
showToast("Something went wrong", "error");
|
showToast(t("something_went_wrong"), "error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await queryMe.refetch();
|
await queryMe.refetch();
|
||||||
router.push(formModal.hrefOff);
|
router.push(formModal.hrefOff);
|
||||||
|
|
||||||
showToast("The start and end times for your day have been changed successfully.", "success");
|
showToast(t("start_end_changed_successfully"), "success");
|
||||||
})}>
|
})}>
|
||||||
<div className="flex mb-4">
|
<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>
|
<div>
|
||||||
<label htmlFor="startHours" className="sr-only">
|
<label htmlFor="startHours" className="sr-only">
|
||||||
Hours
|
{t("hours")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
{...formMethods.register("startHours")}
|
{...formMethods.register("startHours")}
|
||||||
|
@ -162,7 +162,7 @@ export default function Availability() {
|
||||||
<span className="mx-2 pt-1">:</span>
|
<span className="mx-2 pt-1">:</span>
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="startMins" className="sr-only">
|
<label htmlFor="startMins" className="sr-only">
|
||||||
Minutes
|
{t("minutes")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
{...formMethods.register("startMins")}
|
{...formMethods.register("startMins")}
|
||||||
|
@ -174,10 +174,10 @@ export default function Availability() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex mb-4">
|
<div className="flex mb-4">
|
||||||
<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>
|
<div>
|
||||||
<label htmlFor="endHours" className="sr-only">
|
<label htmlFor="endHours" className="sr-only">
|
||||||
Hours
|
{t("hours")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
{...formMethods.register("endHours")}
|
{...formMethods.register("endHours")}
|
||||||
|
@ -190,7 +190,7 @@ export default function Availability() {
|
||||||
<span className="mx-2 pt-1">:</span>
|
<span className="mx-2 pt-1">:</span>
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="endMins" className="sr-only">
|
<label htmlFor="endMins" className="sr-only">
|
||||||
Minutes
|
{t("minutes")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
{...formMethods.register("endMins")}
|
{...formMethods.register("endMins")}
|
||||||
|
@ -202,10 +202,10 @@ export default function Availability() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex mb-4">
|
<div className="flex mb-4">
|
||||||
<label className="w-1/4 pt-2 block text-sm font-medium text-gray-700">Buffer</label>
|
<label className="w-1/4 pt-2 block text-sm font-medium text-gray-700">{t("buffer")}</label>
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="bufferHours" className="sr-only">
|
<label htmlFor="bufferHours" className="sr-only">
|
||||||
Hours
|
{t("hours")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
{...formMethods.register("bufferHours")}
|
{...formMethods.register("bufferHours")}
|
||||||
|
@ -218,7 +218,7 @@ export default function Availability() {
|
||||||
<span className="mx-2 pt-1">:</span>
|
<span className="mx-2 pt-1">:</span>
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="bufferMins" className="sr-only">
|
<label htmlFor="bufferMins" className="sr-only">
|
||||||
Minutes
|
{t("minutes")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
{...formMethods.register("bufferMins")}
|
{...formMethods.register("bufferMins")}
|
||||||
|
@ -231,10 +231,10 @@ export default function Availability() {
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5 sm:mt-4 sm:flex space-x-2">
|
<div className="mt-5 sm:mt-4 sm:flex space-x-2">
|
||||||
<Button href={formModal.hrefOff} color="secondary" tabIndex={-1}>
|
<Button href={formModal.hrefOff} color="secondary" tabIndex={-1}>
|
||||||
Cancel
|
{t("cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="submit" loading={formMethods.formState.isSubmitting}>
|
<Button type="submit" loading={formMethods.formState.isSubmitting}>
|
||||||
Update
|
{t("update")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import utc from "dayjs/plugin/utc";
|
import utc from "dayjs/plugin/utc";
|
||||||
import { GetServerSidePropsContext } from "next";
|
import { GetServerSidePropsContext } from "next";
|
||||||
|
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
import { getSession } from "@lib/auth";
|
import { getSession } from "@lib/auth";
|
||||||
|
import { getOrSetUserLocaleFromHeaders } from "@lib/core/i18n/i18n.utils";
|
||||||
|
import { useLocale } from "@lib/hooks/useLocale";
|
||||||
import prisma from "@lib/prisma";
|
import prisma from "@lib/prisma";
|
||||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||||
|
|
||||||
|
@ -13,6 +16,7 @@ import Shell from "@components/Shell";
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
|
|
||||||
export default function Troubleshoot({ user }: inferSSRProps<typeof getServerSideProps>) {
|
export default function Troubleshoot({ user }: inferSSRProps<typeof getServerSideProps>) {
|
||||||
|
const { t } = useLocale();
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [availability, setAvailability] = useState([]);
|
const [availability, setAvailability] = useState([]);
|
||||||
const [selectedDate, setSelectedDate] = useState(dayjs());
|
const [selectedDate, setSelectedDate] = useState(dayjs());
|
||||||
|
@ -52,12 +56,10 @@ export default function Troubleshoot({ user }: inferSSRProps<typeof getServerSid
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Shell
|
<Shell heading={t("troubleshoot")} subtitle={t("troubleshoot_description")}>
|
||||||
heading="Troubleshoot"
|
|
||||||
subtitle="Understand why certain times are available and others are blocked.">
|
|
||||||
<div className="bg-white max-w-xl overflow-hidden shadow rounded-sm">
|
<div className="bg-white max-w-xl overflow-hidden shadow rounded-sm">
|
||||||
<div className="px-4 py-5 sm:p-6">
|
<div className="px-4 py-5 sm:p-6">
|
||||||
Here is an overview of your day on{" "}
|
{t("overview_of_day")}{" "}
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
className="inline border-none h-8 p-0"
|
className="inline border-none h-8 p-0"
|
||||||
|
@ -66,34 +68,33 @@ export default function Troubleshoot({ user }: inferSSRProps<typeof getServerSid
|
||||||
setSelectedDate(dayjs(e.target.value));
|
setSelectedDate(dayjs(e.target.value));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<small className="block text-neutral-400">
|
<small className="block text-neutral-400">{t("hover_over_bold_times_tip")}</small>
|
||||||
Tip: Hover over the bold times for a full timestamp
|
|
||||||
</small>
|
|
||||||
<div className="mt-4 space-y-4">
|
<div className="mt-4 space-y-4">
|
||||||
<div className="bg-black overflow-hidden rounded-sm">
|
<div className="bg-black overflow-hidden rounded-sm">
|
||||||
<div className="px-4 sm:px-6 py-2 text-white">
|
<div className="px-4 sm:px-6 py-2 text-white">
|
||||||
Your day starts at {convertMinsToHrsMins(user.startTime)}
|
{t("your_day_starts_at")} {convertMinsToHrsMins(user.startTime)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{availability.map((slot) => (
|
{availability.map((slot) => (
|
||||||
<div key={slot.start} className="bg-neutral-100 overflow-hidden rounded-sm">
|
<div key={slot.start} className="bg-neutral-100 overflow-hidden rounded-sm">
|
||||||
<div className="px-4 py-5 sm:p-6 text-black">
|
<div className="px-4 py-5 sm:p-6 text-black">
|
||||||
Your calendar shows you as busy between{" "}
|
{t("calendar_shows_busy_between")}{" "}
|
||||||
<span className="font-medium text-neutral-800" title={slot.start}>
|
<span className="font-medium text-neutral-800" title={slot.start}>
|
||||||
{dayjs(slot.start).format("HH:mm")}
|
{dayjs(slot.start).format("HH:mm")}
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
and{" "}
|
{t("and")}{" "}
|
||||||
<span className="font-medium text-neutral-800" title={slot.end}>
|
<span className="font-medium text-neutral-800" title={slot.end}>
|
||||||
{dayjs(slot.end).format("HH:mm")}
|
{dayjs(slot.end).format("HH:mm")}
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
on {dayjs(slot.start).format("D MMMM YYYY")}
|
{t("on")} {dayjs(slot.start).format("D")}{" "}
|
||||||
|
{t(dayjs(slot.start).format("MMMM").toLowerCase())} {dayjs(slot.start).format("YYYY")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{availability.length === 0 && <Loader />}
|
{availability.length === 0 && <Loader />}
|
||||||
<div className="bg-black overflow-hidden rounded-sm">
|
<div className="bg-black overflow-hidden rounded-sm">
|
||||||
<div className="px-4 sm:px-6 py-2 text-white">
|
<div className="px-4 sm:px-6 py-2 text-white">
|
||||||
Your day ends at {convertMinsToHrsMins(user.endTime)}
|
{t("your_day_ends_at")} {convertMinsToHrsMins(user.endTime)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -106,6 +107,8 @@ export default function Troubleshoot({ user }: inferSSRProps<typeof getServerSid
|
||||||
|
|
||||||
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
|
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
|
||||||
const session = await getSession(context);
|
const session = await getSession(context);
|
||||||
|
const locale = await getOrSetUserLocaleFromHeaders(context.req);
|
||||||
|
|
||||||
if (!session?.user?.id) {
|
if (!session?.user?.id) {
|
||||||
return { redirect: { permanent: false, destination: "/auth/login" } };
|
return { redirect: { permanent: false, destination: "/auth/login" } };
|
||||||
}
|
}
|
||||||
|
@ -124,6 +127,10 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
||||||
if (!user) return { redirect: { permanent: false, destination: "/auth/login" } };
|
if (!user) return { redirect: { permanent: false, destination: "/auth/login" } };
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: { session, user },
|
props: {
|
||||||
|
session,
|
||||||
|
user,
|
||||||
|
...(await serverSideTranslations(locale, ["common"])),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { CalendarIcon } from "@heroicons/react/outline";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
import { QueryCell } from "@lib/QueryCell";
|
import { QueryCell } from "@lib/QueryCell";
|
||||||
|
import { useLocale } from "@lib/hooks/useLocale";
|
||||||
import { inferQueryInput, trpc } from "@lib/trpc";
|
import { inferQueryInput, trpc } from "@lib/trpc";
|
||||||
|
|
||||||
import BookingsShell from "@components/BookingsShell";
|
import BookingsShell from "@components/BookingsShell";
|
||||||
|
@ -11,19 +12,21 @@ import BookingListItem from "@components/booking/BookingListItem";
|
||||||
|
|
||||||
type BookingListingStatus = inferQueryInput<"viewer.bookings">["status"];
|
type BookingListingStatus = inferQueryInput<"viewer.bookings">["status"];
|
||||||
|
|
||||||
|
export default function Bookings() {
|
||||||
|
const { t } = useLocale();
|
||||||
|
|
||||||
const descriptionByStatus: Record<BookingListingStatus, string> = {
|
const descriptionByStatus: Record<BookingListingStatus, string> = {
|
||||||
upcoming: "As soon as someone books a time with you it will show up here.",
|
upcoming: t("upcoming_bookings"),
|
||||||
past: "Your past bookings will show up here.",
|
past: t("past_bookings"),
|
||||||
cancelled: "Your cancelled bookings will show up here.",
|
cancelled: t("cancelled_bookings"),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Bookings() {
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const status = router.query?.status as BookingListingStatus;
|
const status = router.query?.status as BookingListingStatus;
|
||||||
const query = trpc.useQuery(["viewer.bookings", { status }]);
|
const query = trpc.useQuery(["viewer.bookings", { status }]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Shell heading="Bookings" subtitle="See upcoming and past events booked through your event type links.">
|
<Shell heading={t("bookings")} subtitle={t("bookings_description")}>
|
||||||
<BookingsShell>
|
<BookingsShell>
|
||||||
<div className="-mx-4 sm:mx-auto flex flex-col">
|
<div className="-mx-4 sm:mx-auto flex flex-col">
|
||||||
<div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
<div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
||||||
|
@ -44,8 +47,11 @@ export default function Bookings() {
|
||||||
empty={() => (
|
empty={() => (
|
||||||
<EmptyScreen
|
<EmptyScreen
|
||||||
Icon={CalendarIcon}
|
Icon={CalendarIcon}
|
||||||
headline={`No ${status} bookings, yet`}
|
headline={t("no_status_bookings_yet", { status: status })}
|
||||||
description={`You have no ${status} bookings. ${descriptionByStatus[status]}`}
|
description={t("no_status_bookings_yet_description", {
|
||||||
|
status: status,
|
||||||
|
description: descriptionByStatus[status],
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
import { XIcon } from "@heroicons/react/outline";
|
import { XIcon } from "@heroicons/react/outline";
|
||||||
import { ArrowRightIcon } from "@heroicons/react/solid";
|
import { ArrowRightIcon } from "@heroicons/react/solid";
|
||||||
|
|
||||||
|
import { useLocale } from "@lib/hooks/useLocale";
|
||||||
|
|
||||||
import { HeadSeo } from "@components/seo/head-seo";
|
import { HeadSeo } from "@components/seo/head-seo";
|
||||||
import Button from "@components/ui/Button";
|
import Button from "@components/ui/Button";
|
||||||
|
|
||||||
export default function NoMeetingFound() {
|
export default function NoMeetingFound() {
|
||||||
|
const { t } = useLocale();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<HeadSeo title={`No meeting found`} description={`No Meeting found`} />
|
<HeadSeo title={t("no_meeting_found")} description={t("no_meeting_found")} />
|
||||||
<main className="max-w-3xl mx-auto my-24">
|
<main className="max-w-3xl mx-auto my-24">
|
||||||
<div className="fixed inset-0 z-50 overflow-y-auto">
|
<div className="fixed inset-0 z-50 overflow-y-auto">
|
||||||
<div className="flex items-end justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
<div className="flex items-end justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
||||||
|
@ -26,19 +30,17 @@ export default function NoMeetingFound() {
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-3 text-center sm:mt-5">
|
<div className="mt-3 text-center sm:mt-5">
|
||||||
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-headline">
|
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-headline">
|
||||||
No Meeting Found
|
{t("no_meeting_found")}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<p className="text-sm text-center text-gray-500">
|
<p className="text-sm text-center text-gray-500">{t("no_meeting_found_description")}</p>
|
||||||
This meeting does not exist. Contact the meeting owner for an updated link.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5 text-center sm:mt-6">
|
<div className="mt-5 text-center sm:mt-6">
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<Button data-testid="return-home" href="/event-types" EndIcon={ArrowRightIcon}>
|
<Button data-testid="return-home" href="/event-types" EndIcon={ArrowRightIcon}>
|
||||||
Go back home
|
{t("go_back_home")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,9 +2,12 @@ import { CalendarIcon, XIcon } from "@heroicons/react/solid";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import utc from "dayjs/plugin/utc";
|
import utc from "dayjs/plugin/utc";
|
||||||
import { getSession } from "next-auth/client";
|
import { getSession } from "next-auth/client";
|
||||||
|
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
|
import { getOrSetUserLocaleFromHeaders } from "@lib/core/i18n/i18n.utils";
|
||||||
|
import { useLocale } from "@lib/hooks/useLocale";
|
||||||
import prisma from "@lib/prisma";
|
import prisma from "@lib/prisma";
|
||||||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@lib/telemetry";
|
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@lib/telemetry";
|
||||||
|
|
||||||
|
@ -14,6 +17,7 @@ import { Button } from "@components/ui/Button";
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
|
|
||||||
export default function Type(props) {
|
export default function Type(props) {
|
||||||
|
const { t } = useLocale();
|
||||||
// Get router variables
|
// Get router variables
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { uid } = router.query;
|
const { uid } = router.query;
|
||||||
|
@ -21,7 +25,7 @@ export default function Type(props) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const [is24h, setIs24h] = useState(false);
|
const [is24h, setIs24h] = useState(false);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState(props.booking ? null : "This booking was already cancelled");
|
const [error, setError] = useState(props.booking ? null : t("booking_already_cancelled"));
|
||||||
const telemetry = useTelemetry();
|
const telemetry = useTelemetry();
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
@ -52,15 +56,15 @@ export default function Type(props) {
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setError("An error with status code " + res.status + " occurred. Please try again later.");
|
setError(`${t("error_with_status_code_occured", { status: res.status })} ${t("please_try_again")}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<HeadSeo
|
<HeadSeo
|
||||||
title={`Cancel ${props.booking && props.booking.title} | ${props.profile.name}`}
|
title={`${t("cancel")} ${props.booking && props.booking.title} | ${props.profile.name}`}
|
||||||
description={`Cancel ${props.booking && props.booking.title} | ${props.profile.name}`}
|
description={`${t("cancel")} ${props.booking && props.booking.title} | ${props.profile.name}`}
|
||||||
/>
|
/>
|
||||||
<main className="max-w-3xl mx-auto my-24">
|
<main className="max-w-3xl mx-auto my-24">
|
||||||
<div className="fixed inset-0 z-50 overflow-y-auto">
|
<div className="fixed inset-0 z-50 overflow-y-auto">
|
||||||
|
@ -95,14 +99,12 @@ export default function Type(props) {
|
||||||
<div className="mt-3 text-center sm:mt-5">
|
<div className="mt-3 text-center sm:mt-5">
|
||||||
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-headline">
|
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-headline">
|
||||||
{props.cancellationAllowed
|
{props.cancellationAllowed
|
||||||
? "Really cancel your booking?"
|
? t("really_cancel_booking")
|
||||||
: "You cannot cancel this booking"}
|
: t("cannot_cancel_booking")}
|
||||||
</h3>
|
</h3>
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<p className="text-sm text-gray-500">
|
<p className="text-sm text-gray-500">
|
||||||
{props.cancellationAllowed
|
{props.cancellationAllowed ? t("reschedule_instead") : t("event_is_in_the_past")}
|
||||||
? "Instead, you could also reschedule it."
|
|
||||||
: "The event is in the past"}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="py-4 mt-4 border-t border-b">
|
<div className="py-4 mt-4 border-t border-b">
|
||||||
|
@ -125,9 +127,9 @@ export default function Type(props) {
|
||||||
data-testid="cancel"
|
data-testid="cancel"
|
||||||
onClick={cancellationHandler}
|
onClick={cancellationHandler}
|
||||||
loading={loading}>
|
loading={loading}>
|
||||||
Cancel
|
{t("cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => router.push("/reschedule/" + uid)}>Reschedule</Button>
|
<Button onClick={() => router.push("/reschedule/" + uid)}>{t("reschedule")}</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -143,6 +145,7 @@ export default function Type(props) {
|
||||||
|
|
||||||
export async function getServerSideProps(context) {
|
export async function getServerSideProps(context) {
|
||||||
const session = await getSession(context);
|
const session = await getSession(context);
|
||||||
|
const locale = await getOrSetUserLocaleFromHeaders(context.req);
|
||||||
const booking = await prisma.booking.findUnique({
|
const booking = await prisma.booking.findUnique({
|
||||||
where: {
|
where: {
|
||||||
uid: context.query.uid,
|
uid: context.query.uid,
|
||||||
|
@ -199,6 +202,7 @@ export async function getServerSideProps(context) {
|
||||||
booking: bookingObj,
|
booking: bookingObj,
|
||||||
cancellationAllowed:
|
cancellationAllowed:
|
||||||
(!!session?.user && session.user.id == booking.user?.id) || booking.startTime >= new Date(),
|
(!!session?.user && session.user.id == booking.user?.id) || booking.startTime >= new Date(),
|
||||||
|
...(await serverSideTranslations(locale, ["common"])),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,17 +3,23 @@ import { ArrowRightIcon } from "@heroicons/react/solid";
|
||||||
import { useSession } from "next-auth/client";
|
import { useSession } from "next-auth/client";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
|
import { useLocale } from "@lib/hooks/useLocale";
|
||||||
|
|
||||||
import { HeadSeo } from "@components/seo/head-seo";
|
import { HeadSeo } from "@components/seo/head-seo";
|
||||||
import Button from "@components/ui/Button";
|
import Button from "@components/ui/Button";
|
||||||
|
|
||||||
export default function CancelSuccess() {
|
export default function CancelSuccess() {
|
||||||
|
const { t } = useLocale();
|
||||||
// Get router variables
|
// Get router variables
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { title, name, eventPage } = router.query;
|
const { title, name, eventPage } = router.query;
|
||||||
const [session, loading] = useSession();
|
const [session, loading] = useSession();
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<HeadSeo title={`Cancelled ${title} | ${name}`} description={`Cancelled ${title} | ${name}`} />
|
<HeadSeo
|
||||||
|
title={`${t("cancelled")} ${title} | ${name}`}
|
||||||
|
description={`${t("cancelled")} ${title} | ${name}`}
|
||||||
|
/>
|
||||||
<main className="max-w-3xl mx-auto my-24">
|
<main className="max-w-3xl mx-auto my-24">
|
||||||
<div className="fixed z-50 inset-0 overflow-y-auto">
|
<div className="fixed z-50 inset-0 overflow-y-auto">
|
||||||
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||||
|
@ -32,11 +38,11 @@ export default function CancelSuccess() {
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-3 text-center sm:mt-5">
|
<div className="mt-3 text-center sm:mt-5">
|
||||||
<h3 className="text-lg leading-6 font-medium text-gray-900" id="modal-headline">
|
<h3 className="text-lg leading-6 font-medium text-gray-900" id="modal-headline">
|
||||||
Cancellation successful
|
{t("cancellation_successful")}
|
||||||
</h3>
|
</h3>
|
||||||
{!loading && !session.user && (
|
{!loading && !session.user && (
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<p className="text-sm text-gray-500">Feel free to pick another event anytime.</p>
|
<p className="text-sm text-gray-500">{t("free_to_pick_another_event_type")}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -46,7 +52,7 @@ export default function CancelSuccess() {
|
||||||
{!loading && !session.user && <Button href={eventPage}>Pick another</Button>}
|
{!loading && !session.user && <Button href={eventPage}>Pick another</Button>}
|
||||||
{!loading && session.user && (
|
{!loading && session.user && (
|
||||||
<Button data-testid="back-to-bookings" href="/bookings" EndIcon={ArrowRightIcon}>
|
<Button data-testid="back-to-bookings" href="/bookings" EndIcon={ArrowRightIcon}>
|
||||||
Back to bookings
|
{t("back_to_bookings")}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,45 @@
|
||||||
{
|
{
|
||||||
|
"back_to_bookings": "Back to bookings",
|
||||||
|
"free_to_pick_another_event_type": "Feel free to pick another event anytime.",
|
||||||
|
"cancelled": "Cancelled",
|
||||||
|
"cancellation_successful": "Cancellation successful",
|
||||||
|
"really_cancel_booking": "Really cancel your booking?",
|
||||||
|
"cannot_cancel_booking": "You cannot cancel this booking",
|
||||||
|
"reschedule_instead": "Instead, you could also reschedule it.",
|
||||||
|
"event_is_in_the_past": "The event is in the past",
|
||||||
|
"error_with_status_code_occured": "An error with status code {{status}} occurred.",
|
||||||
|
"booking_already_cancelled": "This booking was already cancelled",
|
||||||
|
"go_back_home": "Go back home",
|
||||||
|
"no_meeting_found": "No Meeting Found",
|
||||||
|
"no_meeting_found_description": "This meeting does not exist. Contact the meeting owner for an updated link.",
|
||||||
|
"no_status_bookings_yet": "No {{status}} bookings, yet",
|
||||||
|
"no_status_bookings_yet_description": "You have no {{status}} bookings. {{description}}",
|
||||||
|
"bookings": "Bookings",
|
||||||
|
"bookings_description": "See upcoming and past events booked through your event type links.",
|
||||||
|
"upcoming_bookings": "As soon as someone books a time with you it will show up here.",
|
||||||
|
"past_bookings": "Your past bookings will show up here.",
|
||||||
|
"cancelled_bookings": "Your cancelled bookings will show up here.",
|
||||||
|
"on": "on",
|
||||||
|
"and": "and",
|
||||||
|
"calendar_shows_busy_between": "Your calendar shows you as busy between",
|
||||||
|
"troubleshoot": "Troubleshoot",
|
||||||
|
"troubleshoot_description": "Understand why certain times are available and others are blocked.",
|
||||||
|
"overview_of_day": "Here is an overview of your day on",
|
||||||
|
"hover_over_bold_times_tip": "Tip: Hover over the bold times for a full timestamp",
|
||||||
|
"start_time": "Start time",
|
||||||
|
"end_time": "End time",
|
||||||
|
"buffer": "Buffer",
|
||||||
|
"your_day_starts_at": "Your day starts at",
|
||||||
|
"your_day_ends_at": "Your day ends at",
|
||||||
|
"launch_troubleshooter": "Launch troubleshooter",
|
||||||
|
"troubleshoot_availability": "Troubleshoot your availability to explore why your times are showing as they are.",
|
||||||
|
"change_available_times": "Change available times",
|
||||||
|
"change_your_available_times": "Change your available times",
|
||||||
|
"change_start_end": "Change the start and end times of your day",
|
||||||
|
"change_start_end_buffer": "Set the start and end time of your day and a minimum buffer between your meetings.",
|
||||||
|
"current_start_date": "Currently, your day is set to start at",
|
||||||
|
"start_end_changed_successfully": "The start and end times for your day have been changed successfully.",
|
||||||
|
"and_end_at": "and end at",
|
||||||
"light": "Light",
|
"light": "Light",
|
||||||
"dark": "Dark",
|
"dark": "Dark",
|
||||||
"automatically_adjust_theme": "Automatically adjust theme based on invitee preferences",
|
"automatically_adjust_theme": "Automatically adjust theme based on invitee preferences",
|
||||||
|
@ -50,6 +91,7 @@
|
||||||
"password_has_been_changed": "Your password has been successfully changed.",
|
"password_has_been_changed": "Your password has been successfully changed.",
|
||||||
"error_changing_password": "Error changing password",
|
"error_changing_password": "Error changing password",
|
||||||
"something_went_wrong": "Something went wrong",
|
"something_went_wrong": "Something went wrong",
|
||||||
|
"something_doesnt_look_right": "Something doesn't look right?",
|
||||||
"please_try_again": "Please try again",
|
"please_try_again": "Please try again",
|
||||||
"super_secure_new_password": "Your super secure new password",
|
"super_secure_new_password": "Your super secure new password",
|
||||||
"new_password": "New Password",
|
"new_password": "New Password",
|
||||||
|
@ -158,7 +200,7 @@
|
||||||
"collective": "Collective",
|
"collective": "Collective",
|
||||||
"collective_description": "Schedule meetings when all selected team members are available.",
|
"collective_description": "Schedule meetings when all selected team members are available.",
|
||||||
"duration": "Duration",
|
"duration": "Duration",
|
||||||
"minutes": "minutes",
|
"minutes": "Minutes",
|
||||||
"round_robin": "Round Robin",
|
"round_robin": "Round Robin",
|
||||||
"round_robin_description": "Cycle meetings between multiple team members.",
|
"round_robin_description": "Cycle meetings between multiple team members.",
|
||||||
"url": "URL",
|
"url": "URL",
|
||||||
|
@ -210,7 +252,7 @@
|
||||||
"billing": "Billing",
|
"billing": "Billing",
|
||||||
"manage_your_billing_info": "Manage your billing information and cancel your subscription.",
|
"manage_your_billing_info": "Manage your billing information and cancel your subscription.",
|
||||||
"availability": "Availability",
|
"availability": "Availability",
|
||||||
"configure_times_available_bookings": "Configure times when you are available for bookings.",
|
"configure_availability": "Configure times when you are available for bookings.",
|
||||||
"change_weekly_schedule": "Change your weekly schedule",
|
"change_weekly_schedule": "Change your weekly schedule",
|
||||||
"logo": "Logo",
|
"logo": "Logo",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
|
|
Loading…
Reference in a new issue