fix: rework main page to use Chakra, and a little bit of the browse page (#402)

This commit is contained in:
2024-09-04 03:27:20 +02:00
committed by GitHub
parent 873cbb0a42
commit 42796d7d3f
6 changed files with 271 additions and 175 deletions

View File

@@ -1,5 +1,6 @@
"use client"; "use client";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useSession } from "next-auth/react";
import { GetTranscript } from "../../api"; import { GetTranscript } from "../../api";
import Pagination from "./pagination"; import Pagination from "./pagination";
@@ -44,6 +45,7 @@ import { ExpandableText } from "../../lib/expandableText";
export default function TranscriptBrowser() { export default function TranscriptBrowser() {
const [page, setPage] = useState<number>(1); const [page, setPage] = useState<number>(1);
const { loading, response, refetch } = useTranscriptList(page); const { loading, response, refetch } = useTranscriptList(page);
const { data: session } = useSession();
const [deletionLoading, setDeletionLoading] = useState(false); const [deletionLoading, setDeletionLoading] = useState(false);
const api = useApi(); const api = useApi();
const { setError } = useError(); const { setError } = useError();
@@ -124,8 +126,9 @@ export default function TranscriptBrowser() {
flexDir="column" flexDir="column"
margin="auto" margin="auto"
gap={2} gap={2}
overflowY="scroll" overflowY="auto"
maxH="100%" minH="100%"
mt={8}
> >
<Flex <Flex
flexDir="row" flexDir="row"
@@ -133,8 +136,12 @@ export default function TranscriptBrowser() {
align="center" align="center"
flexWrap={"wrap-reverse"} flexWrap={"wrap-reverse"}
> >
{/* <Heading>{user?.fields?.name}'s Meetings</Heading> */} {session?.user?.name ? (
<Heading>Your Meetings</Heading> <Heading size="md">{session?.user?.name}'s Meetings</Heading>
) : (
<Heading size="md">Your meetings</Heading>
)}
{loading || (deletionLoading && <Spinner></Spinner>)} {loading || (deletionLoading && <Spinner></Spinner>)}
<Spacer /> <Spacer />
@@ -149,7 +156,6 @@ export default function TranscriptBrowser() {
New Meeting New Meeting
</Button> </Button>
</Flex> </Flex>
<Grid <Grid
templateColumns={{ templateColumns={{
base: "repeat(1, 1fr)", base: "repeat(1, 1fr)",
@@ -161,7 +167,7 @@ export default function TranscriptBrowser() {
lg: 4, lg: 4,
}} }}
maxH="100%" maxH="100%"
overflowY={"scroll"} overflowY="auto"
mb="4" mb="4"
> >
{response?.items {response?.items
@@ -169,49 +175,7 @@ export default function TranscriptBrowser() {
.map((item: GetTranscript) => ( .map((item: GetTranscript) => (
<Card key={item.id} border="gray.light" variant="outline"> <Card key={item.id} border="gray.light" variant="outline">
<CardBody> <CardBody>
<Flex align={"center"} ml="-6px"> <Flex align={"center"} gap={2}>
{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"> <Heading size="md">
<Link <Link
as={NextLink} as={NextLink}
@@ -279,12 +243,61 @@ export default function TranscriptBrowser() {
</MenuList> </MenuList>
</Menu> </Menu>
</Flex> </Flex>
<Stack mt="6" spacing="3"> <Stack mt="4" spacing="2">
<Flex align={"center"} gap={2}>
{item.status == "ended" && (
<Tooltip label="Processing done">
<span>
<Icon color="green" as={FaCheck} />
</span>
</Tooltip>
)}
{item.status == "error" && (
<Tooltip label="Processing error">
<span>
<Icon color="red.primary" as={MdError} />
</span>
</Tooltip>
)}
{item.status == "idle" && (
<Tooltip label="New meeting, no recording">
<span>
<Icon color="yellow.500" as={FaStar} />
</span>
</Tooltip>
)}
{item.status == "processing" && (
<Tooltip label="Processing in progress">
<span>
<Icon
color="grey.primary"
as={FaGear}
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} />
</span>
</Tooltip>
)}
<Text fontSize="small"> <Text fontSize="small">
{new Date(item.created_at).toLocaleString("en-US")} {new Date(item.created_at).toLocaleString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
minute: "numeric",
})}
{"\u00A0"}-{"\u00A0"} {"\u00A0"}-{"\u00A0"}
{formatTimeMs(item.duration)} {formatTimeMs(item.duration)}
</Text> </Text>
</Flex>
<ExpandableText noOfLines={5}> <ExpandableText noOfLines={5}>
{item.short_summary} {item.short_summary}
</ExpandableText> </ExpandableText>

View File

@@ -29,25 +29,22 @@ export default async function AppLayout({
w="100%" w="100%"
py="2" py="2"
px="0" px="0"
mt="1"
> >
{/* Logo on the left */} {/* Logo on the left */}
<Link <Link as={NextLink} href="/" className="flex">
as={NextLink}
href="/"
className="flex outline-blue-300 md:outline-none focus-visible:underline underline-offset-2 decoration-[.5px] decoration-gray-500"
>
<Image <Image
src="/reach.png" src="/reach.svg"
width={16} width={32}
height={16} height={40}
className="h-10 w-auto" className="h-11 w-auto"
alt="Reflector" alt="Reflector"
/> />
<div className="hidden flex-col ml-2 md:block"> <div className="hidden flex-col ml-3 md:block">
<h1 className="text-[38px] font-bold tracking-wide leading-tight"> <h1 className="text-[28px] font-semibold leading-tight">
Reflector Reflector
</h1> </h1>
<p className="text-gray-500 text-xs tracking-tighter"> <p className="text-gray-500 text-xs tracking-tight -mt-1">
Capture the signal, not the noise Capture the signal, not the noise
</p> </p>
</div> </div>

View File

@@ -12,9 +12,36 @@ import SelectSearch from "react-select-search";
import { supportedLanguages } from "../../../supportedLanguages"; import { supportedLanguages } from "../../../supportedLanguages";
import { useSession } from "next-auth/react"; import { useSession } from "next-auth/react";
import { featureEnabled } from "../../../domainContext"; import { featureEnabled } from "../../../domainContext";
import { Button, Text } from "@chakra-ui/react";
import { signIn } from "next-auth/react"; import { signIn } from "next-auth/react";
import { Spinner } from "@chakra-ui/react"; import {
Flex,
Box,
Spinner,
Heading,
Button,
Card,
Center,
Link,
CardBody,
Stack,
Text,
Icon,
Grid,
IconButton,
Spacer,
Menu,
MenuButton,
MenuItem,
MenuList,
AlertDialog,
AlertDialogOverlay,
AlertDialogContent,
AlertDialogHeader,
AlertDialogBody,
AlertDialogFooter,
Tooltip,
Input,
} from "@chakra-ui/react";
const TranscriptCreate = () => { const TranscriptCreate = () => {
const router = useRouter(); const router = useRouter();
const { status } = useSession(); const { status } = useSession();
@@ -70,46 +97,70 @@ const TranscriptCreate = () => {
useAudioDevice(); useAudioDevice();
return ( return (
<div className="grid grid-rows-layout-topbar gap-2 lg:gap-4 max-h-full overflow-y-scroll"> <Flex
<div className="lg:grid lg:grid-cols-2 lg:grid-rows-1 lg:gap-4 lg:h-full h-auto flex flex-col"> maxW="container.xl"
<section className="flex flex-col w-full lg:h-full items-center justify-evenly p-4 md:px-6 md:py-8"> flexDir="column"
<div className="flex flex-col max-w-xl items-center justify-center"> margin="auto"
<h1 className="text-2xl font-bold mb-2">Welcome to Reflector</h1> gap={2}
<p> overflowY="auto"
maxH="100%"
>
<Flex
flexDir={{ base: "column", md: "row" }}
justify="space-between"
align="center"
gap={4}
>
<Flex
flexDir="column"
h="full"
justify="evenly"
flexBasis="1"
flexGrow={1}
>
<Heading size="lg" textAlign={{ base: "center", md: "left" }}>
Welcome to Reflector
</Heading>
<Text mt={6}>
Reflector is a transcription and summarization pipeline that Reflector is a transcription and summarization pipeline that
transforms audio into knowledge. transforms audio into knowledge.
<span className="hidden md:block"> <Text className="hidden md:block">
The output is meeting minutes and topic summaries enabling The output is meeting minutes and topic summaries enabling
topic-specific analyses stored in your systems of record. This topic-specific analyses stored in your systems of record. This is
is accomplished on your infrastructure without 3rd parties accomplished on your infrastructure without 3rd parties
keeping your data private, secure, and organized. keeping your data private, secure, and organized.
</span> </Text>
</p> </Text>
<About buttonText="Learn more" /> <About buttonText="Learn more" />
<p className="mt-6"> <Text mt={6}>
In order to use Reflector, we kindly request permission to access In order to use Reflector, we kindly request permission to access
your microphone during meetings and events. your microphone during meetings and events.
</p> </Text>
{featureEnabled("privacy") && ( {featureEnabled("privacy") && <Privacy buttonText="Privacy policy" />}
<Privacy buttonText="Privacy policy" /> </Flex>
)} <Flex flexDir="column" h="full" flexBasis="1" flexGrow={1}>
</div> <Center>
</section>
<section className="flex flex-col justify-center items-center w-full h-full">
{!sessionReady ? ( {!sessionReady ? (
<Spinner /> <Spinner />
) : requireLogin && !isAuthenticated ? ( ) : requireLogin && !isAuthenticated ? (
<button <Button onClick={() => signIn("authentik")} colorScheme="blue">
className="mt-4 bg-blue-400 hover:bg-blue-500 focus-visible:bg-blue-500 text-white font-bold py-2 px-4 rounded"
onClick={() => signIn("authentik")}
>
Log in Log in
</button> </Button>
) : ( ) : (
<div className="rounded-xl md:bg-blue-200 md:w-96 p-4 lg:p-6 flex flex-col mb-4 md:mb-10"> <Flex
<h2 className="text-2xl font-bold mt-2 mb-2">Try Reflector</h2> rounded="xl"
<label className="mb-3"> bg="blue.primary"
<p>Recording name</p> color="white"
maxW="96"
p={8}
flexDir="column"
my={4}
>
<Heading size="md" mb={4}>
Try Reflector
</Heading>
<Box mb={4}>
<Text>Recording name</Text>
<div className="select-search-container"> <div className="select-search-container">
<input <input
className="select-search-input" className="select-search-input"
@@ -118,9 +169,9 @@ const TranscriptCreate = () => {
placeholder="Optional" placeholder="Optional"
/> />
</div> </div>
</label> </Box>
<label className="mb-3"> <Box mb={4}>
<p>Do you want to enable live translation?</p> <Text>Do you want to enable live translation?</Text>
<SelectSearch <SelectSearch
search search
options={supportedLanguages} options={supportedLanguages}
@@ -128,19 +179,20 @@ const TranscriptCreate = () => {
onChange={onLanguageChange} onChange={onLanguageChange}
placeholder="Choose your language" placeholder="Choose your language"
/> />
</label> </Box>
{loading ? ( {loading ? (
<p className="">Checking permissions...</p> <Text className="">Checking permissions...</Text>
) : permissionOk ? ( ) : permissionOk ? (
<p className=""> Microphone permission granted </p> <Spacer />
) : permissionDenied ? ( ) : permissionDenied ? (
<p className=""> <Text className="">
Permission to use your microphone was denied, please change Permission to use your microphone was denied, please change
the permission setting in your browser and refresh this page. the permission setting in your browser and refresh this
</p> page.
</Text>
) : ( ) : (
<Button <Button
colorScheme="blue" colorScheme="whiteAlpha"
onClick={requestPermission} onClick={requestPermission}
disabled={permissionDenied} disabled={permissionDenied}
> >
@@ -148,7 +200,7 @@ const TranscriptCreate = () => {
</Button> </Button>
)} )}
<Button <Button
colorScheme="blue" colorScheme="whiteAlpha"
onClick={send} onClick={send}
isDisabled={!permissionOk || loadingRecord || loadingUpload} isDisabled={!permissionOk || loadingRecord || loadingUpload}
mt={2} mt={2}
@@ -159,17 +211,18 @@ const TranscriptCreate = () => {
OR OR
</Text> </Text>
<Button <Button
colorScheme="blue" colorScheme="whiteAlpha"
onClick={uploadFile} onClick={uploadFile}
isDisabled={loadingRecord || loadingUpload} isDisabled={loadingRecord || loadingUpload}
> >
{loadingUpload ? "Loading..." : "Upload File"} {loadingUpload ? "Loading..." : "Upload File"}
</Button> </Button>
</div> </Flex>
)} )}
</section> </Center>
</div> </Flex>
</div> </Flex>
</Flex>
); );
}; };

View File

@@ -1,5 +1,4 @@
import "./styles/globals.scss"; import "./styles/globals.scss";
import { Poppins } from "next/font/google";
import { Metadata, Viewport } from "next"; import { Metadata, Viewport } from "next";
import SessionProvider from "./lib/SessionProvider"; import SessionProvider from "./lib/SessionProvider";
import { ErrorProvider } from "./(errors)/errorContext"; import { ErrorProvider } from "./(errors)/errorContext";
@@ -9,8 +8,6 @@ import { getConfig } from "./lib/edgeConfig";
import { ErrorBoundary } from "@sentry/nextjs"; import { ErrorBoundary } from "@sentry/nextjs";
import { Providers } from "./providers"; import { Providers } from "./providers";
const poppins = Poppins({ subsets: ["latin"], weight: ["200", "400", "600"] });
export const viewport: Viewport = { export const viewport: Viewport = {
themeColor: "black", themeColor: "black",
width: "device-width", width: "device-width",
@@ -68,11 +65,7 @@ export default async function RootLayout({
return ( return (
<html lang="en"> <html lang="en">
<body <body className={"h-[100svh] w-[100svw] overflow-hidden relative"}>
className={
poppins.className + "h-[100svh] w-[100svw] overflow-hidden relative"
}
>
<SessionProvider> <SessionProvider>
<DomainContextProvider config={config}> <DomainContextProvider config={config}>
<ErrorBoundary fallback={<p>"something went really wrong"</p>}> <ErrorBoundary fallback={<p>"something went really wrong"</p>}>

View File

@@ -1,12 +1,16 @@
// 1. Import `extendTheme`
import { extendTheme } from "@chakra-ui/react"; import { extendTheme } from "@chakra-ui/react";
import { Poppins } from "next/font/google";
import { accordionAnatomy } from "@chakra-ui/anatomy"; import { accordionAnatomy } from "@chakra-ui/anatomy";
import { createMultiStyleConfigHelpers, defineStyle } from "@chakra-ui/react"; import { createMultiStyleConfigHelpers, defineStyle } from "@chakra-ui/react";
const { definePartsStyle, defineMultiStyleConfig } = const { definePartsStyle, defineMultiStyleConfig } =
createMultiStyleConfigHelpers(accordionAnatomy.keys); createMultiStyleConfigHelpers(accordionAnatomy.keys);
const poppins = Poppins({
subsets: ["latin"],
weight: ["200", "400", "600"],
display: "swap",
});
const custom = definePartsStyle({ const custom = definePartsStyle({
container: { container: {
border: "0", border: "0",
@@ -29,6 +33,14 @@ const accordionTheme = defineMultiStyleConfig({
variants: { custom }, variants: { custom },
}); });
const linkTheme = defineStyle({
baseStyle: {
_hover: {
color: "blue.500",
textDecoration: "none",
},
},
});
export const colors = { export const colors = {
blue: { blue: {
primary: "#3158E2", primary: "#3158E2",
@@ -60,6 +72,11 @@ const theme = extendTheme({
colors, colors,
components: { components: {
Accordion: accordionTheme, Accordion: accordionTheme,
Link: linkTheme,
},
fonts: {
body: poppins.style.fontFamily,
heading: poppins.style.fontFamily,
}, },
}); });

23
www/public/reach.svg Normal file
View File

@@ -0,0 +1,23 @@
<svg width="39" height="47" viewBox="0 0 39 47" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M39 30.5L20 25.5L4 41.5L26 46.5L39 30.5Z" fill="#4A4A4A"/>
<path d="M39 30.5L20 25.5L4 41.5L26 46.5L39 30.5Z" fill="#4A4A4A"/>
<g filter="url(#filter0_d_101_28)">
<path d="M20 4V25.5L4 41.5V16.5L20 4Z" fill="#B6B6B6"/>
<path d="M20 4V25.5L4 41.5V16.5L20 4Z" fill="#B6B6B6"/>
<path d="M20 4V25.5L4 41.5V16.5L20 4Z" fill="#B6B6B6"/>
<path d="M20 4V25.5L4 41.5V16.5L20 4Z" fill="#B6B6B6"/>
</g>
<defs>
<filter id="filter0_d_101_28" x="0" y="0" width="24" height="45.5" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.25 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_101_28"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_101_28" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB