From ded82a9e673f0e2396e01c8273017154032c2960 Mon Sep 17 00:00:00 2001 From: Tom Wise <79859203+tomwisecodes@users.noreply.github.com> Date: Thu, 21 Nov 2024 09:29:46 +0000 Subject: [PATCH 01/25] chore: test build --- apps/nextjs/src/app/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/nextjs/src/app/page.tsx b/apps/nextjs/src/app/page.tsx index d5efcebec..b9d5048d0 100644 --- a/apps/nextjs/src/app/page.tsx +++ b/apps/nextjs/src/app/page.tsx @@ -4,6 +4,6 @@ import HomePage from "./home-page"; export default async function Page() { const result = await fetchAiHomepage(); - + console.log("test"); return ; } From b936de90d523575a1b8b35a36bbcbf73621cc28e Mon Sep 17 00:00:00 2001 From: Tom Wise <79859203+tomwisecodes@users.noreply.github.com> Date: Thu, 21 Nov 2024 10:19:15 +0000 Subject: [PATCH 02/25] chore: types and storeis --- apps/nextjs/src/components/AppComponents/Chat/Chat/types.ts | 4 +++- .../components/AppComponents/Chat/chat-history.stories.tsx | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/nextjs/src/components/AppComponents/Chat/Chat/types.ts b/apps/nextjs/src/components/AppComponents/Chat/Chat/types.ts index f231896ee..0ff4448f1 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/Chat/types.ts +++ b/apps/nextjs/src/components/AppComponents/Chat/Chat/types.ts @@ -5,4 +5,6 @@ export type DialogTypes = | "report-content" | "sensitive-moderation-user-comment" | "demo-interstitial" - | "demo-share-locked"; + | "demo-share-locked" + | "clear-history" + | "clear-single-chat"; diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-history.stories.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-history.stories.tsx index 21c3e5120..d5ccab452 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-history.stories.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-history.stories.tsx @@ -23,9 +23,7 @@ export default meta; type Story = StoryObj; export const Default: Story = { - args: { - userId: "user123", - }, + args: {}, }; export const WithoutUserId: Story = { From e46dd14b4194f5bdfc48a999debf11e52726536a Mon Sep 17 00:00:00 2001 From: Tom Wise <79859203+tomwisecodes@users.noreply.github.com> Date: Thu, 21 Nov 2024 10:22:13 +0000 Subject: [PATCH 03/25] chore: add top files --- .../AppComponents/Chat/chat-history.tsx | 69 ++++++++++++------- .../Chat/chat-start-accordion.tsx | 2 +- 2 files changed, 44 insertions(+), 27 deletions(-) diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-history.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-history.tsx index 430d74916..42647ab80 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-history.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-history.tsx @@ -2,20 +2,36 @@ import * as React from "react"; -import { OakIcon } from "@oaknational/oak-components"; +import { OakIcon, OakModal, OakModalFooter } from "@oaknational/oak-components"; import { usePathname } from "next/navigation"; import { SidebarList } from "@/components/AppComponents/Chat/sidebar-list"; -import { SheetTrigger } from "@/components/AppComponents/Chat/ui/sheet"; +import { ClearHistory } from "./clear-history"; import ChatButton from "./ui/chat-button"; -export function ChatHistory() { +export function ChatHistory({ + openSidebar, + setOpenSidebar, +}: { + openSidebar: boolean; + setOpenSidebar: (value: boolean) => void; +}) { const ailaId = usePathname().split("aila/")[1]; + return ( -
-
- + setOpenSidebar(false)} + footerSlot={ + + + + } + > +
+
{}}> Create new lesson - - - - - - AI experiments page - - - - - - Help - + + + + + + AI experiments page + + + + + + Help + +
+ + +
- - - -
+ ); } diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-start-accordion.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-start-accordion.tsx index 5f5c7953d..43af4bd6d 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-start-accordion.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-start-accordion.tsx @@ -195,7 +195,7 @@ AccordionContent.displayName = "AccordionContent"; export default ChatStartAccordion; -function convertTitleCaseToSentenceCase(titleCase: string) { +export function convertTitleCaseToSentenceCase(titleCase: string) { const lowerCaseTitle = titleCase.toLowerCase(); return lowerCaseTitle.charAt(0).toUpperCase() + lowerCaseTitle.slice(1); } From bb51c126e0742100a4d48dd058d2cc35feaeada1 Mon Sep 17 00:00:00 2001 From: Tom Wise <79859203+tomwisecodes@users.noreply.github.com> Date: Thu, 21 Nov 2024 10:27:24 +0000 Subject: [PATCH 04/25] chore: add top files --- .../AppComponents/Chat/clear-history.tsx | 98 ++++++------------- .../components/AppComponents/Chat/header.tsx | 8 +- .../Chat/sidebar-actions.stories.tsx | 1 + 3 files changed, 32 insertions(+), 75 deletions(-) diff --git a/apps/nextjs/src/components/AppComponents/Chat/clear-history.tsx b/apps/nextjs/src/components/AppComponents/Chat/clear-history.tsx index 52321335b..8dd176f25 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/clear-history.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/clear-history.tsx @@ -1,81 +1,39 @@ "use client"; import * as React from "react"; -import { toast } from "react-hot-toast"; -import { useRouter } from "next/navigation"; +import { OakFlex, OakSpan } from "@oaknational/oak-components"; -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, - AlertDialogTrigger, -} from "@/components/AppComponents/Chat/ui/alert-dialog"; -import { Button } from "@/components/AppComponents/Chat/ui/button"; -import { IconSpinner } from "@/components/AppComponents/Chat/ui/icons"; -import { trpc } from "@/utils/trpc"; +import BinIcon from "@/components/BinIcon"; -type ClearHistoryProps = Readonly<{ - isEnabled: boolean; -}>; - -export function ClearHistory({ isEnabled = false }: ClearHistoryProps) { - const clearChats = trpc.chat.appSessions.deleteAllChats.useMutation({ - onSuccess() { - toast.success("Chat history cleared"); - setOpen(false); - router.push("/"); - }, - onError() { - toast.error("Failed to clear chat history"); - }, - }).mutate; +import { useDialog } from "../DialogContext"; - const [open, setOpen] = React.useState(false); - const [isPending, startTransition] = React.useTransition(); - const router = useRouter(); +type ClearHistoryProps = { + isEnabled: boolean; +}; +export function ClearHistory({ isEnabled }: Readonly) { + const { setDialogWindow, setOpenSidebar } = useDialog(); + if (!isEnabled) { + return null; + } return ( - - - - - - - Are you absolutely sure? - - This will permanently delete your chat history and remove your data - from our servers. - - - - Cancel - { - event.preventDefault(); - startTransition(() => { - clearChats(); - }); - }} - > - {isPending && } - Delete - - - - + + + + ); } diff --git a/apps/nextjs/src/components/AppComponents/Chat/header.tsx b/apps/nextjs/src/components/AppComponents/Chat/header.tsx index 08af379db..39b4efa8c 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/header.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/header.tsx @@ -15,8 +15,8 @@ import { usePathname } from "next/navigation"; import { useDemoUser } from "@/components/ContextProviders/Demo"; import OakIconLogo from "@/components/OakIconLogo"; +import { useDialog } from "../DialogContext"; import { BetaTagHeader } from "./beta-tag"; -import { ChatHistory } from "./chat-history"; import { SidebarMobile } from "./sidebar-mobile"; import { UserOrLogin } from "./user-or-login"; @@ -27,7 +27,7 @@ export function Header() { const clerkMetadata = useClerkDemoMetadata(); const ailaId = usePathname().split("aila/")[1]; - + const { setOpenSidebar } = useDialog(); return ( - - - + diff --git a/apps/nextjs/src/components/AppComponents/Chat/sidebar-actions.stories.tsx b/apps/nextjs/src/components/AppComponents/Chat/sidebar-actions.stories.tsx index d0310566d..b970589d9 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/sidebar-actions.stories.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/sidebar-actions.stories.tsx @@ -19,6 +19,7 @@ const mockChat = { id: "1", title: "Mock chat title", isShared: false, + updatedAt: new Date(), }; export const Default: Story = { From 9a6315a79cd888150b1cf63333fa48ccd02bd6c6 Mon Sep 17 00:00:00 2001 From: Tom Wise <79859203+tomwisecodes@users.noreply.github.com> Date: Thu, 21 Nov 2024 10:28:05 +0000 Subject: [PATCH 05/25] chore: add top files --- .../AppComponents/Chat/sidebar-item.tsx | 88 +++++++++---------- 1 file changed, 43 insertions(+), 45 deletions(-) diff --git a/apps/nextjs/src/components/AppComponents/Chat/sidebar-item.tsx b/apps/nextjs/src/components/AppComponents/Chat/sidebar-item.tsx index 71dec1e16..22ac37fcd 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/sidebar-item.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/sidebar-item.tsx @@ -1,53 +1,52 @@ "use client"; import * as React from "react"; +import { useState } from "react"; -import { motion } from "framer-motion"; +import { OakFlex, OakSpan } from "@oaknational/oak-components"; import Link from "next/link"; -import { usePathname } from "next/navigation"; import AiIcon from "@/components/AiIcon"; -import { buttonVariants } from "@/components/AppComponents/Chat/ui/button"; import { IconUsers } from "@/components/AppComponents/Chat/ui/icons"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/AppComponents/Chat/ui/tooltip"; +import BinIcon from "@/components/BinIcon"; import type { SideBarChatItem } from "@/lib/types"; -import { cn } from "@/lib/utils"; +import { useDialog } from "../DialogContext"; import { constructChatPath } from "./Chat/utils"; +import { convertTitleCaseToSentenceCase } from "./chat-start-accordion"; interface SidebarItemProps { chat: SideBarChatItem; children?: React.ReactNode; } -export function SidebarItem({ chat, children }: SidebarItemProps) { - const pathname = usePathname(); - - const isActive = pathname.includes(chat.id); - +export function SidebarItem({ chat }: SidebarItemProps) { + const { setDialogWindow, setDialogProps, setOpenSidebar } = useDialog(); + const [isHovered, setIsHovered] = useState(false); + function deleteChatById() { + setDialogProps({ chatIdToDelete: chat.id }); + setOpenSidebar(false); + setDialogWindow("clear-single-chat"); + } return ( - -
+ {chat.isShared ? ( )} -
- -
setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} > - - {chat.title} - -
- -
{children}
-
+ +
+ + {convertTitleCaseToSentenceCase(chat.title)} + +
+
+ + + + ); } From 7eaaaf0b32be03dd73162490e3f4c867b8bb43e8 Mon Sep 17 00:00:00 2001 From: Tom Wise <79859203+tomwisecodes@users.noreply.github.com> Date: Thu, 21 Nov 2024 10:30:41 +0000 Subject: [PATCH 06/25] chore: add top files --- .../AppComponents/Chat/sidebar-items.tsx | 72 +++++++++++++++---- .../AppComponents/Chat/sidebar-list.tsx | 2 +- 2 files changed, 60 insertions(+), 14 deletions(-) diff --git a/apps/nextjs/src/components/AppComponents/Chat/sidebar-items.tsx b/apps/nextjs/src/components/AppComponents/Chat/sidebar-items.tsx index ff3503223..e2441f9a9 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/sidebar-items.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/sidebar-items.tsx @@ -1,5 +1,7 @@ "use client"; +import { OakBox, OakFlex, OakP } from "@oaknational/oak-components"; +import { format, isToday, isThisWeek, isThisMonth } from "date-fns"; import { AnimatePresence, motion } from "framer-motion"; import { SidebarItem } from "@/components/AppComponents/Chat/sidebar-item"; @@ -12,21 +14,65 @@ interface SidebarItemsProps { export function SidebarItems({ chats }: Readonly) { if (!chats.length) return null; - return ( - - {chats.map((chat) => { - return ( - + const parsedChats = chats + .map((chat) => ({ + ...chat, + updatedAt: new Date(chat.updatedAt), + })) + .sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()); + + const todayChats = parsedChats.filter((chat) => isToday(chat.updatedAt)); + const lastWeekChats = parsedChats.filter( + (chat) => + isThisWeek(chat.updatedAt, { weekStartsOn: 1 }) && + !isToday(chat.updatedAt), + ); + const lastMonthChats = parsedChats.filter( + (chat) => + isThisMonth(chat.updatedAt) && + !isThisWeek(chat.updatedAt, { weekStartsOn: 1 }), + ); + + const olderChatsByMonth = parsedChats.reduce< + Record + >((acc, chat) => { + if (!isThisMonth(chat.updatedAt)) { + const monthYear = format(chat.updatedAt, "MMMM yyyy"); + if (!acc[monthYear]) { + acc[monthYear] = []; + } + acc[monthYear]?.push(chat); + } + return acc; + }, {}); + + const renderChats = (title: string, chats: SideBarChatItem[]) => ( + + + {title} + + + {chats.map((chat) => ( + - ); - })} + ))} + + + ); + + return ( + + {todayChats.length > 0 && renderChats("Today", todayChats)} + {lastWeekChats.length > 0 && renderChats("Last week", lastWeekChats)} + {lastMonthChats.length > 0 && renderChats("Last 30 days", lastMonthChats)} + {Object.entries(olderChatsByMonth).map(([monthYear, chats]) => + renderChats(monthYear, chats), + )} ); } diff --git a/apps/nextjs/src/components/AppComponents/Chat/sidebar-list.tsx b/apps/nextjs/src/components/AppComponents/Chat/sidebar-list.tsx index d68c6860a..70bf7bc42 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/sidebar-list.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/sidebar-list.tsx @@ -12,7 +12,7 @@ export function SidebarList() { return (
-
+

Lesson history

From 9a17719e0c5508efc1fedae404a0bb7fd86b3931 Mon Sep 17 00:00:00 2001 From: Tom Wise <79859203+tomwisecodes@users.noreply.github.com> Date: Thu, 21 Nov 2024 10:35:40 +0000 Subject: [PATCH 07/25] chore: add top files --- .../AppComponents/Chat/sidebar-mobile.tsx | 56 +++++++------------ 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/apps/nextjs/src/components/AppComponents/Chat/sidebar-mobile.tsx b/apps/nextjs/src/components/AppComponents/Chat/sidebar-mobile.tsx index 5e3b9c1de..d53e49cb8 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/sidebar-mobile.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/sidebar-mobile.tsx @@ -2,47 +2,33 @@ import { OakFlex, OakSpan } from "@oaknational/oak-components"; -import { Sidebar } from "@/components/AppComponents/Chat/sidebar"; import { Button } from "@/components/AppComponents/Chat/ui/button"; -import { - Sheet, - SheetContent, - SheetTrigger, -} from "@/components/AppComponents/Chat/ui/sheet"; import { Icon } from "@/components/Icon"; import useAnalytics from "@/lib/analytics/useAnalytics"; -interface SidebarMobileProps { - children: React.ReactNode; -} - -export function SidebarMobile({ children }: Readonly) { +export function SidebarMobile({ + setOpenSidebar, +}: { + setOpenSidebar: (value: boolean) => void; +}) { const { trackEvent } = useAnalytics(); + return ( - - - - - - {children} - - + Toggle Sidebar + ); } From 45301fc1908423391796b7ac6a707db7857f782a Mon Sep 17 00:00:00 2001 From: Tom Wise <79859203+tomwisecodes@users.noreply.github.com> Date: Thu, 21 Nov 2024 10:39:48 +0000 Subject: [PATCH 08/25] chore: add top files --- .../AppComponents/DialogContext/index.tsx | 17 +++- .../components/AppComponents/Layout/index.tsx | 20 +++-- apps/nextjs/src/components/BinIcon.tsx | 18 ++++ .../ContentOptions/ClearChatHistory.tsx | 71 +++++++++++++++ .../ClearSingleChatFromChatHistory.tsx | 89 +++++++++++++++++++ 5 files changed, 204 insertions(+), 11 deletions(-) create mode 100644 apps/nextjs/src/components/BinIcon.tsx create mode 100644 apps/nextjs/src/components/DialogControl/ContentOptions/ClearChatHistory.tsx create mode 100644 apps/nextjs/src/components/DialogControl/ContentOptions/ClearSingleChatFromChatHistory.tsx diff --git a/apps/nextjs/src/components/AppComponents/DialogContext/index.tsx b/apps/nextjs/src/components/AppComponents/DialogContext/index.tsx index d59b8ae55..5d054bea8 100644 --- a/apps/nextjs/src/components/AppComponents/DialogContext/index.tsx +++ b/apps/nextjs/src/components/AppComponents/DialogContext/index.tsx @@ -7,6 +7,10 @@ import type { DialogTypes } from "../Chat/Chat/types"; interface DialogContextType { dialogWindow: DialogTypes; setDialogWindow: React.Dispatch>; + dialogProps?: Record; + setDialogProps: React.Dispatch>>; + openSidebar: boolean; + setOpenSidebar: React.Dispatch>; } const DialogContext = createContext(undefined); @@ -15,10 +19,19 @@ export const DialogProvider: React.FC = ({ children, }) => { const [dialogWindow, setDialogWindow] = useState(""); + const [dialogProps, setDialogProps] = useState>({}); + const [openSidebar, setOpenSidebar] = useState(false); const value = useMemo( - () => ({ dialogWindow, setDialogWindow }), - [dialogWindow], + () => ({ + dialogWindow, + setDialogWindow, + dialogProps, + setDialogProps, + openSidebar, + setOpenSidebar, + }), + [dialogWindow, dialogProps, openSidebar], ); return ( diff --git a/apps/nextjs/src/components/AppComponents/Layout/index.tsx b/apps/nextjs/src/components/AppComponents/Layout/index.tsx index 805022741..d753b033d 100644 --- a/apps/nextjs/src/components/AppComponents/Layout/index.tsx +++ b/apps/nextjs/src/components/AppComponents/Layout/index.tsx @@ -15,15 +15,17 @@ const Layout = ({ }) => { const isDemoUser = useDemoUser().isDemoUser; return ( -
-
-
- {children} -
- {includeFooter &&
} -
+ +
+
+
+ {children} +
+ {includeFooter &&
} +
+
); }; diff --git a/apps/nextjs/src/components/BinIcon.tsx b/apps/nextjs/src/components/BinIcon.tsx new file mode 100644 index 000000000..9497ee5e0 --- /dev/null +++ b/apps/nextjs/src/components/BinIcon.tsx @@ -0,0 +1,18 @@ +const BinIcon = () => { + return ( + + + + ); +}; + +export default BinIcon; diff --git a/apps/nextjs/src/components/DialogControl/ContentOptions/ClearChatHistory.tsx b/apps/nextjs/src/components/DialogControl/ContentOptions/ClearChatHistory.tsx new file mode 100644 index 000000000..f6c7db83b --- /dev/null +++ b/apps/nextjs/src/components/DialogControl/ContentOptions/ClearChatHistory.tsx @@ -0,0 +1,71 @@ +"use client"; + +import { useTransition } from "react"; +import { toast } from "react-hot-toast"; + +import { + OakFlex, + OakP, + OakSmallPrimaryButton, + OakSmallSecondaryButton, +} from "@oaknational/oak-components"; +import { useRouter } from "next/navigation"; + +import LoadingWheel from "@/components/LoadingWheel"; +import { trpc } from "@/utils/trpc"; + +const ClearChatHistory = ({ closeDialog }: { closeDialog: () => void }) => { + const router = useRouter(); + const clearChats = trpc.chat.appSessions.deleteAllChats.useMutation({ + onSuccess() { + toast.success("Chat history cleared"); + closeDialog(); + router.push("/aila"); + }, + onError() { + toast.error("Failed to clear chat history"); + }, + }).mutate; + + const [isPending, startTransition] = useTransition(); + + return ( + + Are you absolutely sure? + + This will permanently delete your chat history and remove your data from + our servers. + + + closeDialog()} + > + Cancel + + { + event.preventDefault(); + startTransition(() => { + clearChats(); + }); + }} + > + {isPending && ( + + + + )} + Delete + + + + ); +}; + +export default ClearChatHistory; diff --git a/apps/nextjs/src/components/DialogControl/ContentOptions/ClearSingleChatFromChatHistory.tsx b/apps/nextjs/src/components/DialogControl/ContentOptions/ClearSingleChatFromChatHistory.tsx new file mode 100644 index 000000000..2007bc3f5 --- /dev/null +++ b/apps/nextjs/src/components/DialogControl/ContentOptions/ClearSingleChatFromChatHistory.tsx @@ -0,0 +1,89 @@ +"use client"; + +import { useTransition } from "react"; + +import { + OakFlex, + OakP, + OakSmallPrimaryButton, + OakSmallSecondaryButton, +} from "@oaknational/oak-components"; +import * as Sentry from "@sentry/nextjs"; + +import { useDialog } from "@/components/AppComponents/DialogContext"; +import LoadingWheel from "@/components/LoadingWheel"; +import { trpc } from "@/utils/trpc"; + +const ClearSingleChatFromChatHistory = ({ + closeDialog, +}: { + closeDialog: () => void; +}) => { + const { dialogProps, setDialogProps } = useDialog(); + const { mutate, isLoading } = + trpc.chat.appSessions.deleteChatById.useMutation({ + onSuccess() { + closeDialog(); + setDialogProps({}); + }, + onError() { + Sentry.captureMessage("Error deleting chat"); + }, + }); + + function deleteChatById() { + const id = dialogProps?.chatIdToDelete; + if (typeof id !== "string") { + return; + } + mutate({ id: id }); + } + + const [isPending, startTransition] = useTransition(); + + return ( + + Are you absolutely sure? + + This will permanently delete your chat history and remove your data from + our servers. + + {isLoading ? ( + + + + ) : ( + + closeDialog()} + > + Cancel + + { + event.preventDefault(); + startTransition(() => { + deleteChatById(); + }); + }} + > + {isPending && ( + + + + )} + Delete + + + )} + + ); +}; + +export default ClearSingleChatFromChatHistory; From e57035134e055224164393311960833d37a1049a Mon Sep 17 00:00:00 2001 From: Tom Wise <79859203+tomwisecodes@users.noreply.github.com> Date: Thu, 21 Nov 2024 10:43:48 +0000 Subject: [PATCH 09/25] chore: add top files --- .../DialogControl/DialogContents.tsx | 118 ++++++++++++------ .../components/DialogControl/dialogStyles.ts | 2 +- .../src/components/OakBoxWithCustomWidth.tsx | 8 ++ apps/nextjs/src/lib/types.ts | 3 +- packages/api/src/router/appSessions.ts | 6 + 5 files changed, 98 insertions(+), 39 deletions(-) create mode 100644 apps/nextjs/src/components/OakBoxWithCustomWidth.tsx diff --git a/apps/nextjs/src/components/DialogControl/DialogContents.tsx b/apps/nextjs/src/components/DialogControl/DialogContents.tsx index 0115ec7a1..d15293647 100644 --- a/apps/nextjs/src/components/DialogControl/DialogContents.tsx +++ b/apps/nextjs/src/components/DialogControl/DialogContents.tsx @@ -1,13 +1,20 @@ +import { useState } from "react"; + import type { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; import { + OakModal, OakModalCenter, OakModalCenterBody, type OakIconName, } from "@oaknational/oak-components"; import type { Message } from "ai"; +import styled from "styled-components"; import type { DialogTypes } from "../AppComponents/Chat/Chat/types"; +import { ChatHistory } from "../AppComponents/Chat/chat-history"; import { useDialog } from "../AppComponents/DialogContext"; +import ClearChatHistory from "./ContentOptions/ClearChatHistory"; +import ClearSingleChatFromChatHistory from "./ContentOptions/ClearSingleChatFromChatHistory"; import DemoInterstitialDialog from "./ContentOptions/DemoInterstitialDialog"; import DemoShareLockedDialog from "./ContentOptions/DemoShareLockedDialog"; import EndOfLessonFeedback from "./ContentOptions/EndOfLessonFeedback"; @@ -16,7 +23,7 @@ import ShareChatDialog from "./ContentOptions/ShareChatDialog"; const dialogTitlesAndIcons: Record< Exclude, - { title: string; iconName: OakIconName } + { title: string; iconName: OakIconName | null } > = { "share-chat": { title: "Share lesson", @@ -42,8 +49,20 @@ const dialogTitlesAndIcons: Record< title: "Sharing and downloading", iconName: "warning", }, + "clear-history": { + title: "Clear chat history", + iconName: null, + }, + "clear-single-chat": { + title: "Clear chat", + iconName: null, + }, }; +const OakModalAtTheFront = styled(OakModalCenter)` + z-index: 100000; +`; + const DialogContents = ({ chatId, lesson, @@ -59,43 +78,68 @@ const DialogContents = ({ submit?: () => void; isShared?: boolean | undefined; }) => { - const { dialogWindow, setDialogWindow } = useDialog(); - const closeDialog = () => setDialogWindow(""); - if (dialogWindow === "") return null; + const { + dialogWindow, + setDialogWindow, + setDialogProps, + openSidebar, + setOpenSidebar, + } = useDialog(); + const closeDialog = () => { + setDialogWindow(""); + setDialogProps({}); + }; + + if (dialogWindow === "" && !openSidebar) return null; + return ( - - - {children} - {dialogWindow === "share-chat" && chatId && ( - - )} - {dialogWindow === "report-content" && ( - - )} - {dialogWindow === "demo-share-locked" && ( - - )} - {dialogWindow === "demo-interstitial" && ( - - )} - {dialogWindow === "feedback" && ( - - )} - - + <> + + {dialogWindow !== "" && ( + + + {children} + {dialogWindow === "share-chat" && chatId && ( + + )} + {dialogWindow === "report-content" && ( + + )} + {dialogWindow === "demo-share-locked" && ( + + )} + {dialogWindow === "demo-interstitial" && ( + + )} + {dialogWindow === "feedback" && ( + + )} + {dialogWindow === "clear-history" && ( + + )} + {dialogWindow === "clear-single-chat" && ( + + )} + + + )} + ); }; export default DialogContents; diff --git a/apps/nextjs/src/components/DialogControl/dialogStyles.ts b/apps/nextjs/src/components/DialogControl/dialogStyles.ts index ef9c915d1..e5f122ba6 100644 --- a/apps/nextjs/src/components/DialogControl/dialogStyles.ts +++ b/apps/nextjs/src/components/DialogControl/dialogStyles.ts @@ -13,7 +13,7 @@ export const dialogContentInner = cva([ ]); export const dialogOverlay = cva( - ["fixed inset-0 z-40 flex items-center justify-center duration-300 "], + ["fixed inset-0 z-50 flex items-center justify-center duration-300 "], { variants: { opacity: { diff --git a/apps/nextjs/src/components/OakBoxWithCustomWidth.tsx b/apps/nextjs/src/components/OakBoxWithCustomWidth.tsx new file mode 100644 index 000000000..1fbda4b18 --- /dev/null +++ b/apps/nextjs/src/components/OakBoxWithCustomWidth.tsx @@ -0,0 +1,8 @@ +import { OakBox } from "@oaknational/oak-components"; +import styled from "styled-components"; + +const OakBoxWithCustomWidth = styled(OakBox)<{ width: number }>` + width: ${({ width }) => width}px; +`; + +export default OakBoxWithCustomWidth; diff --git a/apps/nextjs/src/lib/types.ts b/apps/nextjs/src/lib/types.ts index 53d776992..aeeb63917 100644 --- a/apps/nextjs/src/lib/types.ts +++ b/apps/nextjs/src/lib/types.ts @@ -1,10 +1,11 @@ -import { z } from "zod"; +import { date, z } from "zod"; export const sideBarChatItemSchema = z .object({ id: z.string(), title: z.string(), isShared: z.boolean().nullish(), + updatedAt: date(), }) .passthrough(); diff --git a/packages/api/src/router/appSessions.ts b/packages/api/src/router/appSessions.ts index b01c7bccc..1d5b8f59c 100644 --- a/packages/api/src/router/appSessions.ts +++ b/packages/api/src/router/appSessions.ts @@ -199,6 +199,7 @@ export const appSessionsRouter = router({ const sessions = await ctx.prisma.$queryRaw` SELECT id, + "updated_at" as "updatedAt", output->>'title' as title, output->>'isShared' as "isShared" FROM @@ -224,6 +225,11 @@ export const appSessionsRouter = router({ }) .parse(session); } catch (error) { + Sentry.captureException(error, { + extra: { + session, + }, + }); return null; } }) From be9c5171e8b302fe15fedfa728cb8f83c4315ed3 Mon Sep 17 00:00:00 2001 From: Tom Wise <79859203+tomwisecodes@users.noreply.github.com> Date: Thu, 21 Nov 2024 10:51:18 +0000 Subject: [PATCH 10/25] chore: update the date --- packages/api/src/router/appSessions.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/api/src/router/appSessions.ts b/packages/api/src/router/appSessions.ts index 1d5b8f59c..8f910370e 100644 --- a/packages/api/src/router/appSessions.ts +++ b/packages/api/src/router/appSessions.ts @@ -222,6 +222,7 @@ export const appSessionsRouter = router({ id: z.string(), title: z.string(), isShared: z.boolean().nullish(), + updatedAt: z.date(), }) .parse(session); } catch (error) { From af1e19e2d74699c346132311eafa393bc045c275 Mon Sep 17 00:00:00 2001 From: Tom Wise <79859203+tomwisecodes@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:06:50 +0000 Subject: [PATCH 11/25] chore: update test --- apps/nextjs/tests-e2e/tests/sidebar.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/nextjs/tests-e2e/tests/sidebar.test.ts b/apps/nextjs/tests-e2e/tests/sidebar.test.ts index 41bde008b..f0ad273cd 100644 --- a/apps/nextjs/tests-e2e/tests/sidebar.test.ts +++ b/apps/nextjs/tests-e2e/tests/sidebar.test.ts @@ -17,7 +17,7 @@ test( await test.step("Select a lesson", async () => { await page.getByTestId("sidebar-button").click(); const sidebar = page.getByTestId("sidebar"); - expect(sidebar).toBeVisible(); + await expect(sidebar).toBeVisible(); await sidebar.getByText("Software Testing Techniques").click(); }); From a74693b7e6d8aca628171963da7b8eefb9b763ae Mon Sep 17 00:00:00 2001 From: Tom Wise <79859203+tomwisecodes@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:43:08 +0000 Subject: [PATCH 12/25] chore: fix story --- .../src/components/DialogControl/DialogContents.stories.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/nextjs/src/components/DialogControl/DialogContents.stories.tsx b/apps/nextjs/src/components/DialogControl/DialogContents.stories.tsx index 9f9d88ae5..77e245f78 100644 --- a/apps/nextjs/src/components/DialogControl/DialogContents.stories.tsx +++ b/apps/nextjs/src/components/DialogControl/DialogContents.stories.tsx @@ -17,6 +17,10 @@ const meta: Meta = { value={{ dialogWindow: parameters.dialogWindow, setDialogWindow: () => {}, + dialogProps: {}, + setDialogProps: () => {}, + openSidebar: false, + setOpenSidebar: () => {}, }} > From 1718dd2e43c883f4a8150f2868ac05d7d9ee6867 Mon Sep 17 00:00:00 2001 From: Tom Wise <79859203+tomwisecodes@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:58:11 +0000 Subject: [PATCH 13/25] chore: playwright fix --- apps/nextjs/src/components/AppComponents/Chat/chat-history.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-history.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-history.tsx index 42647ab80..a11736c84 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-history.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-history.tsx @@ -21,6 +21,7 @@ export function ChatHistory({ return ( setOpenSidebar(false)} From 7af730f0035f4bea1ca5884336f9acf34b7bbb17 Mon Sep 17 00:00:00 2001 From: Tom Wise <79859203+tomwisecodes@users.noreply.github.com> Date: Thu, 21 Nov 2024 12:18:48 +0000 Subject: [PATCH 14/25] feat: oak modal on the right hand side --- apps/nextjs/package.json | 2 +- apps/nextjs/src/app/page.tsx | 1 - .../AppComponents/Chat/chat-history.tsx | 12 ++++------ .../components/AppComponents/Chat/header.tsx | 6 +++-- ...ar-mobile.tsx => open-side-bar-button.tsx} | 2 +- .../AppComponents/Chat/sidebar-items.tsx | 4 ++-- .../AppComponents/Chat/sidebar-list.tsx | 4 ---- .../components/AppComponents/Chat/sidebar.tsx | 22 ------------------- .../DialogControl/DialogContents.tsx | 10 ++------- pnpm-lock.yaml | 8 +++---- 10 files changed, 18 insertions(+), 53 deletions(-) rename apps/nextjs/src/components/AppComponents/Chat/{sidebar-mobile.tsx => open-side-bar-button.tsx} (95%) delete mode 100644 apps/nextjs/src/components/AppComponents/Chat/sidebar.tsx diff --git a/apps/nextjs/package.json b/apps/nextjs/package.json index 413ccbc3b..136f481e7 100644 --- a/apps/nextjs/package.json +++ b/apps/nextjs/package.json @@ -45,7 +45,7 @@ "@oakai/exports": "*", "@oakai/logger": "*", "@oakai/prettier-config": "*", - "@oaknational/oak-components": "^1.44.0", + "@oaknational/oak-components": "^1.50.0", "@oaknational/oak-consent-client": "^2.1.0", "@portabletext/react": "^3.1.0", "@prisma/client": "5.16.1", diff --git a/apps/nextjs/src/app/page.tsx b/apps/nextjs/src/app/page.tsx index b9d5048d0..ad17c708f 100644 --- a/apps/nextjs/src/app/page.tsx +++ b/apps/nextjs/src/app/page.tsx @@ -4,6 +4,5 @@ import HomePage from "./home-page"; export default async function Page() { const result = await fetchAiHomepage(); - console.log("test"); return ; } diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-history.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-history.tsx index a11736c84..4559ca03f 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-history.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-history.tsx @@ -7,21 +7,17 @@ import { usePathname } from "next/navigation"; import { SidebarList } from "@/components/AppComponents/Chat/sidebar-list"; +import { useDialog } from "../DialogContext"; import { ClearHistory } from "./clear-history"; import ChatButton from "./ui/chat-button"; -export function ChatHistory({ - openSidebar, - setOpenSidebar, -}: { - openSidebar: boolean; - setOpenSidebar: (value: boolean) => void; -}) { +export function ChatHistory() { const ailaId = usePathname().split("aila/")[1]; - + const { openSidebar, setOpenSidebar } = useDialog(); return ( setOpenSidebar(false)} diff --git a/apps/nextjs/src/components/AppComponents/Chat/header.tsx b/apps/nextjs/src/components/AppComponents/Chat/header.tsx index 39b4efa8c..24ee90044 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/header.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/header.tsx @@ -17,7 +17,8 @@ import OakIconLogo from "@/components/OakIconLogo"; import { useDialog } from "../DialogContext"; import { BetaTagHeader } from "./beta-tag"; -import { SidebarMobile } from "./sidebar-mobile"; +import { ChatHistory } from "./chat-history"; +import { OpenSideBarButton } from "./open-side-bar-button"; import { UserOrLogin } from "./user-or-login"; export function Header() { @@ -122,7 +123,8 @@ export function Header() { - + + diff --git a/apps/nextjs/src/components/AppComponents/Chat/sidebar-mobile.tsx b/apps/nextjs/src/components/AppComponents/Chat/open-side-bar-button.tsx similarity index 95% rename from apps/nextjs/src/components/AppComponents/Chat/sidebar-mobile.tsx rename to apps/nextjs/src/components/AppComponents/Chat/open-side-bar-button.tsx index d53e49cb8..8835574dd 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/sidebar-mobile.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/open-side-bar-button.tsx @@ -6,7 +6,7 @@ import { Button } from "@/components/AppComponents/Chat/ui/button"; import { Icon } from "@/components/Icon"; import useAnalytics from "@/lib/analytics/useAnalytics"; -export function SidebarMobile({ +export function OpenSideBarButton({ setOpenSidebar, }: { setOpenSidebar: (value: boolean) => void; diff --git a/apps/nextjs/src/components/AppComponents/Chat/sidebar-items.tsx b/apps/nextjs/src/components/AppComponents/Chat/sidebar-items.tsx index e2441f9a9..aa2a860fd 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/sidebar-items.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/sidebar-items.tsx @@ -13,8 +13,8 @@ interface SidebarItemsProps { export function SidebarItems({ chats }: Readonly) { if (!chats.length) return null; - - const parsedChats = chats + const filteredEmptyTitles = chats.filter((chat) => chat.title !== ""); + const parsedChats = filteredEmptyTitles .map((chat) => ({ ...chat, updatedAt: new Date(chat.updatedAt), diff --git a/apps/nextjs/src/components/AppComponents/Chat/sidebar-list.tsx b/apps/nextjs/src/components/AppComponents/Chat/sidebar-list.tsx index 70bf7bc42..d731433e6 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/sidebar-list.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/sidebar-list.tsx @@ -1,6 +1,5 @@ "use client"; -import { ClearHistory } from "@/components/AppComponents/Chat/clear-history"; import { SidebarItems } from "@/components/AppComponents/Chat/sidebar-items"; import { trpc } from "@/utils/trpc"; @@ -19,9 +18,6 @@ export function SidebarList() { {chats?.length ? : null}
-
- 0 : false} /> -
); } diff --git a/apps/nextjs/src/components/AppComponents/Chat/sidebar.tsx b/apps/nextjs/src/components/AppComponents/Chat/sidebar.tsx deleted file mode 100644 index ab4f4ab23..000000000 --- a/apps/nextjs/src/components/AppComponents/Chat/sidebar.tsx +++ /dev/null @@ -1,22 +0,0 @@ -"use client"; - -import * as React from "react"; - -import { useSidebar } from "@/lib/hooks/use-sidebar"; -import { cn } from "@/lib/utils"; - -interface SidebarProps extends React.ComponentProps<"div"> {} - -export function Sidebar({ className, children }: SidebarProps) { - const { isSidebarOpen, isLoading } = useSidebar(); - - return ( -
- {children} -
- ); -} diff --git a/apps/nextjs/src/components/DialogControl/DialogContents.tsx b/apps/nextjs/src/components/DialogControl/DialogContents.tsx index fca9459d4..2e89b03de 100644 --- a/apps/nextjs/src/components/DialogControl/DialogContents.tsx +++ b/apps/nextjs/src/components/DialogControl/DialogContents.tsx @@ -78,13 +78,8 @@ const DialogContents = ({ submit?: () => void; isShared?: boolean | undefined; }) => { - const { - dialogWindow, - setDialogWindow, - setDialogProps, - openSidebar, - setOpenSidebar, - } = useDialog(); + const { dialogWindow, setDialogWindow, setDialogProps, openSidebar } = + useDialog(); const closeDialog = () => { setDialogWindow(""); setDialogProps({}); @@ -94,7 +89,6 @@ const DialogContents = ({ return ( <> - {dialogWindow !== "" && ( Date: Thu, 21 Nov 2024 13:51:35 +0000 Subject: [PATCH 15/25] chore: comments --- .../Chat/chat-start-accordion.tsx | 6 +--- .../Chat/render-chat-history.tsx | 23 +++++++++++++ .../AppComponents/Chat/sidebar-item.tsx | 2 +- .../AppComponents/Chat/sidebar-items.tsx | 33 ++++--------------- .../utils/convertTitleCaseToSentenceCase.ts | 4 +++ 5 files changed, 36 insertions(+), 32 deletions(-) create mode 100644 apps/nextjs/src/components/AppComponents/Chat/render-chat-history.tsx create mode 100644 apps/nextjs/src/utils/convertTitleCaseToSentenceCase.ts diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-start-accordion.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-start-accordion.tsx index 43af4bd6d..d1fdcca50 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-start-accordion.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-start-accordion.tsx @@ -10,6 +10,7 @@ import AiIcon from "@/components/SVGParts/AiIcon"; import LessonIcon from "@/components/SVGParts/LessonIcon"; import QuizIcon from "@/components/SVGParts/QuizIcon"; import SlidesIcon from "@/components/SVGParts/SlidesIcon"; +import { convertTitleCaseToSentenceCase } from "@/utils/convertTitleCaseToSentenceCase"; import { handleRewordingSections } from "./export-buttons"; @@ -194,8 +195,3 @@ const AccordionContent = React.forwardRef< AccordionContent.displayName = "AccordionContent"; export default ChatStartAccordion; - -export function convertTitleCaseToSentenceCase(titleCase: string) { - const lowerCaseTitle = titleCase.toLowerCase(); - return lowerCaseTitle.charAt(0).toUpperCase() + lowerCaseTitle.slice(1); -} diff --git a/apps/nextjs/src/components/AppComponents/Chat/render-chat-history.tsx b/apps/nextjs/src/components/AppComponents/Chat/render-chat-history.tsx new file mode 100644 index 000000000..115c8fb61 --- /dev/null +++ b/apps/nextjs/src/components/AppComponents/Chat/render-chat-history.tsx @@ -0,0 +1,23 @@ +import { OakBox, OakFlex, OakP } from "@oaknational/oak-components"; +import { motion } from "framer-motion"; + +import type { SideBarChatItem } from "@/lib/types"; + +import { SidebarItem } from "./sidebar-item"; + +const RenderChats = (title: string, chats: SideBarChatItem[]) => ( + + + {title} + + + {chats.map((chat) => ( + + + + ))} + + +); + +export default RenderChats; diff --git a/apps/nextjs/src/components/AppComponents/Chat/sidebar-item.tsx b/apps/nextjs/src/components/AppComponents/Chat/sidebar-item.tsx index 22ac37fcd..671698ca1 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/sidebar-item.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/sidebar-item.tsx @@ -15,10 +15,10 @@ import { } from "@/components/AppComponents/Chat/ui/tooltip"; import BinIcon from "@/components/BinIcon"; import type { SideBarChatItem } from "@/lib/types"; +import { convertTitleCaseToSentenceCase } from "@/utils/convertTitleCaseToSentenceCase"; import { useDialog } from "../DialogContext"; import { constructChatPath } from "./Chat/utils"; -import { convertTitleCaseToSentenceCase } from "./chat-start-accordion"; interface SidebarItemProps { chat: SideBarChatItem; diff --git a/apps/nextjs/src/components/AppComponents/Chat/sidebar-items.tsx b/apps/nextjs/src/components/AppComponents/Chat/sidebar-items.tsx index aa2a860fd..679d327e9 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/sidebar-items.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/sidebar-items.tsx @@ -1,12 +1,12 @@ "use client"; -import { OakBox, OakFlex, OakP } from "@oaknational/oak-components"; import { format, isToday, isThisWeek, isThisMonth } from "date-fns"; -import { AnimatePresence, motion } from "framer-motion"; +import { AnimatePresence } from "framer-motion"; -import { SidebarItem } from "@/components/AppComponents/Chat/sidebar-item"; import type { SideBarChatItem } from "@/lib/types"; +import RenderChats from "./render-chat-history"; + interface SidebarItemsProps { chats: SideBarChatItem[]; } @@ -46,32 +46,13 @@ export function SidebarItems({ chats }: Readonly) { return acc; }, {}); - const renderChats = (title: string, chats: SideBarChatItem[]) => ( - - - {title} - - - {chats.map((chat) => ( - - - - ))} - - - ); - return ( - {todayChats.length > 0 && renderChats("Today", todayChats)} - {lastWeekChats.length > 0 && renderChats("Last week", lastWeekChats)} - {lastMonthChats.length > 0 && renderChats("Last 30 days", lastMonthChats)} + {todayChats.length > 0 && RenderChats("Today", todayChats)} + {lastWeekChats.length > 0 && RenderChats("Last week", lastWeekChats)} + {lastMonthChats.length > 0 && RenderChats("Last 30 days", lastMonthChats)} {Object.entries(olderChatsByMonth).map(([monthYear, chats]) => - renderChats(monthYear, chats), + RenderChats(monthYear, chats), )} ); diff --git a/apps/nextjs/src/utils/convertTitleCaseToSentenceCase.ts b/apps/nextjs/src/utils/convertTitleCaseToSentenceCase.ts new file mode 100644 index 000000000..c29b0c29e --- /dev/null +++ b/apps/nextjs/src/utils/convertTitleCaseToSentenceCase.ts @@ -0,0 +1,4 @@ +export function convertTitleCaseToSentenceCase(titleCase: string) { + const lowerCaseTitle = titleCase.toLowerCase(); + return lowerCaseTitle.charAt(0).toUpperCase() + lowerCaseTitle.slice(1); +} From 09c4ba93e25314f2f829959dc53363e39ced3a51 Mon Sep 17 00:00:00 2001 From: Tom Wise <79859203+tomwisecodes@users.noreply.github.com> Date: Mon, 25 Nov 2024 11:16:35 +0000 Subject: [PATCH 16/25] chore: hide gleap --- .../AppComponents/Chat/chat-history.tsx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-history.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-history.tsx index 4559ca03f..f4674d2ee 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-history.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-history.tsx @@ -1,6 +1,7 @@ "use client"; import * as React from "react"; +import { useEffect } from "react"; import { OakIcon, OakModal, OakModalFooter } from "@oaknational/oak-components"; import { usePathname } from "next/navigation"; @@ -14,6 +15,22 @@ import ChatButton from "./ui/chat-button"; export function ChatHistory() { const ailaId = usePathname().split("aila/")[1]; const { openSidebar, setOpenSidebar } = useDialog(); + + useEffect(() => { + if (openSidebar) { + const style = document.createElement("style"); + style.innerHTML = ` + .bb-feedback-button.gleap-font.gl-block { + display: none !important; + } + `; + document.head.appendChild(style); + + return () => { + document.head.removeChild(style); + }; + } + }, [openSidebar]); return ( Date: Mon, 25 Nov 2024 11:17:40 +0000 Subject: [PATCH 17/25] chore: remove sentence case --- .../src/components/AppComponents/Chat/sidebar-item.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/nextjs/src/components/AppComponents/Chat/sidebar-item.tsx b/apps/nextjs/src/components/AppComponents/Chat/sidebar-item.tsx index 671698ca1..3d5561503 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/sidebar-item.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/sidebar-item.tsx @@ -15,7 +15,6 @@ import { } from "@/components/AppComponents/Chat/ui/tooltip"; import BinIcon from "@/components/BinIcon"; import type { SideBarChatItem } from "@/lib/types"; -import { convertTitleCaseToSentenceCase } from "@/utils/convertTitleCaseToSentenceCase"; import { useDialog } from "../DialogContext"; import { constructChatPath } from "./Chat/utils"; @@ -68,9 +67,7 @@ export function SidebarItem({ chat }: SidebarItemProps) { >
- - {convertTitleCaseToSentenceCase(chat.title)} - + {chat.title}
From 759700bacef5f79070fd406bfbd91c02b4b67567 Mon Sep 17 00:00:00 2001 From: Tom Wise <79859203+tomwisecodes@users.noreply.github.com> Date: Mon, 25 Nov 2024 11:18:51 +0000 Subject: [PATCH 18/25] chore: focus on open --- .../AppComponents/Chat/chat-history.tsx | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-history.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-history.tsx index f4674d2ee..6944600c8 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-history.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-history.tsx @@ -3,7 +3,14 @@ import * as React from "react"; import { useEffect } from "react"; -import { OakIcon, OakModal, OakModalFooter } from "@oaknational/oak-components"; +import { + OakBox, + OakIcon, + OakLink, + OakModal, + OakModalFooter, + OakSpan, +} from "@oaknational/oak-components"; import { usePathname } from "next/navigation"; import { SidebarList } from "@/components/AppComponents/Chat/sidebar-list"; @@ -44,6 +51,19 @@ export function ChatHistory() { } > + + setOpenSidebar(false)}> + + Close + + +
{}}> From 73aca9fa4ce4be2f3c3fa0826d78ed8bab12915c Mon Sep 17 00:00:00 2001 From: Tom Wise <79859203+tomwisecodes@users.noreply.github.com> Date: Mon, 25 Nov 2024 11:19:40 +0000 Subject: [PATCH 19/25] chore: clear single chat --- .../ClearSingleChatFromChatHistory.tsx | 72 +++++++++---------- 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/apps/nextjs/src/components/DialogControl/ContentOptions/ClearSingleChatFromChatHistory.tsx b/apps/nextjs/src/components/DialogControl/ContentOptions/ClearSingleChatFromChatHistory.tsx index 2007bc3f5..48d02c51c 100644 --- a/apps/nextjs/src/components/DialogControl/ContentOptions/ClearSingleChatFromChatHistory.tsx +++ b/apps/nextjs/src/components/DialogControl/ContentOptions/ClearSingleChatFromChatHistory.tsx @@ -1,25 +1,22 @@ "use client"; -import { useTransition } from "react"; +import { useCallback, useTransition } from "react"; -import { - OakFlex, - OakP, - OakSmallPrimaryButton, - OakSmallSecondaryButton, -} from "@oaknational/oak-components"; +import { OakFlex, OakP, OakPrimaryButton } from "@oaknational/oak-components"; import * as Sentry from "@sentry/nextjs"; import { useDialog } from "@/components/AppComponents/DialogContext"; import LoadingWheel from "@/components/LoadingWheel"; import { trpc } from "@/utils/trpc"; +import ModalFooterButtons from "./ModalFooterButtons"; + const ClearSingleChatFromChatHistory = ({ closeDialog, }: { closeDialog: () => void; }) => { - const { dialogProps, setDialogProps } = useDialog(); + const { dialogProps, setDialogProps, setOpenSidebar } = useDialog(); const { mutate, isLoading } = trpc.chat.appSessions.deleteChatById.useMutation({ onSuccess() { @@ -31,6 +28,11 @@ const ClearSingleChatFromChatHistory = ({ }, }); + const handleCloseDialog = useCallback(() => { + closeDialog(); + setOpenSidebar(true); + }, [closeDialog, setOpenSidebar]); + function deleteChatById() { const id = dialogProps?.chatIdToDelete; if (typeof id !== "string") { @@ -40,7 +42,26 @@ const ClearSingleChatFromChatHistory = ({ } const [isPending, startTransition] = useTransition(); - + const handleDeleteChat = () => { + return ( + { + event.preventDefault(); + startTransition(() => { + deleteChatById(); + }); + }} + > + {isPending && ( + + + + )} + Delete + + ); + }; return ( Are you absolutely sure? - - This will permanently delete your chat history and remove your data from - our servers. - + This will permanently delete this lesson. {isLoading ? ( ) : ( - - closeDialog()} - > - Cancel - - { - event.preventDefault(); - startTransition(() => { - deleteChatById(); - }); - }} - > - {isPending && ( - - - - )} - Delete - - + )} ); From 29e31302b4686b0503a8e70ac2deb29b480efa1a Mon Sep 17 00:00:00 2001 From: Tom Wise <79859203+tomwisecodes@users.noreply.github.com> Date: Mon, 25 Nov 2024 11:20:07 +0000 Subject: [PATCH 20/25] chore: clear chat history --- .../ContentOptions/ClearChatHistory.tsx | 73 ++++++++++--------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/apps/nextjs/src/components/DialogControl/ContentOptions/ClearChatHistory.tsx b/apps/nextjs/src/components/DialogControl/ContentOptions/ClearChatHistory.tsx index f6c7db83b..c7316d799 100644 --- a/apps/nextjs/src/components/DialogControl/ContentOptions/ClearChatHistory.tsx +++ b/apps/nextjs/src/components/DialogControl/ContentOptions/ClearChatHistory.tsx @@ -1,21 +1,26 @@ "use client"; -import { useTransition } from "react"; +import { useCallback, useTransition } from "react"; import { toast } from "react-hot-toast"; -import { - OakFlex, - OakP, - OakSmallPrimaryButton, - OakSmallSecondaryButton, -} from "@oaknational/oak-components"; +import { OakFlex, OakP, OakPrimaryButton } from "@oaknational/oak-components"; import { useRouter } from "next/navigation"; +import { useDialog } from "@/components/AppComponents/DialogContext"; import LoadingWheel from "@/components/LoadingWheel"; import { trpc } from "@/utils/trpc"; +import ModalFooterButtons from "./ModalFooterButtons"; + const ClearChatHistory = ({ closeDialog }: { closeDialog: () => void }) => { const router = useRouter(); + + const { setOpenSidebar } = useDialog(); + const handleCloseDialog = useCallback(() => { + closeDialog(); + setOpenSidebar(true); + }, [closeDialog, setOpenSidebar]); + const clearChats = trpc.chat.appSessions.deleteAllChats.useMutation({ onSuccess() { toast.success("Chat history cleared"); @@ -28,7 +33,26 @@ const ClearChatHistory = ({ closeDialog }: { closeDialog: () => void }) => { }).mutate; const [isPending, startTransition] = useTransition(); - + const handleDeleteChat = () => { + return ( + { + event.preventDefault(); + startTransition(() => { + clearChats(); + }); + }} + > + {isPending && ( + + + + )} + Delete + + ); + }; return ( void }) => { $gap="space-between-m" > Are you absolutely sure? - - This will permanently delete your chat history and remove your data from - our servers. - - - closeDialog()} - > - Cancel - - { - event.preventDefault(); - startTransition(() => { - clearChats(); - }); - }} - > - {isPending && ( - - - - )} - Delete - - + This will permanently delete all of your lesson history. + ); }; From 359f99a11ad539fcef3247c331d24b258dfb16b0 Mon Sep 17 00:00:00 2001 From: Tom Wise <79859203+tomwisecodes@users.noreply.github.com> Date: Tue, 26 Nov 2024 10:45:44 +0000 Subject: [PATCH 21/25] chore: change text --- .../DialogControl/ContentOptions/ClearChatHistory.tsx | 1 - .../ContentOptions/ClearSingleChatFromChatHistory.tsx | 1 - apps/nextjs/src/components/DialogControl/DialogContents.tsx | 4 ++-- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/nextjs/src/components/DialogControl/ContentOptions/ClearChatHistory.tsx b/apps/nextjs/src/components/DialogControl/ContentOptions/ClearChatHistory.tsx index c7316d799..57896bba3 100644 --- a/apps/nextjs/src/components/DialogControl/ContentOptions/ClearChatHistory.tsx +++ b/apps/nextjs/src/components/DialogControl/ContentOptions/ClearChatHistory.tsx @@ -59,7 +59,6 @@ const ClearChatHistory = ({ closeDialog }: { closeDialog: () => void }) => { $justifyContent="center" $gap="space-between-m" > - Are you absolutely sure? This will permanently delete all of your lesson history. - Are you absolutely sure? This will permanently delete this lesson. {isLoading ? ( diff --git a/apps/nextjs/src/components/DialogControl/DialogContents.tsx b/apps/nextjs/src/components/DialogControl/DialogContents.tsx index 2e89b03de..a91b69f9a 100644 --- a/apps/nextjs/src/components/DialogControl/DialogContents.tsx +++ b/apps/nextjs/src/components/DialogControl/DialogContents.tsx @@ -50,11 +50,11 @@ const dialogTitlesAndIcons: Record< iconName: "warning", }, "clear-history": { - title: "Clear chat history", + title: "Are you absolutely sure?", iconName: null, }, "clear-single-chat": { - title: "Clear chat", + title: "Are you absolutely sure?", iconName: null, }, }; From 2e83b249beab3b770dc6b37ec7a9262bdbf7f281 Mon Sep 17 00:00:00 2001 From: Tom Wise <79859203+tomwisecodes@users.noreply.github.com> Date: Tue, 26 Nov 2024 11:16:29 +0000 Subject: [PATCH 22/25] feat: add deleted at --- packages/api/src/router/appSessions.ts | 13 ++++++++++--- packages/db/prisma/schema.prisma | 5 +++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/api/src/router/appSessions.ts b/packages/api/src/router/appSessions.ts index 8f910370e..a47d3dbe3 100644 --- a/packages/api/src/router/appSessions.ts +++ b/packages/api/src/router/appSessions.ts @@ -80,6 +80,7 @@ export async function getChat(id: string, prisma: PrismaClientWithAccelerate) { const chatRecord = await prisma.appSession.findUnique({ where: { id: id, + deletedAt: null, }, }); if (!chatRecord) { @@ -206,6 +207,7 @@ export const appSessionsRouter = router({ "app_sessions" WHERE "user_id" = ${userId} AND "app_id" = 'lesson-planner' + AND "deleted_at" IS NULL ORDER BY "updated_at" DESC `; @@ -246,22 +248,27 @@ export const appSessionsRouter = router({ const { userId } = ctx.auth; const { id } = input; - await ctx.prisma.appSession.deleteMany({ + await ctx.prisma.appSession.update({ where: { id, appId: "lesson-planner", userId, }, + data: { + deletedAt: new Date(), + }, }); }), deleteAllChats: protectedProcedure.mutation(async ({ ctx }) => { const { userId } = ctx.auth; - - await ctx.prisma.appSession.deleteMany({ + await ctx.prisma.appSession.updateMany({ where: { userId, appId: "lesson-planner", }, + data: { + deletedAt: new Date(), + }, }); }), shareChat: protectedProcedure diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma index 7a9cc441a..ca65c4821 100644 --- a/packages/db/prisma/schema.prisma +++ b/packages/db/prisma/schema.prisma @@ -344,14 +344,15 @@ model AppSession { id String @id @default(cuid()) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") - + deletedAt DateTime? @map("deleted_at") + app App @relation(fields: [appId], references: [id], onDelete: Cascade) appId String @map("app_id") userId String @map("user_id") // The user that requested this generation output Json? // The final output of the session - + userTweaks UserTweak[] reGenerations ReGeneration[] generationUserFlags GenerationUserFlag[] From aaae68e873c381e82f2857c3d7323e024940c832 Mon Sep 17 00:00:00 2001 From: Tom Wise <79859203+tomwisecodes@users.noreply.github.com> Date: Tue, 26 Nov 2024 12:04:10 +0000 Subject: [PATCH 23/25] feat: stop get chat from finding deleted chats --- apps/nextjs/src/app/actions.ts | 2 +- .../aila/src/features/persistence/adaptors/prisma/index.ts | 1 + packages/api/src/router/appSessions.ts | 1 + packages/api/src/router/chats.ts | 3 ++- packages/core/src/models/apps.ts | 2 ++ 5 files changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/nextjs/src/app/actions.ts b/apps/nextjs/src/app/actions.ts index 7cc3bb9b5..398099f8d 100644 --- a/apps/nextjs/src/app/actions.ts +++ b/apps/nextjs/src/app/actions.ts @@ -43,7 +43,7 @@ export async function getChatById( id: string, ): Promise { const session = await prisma?.appSession.findUnique({ - where: { id }, + where: { id, deletedAt: null }, }); if (!session) { diff --git a/packages/aila/src/features/persistence/adaptors/prisma/index.ts b/packages/aila/src/features/persistence/adaptors/prisma/index.ts index de7c8cb17..f04fb1b65 100644 --- a/packages/aila/src/features/persistence/adaptors/prisma/index.ts +++ b/packages/aila/src/features/persistence/adaptors/prisma/index.ts @@ -39,6 +39,7 @@ export class AilaPrismaPersistence extends AilaPersistence { const appSession = await this._prisma.appSession.findFirst({ where: { id, + deletedAt: null, }, }); diff --git a/packages/api/src/router/appSessions.ts b/packages/api/src/router/appSessions.ts index a47d3dbe3..2d75c7ab8 100644 --- a/packages/api/src/router/appSessions.ts +++ b/packages/api/src/router/appSessions.ts @@ -286,6 +286,7 @@ export const appSessionsRouter = router({ id, userId, appId: "lesson-planner", + deletedAt: null, }, }); diff --git a/packages/api/src/router/chats.ts b/packages/api/src/router/chats.ts index 403dc29fb..c63158839 100644 --- a/packages/api/src/router/chats.ts +++ b/packages/api/src/router/chats.ts @@ -45,7 +45,7 @@ export const chatsRouter = router({ const { id } = input; const session = await prisma?.appSession.findUnique({ - where: { id }, + where: { id, deletedAt: null }, }); if (!session) { @@ -72,6 +72,7 @@ export const chatsRouter = router({ where: { userId, appId: "lesson-planner", + deletedAt: null, }, }); diff --git a/packages/core/src/models/apps.ts b/packages/core/src/models/apps.ts index 4c9b6563a..59ba0007d 100644 --- a/packages/core/src/models/apps.ts +++ b/packages/core/src/models/apps.ts @@ -46,6 +46,7 @@ export class Apps { where: { id: sessionId, userId: userId, + deletedAt: null, output: { not: Prisma.AnyNull, }, @@ -59,6 +60,7 @@ export class Apps { return this.prisma.appSession.findMany({ where: { userId: userId, + deletedAt: null, output: { not: Prisma.AnyNull, }, From 2edb4c0dd332bca2f796ebc57fb98acbb4e12056 Mon Sep 17 00:00:00 2001 From: Tom Wise <79859203+tomwisecodes@users.noreply.github.com> Date: Tue, 26 Nov 2024 12:05:34 +0000 Subject: [PATCH 24/25] chore: center align text on clear chat dialogs on mobile --- .../DialogControl/ContentOptions/ClearChatHistory.tsx | 4 +++- .../ContentOptions/ClearSingleChatFromChatHistory.tsx | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/nextjs/src/components/DialogControl/ContentOptions/ClearChatHistory.tsx b/apps/nextjs/src/components/DialogControl/ContentOptions/ClearChatHistory.tsx index 07e9ed1ca..bcdb28aa8 100644 --- a/apps/nextjs/src/components/DialogControl/ContentOptions/ClearChatHistory.tsx +++ b/apps/nextjs/src/components/DialogControl/ContentOptions/ClearChatHistory.tsx @@ -61,7 +61,9 @@ const ClearChatHistory = ({ $justifyContent="center" $gap="space-between-m" > - This will permanently delete all of your lesson history. + + This will permanently delete all of your lesson history. + - This will permanently delete this lesson. + This will permanently delete this lesson. {isLoading ? ( From d115b224406a8014b9b54ccb2e138af9cbf888b9 Mon Sep 17 00:00:00 2001 From: Tom Wise <79859203+tomwisecodes@users.noreply.github.com> Date: Tue, 26 Nov 2024 13:50:25 +0000 Subject: [PATCH 25/25] chore: migration --- .../20241126134932_add_deleted_session_col/migration.sql | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 packages/db/prisma/migrations/20241126134932_add_deleted_session_col/migration.sql diff --git a/packages/db/prisma/migrations/20241126134932_add_deleted_session_col/migration.sql b/packages/db/prisma/migrations/20241126134932_add_deleted_session_col/migration.sql new file mode 100644 index 000000000..b9e9d9f95 --- /dev/null +++ b/packages/db/prisma/migrations/20241126134932_add_deleted_session_col/migration.sql @@ -0,0 +1,3 @@ + +-- AlterTable +ALTER TABLE "public"."app_sessions" ADD COLUMN "deleted_at" TIMESTAMP(3);