"use client"; import { Button, Card, CardBody, Flex, FormControl, FormHelperText, FormLabel, Heading, Input, Link, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, Spacer, Spinner, useDisclosure, VStack, Text, Menu, MenuButton, MenuList, MenuItem, IconButton, Checkbox, } from "@chakra-ui/react"; import { useContext, useEffect, useState } from "react"; import { Container } from "@chakra-ui/react"; import { FaEllipsisVertical, FaTrash, FaPencil } from "react-icons/fa6"; import useApi from "../../lib/useApi"; import useRoomList from "./useRoomList"; import { DomainContext } from "../../domainContext"; import { Select, Options, OptionBase } from "chakra-react-select"; interface Stream { id: number; name: string; topics: string[]; } interface SelectOption extends OptionBase { label: string; value: string; } const RESERVED_PATHS = ["browse", "rooms", "transcripts"]; export default function RoomsList() { const { isOpen, onOpen, onClose } = useDisclosure(); const [room, setRoom] = useState({ name: "", zulipAutoPost: false, zulipStream: "", zulipTopic: "", }); const [isEditing, setIsEditing] = useState(false); const [editRoomId, setEditRoomId] = useState(""); const api = useApi(); const [page, setPage] = useState(1); const { loading, response, refetch } = useRoomList(page); const [streams, setStreams] = useState([]); const [error, setError] = useState(""); const { zulip_streams } = useContext(DomainContext); useEffect(() => { const fetchZulipStreams = async () => { try { const response = await fetch(zulip_streams + "/streams.json"); if (!response.ok) { throw new Error("Network response was not ok"); } let data = await response.json(); data = data.sort((a: Stream, b: Stream) => a.name.localeCompare(b.name), ); setStreams(data); } catch (err) { console.error("Error fetching streams:", err); } }; if (room.zulipAutoPost) { fetchZulipStreams(); } }, [room.zulipAutoPost]); const streamOptions: Options = streams.map((stream) => { return { label: stream.name, value: stream.name }; }); const topicOptions = streams .find((stream) => stream.name === room.zulipStream) ?.topics.map((topic) => ({ label: topic, value: topic })) || []; const handleSaveRoom = async () => { try { if (RESERVED_PATHS.includes(room.name)) { setError("This room name is reserved. Please choose another name."); return; } if (isEditing) { await api?.v1RoomsUpdate({ roomId: editRoomId, requestBody: { name: room.name, zulip_auto_post: room.zulipAutoPost, zulip_stream: room.zulipStream, zulip_topic: room.zulipTopic, }, }); } else { await api?.v1RoomsCreate({ requestBody: { name: room.name, zulip_auto_post: room.zulipAutoPost, zulip_stream: room.zulipStream, zulip_topic: room.zulipTopic, }, }); } setRoom({ name: "", zulipAutoPost: false, zulipStream: "", zulipTopic: "", }); setIsEditing(false); setEditRoomId(""); setError(""); refetch(); } catch (err) { console.error(err); } onClose(); }; const handleEditRoom = (roomId, roomData) => { setRoom({ name: roomData.name, zulipAutoPost: roomData.zulip_auto_post, zulipStream: roomData.zulip_stream, zulipTopic: roomData.zulip_topic, }); setEditRoomId(roomId); setIsEditing(true); onOpen(); }; const handleDeleteRoom = async (roomId: string) => { try { await api?.v1RoomsDelete({ roomId, }); refetch(); } catch (err) { console.error(err); } }; const handleRoomChange = (e) => { let { name, value, type, checked } = e.target; if (name === "name") { value = value .replace(/[^a-zA-Z0-9\s-]/g, "") .replace(/\s+/g, "-") .toLowerCase(); setError(""); } setRoom({ ...room, [name]: type === "checkbox" ? checked : value, }); }; if (loading && !response) return ( ); return ( <> Rooms {isEditing ? "Edit Room" : "Add Room"} Room name No spaces or special characters allowed {error && {error}} Automatically post transcription to Zulip Zulip stream setRoom({ ...room, zulipTopic: newValue!.value, }) } isDisabled={!room.zulipAutoPost} /> {response?.items && response.items.length > 0 ? ( response.items.map((roomData) => ( {roomData.name} } aria-label="actions" /> handleEditRoom(roomData.id, roomData)} icon={} > Edit handleDeleteRoom(roomData.id)} icon={} > Delete )) ) : ( No rooms found )} ); }