Skip to content
Closed
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
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["next/babel"]
}
33 changes: 33 additions & 0 deletions __tests__/components/ReportPreview.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'

// Mock ReactMarkdown
jest.mock('react-markdown', () => {
return function MockReactMarkdown({ children }: { children: string }) {
return <div data-testid="markdown-content">{children}</div>
}
})

import ReportPreview from '../../components/ReportPreview'

describe('ReportPreview', () => {
it('renders with correct content', () => {
const { getByTestId } = render(
<ReportPreview
project_name="Test Project"
report_number="1"
time_spent="test progress"
next_quarter="test plans"
money_usage="test usage"
help_needed="test help"
/>
)

const content = getByTestId('markdown-content').textContent || ''

// Just verify the key elements we care about
expect(content).toContain('Progress Report # 1')
expect(content).toContain('Use of Funds')
})
})
70 changes: 70 additions & 0 deletions components/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React from 'react'

interface Props {
children: React.ReactNode
}

interface State {
hasError: boolean
error?: Error
}

class ErrorBoundary extends React.Component<Props, State> {
constructor(props: Props) {
super(props)
this.state = { hasError: false }
}

static getDerivedStateFromError(error: Error): State {
return { hasError: true, error }
}

componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error('Error caught by boundary:', error, errorInfo)
}

render() {
if (this.state.hasError) {
return (
<div className="min-h-screen bg-white px-4 py-16 dark:bg-gray-900 sm:px-6 sm:py-24 md:grid md:place-items-center lg:px-8">
<div className="mx-auto max-w-max">
<main className="sm:flex">
<p className="text-4xl font-bold tracking-tight text-orange-500 sm:text-5xl">
Oops!
</p>
<div className="sm:ml-6">
<div className="dark:border-gray-700 sm:border-l sm:border-gray-200 sm:pl-6">
<h1 className="text-4xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-5xl">
Something went wrong
</h1>
<p className="mt-1 text-base text-gray-500 dark:text-gray-400">
{this.state.error?.message ||
'An unexpected error occurred'}
</p>
<div className="mt-6 flex space-x-3 sm:border-l sm:border-transparent sm:pl-6">
<button
onClick={() => window.location.reload()}
className="inline-flex items-center rounded-md border border-transparent bg-orange-500 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-orange-600 focus:outline-none focus:ring-2 focus:ring-orange-500 focus:ring-offset-2"
>
Try again
</button>
<button
onClick={() => (window.location.href = '/')}
className="inline-flex items-center rounded-md border border-transparent bg-gray-100 px-4 py-2 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-orange-500 focus:ring-offset-2 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700"
>
Go back home
</button>
</div>
</div>
</div>
</main>
</div>
</div>
)
}

return this.props.children
}
}

export default ErrorBoundary
59 changes: 54 additions & 5 deletions components/FormButton.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,63 @@
function FormButton({ variant, children, ...rest }) {
interface FormButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'enabled' | 'disabled' | 'primary'
loading?: boolean
children?: React.ReactNode
}

function FormButton({
variant = 'primary',
loading = false,
children,
className = '',
...rest
}: FormButtonProps) {
const defaultVariant =
'bg-orange-500 hover:bg-orange-700 text-xl text-white font-bold py-2 px-4 rounded'
'inline-flex justify-center items-center bg-orange-500 hover:bg-orange-600 text-white font-bold py-2 px-4 rounded-md transition-colors duration-200 ease-in-out border-0'
const buttonVariants = {
enabled: defaultVariant,
disabled: `${defaultVariant} opacity-50 cursor-not-allowed`,
disabled: `${defaultVariant} cursor-not-allowed opacity-50`,
primary: defaultVariant,
}

return (
<button className={`${buttonVariants[variant]} ...`} {...rest}>
{children}
<button
className={`${buttonVariants[variant]} ${
loading ? 'cursor-not-allowed opacity-50' : ''
} ${className}`}
disabled={loading}
type="submit"
{...rest}
>
{loading ? (
<span className="flex w-full items-center justify-center">
<svg
className="-ml-1 mr-3 h-5 w-5 animate-spin text-white"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
/>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
Processing...
</span>
) : (
<span className="flex w-full items-center justify-center">
{children || 'Submit'}
</span>
)}
</button>
)
}
Expand Down
Loading
Loading