mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2025-12-20 20:29:06 +00:00
past meeting transcript side
This commit is contained in:
@@ -71,21 +71,30 @@ export default function TranscriptDetails(details: TranscriptDetails) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Grid templateColumns="1fr" templateRows="minmax(0, 1fr) auto">
|
<Grid
|
||||||
|
templateColumns="1fr"
|
||||||
|
templateRows="minmax(0, 1fr) auto"
|
||||||
|
gap={4}
|
||||||
|
mb={4}
|
||||||
|
>
|
||||||
<Grid
|
<Grid
|
||||||
templateColumns="repeat(2, 1fr)"
|
templateColumns="repeat(2, 1fr)"
|
||||||
templateRows="auto minmax(0, 1fr)"
|
templateRows="auto minmax(0, 1fr)"
|
||||||
gap={4}
|
gap={2}
|
||||||
padding={4}
|
padding={4}
|
||||||
background="gray.100"
|
background="gray.100"
|
||||||
borderRadius={2}
|
borderRadius={8}
|
||||||
>
|
>
|
||||||
<GridItem display="flex" flexDir="row" colSpan={2}>
|
<GridItem
|
||||||
|
display="flex"
|
||||||
|
flexDir="row"
|
||||||
|
alignItems={"center"}
|
||||||
|
colSpan={2}
|
||||||
|
>
|
||||||
<TranscriptTitle
|
<TranscriptTitle
|
||||||
title={transcript.response.title || "Unamed Transcript"}
|
title={transcript.response.title || "Unamed Transcript"}
|
||||||
transcriptId={transcriptId}
|
transcriptId={transcriptId}
|
||||||
/>
|
/>
|
||||||
<IconButton icon={<FaPen />} aria-label="Edit Transcript Title" />
|
|
||||||
</GridItem>
|
</GridItem>
|
||||||
<TopicList
|
<TopicList
|
||||||
topics={topics.topics || []}
|
topics={topics.topics || []}
|
||||||
|
|||||||
@@ -9,6 +9,18 @@ import ScrollToBottom from "./scrollToBottom";
|
|||||||
import { Topic } from "./webSocketTypes";
|
import { Topic } from "./webSocketTypes";
|
||||||
import { generateHighContrastColor } from "../../lib/utils";
|
import { generateHighContrastColor } from "../../lib/utils";
|
||||||
import useParticipants from "./useParticipants";
|
import useParticipants from "./useParticipants";
|
||||||
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionButton,
|
||||||
|
AccordionIcon,
|
||||||
|
AccordionItem,
|
||||||
|
AccordionPanel,
|
||||||
|
Box,
|
||||||
|
Flex,
|
||||||
|
Icon,
|
||||||
|
Text,
|
||||||
|
} from "@chakra-ui/react";
|
||||||
|
import { FaChevronDown, FaChevronRight } from "react-icons/fa";
|
||||||
|
|
||||||
type TopicListProps = {
|
type TopicListProps = {
|
||||||
topics: Topic[];
|
topics: Topic[];
|
||||||
@@ -75,11 +87,16 @@ export function TopicList({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="relative w-full h-full bg-blue-400/20 rounded-lg md:rounded-xl p-1 sm:p-2 md:px-4 flex flex-col justify-center align-center">
|
<Flex
|
||||||
|
position={"relative"}
|
||||||
|
w={"100%"}
|
||||||
|
h={"100%"}
|
||||||
|
dir="column"
|
||||||
|
justify={"center"}
|
||||||
|
align={"center"}
|
||||||
|
>
|
||||||
{topics.length > 0 ? (
|
{topics.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
<h2 className="ml-2 md:text-lg font-bold mb-2">Topics</h2>
|
|
||||||
|
|
||||||
{autoscroll && (
|
{autoscroll && (
|
||||||
<ScrollToBottom
|
<ScrollToBottom
|
||||||
visible={!autoscrollEnabled}
|
visible={!autoscrollEnabled}
|
||||||
@@ -87,80 +104,95 @@ export function TopicList({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div
|
<Accordion
|
||||||
id="topics-div"
|
id="topics-div"
|
||||||
className="overflow-y-auto h-full"
|
overflowY={"auto"}
|
||||||
|
h={"100%"}
|
||||||
onScroll={handleScroll}
|
onScroll={handleScroll}
|
||||||
|
defaultIndex={[
|
||||||
|
topics.findIndex((topic) => topic.id == activeTopic?.id),
|
||||||
|
]}
|
||||||
|
variant="custom"
|
||||||
|
allowToggle
|
||||||
>
|
>
|
||||||
{topics.map((topic, index) => (
|
{topics.map((topic, index) => (
|
||||||
<button
|
<AccordionItem
|
||||||
key={index}
|
key={index}
|
||||||
className="rounded-none border-solid border-0 border-bluegrey border-b last:border-none last:rounded-b-lg p-2 hover:bg-blue-400/20 focus-visible:bg-blue-400/20 text-left block w-full"
|
background={{
|
||||||
onClick={() =>
|
base: "light",
|
||||||
setActiveTopic(activeTopic?.id == topic.id ? null : topic)
|
_hover: "gray.100",
|
||||||
}
|
_focus: "gray.100",
|
||||||
|
}}
|
||||||
|
padding={2}
|
||||||
|
onClick={() => {
|
||||||
|
setActiveTopic(activeTopic?.id == topic.id ? null : topic);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div className="w-full flex justify-between items-center rounded-lg md:rounded-xl xs:text-base sm:text-lg md:text-xl font-bold leading-tight">
|
<Flex dir="row" letterSpacing={".2"}>
|
||||||
<p>
|
<AccordionButton>
|
||||||
<span className="font-light font-mono text-slate-500 text-base md:text-lg">
|
<AccordionIcon />
|
||||||
[{formatTime(topic.timestamp)}]
|
<Box as="span" textAlign="left" ml="1">
|
||||||
</span>
|
{topic.title}{" "}
|
||||||
<span>{topic.title}</span>
|
<Text
|
||||||
</p>
|
as="span"
|
||||||
<FontAwesomeIcon
|
color="gray.500"
|
||||||
className="transform transition-transform duration-200 ml-2"
|
fontSize="sm"
|
||||||
icon={
|
fontWeight="bold"
|
||||||
activeTopic?.id == topic.id
|
>
|
||||||
? faChevronDown
|
[{formatTime(topic.timestamp)}] - [
|
||||||
: faChevronRight
|
{formatTime(topic.timestamp + (topic.duration || 0))}]
|
||||||
}
|
</Text>
|
||||||
/>
|
</Box>
|
||||||
</div>
|
</AccordionButton>
|
||||||
{activeTopic?.id == topic.id && (
|
</Flex>
|
||||||
<div className="p-2">
|
<AccordionPanel>
|
||||||
{topic.segments ? (
|
{topic.segments ? (
|
||||||
<>
|
<>
|
||||||
{topic.segments.map((segment, index: number) => (
|
{topic.segments.map((segment, index: number) => (
|
||||||
<p
|
<Text
|
||||||
key={index}
|
key={index}
|
||||||
className="text-left text-slate-500 text-sm md:text-base"
|
className="text-left text-slate-500 text-sm md:text-base"
|
||||||
|
pb={2}
|
||||||
|
lineHeight={"1.3"}
|
||||||
|
>
|
||||||
|
<span className="font-mono text-slate-500">
|
||||||
|
[{formatTime(segment.start)}]
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className="font-bold text-slate-500"
|
||||||
|
style={{
|
||||||
|
color: generateHighContrastColor(
|
||||||
|
`Speaker ${segment.speaker}`,
|
||||||
|
[96, 165, 250],
|
||||||
|
),
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<span className="font-mono text-slate-500">
|
{" "}
|
||||||
[{formatTime(segment.start)}]
|
{getSpeakerName(segment.speaker)}:
|
||||||
</span>
|
</span>{" "}
|
||||||
<span
|
<span>{segment.text}</span>
|
||||||
className="font-bold text-slate-500"
|
</Text>
|
||||||
style={{
|
))}
|
||||||
color: generateHighContrastColor(
|
</>
|
||||||
`Speaker ${segment.speaker}`,
|
) : (
|
||||||
[96, 165, 250],
|
<>{topic.transcript}</>
|
||||||
),
|
)}
|
||||||
}}
|
</AccordionPanel>
|
||||||
>
|
</AccordionItem>
|
||||||
{" "}
|
|
||||||
{getSpeakerName(segment.speaker)}:
|
|
||||||
</span>{" "}
|
|
||||||
<span>{segment.text}</span>
|
|
||||||
</p>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>{topic.transcript}</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</Accordion>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-center text-gray-500">
|
<Box textAlign={"center"} textColor="gray">
|
||||||
Discussion topics will appear here after you start recording.
|
<Text>
|
||||||
<br />
|
Discussion topics will appear here after you start recording.
|
||||||
It may take up to 5 minutes of conversation for the first topic to
|
</Text>
|
||||||
appear.
|
<Text>
|
||||||
</div>
|
It may take up to 5 minutes of conversation for the first topic to
|
||||||
|
appear.
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
)}
|
)}
|
||||||
</section>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { UpdateTranscript } from "../../api";
|
import { UpdateTranscript } from "../../api";
|
||||||
import useApi from "../../lib/useApi";
|
import useApi from "../../lib/useApi";
|
||||||
|
import { Heading, IconButton, Input } from "@chakra-ui/react";
|
||||||
|
import { FaPen } from "react-icons/fa";
|
||||||
|
|
||||||
type TranscriptTitle = {
|
type TranscriptTitle = {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -19,7 +21,6 @@ const TranscriptTitle = (props: TranscriptTitle) => {
|
|||||||
const requestBody: UpdateTranscript = {
|
const requestBody: UpdateTranscript = {
|
||||||
title: newTitle,
|
title: newTitle,
|
||||||
};
|
};
|
||||||
const api = useApi();
|
|
||||||
const updatedTranscript = await api?.v1TranscriptUpdate(
|
const updatedTranscript = await api?.v1TranscriptUpdate(
|
||||||
transcriptId,
|
transcriptId,
|
||||||
requestBody,
|
requestBody,
|
||||||
@@ -46,6 +47,12 @@ const TranscriptTitle = (props: TranscriptTitle) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleBlur = () => {
|
||||||
|
if (displayedTitle !== preEditTitle) {
|
||||||
|
updateTitle(displayedTitle, props.transcriptId);
|
||||||
|
}
|
||||||
|
setIsEditing(false);
|
||||||
|
};
|
||||||
const handleChange = (e) => {
|
const handleChange = (e) => {
|
||||||
setDisplayedTitle(e.target.value);
|
setDisplayedTitle(e.target.value);
|
||||||
};
|
};
|
||||||
@@ -63,21 +70,36 @@ const TranscriptTitle = (props: TranscriptTitle) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isEditing ? (
|
{isEditing ? (
|
||||||
<input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
value={displayedTitle}
|
value={displayedTitle}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
autoFocus
|
autoFocus
|
||||||
className="text-2xl lg:text-4xl font-extrabold text-center mb-4 w-full border-none bg-transparent overflow-hidden h-[fit-content]"
|
onBlur={handleBlur}
|
||||||
|
size={"lg"}
|
||||||
|
fontSize={"xl"}
|
||||||
|
fontWeight={"bold"}
|
||||||
|
// className="text-2xl lg:text-4xl font-extrabold text-center mb-4 w-full border-none bg-transparent overflow-hidden h-[fit-content]"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<h2
|
<>
|
||||||
className="text-2xl lg:text-4xl font-extrabold text-center mb-4 cursor-pointer"
|
<Heading
|
||||||
onClick={handleTitleClick}
|
// className="text-2xl lg:text-4xl font-extrabold text-center mb-4 cursor-pointer"
|
||||||
>
|
onClick={handleTitleClick}
|
||||||
{displayedTitle}
|
cursor={"pointer"}
|
||||||
</h2>
|
size={"lg"}
|
||||||
|
noOfLines={1}
|
||||||
|
>
|
||||||
|
{displayedTitle}
|
||||||
|
</Heading>
|
||||||
|
<IconButton
|
||||||
|
icon={<FaPen />}
|
||||||
|
aria-label="Edit Transcript Title"
|
||||||
|
onClick={handleTitleClick}
|
||||||
|
fontSize={"15px"}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,34 @@
|
|||||||
// 1. Import `extendTheme`
|
// 1. Import `extendTheme`
|
||||||
import { extendTheme } from "@chakra-ui/react";
|
import { extendTheme } from "@chakra-ui/react";
|
||||||
|
|
||||||
// 2. Call `extendTheme` and pass your custom values
|
import { accordionAnatomy } from "@chakra-ui/anatomy";
|
||||||
|
import { createMultiStyleConfigHelpers, defineStyle } from "@chakra-ui/react";
|
||||||
|
|
||||||
|
const { definePartsStyle, defineMultiStyleConfig } =
|
||||||
|
createMultiStyleConfigHelpers(accordionAnatomy.keys);
|
||||||
|
|
||||||
|
const custom = definePartsStyle({
|
||||||
|
container: {
|
||||||
|
border: "0",
|
||||||
|
borderRadius: "8px",
|
||||||
|
backgroundColor: "white",
|
||||||
|
mb: 2,
|
||||||
|
mr: 2,
|
||||||
|
},
|
||||||
|
panel: {
|
||||||
|
pl: 8,
|
||||||
|
pb: 0,
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
pl: 2,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const accordionTheme = defineMultiStyleConfig({
|
||||||
|
variants: { custom },
|
||||||
|
});
|
||||||
|
|
||||||
const theme = extendTheme({
|
const theme = extendTheme({
|
||||||
colors: {
|
colors: {
|
||||||
blue: {
|
blue: {
|
||||||
@@ -29,6 +56,9 @@ const theme = extendTheme({
|
|||||||
light: "#FFFFFF",
|
light: "#FFFFFF",
|
||||||
dark: "#0C0D0E",
|
dark: "#0C0D0E",
|
||||||
},
|
},
|
||||||
|
components: {
|
||||||
|
Accordion: accordionTheme,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default theme;
|
export default theme;
|
||||||
|
|||||||
Reference in New Issue
Block a user