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";
import React, { useEffect, useState } from "react";
import { useSession } from "next-auth/react";
import { GetTranscript } from "../../api";
import Pagination from "./pagination";
@@ -44,6 +45,7 @@ import { ExpandableText } from "../../lib/expandableText";
export default function TranscriptBrowser() {
const [page, setPage] = useState<number>(1);
const { loading, response, refetch } = useTranscriptList(page);
const { data: session } = useSession();
const [deletionLoading, setDeletionLoading] = useState(false);
const api = useApi();
const { setError } = useError();
@@ -124,8 +126,9 @@ export default function TranscriptBrowser() {
flexDir="column"
margin="auto"
gap={2}
overflowY="scroll"
maxH="100%"
overflowY="auto"
minH="100%"
mt={8}
>
<Flex
flexDir="row"
@@ -133,8 +136,12 @@ export default function TranscriptBrowser() {
align="center"
flexWrap={"wrap-reverse"}
>
{/* <Heading>{user?.fields?.name}'s Meetings</Heading> */}
<Heading>Your Meetings</Heading>
{session?.user?.name ? (
<Heading size="md">{session?.user?.name}'s Meetings</Heading>
) : (
<Heading size="md">Your meetings</Heading>
)}
{loading || (deletionLoading && <Spinner></Spinner>)}
<Spacer />
@@ -149,7 +156,6 @@ export default function TranscriptBrowser() {
New Meeting
</Button>
</Flex>
<Grid
templateColumns={{
base: "repeat(1, 1fr)",
@@ -161,7 +167,7 @@ export default function TranscriptBrowser() {
lg: 4,
}}
maxH="100%"
overflowY={"scroll"}
overflowY="auto"
mb="4"
>
{response?.items
@@ -169,49 +175,7 @@ export default function TranscriptBrowser() {
.map((item: GetTranscript) => (
<Card key={item.id} border="gray.light" variant="outline">
<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>
)}
<Flex align={"center"} gap={2}>
<Heading size="md">
<Link
as={NextLink}
@@ -279,12 +243,61 @@ export default function TranscriptBrowser() {
</MenuList>
</Menu>
</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">
{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"}
{formatTimeMs(item.duration)}
</Text>
</Flex>
<ExpandableText noOfLines={5}>
{item.short_summary}
</ExpandableText>

View File

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

View File

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

View File

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

View File

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