diff --git a/.vscode/settings.json b/.vscode/settings.json index daa6fc1fd..97cf21f98 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -51,6 +51,7 @@ "eyfs", "firstname", "fkey", + "flagbuttonformitem", "fontsource", "gdrive", "Geist", @@ -146,6 +147,7 @@ "transpiles", "trivago", "trpc", + "TSES", "Turbopack", "turborepo", "uidotdev", diff --git a/apps/nextjs/.eslintrc.cjs b/apps/nextjs/.eslintrc.cjs index d815e14a5..36eabc292 100644 --- a/apps/nextjs/.eslintrc.cjs +++ b/apps/nextjs/.eslintrc.cjs @@ -2,6 +2,7 @@ module.exports = { extends: ["../../.eslintrc.cjs", "next", "plugin:storybook/recommended"], rules: { + "react/prefer-read-only-props": "error", "no-restricted-imports": [ "error", { diff --git a/apps/nextjs/next-env.d.ts b/apps/nextjs/next-env.d.ts index 4f11a03dc..40c3d6809 100644 --- a/apps/nextjs/next-env.d.ts +++ b/apps/nextjs/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. +// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information. diff --git a/apps/nextjs/src/app/admin/aila/[chatId]/view.tsx b/apps/nextjs/src/app/admin/aila/[chatId]/view.tsx index fa67b0510..8b7174c78 100644 --- a/apps/nextjs/src/app/admin/aila/[chatId]/view.tsx +++ b/apps/nextjs/src/app/admin/aila/[chatId]/view.tsx @@ -7,7 +7,7 @@ import { OakAccordion, OakPrimaryButton } from "@oaknational/oak-components"; import { trpc } from "@/utils/trpc"; -function ModerationListItem({ moderation }: { moderation: Moderation }) { +function ModerationListItem({ moderation }: { readonly moderation: Moderation }) { const { id, invalidatedAt } = moderation; const [invalidated, setInvalidated] = useState(Boolean(invalidatedAt)); const invalidateModeration = trpc.admin.invalidateModeration.useMutation({ @@ -60,8 +60,8 @@ export function AdminChatView({ chat, moderations, }: { - chat: AilaPersistedChat; - moderations: Moderation[]; + readonly chat: AilaPersistedChat; + readonly moderations: Moderation[]; }) { return ( <> diff --git a/apps/nextjs/src/app/aila/page-contents.tsx b/apps/nextjs/src/app/aila/page-contents.tsx index a616d3e8d..49119a78d 100644 --- a/apps/nextjs/src/app/aila/page-contents.tsx +++ b/apps/nextjs/src/app/aila/page-contents.tsx @@ -7,7 +7,7 @@ import Layout from "@/components/AppComponents/Layout"; import { ChatProvider } from "@/components/ContextProviders/ChatProvider"; import LessonPlanTrackingProvider from "@/lib/analytics/lessonPlanTrackingContext"; -const ChatPageContents = ({ id }: { id: string }) => { +const ChatPageContents = ({ id }: { readonly id: string }) => { return ( diff --git a/apps/nextjs/src/app/global-error.tsx b/apps/nextjs/src/app/global-error.tsx index 98fcdb425..ce7e1baaa 100644 --- a/apps/nextjs/src/app/global-error.tsx +++ b/apps/nextjs/src/app/global-error.tsx @@ -9,7 +9,7 @@ import FullPageWarning from "@/components/FullPageWarning"; export default function GlobalError({ error, }: { - error: Error & { digest?: string }; + readonly error: Error & { digest?: string }; }) { useEffect(() => { Sentry.captureException(error, { diff --git a/apps/nextjs/src/app/home-page.tsx b/apps/nextjs/src/app/home-page.tsx index 1e434927a..f30dbe2e2 100644 --- a/apps/nextjs/src/app/home-page.tsx +++ b/apps/nextjs/src/app/home-page.tsx @@ -55,9 +55,9 @@ const OakFlexCustomMaxWidthWithHalfWidth = styled(OakFlexCustomMaxWidth)` } `; -type HomePageProps = { +export type HomePageProps = Readonly<{ pageData: HomePageQueryResult | null; -}; +}>; export default function HomePage(props: HomePageProps) { return ( diff --git a/apps/nextjs/src/app/legal/[slug]/legal.tsx b/apps/nextjs/src/app/legal/[slug]/legal.tsx index 3b8c9094e..7631c4dbb 100644 --- a/apps/nextjs/src/app/legal/[slug]/legal.tsx +++ b/apps/nextjs/src/app/legal/[slug]/legal.tsx @@ -7,9 +7,9 @@ import type { PolicyDocument } from "cms/types/policyDocument"; import Layout from "@/components/Layout"; import { portableTextComponents } from "@/components/PortableText/portableTextComponents"; -interface LegalContentProps { +export type LegalContentProps = Readonly<{ pageData: PolicyDocument; -} +}>; export const LegalContent = ({ pageData }: LegalContentProps) => { return ( diff --git a/apps/nextjs/src/app/legal/[slug]/page.tsx b/apps/nextjs/src/app/legal/[slug]/page.tsx index 6786e9af5..a9f388acc 100644 --- a/apps/nextjs/src/app/legal/[slug]/page.tsx +++ b/apps/nextjs/src/app/legal/[slug]/page.tsx @@ -3,11 +3,13 @@ import { notFound } from "next/navigation"; import LegalContent from "./legal"; +export type PolicyContentPageProps = Readonly<{ + params: { readonly slug: string }; +}>; + export default async function PolicyContentPage({ params, -}: { - params: { slug: string }; -}) { +}: PolicyContentPageProps) { const pageData = await fetchPolicyDocument({ slug: params.slug }); if (!pageData) { diff --git a/apps/nextjs/src/app/lesson-planner/preview/[slug]/page.tsx b/apps/nextjs/src/app/lesson-planner/preview/[slug]/page.tsx index 4de40351c..fcced6393 100644 --- a/apps/nextjs/src/app/lesson-planner/preview/[slug]/page.tsx +++ b/apps/nextjs/src/app/lesson-planner/preview/[slug]/page.tsx @@ -23,11 +23,13 @@ async function getData(slug: string) { return planSections; } +export type QuizPreviewPageProps = Readonly<{ + params: { readonly slug: string }; +}>; + export default async function QuizPreviewPage({ params, -}: { - params: { slug: string }; -}) { +}: QuizPreviewPageProps) { log.info("params", params); const planSections = await getData(params.slug); diff --git a/apps/nextjs/src/app/prompts/prompts.tsx b/apps/nextjs/src/app/prompts/prompts.tsx index 7ac888fe2..c358e617f 100644 --- a/apps/nextjs/src/app/prompts/prompts.tsx +++ b/apps/nextjs/src/app/prompts/prompts.tsx @@ -18,9 +18,9 @@ import HeroContainer from "@/components/HeroContainer"; import Layout from "@/components/Layout"; import { slugify } from "@/utils/slugify"; -type PromptsPageData = { +export type PromptsPageData = Readonly<{ apps: SerializedAppWithPrompt[]; -}; +}>; export const PromptsContent = ({ apps }: PromptsPageData) => { const pathname = usePathname(); diff --git a/apps/nextjs/src/app/quiz-designer/[slug]/page.tsx b/apps/nextjs/src/app/quiz-designer/[slug]/page.tsx index 7ac8b364a..e362898f0 100644 --- a/apps/nextjs/src/app/quiz-designer/[slug]/page.tsx +++ b/apps/nextjs/src/app/quiz-designer/[slug]/page.tsx @@ -19,11 +19,13 @@ async function getData(slug: string) { return parsedData; } +export type GenerationsPageProps = Readonly<{ + params: { readonly slug: string }; +}>; + export default async function GenerationsPage({ params, -}: { - params: { slug: string }; -}) { +}: GenerationsPageProps) { const data = await getData(params.slug); return ; } diff --git a/apps/nextjs/src/app/quiz-designer/preview/[slug]/page.tsx b/apps/nextjs/src/app/quiz-designer/preview/[slug]/page.tsx index c5c79051d..c8d9b3d05 100644 --- a/apps/nextjs/src/app/quiz-designer/preview/[slug]/page.tsx +++ b/apps/nextjs/src/app/quiz-designer/preview/[slug]/page.tsx @@ -22,11 +22,13 @@ async function getData(slug: string) { return questions; } +export type QuizPreviewPageProps = Readonly<{ + params: { readonly slug: string }; +}>; + export default async function QuizPreviewPage({ params, -}: { - params: { slug: string }; -}) { +}: QuizPreviewPageProps) { log.info("params", params); const questions = await getData(params.slug); diff --git a/apps/nextjs/src/app/styles-registry.tsx b/apps/nextjs/src/app/styles-registry.tsx index 53e3196b9..c43622606 100644 --- a/apps/nextjs/src/app/styles-registry.tsx +++ b/apps/nextjs/src/app/styles-registry.tsx @@ -9,7 +9,7 @@ import { ServerStyleSheet, StyleSheetManager } from "styled-components"; export default function StyledComponentsRegistry({ children, }: { - children: React.ReactNode; + readonly children: React.ReactNode; }) { // Only create stylesheet once with lazy initial state // x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state diff --git a/apps/nextjs/src/components/AiIcon.tsx b/apps/nextjs/src/components/AiIcon.tsx index 926fc5def..75676a8c1 100644 --- a/apps/nextjs/src/components/AiIcon.tsx +++ b/apps/nextjs/src/components/AiIcon.tsx @@ -1,4 +1,4 @@ -const AiIcon = ({ color = "black" }: { color?: "black" | "white" }) => { +const AiIcon = ({ color = "black" }: { readonly color?: "black" | "white" }) => { return ( ; const ChatModeration = ({ children }: ChatModerationProps) => { const chat = useLessonChat(); diff --git a/apps/nextjs/src/components/AppComponents/Chat/Chat/ChatModerationDisplay.tsx b/apps/nextjs/src/components/AppComponents/Chat/Chat/ChatModerationDisplay.tsx index fbf72ab34..a63ff43ff 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/Chat/ChatModerationDisplay.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/Chat/ChatModerationDisplay.tsx @@ -5,10 +5,10 @@ import { Flex } from "@radix-ui/themes"; import ToxicModerationView from "../toxic-moderation-view"; -export interface ModerationDisplayProps { +export type ModerationDisplayProps = Readonly<{ toxicModeration: PersistedModerationBase | null; chatId: string; -} +}>; export const ChatModerationDisplay: React.FC = ({ toxicModeration, diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-lessonPlanDisplay.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-lessonPlanDisplay.tsx index 46764f3e9..064fa82b1 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-lessonPlanDisplay.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-lessonPlanDisplay.tsx @@ -28,17 +28,19 @@ const displayStyles = cva( "relative flex flex-col space-y-10 px-14 pb-28 opacity-100 sm:px-24 ", ); +export type LessonPlanDisplayProps = Readonly<{ + chatEndRef: React.MutableRefObject; + sectionRefs: Record>; + documentContainerRef: React.MutableRefObject; + showLessonMobile: boolean; +}>; + export const LessonPlanDisplay = ({ chatEndRef, sectionRefs, documentContainerRef, showLessonMobile, -}: { - chatEndRef: React.MutableRefObject; - sectionRefs: Record>; - documentContainerRef: React.MutableRefObject; - showLessonMobile: boolean; -}) => { +}: LessonPlanDisplayProps) => { const chat = useLessonChat(); const { ailaStreamingStatus, lastModeration } = chat; const lessonPlan = { diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-list.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-list.tsx index 5a6aaff8b..32b00ee94 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-list.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-list.tsx @@ -111,6 +111,15 @@ export function ChatList({ ); } +export type ChatMessagesDisplayProps = Readonly<{ + id: string; + messages: Message[]; + lastModeration: PersistedModerationBase | null; + persistedModerations: PersistedModerationBase[]; + ailaStreamingStatus: AilaStreamingStatus; + demo: DemoContextProps; +}>; + export const ChatMessagesDisplay = ({ messages, id, @@ -118,14 +127,7 @@ export const ChatMessagesDisplay = ({ persistedModerations = [], ailaStreamingStatus, demo, -}: { - id: string; - messages: Message[]; - lastModeration: PersistedModerationBase | null; - persistedModerations: PersistedModerationBase[]; - ailaStreamingStatus: AilaStreamingStatus; - demo: DemoContextProps; -}) => { +}: ChatMessagesDisplayProps) => { const { lessonPlan, isStreaming } = useLessonChat(); const { setDialogWindow } = useDialog(); const { totalSections, totalSectionsComplete } = useProgressForDownloads({ @@ -236,9 +238,9 @@ const InChatDownloadButtons = ({ id, setDialogWindow, }: { - demo: DemoContextProps; - id: string; - setDialogWindow: Dispatch>; + readonly demo: DemoContextProps; + readonly id: string; + readonly setDialogWindow: Dispatch>; }) => { return ( @@ -274,9 +276,9 @@ const InnerInChatButton = ({ children, }: { - iconName: "download" | "share"; + readonly iconName: "download" | "share"; - children: string; + readonly children: string; }) => { return ( ; + isDemoLocked: boolean; +}>; + export const ChatPanelArea = ({ children, chatAreaRef, isDemoLocked, -}: { - children: React.ReactNode; - chatAreaRef?: React.RefObject; - isDemoLocked: boolean; -}) => { +}: ChatPanelAreaProps) => { const demo = useDemoUser(); return ( diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-panel-disclaimer.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-panel-disclaimer.tsx index 21746df7e..4d291ae7a 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-panel-disclaimer.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-panel-disclaimer.tsx @@ -1,4 +1,4 @@ -const ChatPanelDisclaimer = ({ size }: { size: "sm" | "md" | "lg" }) => { +const ChatPanelDisclaimer = ({ size }: { readonly size: "sm" | "md" | "lg" }) => { return (

Aila can make mistakes. Check your lesson before use. See our{" "} diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-quick-buttons.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-quick-buttons.tsx index 3226fc83d..4a97e507f 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-quick-buttons.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-quick-buttons.tsx @@ -12,9 +12,9 @@ import type { AilaStreamingStatus } from "./Chat/hooks/useAilaStreamingStatus"; import ChatButton from "./ui/chat-button"; import { IconRefresh, IconStop } from "./ui/icons"; -interface QuickActionButtonsProps { +export type QuickActionButtonsProps = Readonly<{ isEmptyScreen: boolean; -} +}>; const shouldAllowStop = ( ailaStreamingStatus: AilaStreamingStatus, diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-share-dialog.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-share-dialog.tsx index f6ab67705..37bc4dc1a 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-share-dialog.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-share-dialog.tsx @@ -25,8 +25,8 @@ import { constructSharePath } from "./Chat/utils"; const log = aiLogger("chat"); interface ChatShareDialogProps extends DialogProps { - chat: SideBarChatItem; - onCopy: () => void; + readonly chat: SideBarChatItem; + readonly onCopy: () => void; } export function ChatShareDialog({ 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..6f3a787e5 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-start-accordion.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-start-accordion.tsx @@ -140,17 +140,17 @@ const ChatStartAccordion = () => { // Define prop types for each component interface AccordionItemProps extends React.ComponentPropsWithoutRef { - children: React.ReactNode; + readonly children: React.ReactNode; } interface AccordionTriggerProps extends React.ComponentPropsWithoutRef { - children: React.ReactNode; + readonly children: React.ReactNode; } interface AccordionContentProps extends React.ComponentPropsWithoutRef { - children: React.ReactNode; + readonly children: React.ReactNode; } const AccordionItem = React.forwardRef( diff --git a/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/action-button-wrapper.tsx b/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/action-button-wrapper.tsx index a2e602193..3db3b8c30 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/action-button-wrapper.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/action-button-wrapper.tsx @@ -15,7 +15,7 @@ import type { import { ActionDropDown } from "./action-drop-down"; import type { FeedbackOption } from "./drop-down-form-wrapper"; -type ActionButtonWrapperProps = { +export type ActionButtonWrapperProps = Readonly<{ sectionTitle: string; sectionPath: string; sectionValue: Record | string | Array; @@ -28,7 +28,7 @@ type ActionButtonWrapperProps = { option: FeedbackOption, userFeedbackText: string, ) => string; -}; +}>; const ActionButtonWrapper = ({ sectionTitle, diff --git a/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/action-button.tsx b/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/action-button.tsx index 686a6a594..45b564dcd 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/action-button.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/action-button.tsx @@ -7,15 +7,12 @@ import { } from "@oaknational/oak-components"; import styled from "styled-components"; -const ActionButton = ({ - children, - onClick, - tooltip, -}: { +export type ActionButtonProps = Readonly<{ children: React.ReactNode; onClick: () => void; tooltip: string; -}) => { +}>; +const ActionButton = ({ children, onClick, tooltip }: ActionButtonProps) => { const [showTooltip, setShowTooltip] = useState(false); return ( diff --git a/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/action-drop-down.tsx b/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/action-drop-down.tsx index 8bce2a066..c4d240129 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/action-drop-down.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/action-drop-down.tsx @@ -1,20 +1,21 @@ -import { Dispatch, RefObject, SetStateAction } from "react"; +import type { Dispatch, RefObject, SetStateAction } from "react"; import { aiLogger } from "@oakai/logger"; import { OakP, OakRadioGroup } from "@oaknational/oak-components"; -import { $Enums, AilaUserModificationAction } from "@prisma/client"; +import type { $Enums, AilaUserModificationAction } from "@prisma/client"; import { TextArea } from "@radix-ui/themes"; -import { +import type { AdditionalMaterialOptions, ModifyOptions, } from "./action-button.types"; -import { DropDownFormWrapper, FeedbackOption } from "./drop-down-form-wrapper"; +import type { FeedbackOption } from "./drop-down-form-wrapper"; +import { DropDownFormWrapper } from "./drop-down-form-wrapper"; import { SmallRadioButton } from "./small-radio-button"; const log = aiLogger("chat"); -type DropDownProps = { +export type DropDownProps = Readonly<{ sectionTitle: string; options: ModifyOptions | AdditionalMaterialOptions; selectedRadio: FeedbackOption | null; @@ -31,7 +32,7 @@ type DropDownProps = { userSuggestionTitle: string; dropdownRef: RefObject; id: string; -}; +}>; export const ActionDropDown = ({ sectionTitle, diff --git a/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/add-additional-materials-button.tsx b/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/add-additional-materials-button.tsx index c3eaa718a..23a0353b3 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/add-additional-materials-button.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/add-additional-materials-button.tsx @@ -4,11 +4,11 @@ import ActionButtonWrapper from "./action-button-wrapper"; import { additionalMaterialsModifyOptions } from "./action-button.types"; import type { FeedbackOption } from "./drop-down-form-wrapper"; -export type AdditionalMaterialsProps = { +export type AdditionalMaterialsProps = Readonly<{ sectionTitle: string; sectionPath: string; sectionValue: Record | string | Array; -}; +}>; const AddAdditionalMaterialsButton = ({ sectionTitle, diff --git a/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/chat-section.tsx b/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/chat-section.tsx index 7f6d29d77..e999b74c6 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/chat-section.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/chat-section.tsx @@ -8,13 +8,11 @@ import AddAdditionalMaterialsButton from "./add-additional-materials-button"; import FlagButton from "./flag-button"; import ModifyButton from "./modify-button"; -const ChatSection = ({ - objectKey, - value, -}: { +export type ChatSectionProps = Readonly<{ objectKey: string; value: Record | string | Array; -}) => { +}>; +const ChatSection = ({ objectKey, value }: ChatSectionProps) => { return ( ) => void; setIsOpen: (value: boolean) => void; @@ -36,7 +36,7 @@ export const DropDownFormWrapper = < buttonText: string; isOpen: boolean; dropdownRef: React.RefObject; -}) => { +}>) => { const { isStreaming } = useLessonChat(); const firstButtonRef = useRef(null); useEffect(() => { diff --git a/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/flag-button.tsx b/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/flag-button.tsx index 48a17ecd5..0254d8ef4 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/flag-button.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/flag-button.tsx @@ -23,11 +23,11 @@ const flagOptions = [ type FlagButtonOptions = typeof flagOptions; -type FlagButtonProps = { +export type FlagButtonProps = Readonly<{ sectionTitle: string; sectionPath: string; sectionValue: Record | string | Array; -}; +}>; const FlagButton = ({ sectionTitle, @@ -115,11 +115,11 @@ const FlagButtonFormItem = ({ displayTextBox, setUserFeedbackText, }: { - option: FlagButtonOptions[number]; - setSelectedRadio: (value: FeedbackOption) => void; - setDisplayTextBox: (value: string | null) => void; - displayTextBox: string | null; - setUserFeedbackText: (value: string) => void; + readonly option: FlagButtonOptions[number]; + readonly setSelectedRadio: (value: FeedbackOption) => void; + readonly setDisplayTextBox: (value: string | null) => void; + readonly displayTextBox: string | null; + readonly setUserFeedbackText: (value: string) => void; }) => { return ( <> diff --git a/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/index.tsx b/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/index.tsx index 52663adf2..aac7f06e6 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/index.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/index.tsx @@ -14,7 +14,7 @@ import ChatSection from "./chat-section"; const HALF_SECOND = 500; -type DropDownSectionProps = { +export type DropDownSectionProps = Readonly<{ objectKey: string; sectionRefs: Record>; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -23,7 +23,7 @@ type DropDownSectionProps = { userHasCancelledAutoScroll: boolean; showLessonMobile: boolean; streamingTimeout?: number; -}; +}>; const DropDownSection = ({ objectKey, diff --git a/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/modify-button.tsx b/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/modify-button.tsx index 16b3b9947..427042a61 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/modify-button.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/modify-button.tsx @@ -4,11 +4,11 @@ import ActionButtonWrapper from "./action-button-wrapper"; import { modifyOptions } from "./action-button.types"; import type { FeedbackOption } from "./drop-down-form-wrapper"; -type ModifyButtonProps = { +export type ModifyButtonProps = Readonly<{ sectionTitle: string; sectionPath: string; sectionValue: Record | string | Array; -}; +}>; const ModifyButton = ({ sectionTitle, diff --git a/apps/nextjs/src/components/AppComponents/Chat/empty-screen-accordion.tsx b/apps/nextjs/src/components/AppComponents/Chat/empty-screen-accordion.tsx index 4a4efa09f..887c6d368 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/empty-screen-accordion.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/empty-screen-accordion.tsx @@ -130,17 +130,17 @@ const EmptyScreenAccordion = () => { // Define prop types for each component interface AccordionItemProps extends React.ComponentPropsWithoutRef { - children: React.ReactNode; + readonly children: React.ReactNode; } interface AccordionTriggerProps extends React.ComponentPropsWithoutRef { - children: React.ReactNode; + readonly children: React.ReactNode; } interface AccordionContentProps extends React.ComponentPropsWithoutRef { - children: React.ReactNode; + readonly children: React.ReactNode; } const AccordionItem = React.forwardRef( diff --git a/apps/nextjs/src/components/AppComponents/Chat/export-buttons/MobileExportButtons.tsx b/apps/nextjs/src/components/AppComponents/Chat/export-buttons/MobileExportButtons.tsx index f8d367752..b10d70274 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/export-buttons/MobileExportButtons.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/export-buttons/MobileExportButtons.tsx @@ -6,9 +6,9 @@ import { useDemoUser } from "@/components/ContextProviders/Demo"; import { useDialog } from "../../DialogContext"; -type MobileExportButtonsProps = { +export type MobileExportButtonsProps = Readonly<{ closeMobileLessonPullOut: () => void; -}; +}>; export const MobileExportButtons = ({ closeMobileLessonPullOut, diff --git a/apps/nextjs/src/components/AppComponents/Chat/export-buttons/index.tsx b/apps/nextjs/src/components/AppComponents/Chat/export-buttons/index.tsx index 42276d2e7..671e9ee56 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/export-buttons/index.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/export-buttons/index.tsx @@ -10,10 +10,10 @@ import useAnalytics from "@/lib/analytics/useAnalytics"; import { useDialog } from "../../DialogContext"; import { LessonPlanProgressDropdown } from "./LessonPlanProgressDropdown"; -export type ExportButtonsProps = { +export type ExportButtonsProps = Readonly<{ sectionRefs: Record>; documentContainerRef: React.MutableRefObject; -}; +}>; const ExportButtons = ({ sectionRefs, diff --git a/apps/nextjs/src/components/AppComponents/Chat/markdown.tsx b/apps/nextjs/src/components/AppComponents/Chat/markdown.tsx index 1fe02df05..d935e040c 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/markdown.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/markdown.tsx @@ -18,15 +18,17 @@ const MemoizedReactMarkdown: FC = memo( prevProps.className === nextProps.className, ); +export type ReactMarkdownWithStylesProps = Readonly<{ + markdown: string; + lessonPlanSectionDescription?: string; + className?: string; +}>; + export const MemoizedReactMarkdownWithStyles = ({ markdown, lessonPlanSectionDescription, className, -}: { - markdown: string; - lessonPlanSectionDescription?: string; - className?: string; -}) => { +}: ReactMarkdownWithStylesProps) => { return ( diff --git a/apps/nextjs/src/components/AppComponents/Chat/sidebar-item.tsx b/apps/nextjs/src/components/AppComponents/Chat/sidebar-item.tsx index 71dec1e16..1838ba202 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/sidebar-item.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/sidebar-item.tsx @@ -19,10 +19,10 @@ import { cn } from "@/lib/utils"; import { constructChatPath } from "./Chat/utils"; -interface SidebarItemProps { +export type SidebarItemProps = Readonly<{ chat: SideBarChatItem; children?: React.ReactNode; -} +}>; export function SidebarItem({ chat, children }: SidebarItemProps) { const pathname = usePathname(); diff --git a/apps/nextjs/src/components/AppComponents/Chat/toxic-moderation-view.tsx b/apps/nextjs/src/components/AppComponents/Chat/toxic-moderation-view.tsx index 853024644..4d3af5988 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/toxic-moderation-view.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/toxic-moderation-view.tsx @@ -9,13 +9,14 @@ import { Icon } from "@/components/Icon"; import ChatButton from "./ui/chat-button"; +export type ToxicModerationViewProps = Readonly<{ + chatId: string; + moderation: PersistedModerationBase; +}>; const ToxicModerationView = ({ chatId, moderation, -}: { - chatId: string; - moderation: PersistedModerationBase; -}) => { +}: ToxicModerationViewProps) => { const { onSubmit, comment, setComment, hasSubmitted, isValid } = useModerationFeedbackSurvey({ chatId, diff --git a/apps/nextjs/src/components/AppComponents/Chat/ui/button.tsx b/apps/nextjs/src/components/AppComponents/Chat/ui/button.tsx index 8f4e0aa35..9ce6ee098 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/ui/button.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/ui/button.tsx @@ -38,7 +38,7 @@ const buttonVariants = cva( export interface ButtonProps extends React.ButtonHTMLAttributes, VariantProps { - asChild?: boolean; + readonly asChild?: boolean; } const Button = React.forwardRef( diff --git a/apps/nextjs/src/components/AppComponents/ComparativeJudgement/PreviewContent.tsx b/apps/nextjs/src/components/AppComponents/ComparativeJudgement/PreviewContent.tsx index c742826b3..d0c4e09d9 100644 --- a/apps/nextjs/src/components/AppComponents/ComparativeJudgement/PreviewContent.tsx +++ b/apps/nextjs/src/components/AppComponents/ComparativeJudgement/PreviewContent.tsx @@ -6,10 +6,10 @@ import { sortAlphabetically } from "@/utils/alphabetiseArray"; import PromptTemplate from "./PromptTemplate"; import QuestionInner from "./QuestionInner"; -type PreviewContentProps = { +export type PreviewContentProps = Readonly<{ option: OptionWithPrompt; question?: string; -}; +}>; const PreviewContent = ({ option, question }: PreviewContentProps) => { if (!option?.answerAndDistractor) return null; diff --git a/apps/nextjs/src/components/AppComponents/FeedbackForms/ModerationFeedbackForm.tsx b/apps/nextjs/src/components/AppComponents/FeedbackForms/ModerationFeedbackForm.tsx index a4407a22c..22366e46a 100644 --- a/apps/nextjs/src/components/AppComponents/FeedbackForms/ModerationFeedbackForm.tsx +++ b/apps/nextjs/src/components/AppComponents/FeedbackForms/ModerationFeedbackForm.tsx @@ -11,11 +11,11 @@ import { Icon } from "@/components/Icon"; import ChatButton from "../Chat/ui/chat-button"; import { Textarea } from "../Chat/ui/textarea"; -export type ModerationFeedbackFormProps = { +export type ModerationFeedbackFormProps = Readonly<{ chatId: string; moderation: PersistedModerationBase; closeModal: () => void; -}; +}>; export const ModerationFeedbackForm = ({ chatId, diff --git a/apps/nextjs/src/components/AppComponents/Layout/index.tsx b/apps/nextjs/src/components/AppComponents/Layout/index.tsx index 805022741..d2e7d90a1 100644 --- a/apps/nextjs/src/components/AppComponents/Layout/index.tsx +++ b/apps/nextjs/src/components/AppComponents/Layout/index.tsx @@ -6,13 +6,12 @@ import Footer from "@/components/Footer"; import { Header } from "../Chat/header"; import { DialogProvider } from "../DialogContext"; -const Layout = ({ - children, - includeFooter, -}: { - children: React.ReactNode; - includeFooter?: boolean; -}) => { +export type LayoutProps = Readonly<{ + readonly children: React.ReactNode; + readonly includeFooter?: boolean; +}>; + +const Layout = ({ children, includeFooter }: LayoutProps) => { const isDemoUser = useDemoUser().isDemoUser; return (

diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizDesignerPageContent.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizDesignerPageContent.tsx index 8b920cee7..ff0ab3fd5 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizDesignerPageContent.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizDesignerPageContent.tsx @@ -16,7 +16,7 @@ import type { QuizAppState } from "../../../ai-apps/quiz-designer/state/types"; import { QuizAppStatus } from "../../../ai-apps/quiz-designer/state/types"; import SuggestedQuestions from "./SuggestedQuestions"; -type Props = { +export type QuizDesignerPageContentProps = Readonly<{ state: QuizAppState; dispatch: Dispatch; isExportMenuOpen: boolean; @@ -27,7 +27,7 @@ type Props = { shareContent: () => void; shareId: string | null; shareLoading: boolean; -}; +}>; const QuizDesignerPageContent = ({ state, @@ -40,7 +40,7 @@ const QuizDesignerPageContent = ({ shareContent, shareId, shareLoading, -}: Props) => { +}: QuizDesignerPageContentProps) => { const { error: suggestedQuestionsError, suggestedQuestionsGeneration, diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/RegenButtonGroup.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/RegenButtonGroup.tsx index 420fede29..51f435027 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/RegenButtonGroup.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/RegenButtonGroup.tsx @@ -6,12 +6,12 @@ import { trpc } from "@/utils/trpc"; import GenerateButton from "../GenerateAllButton"; -type RegenButtonGroupProps = { +export type RegenButtonGroupProps = Readonly<{ requestRegenerateAnswersGeneration: () => void; requestRegenerateAllDistractorsGeneration: () => void; answersStatus: UseGenerationStatus; distractorStatus: UseGenerationStatus; -}; +}>; const RegenButtonGroup = (props: RegenButtonGroupProps) => { const { diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/SuggestedQuestionCard.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/SuggestedQuestionCard.tsx index 5c907490a..73db3051e 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/SuggestedQuestionCard.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/SuggestedQuestionCard.tsx @@ -8,6 +8,15 @@ import type { PotentialQuestionsType } from "hooks/useSuggestedQuestions"; import { Icon } from "@/components/Icon"; +export type SuggestedLessonCardProps = Readonly<{ + answer: PotentialQuestionsType[0]; + dispatch: Dispatch; + questionsWrapperRef: React.RefObject; + potentialNewQuestions: PotentialQuestionsType; + setPotentialNewQuestions: React.Dispatch; + questionRefs: React.MutableRefObject<(HTMLDivElement | null)[]>; +}>; + const SuggestedLessonCard = ({ answer: question, dispatch, @@ -15,14 +24,7 @@ const SuggestedLessonCard = ({ potentialNewQuestions, setPotentialNewQuestions, questionRefs, -}: { - answer: PotentialQuestionsType[0]; - dispatch: Dispatch; - questionsWrapperRef: React.RefObject; - potentialNewQuestions: PotentialQuestionsType; - setPotentialNewQuestions: React.Dispatch; - questionRefs: React.MutableRefObject<(HTMLDivElement | null)[]>; -}) => { +}: SuggestedLessonCardProps) => { const [showAnswers, setShowAnswers] = useState(false); return ( ; questionsWrapperRef: React.RefObject; questionRefs: React.MutableRefObject<(HTMLDivElement | null)[]>; -}; +}>; const SuggestedQuestions = ({ suggestedQuestionsGeneration, diff --git a/apps/nextjs/src/components/AppComponents/download/DownloadButton.tsx b/apps/nextjs/src/components/AppComponents/download/DownloadButton.tsx index 2ed87a0a4..e8727e486 100644 --- a/apps/nextjs/src/components/AppComponents/download/DownloadButton.tsx +++ b/apps/nextjs/src/components/AppComponents/download/DownloadButton.tsx @@ -15,6 +15,18 @@ import QuizIcon from "../../SVGParts/QuizIcon"; import SlidesIcon from "../../SVGParts/SlidesIcon"; import { SendEmailIcon } from "./DownloadAllButton"; +export type DownloadButtonProps = Readonly<{ + chatId: string; + onClick: () => void; + lesson: LooseLessonPlan; + title: string; + subTitle: string; + downloadAvailable: boolean; + downloadLoading: boolean; + data?: { link: string } | { message: string; error?: unknown }; + exportsType: ExportsType; + "data-testid"?: string; +}>; export const DownloadButton = ({ chatId, onClick, @@ -26,18 +38,7 @@ export const DownloadButton = ({ data, exportsType, "data-testid": dataTestId, -}: { - chatId: string; - onClick: () => void; - lesson: LooseLessonPlan; - title: string; - subTitle: string; - downloadAvailable: boolean; - downloadLoading: boolean; - data?: { link: string } | { message: string; error?: unknown }; - exportsType: ExportsType; - "data-testid"?: string; -}) => { +}: DownloadButtonProps) => { const lessonTitle = lesson.title; const link = data && "link" in data ? data.link : ""; const hasError = data && "message" in data; diff --git a/apps/nextjs/src/components/AppComponents/download/SectionsNotCompleteDownloadNotice.tsx b/apps/nextjs/src/components/AppComponents/download/SectionsNotCompleteDownloadNotice.tsx index 65a9c7129..2fe9630b6 100644 --- a/apps/nextjs/src/components/AppComponents/download/SectionsNotCompleteDownloadNotice.tsx +++ b/apps/nextjs/src/components/AppComponents/download/SectionsNotCompleteDownloadNotice.tsx @@ -14,11 +14,13 @@ import AiIcon from "@/components/SVGParts/AiIcon"; import type { ProgressSections } from "../Chat/Chat/hooks/useProgressForDownloads"; +export type SectionsNotCompleteDownloadNoticeProps = Readonly<{ + sections: ProgressSections; +}>; + const SectionsNotCompleteDownloadNotice = ({ sections, -}: { - sections: ProgressSections; -}) => { +}: SectionsNotCompleteDownloadNoticeProps) => { const [showMissingSections, setShowMissingSections] = useState(false); const inCompleteSections = sections.filter((section) => !section.complete); return ( diff --git a/apps/nextjs/src/components/ButtonCore.tsx b/apps/nextjs/src/components/ButtonCore.tsx index d9e5d1601..32580925d 100644 --- a/apps/nextjs/src/components/ButtonCore.tsx +++ b/apps/nextjs/src/components/ButtonCore.tsx @@ -22,7 +22,7 @@ type ButtonClassNameType = (options: { disabled?: boolean; }) => string; -type ButtonCoreProps = { +export type ButtonCoreProps = Readonly<{ children?: React.ReactNode; onClick?: () => void; href?: string; @@ -42,7 +42,7 @@ type ButtonCoreProps = { download?: boolean; title?: string; testId?: string; -}; +}>; export type ButtonVariant = "primary" | "secondary" | "text-link" | "icon-only"; diff --git a/apps/nextjs/src/components/ContextProviders/ChatModerationContext.tsx b/apps/nextjs/src/components/ContextProviders/ChatModerationContext.tsx index 2e90dfe4a..b960e0a77 100644 --- a/apps/nextjs/src/components/ContextProviders/ChatModerationContext.tsx +++ b/apps/nextjs/src/components/ContextProviders/ChatModerationContext.tsx @@ -11,13 +11,14 @@ const ChatModerationContext = createContext< ChatModerationContextProps | undefined >(undefined); +export type ChatModerationProviderProps = Readonly<{ + chatId: string; + children: ReactNode; +}>; export const ChatModerationProvider = ({ chatId, children, -}: { - chatId: string; - children: ReactNode; -}) => { +}: ChatModerationProviderProps) => { const moderationModalHelpers = useModerationModal({ chatId }); const value = useMemo( () => ({ moderationModalHelpers }), diff --git a/apps/nextjs/src/components/ContextProviders/FeatureFlagProvider.tsx b/apps/nextjs/src/components/ContextProviders/FeatureFlagProvider.tsx index 9ad4cc76d..bca43c929 100644 --- a/apps/nextjs/src/components/ContextProviders/FeatureFlagProvider.tsx +++ b/apps/nextjs/src/components/ContextProviders/FeatureFlagProvider.tsx @@ -24,10 +24,10 @@ const FeatureFlagContext = createContext({ bootstrappedFeatures: {}, }); -export interface FeatureFlagProviderProps { +export type FeatureFlagProviderProps = Readonly<{ children: ReactNode; bootstrappedFeatures: Record; -} +}>; export const FeatureFlagProvider = ({ children, diff --git a/apps/nextjs/src/components/DialogControl/ContentOptions/DemoInterstitialDialog.tsx b/apps/nextjs/src/components/DialogControl/ContentOptions/DemoInterstitialDialog.tsx index 904f0e0f6..48d1c22a9 100644 --- a/apps/nextjs/src/components/DialogControl/ContentOptions/DemoInterstitialDialog.tsx +++ b/apps/nextjs/src/components/DialogControl/ContentOptions/DemoInterstitialDialog.tsx @@ -37,13 +37,15 @@ function friendlyNumber( } } +export type CreatingChatDialogProps = Readonly<{ + submit?: () => void; + closeDialog: () => void; +}>; + const CreatingChatDialog = ({ submit, closeDialog, -}: { - submit?: () => void; - closeDialog: () => void; -}) => { +}: CreatingChatDialogProps) => { const demo = useDemoUser(); const [isSubmitting, setIsSubmitting] = useState(false); const [appSessionsRemaining, setAppSessionsRemaining] = useState< diff --git a/apps/nextjs/src/components/DialogControl/ContentOptions/DemoShareLockedDialog.tsx b/apps/nextjs/src/components/DialogControl/ContentOptions/DemoShareLockedDialog.tsx index 26f303fbb..d18c9170f 100644 --- a/apps/nextjs/src/components/DialogControl/ContentOptions/DemoShareLockedDialog.tsx +++ b/apps/nextjs/src/components/DialogControl/ContentOptions/DemoShareLockedDialog.tsx @@ -4,7 +4,7 @@ import { Flex } from "@radix-ui/themes"; import Button from "@/components/Button"; import { useDemoUser } from "@/components/ContextProviders/Demo"; -function DialogContainer({ children }: { children: React.ReactNode }) { +function DialogContainer({ children }: { readonly children: React.ReactNode }) { return ( {children}

; } -function Content({ children }: { children: React.ReactNode }) { +function Content({ children }: { readonly children: React.ReactNode }) { return

{children}

; } const DemoShareLockedDialog = ({ closeDialog, }: { - closeDialog: () => void; + readonly closeDialog: () => void; }) => { const demo = useDemoUser(); diff --git a/apps/nextjs/src/components/DialogControl/ContentOptions/DemoSharedComponents.tsx b/apps/nextjs/src/components/DialogControl/ContentOptions/DemoSharedComponents.tsx index 2128f11eb..2a9c6aed3 100644 --- a/apps/nextjs/src/components/DialogControl/ContentOptions/DemoSharedComponents.tsx +++ b/apps/nextjs/src/components/DialogControl/ContentOptions/DemoSharedComponents.tsx @@ -1,6 +1,6 @@ import { OakFlex, OakHeading, OakP } from "@oaknational/oak-components"; -export function DialogContainer({ children }: { children: React.ReactNode }) { +export function DialogContainer({ children }: { readonly children: React.ReactNode }) { return ( {children} @@ -23,6 +23,6 @@ export function DialogHeading({ children }: { children: React.ReactNode }) { ); } -export function DialogContent({ children }: { children: React.ReactNode }) { +export function DialogContent({ children }: { readonly children: React.ReactNode }) { return {children}; } diff --git a/apps/nextjs/src/components/DialogControl/ContentOptions/EndOfLessonFeedback.tsx b/apps/nextjs/src/components/DialogControl/ContentOptions/EndOfLessonFeedback.tsx index 37e0c49fc..95200167d 100644 --- a/apps/nextjs/src/components/DialogControl/ContentOptions/EndOfLessonFeedback.tsx +++ b/apps/nextjs/src/components/DialogControl/ContentOptions/EndOfLessonFeedback.tsx @@ -3,7 +3,7 @@ import { usePosthogFeedbackSurvey } from "hooks/surveys/usePosthogFeedbackSurvey import FeedBack from "@/components/Feedback"; -const EndOfLessonFeedback = ({ closeDialog }: { closeDialog: () => void }) => { +const EndOfLessonFeedback = ({ closeDialog }: { readonly closeDialog: () => void }) => { const { survey, submitSurvey, closeDialogWithPostHogDismiss } = usePosthogFeedbackSurvey({ closeDialog, diff --git a/apps/nextjs/src/components/DialogControl/ContentOptions/ModalFooterButtons.tsx b/apps/nextjs/src/components/DialogControl/ContentOptions/ModalFooterButtons.tsx index 249d79fd4..71f4f4f74 100644 --- a/apps/nextjs/src/components/DialogControl/ContentOptions/ModalFooterButtons.tsx +++ b/apps/nextjs/src/components/DialogControl/ContentOptions/ModalFooterButtons.tsx @@ -2,13 +2,14 @@ import { OakFlex, OakSpan } from "@oaknational/oak-components"; import { OakLinkNoUnderline } from "@/components/OakLinkNoUnderline"; +export type ModalFooterButtonsProps = Readonly<{ + closeDialog: () => void; + actionButtonStates: () => JSX.Element; +}>; const ModalFooterButtons = ({ closeDialog, actionButtonStates, -}: { - closeDialog: () => void; - actionButtonStates: () => JSX.Element; -}) => { +}: ModalFooterButtonsProps) => { return ( void; + isShared?: boolean | undefined; +}>; + const DialogContents = ({ chatId, lesson, @@ -51,14 +60,7 @@ const DialogContents = ({ messages, submit, isShared, -}: { - chatId: string | undefined; - lesson: LooseLessonPlan; - children?: React.ReactNode; - messages?: Message[]; - submit?: () => void; - isShared?: boolean | undefined; -}) => { +}: DialogContentsProps) => { const { dialogWindow, setDialogWindow } = useDialog(); const closeDialog = () => setDialogWindow(""); if (dialogWindow === "") return null; diff --git a/apps/nextjs/src/components/DialogControl/DialogRoot.tsx b/apps/nextjs/src/components/DialogControl/DialogRoot.tsx index c86249eb8..1a8b9af99 100644 --- a/apps/nextjs/src/components/DialogControl/DialogRoot.tsx +++ b/apps/nextjs/src/components/DialogControl/DialogRoot.tsx @@ -5,7 +5,7 @@ import * as Dialog from "@radix-ui/react-dialog"; import { useDialog } from "../AppComponents/DialogContext"; -const DialogRoot = ({ children }: { children: React.ReactNode }) => { +const DialogRoot = ({ children }: { readonly children: React.ReactNode }) => { const { isSignedIn } = useAuth(); const { dialogWindow, setDialogWindow } = useDialog(); diff --git a/apps/nextjs/src/components/Feedback/Smiley.tsx b/apps/nextjs/src/components/Feedback/Smiley.tsx index fbeec1d2c..62e01ab79 100644 --- a/apps/nextjs/src/components/Feedback/Smiley.tsx +++ b/apps/nextjs/src/components/Feedback/Smiley.tsx @@ -1,10 +1,8 @@ -const Smiley = ({ - selected, - number, -}: { - selected: boolean; - number: number; -}) => { +export type SmileyProps = Readonly<{ + readonly selected: boolean; + readonly number: number; +}>; +const Smiley = ({ selected, number }: SmileyProps) => { const color = selected ? "#287C34" : "#222222"; if (number === 5) { diff --git a/apps/nextjs/src/components/Feedback/index.tsx b/apps/nextjs/src/components/Feedback/index.tsx index 455c3d6ae..e41694b10 100644 --- a/apps/nextjs/src/components/Feedback/index.tsx +++ b/apps/nextjs/src/components/Feedback/index.tsx @@ -7,17 +7,18 @@ import type { Survey } from "posthog-js"; import ChatButton from "../AppComponents/Chat/ui/chat-button"; import { Icon } from "../Icon"; +export type FeedBackProps = Readonly<{ + submitSurvey: (usersResponse: { [key: string]: string }) => void; + survey: Survey; + onSubmit: () => void; + closeDialogWithPostHogDismiss: () => void; +}>; const FeedBack = ({ submitSurvey, survey, onSubmit, closeDialogWithPostHogDismiss, -}: { - survey: Survey; - submitSurvey: (usersResponse: { [key: string]: string }) => void; - closeDialogWithPostHogDismiss: () => void; - onSubmit: () => void; -}) => { +}: FeedBackProps) => { const closeButtonRef = useRef(null); const rating = [ { number: 1 }, diff --git a/apps/nextjs/src/components/Footer.tsx b/apps/nextjs/src/components/Footer.tsx index 282a1266e..194b60aee 100644 --- a/apps/nextjs/src/components/Footer.tsx +++ b/apps/nextjs/src/components/Footer.tsx @@ -198,19 +198,21 @@ const StyledOakLink = styled(OakLink)` } `; +export type FooterButtonProps = Readonly<{ + href?: string; + onClick?: () => void; + disabled?: boolean; + target?: string; + children: React.ReactNode; +}>; + const FooterButton = ({ href, onClick, disabled, target, children, -}: { - href?: string; - onClick?: () => void; - disabled?: boolean; - target?: string; - children: React.ReactNode; -}) => { +}: FooterButtonProps) => { const element = href ? Link : "button"; return ( ; + const FullPageWarning = ({ children }: FullPageWarningProps) => { return (
@@ -17,32 +18,36 @@ const FullPageWarning = ({ children }: FullPageWarningProps) => { ); }; -interface FullPageWarningIconProps { +export type FullPageWarningIconProps = Readonly<{ icon: IconName; size: IconSize; -} +}>; + const FullPageWarningIcon = ({ icon, size }: FullPageWarningIconProps) => { return ; }; -interface FullPageWarningHeaderProps { +export type FullPageWarningHeaderProps = Readonly<{ children: React.ReactNode; -} +}>; + const FullPageWarningHeader = ({ children }: FullPageWarningHeaderProps) => { return

{children}

; }; -interface FullPageWarningContentProps { +export type FullPageWarningContentProps = Readonly<{ children: React.ReactNode; -} +}>; + const FullPageWarningContent = ({ children }: FullPageWarningContentProps) => { return

{children}

; }; -interface FullPageWarningButtonProps { +export type FullPageWarningButtonProps = Readonly<{ children: React.ReactNode; href: string; -} +}>; + const FullPageWarningButton = ({ children, href, diff --git a/apps/nextjs/src/components/ImageAltGenerator/ImageRow.tsx b/apps/nextjs/src/components/ImageAltGenerator/ImageRow.tsx index c1da6d492..3b95734f9 100644 --- a/apps/nextjs/src/components/ImageAltGenerator/ImageRow.tsx +++ b/apps/nextjs/src/components/ImageAltGenerator/ImageRow.tsx @@ -14,13 +14,11 @@ import LoadingWheel from "../LoadingWheel"; const log = aiLogger("ui"); -const ImageRow = ({ - resource, - isPreview, -}: { - resource: Resource; - isPreview?: boolean; -}) => { +export type ImageRowProps = Readonly<{ + readonly resource: Resource; + readonly isPreview?: boolean; +}>; +const ImageRow = ({ resource, isPreview }: ImageRowProps) => { const router = useRouter(); const [newAlt, setNewAlt] = useState(); const [newAltForQuiz, setNewAltForQuiz] = useState< diff --git a/apps/nextjs/src/components/Input.tsx b/apps/nextjs/src/components/Input.tsx index 254d25e35..6c2d716f5 100644 --- a/apps/nextjs/src/components/Input.tsx +++ b/apps/nextjs/src/components/Input.tsx @@ -34,7 +34,7 @@ const labelStyles = cva( "absolute -top-8 left-10 z-10 w-fit -rotate-2 bg-teachersYellow px-8 text-sm text-black sm:-top-10 sm:text-base", ); -interface InputProps { +export type InputProps = Readonly<{ label: string; name: string; type: "text" | "dropdown" | "email" | "textarea"; // Added "textarea" type @@ -54,7 +54,7 @@ interface InputProps { disabled?: boolean; resize?: boolean; size?: "sm" | "md" | "lg"; // Implement for all input types -} +}>; const Input: React.FC = ({ label, diff --git a/apps/nextjs/src/components/LoadingWheel.tsx b/apps/nextjs/src/components/LoadingWheel.tsx index 0ce8e90f7..ed83112ee 100644 --- a/apps/nextjs/src/components/LoadingWheel.tsx +++ b/apps/nextjs/src/components/LoadingWheel.tsx @@ -3,28 +3,29 @@ import React from "react"; import type { IconName, IconSize } from "./Icon"; import { Icon } from "./Icon"; -const LoadingWheel = React.forwardRef< - HTMLDivElement, - { - icon?: IconName; - size?: IconSize; - visible?: boolean; - } ->(({ icon = "reload", size = "md", visible = true }, ref) => { - return ( -
- -
- ); -}); +export type LoadingWheelProps = Readonly<{ + icon?: IconName; + size?: IconSize; + visible?: boolean; +}>; + +const LoadingWheel = React.forwardRef( + ({ icon = "reload", size = "md", visible = true }, ref) => { + return ( +
+ +
+ ); + }, +); LoadingWheel.displayName = "LoadingWheel"; diff --git a/apps/nextjs/src/lib/analytics/hubspot/HubspotLoader.tsx b/apps/nextjs/src/lib/analytics/hubspot/HubspotLoader.tsx index ecd0a440f..a1f3aaa95 100644 --- a/apps/nextjs/src/lib/analytics/hubspot/HubspotLoader.tsx +++ b/apps/nextjs/src/lib/analytics/hubspot/HubspotLoader.tsx @@ -25,10 +25,10 @@ const reportToSentry = (e: unknown) => { }); }; -type HubspotScriptProps = { +export type HubspotScriptProps = Readonly<{ consent: ConsentState; onLoad: () => void; -}; +}>; const HubspotLoader: FC = ({ consent, onLoad }) => { const pathname = usePathname(); const currentParams = useSearchParams(); diff --git a/apps/nextjs/src/lib/analytics/lessonPlanTrackingContext.tsx b/apps/nextjs/src/lib/analytics/lessonPlanTrackingContext.tsx index 5d5e9304d..2f9e9a3af 100644 --- a/apps/nextjs/src/lib/analytics/lessonPlanTrackingContext.tsx +++ b/apps/nextjs/src/lib/analytics/lessonPlanTrackingContext.tsx @@ -33,10 +33,14 @@ type LessonPlanTrackingContext = { const lessonPlanTrackingContext = createContext(null); -const LessonPlanTrackingProvider: FC<{ - children?: React.ReactNode; - chatId: string; -}> = ({ children, chatId }) => { +export type LessonPlanTrackingProviderProps = Readonly<{ + readonly children?: React.ReactNode; + readonly chatId: string; +}>; +const LessonPlanTrackingProvider: FC = ({ + children, + chatId, +}) => { const { track } = useAnalytics(); const [action, setAction] = useState(null); const [userMessageContent, setUserMessageContent] = useState(""); diff --git a/apps/nextjs/src/lib/feature-flags/bootstrap.ts b/apps/nextjs/src/lib/feature-flags/bootstrap.ts index 216e016bb..36ab380de 100644 --- a/apps/nextjs/src/lib/feature-flags/bootstrap.ts +++ b/apps/nextjs/src/lib/feature-flags/bootstrap.ts @@ -24,7 +24,7 @@ function getDistinctIdFromCookie(headers: ReadonlyHeaders) { if (!cookieHeader) { return null; } - const cookies = cookie.parse(cookieHeader) as Record; + const cookies = cookie.parse(cookieHeader); const phCookieKey = `ph_${process.env.NEXT_PUBLIC_POSTHOG_API_KEY}_posthog`; const phCookie = cookies[phCookieKey]; if (!phCookie) { diff --git a/apps/nextjs/src/lib/hooks/use-sidebar.tsx b/apps/nextjs/src/lib/hooks/use-sidebar.tsx index d573b85dc..176feded9 100644 --- a/apps/nextjs/src/lib/hooks/use-sidebar.tsx +++ b/apps/nextjs/src/lib/hooks/use-sidebar.tsx @@ -23,7 +23,7 @@ export function useSidebar() { } interface SidebarProviderProps { - children: React.ReactNode; + readonly children: React.ReactNode; } export function SidebarProvider({ children }: SidebarProviderProps) { diff --git a/apps/nextjs/src/lib/lessonPlan/organiseSections.ts b/apps/nextjs/src/lib/lessonPlan/organiseSections.ts index 4e5c1592f..3b52c4f68 100644 --- a/apps/nextjs/src/lib/lessonPlan/organiseSections.ts +++ b/apps/nextjs/src/lib/lessonPlan/organiseSections.ts @@ -1,4 +1,4 @@ -import { LessonPlanKeys } from "@oakai/aila/src/protocol/schema"; +import type { LessonPlanKeys } from "@oakai/aila/src/protocol/schema"; export const organiseSections: { trigger: LessonPlanKeys; diff --git a/apps/nextjs/src/mocks/analytics/provider.tsx b/apps/nextjs/src/mocks/analytics/provider.tsx index 837784288..5623f1270 100644 --- a/apps/nextjs/src/mocks/analytics/provider.tsx +++ b/apps/nextjs/src/mocks/analytics/provider.tsx @@ -46,7 +46,7 @@ const mockAnalyticsContext: AnalyticsContext = { } as unknown as PostHog, }; -export const AnalyticsProvider: React.FC<{ children: React.ReactNode }> = ({ +export const AnalyticsProvider: React.FC<{ readonly children: React.ReactNode }> = ({ children, }) => ( diff --git a/apps/nextjs/src/mocks/clerk/nextjsComponents.tsx b/apps/nextjs/src/mocks/clerk/nextjsComponents.tsx index 927c35744..c96d21080 100644 --- a/apps/nextjs/src/mocks/clerk/nextjsComponents.tsx +++ b/apps/nextjs/src/mocks/clerk/nextjsComponents.tsx @@ -52,8 +52,8 @@ const states: Record = { const ClerkContext = React.createContext(states.signedIn); type ClerkProviderProps = { - state: "loading" | "signedIn" | "signedInDemo" | "signedOut"; - children: React.ReactNode; + readonly state: "loading" | "signedIn" | "signedInDemo" | "signedOut"; + readonly children: React.ReactNode; }; export const ClerkProvider = ({ state, children }: ClerkProviderProps) => ( @@ -87,12 +87,12 @@ export const useAuth = () => { }; }; -export const SignedIn = ({ children }: { children: React.ReactNode }) => { +export const SignedIn = ({ children }: { readonly children: React.ReactNode }) => { const context = React.useContext(ClerkContext); return context.isSignedIn ? children : null; }; -export const SignedOut = ({ children }: { children: React.ReactNode }) => { +export const SignedOut = ({ children }: { readonly children: React.ReactNode }) => { const context = React.useContext(ClerkContext); return context.isSignedIn ? null : children; };