Skip to content
Merged
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,214 changes: 552 additions & 662 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
"eslint-plugin-prettier": "^4.2.1",
"prettier": "^2.8.8",
"husky": "^8.0.3",
"lint-staged": "^14.3.0",
"lint-staged": "^16.4.0",
"fast-check": "^3.22.0",
"jsdom": "^26.1.0",
"postcss": "^8.5.6",
Expand Down
62 changes: 36 additions & 26 deletions src/app/(auth)/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { loginSchema, LoginFormData } from '../../lib/validationSchemas';
import { FormError, FieldError } from '../../../components/forms/FormError';
import { apiClient } from '@/lib/api';
import { parseApiError } from '@/utils/error-handler';

export default function LoginPage() {
const [isLoading, setIsLoading] = useState(false);
Expand All @@ -31,22 +33,11 @@ export default function LoginPage() {
setSuccessMessage('');

try {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});

const result = await response.json();

if (!response.ok) {
throw new Error(result.message || 'Login failed');
}

await apiClient.post('/api/auth/login', data);
setSuccessMessage('Login successful! Redirecting...');
setTimeout(() => router.push('/dashboard'), 1500);
} catch (error) {
setApiError(error instanceof Error ? error.message : 'An error occurred');
setApiError(parseApiError(error).userMessage);
} finally {
setIsLoading(false);
}
Expand All @@ -71,8 +62,18 @@ export default function LoginPage() {
>
<div className="flex items-center justify-center gap-2 mb-8">
<div className="w-8 h-8 bg-gradient-to-r from-cyan-400 to-blue-500 rounded-lg flex items-center justify-center">
<svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
<svg
className="w-5 h-5 text-white"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"
/>
</svg>
</div>
<span className="text-xl font-semibold text-gray-900">TeachLink</span>
Expand All @@ -91,9 +92,7 @@ export default function LoginPage() {
className="space-y-5"
>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Email
</label>
<label className="block text-sm font-medium text-gray-700 mb-2">Email</label>
<input
type="email"
placeholder="[email protected]"
Expand All @@ -104,9 +103,7 @@ export default function LoginPage() {
</div>

<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Password
</label>
<label className="block text-sm font-medium text-gray-700 mb-2">Password</label>
<div className="relative">
<input
type={showPassword ? 'text' : 'password'}
Expand Down Expand Up @@ -185,10 +182,22 @@ export default function LoginPage() {
className="px-4 py-2.5 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors flex items-center justify-center gap-2"
>
<svg className="w-5 h-5" viewBox="0 0 24 24">
<path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" />
<path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" />
<path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" />
<path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" />
<path
fill="#4285F4"
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
/>
<path
fill="#34A853"
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
/>
<path
fill="#FBBC05"
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
/>
<path
fill="#EA4335"
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
/>
</svg>
<span className="text-sm font-medium text-gray-700">Google</span>
</button>
Expand Down Expand Up @@ -217,7 +226,8 @@ export default function LoginPage() {
<div
className="absolute inset-0 bg-cover bg-center"
style={{
backgroundImage: 'url(https://images.unsplash.com/photo-1522202176988-66273c2fd55f?q=80&w=2071&auto=format&fit=crop)',
backgroundImage:
'url(https://images.unsplash.com/photo-1522202176988-66273c2fd55f?q=80&w=2071&auto=format&fit=crop)',
}}
/>

Expand Down
66 changes: 37 additions & 29 deletions src/app/(auth)/signup/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { signupSchema, SignupFormData } from '../../lib/validationSchemas';
import { FormError, FieldError } from '../../../components/forms/FormError';
import { apiClient } from '@/lib/api';
import { parseApiError } from '@/utils/error-handler';

export default function SignupPage() {
const [isLoading, setIsLoading] = useState(false);
Expand All @@ -31,22 +33,11 @@ export default function SignupPage() {
setSuccessMessage('');

try {
const response = await fetch('/api/auth/signup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});

const result = await response.json();

if (!response.ok) {
throw new Error(result.message || 'Signup failed');
}

await apiClient.post('/api/auth/signup', data);
setSuccessMessage('Account created successfully! Redirecting...');
setTimeout(() => router.push('/dashboard'), 1500);
} catch (error) {
setApiError(error instanceof Error ? error.message : 'An error occurred');
setApiError(parseApiError(error).userMessage);
} finally {
setIsLoading(false);
}
Expand All @@ -71,8 +62,18 @@ export default function SignupPage() {
>
<div className="flex items-center justify-center gap-2 mb-8">
<div className="w-8 h-8 bg-gradient-to-r from-cyan-400 to-blue-500 rounded-lg flex items-center justify-center">
<svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
<svg
className="w-5 h-5 text-white"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"
/>
</svg>
</div>
<span className="text-xl font-semibold text-gray-900">TeachLink</span>
Expand All @@ -91,9 +92,7 @@ export default function SignupPage() {
className="space-y-5"
>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Full Name
</label>
<label className="block text-sm font-medium text-gray-700 mb-2">Full Name</label>
<input
type="text"
placeholder="John Doe"
Expand All @@ -104,9 +103,7 @@ export default function SignupPage() {
</div>

<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Email
</label>
<label className="block text-sm font-medium text-gray-700 mb-2">Email</label>
<input
type="email"
placeholder="[email protected]"
Expand All @@ -117,9 +114,7 @@ export default function SignupPage() {
</div>

<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Password
</label>
<label className="block text-sm font-medium text-gray-700 mb-2">Password</label>
<div className="relative">
<input
type={showPassword ? 'text' : 'password'}
Expand Down Expand Up @@ -189,10 +184,22 @@ export default function SignupPage() {
className="px-4 py-2.5 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors flex items-center justify-center gap-2"
>
<svg className="w-5 h-5" viewBox="0 0 24 24">
<path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" />
<path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" />
<path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" />
<path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" />
<path
fill="#4285F4"
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
/>
<path
fill="#34A853"
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
/>
<path
fill="#FBBC05"
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
/>
<path
fill="#EA4335"
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
/>
</svg>
<span className="text-sm font-medium text-gray-700">Google</span>
</button>
Expand Down Expand Up @@ -221,7 +228,8 @@ export default function SignupPage() {
<div
className="absolute inset-0 bg-cover bg-center"
style={{
backgroundImage: 'url(https://images.unsplash.com/photo-1522202176988-66273c2fd55f?q=80&w=2071&auto=format&fit=crop)',
backgroundImage:
'url(https://images.unsplash.com/photo-1522202176988-66273c2fd55f?q=80&w=2071&auto=format&fit=crop)',
}}
/>

Expand Down
75 changes: 57 additions & 18 deletions src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,59 @@ import { VideoPlayer } from './components/video/VideoPlayer';

// Sample transcript data
const sampleTranscript = [
{ time: 0, text: "Welcome to this comprehensive tutorial on React development.", speaker: "Instructor" },
{ time: 5, text: "Today we'll be covering advanced concepts that will help you build better applications.", speaker: "Instructor" },
{ time: 12, text: "Let's start with the fundamentals of component architecture.", speaker: "Instructor" },
{ time: 18, text: "Components are the building blocks of React applications.", speaker: "Instructor" },
{ time: 25, text: "They allow us to create reusable pieces of UI that can be composed together.", speaker: "Instructor" },
{ time: 32, text: "Now let's look at some practical examples.", speaker: "Instructor" },
{ time: 40, text: "Here's how you can create a functional component.", speaker: "Instructor" },
{ time: 48, text: "Notice how we use the useState hook to manage local state.", speaker: "Instructor" },
{ time: 55, text: "This is a powerful pattern that React provides for state management.", speaker: "Instructor" },
{ time: 62, text: "Let's move on to more advanced topics like custom hooks.", speaker: "Instructor" },
{ time: 70, text: "Custom hooks allow you to extract component logic into reusable functions.", speaker: "Instructor" },
{ time: 78, text: "This makes your code more modular and easier to test.", speaker: "Instructor" },
{ time: 85, text: "That concludes our tutorial. Thanks for watching!", speaker: "Instructor" }
{
time: 0,
text: 'Welcome to this comprehensive tutorial on React development.',
speaker: 'Instructor',
},
{
time: 5,
text: "Today we'll be covering advanced concepts that will help you build better applications.",
speaker: 'Instructor',
},
{
time: 12,
text: "Let's start with the fundamentals of component architecture.",
speaker: 'Instructor',
},
{
time: 18,
text: 'Components are the building blocks of React applications.',
speaker: 'Instructor',
},
{
time: 25,
text: 'They allow us to create reusable pieces of UI that can be composed together.',
speaker: 'Instructor',
},
{ time: 32, text: "Now let's look at some practical examples.", speaker: 'Instructor' },
{ time: 40, text: "Here's how you can create a functional component.", speaker: 'Instructor' },
{
time: 48,
text: 'Notice how we use the useState hook to manage local state.',
speaker: 'Instructor',
},
{
time: 55,
text: 'This is a powerful pattern that React provides for state management.',
speaker: 'Instructor',
},
{
time: 62,
text: "Let's move on to more advanced topics like custom hooks.",
speaker: 'Instructor',
},
{
time: 70,
text: 'Custom hooks allow you to extract component logic into reusable functions.',
speaker: 'Instructor',
},
{
time: 78,
text: 'This makes your code more modular and easier to test.',
speaker: 'Instructor',
},
{ time: 85, text: 'That concludes our tutorial. Thanks for watching!', speaker: 'Instructor' },
];

function App() {
Expand All @@ -35,11 +75,10 @@ function App() {
<div className="min-h-screen bg-gray-100 p-8">
<div className="max-w-6xl mx-auto">
<div className="mb-8">
<h1 className="text-3xl font-bold text-gray-900 mb-2">
Enhanced Video Player Demo
</h1>
<h1 className="text-3xl font-bold text-gray-900 mb-2">Enhanced Video Player Demo</h1>
<p className="text-gray-600">
A comprehensive video player with learning features including bookmarks, notes, and transcript synchronization.
A comprehensive video player with learning features including bookmarks, notes, and
transcript synchronization.
</p>
</div>

Expand Down Expand Up @@ -128,4 +167,4 @@ function App() {
);
}

export default App;
export default App;
8 changes: 2 additions & 6 deletions src/app/accessibility-demo/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,8 @@ export default function AccessibilityDemoPage() {
{/* Header */}
<header role="banner" className="bg-white shadow-sm">
<div className="max-w-7xl mx-auto px-4 py-6">
<h1 className="text-3xl font-bold text-gray-900">
Accessibility Features Demo
</h1>
<p className="mt-2 text-gray-600">
WCAG 2.1 AA Compliant Components and Tools
</p>
<h1 className="text-3xl font-bold text-gray-900">Accessibility Features Demo</h1>
<p className="mt-2 text-gray-600">WCAG 2.1 AA Compliant Components and Tools</p>
</div>
</header>

Expand Down
Loading
Loading