Compare commits

...
12 Commits
Author SHA1 Message Date
Alex van Andel 3b51cc9aef v2.5.8
release.yml / release (push) Canceled after 0s
2023-02-02 15:54:38 +00:00
Hariom Balhara 50c8959cfa Use trackingId only for trackingDomain as thats the only variable supported (#6856) 2023-02-02 15:13:42 +00:00
Hariom Balhara c96d258f5e Give highest possible value to z-index (#6853) 2023-02-02 15:04:38 +00:00
Alex van Andel 6e45b1c383 Don't override but decorate the built-in encode function (#6850)
* Don't override but decorate the built-in encode function

* Removed jose + synced lockfile
2023-02-02 14:26:00 +00:00
GitStart-Cal.comandgitstart-calcom 551ebe863b [CAL-90] Event Types Single / Allow multiple physical locations (#6692)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-02-02 12:42:56 +00:00
Peer Richelsen fa7b94bfe9 Merge branch 'main' of https://github.com/calcom/cal.com 2023-02-02 13:33:49 +01:00
Peer Richelsen 56c5d39cd3 i18n fix 2023-02-02 12:50:28 +01:00
Peer Richelsen 7574fb6f44 i18n fix 2023-02-02 12:49:11 +01:00
Nafees Nazik 901d1f0e01 fix: make cal video default value (#6716) 2023-02-02 12:24:34 +01:00
GitStart-Cal.comandgitstart-calcom 828fcbe1d9 [CAL-713] /availability single - layout issues. Check spacing, margin, alignment with spec'd design (#6724)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-02-02 12:21:05 +01:00
Joe Au-Yeung 34db46869e Move e2e to buildjet (#6840) 2023-02-02 08:42:28 +00:00
Nafees Nazik 1f1cab2be1 fix: padding in shell for mobile (#6825)
* fix: padding

* fix: breakpoints in fab

* fix: padding
2023-02-02 09:24:31 +01:00
21 changed files with 1031 additions and 152 deletions

No files matched your search

+1 -1
View File
@@ -13,7 +13,7 @@ concurrency:
jobs: jobs:
e2e: e2e:
timeout-minutes: 20 timeout-minutes: 20
runs-on: ubuntu-latest runs-on: buildjet-4vcpu-ubuntu-2204
env: env:
DATABASE_URL: postgresql://postgres:@localhost:5432/calendso DATABASE_URL: postgresql://postgres:@localhost:5432/calendso
+33 -10
View File
@@ -9,7 +9,12 @@ import { Controller, useForm, useFormContext } from "react-hook-form";
import { MultiValue } from "react-select"; import { MultiValue } from "react-select";
import { z } from "zod"; import { z } from "zod";
import { EventLocationType, getEventLocationType, MeetLocationType } from "@calcom/app-store/locations"; import {
EventLocationType,
getEventLocationType,
MeetLocationType,
LocationType,
} from "@calcom/app-store/locations";
import { CAL_URL } from "@calcom/lib/constants"; import { CAL_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale"; import { useLocale } from "@calcom/lib/hooks/useLocale";
import { slugify } from "@calcom/lib/slugify"; import { slugify } from "@calcom/lib/slugify";
@@ -34,6 +39,16 @@ const getLocationFromType = (
} }
}; };
const getDefaultLocationValue = (options: EventTypeSetupProps["locationOptions"], type: string) => {
for (const locationType of options) {
for (const location of locationType.options) {
if (location.value === type && location.disabled === false) {
return location;
}
}
}
};
export const EventSetupTab = ( export const EventSetupTab = (
props: Pick<EventTypeSetupProps, "eventType" | "locationOptions" | "team" | "teamMembers"> props: Pick<EventTypeSetupProps, "eventType" | "locationOptions" | "team" | "teamMembers">
) => { ) => {
@@ -69,7 +84,12 @@ export const EventSetupTab = (
const removeLocation = (selectedLocation: typeof eventType.locations[number]) => { const removeLocation = (selectedLocation: typeof eventType.locations[number]) => {
formMethods.setValue( formMethods.setValue(
"locations", "locations",
formMethods.getValues("locations").filter((location) => location.type !== selectedLocation.type), formMethods.getValues("locations").filter((location) => {
if (location.type === LocationType.InPerson) {
return location.address !== selectedLocation.address;
}
return location.type !== selectedLocation.type;
}),
{ shouldValidate: true } { shouldValidate: true }
); );
}; };
@@ -84,14 +104,14 @@ export const EventSetupTab = (
...details, ...details,
type: newLocationType, type: newLocationType,
}; };
} else {
copy[existingIdx] = {
...formMethods.getValues("locations")[existingIdx],
...details,
};
} }
formMethods.setValue("locations", copy); formMethods.setValue("locations", [
...copy,
...(newLocationType === LocationType.InPerson && editingLocationType === ""
? [{ ...details, type: newLocationType }]
: []),
]);
} else { } else {
formMethods.setValue( formMethods.setValue(
"locations", "locations",
@@ -138,7 +158,8 @@ export const EventSetupTab = (
return true; return true;
}); });
const defaultValue = locationOptions.find((item) => item.label === "video")?.options; const defaultValue = getDefaultLocationValue(locationOptions, "integrations:daily");
return ( return (
<div className="w-full"> <div className="w-full">
{validLocations.length === 0 && ( {validLocations.length === 0 && (
@@ -176,7 +197,9 @@ export const EventSetupTab = (
return null; return null;
} }
return ( return (
<li key={location.type} className="mb-2 rounded-md border border-gray-300 py-1.5 px-2"> <li
key={`${location.type}${index}`}
className="mb-2 rounded-md border border-gray-300 py-1.5 px-2">
<div className="flex max-w-full justify-between"> <div className="flex max-w-full justify-between">
<div key={index} className="flex flex-grow items-center"> <div key={index} className="flex flex-grow items-center">
<img <img
+1 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@calcom/web", "name": "@calcom/web",
"version": "2.5.7", "version": "2.5.8",
"private": true, "private": true,
"scripts": { "scripts": {
"analyze": "ANALYZE=true next build", "analyze": "ANALYZE=true next build",
@@ -74,7 +74,6 @@
"handlebars": "^4.7.7", "handlebars": "^4.7.7",
"ical.js": "^1.4.0", "ical.js": "^1.4.0",
"ics": "^2.37.0", "ics": "^2.37.0",
"jose": "^4.11.1",
"kbar": "^0.1.0-beta.36", "kbar": "^0.1.0-beta.36",
"libphonenumber-js": "^1.10.12", "libphonenumber-js": "^1.10.12",
"lodash": "^4.17.21", "lodash": "^4.17.21",
+24 -34
View File
@@ -1,9 +1,8 @@
import { IdentityProvider, UserPermissionRole } from "@prisma/client"; import { IdentityProvider, UserPermissionRole } from "@prisma/client";
import { BinaryLike, hkdfSync, KeyObject } from "crypto";
import { readFileSync } from "fs"; import { readFileSync } from "fs";
import Handlebars from "handlebars"; import Handlebars from "handlebars";
import * as jose from "jose";
import NextAuth, { Session } from "next-auth"; import NextAuth, { Session } from "next-auth";
import { encode } from "next-auth/jwt";
import { Provider } from "next-auth/providers"; import { Provider } from "next-auth/providers";
import CredentialsProvider from "next-auth/providers/credentials"; import CredentialsProvider from "next-auth/providers/credentials";
import EmailProvider from "next-auth/providers/email"; import EmailProvider from "next-auth/providers/email";
@@ -11,7 +10,6 @@ import GoogleProvider from "next-auth/providers/google";
import nodemailer, { TransportOptions } from "nodemailer"; import nodemailer, { TransportOptions } from "nodemailer";
import { authenticator } from "otplib"; import { authenticator } from "otplib";
import path from "path"; import path from "path";
import { v4 as uuidv4 } from "uuid";
import checkLicense from "@calcom/features/ee/common/server/checkLicense"; import checkLicense from "@calcom/features/ee/common/server/checkLicense";
import ImpersonationProvider from "@calcom/features/ee/impersonation/lib/ImpersonationProvider"; import ImpersonationProvider from "@calcom/features/ee/impersonation/lib/ImpersonationProvider";
@@ -238,10 +236,13 @@ if (true) {
}) })
); );
} }
function isNumber(n: string) {
return !isNaN(parseFloat(n)) && !isNaN(+n);
}
const calcomAdapter = CalComAdapter(prisma); const calcomAdapter = CalComAdapter(prisma);
const getDerivedEncryptionKey = async (secret: BinaryLike | KeyObject) => {
return await hkdfSync("sha256", secret, "", "NextAuth.js Generated Encryption Key", 32);
};
export default NextAuth({ export default NextAuth({
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
@@ -250,34 +251,23 @@ export default NextAuth({
strategy: "jwt", strategy: "jwt",
}, },
jwt: { jwt: {
encode: async ({ secret, token }) => { // decorate the native JWT encode function
if (!token || token.sub === undefined) throw new Error("Not valid token"); // Impl. detail: We don't pass through as this function is called with encode/decode functions.
const user = await prisma.user.findFirst({ encode: async ({ token, maxAge, secret }) => {
where: { id: Number(token.sub) }, if (token?.sub && isNumber(token.sub)) {
select: { metadata: true }, const user = await prisma.user.findFirst({
}); where: { id: Number(token.sub) },
const encryptionSecret = await getDerivedEncryptionKey(secret); select: { metadata: true },
const metadata = userMetadata.parse(user?.metadata); });
return await new jose.EncryptJWT({ // if no user is found, we still don't want to crash here.
sub: token?.sub, if (user) {
name: token?.name, const metadata = userMetadata.parse(user.metadata);
email: token?.email, if (metadata?.sessionTimeout) {
}) maxAge = metadata.sessionTimeout / 60;
.setProtectedHeader({ }
alg: "dir", }
enc: "A256GCM", }
}) return encode({ secret, token, maxAge });
.setIssuedAt()
.setExpirationTime(metadata?.sessionTimeout ? `${metadata.sessionTimeout}m` : "30d")
.setJti(uuidv4())
.encrypt(new Uint8Array(encryptionSecret));
},
decode: async ({ secret, token }) => {
const encryptionSecret = await getDerivedEncryptionKey(secret);
const { payload } = await jose.jwtDecrypt(token || "", new Uint8Array(encryptionSecret), {
clockTolerance: 15,
});
return payload;
}, },
}, },
cookies: defaultCookies(WEBAPP_URL?.startsWith("https://")), cookies: defaultCookies(WEBAPP_URL?.startsWith("https://")),
+26 -22
View File
@@ -51,7 +51,7 @@ const DateOverride = ({ workingHours }: { workingHours: WorkingHours[] }) => {
}); });
const { t } = useLocale(); const { t } = useLocale();
return ( return (
<div className="px-4 py-5 sm:p-6"> <div className="p-6">
<h3 className="font-medium leading-6 text-gray-900"> <h3 className="font-medium leading-6 text-gray-900">
{t("date_overrides")}{" "} {t("date_overrides")}{" "}
<Tooltip content={t("date_overrides_info")}> <Tooltip content={t("date_overrides_info")}>
@@ -60,8 +60,8 @@ const DateOverride = ({ workingHours }: { workingHours: WorkingHours[] }) => {
</span> </span>
</Tooltip> </Tooltip>
</h3> </h3>
<p className="mb-4 text-sm text-gray-500 ltr:mr-4 rtl:ml-4">{t("date_overrides_subtitle")}</p> <p className="mb-4 text-sm text-gray-500">{t("date_overrides_subtitle")}</p>
<div className="mt-1 space-y-2"> <div className="space-y-2">
<DateOverrideList <DateOverrideList
excludedDates={fields.map((field) => yyyymmdd(field.ranges[0].start))} excludedDates={fields.map((field) => yyyymmdd(field.ranges[0].start))}
remove={remove} remove={remove}
@@ -148,7 +148,7 @@ export default function Availability({ schedule }: { schedule: number }) {
) )
} }
CTA={ CTA={
<div className="flex items-center justify-end"> <div className="mr-10 flex items-center justify-end xl:mr-36">
<div className="flex items-center rounded-md px-2 sm:hover:bg-gray-100"> <div className="flex items-center rounded-md px-2 sm:hover:bg-gray-100">
<Skeleton <Skeleton
as={Label} as={Label}
@@ -185,25 +185,29 @@ export default function Availability({ schedule }: { schedule: number }) {
...values, ...values,
}); });
}} }}
className="flex flex-col pb-16 sm:mx-0 xl:flex-row xl:space-x-6"> className="flex flex-col sm:mx-0 xl:flex-row xl:space-x-6">
<div className="flex-1 divide-y divide-gray-200 rounded-md border"> <div className="mx-10 flex-1 flex-row xl:mr-0 xl:ml-36">
<div className=" py-5 sm:p-6"> <div className="mb-6 rounded-md border">
{typeof me.data?.weekStart === "string" && ( <div>
<Schedule {typeof me.data?.weekStart === "string" && (
control={control} <Schedule
name="schedule" control={control}
weekStart={ name="schedule"
["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"].indexOf( weekStart={
me.data?.weekStart ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"].indexOf(
) as 0 | 1 | 2 | 3 | 4 | 5 | 6 me.data?.weekStart
} ) as 0 | 1 | 2 | 3 | 4 | 5 | 6
/> }
)} />
)}
</div>
</div>
<div className="my-6 rounded-md border">
{data?.workingHours && <DateOverride workingHours={data.workingHours} />}
</div> </div>
{data?.workingHours && <DateOverride workingHours={data.workingHours} />}
</div> </div>
<div className="min-w-40 col-span-3 space-y-2 lg:col-span-1"> <div className="min-w-40 col-span-3 ml-10 space-y-2 lg:col-span-1 xl:ml-36">
<div className="xl:max-w-80 mt-4 w-full pr-4 sm:p-0"> <div className="xl:max-w-80 w-full pr-4 sm:ml-0 sm:mr-36 sm:p-0">
<div> <div>
<label htmlFor="timeZone" className="block text-sm font-medium text-gray-700"> <label htmlFor="timeZone" className="block text-sm font-medium text-gray-700">
{t("timezone")} {t("timezone")}
@@ -223,7 +227,7 @@ export default function Availability({ schedule }: { schedule: number }) {
} }
/> />
</div> </div>
<hr className="my-8" /> <hr className="my-6 mr-8" />
<div className="rounded-md"> <div className="rounded-md">
<h3 className="text-sm font-medium text-gray-900">{t("something_doesnt_look_right")}</h3> <h3 className="text-sm font-medium text-gray-900">{t("something_doesnt_look_right")}</h3>
<div className="mt-3 flex"> <div className="mt-3 flex">
@@ -1562,6 +1562,7 @@
"recommended_next_steps": "Recommended next steps", "recommended_next_steps": "Recommended next steps",
"create_a_managed_event": "Create a managed event type", "create_a_managed_event": "Create a managed event type",
"meetings_are_better_with_the_right": "Meetings are better with the right team members there. Invite them now.", "meetings_are_better_with_the_right": "Meetings are better with the right team members there. Invite them now.",
"create_a_one_one_template": "Create a one-one one template for an event type and distribute it to multiple members.",
"collective_or_roundrobin": "Collective or round-robin", "collective_or_roundrobin": "Collective or round-robin",
"book_your_team_members": "Book your team members together with collective events or cycle through to get the right person with round-robin." "book_your_team_members": "Book your team members together with collective events or cycle through to get the right person with round-robin."
} }
@@ -22,6 +22,7 @@ export default function BookingPageTagManager({
return null; return null;
} }
const parseValue = <T extends string | undefined>(val: T): T => const parseValue = <T extends string | undefined>(val: T): T =>
//TODO: Support more template variables.
val ? (val.replace(/\{TRACKING_ID\}/g, trackingId) as T) : val; val ? (val.replace(/\{TRACKING_ID\}/g, trackingId) as T) : val;
return tag.scripts.map((script, index) => { return tag.scripts.map((script, index) => {
@@ -20,7 +20,6 @@ export const InstallAppButtonMap = {
larkcalendar: dynamic(() => import("./larkcalendar/components/InstallAppButton")), larkcalendar: dynamic(() => import("./larkcalendar/components/InstallAppButton")),
office365calendar: dynamic(() => import("./office365calendar/components/InstallAppButton")), office365calendar: dynamic(() => import("./office365calendar/components/InstallAppButton")),
office365video: dynamic(() => import("./office365video/components/InstallAppButton")), office365video: dynamic(() => import("./office365video/components/InstallAppButton")),
plausible: dynamic(() => import("./plausible/components/InstallAppButton")),
riverside: dynamic(() => import("./riverside/components/InstallAppButton")), riverside: dynamic(() => import("./riverside/components/InstallAppButton")),
tandemvideo: dynamic(() => import("./tandemvideo/components/InstallAppButton")), tandemvideo: dynamic(() => import("./tandemvideo/components/InstallAppButton")),
vital: dynamic(() => import("./vital/components/InstallAppButton")), vital: dynamic(() => import("./vital/components/InstallAppButton")),
@@ -9,7 +9,7 @@ import { appDataSchema } from "../zod";
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app }) { const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app }) {
const [getAppData, setAppData] = useAppContextWithSchema<typeof appDataSchema>(); const [getAppData, setAppData] = useAppContextWithSchema<typeof appDataSchema>();
const trackedDomain = getAppData("trackedDomain"); const trackingId = getAppData("trackingId");
const [enabled, setEnabled] = useState(getAppData("enabled")); const [enabled, setEnabled] = useState(getAppData("enabled"));
return ( return (
@@ -26,9 +26,9 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
switchChecked={enabled}> switchChecked={enabled}>
<TextField <TextField
name="Tracked Domain" name="Tracked Domain"
value={trackedDomain} value={trackingId}
onChange={(e) => { onChange={(e) => {
setAppData("trackedDomain", e.target.value); setAppData("trackingId", e.target.value);
}} }}
/> />
</AppCard> </AppCard>
@@ -1,18 +0,0 @@
import type { InstallAppButtonProps } from "@calcom/app-store/types";
import useAddAppMutation from "../../_utils/useAddAppMutation";
export default function InstallAppButton(props: InstallAppButtonProps) {
const mutation = useAddAppMutation("plausible_analytics");
return (
<>
{props.render({
onClick() {
mutation.mutate("");
},
loading: mutation.isLoading,
})}
</>
);
}
@@ -1 +0,0 @@
export { default as InstallAppButton } from "./InstallAppButton";
+1 -1
View File
@@ -17,7 +17,7 @@
{ {
"src": "https://plausible.io/js/script.js", "src": "https://plausible.io/js/script.js",
"attrs": { "attrs": {
"data-domain": "{TRACKED_DOMAIN}" "data-domain": "{TRACKING_ID}"
} }
} }
] ]
+1 -1
View File
@@ -4,7 +4,7 @@ import { eventTypeAppCardZod } from "../eventTypeAppCardZod";
export const appDataSchema = eventTypeAppCardZod.merge( export const appDataSchema = eventTypeAppCardZod.merge(
z.object({ z.object({
trackedDomain: z.string(), trackingId: z.string(),
}) })
); );
@@ -11,7 +11,7 @@ const getHtml = ({
}) => { }) => {
// IT IS A REQUIREMENT THAT ALL POSSIBLE CLASSES ARE HERE OTHERWISE TAILWIND WONT GENERATE THE CSS FOR CONDITIONAL CLASSES // IT IS A REQUIREMENT THAT ALL POSSIBLE CLASSES ARE HERE OTHERWISE TAILWIND WONT GENERATE THE CSS FOR CONDITIONAL CLASSES
// To not let all these classes apply and visible, keep it hidden initially // To not let all these classes apply and visible, keep it hidden initially
return `<button class="hidden fixed md:bottom-6 bottom-4 md:right-10 right-4 md:left-10 left-4 ${buttonClasses.join( return `<button class="z-[10000000000] hidden fixed md:bottom-6 bottom-4 md:right-10 right-4 md:left-10 left-4 ${buttonClasses.join(
" " " "
)} flex h-16 origin-center bg-red-50 transform cursor-pointer items-center )} flex h-16 origin-center bg-red-50 transform cursor-pointer items-center
rounded-full py-4 px-6 text-base outline-none drop-shadow-md transition focus:outline-none fo rounded-full py-4 px-6 text-base outline-none drop-shadow-md transition focus:outline-none fo
@@ -66,7 +66,7 @@ export default function TeamList(props: Props) {
icon={<FiUserPlus className="h-5 w-5 text-green-700" />} icon={<FiUserPlus className="h-5 w-5 text-green-700" />}
variant="basic" variant="basic"
title={t("invite_team_member")} title={t("invite_team_member")}
description="Meetings are better with the right team members there. Invite them now." description={t("meetings_are_better_with_the_right")}
actionButton={{ actionButton={{
href: "/settings/teams/" + team.id + "/members", href: "/settings/teams/" + team.id + "/members",
child: t("invite"), child: t("invite"),
@@ -77,7 +77,7 @@ export default function TeamList(props: Props) {
icon={<FiUnlock className="h-5 w-5 text-blue-700" />} icon={<FiUnlock className="h-5 w-5 text-blue-700" />}
variant="basic" variant="basic"
title={t("create_a_managed_event")} title={t("create_a_managed_event")}
description={t("meetings_are_better_with_the_right")} description={t("create_a_one_one_template")}
actionButton={{ actionButton={{
href: href:
"/event-types?dialog=new-eventtype&eventPage=team%2F" + "/event-types?dialog=new-eventtype&eventPage=team%2F" +
@@ -51,9 +51,9 @@ const ScheduleDay = <TFieldValues extends FieldValues>({
const watchDayRange = watch(name); const watchDayRange = watch(name);
return ( return (
<div className="mb-1 flex w-full flex-col px-5 py-1 sm:flex-row sm:px-0"> <div className="mb-4 flex w-full flex-col last:mb-0 sm:flex-row sm:px-0">
{/* Label & switch container */} {/* Label & switch container */}
<div className="flex h-11 items-center justify-between sm:w-32"> <div className="flex h-[36px] items-center justify-between sm:w-32">
<div> <div>
<label className="flex flex-row items-center space-x-2 rtl:space-x-reverse"> <label className="flex flex-row items-center space-x-2 rtl:space-x-reverse">
<div> <div>
@@ -67,9 +67,7 @@ const ScheduleDay = <TFieldValues extends FieldValues>({
/> />
</div> </div>
<span className="inline-block min-w-[88px] text-sm capitalize">{weekday}</span> <span className="inline-block min-w-[88px] text-sm capitalize">{weekday}</span>
{watchDayRange && !!watchDayRange.length && ( {watchDayRange && !!watchDayRange.length && <div className="sm:hidden">{CopyButton}</div>}
<div className="mt-1 mb-1 sm:hidden">{CopyButton}</div>
)}
</label> </label>
</div> </div>
</div> </div>
@@ -77,7 +75,7 @@ const ScheduleDay = <TFieldValues extends FieldValues>({
{watchDayRange ? ( {watchDayRange ? (
<div className="flex sm:ml-2"> <div className="flex sm:ml-2">
<DayRanges control={control} name={name} /> <DayRanges control={control} name={name} />
{!!watchDayRange.length && <div className="mt-1 hidden sm:block">{CopyButton}</div>} {!!watchDayRange.length && <div className="hidden sm:block">{CopyButton}</div>}
</div> </div>
) : ( ) : (
<SkeletonText className="mt-2.5 ml-1 h-6 w-48" /> <SkeletonText className="mt-2.5 ml-1 h-6 w-48" />
@@ -142,7 +140,7 @@ const Schedule = <
const { i18n } = useLocale(); const { i18n } = useLocale();
return ( return (
<div className="divide-y sm:divide-none"> <div className="divide-y p-4 sm:divide-none">
{/* First iterate for each day */} {/* First iterate for each day */}
{weekdayNames(i18n.language, weekStart, "long").map((weekday, num) => { {weekdayNames(i18n.language, weekStart, "long").map((weekday, num) => {
const weekdayIndex = (num + weekStart) % 7; const weekdayIndex = (num + weekStart) % 7;
@@ -179,7 +177,7 @@ export const DayRanges = <TFieldValues extends FieldValues>({
<div> <div>
{fields.map((field, index: number) => ( {fields.map((field, index: number) => (
<Fragment key={field.id}> <Fragment key={field.id}>
<div className="mb-2 flex first:mt-1"> <div className="mb-2 flex last:mb-0">
<Controller name={`${name}.${index}`} render={({ field }) => <TimeRangeField {...field} />} /> <Controller name={`${name}.${index}`} render={({ field }) => <TimeRangeField {...field} />} />
{index === 0 && ( {index === 0 && (
<Button <Button
@@ -230,7 +228,7 @@ const TimeRangeField = ({ className, value, onChange }: { className?: string } &
return ( return (
<div className={className}> <div className={className}>
<LazySelect <LazySelect
className="inline-block h-9 w-[100px]" className="inline-block w-[100px]"
value={value.start} value={value.start}
max={value.end} max={value.end}
onChange={(option) => { onChange={(option) => {
+11 -6
View File
@@ -780,10 +780,15 @@ function SideBar() {
export function ShellMain(props: LayoutProps) { export function ShellMain(props: LayoutProps) {
const router = useRouter(); const router = useRouter();
const { isLocaleReady } = useLocale(); const { isLocaleReady } = useLocale();
return ( return (
<> <>
<div <div
className={classNames("mb-6 flex items-center sm:mt-0", props.smallHeading ? "lg:mb-7" : "lg:mb-8")}> className={classNames(
"mb-6 flex items-center sm:mt-0",
props.smallHeading ? "lg:mb-7" : "lg:mb-8",
props.backPath === "/availability" && "ml-2.5 xl:ml-[7rem]"
)}>
{!!props.backPath && ( {!!props.backPath && (
<Button <Button
variant="icon" variant="icon"
@@ -800,18 +805,18 @@ export function ShellMain(props: LayoutProps) {
{props.heading && ( {props.heading && (
<header className={classNames(props.large && "py-8", "flex w-full max-w-full items-center")}> <header className={classNames(props.large && "py-8", "flex w-full max-w-full items-center")}>
{props.HeadingLeftIcon && <div className="ltr:mr-4">{props.HeadingLeftIcon}</div>} {props.HeadingLeftIcon && <div className="ltr:mr-4">{props.HeadingLeftIcon}</div>}
<div className={classNames("w-full ltr:mr-4 rtl:ml-4 sm:block", props.headerClassName)}> <div className={classNames("w-full ltr:mr-4 rtl:ml-4 md:block", props.headerClassName)}>
{props.heading && ( {props.heading && (
<h1 <h1
className={classNames( className={classNames(
"font-cal max-w-28 sm:max-w-72 md:max-w-80 mt-1 hidden truncate text-xl font-semibold tracking-wide text-black sm:block xl:max-w-full", "font-cal max-w-28 sm:max-w-72 md:max-w-80 mt-1 hidden truncate text-xl font-semibold tracking-wide text-black md:block xl:max-w-full",
props.smallHeading ? "text-base" : "text-xl" props.smallHeading ? "text-base" : "text-xl"
)}> )}>
{!isLocaleReady ? <SkeletonText invisible /> : props.heading} {!isLocaleReady ? <SkeletonText invisible /> : props.heading}
</h1> </h1>
)} )}
{props.subtitle && ( {props.subtitle && (
<p className="hidden text-sm text-gray-500 sm:block"> <p className="hidden text-sm text-gray-500 md:block">
{!isLocaleReady ? <SkeletonText invisible /> : props.subtitle} {!isLocaleReady ? <SkeletonText invisible /> : props.subtitle}
</p> </p>
)} )}
@@ -821,8 +826,8 @@ export function ShellMain(props: LayoutProps) {
className={classNames( className={classNames(
props.backPath props.backPath
? "relative" ? "relative"
: "fixed bottom-[88px] z-40 ltr:right-4 rtl:left-4 sm:z-auto md:ltr:right-0 md:rtl:left-0", : "fixed bottom-[88px] z-40 ltr:right-4 rtl:left-4 md:z-auto md:ltr:right-0 md:rtl:left-0",
"flex-shrink-0 sm:relative sm:bottom-auto sm:right-auto" "flex-shrink-0 md:relative md:bottom-auto md:right-auto"
)}> )}>
{props.CTA} {props.CTA}
</div> </div>
@@ -687,7 +687,7 @@ export const viewerTeamsRouter = router({
}, },
}, },
}); });
type UserMap = Record<number, (typeof teams)[number]["members"][number]["user"]>; type UserMap = Record<number, typeof teams[number]["members"][number]["user"]>;
// flattern users to be unique by id // flattern users to be unique by id
const users = teams const users = teams
.flatMap((t) => t.members) .flatMap((t) => t.members)
+7 -7
View File
@@ -40,7 +40,7 @@ const buttonClasses = cva(
variant: { variant: {
button: "", button: "",
icon: "flex justify-center", icon: "flex justify-center",
fab: "rounded-full justify-center sm:rounded-md radix-state-open:rotate-45 sm:radix-state-open:rotate-0 transition-transform radix-state-open:shadown-none radix-state-open:ring-0 !shadow-none", fab: "rounded-full justify-center md:rounded-md radix-state-open:rotate-45 md:radix-state-open:rotate-0 transition-transform radix-state-open:shadown-none radix-state-open:ring-0 !shadow-none",
}, },
color: { color: {
primary: "text-white dark:text-black", primary: "text-white dark:text-black",
@@ -149,7 +149,7 @@ const buttonClasses = cva(
{ {
variant: "fab", variant: "fab",
size: "base", size: "base",
className: "h-14 sm:h-9 sm:w-auto sm:px-4 sm:py-2.5", className: "h-14 md:h-9 md:w-auto md:px-4 md:py-2.5",
}, },
], ],
defaultVariants: { defaultVariants: {
@@ -204,8 +204,8 @@ export const Button = forwardRef<HTMLAnchorElement | HTMLButtonElement, ButtonPr
<> <>
{variant === "fab" ? ( {variant === "fab" ? (
<> <>
<StartIcon className="hidden h-4 w-4 stroke-[1.5px] ltr:mr-2 rtl:ml-2 sm:inline-flex" /> <StartIcon className="hidden h-4 w-4 stroke-[1.5px] ltr:mr-2 rtl:ml-2 md:inline-flex" />
<FiPlus className="inline h-6 w-6 sm:hidden" /> <FiPlus className="inline h-6 w-6 md:hidden" />
</> </>
) : ( ) : (
<StartIcon <StartIcon
@@ -217,7 +217,7 @@ export const Button = forwardRef<HTMLAnchorElement | HTMLButtonElement, ButtonPr
)} )}
</> </>
)} )}
{variant === "fab" ? <span className="hidden sm:inline">{props.children}</span> : props.children} {variant === "fab" ? <span className="hidden md:inline">{props.children}</span> : props.children}
{loading && ( {loading && (
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 transform"> <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 transform">
<svg <svg
@@ -238,8 +238,8 @@ export const Button = forwardRef<HTMLAnchorElement | HTMLButtonElement, ButtonPr
<> <>
{variant === "fab" ? ( {variant === "fab" ? (
<> <>
<EndIcon className="-mr-1 hidden h-5 w-5 ltr:ml-2 rtl:-ml-1 rtl:mr-2 sm:inline" /> <EndIcon className="-mr-1 hidden h-5 w-5 ltr:ml-2 rtl:-ml-1 rtl:mr-2 md:inline" />
<FiPlus className="inline h-6 w-6 sm:hidden" /> <FiPlus className="inline h-6 w-6 md:hidden" />
</> </>
) : ( ) : (
<EndIcon <EndIcon
@@ -80,7 +80,7 @@ export const ControlComponent = <
{...props} {...props}
className={classNames( className={classNames(
className, className,
"dark:bg-darkgray-100 dark:border-darkgray-300 border-gray-300 bg-white text-sm leading-4 placeholder:text-sm placeholder:font-normal focus-within:border-0 focus-within:ring-2 focus-within:ring-neutral-800 hover:border-neutral-400 dark:focus-within:ring-white" "dark:bg-darkgray-100 dark:border-darkgray-300 !min-h-9 h-9 border-gray-300 bg-white text-sm leading-4 placeholder:text-sm placeholder:font-normal focus-within:border-0 focus-within:ring-2 focus-within:ring-neutral-800 hover:border-neutral-400 dark:focus-within:ring-white"
)} )}
/> />
); );
+909 -31
View File
File diff suppressed because it is too large. Load diff