Skip to content
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,6 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts

# clerk configuration (can include secrets)
/.clerk/
4 changes: 4 additions & 0 deletions @types/react-syntax-highlighter.d.ts
Original file line number Diff line number Diff line change
@@ -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';
42 changes: 42 additions & 0 deletions app/components/ConfirmModal.tsx
Original file line number Diff line number Diff line change
@@ -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) => (
<Dialog open={isOpen} onClose={onClose} className="fixed z-50 inset-0 flex items-center justify-center">
<div className="fixed inset-0 bg-black opacity-30" aria-hidden="true" />
<div className="relative bg-white p-6 rounded-xl shadow-xl w-full max-w-sm z-50">
<Dialog.Title className="text-lg font-bold mb-2">{title || 'Confirm'}</Dialog.Title>
<Dialog.Description className="text-sm text-gray-700 mb-4">
{message || 'Are you sure you want to proceed?'}
</Dialog.Description>
<div className="flex justify-end gap-3">
<button
onClick={onClose}
className="px-4 py-2 rounded bg-gray-200 hover:bg-gray-300 text-sm"
>
Cancel
</button>
<button
onClick={() => {
onConfirm();
onClose();
}}
className="px-4 py-2 rounded bg-red-600 text-white hover:bg-red-700 text-sm"
>
Delete
</button>
</div>
</div>
</Dialog>
);

export default ConfirmModal;
43 changes: 43 additions & 0 deletions app/components/atoms/Button.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLButtonElement> & {
variant?: "default" | "outline" | "ghost" | "danger" | "icon";
size?: "sm" | "md" | "lg";
isLoading?: boolean;
startIcon?: React.ReactNode;
endIcon?: React.ReactNode;
};

export const Button: React.FC<ButtonProps> = ({
children,
className,
variant = "default",
size = "md",
isLoading = false,
startIcon,
endIcon,
disabled,
...props
}) => {
return (
<button
className={buttonClasses(className, variant, size)}
disabled={isLoading || disabled}
{...props}
>
{isLoading && (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
)}
{!isLoading && startIcon && (
<span className="mr-2">{startIcon}</span>
)}
{children}
{!isLoading && endIcon && (
<span className="ml-2">{endIcon}</span>
)}
</button>
);
};
27 changes: 0 additions & 27 deletions app/components/clerk-logo.tsx

This file was deleted.

42 changes: 14 additions & 28 deletions app/components/code-switcher.tsx
Original file line number Diff line number Diff line change
@@ -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<string, string>;
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 (
<div className={clsx(organization ? "h-[54.625rem]" : "h-[41.625rem]")}>
<div className="w-full bg-[#F7F7F8] rounded-md p-[0.1875rem] flex gap-1.5">
<div className="rounded-lg border border-[#EEEEF0] bg-[#FAFAFA] p-4">
<div className="mb-4 flex gap-2 rounded bg-[#EEEEF0] p-1">
{typesToShow.map((type) => (
<button
className={clsx(
Expand All @@ -48,7 +35,6 @@ export function CodeSwitcher() {
</div>
<div className="relative h-[calc(100%-42px)]">
<div className="mask h-full">
{/* @ts-expect-error */}
<SyntaxHighlighter language="javascript" style={theme}>
{selectedCode}
</SyntaxHighlighter>
Expand Down
29 changes: 29 additions & 0 deletions app/components/components/FileInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// components/FileInput.tsx
import React from "react";

type FileInputProps = {
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
accept?: string;
multiple?: boolean;
inputRef?: React.RefObject<HTMLInputElement>;
className?: string;
};

export const FileInput: React.FC<FileInputProps> = ({
onChange,
accept = "image/*",
multiple = true,
inputRef,
className = "hidden",
}) => {
return (
<input
type="file"
ref={inputRef}
className={className}
accept={accept}
multiple={multiple}
onChange={onChange}
/>
);
};
34 changes: 34 additions & 0 deletions app/components/header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// components/header.tsx
"use client";
import { Menu } from "lucide-react";

interface HeaderProps {
onMenuClick: () => void;
}

export default function Header({ onMenuClick }: HeaderProps) {
return (
<header className="fixed top-0 left-0 right-0 h-16 bg-neutral-900 border-b border-neutral-800 flex items-center justify-between px-4 z-10">
{/* Left: Menu + Title */}
<div className="flex items-center space-x-4">
<button
onClick={onMenuClick}
className="text-white bg-neutral-800 p-2 rounded hover:bg-neutral-700 transition"
>
<Menu size={20} />
</button>
<h1 className="text-3xl font-bold text-[#4f46e5]">Notes</h1>
</div>
{/* Center: Search Bar */}
<div className="flex-grow flex justify-center">
<input
type="text"
placeholder="Search"
className="border border-neutral-700 px-4 py-2 rounded-lg w-full max-w-2xl bg-neutral-900 text-white focus:outline-none focus:ring-1 focus:ring-orange-500"
/>
</div>
{/* Optional Right Section (for future buttons) */}
<div className="w-12" /> {/* Placeholder for alignment */}
</header>
);
}
99 changes: 99 additions & 0 deletions app/components/hooks/TaskForm.tsx
Original file line number Diff line number Diff line change
@@ -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<Note>) => Promise<void>; // 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<NoteInputProps> = ({ onSave }) => {
const [formValues, setFormValues] = useState<FormValues>({
title: '',
content: '',
tasks: [],
isChecklist: false,
images: []
});
const [errors, setErrors] = useState<Record<string, string>>({});
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<string, string> = {};
err.inner.forEach((error) => {
if (error.path) {
validationErrors[error.path] = error.message;
}
});
setErrors(validationErrors);
}
}
};

return (
<form onSubmit={handleSubmit} className="space-y-4">
<input
type="text"
value={formValues.title}
onChange={(e) => setFormValues({ ...formValues, title: e.target.value })}
placeholder="Title"
className="w-full p-2 border rounded"
/>
{errors.title && <span className="text-red-500">{errors.title}</span>}

<textarea
value={formValues.content}
onChange={(e) => setFormValues({ ...formValues, content: e.target.value })}
placeholder="Content"
className="w-full p-2 border rounded"
/>
{errors.content && <span className="text-red-500">{errors.content}</span>}

<button type="submit" className="px-4 py-2 bg-blue-500 text-white rounded">
Save
</button>
</form>
);
};
Loading