Improvements based on feedback

This commit is contained in:
Sara
2024-01-25 13:51:22 +01:00
parent 8924067a45
commit 20681b0810
2 changed files with 214 additions and 80 deletions

View File

@@ -1,9 +1,9 @@
"use client"; "use client";
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
import { GetTranscript } from "../../api"; import { GetTranscript } from "../../api";
import Pagination from "./pagination"; import Pagination from "./pagination";
import Link from "next/link"; import NextLink from "next/link";
import { FaGear } from "react-icons/fa6"; import { FaGear } from "react-icons/fa6";
import { FaCheck, FaTrash, FaStar, FaMicrophone } from "react-icons/fa"; import { FaCheck, FaTrash, FaStar, FaMicrophone } from "react-icons/fa";
import { MdError } from "react-icons/md"; import { MdError } from "react-icons/md";
@@ -11,12 +11,14 @@ import useTranscriptList from "../transcripts/useTranscriptList";
import { formatTime } from "../../lib/time"; import { formatTime } from "../../lib/time";
import useApi from "../../lib/useApi"; import useApi from "../../lib/useApi";
import { useError } from "../../(errors)/errorContext"; import { useError } from "../../(errors)/errorContext";
import { FaEllipsisVertical } from "react-icons/fa6";
import { import {
Flex, Flex,
Spinner, Spinner,
Heading, Heading,
Button, Button,
Card, Card,
Link,
CardBody, CardBody,
CardFooter, CardFooter,
Stack, Stack,
@@ -33,8 +35,22 @@ import {
PopoverBody, PopoverBody,
PopoverFooter, PopoverFooter,
IconButton, IconButton,
Spacer,
Menu,
MenuButton,
MenuItem,
MenuList,
AlertDialog,
AlertDialogOverlay,
AlertDialogContent,
AlertDialogHeader,
AlertDialogBody,
AlertDialogFooter,
keyframes,
Tooltip,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { PlusSquareIcon } from "@chakra-ui/icons"; import { PlusSquareIcon } from "@chakra-ui/icons";
import { ExpandableText } from "../../lib/expandableText";
// import { useFiefUserinfo } from "@fief/fief/nextjs/react"; // import { useFiefUserinfo } from "@fief/fief/nextjs/react";
export default function TranscriptBrowser() { export default function TranscriptBrowser() {
@@ -43,11 +59,19 @@ export default function TranscriptBrowser() {
const [deletionLoading, setDeletionLoading] = useState(false); const [deletionLoading, setDeletionLoading] = useState(false);
const api = useApi(); const api = useApi();
const { setError } = useError(); const { setError } = useError();
const cancelRef = React.useRef(null);
const [transcriptToDeleteId, setTranscriptToDeleteId] =
React.useState<string>();
const [deletedItemIds, setDeletedItemIds] = React.useState<string[]>();
// Todo: fief add name field to userinfo // Todo: fief add name field to userinfo
// const user = useFiefUserinfo(); // const user = useFiefUserinfo();
// console.log(user); // console.log(user);
useEffect(() => {
setDeletedItemIds([]);
}, [page, response]);
if (loading && !response) if (loading && !response)
return ( return (
<Flex flexDir="column" align="center" justify="center" h="100%"> <Flex flexDir="column" align="center" justify="center" h="100%">
@@ -67,6 +91,7 @@ export default function TranscriptBrowser() {
</Text> </Text>
</Flex> </Flex>
); );
const onCloseDeletion = () => setTranscriptToDeleteId(undefined);
const handleDeleteTranscript = (transcriptToDeleteId) => (e) => { const handleDeleteTranscript = (transcriptToDeleteId) => (e) => {
e.stopPropagation(); e.stopPropagation();
@@ -77,6 +102,11 @@ export default function TranscriptBrowser() {
.then(() => { .then(() => {
setDeletionLoading(false); setDeletionLoading(false);
refetch(); refetch();
onCloseDeletion();
setDeletedItemIds((deletedItemIds) => [
deletedItemIds,
...transcriptToDeleteId,
]);
}) })
.catch((err) => { .catch((err) => {
setDeletionLoading(false); setDeletionLoading(false);
@@ -94,23 +124,27 @@ export default function TranscriptBrowser() {
overflowY="scroll" overflowY="scroll"
maxH="100%" maxH="100%"
> >
<Flex flexDir="row" justify="space-between" align="center"> <Flex
flexDir="row"
justify="flex-end"
align="center"
flexWrap={"wrap-reverse"}
>
{/* <Heading>{user?.fields?.name}'s Meetings</Heading> */} {/* <Heading>{user?.fields?.name}'s Meetings</Heading> */}
<Heading>Your Meetings</Heading> <Heading>Your Meetings</Heading>
<Flex flexDir="row" align="center"> {loading || (deletionLoading && <Spinner></Spinner>)}
{loading || (deletionLoading && <Spinner></Spinner>)}
<Pagination <Spacer />
page={page} <Pagination
setPage={setPage} page={page}
total={response?.total || 0} setPage={setPage}
size={response?.size || 0} total={response?.total || 0}
/> size={response?.size || 0}
/>
<Button colorScheme="blue" rightIcon={<PlusSquareIcon />}> <Button colorScheme="blue" rightIcon={<PlusSquareIcon />}>
New Meeting New Meeting
</Button> </Button>
</Flex>
</Flex> </Flex>
<Grid <Grid
@@ -127,73 +161,127 @@ export default function TranscriptBrowser() {
overflowY={"scroll"} overflowY={"scroll"}
mb="4" mb="4"
> >
{response?.items.map((item: GetTranscript) => ( {response?.items
<Card key={item.id} border="gray.light" variant="outline"> .filter((i) => !deletedItemIds?.includes(i.id))
<CardBody as={Link} href={`/transcripts/${item.id}`}> .map((item: GetTranscript) => (
<Heading size="md"> <Card key={item.id} border="gray.light" variant="outline">
{item.title || item.name || "Unamed Transcript"} <CardBody>
<Flex align={"center"} ml="-6px">
{item.status == "ended" && (
<Tooltip label="Processing done">
<span>
<Icon color="green" as={FaCheck} mr="2" />
</span>
</Tooltip>
)}
{item.status == "error" && (
<Tooltip label="Processing error">
<span>
<Icon color="red.primary" as={MdError} mr="2" />
</span>
</Tooltip>
)}
{item.status == "idle" && (
<Tooltip label="New meeting, no recording">
<span>
<Icon color="yellow.500" as={FaStar} mr="2" />
</span>
</Tooltip>
)}
{item.status == "processing" && (
<Tooltip label="Processing in progress">
<span>
<Icon
color="grey.primary"
as={FaGear}
mr="2"
transition={"all 2s ease"}
transform={"rotate(0deg)"}
_hover={{ transform: "rotate(360deg)" }}
/>
</span>
</Tooltip>
)}
{item.status == "recording" && (
<Tooltip label="Recording in progress">
<span>
<Icon color="blue.primary" as={FaMicrophone} mr="2" />
</span>
</Tooltip>
)}
<Heading size="md">
<Link
as={NextLink}
href={`/transcripts/${item.id}`}
noOfLines={2}
>
{item.title || item.name || "Unamed Transcript"}
</Link>
</Heading>
{item.status == "ended" && ( <Spacer />
<Icon color="green" as={FaCheck} ml="2" /> <Menu closeOnSelect={false}>
)} <MenuButton
{item.status == "error" && ( as={IconButton}
<Icon color="red.primary" as={MdError} ml="2" /> icon={<FaEllipsisVertical />}
)} aria-label="actions"
{item.status == "idle" && ( />
<Icon color="yellow.500" as={FaStar} ml="2" /> <MenuList>
)} <MenuItem
{item.status == "processing" && (
<Icon color="grey.primary" as={FaGear} ml="2" />
)}
{item.status == "recording" && (
<Icon color="blue.primary" as={FaMicrophone} ml="2" />
)}
</Heading>
<Stack mt="6" spacing="3">
<Text fontSize="small">
{new Date(item.created_at).toLocaleString("en-US")}
{"\u00A0"}-{"\u00A0"}
{formatTime(Math.floor(item.duration / 1000))}
</Text>
<Text>{item.short_summary}</Text>
</Stack>
</CardBody>
{item.status !== "ended" && (
<>
<Divider />
<CardFooter>
<Popover>
<PopoverTrigger>
<IconButton
colorScheme="red"
disabled={deletionLoading} disabled={deletionLoading}
icon={<FaTrash />} onClick={() => setTranscriptToDeleteId(item.id)}
aria-label="Delete" icon={<FaTrash color={"red.500"} />}
/> >
</PopoverTrigger> Delete
<PopoverContent> </MenuItem>
<PopoverArrow /> <AlertDialog
<PopoverCloseButton /> isOpen={transcriptToDeleteId === item.id}
<PopoverHeader> leastDestructiveRef={cancelRef}
Are you sure you want to delete {item.title} ? onClose={onCloseDeletion}
</PopoverHeader> >
<PopoverBody>This action is not reversible.</PopoverBody> <AlertDialogOverlay>
<PopoverFooter> <AlertDialogContent>
<Button <AlertDialogHeader fontSize="lg" fontWeight="bold">
colorScheme="red" Delete{" "}
onClick={handleDeleteTranscript(item.id)} {item.title || item.name || "Unamed Transcript"}
> </AlertDialogHeader>
Confirm
</Button> <AlertDialogBody>
</PopoverFooter> Are you sure? You can't undo this action
</PopoverContent> afterwards.
</Popover> </AlertDialogBody>
</CardFooter>
</> <AlertDialogFooter>
)} <Button ref={cancelRef} onClick={onCloseDeletion}>
</Card> Cancel
))} </Button>
<Button
colorScheme="red"
onClick={handleDeleteTranscript(item.id)}
ml={3}
>
Delete
</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialogOverlay>
</AlertDialog>
</MenuList>
</Menu>
</Flex>
<Stack mt="6" spacing="3">
<Text fontSize="small">
{new Date(item.created_at).toLocaleString("en-US")}
{"\u00A0"}-{"\u00A0"}
{formatTime(Math.floor(item.duration / 1000))}
</Text>
<ExpandableText noOfLines={5}>
{item.short_summary}
</ExpandableText>
</Stack>
</CardBody>
</Card>
))}
</Grid> </Grid>
</Flex> </Flex>
); );

View File

@@ -0,0 +1,46 @@
import type { BoxProps } from "@chakra-ui/react";
import { Box, Button, Text } from "@chakra-ui/react";
import React, { forwardRef, useState } from "react";
interface Props extends BoxProps {
children: React.ReactNode;
noOfLines: number;
}
export const ExpandableText = forwardRef<HTMLDivElement, Props>(
({ children, noOfLines, ...rest }, ref) => {
const [expandedCount, setExpandedCount] = useState<number | undefined>(
noOfLines,
);
const [isClicked, setIsClicked] = useState(false);
const handleToggle = () => {
setIsClicked(true);
setExpandedCount(expandedCount ? undefined : noOfLines);
};
const inputRef = React.useRef<HTMLInputElement>(null);
const isTextClamped =
(inputRef.current?.scrollHeight as number) >
(inputRef.current?.clientHeight as number) || isClicked;
return (
<Box ref={ref} {...rest}>
<Box ref={inputRef} noOfLines={expandedCount}>
{children}
</Box>
<Button
display={isTextClamped ? "block" : "none"}
size="sm"
variant="link"
onClick={handleToggle}
mt={2}
>
<Text>{!expandedCount ? "Show less" : "Read more"}</Text>
</Button>
</Box>
);
},
);
ExpandableText.displayName = "ExpandableText";