calendar refresh quick action icon

This commit is contained in:
Igor Loskutov
2025-09-16 17:15:13 -04:00
parent f4d59a4af8
commit 4d66eacab2
2 changed files with 94 additions and 14 deletions

View File

@@ -201,20 +201,32 @@ export default function ICSSettings({
bg="gray.100" bg="gray.100"
_hover={{ bg: "gray.200" }} _hover={{ bg: "gray.200" }}
_focus={{ bg: "gray.200" }} _focus={{ bg: "gray.200" }}
pr="48px" pr="90px"
width="100%" width="100%"
/> />
<IconButton <HStack position="absolute" right="4px" gap={1} zIndex={1}>
aria-label="Copy room URL" <IconButton
onClick={handleCopyRoomUrl} aria-label="Force sync calendar"
variant="ghost" onClick={handleForceSync}
position="absolute" variant="ghost"
right="4px" size="sm"
size="sm" disabled={syncStatus === "syncing" || !icsUrl || !isEditing}
zIndex={1} >
> {syncStatus === "syncing" ? (
{justCopied ? <LuCheck /> : <LuCopy />} <Spinner size="sm" />
</IconButton> ) : (
<LuRefreshCw />
)}
</IconButton>
<IconButton
aria-label="Copy room URL"
onClick={handleCopyRoomUrl}
variant="ghost"
size="sm"
>
{justCopied ? <LuCheck /> : <LuCopy />}
</IconButton>
</HStack>
</HStack> </HStack>
</Field.Root> </Field.Root>

View File

@@ -1,4 +1,4 @@
import React from "react"; import React, { useState } from "react";
import { import {
Box, Box,
Table, Table,
@@ -9,12 +9,15 @@ import {
Spinner, Spinner,
Badge, Badge,
VStack, VStack,
Icon,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { LuLink } from "react-icons/lu"; import { LuLink, LuRefreshCw } from "react-icons/lu";
import { FaCalendarAlt } from "react-icons/fa";
import type { components } from "../../../reflector-api"; import type { components } from "../../../reflector-api";
import { import {
useRoomActiveMeetings, useRoomActiveMeetings,
useRoomUpcomingMeetings, useRoomUpcomingMeetings,
useRoomIcsSync,
} from "../../../lib/apiHooks"; } from "../../../lib/apiHooks";
type Room = components["schemas"]["Room"]; type Room = components["schemas"]["Room"];
@@ -23,6 +26,34 @@ type CalendarEventResponse = components["schemas"]["CalendarEventResponse"];
import { RoomActionsMenu } from "./RoomActionsMenu"; import { RoomActionsMenu } from "./RoomActionsMenu";
import { MEETING_DEFAULT_TIME_MINUTES } from "../../../[roomName]/[meetingId]/constants"; import { MEETING_DEFAULT_TIME_MINUTES } from "../../../[roomName]/[meetingId]/constants";
// Custom icon component that combines calendar and refresh icons
const CalendarSyncIcon = () => (
<Box position="relative" display="inline-block" w="20px" h="20px">
<Icon
as={FaCalendarAlt}
position="absolute"
top={0}
left={0}
boxSize="20px"
/>
<Box
position="absolute"
bottom="-2px"
right="-2px"
bg="white"
borderRadius="full"
p="1px"
display="flex"
alignItems="center"
justifyContent="center"
w="12px"
h="12px"
>
<Icon as={LuRefreshCw} boxSize="10px" color="gray.700" />
</Box>
</Box>
);
interface RoomTableProps { interface RoomTableProps {
rooms: Room[]; rooms: Room[];
linkCopied: string; linkCopied: string;
@@ -148,6 +179,28 @@ export function RoomTable({
onDelete, onDelete,
loading, loading,
}: RoomTableProps) { }: RoomTableProps) {
const [syncingRooms, setSyncingRooms] = useState<Set<string>>(new Set());
const syncMutation = useRoomIcsSync();
const handleForceSync = async (roomName: string) => {
setSyncingRooms((prev) => new Set(prev).add(roomName));
try {
await syncMutation.mutateAsync({
params: {
path: { room_name: roomName },
},
});
} catch (err) {
console.error("Failed to sync calendar:", err);
} finally {
setSyncingRooms((prev) => {
const next = new Set(prev);
next.delete(roomName);
return next;
});
}
};
return ( return (
<Box display={{ base: "none", lg: "block" }} position="relative"> <Box display={{ base: "none", lg: "block" }} position="relative">
{loading && ( {loading && (
@@ -217,6 +270,21 @@ export function RoomTable({
</Table.Cell> </Table.Cell>
<Table.Cell> <Table.Cell>
<Flex alignItems="center" gap={2}> <Flex alignItems="center" gap={2}>
{room.ics_enabled && (
<IconButton
aria-label="Force sync calendar"
onClick={() => handleForceSync(room.name)}
size="sm"
variant="ghost"
disabled={syncingRooms.has(room.name)}
>
{syncingRooms.has(room.name) ? (
<Spinner size="sm" />
) : (
<CalendarSyncIcon />
)}
</IconButton>
)}
{linkCopied === room.name ? ( {linkCopied === room.name ? (
<Text color="green.500" fontSize="sm"> <Text color="green.500" fontSize="sm">
Copied! Copied!