+15









sean-brydon
Omar López
Carina Wollendorfer
gitstart-calcom
Keith Williams
Hariom Balhara
CarinaWolli
Alex van Andel
Peer Richelsen
Raghul D
Udit Takkar
Leo Giovanetti
Crowdin Bot
Zain Gulbaz
GitStart-Cal.com
Suyash Srivastava
Shivam Kalra
alannnc
Joe Au-Yeung
Anik Dhabal Babu
Pradumn Kumar
Raghul
ABDERRAHMANI IDRISSI HAMZA
Nafees Nazik
Danila
67bb2ed798
* Init + get timezone + offset data agh * Add 12/24h mode - style correctly * User users timezone + working hours. Still some stuff to figure out * Multiple working hours * move calc to once per day * Demo with two users and differnt timezones * availabillity control tab via search params * WIP hover overlay * THIS WORKS ISH * fix: multiple duration getSchedule calls [CAL-2336] (#10709) Co-authored-by: Omar López <zomars@me.com> * New Crowdin translations by Github Action * fix: If the input type "Name" is selected, the label can't be changed from our default label "Your Name" (#10618) Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com> * New Crowdin translations by Github Action * test: Create unit tests for react components in packages/ui/components/form/step (#10442) Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com> Co-authored-by: Keith Williams <keithwillcode@gmail.com> * feat: element call app added (#10585) Co-authored-by: Hariom Balhara <hariombalhara@gmail.com> * New Crowdin translations by Github Action * New Crowdin translations by Github Action * fix: e2e test for rescheduling overlapping time (#10721) Co-authored-by: CarinaWolli <wollencarina@gmail.com> * feat: mailhog fixture (#10606) * feat: mailhog fixture * fix: nodemailer to dispatch emails with e2e env * fix: remove space from email subject * feat: fixture getFirstEventAsOwner * feat: assert email subjects * fix: and enable dynamic booking test (#10642) * fix and enable dynamic booking test * remove page pause --------- Co-authored-by: Alex van Andel <me@alexvanandel.com> * fix: Broken team events if a user with the same name exists (#10724) * fix: Broken team events if a user with the same name exists * Fix tests + fix usernameList optionality * Try to list calendars, if not continue (#10720) Co-authored-by: Omar López <zomars@me.com> * v3.1.9 * link to org settings (#10718) * feat: app paypal payment (#8797) Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com> Co-authored-by: Omar López <zomars@me.com> * fix: RTL issues on booking pages + email confirmation (#10526) Co-authored-by: Peer Richelsen <peeroke@gmail.com> * fix: merge conflict * Fixing org slug (#10538) * fix: paypal build fixes * Fix avatar for org in Shell top (#10712) * feat: add range of dates for availability over-ride (#10462) * feat: add range of dates for availability over-ride * chore: changed range select to multiple select --------- Co-authored-by: Alex van Andel <me@alexvanandel.com> * fix: border issue for time slots (#10577) Co-authored-by: Raghul D <v-raghuld@microsoft.com> * style: Fix text wrapping issue in button (#10725) Co-authored-by: Omar López <zomars@me.com> * New Crowdin translations by Github Action * fix: App Install Dropdown Sort Properly [CAL-2285] (#10672) Co-authored-by: Peer Richelsen <peeroke@gmail.com> * fix: link escaping in booking page (#10360) Co-authored-by: Hariom Balhara <hariombalhara@gmail.com> * chore: fix refund i18n message (#10731) * chore: remove tailwind-scrollbar warning (#10523) Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com> * New Crowdin translations by Github Action * chore: Simplified date overrides (#10728) * chore: Simplified date overrides * Fixed a test that had a date override that wasn't at midnight utc * Wrote test that showed a fixed Europe/Brussels * Lint fix * New Crowdin translations by Github Action * Fix offset time + fetching correct dates * Deal with awkward minute offsets * remove store overhead * Format H based on tz * Cleanup store logic * Cleanup * Remove comments * Remove comments * Remove yarn.lock * Dark mode & v-align text fixes * Move ButtonGroup to the left to prevent chevron from jumping * Shift based on timezone (non-hour) and have 15min granularity in hour display background color --------- Co-authored-by: Leo Giovanetti <hello@leog.me> Co-authored-by: Omar López <zomars@me.com> Co-authored-by: Crowdin Bot <support+bot@crowdin.com> Co-authored-by: Zain Gulbaz <zaingulbaz8@gmail.com> Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com> Co-authored-by: GitStart-Cal.com <121884634+gitstart-calcom@users.noreply.github.com> Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com> Co-authored-by: Keith Williams <keithwillcode@gmail.com> Co-authored-by: Suyash Srivastava <suyashsrivastava5053@gmail.com> Co-authored-by: Hariom Balhara <hariombalhara@gmail.com> Co-authored-by: CarinaWolli <wollencarina@gmail.com> Co-authored-by: Shivam Kalra <shivamkalra98@gmail.com> Co-authored-by: alannnc <alannnc@gmail.com> Co-authored-by: Alex van Andel <me@alexvanandel.com> Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com> Co-authored-by: Peer Richelsen <peeroke@gmail.com> Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com> Co-authored-by: Pradumn Kumar <pradumn@tealfeed.com> Co-authored-by: Raghul <123321540+Raghul18@users.noreply.github.com> Co-authored-by: Raghul D <v-raghuld@microsoft.com> Co-authored-by: ABDERRAHMANI IDRISSI HAMZA <97639117+idrissi-hamza@users.noreply.github.com> Co-authored-by: Nafees Nazik <84864519+G3root@users.noreply.github.com> Co-authored-by: Danila <daniil.demidovich@gmail.com> Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
164 lines
5.7 KiB
TypeScript
164 lines
5.7 KiB
TypeScript
import type { ColumnDef } from "@tanstack/react-table";
|
|
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
|
|
import { useMemo, useRef, useCallback, useEffect, useState } from "react";
|
|
|
|
import dayjs from "@calcom/dayjs";
|
|
import type { DateRange } from "@calcom/lib/date-ranges";
|
|
import type { MembershipRole } from "@calcom/prisma/enums";
|
|
import { trpc } from "@calcom/trpc";
|
|
import { Avatar, Button, ButtonGroup, DataTable } from "@calcom/ui";
|
|
|
|
import { TBContext, createTimezoneBuddyStore } from "../store";
|
|
import { TimeDial } from "./TimeDial";
|
|
|
|
export interface SliderUser {
|
|
id: number;
|
|
username: string | null;
|
|
email: string;
|
|
timeZone: string;
|
|
role: MembershipRole;
|
|
dateRanges: DateRange[];
|
|
}
|
|
|
|
export function AvailabilitySliderTable() {
|
|
const tableContainerRef = useRef<HTMLDivElement>(null);
|
|
const [browsingDate, setBrowsingDate] = useState(dayjs());
|
|
|
|
const { data, isLoading, fetchNextPage, isFetching } = trpc.viewer.availability.listTeam.useInfiniteQuery(
|
|
{
|
|
limit: 10,
|
|
loggedInUsersTz: dayjs.tz.guess(),
|
|
startDate: browsingDate.startOf("day").toISOString(),
|
|
endDate: browsingDate.endOf("day").toISOString(),
|
|
},
|
|
{
|
|
getNextPageParam: (lastPage) => lastPage.nextCursor,
|
|
keepPreviousData: true,
|
|
}
|
|
);
|
|
|
|
const memorisedColumns = useMemo(() => {
|
|
const cols: ColumnDef<SliderUser>[] = [
|
|
{
|
|
id: "member",
|
|
accessorFn: (data) => data.email,
|
|
header: "Member",
|
|
cell: ({ row }) => {
|
|
const { username, email, timeZone } = row.original;
|
|
return (
|
|
<div className="max-w-64 flex flex-shrink-0 items-center gap-2 overflow-hidden">
|
|
<Avatar
|
|
size="sm"
|
|
alt={username || email}
|
|
imageSrc={"/" + username + "/avatar.png"}
|
|
gravatarFallbackMd5="fallback"
|
|
/>
|
|
<div className="">
|
|
<div className="text-emphasis max-w-64 truncate text-sm font-medium" title={email}>
|
|
{username || "No username"}
|
|
</div>
|
|
<div className="text-subtle text-xs leading-none">{timeZone}</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
id: "timezone",
|
|
accessorFn: (data) => data.timeZone,
|
|
header: "Timezone",
|
|
cell: ({ row }) => {
|
|
const { timeZone } = row.original;
|
|
const timeRaw = dayjs().tz(timeZone);
|
|
const time = timeRaw.format("HH:mm");
|
|
const utcOffsetInMinutes = timeRaw.utcOffset();
|
|
const hours = Math.abs(Math.floor(utcOffsetInMinutes / 60));
|
|
const minutes = Math.abs(utcOffsetInMinutes % 60);
|
|
const offsetFormatted = `${utcOffsetInMinutes < 0 ? "-" : "+"}${hours
|
|
.toString()
|
|
.padStart(2, "0")}:${minutes.toString().padStart(2, "0")}`;
|
|
|
|
return (
|
|
<div className="flex flex-col">
|
|
<span className="text-default text-sm font-medium">{time}</span>
|
|
<span className="text-subtle text-xs leading-none">GMT {offsetFormatted}</span>
|
|
</div>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
id: "slider",
|
|
header: () => {
|
|
return (
|
|
<div className="flex items-center">
|
|
<ButtonGroup containerProps={{ className: "space-x-0" }}>
|
|
<Button
|
|
color="minimal"
|
|
variant="icon"
|
|
StartIcon={ChevronLeftIcon}
|
|
onClick={() => setBrowsingDate(browsingDate.subtract(1, "day"))}
|
|
/>
|
|
<Button
|
|
onClick={() => setBrowsingDate(browsingDate.add(1, "day"))}
|
|
color="minimal"
|
|
StartIcon={ChevronRightIcon}
|
|
variant="icon"
|
|
/>
|
|
</ButtonGroup>
|
|
<span>{browsingDate.format("DD dddd MMM, YYYY")}</span>
|
|
</div>
|
|
);
|
|
},
|
|
cell: ({ row }) => {
|
|
const { timeZone, dateRanges } = row.original;
|
|
// return <pre>{JSON.stringify(dateRanges, null, 2)}</pre>;
|
|
return <TimeDial timezone={timeZone} dateRanges={dateRanges} />;
|
|
},
|
|
},
|
|
];
|
|
|
|
return cols;
|
|
}, [browsingDate]);
|
|
|
|
//we must flatten the array of arrays from the useInfiniteQuery hook
|
|
const flatData = useMemo(() => data?.pages?.flatMap((page) => page.rows) ?? [], [data]) as SliderUser[];
|
|
const totalDBRowCount = data?.pages?.[0]?.meta?.totalRowCount ?? 0;
|
|
const totalFetched = flatData.length;
|
|
|
|
//called on scroll and possibly on mount to fetch more data as the user scrolls and reaches bottom of table
|
|
const fetchMoreOnBottomReached = useCallback(
|
|
(containerRefElement?: HTMLDivElement | null) => {
|
|
if (containerRefElement) {
|
|
const { scrollHeight, scrollTop, clientHeight } = containerRefElement;
|
|
//once the user has scrolled within 300px of the bottom of the table, fetch more data if there is any
|
|
if (scrollHeight - scrollTop - clientHeight < 300 && !isFetching && totalFetched < totalDBRowCount) {
|
|
fetchNextPage();
|
|
}
|
|
}
|
|
},
|
|
[fetchNextPage, isFetching, totalFetched, totalDBRowCount]
|
|
);
|
|
|
|
useEffect(() => {
|
|
fetchMoreOnBottomReached(tableContainerRef.current);
|
|
}, [fetchMoreOnBottomReached]);
|
|
|
|
return (
|
|
<TBContext.Provider
|
|
value={createTimezoneBuddyStore({
|
|
browsingDate: browsingDate.toDate(),
|
|
})}>
|
|
<div className="relative">
|
|
<DataTable
|
|
tableContainerRef={tableContainerRef}
|
|
columns={memorisedColumns}
|
|
data={flatData}
|
|
isLoading={isLoading}
|
|
// tableOverlay={<HoverOverview />}
|
|
onScroll={(e) => fetchMoreOnBottomReached(e.target as HTMLDivElement)}
|
|
/>
|
|
</div>
|
|
</TBContext.Provider>
|
|
);
|
|
}
|