mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2025-12-20 20:29:06 +00:00
fix: rework main page to use Chakra, and a little bit of the browse page (#402)
This commit is contained in:
@@ -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">
|
||||||
<Text fontSize="small">
|
<Flex align={"center"} gap={2}>
|
||||||
{new Date(item.created_at).toLocaleString("en-US")}
|
{item.status == "ended" && (
|
||||||
{"\u00A0"}-{"\u00A0"}
|
<Tooltip label="Processing done">
|
||||||
{formatTimeMs(item.duration)}
|
<span>
|
||||||
</Text>
|
<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", {
|
||||||
|
year: "numeric",
|
||||||
|
month: "long",
|
||||||
|
day: "numeric",
|
||||||
|
hour: "numeric",
|
||||||
|
minute: "numeric",
|
||||||
|
})}
|
||||||
|
{"\u00A0"}-{"\u00A0"}
|
||||||
|
{formatTimeMs(item.duration)}
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
<ExpandableText noOfLines={5}>
|
<ExpandableText noOfLines={5}>
|
||||||
{item.short_summary}
|
{item.short_summary}
|
||||||
</ExpandableText>
|
</ExpandableText>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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,106 +97,132 @@ 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"
|
||||||
Reflector is a transcription and summarization pipeline that
|
maxH="100%"
|
||||||
transforms audio into knowledge.
|
>
|
||||||
<span className="hidden md:block">
|
<Flex
|
||||||
The output is meeting minutes and topic summaries enabling
|
flexDir={{ base: "column", md: "row" }}
|
||||||
topic-specific analyses stored in your systems of record. This
|
justify="space-between"
|
||||||
is accomplished on your infrastructure – without 3rd parties –
|
align="center"
|
||||||
keeping your data private, secure, and organized.
|
gap={4}
|
||||||
</span>
|
>
|
||||||
</p>
|
<Flex
|
||||||
<About buttonText="Learn more" />
|
flexDir="column"
|
||||||
<p className="mt-6">
|
h="full"
|
||||||
In order to use Reflector, we kindly request permission to access
|
justify="evenly"
|
||||||
your microphone during meetings and events.
|
flexBasis="1"
|
||||||
</p>
|
flexGrow={1}
|
||||||
{featureEnabled("privacy") && (
|
>
|
||||||
<Privacy buttonText="Privacy policy" />
|
<Heading size="lg" textAlign={{ base: "center", md: "left" }}>
|
||||||
)}
|
Welcome to Reflector
|
||||||
</div>
|
</Heading>
|
||||||
</section>
|
<Text mt={6}>
|
||||||
<section className="flex flex-col justify-center items-center w-full h-full">
|
Reflector is a transcription and summarization pipeline that
|
||||||
{!sessionReady ? (
|
transforms audio into knowledge.
|
||||||
<Spinner />
|
<Text className="hidden md:block">
|
||||||
) : requireLogin && !isAuthenticated ? (
|
The output is meeting minutes and topic summaries enabling
|
||||||
<button
|
topic-specific analyses stored in your systems of record. This is
|
||||||
className="mt-4 bg-blue-400 hover:bg-blue-500 focus-visible:bg-blue-500 text-white font-bold py-2 px-4 rounded"
|
accomplished on your infrastructure – without 3rd parties –
|
||||||
onClick={() => signIn("authentik")}
|
keeping your data private, secure, and organized.
|
||||||
>
|
</Text>
|
||||||
Log in
|
</Text>
|
||||||
</button>
|
<About buttonText="Learn more" />
|
||||||
) : (
|
<Text mt={6}>
|
||||||
<div className="rounded-xl md:bg-blue-200 md:w-96 p-4 lg:p-6 flex flex-col mb-4 md:mb-10">
|
In order to use Reflector, we kindly request permission to access
|
||||||
<h2 className="text-2xl font-bold mt-2 mb-2">Try Reflector</h2>
|
your microphone during meetings and events.
|
||||||
<label className="mb-3">
|
</Text>
|
||||||
<p>Recording name</p>
|
{featureEnabled("privacy") && <Privacy buttonText="Privacy policy" />}
|
||||||
<div className="select-search-container">
|
</Flex>
|
||||||
<input
|
<Flex flexDir="column" h="full" flexBasis="1" flexGrow={1}>
|
||||||
className="select-search-input"
|
<Center>
|
||||||
type="text"
|
{!sessionReady ? (
|
||||||
onChange={nameChange}
|
<Spinner />
|
||||||
placeholder="Optional"
|
) : requireLogin && !isAuthenticated ? (
|
||||||
|
<Button onClick={() => signIn("authentik")} colorScheme="blue">
|
||||||
|
Log in
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<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"
|
||||||
|
type="text"
|
||||||
|
onChange={nameChange}
|
||||||
|
placeholder="Optional"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Box>
|
||||||
|
<Box mb={4}>
|
||||||
|
<Text>Do you want to enable live translation?</Text>
|
||||||
|
<SelectSearch
|
||||||
|
search
|
||||||
|
options={supportedLanguages}
|
||||||
|
value={targetLanguage}
|
||||||
|
onChange={onLanguageChange}
|
||||||
|
placeholder="Choose your language"
|
||||||
/>
|
/>
|
||||||
</div>
|
</Box>
|
||||||
</label>
|
{loading ? (
|
||||||
<label className="mb-3">
|
<Text className="">Checking permissions...</Text>
|
||||||
<p>Do you want to enable live translation?</p>
|
) : permissionOk ? (
|
||||||
<SelectSearch
|
<Spacer />
|
||||||
search
|
) : permissionDenied ? (
|
||||||
options={supportedLanguages}
|
<Text className="">
|
||||||
value={targetLanguage}
|
Permission to use your microphone was denied, please change
|
||||||
onChange={onLanguageChange}
|
the permission setting in your browser and refresh this
|
||||||
placeholder="Choose your language"
|
page.
|
||||||
/>
|
</Text>
|
||||||
</label>
|
) : (
|
||||||
{loading ? (
|
<Button
|
||||||
<p className="">Checking permissions...</p>
|
colorScheme="whiteAlpha"
|
||||||
) : permissionOk ? (
|
onClick={requestPermission}
|
||||||
<p className=""> Microphone permission granted </p>
|
disabled={permissionDenied}
|
||||||
) : permissionDenied ? (
|
>
|
||||||
<p className="">
|
Request Microphone Permission
|
||||||
Permission to use your microphone was denied, please change
|
</Button>
|
||||||
the permission setting in your browser and refresh this page.
|
)}
|
||||||
</p>
|
|
||||||
) : (
|
|
||||||
<Button
|
<Button
|
||||||
colorScheme="blue"
|
colorScheme="whiteAlpha"
|
||||||
onClick={requestPermission}
|
onClick={send}
|
||||||
disabled={permissionDenied}
|
isDisabled={!permissionOk || loadingRecord || loadingUpload}
|
||||||
|
mt={2}
|
||||||
>
|
>
|
||||||
Request Microphone Permission
|
{loadingRecord ? "Loading..." : "Record Meeting"}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
<Text align="center" m="2">
|
||||||
<Button
|
OR
|
||||||
colorScheme="blue"
|
</Text>
|
||||||
onClick={send}
|
<Button
|
||||||
isDisabled={!permissionOk || loadingRecord || loadingUpload}
|
colorScheme="whiteAlpha"
|
||||||
mt={2}
|
onClick={uploadFile}
|
||||||
>
|
isDisabled={loadingRecord || loadingUpload}
|
||||||
{loadingRecord ? "Loading..." : "Record Meeting"}
|
>
|
||||||
</Button>
|
{loadingUpload ? "Loading..." : "Upload File"}
|
||||||
<Text align="center" m="2">
|
</Button>
|
||||||
OR
|
</Flex>
|
||||||
</Text>
|
)}
|
||||||
<Button
|
</Center>
|
||||||
colorScheme="blue"
|
</Flex>
|
||||||
onClick={uploadFile}
|
</Flex>
|
||||||
isDisabled={loadingRecord || loadingUpload}
|
</Flex>
|
||||||
>
|
|
||||||
{loadingUpload ? "Loading..." : "Upload File"}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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>}>
|
||||||
|
|||||||
@@ -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
23
www/public/reach.svg
Normal 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 |
Reference in New Issue
Block a user