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
3,235 changes: 1,215 additions & 2,020 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 1 addition & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"type": "module",
"scripts": {
"dev": "next dev --turbopack",
"dev": "next dev",
"build": "next build",
"start": "next start",
"type-check": "tsc --noEmit",
Expand All @@ -19,9 +19,6 @@
"test:coverage": "vitest run --coverage",
"test:ui": "vitest --ui",
"test:watch": "vitest --watch",
"type-check": "tsc --noEmit",
"validate:ui": "node scripts/validate-ui.js",
"validate:web3": "node scripts/validate-web3.js",
"validate": "npm run validate:ui && npm run validate:web3"
},
"dependencies": {
Expand Down
6 changes: 3 additions & 3 deletions src/app/(auth)/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export default function LoginPage() {
className="mb-8"
>
<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">
<div className="w-8 h-8 bg-linear-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"
Expand Down Expand Up @@ -146,7 +146,7 @@ export default function LoginPage() {
<button
type="submit"
disabled={isLoading}
className="w-full py-3 bg-gradient-to-r from-cyan-400 to-blue-500 text-white font-semibold rounded-lg hover:from-cyan-500 hover:to-blue-600 transition-all disabled:opacity-50 disabled:cursor-not-allowed"
className="w-full py-3 bg-linear-to-r from-cyan-400 to-blue-500 text-white font-semibold rounded-lg hover:from-cyan-500 hover:to-blue-600 transition-all disabled:opacity-50 disabled:cursor-not-allowed"
>
{isLoading ? 'Signing in...' : 'Sign in'}
</button>
Expand Down Expand Up @@ -259,7 +259,7 @@ export default function LoginPage() {
>
<Link
href="/signup"
className="inline-block px-8 py-3 bg-gradient-to-r from-cyan-400 to-blue-500 text-white font-semibold rounded-lg hover:from-cyan-500 hover:to-blue-600 transition-all"
className="inline-block px-8 py-3 bg-linear-to-r from-cyan-400 to-blue-500 text-white font-semibold rounded-lg hover:from-cyan-500 hover:to-blue-600 transition-all"
>
Create an account
</Link>
Expand Down
12 changes: 9 additions & 3 deletions src/app/components/courses/CourseCard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// CourseCard.tsx
import { Play } from 'lucide-react'; // or your preferred icon library
import Image from 'next/image';

interface CourseCardProps {
title: string;
Expand All @@ -24,13 +25,18 @@ export default function CourseCard({
<div className="relative h-48 w-full overflow-hidden bg-gray-900">
{imageUrl ? (
// eslint-disable-next-line @next/next/no-img-element
<img

<Image
src={imageUrl}
alt={title}
className="h-full w-full object-cover transition-transform duration-300 group-hover:scale-105"
fill
sizes="(max-width: 768px) 100vw, 33vw"
className="object-cover transition-transform duration-300 group-hover:scale-105"
placeholder="blur"
blurDataURL="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0nMTAnIGhlaWdodD0nMTAnIHhtbG5zPSciaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPjwvc3ZnPg=="
/>
) : (
<div className="h-full w-full bg-gradient-to-br from-gray-800 via-gray-900 to-gray-800" />
<div className="h-full w-full bg-linear-to-br from-gray-800 via-gray-900 to-gray-800" />
)}
</div>

Expand Down
23 changes: 15 additions & 8 deletions src/app/components/courses/CourseReviews.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';

import { useState } from 'react';
import Image from 'next/image';

interface Review {
id: string;
Expand Down Expand Up @@ -71,9 +72,8 @@ export default function CourseReviews({
{[1, 2, 3, 4, 5].map((star) => (
<svg
key={star}
className={`w-5 h-5 ${
star <= rating ? 'text-yellow-400' : 'text-[#E2E8F0] dark:text-[#334155]'
}`}
className={`w-5 h-5 ${star <= rating ? 'text-yellow-400' : 'text-[#E2E8F0] dark:text-[#334155]'
}`}
fill="currentColor"
viewBox="0 0 20 20"
>
Expand Down Expand Up @@ -139,11 +139,18 @@ export default function CourseReviews({
>
<div className="flex items-start gap-4">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={review.userAvatar}
alt={review.userName}
className="w-12 h-12 rounded-full object-cover"
/>

<div className="relative w-12 h-12 rounded-full overflow-hidden">
<Image
src={review.userAvatar}
alt={review.userName}
fill
sizes="48px"
className="object-cover"
placeholder="blur"
blurDataURL="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0nMTAnIGhlaWdodD0nMTAnIHhtbG5zPSciaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPjwvc3ZnPg=="
/>
</div>
<div className="flex-1">
<div className="flex items-start justify-between mb-2">
<div>
Expand Down
6 changes: 3 additions & 3 deletions src/app/components/courses/CourseSyllabus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export default function CourseSyllabus({
</p>
</div>
<svg
className={`w-5 h-5 text-[#64748B] dark:text-[#94A3B8] transition-transform duration-200 ml-4 flex-shrink-0 ${
className={`w-5 h-5 text-[#64748B] dark:text-[#94A3B8] transition-transform duration-200 ml-4 shrink-0 ${
expandedSections.has(section.id) ? 'rotate-90' : ''
}`}
fill="currentColor"
Expand All @@ -100,7 +100,7 @@ export default function CourseSyllabus({
>
<div className="flex items-center gap-3 flex-1">
<svg
className="w-5 h-5 text-[#0066FF] dark:text-[#00C2FF] flex-shrink-0"
className="w-5 h-5 text-[#0066FF] dark:text-[#00C2FF] shrink-0"
fill="currentColor"
viewBox="0 0 20 20"
>
Expand All @@ -115,7 +115,7 @@ export default function CourseSyllabus({
</span>
)}
</div>
<span className="text-sm text-[#64748B] dark:text-[#94A3B8] ml-4 flex-shrink-0">
<span className="text-sm text-[#64748B] dark:text-[#94A3B8] ml-4 shrink-0">
{lesson.duration}
</span>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/courses/EnrollmentCTA.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export default function EnrollmentCTA({
{option.features.map((feature, index) => (
<li key={index} className="flex items-start gap-2 text-sm">
<svg
className="w-5 h-5 text-green-500 dark:text-green-400 flex-shrink-0 mt-0.5"
className="w-5 h-5 text-green-500 dark:text-green-400 shrink-0 mt-0.5"
fill="currentColor"
viewBox="0 0 20 20"
>
Expand Down
14 changes: 13 additions & 1 deletion src/app/components/courses/VideoPreview.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';

import { useState } from 'react';
import Image from 'next/image';

interface VideoPreviewProps {
videoUrl?: string;
Expand All @@ -23,7 +24,18 @@ export default function VideoPreview({
className="group relative w-full aspect-video rounded-xl overflow-hidden border-2 border-[#E2E8F0] dark:border-[#334155] hover:border-[#0066FF] dark:hover:border-[#00C2FF] transition-all duration-200"
>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img src={thumbnailUrl} alt="Video preview" className="w-full h-full object-cover" />
<div className="absolute inset-0">
<Image
src={thumbnailUrl}
alt="Video preview"
fill
sizes="100vw"
className="object-cover"
placeholder="blur"
blurDataURL="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0nMTAnIGhlaWdodD0nMTAnIHhtbG5zPSciaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPjwvc3ZnPg=="
priority={false}
/>
</div>
<div className="absolute inset-0 bg-black/40 group-hover:bg-black/50 transition-colors flex items-center justify-center">
<div className="w-16 h-16 rounded-full bg-white/90 group-hover:bg-white group-hover:scale-110 transition-all duration-200 flex items-center justify-center">
<svg className="w-8 h-8 text-[#0066FF] ml-1" fill="currentColor" viewBox="0 0 20 20">
Expand Down
15 changes: 13 additions & 2 deletions src/app/components/dashboard/widgets/RecommendedCoursesWidget.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react';
import { motion } from 'framer-motion';
import Image from 'next/image';
import { BookOpen, Star, Clock, Users, Settings } from 'lucide-react';

interface Course {
Expand Down Expand Up @@ -228,9 +229,19 @@ export const RecommendedCoursesWidget: React.FC<RecommendedCoursesWidgetProps> =
className="border border-gray-200 rounded-lg overflow-hidden hover:shadow-md transition-shadow cursor-pointer"
>
<div className="flex">
<div className="w-24 h-20 bg-gray-200 flex-shrink-0">
<div className="w-24 h-20 bg-gray-200 shrink-0">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img src={course.image} alt={course.title} className="w-full h-full object-cover" />

<Image
src={course.image}
alt={course.title}
fill
sizes="96px"
className="object-cover"
placeholder="blur"
blurDataURL="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0nMTAnIGhlaWdodD0nMTAnIHhtbG5zPSciaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPjwvc3ZnPg=="
/>

</div>
<div className="flex-1 p-3">
<h4 className="font-medium text-gray-900 text-sm line-clamp-2 mb-1">
Expand Down
55 changes: 28 additions & 27 deletions src/app/components/messaging/MessageThread.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';

import { useEffect, useRef, useCallback } from 'react';
import Image from 'next/image';
import { format, isToday, isYesterday, isSameDay } from 'date-fns';
import {
FiCheck,
Expand Down Expand Up @@ -74,11 +75,17 @@ function AttachmentPreview({ attachment }: { attachment: Attachment }) {
{isImage ? (
<div className="relative group">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={attachment.url}
alt={attachment.name}
className="max-w-[240px] max-h-[180px] rounded-lg object-cover"
/>
<div className="relative w-60 h-45 rounded-lg overflow-hidden">
<Image
src={attachment.url}
alt={attachment.name}
fill
sizes="240px"
className="object-cover"
placeholder="blur"
blurDataURL="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0nMTAnIGhlaWdodD0nMTAnIHhtbG5zPSciaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPjwvc3ZnPg=="
/>
</div>
<div className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity rounded-lg flex items-center justify-center">
<a
href={attachment.url}
Expand Down Expand Up @@ -140,11 +147,11 @@ function TypingIndicator({ text }: { text: string }) {
function DateDivider({ date }: { date: Date }) {
return (
<div className="flex items-center gap-4 my-6">
<div className="flex-1 h-px bg-gradient-to-r from-transparent via-gray-200 dark:via-gray-700 to-transparent" />
<div className="flex-1 h-px bg-linear-to-r from-transparent via-gray-200 dark:via-gray-700 to-transparent" />
<span className="text-xs font-medium text-gray-400 dark:text-gray-500 px-3 py-1 bg-gray-50 dark:bg-gray-800/50 rounded-full">
{formatMessageDate(date)}
</span>
<div className="flex-1 h-px bg-gradient-to-r from-transparent via-gray-200 dark:via-gray-700 to-transparent" />
<div className="flex-1 h-px bg-linear-to-r from-transparent via-gray-200 dark:via-gray-700 to-transparent" />
</div>
);
}
Expand Down Expand Up @@ -198,7 +205,7 @@ export default function MessageThread({
return (
<div className="flex-1 flex items-center justify-center">
<div className="text-center max-w-sm">
<div className="w-24 h-24 mx-auto mb-6 rounded-full bg-gradient-to-br from-violet-100 to-purple-100 dark:from-violet-900/20 dark:to-purple-900/20 flex items-center justify-center">
<div className="w-24 h-24 mx-auto mb-6 rounded-full bg-linear-to-br from-violet-100 to-purple-100 dark:from-violet-900/20 dark:to-purple-900/20 flex items-center justify-center">
<svg
className="w-12 h-12 text-violet-500"
fill="none"
Expand Down Expand Up @@ -230,7 +237,7 @@ export default function MessageThread({
{/* Chat Header */}
<div className="flex items-center gap-3 px-6 py-4 border-b border-gray-100 dark:border-gray-800 bg-white/80 dark:bg-gray-900/80 backdrop-blur-sm">
<div className="relative">
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-violet-500 to-purple-600 flex items-center justify-center text-white text-sm font-semibold">
<div className="w-10 h-10 rounded-full bg-linear-to-br from-violet-500 to-purple-600 flex items-center justify-center text-white text-sm font-semibold">
{participantName
.split(' ')
.map((n) => n[0])
Expand Down Expand Up @@ -281,11 +288,10 @@ export default function MessageThread({
{[...Array(4)].map((_, i) => (
<div key={i} className={`flex ${i % 2 === 0 ? 'justify-start' : 'justify-end'}`}>
<div
className={`rounded-2xl animate-pulse ${
i % 2 === 0
className={`rounded-2xl animate-pulse ${i % 2 === 0
? 'bg-gray-200 dark:bg-gray-700'
: 'bg-violet-200 dark:bg-violet-800/50'
}`}
}`}
style={{
width: `${150 + Math.random() * 200}px`,
height: `${40 + Math.random() * 30}px`,
Expand All @@ -310,9 +316,8 @@ export default function MessageThread({
<div key={message.id} data-message-id={message.id}>
{showDateDivider && <DateDivider date={new Date(message.timestamp)} />}
<div
className={`flex items-end gap-2 mb-1 ${
isOwn ? 'justify-end' : 'justify-start'
} animate-fadeIn`}
className={`flex items-end gap-2 mb-1 ${isOwn ? 'justify-end' : 'justify-start'
} animate-fadeIn`}
>
{/* Avatar Space */}
{!isOwn && (
Expand All @@ -332,20 +337,18 @@ export default function MessageThread({

{/* Message Bubble */}
<div
className={`max-w-[70%] rounded-2xl px-4 py-2.5 shadow-sm transition-all duration-200 hover:shadow-md ${
isOwn
className={`max-w-[70%] rounded-2xl px-4 py-2.5 shadow-sm transition-all duration-200 hover:shadow-md ${isOwn
? 'bg-gradient-to-br from-violet-500 to-purple-600 text-white rounded-br-md'
: 'bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-bl-md'
}`}
}`}
>
{/* Message Content */}
{message.content && (
<div
className={`text-sm leading-relaxed break-words ${
isOwn
className={`text-sm leading-relaxed break-words ${isOwn
? '[&_p]:text-white [&_strong]:text-white [&_li]:text-white/90 [&_a]:text-violet-200 [&_a]:underline'
: '[&_a]:text-violet-600 [&_a]:underline'
}`}
}`}
dangerouslySetInnerHTML={{
__html: message.content,
}}
Expand All @@ -359,14 +362,12 @@ export default function MessageThread({

{/* Timestamp & Read Receipt */}
<div
className={`flex items-center gap-1 mt-1 ${
isOwn ? 'justify-end' : 'justify-start'
}`}
className={`flex items-center gap-1 mt-1 ${isOwn ? 'justify-end' : 'justify-start'
}`}
>
<span
className={`text-[10px] ${
isOwn ? 'text-white/60' : 'text-gray-400 dark:text-gray-500'
}`}
className={`text-[10px] ${isOwn ? 'text-white/60' : 'text-gray-400 dark:text-gray-500'
}`}
>
{format(new Date(message.timestamp), 'h:mm a')}
</span>
Expand Down
2 changes: 0 additions & 2 deletions src/app/components/quizzes/QuizContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { useEffect } from 'react';
import { useRouter } from 'next/navigation';
import Countdown from 'react-countdown';
import { FaArrowLeft, FaArrowRight, FaCheck } from 'react-icons/fa';
import { useQuizStore } from '@/store/quizStore';
import type { Quiz } from '@/store/quizStore';
import { Quiz, useQuizStore } from '@/app/store/quizStore';
import QuestionCard from './QuestionCard';

Expand Down
Loading
Loading