diff --git a/.gitignore b/.gitignore index 9b1913ec..699be711 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,6 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +# clerk configuration (can include secrets) +/.clerk/ diff --git a/@types/react-syntax-highlighter.d.ts b/@types/react-syntax-highlighter.d.ts new file mode 100644 index 00000000..fcaae402 --- /dev/null +++ b/@types/react-syntax-highlighter.d.ts @@ -0,0 +1,4 @@ +// types/react-syntax-highlighter.d.ts +declare module 'react-syntax-highlighter'; +declare module 'react-syntax-highlighter/dist/esm/styles/hljs'; +declare module 'react-syntax-highlighter/dist/esm/styles/prism'; diff --git a/app/components/ConfirmModal.tsx b/app/components/ConfirmModal.tsx new file mode 100644 index 00000000..e357030b --- /dev/null +++ b/app/components/ConfirmModal.tsx @@ -0,0 +1,42 @@ +// components/ConfirmModal.tsx +import React from 'react'; +import { Dialog } from '@headlessui/react'; + +type Props = { + isOpen: boolean; + onClose: () => void; + onConfirm: () => void; + title?: string; + message?: string; +}; + +const ConfirmModal = ({ isOpen, onClose, onConfirm, title, message }: Props) => ( + + +); + +export default ConfirmModal; diff --git a/app/components/atoms/Button.tsx b/app/components/atoms/Button.tsx new file mode 100644 index 00000000..eda9e5ba --- /dev/null +++ b/app/components/atoms/Button.tsx @@ -0,0 +1,43 @@ +// components/Button.tsx +import React from "react"; +import { buttonClasses } from "@/lib/utils"; +import { Loader2 } from "lucide-react"; // For loading spinner + +type ButtonProps = React.ButtonHTMLAttributes & { + variant?: "default" | "outline" | "ghost" | "danger" | "icon"; + size?: "sm" | "md" | "lg"; + isLoading?: boolean; + startIcon?: React.ReactNode; + endIcon?: React.ReactNode; +}; + +export const Button: React.FC = ({ + children, + className, + variant = "default", + size = "md", + isLoading = false, + startIcon, + endIcon, + disabled, + ...props +}) => { + return ( + + ); +}; diff --git a/app/components/clerk-logo.tsx b/app/components/clerk-logo.tsx deleted file mode 100644 index 312a92ec..00000000 --- a/app/components/clerk-logo.tsx +++ /dev/null @@ -1,27 +0,0 @@ -export function ClerkLogo() { - return ( - - - - - - - - ); -} diff --git a/app/components/code-switcher.tsx b/app/components/code-switcher.tsx index 3a2db2da..2c9e7d2f 100644 --- a/app/components/code-switcher.tsx +++ b/app/components/code-switcher.tsx @@ -1,36 +1,23 @@ "use client"; -import { useOrganization, useSession, useUser } from "@clerk/nextjs"; -import clsx from "clsx"; -import { useState } from "react"; -import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; -import theme from "./theme"; +import { useState } from 'react'; +import SyntaxHighlighter from 'react-syntax-highlighter'; +import { docco } from 'react-syntax-highlighter/dist/esm/styles/hljs'; +import clsx from 'clsx'; -const TYPES = ["user", "session", "organization"]; - -export function CodeSwitcher() { - const [selectedType, setSelectedType] = useState(TYPES[0]); - const { user } = useUser(); - const { session } = useSession(); - const { organization } = useOrganization(); - - const selectedCode = JSON.stringify( - { - user, - session, - organization, - }[selectedType], - null, - 2 - ); +interface CodeSwitcherProps { + typesToShow: string[]; + codeMap: Record; + theme?: any; // Replace with proper theme type from react-syntax-highlighter +} - const typesToShow = organization - ? TYPES - : TYPES.filter((type) => type !== "organization"); +export function CodeSwitcher({ typesToShow, codeMap, theme = docco }: CodeSwitcherProps) { + const [selectedType, setSelectedType] = useState(typesToShow[0]); + const selectedCode = codeMap[selectedType]; return ( -
-
+
+
{typesToShow.map((type) => ( +

Notes

+
+ {/* Center: Search Bar */} +
+ +
+ {/* Optional Right Section (for future buttons) */} +
{/* Placeholder for alignment */} + + ); +} diff --git a/app/components/hooks/TaskForm.tsx b/app/components/hooks/TaskForm.tsx new file mode 100644 index 00000000..3d49d428 --- /dev/null +++ b/app/components/hooks/TaskForm.tsx @@ -0,0 +1,99 @@ +import React, { useState, useRef } from 'react'; +import * as yup from 'yup'; +import { useTasks } from "../hooks/useTasks"; +import { Note } from '../types'; + + +interface Task { + id: string; + text: string; + completed: boolean; +} + +interface FormValues { + title: string; + content: string; + tasks: Task[]; + isChecklist: boolean; + images: string[]; +} + +interface NoteInputProps { + onSave: (note: Partial) => Promise; // Changed to Partial +} + +const noteSchema = yup.object().shape({ + title: yup.string().trim().required('Title is required'), + content: yup.string().required('Note content is required'), + tasks: yup.array().of( + yup.object().shape({ + id: yup.string().required(), + text: yup.string().required('Task text is required'), + completed: yup.boolean().required() + }) + ), + isChecklist: yup.boolean(), + images: yup.array().of(yup.string()) +}); + +export const TaskForm: React.FC = ({ onSave }) => { + const [formValues, setFormValues] = useState({ + title: '', + content: '', + tasks: [], + isChecklist: false, + images: [] + }); + const [errors, setErrors] = useState>({}); + const { taskInputRefs, handleTaskTextChange, handleTaskKeyDown } = useTasks(); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + try { + await noteSchema.validate(formValues, { abortEarly: false }); + await onSave(formValues); + setFormValues({ + title: '', + content: '', + tasks: [], + isChecklist: false, + images: [] + }); + } catch (err) { + if (err instanceof yup.ValidationError) { + const validationErrors: Record = {}; + err.inner.forEach((error) => { + if (error.path) { + validationErrors[error.path] = error.message; + } + }); + setErrors(validationErrors); + } + } + }; + + return ( +
+ setFormValues({ ...formValues, title: e.target.value })} + placeholder="Title" + className="w-full p-2 border rounded" + /> + {errors.title && {errors.title}} + +