diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml new file mode 100644 index 000000000..663268423 --- /dev/null +++ b/.github/workflows/chromatic.yml @@ -0,0 +1,60 @@ +name: "Chromatic" + +on: push + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-chromatic + cancel-in-progress: true + +jobs: + changed-files: + runs-on: ubuntu-latest + name: changed-files + outputs: + all_changed_files: ${{ steps.changed-files.outputs.all_changed_files }} + any_changed: ${{ steps.changed-files.outputs.any_changed }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v44 + with: + files: | + apps/nextjs/** + + chromatic: + name: Run Chromatic + needs: [changed-files] + if: ${{ needs.changed-files.outputs.any_changed == 'true' }} + + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Setup pnpm + uses: pnpm/action-setup@v3 + with: + version: 8 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: "pnpm" + - name: Install dependencies + run: pnpm install + - name: Inject Doppler env vars + uses: dopplerhq/secrets-fetch-action@v1.2.0 + id: doppler + with: + doppler-token: ${{ secrets.DOPPLER_TOKEN }} + inject-env-vars: true + - name: Run Chromatic + uses: chromaui/action@latest + with: + workingDir: apps/nextjs + exitZeroOnChanges: true + exitOnceUploaded: true + onlyChanged: true diff --git a/CHANGE_LOG.md b/CHANGE_LOG.md index 2e25e2815..c673ff1d6 100644 --- a/CHANGE_LOG.md +++ b/CHANGE_LOG.md @@ -1,3 +1,33 @@ +## [1.14.1](https://github.com/oaknational/oak-ai-lesson-assistant/compare/v1.14.0...v1.14.1) (2024-11-07) + + +### Bug Fixes + +* allow requests without a cookie header ([#352](https://github.com/oaknational/oak-ai-lesson-assistant/issues/352)) ([80f5050](https://github.com/oaknational/oak-ai-lesson-assistant/commit/80f50507ab370032a3ef6767bcfe5da0d8b6fe82)) + +# [1.14.0](https://github.com/oaknational/oak-ai-lesson-assistant/compare/v1.13.1...v1.14.0) (2024-11-07) + + +### Bug Fixes + +* add missing dependencies to lesson plan tracking context ([#307](https://github.com/oaknational/oak-ai-lesson-assistant/issues/307)) ([3758dfc](https://github.com/oaknational/oak-ai-lesson-assistant/commit/3758dfcda8a937ca363c0eb40014b01d18c93c23)) +* add missing prisma import ([#342](https://github.com/oaknational/oak-ai-lesson-assistant/issues/342)) ([a0ac1de](https://github.com/oaknational/oak-ai-lesson-assistant/commit/a0ac1de89dc6dda38ab64a02054e992072332fd5)) +* disable feature flagg polling in tests ([c44e1f1](https://github.com/oaknational/oak-ai-lesson-assistant/commit/c44e1f10e98dcda37367f2cd03b324078c7b910f)) +* null render in sidebar provider ([#337](https://github.com/oaknational/oak-ai-lesson-assistant/issues/337)) ([9a12851](https://github.com/oaknational/oak-ai-lesson-assistant/commit/9a12851471aeb5f36e7aebd9c1652d0c2a565966)) +* remaining linting fixes ([#272](https://github.com/oaknational/oak-ai-lesson-assistant/issues/272)) ([18a0f70](https://github.com/oaknational/oak-ai-lesson-assistant/commit/18a0f7061bc9d8bd72c4f6d44eec003acd878df1)) +* set up server side styled-components ([f221c24](https://github.com/oaknational/oak-ai-lesson-assistant/commit/f221c2442617031fa694708da953b4a5f1ea5e6c)) +* skip instrumentation when running turbopack - fix HMR ([4d6fd3b](https://github.com/oaknational/oak-ai-lesson-assistant/commit/4d6fd3b963710703b547cda5b2085bfc7f00f941)) + + +### Features + +* add rag schema (migration) ([#343](https://github.com/oaknational/oak-ai-lesson-assistant/issues/343)) ([638f43f](https://github.com/oaknational/oak-ai-lesson-assistant/commit/638f43fa0167f28974250f3da2d67ef95aaae57e)) +* allow us to configure the server port for local testing ([#308](https://github.com/oaknational/oak-ai-lesson-assistant/issues/308)) ([33bee19](https://github.com/oaknational/oak-ai-lesson-assistant/commit/33bee19e5e631428b648159452ef5af5dbec36bd)) +* bootstrap posthog feature flags with local evaluation ([15e8a67](https://github.com/oaknational/oak-ai-lesson-assistant/commit/15e8a67e9a1ce8e08baa5642c7fffbd2193d3b64)) +* sync featureFlagGroup from clerk to posthog ([46b4f13](https://github.com/oaknational/oak-ai-lesson-assistant/commit/46b4f13a1e887177035a9d195886f34647a87ac9)) +* update emails to use html ([#319](https://github.com/oaknational/oak-ai-lesson-assistant/issues/319)) ([a71e7bc](https://github.com/oaknational/oak-ai-lesson-assistant/commit/a71e7bc4b6416ef01b48de4c950805f8244e658b)) +* use featureFlagGroup claim for feature flags ([759534a](https://github.com/oaknational/oak-ai-lesson-assistant/commit/759534a8b8189c25d665b34db1cdf7f0b7a8f31e)) + ## [1.13.1](https://github.com/oaknational/oak-ai-lesson-assistant/compare/v1.13.0...v1.13.1) (2024-11-05) diff --git a/apps/nextjs/.gitignore b/apps/nextjs/.gitignore index 8c2f3b8e2..415dc7351 100644 --- a/apps/nextjs/.gitignore +++ b/apps/nextjs/.gitignore @@ -9,6 +9,7 @@ tests-e2e/.auth .env.sentry-build-plugin *storybook.log +.chromatic.log # Playwright artifacts -playwright-report \ No newline at end of file +playwright-report diff --git a/apps/nextjs/.storybook/ThemeDecorator.tsx b/apps/nextjs/.storybook/decorators/RadixThemeDecorator.tsx similarity index 68% rename from apps/nextjs/.storybook/ThemeDecorator.tsx rename to apps/nextjs/.storybook/decorators/RadixThemeDecorator.tsx index de0d350f3..5f213faf5 100644 --- a/apps/nextjs/.storybook/ThemeDecorator.tsx +++ b/apps/nextjs/.storybook/decorators/RadixThemeDecorator.tsx @@ -3,7 +3,7 @@ import React from "react"; import { Theme } from "@radix-ui/themes"; import "@radix-ui/themes/styles.css"; -export const ThemeDecorator = (Story: React.ComponentType) => ( +export const RadixThemeDecorator = (Story: React.ComponentType) => ( diff --git a/apps/nextjs/.storybook/preview.tsx b/apps/nextjs/.storybook/preview.tsx index 887d667e3..ff270b2e2 100644 --- a/apps/nextjs/.storybook/preview.tsx +++ b/apps/nextjs/.storybook/preview.tsx @@ -9,18 +9,10 @@ import "@fontsource/lexend/900.css"; import { OakThemeProvider, oakDefaultTheme } from "@oaknational/oak-components"; import type { Preview, Decorator } from "@storybook/react"; -// ModerationProvider is coming in the main Chat.tsx refactor -//import { ModerationProvider } from "../src/components/AppComponents/Chat/Chat/ModerationProvider"; import { TooltipProvider } from "../src/components/AppComponents/Chat/ui/tooltip"; -import { DialogProvider } from "../src/components/AppComponents/DialogContext"; -import { CookieConsentProvider } from "../src/components/ContextProviders/CookieConsentProvider"; -import { DemoProvider } from "../src/components/ContextProviders/Demo"; -import LessonPlanTrackingProvider from "../src/lib/analytics/lessonPlanTrackingContext"; -import { SidebarProvider } from "../src/lib/hooks/use-sidebar"; import { AnalyticsProvider } from "../src/mocks/analytics/provider"; import { TRPCReactProvider } from "../src/utils/trpc"; -import { MockClerkProvider } from "./MockClerkProvider"; -import { ThemeDecorator } from "./ThemeDecorator"; +import { RadixThemeDecorator } from "./decorators/RadixThemeDecorator"; import "./preview.css"; const preview: Preview = { @@ -35,33 +27,29 @@ const preview: Preview = { tags: ["autodocs"], }; +// Providers not currently used +// - MockClerkProvider +// - CookieConsentProvider +// - DemoProvider +// - LessonPlanTrackingProvider +// - DialogProvider +// - OakThemeProvider +// - SidebarProvider +// - ChatModerationProvider + export const decorators: Decorator[] = [ - ThemeDecorator, + RadixThemeDecorator, (Story) => ( - - - {" "} - - - - - - - - - {/* */} - - {/* */} - - - - - - - {" "} - - - + <> + {/* TODO: Mock tRPC calls with MSW */} + + + + + + + + ), ]; diff --git a/apps/nextjs/chromatic.config.json b/apps/nextjs/chromatic.config.json new file mode 100644 index 000000000..418b9e3fd --- /dev/null +++ b/apps/nextjs/chromatic.config.json @@ -0,0 +1,6 @@ +{ + "onlyChanged": true, + "projectId": "Project:672908473f629875f0be294f", + "storybookBaseDir": "apps/nextjs", + "zip": true +} diff --git a/apps/nextjs/package.json b/apps/nextjs/package.json index e156833f1..e3206734c 100644 --- a/apps/nextjs/package.json +++ b/apps/nextjs/package.json @@ -23,7 +23,8 @@ "with-env": "dotenv -e ../../.env --", "aila": "tsx scripts/aila-cli.ts", "storybook": "dotenv -e ../../.env -- storybook dev -p 6006 --no-open", - "build-storybook": "dotenv -e ../../.env -- storybook build" + "build-storybook": "dotenv -e ../../.env -- storybook build", + "chromatic": "pnpm with-env pnpm dlx chromatic" }, "prettier": "@oakai/prettier-config", "dependencies": { diff --git a/apps/nextjs/src/app/api/aila-download-all/route.ts b/apps/nextjs/src/app/api/aila-download-all/route.ts index 1ed389af7..37d46e7f6 100644 --- a/apps/nextjs/src/app/api/aila-download-all/route.ts +++ b/apps/nextjs/src/app/api/aila-download-all/route.ts @@ -130,7 +130,7 @@ async function getHandler(req: Request): Promise { } const lessonExport = await prisma.lessonExport.findFirst({ - where: { gdriveFileId: fileId, userId }, + where: { gdriveFileId: fileId, userId, expiredAt: null }, }); if (!lessonExport) { diff --git a/apps/nextjs/src/app/api/aila-download/route.ts b/apps/nextjs/src/app/api/aila-download/route.ts index 52d4cf5b3..64c9010bc 100644 --- a/apps/nextjs/src/app/api/aila-download/route.ts +++ b/apps/nextjs/src/app/api/aila-download/route.ts @@ -117,6 +117,7 @@ async function getHandler(req: Request): Promise { where: { gdriveFileId: fileId, userId, + expiredAt: null, }, cacheStrategy: { ttl: 60 * 5, swr: 60 * 2 }, }); diff --git a/apps/nextjs/src/app/api/cron-jobs/expired-exports/route.ts b/apps/nextjs/src/app/api/cron-jobs/expired-exports/route.ts new file mode 100644 index 000000000..114d7d276 --- /dev/null +++ b/apps/nextjs/src/app/api/cron-jobs/expired-exports/route.ts @@ -0,0 +1,140 @@ +import { prisma } from "@oakai/db"; +import { googleDrive } from "@oakai/exports/src/gSuite/drive/client"; +import { aiLogger } from "@oakai/logger"; +import * as Sentry from "@sentry/node"; +import type { NextRequest } from "next/server"; +import { isTruthy } from "remeda"; + +const log = aiLogger("cron"); + +const requiredEnvVars = ["CRON_SECRET", "GOOGLE_DRIVE_OUTPUT_FOLDER_ID"]; + +requiredEnvVars.forEach((envVar) => { + if (!process.env[envVar]) { + throw new Error(`Environment variable ${envVar} is not set.`); + } +}); + +async function updateExpiredAt(fileIds: string[]) { + if (fileIds.length === 0) { + log.info("No file IDs to update."); + return; + } + + try { + const result = await prisma.lessonExport.updateMany({ + where: { + gdriveFileId: { + in: fileIds, + }, + }, + data: { + expiredAt: new Date(), + }, + }); + log.info(`Updated expiredAt for ${fileIds.length} files.`); + + if (result.count === fileIds.length) { + log.info("All files updated successfully."); + } else { + throw new Error( + `Expected to update ${fileIds.length} files, but only updated ${result.count}.`, + ); + } + } catch (error) { + log.error("Error updating expiredAt field in the database:", error); + throw error; + } +} + +async function deleteExpiredExports(fileIds: string[]) { + try { + for (const id of fileIds) { + await googleDrive.files.delete({ fileId: id }); + log.info("Deleted:", id); + } + } catch (error) { + log.error("Error deleting old files from folder:", error); + throw error; + } +} + +interface FetchExpiredExportsOptions { + folderId: string; + daysAgo: number; +} + +async function fetchExpiredExports({ + folderId, + daysAgo, +}: FetchExpiredExportsOptions) { + try { + const currentDate = new Date(); + const targetDate = new Date( + currentDate.setDate(currentDate.getDate() - daysAgo), + ).toISOString(); + + const query = `modifiedTime < '${targetDate}' and '${folderId}' in parents`; + + const res = await googleDrive.files.list({ + q: query, + fields: "files(id, name, modifiedTime, ownedByMe )", + pageSize: 1000, + }); + + const files = + res.data.files?.filter((file) => file.ownedByMe === true) || []; + + if (files.length === 0) { + log.info( + "No files found that are older than one month in the specified folder.", + ); + return null; + } + + log.info(`Found ${files.length} files older than one month in folder:`); + return files; + } catch (error) { + log.error("Error fetching old files from folder:", error); + return null; + } +} + +export async function GET(request: NextRequest) { + try { + const authHeader = request.headers.get("authorization"); + + const cronSecret = process.env.CRON_SECRET; + const folderId = process.env.GOOGLE_DRIVE_OUTPUT_FOLDER_ID; + + if (!cronSecret) { + log.error("Missing cron secret"); + return new Response("Missing cron secret", { status: 500 }); + } + if (!folderId) { + log.error("No folder ID provided."); + return new Response("No folder ID provided", { status: 500 }); + } + + if (authHeader !== `Bearer ${cronSecret}`) { + log.error("Authorization failed. Invalid token."); + return new Response("Unauthorized", { status: 401 }); + } + + const files = await fetchExpiredExports({ folderId, daysAgo: 14 }); + + if (!files || files.length === 0) { + return new Response("No expired files found", { status: 404 }); + } + + const validFileIds = files.map((file) => file.id).filter(isTruthy); + + await updateExpiredAt(validFileIds); + await deleteExpiredExports(validFileIds); + } catch (error) { + Sentry.captureException(error); + return new Response("Internal Server Error", { status: 500 }); + } + + return new Response(JSON.stringify({ success: true }), { status: 200 }); +} diff --git a/apps/nextjs/src/components/AppComponents/Chat/Chat/ChatUserAccessCheck.stories.tsx b/apps/nextjs/src/components/AppComponents/Chat/Chat/ChatUserAccessCheck.stories.tsx deleted file mode 100644 index 849f8f2ed..000000000 --- a/apps/nextjs/src/components/AppComponents/Chat/Chat/ChatUserAccessCheck.stories.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react"; - -import ChatUserAccessCheck from "./ChatUserAccessCheck"; - -const meta: Meta = { - title: "Components/Chat/ChatUserAccessCheck", - component: ChatUserAccessCheck, - tags: ["autodocs"], -}; - -export default meta; -type Story = StoryObj; - -export const UserHasAccess: Story = { - args: { - userCanView: true, - children:
Content that the user can view
, - }, -}; - -export const UserDeniedAccess: Story = { - args: { - userCanView: false, - children:
Content that should not be visible
, - }, -}; diff --git a/apps/nextjs/src/components/AppComponents/Chat/Chat/ChatUserAccessCheck.tsx b/apps/nextjs/src/components/AppComponents/Chat/Chat/ChatUserAccessCheck.tsx deleted file mode 100644 index b8b889bf8..000000000 --- a/apps/nextjs/src/components/AppComponents/Chat/Chat/ChatUserAccessCheck.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React from "react"; - -import { Flex } from "@radix-ui/themes"; - -export interface ChatUserAccessCheckProps { - userCanView: boolean; - children: React.ReactNode; -} - -const ChatUserAccessCheck: React.FC = ({ - userCanView, - children, -}) => { - if (!userCanView) { - return ( - -

- Sorry, you do not have permission to view this page. -

-
- ); - } - - return <>{children}; -}; - -export default ChatUserAccessCheck; 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 ee875272b..21c3e5120 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-history.stories.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-history.stories.tsx @@ -1,3 +1,4 @@ +import * as Dialog from "@radix-ui/react-dialog"; import type { Meta, StoryObj } from "@storybook/react"; import { ChatHistory } from "./chat-history"; @@ -9,6 +10,13 @@ const meta: Meta = { layout: "centered", }, tags: ["autodocs"], + decorators: [ + (Story) => ( + + + + ), + ], }; export default meta; 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 395fef328..d0310566d 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/sidebar-actions.stories.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/sidebar-actions.stories.tsx @@ -1,4 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react"; +import { within } from "@storybook/test"; import { SidebarActions } from "./sidebar-actions"; @@ -26,14 +27,28 @@ export const Default: Story = { }, }; +// NOTE: The modal appears on a parent element which isn't captured by visual testing +// TODO: Test the modal directly +export const SharePending: Story = { + args: { + ...Default.args, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const deleteButton = canvas.getByRole("button", { name: "Share" }); + deleteButton.click(); + }, +}; + +// NOTE: The modal appears on a parent element which isn't captured by visual testing +// TODO: Test the modal directly export const RemovePending: Story = { args: { ...Default.args, }, play: async ({ canvasElement }) => { - const deleteButton = canvasElement.querySelector("button:nth-child(2)"); - if (deleteButton instanceof HTMLElement) { - deleteButton.click(); - } + const canvas = within(canvasElement); + const deleteButton = canvas.getByRole("button", { name: "Delete" }); + deleteButton.click(); }, }; diff --git a/apps/nextjs/src/components/AppComponents/ScaleSpan.tsx b/apps/nextjs/src/components/AppComponents/ScaleSpan.tsx new file mode 100644 index 000000000..8bd29debb --- /dev/null +++ b/apps/nextjs/src/components/AppComponents/ScaleSpan.tsx @@ -0,0 +1,6 @@ +import { OakSpan } from "@oaknational/oak-components"; +import styled from "styled-components"; + +export const ScaleSpan = styled(OakSpan)<{ $scale: number }>` + transform: scale(${(props) => props.$scale}); +`; diff --git a/apps/nextjs/src/components/Footer.tsx b/apps/nextjs/src/components/Footer.tsx index de5d4c9a5..282a1266e 100644 --- a/apps/nextjs/src/components/Footer.tsx +++ b/apps/nextjs/src/components/Footer.tsx @@ -7,11 +7,11 @@ import { OakFlex, OakLink, OakMaxWidth, - OakSpan, useCookieConsent, OakP, OakUL, OakLI, + OakIcon, } from "@oaknational/oak-components"; import { aiTools } from "data/aiTools"; import { legalMenuItems, menuItems, socialMenuItems } from "data/menus"; @@ -22,7 +22,7 @@ import styled from "styled-components"; import loop from "@/assets/svg/loop.svg"; import useAnalytics from "@/lib/analytics/useAnalytics"; -import { Icon } from "./Icon"; +import { ScaleSpan } from "./AppComponents/ScaleSpan"; import { Logo } from "./Logo"; function ManageCookiesButton() { @@ -56,7 +56,7 @@ const Footer = () => { $alignItems={"start"} $width={["100%", "unset"]} > - + Menu @@ -97,7 +97,7 @@ const Footer = () => { $alignItems={"start"} $width={["100%", "unset"]} > - + AI experiments legal @@ -112,11 +112,7 @@ const Footer = () => { if (item.target) { return ( - + {item.title} @@ -134,43 +130,38 @@ const Footer = () => { - + - + - - - {socialMenuItems.map((item) => { - return ( - - - - ); - })} - - -

- © Oak National Academy Limited, No 14174888 -

-

- 1 Scott Place, 2 Hardman Street, Manchester, M3 3AA -

-
+ + {socialMenuItems.map((item) => { + return ( + + + + ); + })} + + + + + +

+ © Oak National Academy Limited, No 14174888 +

+

+ 1 Scott Place, 2 Hardman Street, Manchester, M3 3AA +

@@ -206,19 +197,18 @@ const StyledOakLink = styled(OakLink)` text-decoration: underline; } `; + const FooterButton = ({ href, onClick, disabled, target, - icon, children, }: { href?: string; onClick?: () => void; disabled?: boolean; target?: string; - icon?: OakIconName; children: React.ReactNode; }) => { const element = href ? Link : "button"; @@ -229,12 +219,21 @@ const FooterButton = ({ element={element} target={target} disabled={disabled} - iconName={icon} - isTrailingIcon={true} > - - {children} - + + {children} + {href && href.includes("http") && ( + + + + )} + ); }; diff --git a/apps/nextjs/src/data/menus.tsx b/apps/nextjs/src/data/menus.tsx index 70dc7add2..36c5946fa 100644 --- a/apps/nextjs/src/data/menus.tsx +++ b/apps/nextjs/src/data/menus.tsx @@ -1,7 +1,5 @@ -import type { IconName } from "@/components/Icon"; - interface SocialItem { - icon: IconName; + icon: "twitter" | "facebook" | "linkedin" | "instagram"; href: string; id: string; } diff --git a/apps/nextjs/src/lib/feature-flags/bootstrap.ts b/apps/nextjs/src/lib/feature-flags/bootstrap.ts index e916408b8..216e016bb 100644 --- a/apps/nextjs/src/lib/feature-flags/bootstrap.ts +++ b/apps/nextjs/src/lib/feature-flags/bootstrap.ts @@ -21,7 +21,9 @@ const log = aiLogger("feature-flags"); function getDistinctIdFromCookie(headers: ReadonlyHeaders) { const cookieHeader = headers.get("cookie"); - invariant(cookieHeader, "No cookie header"); + if (!cookieHeader) { + return null; + } const cookies = cookie.parse(cookieHeader) as Record; const phCookieKey = `ph_${process.env.NEXT_PUBLIC_POSTHOG_API_KEY}_posthog`; const phCookie = cookies[phCookieKey]; diff --git a/apps/nextjs/src/lib/hooks/use-sidebar.tsx b/apps/nextjs/src/lib/hooks/use-sidebar.tsx index 595e8ea6b..d573b85dc 100644 --- a/apps/nextjs/src/lib/hooks/use-sidebar.tsx +++ b/apps/nextjs/src/lib/hooks/use-sidebar.tsx @@ -27,7 +27,7 @@ interface SidebarProviderProps { } export function SidebarProvider({ children }: SidebarProviderProps) { - const [isSidebarOpen, setSidebarOpen] = React.useState(true); + const [isSidebarOpen, setSidebarOpen] = React.useState(false); const [isLoading, setLoading] = React.useState(true); React.useEffect(() => { @@ -46,10 +46,6 @@ export function SidebarProvider({ children }: SidebarProviderProps) { }); }; - if (isLoading) { - return null; - } - return ( { const allSteps = lessonConstructionSteps(lessonPlan, relevantLessonPlans); - - const step = allSteps[0] - ? `${allSteps[0]?.title}\n\n${allSteps[0]?.content}` - : "FINAL STEP: Respond to the user and help them edit the lesson plan"; - log.info("Prompt: next lesson step", JSON.stringify(step, null, 2)); + const finalStep = { + title: "FINAL STEP", + content: "Respond to the user and help them edit the lesson plan", + }; + const nextStep = allSteps[0] ?? finalStep; + const step = `${nextStep.title}\n\n${nextStep.content}`; + log.info("Prompt: next lesson step", nextStep.title); const parts = [ `YOUR INSTRUCTIONS FOR INTERACTING WITH THE USER diff --git a/packages/logger/index.ts b/packages/logger/index.ts index 255319ba1..7336f9b7c 100644 --- a/packages/logger/index.ts +++ b/packages/logger/index.ts @@ -59,7 +59,8 @@ type ChildKey = | "transcripts" | "trpc" | "ui" - | "webhooks"; + | "webhooks" + | "cron"; const errorLogger = typeof window === "undefined" diff --git a/turbo.json b/turbo.json index 0cb26a988..45f42d4e1 100644 --- a/turbo.json +++ b/turbo.json @@ -133,6 +133,7 @@ "STRICT_CSP", "TELEMETRY_ENABLED", "UPSTASH_*", - "WOLFRAM_CLIENT_SECRET" + "WOLFRAM_CLIENT_SECRET", + "CRON_SECRET" ] }