Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Thank you for your interest in contributing to DocuGen! This document provides g
- Node.js 18+
- npm 9+
- Git
- ESLint 9+ (for local development, ESLint uses flat config)

### Development Setup

Expand Down
44 changes: 18 additions & 26 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
import js from '@eslint/js';
import typescript from '@typescript-eslint/eslint-plugin';
import typescriptParser from '@typescript-eslint/parser';
import reactRefresh from 'eslint-plugin-react-refresh';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';
import globals from 'globals';

const browserGlobals = {
window: 'readonly',
document: 'readonly',
console: 'readonly',
localStorage: 'readonly',
navigator: 'readonly',
setTimeout: 'readonly',
File: 'readonly',
Element: 'readonly',
HTMLInputElement: 'readonly',
React: 'readonly',
};

export default [
js.configs.recommended,
{
Expand Down Expand Up @@ -34,21 +47,9 @@
},
},
globals: {
window: 'readonly',
document: 'readonly',
console: 'readonly',
localStorage: 'readonly',
navigator: 'readonly',
setTimeout: 'readonly',
File: 'readonly',
Element: 'readonly',
HTMLInputElement: 'readonly',
React: 'readonly',
...browserGlobals,
},
},
rules: {
'no-unused-vars': 'warn',
},
},
{
files: ['**/*.{ts,tsx}'],
Expand All @@ -62,28 +63,19 @@
},
},
globals: {
window: 'readonly',
document: 'readonly',
console: 'readonly',
localStorage: 'readonly',
navigator: 'readonly',
setTimeout: 'readonly',
File: 'readonly',
Element: 'readonly',
HTMLInputElement: 'readonly',
React: 'readonly',
...browserGlobals,
},
},
plugins: {
'@typescript-eslint': typescript,
'react-refresh': reactRefresh,
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...typescript.configs.recommended.rules,
...reactHooks.configs.recommended.rules,
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
},
},
Expand Down
26 changes: 5 additions & 21 deletions src/components/CookieConsent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,45 +6,29 @@ function getInitialConsentState(): boolean {
try {
const hasConsented = localStorage.getItem('docugen-cookie-consent');
return !hasConsented;
} catch (error) {
console.error('Failed to read cookie consent from localStorage:', error);
} catch {
return true;
}
}

/**
* Cookie consent management component.
* Manages GDPR compliance for analytics tracking.
* Shows consent banner and stores user preference in localStorage.
*
* @returns Cookie consent banner component
*/
export function CookieConsent() {
const [showConsent, setShowConsent] = useState(getInitialConsentState);

/**
* Handles accepting all cookies.
* Stores acceptance preference and hides consent banner.
*/
const handleAccept = () => {
setShowConsent(false);
try {
localStorage.setItem('docugen-cookie-consent', 'accepted');
} catch (e) {
console.error('Failed to save cookie consent to storage:', e);
} catch {
// Silently fail
}
};

/**
* Handles declining optional cookies.
* Stores decline preference and hides consent banner.
*/
const handleDecline = () => {
setShowConsent(false);
try {
localStorage.setItem('docugen-cookie-consent', 'declined');
} catch (e) {
console.error('Failed to save cookie consent to storage:', e);
} catch {
// Silently fail
}
};

Expand Down
41 changes: 0 additions & 41 deletions src/components/UploadDemo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,13 @@ import { useState, useRef, useCallback } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { Button } from './ui/Button';

/**
* UploadDemo component that provides an interactive file upload demonstration.
* Supports drag-and-drop and click-to-select functionality for Markdown files (.md, .mdx).
* Features multiple states: initial dropzone, uploading, and completion with animated transitions.
* Includes file validation, progress simulation, and the ability to reset and try again.
*
* @example
* ```tsx
* import { UploadDemo } from '@/components/UploadDemo';
*
* function UploadSection() {
* return <UploadDemo />;
* }
* ```
*
* @returns A JSX element representing the upload demonstration interface
*/
export function UploadDemo() {
const [isDragging, setIsDragging] = useState(false);
const [file, setFile] = useState<File | null>(null);
const [isUploading, setIsUploading] = useState(false);
const [isComplete, setIsComplete] = useState(false);
const fileInputRef = useRef<HTMLInputElement>(null);

/**
* Simulates the file upload process with a 2-second delay.
* Sets uploading state to true immediately, then transitions to complete state after timeout.
*/
const simulateUpload = useCallback(() => {
setIsUploading(true);
setTimeout(() => {
Expand All @@ -38,28 +17,16 @@ export function UploadDemo() {
}, 2000);
}, []);

/**
* Handles the drag over event for the dropzone.
* Prevents default browser behavior and sets the dragging state to true.
*/
const handleDragOver = useCallback((e: React.DragEvent) => {
e.preventDefault();
setIsDragging(true);
}, []);

/**
* Handles the drag leave event for the dropzone.
* Prevents default browser behavior and sets the dragging state to false.
*/
const handleDragLeave = useCallback((e: React.DragEvent) => {
e.preventDefault();
setIsDragging(false);
}, []);

/**
* Handles the drop event for the dropzone.
* Validates file type and initiates upload simulation for valid Markdown files.
*/
const handleDrop = useCallback(
(e: React.DragEvent) => {
e.preventDefault();
Expand All @@ -73,10 +40,6 @@ export function UploadDemo() {
[simulateUpload]
);

/**
* Handles file selection via the file input dialog.
* Validates file type and initiates upload simulation for valid Markdown files.
*/
const handleFileSelect = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const selectedFile = e.target.files?.[0];
Expand All @@ -91,10 +54,6 @@ export function UploadDemo() {
[simulateUpload]
);

/**
* Resets the upload demo to its initial state.
* Clears the selected file and resets all state variables.
*/
const resetDemo = () => {
setFile(null);
setIsUploading(false);
Expand Down
34 changes: 4 additions & 30 deletions src/lib/ThemeContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,6 @@ export const ThemeContext = createContext<ThemeContextType | undefined>(undefine

const THEME_STORAGE_KEY = 'docugen-theme';

/**
* Determines the initial theme based on localStorage and system preferences.
* Checks for stored theme first, then falls back to system preference.
*
* @returns Initial theme ('light' | 'dark')
*/
function getInitialTheme(): Theme {
if (typeof window === 'undefined') {
return 'dark';
Expand All @@ -37,12 +31,6 @@ function getInitialTheme(): Theme {
}
}

/**
* Applies the theme to the document root element.
* Adds or removes the 'dark' class to enable Tailwind CSS dark mode.
*
* @param theme - Theme to apply ('light' | 'dark')
*/
function applyTheme(theme: Theme) {
const root = document.documentElement;
if (theme === 'dark') {
Expand All @@ -56,20 +44,6 @@ interface ThemeProviderProps {
children: ReactNode;
}

/**
* React context provider for theme management.
* Manages theme state, persistence, and DOM theme application.
*
* @param children - Child components to wrap with theme context
* @returns Theme context provider component
*
* @example
* ```typescript
* <ThemeProvider>
* <App />
* </ThemeProvider>
* ```
*/
export function ThemeProvider({ children }: ThemeProviderProps) {
const [theme, setTheme] = useState<Theme>(getInitialTheme);

Expand All @@ -82,17 +56,17 @@ export function ThemeProvider({ children }: ThemeProviderProps) {
setTheme(newTheme);
try {
localStorage.setItem(THEME_STORAGE_KEY, newTheme);
} catch (error) {
console.warn('Failed to persist theme preference:', error);
} catch {
// Silently fail
}
};

const setThemeDirect = (newTheme: Theme) => {
setTheme(newTheme);
try {
localStorage.setItem(THEME_STORAGE_KEY, newTheme);
} catch (error) {
console.warn('Failed to persist theme preference:', error);
} catch {
// Silently fail
}
};

Expand Down
Loading