From 652c2e342fcecd180f9db1183eafe70d35a27f53 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Tue, 22 Feb 2022 23:19:22 +0000 Subject: [PATCH 1/6] Second go at removing Zod generated classes from our repo (#1946) * Second go at removing Zod generated classes from our repo * Directly reference the _EventTypeModel --- .gitignore | 6 +- .../components/eventtype/CreateEventType.tsx | 2 +- apps/web/server/routers/viewer/eventTypes.tsx | 2 +- packages/prisma/zod/attendee.ts | 25 ------- packages/prisma/zod/availability.ts | 29 -------- packages/prisma/zod/booking.ts | 49 ------------- packages/prisma/zod/bookingreference.ts | 26 ------- packages/prisma/zod/credential.ts | 29 -------- packages/prisma/zod/custom/eventtype.ts | 15 ++++ packages/prisma/zod/dailyeventreference.ts | 23 ------ packages/prisma/zod/destinationcalendar.ts | 29 -------- packages/prisma/zod/eventtype.ts | 63 ----------------- packages/prisma/zod/eventtypeCustom.ts | 17 ----- packages/prisma/zod/eventtypecustominput.ts | 26 ------- packages/prisma/zod/index.ts | 19 ----- packages/prisma/zod/membership.ts | 26 ------- packages/prisma/zod/payment.ts | 37 ---------- packages/prisma/zod/remindermail.ts | 11 --- packages/prisma/zod/resetpasswordrequest.ts | 10 --- packages/prisma/zod/schedule.ts | 32 --------- packages/prisma/zod/selectedcalendar.ts | 22 ------ packages/prisma/zod/team.ts | 27 ------- packages/prisma/zod/user.ts | 70 ------------------- packages/prisma/zod/verificationrequest.ts | 11 --- packages/prisma/zod/webhook.ts | 27 ------- 25 files changed, 22 insertions(+), 611 deletions(-) delete mode 100644 packages/prisma/zod/attendee.ts delete mode 100644 packages/prisma/zod/availability.ts delete mode 100644 packages/prisma/zod/booking.ts delete mode 100644 packages/prisma/zod/bookingreference.ts delete mode 100644 packages/prisma/zod/credential.ts create mode 100644 packages/prisma/zod/custom/eventtype.ts delete mode 100644 packages/prisma/zod/dailyeventreference.ts delete mode 100644 packages/prisma/zod/destinationcalendar.ts delete mode 100644 packages/prisma/zod/eventtype.ts delete mode 100644 packages/prisma/zod/eventtypeCustom.ts delete mode 100644 packages/prisma/zod/eventtypecustominput.ts delete mode 100644 packages/prisma/zod/index.ts delete mode 100644 packages/prisma/zod/membership.ts delete mode 100644 packages/prisma/zod/payment.ts delete mode 100644 packages/prisma/zod/remindermail.ts delete mode 100644 packages/prisma/zod/resetpasswordrequest.ts delete mode 100644 packages/prisma/zod/schedule.ts delete mode 100644 packages/prisma/zod/selectedcalendar.ts delete mode 100644 packages/prisma/zod/team.ts delete mode 100644 packages/prisma/zod/user.ts delete mode 100644 packages/prisma/zod/verificationrequest.ts delete mode 100644 packages/prisma/zod/webhook.ts diff --git a/.gitignore b/.gitignore index ea33396e7f..5780dbbcca 100644 --- a/.gitignore +++ b/.gitignore @@ -59,5 +59,9 @@ yarn-error.log* # Typescript tsconfig.tsbuildinfo + # turbo -.turbo \ No newline at end of file +.turbo + +# Prisma-Zod +packages/prisma/zod/*.ts diff --git a/apps/web/components/eventtype/CreateEventType.tsx b/apps/web/components/eventtype/CreateEventType.tsx index 03c9272f0c..1739bbb57a 100644 --- a/apps/web/components/eventtype/CreateEventType.tsx +++ b/apps/web/components/eventtype/CreateEventType.tsx @@ -6,7 +6,7 @@ import React, { useEffect } from "react"; import { useForm } from "react-hook-form"; import type { z } from "zod"; -import { createEventTypeInput } from "@calcom/prisma/zod/eventtypeCustom"; +import { createEventTypeInput } from "@calcom/prisma/zod/custom/eventtype"; import { HttpError } from "@lib/core/http/error"; import { useLocale } from "@lib/hooks/useLocale"; diff --git a/apps/web/server/routers/viewer/eventTypes.tsx b/apps/web/server/routers/viewer/eventTypes.tsx index b6519406d2..c722a9bc93 100644 --- a/apps/web/server/routers/viewer/eventTypes.tsx +++ b/apps/web/server/routers/viewer/eventTypes.tsx @@ -8,7 +8,7 @@ import { _EventTypeModel, } from "@calcom/prisma/zod"; import { stringOrNumber } from "@calcom/prisma/zod-utils"; -import { createEventTypeInput } from "@calcom/prisma/zod/eventtypeCustom"; +import { createEventTypeInput } from "@calcom/prisma/zod/custom/eventtype"; import { createProtectedRouter } from "@server/createRouter"; import { viewerRouter } from "@server/routers/viewer"; diff --git a/packages/prisma/zod/attendee.ts b/packages/prisma/zod/attendee.ts deleted file mode 100644 index 7994803550..0000000000 --- a/packages/prisma/zod/attendee.ts +++ /dev/null @@ -1,25 +0,0 @@ -import * as z from "zod" -import * as imports from "../zod-utils" -import { CompleteBooking, BookingModel } from "./index" - -export const _AttendeeModel = z.object({ - id: z.number().int(), - email: z.string(), - name: z.string(), - timeZone: z.string(), - locale: z.string().nullish(), - bookingId: z.number().int().nullish(), -}) - -export interface CompleteAttendee extends z.infer { - booking?: CompleteBooking | null -} - -/** - * AttendeeModel contains all relations on your model in addition to the scalars - * - * NOTE: Lazy required in case of potential circular dependencies within schema - */ -export const AttendeeModel: z.ZodSchema = z.lazy(() => _AttendeeModel.extend({ - booking: BookingModel.nullish(), -})) diff --git a/packages/prisma/zod/availability.ts b/packages/prisma/zod/availability.ts deleted file mode 100644 index e9adc405a8..0000000000 --- a/packages/prisma/zod/availability.ts +++ /dev/null @@ -1,29 +0,0 @@ -import * as z from "zod" -import * as imports from "../zod-utils" -import { CompleteUser, UserModel, CompleteEventType, EventTypeModel } from "./index" - -export const _AvailabilityModel = z.object({ - id: z.number().int(), - label: z.string().nullish(), - userId: z.number().int().nullish(), - eventTypeId: z.number().int().nullish(), - days: z.number().int().array(), - startTime: z.date(), - endTime: z.date(), - date: z.date().nullish(), -}) - -export interface CompleteAvailability extends z.infer { - user?: CompleteUser | null - eventType?: CompleteEventType | null -} - -/** - * AvailabilityModel contains all relations on your model in addition to the scalars - * - * NOTE: Lazy required in case of potential circular dependencies within schema - */ -export const AvailabilityModel: z.ZodSchema = z.lazy(() => _AvailabilityModel.extend({ - user: UserModel.nullish(), - eventType: EventTypeModel.nullish(), -})) diff --git a/packages/prisma/zod/booking.ts b/packages/prisma/zod/booking.ts deleted file mode 100644 index e00b8e894f..0000000000 --- a/packages/prisma/zod/booking.ts +++ /dev/null @@ -1,49 +0,0 @@ -import * as z from "zod" -import * as imports from "../zod-utils" -import { BookingStatus } from "@prisma/client" -import { CompleteUser, UserModel, CompleteBookingReference, BookingReferenceModel, CompleteEventType, EventTypeModel, CompleteAttendee, AttendeeModel, CompleteDailyEventReference, DailyEventReferenceModel, CompletePayment, PaymentModel, CompleteDestinationCalendar, DestinationCalendarModel } from "./index" - -export const _BookingModel = z.object({ - id: z.number().int(), - uid: z.string(), - userId: z.number().int().nullish(), - eventTypeId: z.number().int().nullish(), - title: z.string(), - description: z.string().nullish(), - startTime: z.date(), - endTime: z.date(), - location: z.string().nullish(), - createdAt: z.date(), - updatedAt: z.date().nullish(), - confirmed: z.boolean(), - rejected: z.boolean(), - status: z.nativeEnum(BookingStatus), - paid: z.boolean(), - cancellationReason: z.string().nullish(), - rejectionReason: z.string().nullish(), -}) - -export interface CompleteBooking extends z.infer { - user?: CompleteUser | null - references: CompleteBookingReference[] - eventType?: CompleteEventType | null - attendees: CompleteAttendee[] - dailyRef?: CompleteDailyEventReference | null - payment: CompletePayment[] - destinationCalendar?: CompleteDestinationCalendar | null -} - -/** - * BookingModel contains all relations on your model in addition to the scalars - * - * NOTE: Lazy required in case of potential circular dependencies within schema - */ -export const BookingModel: z.ZodSchema = z.lazy(() => _BookingModel.extend({ - user: UserModel.nullish(), - references: BookingReferenceModel.array(), - eventType: EventTypeModel.nullish(), - attendees: AttendeeModel.array(), - dailyRef: DailyEventReferenceModel.nullish(), - payment: PaymentModel.array(), - destinationCalendar: DestinationCalendarModel.nullish(), -})) diff --git a/packages/prisma/zod/bookingreference.ts b/packages/prisma/zod/bookingreference.ts deleted file mode 100644 index 3f0ace2a61..0000000000 --- a/packages/prisma/zod/bookingreference.ts +++ /dev/null @@ -1,26 +0,0 @@ -import * as z from "zod" -import * as imports from "../zod-utils" -import { CompleteBooking, BookingModel } from "./index" - -export const _BookingReferenceModel = z.object({ - id: z.number().int(), - type: z.string(), - uid: z.string(), - meetingId: z.string().nullish(), - meetingPassword: z.string().nullish(), - meetingUrl: z.string().nullish(), - bookingId: z.number().int().nullish(), -}) - -export interface CompleteBookingReference extends z.infer { - booking?: CompleteBooking | null -} - -/** - * BookingReferenceModel contains all relations on your model in addition to the scalars - * - * NOTE: Lazy required in case of potential circular dependencies within schema - */ -export const BookingReferenceModel: z.ZodSchema = z.lazy(() => _BookingReferenceModel.extend({ - booking: BookingModel.nullish(), -})) diff --git a/packages/prisma/zod/credential.ts b/packages/prisma/zod/credential.ts deleted file mode 100644 index af4371e144..0000000000 --- a/packages/prisma/zod/credential.ts +++ /dev/null @@ -1,29 +0,0 @@ -import * as z from "zod" -import * as imports from "../zod-utils" -import { CompleteUser, UserModel } from "./index" - -// Helper schema for JSON fields -type Literal = boolean | number | string -type Json = Literal | { [key: string]: Json } | Json[] -const literalSchema = z.union([z.string(), z.number(), z.boolean()]) -const jsonSchema: z.ZodSchema = z.lazy(() => z.union([literalSchema, z.array(jsonSchema), z.record(jsonSchema)])) - -export const _CredentialModel = z.object({ - id: z.number().int(), - type: z.string(), - key: jsonSchema, - userId: z.number().int().nullish(), -}) - -export interface CompleteCredential extends z.infer { - user?: CompleteUser | null -} - -/** - * CredentialModel contains all relations on your model in addition to the scalars - * - * NOTE: Lazy required in case of potential circular dependencies within schema - */ -export const CredentialModel: z.ZodSchema = z.lazy(() => _CredentialModel.extend({ - user: UserModel.nullish(), -})) diff --git a/packages/prisma/zod/custom/eventtype.ts b/packages/prisma/zod/custom/eventtype.ts new file mode 100644 index 0000000000..ee13e2d243 --- /dev/null +++ b/packages/prisma/zod/custom/eventtype.ts @@ -0,0 +1,15 @@ +import { _EventTypeModel } from "../eventtype"; + +export const createEventTypeInput = _EventTypeModel + .pick({ + title: true, + slug: true, + description: true, + length: true, + teamId: true, + schedulingType: true, + }) + .refine((data) => (data.teamId ? data.teamId && data.schedulingType : true), { + path: ["schedulingType"], + message: "You must select a scheduling type for team events", + }); \ No newline at end of file diff --git a/packages/prisma/zod/dailyeventreference.ts b/packages/prisma/zod/dailyeventreference.ts deleted file mode 100644 index 93ed654efb..0000000000 --- a/packages/prisma/zod/dailyeventreference.ts +++ /dev/null @@ -1,23 +0,0 @@ -import * as z from "zod" -import * as imports from "../zod-utils" -import { CompleteBooking, BookingModel } from "./index" - -export const _DailyEventReferenceModel = z.object({ - id: z.number().int(), - dailyurl: z.string(), - dailytoken: z.string(), - bookingId: z.number().int().nullish(), -}) - -export interface CompleteDailyEventReference extends z.infer { - booking?: CompleteBooking | null -} - -/** - * DailyEventReferenceModel contains all relations on your model in addition to the scalars - * - * NOTE: Lazy required in case of potential circular dependencies within schema - */ -export const DailyEventReferenceModel: z.ZodSchema = z.lazy(() => _DailyEventReferenceModel.extend({ - booking: BookingModel.nullish(), -})) diff --git a/packages/prisma/zod/destinationcalendar.ts b/packages/prisma/zod/destinationcalendar.ts deleted file mode 100644 index 4160bdfca5..0000000000 --- a/packages/prisma/zod/destinationcalendar.ts +++ /dev/null @@ -1,29 +0,0 @@ -import * as z from "zod" -import * as imports from "../zod-utils" -import { CompleteUser, UserModel, CompleteBooking, BookingModel, CompleteEventType, EventTypeModel } from "./index" - -export const _DestinationCalendarModel = z.object({ - id: z.number().int(), - integration: z.string(), - externalId: z.string(), - userId: z.number().int().nullish(), - bookingId: z.number().int().nullish(), - eventTypeId: z.number().int().nullish(), -}) - -export interface CompleteDestinationCalendar extends z.infer { - user?: CompleteUser | null - booking?: CompleteBooking | null - eventType?: CompleteEventType | null -} - -/** - * DestinationCalendarModel contains all relations on your model in addition to the scalars - * - * NOTE: Lazy required in case of potential circular dependencies within schema - */ -export const DestinationCalendarModel: z.ZodSchema = z.lazy(() => _DestinationCalendarModel.extend({ - user: UserModel.nullish(), - booking: BookingModel.nullish(), - eventType: EventTypeModel.nullish(), -})) diff --git a/packages/prisma/zod/eventtype.ts b/packages/prisma/zod/eventtype.ts deleted file mode 100644 index 33000ad628..0000000000 --- a/packages/prisma/zod/eventtype.ts +++ /dev/null @@ -1,63 +0,0 @@ -import * as z from "zod" -import * as imports from "../zod-utils" -import { PeriodType, SchedulingType } from "@prisma/client" -import { CompleteUser, UserModel, CompleteTeam, TeamModel, CompleteBooking, BookingModel, CompleteAvailability, AvailabilityModel, CompleteDestinationCalendar, DestinationCalendarModel, CompleteEventTypeCustomInput, EventTypeCustomInputModel, CompleteSchedule, ScheduleModel } from "./index" - -// Helper schema for JSON fields -type Literal = boolean | number | string -type Json = Literal | { [key: string]: Json } | Json[] -const literalSchema = z.union([z.string(), z.number(), z.boolean()]) -const jsonSchema: z.ZodSchema = z.lazy(() => z.union([literalSchema, z.array(jsonSchema), z.record(jsonSchema)])) - -export const _EventTypeModel = z.object({ - id: z.number().int(), - title: z.string().nonempty(), - slug: imports.eventTypeSlug, - description: z.string().nullish(), - position: z.number().int(), - locations: imports.eventTypeLocations, - length: z.number().int(), - hidden: z.boolean(), - userId: z.number().int().nullish(), - teamId: z.number().int().nullish(), - eventName: z.string().nullish(), - timeZone: z.string().nullish(), - periodType: z.nativeEnum(PeriodType), - periodStartDate: z.date().nullish(), - periodEndDate: z.date().nullish(), - periodDays: z.number().int().nullish(), - periodCountCalendarDays: z.boolean().nullish(), - requiresConfirmation: z.boolean(), - disableGuests: z.boolean(), - minimumBookingNotice: z.number().int(), - schedulingType: z.nativeEnum(SchedulingType).nullish(), - price: z.number().int(), - currency: z.string(), - slotInterval: z.number().int().nullish(), - metadata: jsonSchema, -}) - -export interface CompleteEventType extends z.infer { - users: CompleteUser[] - team?: CompleteTeam | null - bookings: CompleteBooking[] - availability: CompleteAvailability[] - destinationCalendar?: CompleteDestinationCalendar | null - customInputs: CompleteEventTypeCustomInput[] - Schedule: CompleteSchedule[] -} - -/** - * EventTypeModel contains all relations on your model in addition to the scalars - * - * NOTE: Lazy required in case of potential circular dependencies within schema - */ -export const EventTypeModel: z.ZodSchema = z.lazy(() => _EventTypeModel.extend({ - users: UserModel.array(), - team: TeamModel.nullish(), - bookings: BookingModel.array(), - availability: AvailabilityModel.array(), - destinationCalendar: DestinationCalendarModel.nullish(), - customInputs: EventTypeCustomInputModel.array(), - Schedule: ScheduleModel.array(), -})) diff --git a/packages/prisma/zod/eventtypeCustom.ts b/packages/prisma/zod/eventtypeCustom.ts deleted file mode 100644 index 0f9c1e6f8d..0000000000 --- a/packages/prisma/zod/eventtypeCustom.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { _EventTypeModel } from "./eventtype"; - -const createEventTypeBaseInput = _EventTypeModel - .pick({ - title: true, - slug: true, - description: true, - length: true, - teamId: true, - schedulingType: true, - }) - .refine((data) => (data.teamId ? data.teamId && data.schedulingType : true), { - path: ["schedulingType"], - message: "You must select a scheduling type for team events", - }); - -export const createEventTypeInput = createEventTypeBaseInput; diff --git a/packages/prisma/zod/eventtypecustominput.ts b/packages/prisma/zod/eventtypecustominput.ts deleted file mode 100644 index 04535b146e..0000000000 --- a/packages/prisma/zod/eventtypecustominput.ts +++ /dev/null @@ -1,26 +0,0 @@ -import * as z from "zod" -import * as imports from "../zod-utils" -import { EventTypeCustomInputType } from "@prisma/client" -import { CompleteEventType, EventTypeModel } from "./index" - -export const _EventTypeCustomInputModel = z.object({ - id: z.number().int(), - eventTypeId: z.number().int(), - label: z.string(), - type: z.nativeEnum(EventTypeCustomInputType), - required: z.boolean(), - placeholder: z.string(), -}) - -export interface CompleteEventTypeCustomInput extends z.infer { - eventType: CompleteEventType -} - -/** - * EventTypeCustomInputModel contains all relations on your model in addition to the scalars - * - * NOTE: Lazy required in case of potential circular dependencies within schema - */ -export const EventTypeCustomInputModel: z.ZodSchema = z.lazy(() => _EventTypeCustomInputModel.extend({ - eventType: EventTypeModel, -})) diff --git a/packages/prisma/zod/index.ts b/packages/prisma/zod/index.ts deleted file mode 100644 index a4a8226e06..0000000000 --- a/packages/prisma/zod/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -export * from "./eventtype" -export * from "./credential" -export * from "./destinationcalendar" -export * from "./user" -export * from "./team" -export * from "./membership" -export * from "./verificationrequest" -export * from "./bookingreference" -export * from "./attendee" -export * from "./dailyeventreference" -export * from "./booking" -export * from "./schedule" -export * from "./availability" -export * from "./selectedcalendar" -export * from "./eventtypecustominput" -export * from "./resetpasswordrequest" -export * from "./remindermail" -export * from "./payment" -export * from "./webhook" diff --git a/packages/prisma/zod/membership.ts b/packages/prisma/zod/membership.ts deleted file mode 100644 index f256281466..0000000000 --- a/packages/prisma/zod/membership.ts +++ /dev/null @@ -1,26 +0,0 @@ -import * as z from "zod" -import * as imports from "../zod-utils" -import { MembershipRole } from "@prisma/client" -import { CompleteTeam, TeamModel, CompleteUser, UserModel } from "./index" - -export const _MembershipModel = z.object({ - teamId: z.number().int(), - userId: z.number().int(), - accepted: z.boolean(), - role: z.nativeEnum(MembershipRole), -}) - -export interface CompleteMembership extends z.infer { - team: CompleteTeam - user: CompleteUser -} - -/** - * MembershipModel contains all relations on your model in addition to the scalars - * - * NOTE: Lazy required in case of potential circular dependencies within schema - */ -export const MembershipModel: z.ZodSchema = z.lazy(() => _MembershipModel.extend({ - team: TeamModel, - user: UserModel, -})) diff --git a/packages/prisma/zod/payment.ts b/packages/prisma/zod/payment.ts deleted file mode 100644 index 7fe5172ce1..0000000000 --- a/packages/prisma/zod/payment.ts +++ /dev/null @@ -1,37 +0,0 @@ -import * as z from "zod" -import * as imports from "../zod-utils" -import { PaymentType } from "@prisma/client" -import { CompleteBooking, BookingModel } from "./index" - -// Helper schema for JSON fields -type Literal = boolean | number | string -type Json = Literal | { [key: string]: Json } | Json[] -const literalSchema = z.union([z.string(), z.number(), z.boolean()]) -const jsonSchema: z.ZodSchema = z.lazy(() => z.union([literalSchema, z.array(jsonSchema), z.record(jsonSchema)])) - -export const _PaymentModel = z.object({ - id: z.number().int(), - uid: z.string(), - type: z.nativeEnum(PaymentType), - bookingId: z.number().int(), - amount: z.number().int(), - fee: z.number().int(), - currency: z.string(), - success: z.boolean(), - refunded: z.boolean(), - data: jsonSchema, - externalId: z.string(), -}) - -export interface CompletePayment extends z.infer { - booking?: CompleteBooking | null -} - -/** - * PaymentModel contains all relations on your model in addition to the scalars - * - * NOTE: Lazy required in case of potential circular dependencies within schema - */ -export const PaymentModel: z.ZodSchema = z.lazy(() => _PaymentModel.extend({ - booking: BookingModel.nullish(), -})) diff --git a/packages/prisma/zod/remindermail.ts b/packages/prisma/zod/remindermail.ts deleted file mode 100644 index 3a74896de4..0000000000 --- a/packages/prisma/zod/remindermail.ts +++ /dev/null @@ -1,11 +0,0 @@ -import * as z from "zod" -import * as imports from "../zod-utils" -import { ReminderType } from "@prisma/client" - -export const _ReminderMailModel = z.object({ - id: z.number().int(), - referenceId: z.number().int(), - reminderType: z.nativeEnum(ReminderType), - elapsedMinutes: z.number().int(), - createdAt: z.date(), -}) diff --git a/packages/prisma/zod/resetpasswordrequest.ts b/packages/prisma/zod/resetpasswordrequest.ts deleted file mode 100644 index c6e8a42247..0000000000 --- a/packages/prisma/zod/resetpasswordrequest.ts +++ /dev/null @@ -1,10 +0,0 @@ -import * as z from "zod" -import * as imports from "../zod-utils" - -export const _ResetPasswordRequestModel = z.object({ - id: z.string(), - createdAt: z.date(), - updatedAt: z.date(), - email: z.string(), - expires: z.date(), -}) diff --git a/packages/prisma/zod/schedule.ts b/packages/prisma/zod/schedule.ts deleted file mode 100644 index d5c35dc664..0000000000 --- a/packages/prisma/zod/schedule.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as z from "zod" -import * as imports from "../zod-utils" -import { CompleteUser, UserModel, CompleteEventType, EventTypeModel } from "./index" - -// Helper schema for JSON fields -type Literal = boolean | number | string -type Json = Literal | { [key: string]: Json } | Json[] -const literalSchema = z.union([z.string(), z.number(), z.boolean()]) -const jsonSchema: z.ZodSchema = z.lazy(() => z.union([literalSchema, z.array(jsonSchema), z.record(jsonSchema)])) - -export const _ScheduleModel = z.object({ - id: z.number().int(), - userId: z.number().int().nullish(), - eventTypeId: z.number().int().nullish(), - title: z.string().nullish(), - freeBusyTimes: jsonSchema, -}) - -export interface CompleteSchedule extends z.infer { - user?: CompleteUser | null - eventType?: CompleteEventType | null -} - -/** - * ScheduleModel contains all relations on your model in addition to the scalars - * - * NOTE: Lazy required in case of potential circular dependencies within schema - */ -export const ScheduleModel: z.ZodSchema = z.lazy(() => _ScheduleModel.extend({ - user: UserModel.nullish(), - eventType: EventTypeModel.nullish(), -})) diff --git a/packages/prisma/zod/selectedcalendar.ts b/packages/prisma/zod/selectedcalendar.ts deleted file mode 100644 index cb09e7e0fc..0000000000 --- a/packages/prisma/zod/selectedcalendar.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as z from "zod" -import * as imports from "../zod-utils" -import { CompleteUser, UserModel } from "./index" - -export const _SelectedCalendarModel = z.object({ - userId: z.number().int(), - integration: z.string(), - externalId: z.string(), -}) - -export interface CompleteSelectedCalendar extends z.infer { - user: CompleteUser -} - -/** - * SelectedCalendarModel contains all relations on your model in addition to the scalars - * - * NOTE: Lazy required in case of potential circular dependencies within schema - */ -export const SelectedCalendarModel: z.ZodSchema = z.lazy(() => _SelectedCalendarModel.extend({ - user: UserModel, -})) diff --git a/packages/prisma/zod/team.ts b/packages/prisma/zod/team.ts deleted file mode 100644 index 0795d03e58..0000000000 --- a/packages/prisma/zod/team.ts +++ /dev/null @@ -1,27 +0,0 @@ -import * as z from "zod" -import * as imports from "../zod-utils" -import { CompleteMembership, MembershipModel, CompleteEventType, EventTypeModel } from "./index" - -export const _TeamModel = z.object({ - id: z.number().int(), - name: z.string().nullish(), - slug: z.string().nullish(), - logo: z.string().nullish(), - bio: z.string().nullish(), - hideBranding: z.boolean(), -}) - -export interface CompleteTeam extends z.infer { - members: CompleteMembership[] - eventTypes: CompleteEventType[] -} - -/** - * TeamModel contains all relations on your model in addition to the scalars - * - * NOTE: Lazy required in case of potential circular dependencies within schema - */ -export const TeamModel: z.ZodSchema = z.lazy(() => _TeamModel.extend({ - members: MembershipModel.array(), - eventTypes: EventTypeModel.array(), -})) diff --git a/packages/prisma/zod/user.ts b/packages/prisma/zod/user.ts deleted file mode 100644 index 81c81df87f..0000000000 --- a/packages/prisma/zod/user.ts +++ /dev/null @@ -1,70 +0,0 @@ -import * as z from "zod" -import * as imports from "../zod-utils" -import { IdentityProvider, UserPlan } from "@prisma/client" -import { CompleteEventType, EventTypeModel, CompleteCredential, CredentialModel, CompleteMembership, MembershipModel, CompleteBooking, BookingModel, CompleteAvailability, AvailabilityModel, CompleteSelectedCalendar, SelectedCalendarModel, CompleteSchedule, ScheduleModel, CompleteWebhook, WebhookModel, CompleteDestinationCalendar, DestinationCalendarModel } from "./index" - -// Helper schema for JSON fields -type Literal = boolean | number | string -type Json = Literal | { [key: string]: Json } | Json[] -const literalSchema = z.union([z.string(), z.number(), z.boolean()]) -const jsonSchema: z.ZodSchema = z.lazy(() => z.union([literalSchema, z.array(jsonSchema), z.record(jsonSchema)])) - -export const _UserModel = z.object({ - id: z.number().int(), - username: z.string().nullish(), - name: z.string().nullish(), - email: z.string().email(), - emailVerified: z.date().nullish(), - password: z.string().nullish(), - bio: z.string().nullish(), - avatar: z.string().nullish(), - timeZone: z.string(), - weekStart: z.string(), - startTime: z.number().int(), - endTime: z.number().int(), - bufferTime: z.number().int(), - hideBranding: z.boolean(), - theme: z.string().nullish(), - createdDate: z.date(), - completedOnboarding: z.boolean(), - locale: z.string().nullish(), - twoFactorSecret: z.string().nullish(), - twoFactorEnabled: z.boolean(), - identityProvider: z.nativeEnum(IdentityProvider), - identityProviderId: z.string().nullish(), - invitedTo: z.number().int().nullish(), - plan: z.nativeEnum(UserPlan), - brandColor: z.string(), - away: z.boolean(), - metadata: jsonSchema, - verified: z.boolean().nullish(), -}) - -export interface CompleteUser extends z.infer { - eventTypes: CompleteEventType[] - credentials: CompleteCredential[] - teams: CompleteMembership[] - bookings: CompleteBooking[] - availability: CompleteAvailability[] - selectedCalendars: CompleteSelectedCalendar[] - Schedule: CompleteSchedule[] - webhooks: CompleteWebhook[] - destinationCalendar?: CompleteDestinationCalendar | null -} - -/** - * UserModel contains all relations on your model in addition to the scalars - * - * NOTE: Lazy required in case of potential circular dependencies within schema - */ -export const UserModel: z.ZodSchema = z.lazy(() => _UserModel.extend({ - eventTypes: EventTypeModel.array(), - credentials: CredentialModel.array(), - teams: MembershipModel.array(), - bookings: BookingModel.array(), - availability: AvailabilityModel.array(), - selectedCalendars: SelectedCalendarModel.array(), - Schedule: ScheduleModel.array(), - webhooks: WebhookModel.array(), - destinationCalendar: DestinationCalendarModel.nullish(), -})) diff --git a/packages/prisma/zod/verificationrequest.ts b/packages/prisma/zod/verificationrequest.ts deleted file mode 100644 index b9a7281df3..0000000000 --- a/packages/prisma/zod/verificationrequest.ts +++ /dev/null @@ -1,11 +0,0 @@ -import * as z from "zod" -import * as imports from "../zod-utils" - -export const _VerificationRequestModel = z.object({ - id: z.number().int(), - identifier: z.string(), - token: z.string(), - expires: z.date(), - createdAt: z.date(), - updatedAt: z.date(), -}) diff --git a/packages/prisma/zod/webhook.ts b/packages/prisma/zod/webhook.ts deleted file mode 100644 index 4d564e3a54..0000000000 --- a/packages/prisma/zod/webhook.ts +++ /dev/null @@ -1,27 +0,0 @@ -import * as z from "zod" -import * as imports from "../zod-utils" -import { WebhookTriggerEvents } from "@prisma/client" -import { CompleteUser, UserModel } from "./index" - -export const _WebhookModel = z.object({ - id: z.string(), - userId: z.number().int(), - subscriberUrl: z.string(), - payloadTemplate: z.string().nullish(), - createdAt: z.date(), - active: z.boolean(), - eventTriggers: z.nativeEnum(WebhookTriggerEvents).array(), -}) - -export interface CompleteWebhook extends z.infer { - user: CompleteUser -} - -/** - * WebhookModel contains all relations on your model in addition to the scalars - * - * NOTE: Lazy required in case of potential circular dependencies within schema - */ -export const WebhookModel: z.ZodSchema = z.lazy(() => _WebhookModel.extend({ - user: UserModel, -})) From ac0c3bdfb9cdf7a8007c0750dd4752d9ab68cfa0 Mon Sep 17 00:00:00 2001 From: Demian Caldelas Date: Tue, 22 Feb 2022 21:23:52 -0300 Subject: [PATCH 2/6] Fix dynamic min/max values for schedule form (#1940) Co-authored-by: Alex van Andel --- .../web/components/ui/modal/SetTimesModal.tsx | 84 ++++++++++++++++--- 1 file changed, 72 insertions(+), 12 deletions(-) diff --git a/apps/web/components/ui/modal/SetTimesModal.tsx b/apps/web/components/ui/modal/SetTimesModal.tsx index c564716f6f..07e82c02cd 100644 --- a/apps/web/components/ui/modal/SetTimesModal.tsx +++ b/apps/web/components/ui/modal/SetTimesModal.tsx @@ -1,11 +1,15 @@ import { ClockIcon } from "@heroicons/react/outline"; -import { useRef } from "react"; +import dayjs from "dayjs"; +import customParseFormat from "dayjs/plugin/customParseFormat"; +import { useRef, useState } from "react"; import { useLocale } from "@lib/hooks/useLocale"; import showToast from "@lib/notification"; import Button from "@components/ui/Button"; +dayjs.extend(customParseFormat); + interface SetTimesModalProps { startTime: number; endTime: number; @@ -21,6 +25,11 @@ export default function SetTimesModal(props: SetTimesModalProps) { const startMinsRef = useRef(null!); const endHoursRef = useRef(null!); const endMinsRef = useRef(null!); + const [endMinuteDisable, setEndMinuteDisable] = useState(false); + const [maximumStartTime, setMaximumStartTime] = useState({ hour: endHours, minute: 59 }); + const [minimumEndTime, setMinimumEndTime] = useState({ hour: startHours, minute: 59 }); + + const STEP = 15; const isValidTime = (startTime: number, endTime: number) => { if (new Date(startTime) > new Date(endTime)) { @@ -34,6 +43,48 @@ export default function SetTimesModal(props: SetTimesModalProps) { return true; }; + // compute dynamic range for minimum and maximum allowed hours/minutes. + const setEdgeTimes = ( + (step) => + ( + startHoursRef: React.MutableRefObject, + startMinsRef: React.MutableRefObject, + endHoursRef: React.MutableRefObject, + endMinsRef: React.MutableRefObject + ) => { + //parse all the refs + const startHour = parseInt(startHoursRef.current.value); + let startMinute = parseInt(startMinsRef.current.value); + const endHour = parseInt(endHoursRef.current.value); + let endMinute = parseInt(endMinsRef.current.value); + + //convert to dayjs object + const startTime = dayjs(`${startHour}-${startMinute}`, "hh:mm"); + const endTime = dayjs(`${endHour}-${endMinute}`, "hh:mm"); + + //compute minimin and maximum allowed + const maximumStartTime = endTime.subtract(step, "minute"); + const maximumStartHour = maximumStartTime.hour(); + const maximumStartMinute = startHour === endHour ? maximumStartTime.minute() : 59; + + const minimumEndTime = startTime.add(step, "minute"); + const minimumEndHour = minimumEndTime.hour(); + const minimumEndMinute = startHour === endHour ? minimumEndTime.minute() : 0; + + //check allow min/max minutes when the end/start hour matches + if (startHoursRef.current.value === endHoursRef.current.value) { + if (parseInt(startMinsRef.current.value) >= maximumStartMinute) + startMinsRef.current.value = maximumStartMinute.toString(); + if (parseInt(endMinsRef.current.value) <= minimumEndMinute) + endMinsRef.current.value = minimumEndMinute.toString(); + } + + //save into state + setMaximumStartTime({ hour: maximumStartHour, minute: maximumStartMinute }); + setMinimumEndTime({ hour: minimumEndHour, minute: minimumEndMinute }); + } + )(STEP); + return (
-
+
@@ -73,17 +124,18 @@ export default function SetTimesModal(props: SetTimesModalProps) { ref={startHoursRef} type="number" min="0" - max="23" - maxLength={2} + max={maximumStartTime.hour} + minLength={2} name="hours" id="startHours" className="focus:border-brand block w-full rounded-md border-gray-300 shadow-sm focus:ring-black sm:text-sm" placeholder="9" defaultValue={startHours} + onChange={() => setEdgeTimes(startHoursRef, startMinsRef, endHoursRef, endMinsRef)} />
: -
+
@@ -91,27 +143,28 @@ export default function SetTimesModal(props: SetTimesModalProps) { ref={startMinsRef} type="number" min="0" - max="59" - step="15" + max={maximumStartTime.minute} + step={STEP} maxLength={2} name="minutes" id="startMinutes" className="focus:border-brand block w-full rounded-md border-gray-300 shadow-sm focus:ring-black sm:text-sm" placeholder="30" defaultValue={startMinutes} + onChange={() => setEdgeTimes(startHoursRef, startMinsRef, endHoursRef, endMinsRef)} />
-
+
{ + if (endHoursRef.current.value === "24") endMinsRef.current.value = "0"; + setEdgeTimes(startHoursRef, startMinsRef, endHoursRef, endMinsRef); + setEndMinuteDisable(endHoursRef.current.value === "24"); + }} />
: -
+
setEdgeTimes(startHoursRef, startMinsRef, endHoursRef, endMinsRef)} />
From 5cf67fdbaad0b45e3966be7588b9c5a085a04242 Mon Sep 17 00:00:00 2001 From: Peer Richelsen Date: Wed, 23 Feb 2022 11:09:22 +0000 Subject: [PATCH 3/6] fixed avatar group and tooltip (#1950) --- apps/web/components/ui/Avatar.tsx | 2 +- apps/web/components/ui/AvatarGroup.tsx | 33 ++------------------------ 2 files changed, 3 insertions(+), 32 deletions(-) diff --git a/apps/web/components/ui/Avatar.tsx b/apps/web/components/ui/Avatar.tsx index 4232d5c807..ecc18d7b70 100644 --- a/apps/web/components/ui/Avatar.tsx +++ b/apps/web/components/ui/Avatar.tsx @@ -36,7 +36,7 @@ export default function Avatar(props: AvatarProps) { return title ? ( {avatar} - + {title} diff --git a/apps/web/components/ui/AvatarGroup.tsx b/apps/web/components/ui/AvatarGroup.tsx index c1320e1a5c..45fb0be623 100644 --- a/apps/web/components/ui/AvatarGroup.tsx +++ b/apps/web/components/ui/AvatarGroup.tsx @@ -4,8 +4,6 @@ import classNames from "@lib/classNames"; import Avatar from "@components/ui/Avatar"; -// import * as Tooltip from "@radix-ui/react-tooltip"; - export type AvatarGroupProps = { size: number; truncateAfter?: number; @@ -18,44 +16,17 @@ export type AvatarGroupProps = { }; export const AvatarGroup = function AvatarGroup(props: AvatarGroupProps) { - /* const truncatedAvatars: string[] = - props.items.length > props.truncateAfter - ? props.items - .slice(props.truncateAfter) - .map((item) => item.title) - .filter(Boolean) - : [];*/ - return ( -
    +
      {props.items.slice(0, props.truncateAfter).map((item, idx) => { if (item.image != null) { return ( -
    • +
    • ); } })} - {/*props.items.length > props.truncateAfter && ( -
    • - - - +1 - - {truncatedAvatars.length !== 0 && ( - - -
        - {truncatedAvatars.map((title) => ( -
      • {title}
      • - ))} -
      -
      - )} -
      -
    • - )*/}
    ); }; From 5eca42bb45fb2ba4972fc9b0e82cdb5e30c1df47 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 23 Feb 2022 12:01:55 +0000 Subject: [PATCH 4/6] New Crowdin translations by Github Action (#1952) Co-authored-by: Crowdin Bot --- apps/web/public/static/locales/it/common.json | 89 +++++++++++- apps/web/public/static/locales/ja/common.json | 134 +++++++++++++++++- .../public/static/locales/zh-TW/common.json | 6 +- 3 files changed, 226 insertions(+), 3 deletions(-) diff --git a/apps/web/public/static/locales/it/common.json b/apps/web/public/static/locales/it/common.json index 5eb1b78f23..a9ff83352f 100644 --- a/apps/web/public/static/locales/it/common.json +++ b/apps/web/public/static/locales/it/common.json @@ -1,5 +1,7 @@ { "trial_days_left": "Hai solo $t(day, {\"count\": {{days}} }) rimasti al piano PRO", + "day": "{{count}} giorni", + "day_plural": "{{count}} giorni", "upgrade_now": "Cambia piano", "accept_invitation": "Accetta Invito", "calcom_explained": "Cal.com è l'alternativa Calendly open source che ti consente di controllare i tuoi dati, il flusso di lavoro e l'aspetto.", @@ -88,7 +90,12 @@ "you_have_been_invited": "Sei stato invitato ad unirti al team {{teamName}}", "user_invited_you": "{{user}} ti ha invitato a unirti alla squadra {{team}} su Cal.com", "hidden_team_member_title": "Sei nascosto da questo team", + "hidden_team_member_message": "Il tuo posto non è pagato, passa a Pro o fai sapere al proprietario del team che può pagare per il tuo posto.", + "hidden_team_owner_message": "Hai bisogno di un account pro per utilizzare i team, sei nascosto fino a quando non si aggiorna.", "link_expires": "p.s. Scade tra {{expiresIn}} ore.", + "upgrade_to_per_seat": "Passa a Per-Post", + "team_upgrade_seats_details": "Dei {{memberCount}} membri del tuo team, {{unpaidCount}} posti non sono stati pagati. A ${{seatPrice}}/m per posto il costo totale stimato della tua adesione è di ${{totalCost}}/m.", + "team_upgraded_successfully": "Il tuo team è stato aggiornato con successo!", "use_link_to_reset_password": "Usa il link qui sotto per reimpostare la tua password", "hey_there": "Ciao,", "forgot_your_password_calcom": "Hai dimenticato la password? - Cal.com", @@ -106,6 +113,7 @@ "webhook_created_successfully": "Webhook creato con successo!", "webhook_updated_successfully": "Webhook aggiornato con successo!", "webhook_removed_successfully": "Webhook rimosso con successo!", + "payload_template": "Template Di Payload", "dismiss": "Ignora", "no_data_yet": "Ancora nessun dato", "ping_test": "Test Ping", @@ -139,6 +147,7 @@ "rejected": "Rifiutato", "unconfirmed": "Non Confermato", "guests": "Ospiti", + "guest": "Ospite", "web_conferencing_details_to_follow": "Dettagli di conferenza web da seguire.", "the_username": "Il nome utente", "username": "Username", @@ -163,6 +172,7 @@ "30min_meeting": "Meeting di 30 minuti", "secret_meeting": "Riunione Segreta", "login_instead": "Effettua il login invece", + "already_have_an_account": "Hai già un account?", "create_account": "Crea Account", "confirm_password": "Conferma password", "create_your_account": "Crea il tuo account", @@ -233,6 +243,15 @@ "failed": "Fallito", "password_has_been_reset_login": "La tua password è stata reimpostata. Ora puoi accedere con la tua password appena creata.", "unexpected_error_try_again": "Si è verificato un errore imprevisto. Riprova.", + "sunday_time_error": "Ora non valida la domenica", + "monday_time_error": "Ora non valida il lunedì", + "tuesday_time_error": "Ora non valida il martedì", + "wednesday_time_error": "Ora non valida il mercoledì", + "thursday_time_error": "Ora non valida il giovedì", + "friday_time_error": "Ora non valida il venerdì", + "saturday_time_error": "Ora non valida il sabato", + "error_end_time_before_start_time": "L'ora di fine non può essere prima dell'orario di inizio", + "error_end_time_next_day": "L'ora di fine non può essere maggiore di 24 ore", "back_to_bookings": "Torna alle prenotazioni", "free_to_pick_another_event_type": "Sentiti libero di scegliere un altro evento in qualsiasi momento.", "cancelled": "Annullato", @@ -312,6 +331,7 @@ "no_event_types_have_been_setup": "Questo utente non ha ancora impostato alcun tipo di evento.", "edit_logo": "Modifica logo", "upload_a_logo": "Carica un logo", + "remove_logo": "Rimuovi logo", "enable": "Abilita", "code": "Codice", "code_is_incorrect": "Il codice non è corretto.", @@ -388,6 +408,7 @@ "email_or_username": "Email o Username", "send_invite_email": "Invia un'email di invito", "role": "Ruolo", + "edit_role": "Modifica Ruolo", "edit_team": "Modifica team", "reject": "Rifiuta", "accept": "Accetta", @@ -403,10 +424,12 @@ "members": "Membri", "member": "Membri", "owner": "Proprietario", + "admin": "Amministratore", "new_member": "Nuovo Membro", "invite": "Invita", "invite_new_member": "Invita un nuovo membro", "invite_new_team_member": "Invita qualcuno nel tuo team.", + "change_member_role": "Cambia il ruolo del membro del team", "disable_cal_branding": "Disabilita il branding Cal.com", "disable_cal_branding_description": "Nascondi tutti i brand Cal.com dalle tue pagine pubbliche.", "danger_zone": "Zona Di Pericolo", @@ -421,6 +444,8 @@ "confirm_remove_member": "Sì, rimuovi membro", "remove_member": "Rimuovi membro", "manage_your_team": "Gestisci il tuo team", + "no_teams": "Non hai ancora nessun team.", + "no_teams_description": "I team permettono ad altri di prenotare eventi condivisi tra i tuoi collaboratori.", "submit": "Invia", "delete": "Elimina", "update": "Aggiorna", @@ -428,6 +453,8 @@ "pending": "In Attesa", "open_options": "Apri opzioni", "copy_link": "Copia link all'evento", + "share": "Condividi", + "share_event": "Ti dispiacerebbe prenotare il mio cal o inviarmi il tuo link?", "copy_link_team": "Copia il link al team", "leave_team": "Lascia il team", "confirm_leave_team": "Sì, lascio il team", @@ -435,6 +462,7 @@ "user_from_team": "{{user}} da {{team}}", "preview": "Anteprima", "link_copied": "Link copiato!", + "link_shared": "Link condiviso!", "title": "Titolo", "description": "Descrizione", "quick_video_meeting": "Una riunione video veloce.", @@ -449,8 +477,11 @@ "url": "URL", "hidden": "Nascosto", "readonly": "Sola lettura", + "plan_description": "Al momento sei sul piano {{plan}}.", + "plan_upgrade_invitation": "Aggiorna il tuo account al piano pro per sbloccare tutte le funzionalità che abbiamo da offrire.", "plan_upgrade": "È necessario aggiornare il piano per avere più di un tipo di evento attivo.", "plan_upgrade_teams": "Devi aggiornare il tuo piano per creare un team.", + "plan_upgrade_instructions": "Puoi aggiornare <1>qui.", "event_types_page_title": "Tipo di Evento", "event_types_page_subtitle": "Crea eventi da condividere per le persone che prenotano sul tuo calendario.", "new_event_type_btn": "Nuovo tipo di evento", @@ -463,6 +494,8 @@ "event_type_created_successfully": "{{eventTypeTitle}} tipo di evento creato con successo", "event_type_updated_successfully": "{{eventTypeTitle}} tipo di evento aggiornato con successo", "event_type_deleted_successfully": "Tipo di evento eliminato con successo", + "web3_metamask_added": "Metamask aggiunto con successo", + "web3_metamask_disconnected": "Metamask disconnesso con successo", "hours": "Ore", "your_email": "La Tua Email", "change_avatar": "Cambia Avatar", @@ -481,10 +514,15 @@ "create_first_team_and_invite_others": "Crea il tuo primo team e invita altri utenti a lavorare insieme a te.", "create_team_to_get_started": "Crea una squadra per iniziare", "teams": "Team", + "team_billing": "Fatturazione Team", + "upgrade_to_flexible_pro_title": "Abbiamo cambiato la fatturazione per i team", + "upgrade_to_flexible_pro_message": "Ci sono membri del tuo team senza posto. Aggiorna il tuo piano pro per coprire i posti mancanti.", + "changed_team_billing_info": "A partire da gennaio 2022 ci addebitiamo su base per posto per i membri del team. I membri del tuo team che hanno avuto Pro gratis sono ora in una prova di 14 giorni. Una volta scaduta la loro prova, questi membri saranno nascosti dal tuo team a meno che tu non aggiorni ora.", "create_manage_teams_collaborative": "Crea e gestisci team per utilizzare funzionalità di collaborazione.", "only_available_on_pro_plan": "Questa funzione è disponibile solo nel piano Pro", "remove_cal_branding_description": "Al fine di rimuovere il marchio Cal dalle pagine di prenotazione, è necessario acquistare un account Pro.", "edit_profile_info_description": "Modifica le informazioni del tuo profilo, che verranno visualizzate sul tuo link di pianificazione.", + "change_email_tip": "Potrebbe essere necessario disconnettersi e tornare per vedere se il cambiamento ha effetto.", "little_something_about": "Qualcosa su di te.", "profile_updated_successfully": "Profilo aggiornato con successo", "your_user_profile_updated_successfully": "Il tuo profilo utente è stato aggiornato con successo.", @@ -507,6 +545,7 @@ "add_attendees": "Aggiungi partecipanti", "show_advanced_settings": "Mostra impostazioni avanzate", "event_name": "Nome Dell'Evento", + "event_name_tooltip": "Il nome che apparirà nei calendari", "meeting_with_user": "Riunione con {USER}", "additional_inputs": "Input Aggiuntivi", "label": "Etichetta", @@ -551,6 +590,9 @@ "delete_event_type_description": "Sei sicuro di voler eliminare questo tipo di evento? Chiunque abbia condiviso questo link non potrà più utilizzarlo per prenotare.", "delete_event_type": "Elimina Tipo Evento", "confirm_delete_event_type": "Sì, elimina tipo evento", + "delete_account": "Elimina account", + "confirm_delete_account": "Sì, elimina account", + "delete_account_confirmation_message": "Sei sicuro di voler eliminare il tuo account Cal.com? Chiunque abbia condiviso il link del tuo account non sarà più in grado di prenotare utilizzandolo e le preferenze che hai salvato andranno perse.", "integrations": "Integrazioni", "settings": "Impostazioni", "event_type_moved_successfully": "Il tipo di evento è stato spostato con successo", @@ -569,6 +611,51 @@ "not_installed": "Non installato", "error_password_mismatch": "Le password non corrispondono.", "error_required_field": "Questo campo è richiesto.", + "status": "Stato", "team_view_user_availability": "Visualizza la disponibilità degli utenti", - "team_view_user_availability_disabled": "L'utente deve accettare l'invito per visualizzare la disponibilità" + "team_view_user_availability_disabled": "L'utente deve accettare l'invito per visualizzare la disponibilità", + "set_as_away": "Mettiti via come assente", + "set_as_free": "Disabilita lo stato assente", + "user_away": "Questo utente è attualmente assente.", + "user_away_description": "La persona che stai cercando di prenotare si è messa assente, e quindi non sta accettando nuove prenotazioni.", + "meet_people_with_the_same_tokens": "Incontra persone con gli stessi token", + "only_book_people_and_allow": "Prenota e consenti le prenotazioni solo da persone che condividono gli stessi token, DAO o NFT.", + "saml_config_deleted_successfully": "Configurazione SAML eliminata con successo", + "account_created_with_identity_provider": "Il tuo account è stato creato utilizzando un Identity Provider.", + "account_managed_by_identity_provider": "Il tuo account è gestito da {{provider}}", + "account_managed_by_identity_provider_description": "Per modificare la tua email, password, abilita l'autenticazione a due fattori e altro, visita le impostazioni dell'account {{provider}}.", + "signin_with_google": "Accedi con Google", + "signin_with_saml": "Accedi con SAML", + "saml_configuration": "Configurazione SAML", + "delete_saml_configuration": "Elimina configurazione SAML", + "delete_saml_configuration_confirmation_message": "Sei sicuro di voler eliminare la configurazione SAML? I membri del tuo team che utilizzano l'accesso SAML non saranno più in grado di accedere a Cal.com.", + "confirm_delete_saml_configuration": "Elimina configurazione SAML", + "saml_not_configured_yet": "SAML non ancora configurato", + "saml_configuration_description": "Per favore incolla i metadata SAML dal tuo Identity Provider nella casella di testo qui sotto per aggiornare la configurazione SAML.", + "saml_configuration_placeholder": "Per favore incolla i metadata SAML dal tuo Identity Provider qui", + "saml_configuration_update_failed": "Aggiornamento configurazione SAML non riuscito", + "saml_configuration_delete_failed": "Aggiornamento configurazione SAML non riuscito", + "saml_email_required": "Inserisci un'email in modo da poter trovare il tuo provider di identità SAML", + "you_will_need_to_generate": "Dovrai generare un token di accesso dal tuo vecchio strumento di pianificazione.", + "import": "Importa", + "import_from": "Importa da", + "access_token": "Token di accesso", + "visit_roadmap": "Roadmap", + "remove": "Rimuovi", + "add": "Aggiungi", + "verify_wallet": "Verifica Wallet", + "connect_metamask": "Connetti Metamask", + "create_events_on": "Crea eventi su:", + "missing_license": "Licenza Mancante", + "signup_requires": "Licenza commerciale richiesta", + "signup_requires_description": "Cal.com, Inc. attualmente non offre una versione open source gratuita nella pagina di iscrizione. Per ricevere l'accesso completo ai componenti di registrazione è necessario acquisire una licenza commerciale. Per uso personale consigliamo la Piattaforma dati Prisma o qualsiasi altra interfaccia Postgres per creare account.", + "next_steps": "Prossimi Passi", + "acquire_commercial_license": "Acquista una licenza commerciale", + "the_infrastructure_plan": "Il piano infrastrutturale è basato sull'utilizzo e ha sconti favorevoli per le startup.", + "prisma_studio_tip": "Crea un account tramite Prisma Studio", + "prisma_studio_tip_description": "Scopri come configurare il tuo primo utente", + "contact_sales": "Contatta Vendite", + "error_404": "Errore 404", + "requires_ownership_of_a_token": "Richiede la proprietà di un token appartenente al seguente indirizzo:", + "example_name": "Paolo Rossi" } diff --git a/apps/web/public/static/locales/ja/common.json b/apps/web/public/static/locales/ja/common.json index 1054065ee6..dafb92a5ce 100644 --- a/apps/web/public/static/locales/ja/common.json +++ b/apps/web/public/static/locales/ja/common.json @@ -1,4 +1,26 @@ { + "trial_days_left": "PROトライアル版に $t(day, {\"count\": {{days}} }) があります", + "day": "{{count}} 日", + "day_plural": "{{count}} 日間", + "upgrade_now": "今すぐアップグレード", + "accept_invitation": "招待を承認", + "calcom_explained": "カレンダーの代わりとして、Cal.comは、所有するデータ、ワークフロー、イベントへの出席状況などを管理できます。", + "have_any_questions": "ご不明な点がございますか?お気軽にお問い合わせください", + "reset_password_subject": "Cal.com: パスワードリセットの手順", + "event_declined_subject": "拒否: {{date}} 、{{name}} による{{eventType}}", + "event_cancelled_subject": "キャンセルしました: {{date}}、 {{name}} による {{eventType}}", + "event_request_declined": "イベントリクエストが却下されました", + "event_request_cancelled": "予定がキャンセルになりました", + "organizer": "主催者", + "need_to_reschedule_or_cancel": "スケジュール変更またはキャンセルが必要ですか?", + "cancellation_reason": "キャンセル理由", + "cancellation_reason_placeholder": "キャンセルする理由 (オプション)", + "rejection_reason": "却下の理由", + "rejection_reason_title": "予約リクエストを却下しますか?", + "rejection_reason_description": "この予約を却下してもよろしいですか?予約しようとした人に、以下の理由を提供できます。", + "rejection_confirmation": "予約を却下する", + "manage_this_event": "このイベントを管理", + "your_event_has_been_scheduled": "イベントが予定されています", "accept_our_license": ".env 変数 <1>NEXT_PUBLIC_LICENSE_CONSENT を '{{agree}} ' に変更することで、ライセンスを承認します。", "remove_banner_instructions": "このバナーを削除するには、.envファイルを開き、<1>NEXT_PUBLIC_LICENSE_CONSENT変数を「{{agree}}」に変更してください", "error_message": "エラーメッセージ: '{{errorMessage}}'", @@ -6,6 +28,7 @@ "refund_failed": "{{userName}} の {{date}} の {{eventType}} に関するイベントの返金に失敗しました。", "check_with_provider_and_user": "支払い業者に確認し、 {{userName}} の処理方法をご確認ください。", "a_refund_failed": "返金に失敗しました", + "awaiting_payment_subject": "お支払いを待っています: {{date}} に開催される {{name}} の {{eventType}}", "meeting_awaiting_payment": "会議の支払いを待っています", "help": "ヘルプ", "price": "料金", @@ -29,12 +52,19 @@ "integration_meeting_id": "{{integrationName}} ミーティングID: {{meetingId}}", "confirmed_event_type_subject": "確認済: {{name}} の {{date}} の {{eventType}}", "new_event_request": "新しいイベントのリクエスト: {{attendeeName}} - {{date}} - {{eventType}}", + "confirm_or_reject_request": "リクエストを承認または却下する", "check_bookings_page_to_confirm_or_reject": "予約ページを確認し、予約を確認または拒否してください。", "event_awaiting_approval": "新しいイベントがあなたの承認を待っています", + "someone_requested_an_event": "誰かがあなたのカレンダーにイベントの追加をリクエストしています。", + "someone_requested_password_reset": "誰かがあなたのパスワードを変更するためのリンクをリクエストしました。", + "password_reset_instructions": "このメールをリクエストしていない場合は、このメールを無視してもパスワードは変更されません。", + "event_awaiting_approval_subject": "承認待ち: {{date}} に開催される {{name}} による {{eventType}}", + "event_still_awaiting_approval": "イベントはあなたの承認を待っています", "your_meeting_has_been_booked": "ミーティングが予約されました", "event_type_has_been_rescheduled_on_time_date": "{{name}} と出席する {{eventType}} は ({{timeZone}}) の {{date}} {{time}} にスケジュール変更されました", "event_has_been_rescheduled": "イベントのスケジュールが変更されました", "hi_user_name": "こんにちは、 {{userName}}", + "ics_event_title": "{{name}} との{{eventType}}", "new_event_subject": "新規イベント: {{attendeeName}} - {{date}} - {{eventType}}", "join_by_entrypoint": "{{entryPoint}} に参加する", "notes": "メモ", @@ -50,13 +80,22 @@ "meeting_password": "ミーティングパスワード", "meeting_url": "ミーティングID", "meeting_request_rejected": "ミーティングリクエストが拒否されました", + "rescheduled_event_type_subject": "変更日時: {{date}}、 {{name}} と {{eventType}}", "rejected_event_type_with_organizer": "拒否: {{date}} の{{organizer}} による{{eventType}}", "hi": "こんにちは", "join_team": "チームに参加", + "manage_this_team": "このチームを管理する", + "team_info": "チーム情報", "request_another_invitation_email": "{{toEmail}} を Cal.com のメールアドレスとして使用しない場合、または Cal.com アカウントをすでに持っている場合は、そのメールへの別の招待をリクエストしてください。", "you_have_been_invited": "チーム {{teamName}} に招待されました。", "user_invited_you": "{{user}} さんがあなたをチーム {{teamName}} に招待しました", + "hidden_team_member_title": "あなたはこのチームで非表示になっています", + "hidden_team_member_message": "あなたの座席は、支払いが完了していません。Proにアップグレードするか、チームオーナーにあなたの座席の支払いを知らせてください。", + "hidden_team_owner_message": "チームを使用するにはプロアカウントが必要です。アップグレードするまで非表示になります。", "link_expires": "追記: {{expiresIn}} 時間後に有効期限が切れます。", + "upgrade_to_per_seat": "座席毎にアップグレード", + "team_upgrade_seats_details": "あなたのチームメンバー {{memberCount}} の内、 {{unpaidCount}} 名のお席のお支払いが完了していません。1席あたり ${{seatPrice}} 、全てのお席の金額は ${{totalCost}} です。", + "team_upgraded_successfully": "チームは正常にアップグレードされました!", "use_link_to_reset_password": "以下のリンクを使用してパスワードをリセットしてください", "hey_there": "こんにちは!", "forgot_your_password_calcom": "パスワードをお忘れですか? - Cal.com", @@ -74,6 +113,7 @@ "webhook_created_successfully": "Webhookの作成に成功しました!", "webhook_updated_successfully": "Webhookが正常に更新されました!", "webhook_removed_successfully": "Webhookの削除に成功しました!", + "payload_template": "ペイロードのテンプレート", "dismiss": "解除", "no_data_yet": "データがまだありません", "ping_test": "Pingテスト", @@ -107,6 +147,7 @@ "rejected": "拒否されました", "unconfirmed": "未確認", "guests": "ゲスト", + "guest": "ゲスト", "web_conferencing_details_to_follow": "ウェブ会議の詳細", "the_username": "ユーザー名", "username": "ユーザー名", @@ -131,6 +172,7 @@ "30min_meeting": "30分間のミーティング", "secret_meeting": "秘密のミーティング", "login_instead": "代わりにログインする", + "already_have_an_account": "既にアカウントをお持ちですか?", "create_account": "アカウントを作成", "confirm_password": "パスワードを確認", "create_your_account": "アカウントを作成", @@ -201,6 +243,15 @@ "failed": "失敗", "password_has_been_reset_login": "パスワードがリセットされました。新しく作成したパスワードでログインできるようになりました。", "unexpected_error_try_again": "予期しないエラーが発生しました。再試行してください。", + "sunday_time_error": "日曜日の無効な時間", + "monday_time_error": "月曜日の無効な時間", + "tuesday_time_error": "火曜日の無効な時間", + "wednesday_time_error": "水曜日の無効な時間", + "thursday_time_error": "木曜日の無効な時間", + "friday_time_error": "金曜日の無効な時間", + "saturday_time_error": "土曜日の無効な時間", + "error_end_time_before_start_time": "終了時刻は開始時刻の前にすることはできません", + "error_end_time_next_day": "終了時刻は24時間以上にすることはできません", "back_to_bookings": "予約に戻る", "free_to_pick_another_event_type": "好きな他のイベントを選んでください。", "cancelled": "キャンセルしました", @@ -280,6 +331,7 @@ "no_event_types_have_been_setup": "このユーザーはまだイベントタイプを設定していません。", "edit_logo": "ロゴを編集", "upload_a_logo": "ロゴをアップロード", + "remove_logo": "ロゴを削除する", "enable": "有効", "code": "コード", "code_is_incorrect": "コードが正しくありません。", @@ -356,6 +408,7 @@ "email_or_username": "メールアドレスまたはユーザー名", "send_invite_email": "招待メールを送信", "role": "権限", + "edit_role": "役割を編集", "edit_team": "チームを編集", "reject": "拒否", "accept": "許可", @@ -371,10 +424,12 @@ "members": "メンバー", "member": "メンバー", "owner": "所有者", + "admin": "管理者", "new_member": "新しいメンバー", "invite": "招待する", "invite_new_member": "新しいメンバーを招待する", "invite_new_team_member": "チームに誰かを招待します。", + "change_member_role": "チームメンバーの役割を変更", "disable_cal_branding": "Cal.comのブランディングを無効にする", "disable_cal_branding_description": "Cal.comのすべてのブランディングを公開ページから非表示にする。", "danger_zone": "危険なエリア", @@ -389,6 +444,8 @@ "confirm_remove_member": "はい、メンバーを削除します", "remove_member": "メンバーを削除", "manage_your_team": "チームを管理", + "no_teams": "まだチームがありません。", + "no_teams_description": "チームを使用すると、同僚間で共有されたイベントを予約できます。", "submit": "送信", "delete": "削除", "update": "更新", @@ -396,8 +453,16 @@ "pending": "保留中", "open_options": "オプションを開く", "copy_link": "イベントのリンクをコピー", + "share": "シェアする", + "share_event": "私のカルを予約もしくはリンクを送っていただけますか?", + "copy_link_team": "チームにリンクをコピー", + "leave_team": "チームから退出する", + "confirm_leave_team": "はい、チームから退出します", + "leave_team_confirmation_message": "このチームから退出してもよろしいですか?チームを使用して予約することができなくなります。", + "user_from_team": "{{user}} からの {{team}}", "preview": "プレビュー", "link_copied": "リンクをコピーしました!", + "link_shared": "リンクを共有!", "title": "タイトル", "description": "説明", "quick_video_meeting": "短時間のビデオミーティング。", @@ -412,7 +477,11 @@ "url": "URL", "hidden": "隠れている", "readonly": "読み込み専用", + "plan_description": "あなたは現在 {{plan}} プランを利用中です。", + "plan_upgrade_invitation": "アカウントをプロプランにアップグレードすると、当社が提供するすべての機能が利用可能となります。", "plan_upgrade": "複数の有効なイベントタイプを持つには、プランをアップグレードする必要があります。", + "plan_upgrade_teams": "チームを作成するには、プランをアップグレードする必要があります。", + "plan_upgrade_instructions": "<1>ここでアップグレードができます。", "event_types_page_title": "イベント種別", "event_types_page_subtitle": "人々があなたのカレンダーを予約するために共有するイベントを作成します。", "new_event_type_btn": "新しいイベントの種類", @@ -425,6 +494,8 @@ "event_type_created_successfully": "{{eventTypeTitle}} イベント種別が正常に作成されました", "event_type_updated_successfully": "{{eventTypeTitle}} イベント種別が正常に更新されました", "event_type_deleted_successfully": "イベント種別が正常に削除されました", + "web3_metamask_added": "メタマスクの追加に成功しました", + "web3_metamask_disconnected": "メタマスクの接続が解除されました", "hours": "時間", "your_email": "あなたのメールアドレス", "change_avatar": "アバターを変更", @@ -432,6 +503,7 @@ "timezone": "タイムゾーン", "first_day_of_week": "週の最初の日", "single_theme": "シングルテーマ", + "brand_color": "ブランドカラー", "file_not_named": "ファイル名が [idOrSlug]/[user] ではありません", "create_team": "チームを作成", "name": "名前", @@ -442,10 +514,15 @@ "create_first_team_and_invite_others": "あなたの最初のチームを作成し、あなたと一緒に仕事をするために他のユーザーを招待してください。", "create_team_to_get_started": "始めるためにチームを作成", "teams": "チーム", + "team_billing": "チーム請求", + "upgrade_to_flexible_pro_title": "チームへの請求を変更しました", + "upgrade_to_flexible_pro_message": "あなたのチームには座席がないメンバーがいます。プロプランにアップグレードして不足している席を確保してください。", + "changed_team_billing_info": "2022年1月より、チームメンバーの座席単位で料金を請求します. Proを無料で利用したチームメンバーは、14日間の試用期間中です。 試用期間が終了すると、今すぐアップグレードしない限り、これらのメンバーはチームに表示されなくなります.", "create_manage_teams_collaborative": "共同作業機能を使用するチームを作成および管理します。", "only_available_on_pro_plan": "この機能はProプランでのみ利用できます。", "remove_cal_branding_description": "予約ページからカルブランディングを削除するには、Proアカウントにアップグレードする必要があります。", "edit_profile_info_description": "スケジューリングリンクに表示されるプロフィール情報を編集します。", + "change_email_tip": "変更を有効にするには、ログアウトして再度ログインする必要があります。", "little_something_about": "あなた自身につい何か少し。", "profile_updated_successfully": "プロフィールが正常に更新されました", "your_user_profile_updated_successfully": "ユーザープロフィールの更新が完了しました。", @@ -468,6 +545,7 @@ "add_attendees": "出席者を追加", "show_advanced_settings": "詳細設定を表示", "event_name": "イベント名", + "event_name_tooltip": "カレンダーに表示する名前", "meeting_with_user": "{USER} とのミーティング", "additional_inputs": "追加入力", "label": "ラベル", @@ -507,11 +585,17 @@ "new_event_type_to_book_description": "人々が時間を予約するための新しいイベント種別を作成します。", "length": "長さ", "minimum_booking_notice": "最低限の予約通知", + "slot_interval": "時間帯の間隔", + "slot_interval_default": "イベントの長さを使用 (デフォルト)", "delete_event_type_description": "このイベント種別を削除してもよろしいですか? あなた'veがこのリンクを共有した人は、もはやそれを使用して予約することはできません。", "delete_event_type": "イベント種別を削除", "confirm_delete_event_type": "はい、イベント種別を削除します", + "delete_account": "アカウントを削除", + "confirm_delete_account": "はい、アカウントを削除します", + "delete_account_confirmation_message": "本当にあなたのアカウントを削除してもよろしいですか?あなたのアカウントリンクを共有している人は、あなたのアカウントを使用して予約することができなくなり、保存した設定は失われます。", "integrations": "統合", "settings": "設定", + "event_type_moved_successfully": "イベントタイプが正常に移動しました", "next_step": "手順をスキップ", "prev_step": "前の手順", "installed": "インストール済み", @@ -520,10 +604,58 @@ "connect_your_favourite_apps": "お気に入りのアプリを接続する", "automation": "自動化", "configure_how_your_event_types_interact": "イベント種別とカレンダーをどのように連携するかを設定します。", + "select_destination_calendar": "イベントを作成:", "connect_an_additional_calendar": "追加のカレンダーを接続する", "conferencing": "ミーティング中", "calendar": "カレンダー", "not_installed": "インストールされていません", "error_password_mismatch": "パスワードが一致しません。", - "error_required_field": "この項目は必須です。" + "error_required_field": "この項目は必須です。", + "status": "ステータス:", + "team_view_user_availability": "ユーザーの利用可否を表示", + "team_view_user_availability_disabled": "利用可否を確認するには招待を承認する必要があります", + "set_as_away": "自分自身を離れた状態に設定する", + "set_as_free": "離れた状態の無効化", + "user_away": "このユーザーは現在離れています。", + "user_away_description": "あなたが予約しようとしている人は離れています。そのため、新しい予約を受け入れていません.", + "meet_people_with_the_same_tokens": "同じトークンを持つ人に会いに行く", + "only_book_people_and_allow": "同じトークン、DAO、またはNFTを共有する人々からの予約のみ予約できます。", + "saml_config_deleted_successfully": "SAML設定を削除しました", + "account_created_with_identity_provider": "あなたのアカウントはIDプロバイダーを使用して作成されました。", + "account_managed_by_identity_provider": "あなたのアカウントは {{provider}} によって管理されています", + "account_managed_by_identity_provider_description": "メールアドレスやパスワードの変更、二段階認証を有効にするには {{provider}} アカウント設定をご覧ください。", + "signin_with_google": "Googleアカウントでログイン", + "signin_with_saml": "SAMLでログイン", + "saml_configuration": "SAML設定", + "delete_saml_configuration": "SAMLの設定を削除", + "delete_saml_configuration_confirmation_message": "SAML設定を削除してもよろしいですか? SAMLでログインしているチームメンバーは、Cal.comにアクセスできなくなります。", + "confirm_delete_saml_configuration": "はい、SAML設定を削除します", + "saml_not_configured_yet": "SAMLが設定されていません", + "saml_configuration_description": "SAML設定を更新するには、以下のテキストボックスに、IDプロバイダーからSAML メタデータを貼り付けてください。", + "saml_configuration_placeholder": "IDプロバイダーからSAMLメタデータをここに貼り付けてください", + "saml_configuration_update_failed": "SAML設定の更新に失敗しました", + "saml_configuration_delete_failed": "SAML設定の削除に失敗しました", + "saml_email_required": "SAML IDプロバイダーを見つけるために、メールアドレスを入力してください", + "you_will_need_to_generate": "古いスケジュールツールからアクセストークンを生成する必要があります。", + "import": "インポートする", + "import_from": "インポート元", + "access_token": "アクセストークン", + "visit_roadmap": "ロードマップ", + "remove": "削除する", + "add": "追加する", + "verify_wallet": "ウォレットを確認する", + "connect_metamask": "メタマスクに接続する", + "create_events_on": "イベントを作成:", + "missing_license": "ライセンスがありません", + "signup_requires": "商用ライセンスが必要です", + "signup_requires_description": "Cal.com, Inc.は現在、無料のオープンソース版のサインアップページを提供していません。 サインアップコンポーネントへのフルアクセスを受けるには、商用ライセンスを取得する必要があります。 個人的に使用するには、Prisma Data Platformまたは他のPostgresインターフェースを使用してアカウントを作成することをお勧めします。", + "next_steps": "次のステップ", + "acquire_commercial_license": "商用ライセンスを取得する", + "the_infrastructure_plan": "インフラストラクチャプランは、使用ベースとなっておりスタートアップには割引があります。", + "prisma_studio_tip": "Prisma Studio経由でアカウントを作成する", + "prisma_studio_tip_description": "最初のユーザーを設定する方法を学ぶ", + "contact_sales": "営業担当にお問い合わせ", + "error_404": "エラー 404", + "requires_ownership_of_a_token": "次のアドレスに属するトークンの所有権が必要です:", + "example_name": "山田太朗" } diff --git a/apps/web/public/static/locales/zh-TW/common.json b/apps/web/public/static/locales/zh-TW/common.json index 57b8bfb146..04b8af13d5 100644 --- a/apps/web/public/static/locales/zh-TW/common.json +++ b/apps/web/public/static/locales/zh-TW/common.json @@ -451,6 +451,8 @@ "pending": "等待中", "open_options": "開啟選項", "copy_link": "複製活動連結", + "share": "分享", + "share_event": "是否可以在我的行事曆預約,還是把連結寄給我?", "copy_link_team": "複製團隊連結", "leave_team": "離開團隊", "confirm_leave_team": "沒錯,離開團隊", @@ -458,6 +460,7 @@ "user_from_team": "{{team}} 的 {{user}}", "preview": "預覽", "link_copied": "網址已複製!", + "link_shared": "已分享連結!", "title": "標題", "description": "描述", "quick_video_meeting": "快速視訊會議。", @@ -650,5 +653,6 @@ "prisma_studio_tip": "透過 Prisma Studio 建立帳號", "prisma_studio_tip_description": "學習如何設定第一位使用者", "contact_sales": "聯繫業務", - "error_404": "404 錯誤" + "error_404": "404 錯誤", + "requires_ownership_of_a_token": "必須有屬於以下位址的 Token 所有權:" } From b860a79d59d1ba962aad0b1f064e30f1a5e56e68 Mon Sep 17 00:00:00 2001 From: Agusti Fernandez Date: Wed, 23 Feb 2022 13:37:15 +0100 Subject: [PATCH 5/6] Detect users browser locale for time format 12/24 hours (#1900) * fix: adds new isBrowserLocal24h timeFormat util, uses in BookingPage * fix: adds new time format locale detector in available times * fix: removes 24h clock from availabletimes * chore: move timeFormat to lib util, add to payment page * chore: remove commented out is24h * fix: adds timeFormat to success page * fix: adds timeFormat to cancel page * fix: adds timeFormat to video meeting ended/not started pages * fix: removes added typo in success page * fix: reverts back to use of is24h Switch in available times / time options, renames timeFormat to detectBrowserTimeFormat to avoid collisions * fix: moves use uf isBrowserLocal24h() to clock util initClock() itself, by calling it only if no localStorage settings are set * chore: move back timeFormat props to line it was so no change * chore: remove empty line in timeOptions * chore: move back timeFormat where it was in TimeOptions props * chore: add back empty line before selectedTimeZone return * fix: reverts back to use of is24h in payments page * feat: adds browser locale as default in payment page in case there's no settings set by the customer * feat: adds browser locale as default in success page * fix: deconstruct props so eslint passes * fix: lint issue for extra empty line in meeting-ended uid page Co-authored-by: Agusti Fernandez Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- apps/web/components/booking/TimeOptions.tsx | 9 ++++----- .../components/booking/pages/AvailabilityPage.tsx | 5 ++++- apps/web/components/booking/pages/BookingPage.tsx | 5 ++--- apps/web/ee/components/stripe/PaymentPage.tsx | 3 ++- apps/web/lib/clock.ts | 4 ++++ apps/web/lib/timeFormat.ts | 12 ++++++++++++ apps/web/pages/cancel/[uid].tsx | 4 ++-- apps/web/pages/success.tsx | 3 ++- apps/web/pages/video/meeting-ended/[uid].tsx | 9 +++------ apps/web/pages/video/meeting-not-started/[uid].tsx | 5 ++--- 10 files changed, 37 insertions(+), 22 deletions(-) create mode 100644 apps/web/lib/timeFormat.ts diff --git a/apps/web/components/booking/TimeOptions.tsx b/apps/web/components/booking/TimeOptions.tsx index ee30239bae..d37f2047c0 100644 --- a/apps/web/components/booking/TimeOptions.tsx +++ b/apps/web/components/booking/TimeOptions.tsx @@ -13,7 +13,7 @@ type Props = { onToggle24hClock: (is24hClock: boolean) => void; }; -const TimeOptions: FC = (props) => { +const TimeOptions: FC = ({ onToggle24hClock, onSelectTimeZone }) => { const [selectedTimeZone, setSelectedTimeZone] = useState(""); const [is24hClock, setIs24hClock] = useState(false); const { t } = useLocale(); @@ -25,13 +25,12 @@ const TimeOptions: FC = (props) => { useEffect(() => { if (selectedTimeZone && timeZone() && selectedTimeZone !== timeZone()) { - props.onSelectTimeZone(timeZone(selectedTimeZone)); + onSelectTimeZone(timeZone(selectedTimeZone)); } - }, [selectedTimeZone]); - + }, [selectedTimeZone, onSelectTimeZone]); const handle24hClockToggle = (is24hClock: boolean) => { setIs24hClock(is24hClock); - props.onToggle24hClock(is24h(is24hClock)); + onToggle24hClock(is24h(is24hClock)); }; return selectedTimeZone !== "" ? ( diff --git a/apps/web/components/booking/pages/AvailabilityPage.tsx b/apps/web/components/booking/pages/AvailabilityPage.tsx index 0691917d68..c8f426a348 100644 --- a/apps/web/components/booking/pages/AvailabilityPage.tsx +++ b/apps/web/components/booking/pages/AvailabilityPage.tsx @@ -15,6 +15,7 @@ import { useLocale } from "@lib/hooks/useLocale"; import useTheme from "@lib/hooks/useTheme"; import { isBrandingHidden } from "@lib/isBrandingHidden"; import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@lib/telemetry"; +import { detectBrowserTimeFormat } from "@lib/timeFormat"; import CustomBranding from "@components/CustomBranding"; import AvailableTimes from "@components/booking/AvailableTimes"; @@ -62,11 +63,13 @@ const AvailabilityPage = ({ profile, eventType, workingHours }: Props) => { }, [router.query.date]); const [isTimeOptionsOpen, setIsTimeOptionsOpen] = useState(false); - const [timeFormat, setTimeFormat] = useState("h:mma"); + const [timeFormat, setTimeFormat] = useState(detectBrowserTimeFormat); + const telemetry = useTelemetry(); useEffect(() => { handleToggle24hClock(localStorage.getItem("timeOption.is24hClock") === "true"); + telemetry.withJitsu((jitsu) => jitsu.track(telemetryEventTypes.pageView, collectPageParameters())); }, [telemetry]); diff --git a/apps/web/components/booking/pages/BookingPage.tsx b/apps/web/components/booking/pages/BookingPage.tsx index 68e9c309f2..2d3271612b 100644 --- a/apps/web/components/booking/pages/BookingPage.tsx +++ b/apps/web/components/booking/pages/BookingPage.tsx @@ -24,6 +24,7 @@ import createBooking from "@lib/mutations/bookings/create-booking"; import { parseZone } from "@lib/parseZone"; import slugify from "@lib/slugify"; import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@lib/telemetry"; +import { detectBrowserTimeFormat } from "@lib/timeFormat"; import CustomBranding from "@components/CustomBranding"; import { EmailInput, Form } from "@components/form/fields"; @@ -110,9 +111,7 @@ const BookingPage = (props: BookingPageProps) => { const rescheduleUid = router.query.rescheduleUid as string; const { isReady, Theme } = useTheme(props.profile.theme); - const date = asStringOrNull(router.query.date); - const timeFormat = asStringOrNull(router.query.clock) === "24h" ? "H:mm" : "h:mma"; const [guestToggle, setGuestToggle] = useState(props.booking && props.booking.attendees.length > 1); @@ -213,7 +212,7 @@ const BookingPage = (props: BookingPageProps) => { if (!date) return "No date"; const parsedZone = parseZone(date); if (!parsedZone?.isValid()) return "Invalid date"; - const formattedTime = parsedZone?.format(timeFormat); + const formattedTime = parsedZone?.format(detectBrowserTimeFormat); return formattedTime + ", " + dayjs(date).toDate().toLocaleString(i18n.language, { dateStyle: "full" }); }; diff --git a/apps/web/ee/components/stripe/PaymentPage.tsx b/apps/web/ee/components/stripe/PaymentPage.tsx index 0587031c4f..28ac7334bd 100644 --- a/apps/web/ee/components/stripe/PaymentPage.tsx +++ b/apps/web/ee/components/stripe/PaymentPage.tsx @@ -14,6 +14,7 @@ import { PaymentPageProps } from "@ee/pages/payment/[uid]"; import { useLocale } from "@lib/hooks/useLocale"; import useTheme from "@lib/hooks/useTheme"; +import { isBrowserLocale24h } from "@lib/timeFormat"; dayjs.extend(utc); dayjs.extend(toArray); @@ -21,7 +22,7 @@ dayjs.extend(timezone); const PaymentPage: FC = (props) => { const { t } = useLocale(); - const [is24h, setIs24h] = useState(false); + const [is24h, setIs24h] = useState(isBrowserLocale24h()); const [date, setDate] = useState(dayjs.utc(props.booking.startTime)); const { isReady, Theme } = useTheme(props.profile.theme); diff --git a/apps/web/lib/clock.ts b/apps/web/lib/clock.ts index 59fb55e7f1..75b5939cb1 100644 --- a/apps/web/lib/clock.ts +++ b/apps/web/lib/clock.ts @@ -3,6 +3,8 @@ import dayjs from "dayjs"; import timezone from "dayjs/plugin/timezone"; import utc from "dayjs/plugin/utc"; +import { isBrowserLocale24h } from "./timeFormat"; + dayjs.extend(utc); dayjs.extend(timezone); @@ -22,6 +24,8 @@ const initClock = () => { if (typeof localStorage === "undefined" || isInitialized) { return; } + // This only sets browser locale if there's no preference on localStorage. + if (!localStorage || !localStorage.getItem("timeOption.is24hClock")) set24hClock(isBrowserLocale24h()); timeOptions.is24hClock = localStorage.getItem("timeOption.is24hClock") === "true"; timeOptions.inviteeTimeZone = localStorage.getItem("timeOption.preferredTimeZone") || dayjs.tz.guess(); }; diff --git a/apps/web/lib/timeFormat.ts b/apps/web/lib/timeFormat.ts new file mode 100644 index 0000000000..6ce6408b04 --- /dev/null +++ b/apps/web/lib/timeFormat.ts @@ -0,0 +1,12 @@ +/* + * Detects navigator locale 24h time preference + * It works by checking whether hour output contains AM ('1 AM' or '01 h') + * based on the user's preferred language + * defaults to 'en-US' (12h) if no navigator language is found + */ +export const isBrowserLocale24h = () => { + let locale = "en-US"; + if (process.browser && navigator) locale = navigator?.language; + return !new Intl.DateTimeFormat(locale, { hour: "numeric" }).format(0).match(/AM/); +}; +export const detectBrowserTimeFormat = isBrowserLocale24h() ? "H:mm" : "h:mma"; diff --git a/apps/web/pages/cancel/[uid].tsx b/apps/web/pages/cancel/[uid].tsx index 41b1d89ff5..3615bb1994 100644 --- a/apps/web/pages/cancel/[uid].tsx +++ b/apps/web/pages/cancel/[uid].tsx @@ -9,6 +9,7 @@ import { getSession } from "@lib/auth"; import { useLocale } from "@lib/hooks/useLocale"; import prisma from "@lib/prisma"; import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@lib/telemetry"; +import { detectBrowserTimeFormat } from "@lib/timeFormat"; import { inferSSRProps } from "@lib/types/inferSSRProps"; import CustomBranding from "@components/CustomBranding"; @@ -23,7 +24,6 @@ export default function Type(props: inferSSRProps) { // Get router variables const router = useRouter(); const { uid } = router.query; - const [is24h] = useState(false); const [loading, setLoading] = useState(false); const [error, setError] = useState(props.booking ? null : t("booking_already_cancelled")); const [cancellationReason, setCancellationReason] = useState(""); @@ -84,7 +84,7 @@ export default function Type(props: inferSSRProps) {

    {dayjs(props.booking?.startTime).format( - (is24h ? "H:mm" : "h:mma") + ", dddd DD MMMM YYYY" + detectBrowserTimeFormat + ", dddd DD MMMM YYYY" )}

diff --git a/apps/web/pages/success.tsx b/apps/web/pages/success.tsx index e374e2c0b4..3697cd4d00 100644 --- a/apps/web/pages/success.tsx +++ b/apps/web/pages/success.tsx @@ -16,6 +16,7 @@ import { useLocale } from "@lib/hooks/useLocale"; import useTheme from "@lib/hooks/useTheme"; import { isBrandingHidden } from "@lib/isBrandingHidden"; import prisma from "@lib/prisma"; +import { isBrowserLocale24h } from "@lib/timeFormat"; import { inferSSRProps } from "@lib/types/inferSSRProps"; import CustomBranding from "@components/CustomBranding"; @@ -34,8 +35,8 @@ export default function Success(props: inferSSRProps) const router = useRouter(); const { location: _location, name, reschedule } = router.query; const location = Array.isArray(_location) ? _location[0] : _location; + const [is24h, setIs24h] = useState(isBrowserLocale24h()); - const [is24h, setIs24h] = useState(false); const [date, setDate] = useState(dayjs.utc(asStringOrThrow(router.query.date))); const { isReady, Theme } = useTheme(props.profile.theme); diff --git a/apps/web/pages/video/meeting-ended/[uid].tsx b/apps/web/pages/video/meeting-ended/[uid].tsx index 30a639055b..19daaaf4ca 100644 --- a/apps/web/pages/video/meeting-ended/[uid].tsx +++ b/apps/web/pages/video/meeting-ended/[uid].tsx @@ -4,10 +4,10 @@ import dayjs from "dayjs"; import { NextPageContext } from "next"; import { getSession } from "next-auth/react"; import { useRouter } from "next/router"; -import { useState } from "react"; import { useEffect } from "react"; import prisma from "@lib/prisma"; +import { detectBrowserTimeFormat } from "@lib/timeFormat"; import { inferSSRProps } from "@lib/types/inferSSRProps"; import { HeadSeo } from "@components/seo/head-seo"; @@ -15,10 +15,7 @@ import Button from "@components/ui/Button"; export default function MeetingUnavailable(props: inferSSRProps) { const router = useRouter(); - - const [is24h, setIs24h] = useState(false); - - //if no booking redirectis to the 404 page + // if no booking redirectis to the 404 page const emptyBooking = props.booking === null; useEffect(() => { if (emptyBooking) { @@ -57,7 +54,7 @@ export default function MeetingUnavailable(props: inferSSRProps {dayjs(props.booking.startTime).format( - (is24h ? "H:mm" : "h:mma") + ", dddd DD MMMM YYYY" + detectBrowserTimeFormat + ", dddd DD MMMM YYYY" )}

diff --git a/apps/web/pages/video/meeting-not-started/[uid].tsx b/apps/web/pages/video/meeting-not-started/[uid].tsx index 65dc056916..7e5268ffa7 100644 --- a/apps/web/pages/video/meeting-not-started/[uid].tsx +++ b/apps/web/pages/video/meeting-not-started/[uid].tsx @@ -4,10 +4,10 @@ import dayjs from "dayjs"; import { NextPageContext } from "next"; import { getSession } from "next-auth/react"; import { useRouter } from "next/router"; -import { useState } from "react"; import { useEffect } from "react"; import prisma from "@lib/prisma"; +import { detectBrowserTimeFormat } from "@lib/timeFormat"; import { inferSSRProps } from "@lib/types/inferSSRProps"; import { HeadSeo } from "@components/seo/head-seo"; @@ -23,7 +23,6 @@ export default function MeetingNotStarted(props: inferSSRProps @@ -56,7 +55,7 @@ export default function MeetingNotStarted(props: inferSSRProps {dayjs(props.booking.startTime).format( - (is24h ? "H:mm" : "h:mma") + ", dddd DD MMMM YYYY" + detectBrowserTimeFormat + ", dddd DD MMMM YYYY" )}

From 9e89f954e8568edcdb6a58c2e8cbc92b3695c175 Mon Sep 17 00:00:00 2001 From: Peer Richelsen Date: Wed, 23 Feb 2022 13:55:59 +0000 Subject: [PATCH 6/6] adding border to avatargroup based on the parent background color (#1954) --- apps/web/components/booking/pages/AvailabilityPage.tsx | 2 ++ apps/web/components/booking/pages/BookingPage.tsx | 1 + apps/web/components/ui/AvatarGroup.tsx | 3 ++- apps/web/pages/event-types/index.tsx | 1 + apps/web/pages/team/[slug].tsx | 1 + 5 files changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/web/components/booking/pages/AvailabilityPage.tsx b/apps/web/components/booking/pages/AvailabilityPage.tsx index c8f426a348..507215d96b 100644 --- a/apps/web/components/booking/pages/AvailabilityPage.tsx +++ b/apps/web/components/booking/pages/AvailabilityPage.tsx @@ -123,6 +123,7 @@ const AvailabilityPage = ({ profile, eventType, workingHours }: Props) => {
{ (selectedDate ? "sm:w-1/3" : "sm:w-1/2") }> {
{ if (item.image != null) { return ( -
  • +
  • ); diff --git a/apps/web/pages/event-types/index.tsx b/apps/web/pages/event-types/index.tsx index 789d764e8f..37d190ae6c 100644 --- a/apps/web/pages/event-types/index.tsx +++ b/apps/web/pages/event-types/index.tsx @@ -159,6 +159,7 @@ const EventTypeList = ({ readOnly, types, profile }: EventTypeListProps): JSX.El
    {type.users?.length > 1 && ( ({ diff --git a/apps/web/pages/team/[slug].tsx b/apps/web/pages/team/[slug].tsx index 2535d3010b..290346be2f 100644 --- a/apps/web/pages/team/[slug].tsx +++ b/apps/web/pages/team/[slug].tsx @@ -42,6 +42,7 @@ function TeamPage({ team }: TeamPageProps) {