project menu updates
This commit is contained in:
@@ -23,6 +23,7 @@ ARG NEXT_PUBLIC_SOCKET_URL
|
|||||||
ARG NEXT_PUBLIC_HOST
|
ARG NEXT_PUBLIC_HOST
|
||||||
ARG NEXT_PUBLIC_SENTRY_DSN
|
ARG NEXT_PUBLIC_SENTRY_DSN
|
||||||
ARG SENTRY_AUTH_TOKEN
|
ARG SENTRY_AUTH_TOKEN
|
||||||
|
ARG NEXT_PUBLIC_FF_SHOW_LOGGED_CALLS
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=deps /app/node_modules ./node_modules
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
|||||||
@@ -52,7 +52,9 @@ const NavSidebar = () => {
|
|||||||
<>
|
<>
|
||||||
<ProjectMenu />
|
<ProjectMenu />
|
||||||
<Divider />
|
<Divider />
|
||||||
<IconLink icon={IoStatsChartOutline} label="Logged Calls" href="/logged-calls" beta />
|
{env.NEXT_PUBLIC_FF_SHOW_LOGGED_CALLS && (
|
||||||
|
<IconLink icon={IoStatsChartOutline} label="Logged Calls" href="/logged-calls" beta />
|
||||||
|
)}
|
||||||
<IconLink icon={RiFlaskLine} label="Experiments" href="/experiments" />
|
<IconLink icon={RiFlaskLine} label="Experiments" href="/experiments" />
|
||||||
{env.NEXT_PUBLIC_SHOW_DATA && (
|
{env.NEXT_PUBLIC_SHOW_DATA && (
|
||||||
<IconLink icon={RiDatabase2Line} label="Data" href="/data" />
|
<IconLink icon={RiDatabase2Line} label="Data" href="/data" />
|
||||||
|
|||||||
@@ -12,9 +12,8 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
useDisclosure,
|
useDisclosure,
|
||||||
Spinner,
|
Spinner,
|
||||||
useBreakpointValue,
|
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { AiFillCaretDown } from "react-icons/ai";
|
import { AiFillCaretDown } from "react-icons/ai";
|
||||||
import { BsGear, BsPlus } from "react-icons/bs";
|
import { BsGear, BsPlus } from "react-icons/bs";
|
||||||
@@ -44,8 +43,6 @@ export default function ProjectMenu() {
|
|||||||
|
|
||||||
const { data: selectedOrg } = useSelectedOrg();
|
const { data: selectedOrg } = useSelectedOrg();
|
||||||
|
|
||||||
const [expandButtonHovered, setExpandButtonHovered] = useState(false);
|
|
||||||
|
|
||||||
const popover = useDisclosure();
|
const popover = useDisclosure();
|
||||||
|
|
||||||
const createMutation = api.organizations.create.useMutation();
|
const createMutation = api.organizations.create.useMutation();
|
||||||
@@ -56,113 +53,84 @@ export default function ProjectMenu() {
|
|||||||
await router.push({ pathname: "/project/settings" });
|
await router.push({ pathname: "/project/settings" });
|
||||||
}, [createMutation, router]);
|
}, [createMutation, router]);
|
||||||
|
|
||||||
const openMenu = useCallback(
|
|
||||||
(event: React.MouseEvent<Element, MouseEvent>) => {
|
|
||||||
event.preventDefault();
|
|
||||||
popover.onToggle();
|
|
||||||
},
|
|
||||||
[popover],
|
|
||||||
);
|
|
||||||
|
|
||||||
const sidebarExpanded = useBreakpointValue({ base: false, md: true });
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<VStack w="full" alignItems="flex-start" spacing={0}>
|
||||||
<Popover
|
<Text
|
||||||
placement="bottom-start"
|
pl={2}
|
||||||
isOpen={popover.isOpen}
|
pb={2}
|
||||||
onClose={popover.onClose}
|
fontSize="xs"
|
||||||
closeOnBlur
|
fontWeight="bold"
|
||||||
|
color="gray.500"
|
||||||
|
display={{ base: "none", md: "flex" }}
|
||||||
>
|
>
|
||||||
<VStack w="full" alignItems="flex-start" spacing={0}>
|
PROJECT
|
||||||
<Text
|
</Text>
|
||||||
pl={2}
|
<NavSidebarOption>
|
||||||
pb={2}
|
<Popover
|
||||||
fontSize="xs"
|
placement="bottom-start"
|
||||||
fontWeight="bold"
|
isOpen={popover.isOpen}
|
||||||
color="gray.500"
|
onClose={popover.onClose}
|
||||||
display={{ base: "none", md: "flex" }}
|
closeOnBlur
|
||||||
>
|
|
||||||
PROJECT
|
|
||||||
</Text>
|
|
||||||
<NavSidebarOption activeHrefPattern="/home" disableHoverEffect={expandButtonHovered}>
|
|
||||||
<Link href="/logged-calls">
|
|
||||||
<HStack w="full" justifyContent="space-between">
|
|
||||||
<HStack>
|
|
||||||
<Flex
|
|
||||||
p={1}
|
|
||||||
borderRadius={4}
|
|
||||||
backgroundColor="orange.100"
|
|
||||||
minW={{ base: 10, md: 8 }}
|
|
||||||
minH={{ base: 10, md: 8 }}
|
|
||||||
m={{ base: 0, md: 1 }}
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="center"
|
|
||||||
onClick={sidebarExpanded ? undefined : openMenu}
|
|
||||||
>
|
|
||||||
<Text>{selectedOrg?.name[0]?.toUpperCase()}</Text>
|
|
||||||
</Flex>
|
|
||||||
<Text fontSize="sm" display={{ base: "none", md: "block" }} py={1}>
|
|
||||||
{selectedOrg?.name}
|
|
||||||
</Text>
|
|
||||||
</HStack>
|
|
||||||
<PopoverTrigger>
|
|
||||||
<IconButton
|
|
||||||
aria-label="Open Project Menu"
|
|
||||||
icon={<Icon as={AiFillCaretDown} boxSize={3} />}
|
|
||||||
size="xs"
|
|
||||||
colorScheme="gray"
|
|
||||||
color="gray.500"
|
|
||||||
variant="ghost"
|
|
||||||
mr={2}
|
|
||||||
borderRadius={4}
|
|
||||||
onMouseEnter={() => setExpandButtonHovered(true)}
|
|
||||||
onMouseLeave={() => setExpandButtonHovered(false)}
|
|
||||||
_hover={{ bgColor: isActive ? "gray.300" : "gray.200", transitionDelay: 0 }}
|
|
||||||
onClick={openMenu}
|
|
||||||
/>
|
|
||||||
</PopoverTrigger>
|
|
||||||
</HStack>
|
|
||||||
</Link>
|
|
||||||
</NavSidebarOption>
|
|
||||||
</VStack>
|
|
||||||
<PopoverContent
|
|
||||||
_focusVisible={{ boxShadow: "unset" }}
|
|
||||||
minW={0}
|
|
||||||
borderColor="blue.400"
|
|
||||||
w="auto"
|
|
||||||
>
|
>
|
||||||
<VStack alignItems="flex-start" spacing={2} py={4} px={2}>
|
<PopoverTrigger>
|
||||||
<Text color="gray.500" fontSize="xs" fontWeight="bold" pb={1}>
|
<HStack w="full" justifyContent="space-between" onClick={popover.onToggle}>
|
||||||
PROJECTS
|
<Flex
|
||||||
</Text>
|
p={1}
|
||||||
<Divider />
|
borderRadius={4}
|
||||||
<VStack spacing={0} w="full">
|
backgroundColor="orange.100"
|
||||||
{orgs?.map((org) => (
|
minW={{ base: 10, md: 8 }}
|
||||||
<ProjectOption
|
minH={{ base: 10, md: 8 }}
|
||||||
key={org.id}
|
m={{ base: 0, md: 1 }}
|
||||||
org={org}
|
alignItems="center"
|
||||||
isActive={org.id === selectedOrgId}
|
justifyContent="center"
|
||||||
onClose={popover.onClose}
|
// onClick={sidebarExpanded ? undefined : openMenu}
|
||||||
/>
|
>
|
||||||
))}
|
<Text>{selectedOrg?.name[0]?.toUpperCase()}</Text>
|
||||||
</VStack>
|
</Flex>
|
||||||
<HStack
|
<Text fontSize="sm" display={{ base: "none", md: "block" }} py={1}>
|
||||||
as={Button}
|
{selectedOrg?.name}
|
||||||
variant="ghost"
|
</Text>
|
||||||
colorScheme="blue"
|
<Icon as={AiFillCaretDown} boxSize={3} size="xs" color="gray.500" mr={2} />
|
||||||
color="blue.400"
|
|
||||||
pr={8}
|
|
||||||
w="full"
|
|
||||||
onClick={createProject}
|
|
||||||
>
|
|
||||||
<Icon as={isLoading ? Spinner : BsPlus} boxSize={6} />
|
|
||||||
<Text>New project</Text>
|
|
||||||
</HStack>
|
</HStack>
|
||||||
</VStack>
|
</PopoverTrigger>
|
||||||
</PopoverContent>
|
<PopoverContent
|
||||||
</Popover>
|
_focusVisible={{ boxShadow: "unset" }}
|
||||||
</>
|
minW={0}
|
||||||
|
borderColor="blue.400"
|
||||||
|
w="full"
|
||||||
|
>
|
||||||
|
<VStack alignItems="flex-start" spacing={2} py={4} px={2}>
|
||||||
|
<Text color="gray.500" fontSize="xs" fontWeight="bold" pb={1}>
|
||||||
|
PROJECTS
|
||||||
|
</Text>
|
||||||
|
<Divider />
|
||||||
|
<VStack spacing={0} w="full">
|
||||||
|
{orgs?.map((org) => (
|
||||||
|
<ProjectOption
|
||||||
|
key={org.id}
|
||||||
|
org={org}
|
||||||
|
isActive={org.id === selectedOrgId}
|
||||||
|
onClose={popover.onClose}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</VStack>
|
||||||
|
<HStack
|
||||||
|
as={Button}
|
||||||
|
variant="ghost"
|
||||||
|
colorScheme="blue"
|
||||||
|
color="blue.400"
|
||||||
|
pr={8}
|
||||||
|
w="full"
|
||||||
|
onClick={createProject}
|
||||||
|
>
|
||||||
|
<Icon as={isLoading ? Spinner : BsPlus} boxSize={6} />
|
||||||
|
<Text>New project</Text>
|
||||||
|
</HStack>
|
||||||
|
</VStack>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</NavSidebarOption>
|
||||||
|
</VStack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,7 +148,7 @@ const ProjectOption = ({
|
|||||||
return (
|
return (
|
||||||
<HStack
|
<HStack
|
||||||
as={Link}
|
as={Link}
|
||||||
href="/logged-calls"
|
href="/experiments"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedOrgId(org.id);
|
setSelectedOrgId(org.id);
|
||||||
onClose();
|
onClose();
|
||||||
@@ -190,7 +158,6 @@ const ProjectOption = ({
|
|||||||
bgColor={isActive ? "gray.100" : "transparent"}
|
bgColor={isActive ? "gray.100" : "transparent"}
|
||||||
_hover={gearHovered ? undefined : { bgColor: "gray.200", textDecoration: "none" }}
|
_hover={gearHovered ? undefined : { bgColor: "gray.200", textDecoration: "none" }}
|
||||||
p={2}
|
p={2}
|
||||||
borderRadius={4}
|
|
||||||
>
|
>
|
||||||
<Text>{org.name}</Text>
|
<Text>{org.name}</Text>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export const DeleteProjectDialog = ({
|
|||||||
if (!selectedOrg.data?.id) return;
|
if (!selectedOrg.data?.id) return;
|
||||||
await deleteMutation.mutateAsync({ id: selectedOrg.data.id });
|
await deleteMutation.mutateAsync({ id: selectedOrg.data.id });
|
||||||
await utils.organizations.list.invalidate();
|
await utils.organizations.list.invalidate();
|
||||||
await router.push({ pathname: "/logged-calls" });
|
await router.push({ pathname: "/experiments" });
|
||||||
onClose();
|
onClose();
|
||||||
}, [deleteMutation, selectedOrg, router]);
|
}, [deleteMutation, selectedOrg, router]);
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export const env = createEnv({
|
|||||||
NEXT_PUBLIC_HOST: z.string().url().default("http://localhost:3000"),
|
NEXT_PUBLIC_HOST: z.string().url().default("http://localhost:3000"),
|
||||||
NEXT_PUBLIC_SENTRY_DSN: z.string().optional(),
|
NEXT_PUBLIC_SENTRY_DSN: z.string().optional(),
|
||||||
NEXT_PUBLIC_SHOW_DATA: z.string().optional(),
|
NEXT_PUBLIC_SHOW_DATA: z.string().optional(),
|
||||||
|
NEXT_PUBLIC_FF_SHOW_LOGGED_CALLS: z.string().optional(),
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,6 +57,7 @@ export const env = createEnv({
|
|||||||
NEXT_PUBLIC_SENTRY_DSN: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
NEXT_PUBLIC_SENTRY_DSN: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
||||||
SENTRY_AUTH_TOKEN: process.env.SENTRY_AUTH_TOKEN,
|
SENTRY_AUTH_TOKEN: process.env.SENTRY_AUTH_TOKEN,
|
||||||
OPENPIPE_API_KEY: process.env.OPENPIPE_API_KEY,
|
OPENPIPE_API_KEY: process.env.OPENPIPE_API_KEY,
|
||||||
|
NEXT_PUBLIC_FF_SHOW_LOGGED_CALLS: process.env.NEXT_PUBLIC_FF_SHOW_LOGGED_CALLS,
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation.
|
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { type GetServerSideProps } from "next";
|
|||||||
export const getServerSideProps: GetServerSideProps = async () => {
|
export const getServerSideProps: GetServerSideProps = async () => {
|
||||||
return {
|
return {
|
||||||
redirect: {
|
redirect: {
|
||||||
destination: "/logged-calls",
|
destination: "/experiments",
|
||||||
permanent: false,
|
permanent: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ export default function Settings() {
|
|||||||
your code.
|
your code.
|
||||||
</Text>
|
</Text>
|
||||||
</VStack>
|
</VStack>
|
||||||
<CopiableCode code={`OPENPIPE_API_KEY=${apiKey}`} />
|
<CopiableCode code={apiKey} />
|
||||||
<Divider />
|
<Divider />
|
||||||
{selectedOrg?.personalOrgUserId ? (
|
{selectedOrg?.personalOrgUserId ? (
|
||||||
<VStack alignItems="flex-start">
|
<VStack alignItems="flex-start">
|
||||||
|
|||||||
@@ -34,9 +34,12 @@ export const dashboardRouter = createTRPCRouter({
|
|||||||
|
|
||||||
let originalDataIndex = periods.length - 1;
|
let originalDataIndex = periods.length - 1;
|
||||||
// *SLAMS DOWN GLASS OF WHISKEY* timezones, amirite?
|
// *SLAMS DOWN GLASS OF WHISKEY* timezones, amirite?
|
||||||
let dayToMatch = dayjs(input.startDate || new Date())
|
let dayToMatch = dayjs(input.startDate || new Date());
|
||||||
// Ensure that the initial date we're matching against is never before the first period
|
// Ensure that the initial date we're matching against is never before the first period
|
||||||
if (periods[originalDataIndex] && dayToMatch.isBefore(periods[originalDataIndex]?.period, "day")) {
|
if (
|
||||||
|
periods[originalDataIndex] &&
|
||||||
|
dayToMatch.isBefore(periods[originalDataIndex]?.period, "day")
|
||||||
|
) {
|
||||||
dayToMatch = dayjs(periods[originalDataIndex]?.period);
|
dayToMatch = dayjs(periods[originalDataIndex]?.period);
|
||||||
}
|
}
|
||||||
const backfilledPeriods: typeof periods = [];
|
const backfilledPeriods: typeof periods = [];
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ fs.writeFileSync(schemaPath, JSON.stringify(openApiDocument, null, 2), "utf-8");
|
|||||||
|
|
||||||
console.log("Generating Typescript client");
|
console.log("Generating Typescript client");
|
||||||
|
|
||||||
const tsClientPath = path.join(clientLibsPath, "js/codegen");
|
const tsClientPath = path.join(clientLibsPath, "typescript/codegen");
|
||||||
|
|
||||||
fs.rmSync(tsClientPath, { recursive: true, force: true });
|
fs.rmSync(tsClientPath, { recursive: true, force: true });
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { type Prisma } from "@prisma/client";
|
import { type Prisma } from "@prisma/client";
|
||||||
import { JsonValue, type JsonObject } from "type-fest";
|
import { type JsonValue, type JsonObject } from "type-fest";
|
||||||
import modelProviders from "~/modelProviders/modelProviders";
|
import modelProviders from "~/modelProviders/modelProviders";
|
||||||
import { prisma } from "~/server/db";
|
import { prisma } from "~/server/db";
|
||||||
import { wsConnection } from "~/utils/wsConnection";
|
import { wsConnection } from "~/utils/wsConnection";
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ export default function useSocket<T>(channel?: string | null) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!channel) return;
|
if (!channel) return;
|
||||||
|
|
||||||
console.log("connecting to channel", channel);
|
|
||||||
// Create websocket connection
|
// Create websocket connection
|
||||||
socketRef.current = io(url);
|
socketRef.current = io(url);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user