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
3 changes: 2 additions & 1 deletion .claude/settings.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"allow": [
"Bash(npm run dev:*)",
"Bash(npm run build:*)",
"Bash(timeout 30 npm run dev:*)"
"Bash(timeout 30 npm run dev:*)",
"Bash(cat:*)"
],
"deny": [],
"ask": []
Expand Down
119 changes: 119 additions & 0 deletions DATABASE_SETUP_REQUIRED.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# 🚨 DATABASE SETUP REQUIRED

## Current Problem

Your application shows these errors:
- ❌ "Error fetching submissions"
- ❌ "Error fetching task resources"
- ❌ File uploads are broken

**Root cause:** Database tables `submissions` and `task_resources` don't exist yet.

---

## Solution: Run Migrations in Supabase Dashboard

### Step-by-Step Instructions:

1. **Open Supabase Dashboard**
- Go to: https://app.supabase.com
- Select your project
- Click on **SQL Editor** in the left menu

2. **Run These Migrations in Order:**

**Migration 003** - Submissions Table
```
📁 Open: supabase/migrations/003_add_submissions.sql
➡️ Copy all contents → Paste in SQL Editor → Click "Run"
```

**Migration 004** - Enrollment Fields
```
📁 Open: supabase/migrations/004_add_enrollment_fields.sql
➡️ Copy all contents → Paste in SQL Editor → Click "Run"
```

**Migration 005** - Task Resources Table
```
📁 Open: supabase/migrations/005_add_task_resources.sql
➡️ Copy all contents → Paste in SQL Editor → Click "Run"
```

**Migration 006** - Storage Setup + Cleanup ⭐ **IMPORTANT**
```
📁 Open: supabase/migrations/006_setup_storage_and_cleanup.sql
➡️ Copy all contents → Paste in SQL Editor → Click "Run"
```

3. **Verify Everything Worked**
```
📁 Open: supabase/verify_migrations.sql
➡️ Copy all contents → Paste in SQL Editor → Click "Run"
✅ You should see all checks showing green checkmarks
```

4. **Refresh Your Application**
- Reload the page in your browser
- Errors should be gone
- File uploads should work

---

## What Gets Fixed:

✅ Creates `submissions` table for student file uploads
✅ Creates `task_resources` table for teacher resource uploads
✅ Creates storage buckets: `submissions` and `task-resources`
✅ Sets up all security policies automatically
✅ Removes deadline functionality (cleaned up)
✅ Adds submission fields to enrollments
✅ Enables file upload error handling with detailed feedback

---

## After Setup:

### Test File Uploads:

**As a Teacher:**
1. Create a new homework/task
2. Upload resource files (PDF, images, etc.)
3. ✅ Should see success message with file name

**As a Student:**
1. Enroll in a homework
2. Upload submission files
3. ✅ Should see success message with file name

---

## Need Help?

**If migrations fail:**
- Make sure you're using the project owner account in Supabase
- Try running one migration at a time
- Check the error message in SQL Editor

**If storage buckets don't create:**
- Go to Storage section in Supabase dashboard
- Manually create two buckets:
- Name: `submissions` (make it public)
- Name: `task-resources` (make it public)
- Then re-run migration 006

**Still having issues?**
- Check `supabase/apply-migrations.md` for detailed troubleshooting
- Run `supabase/verify_migrations.sql` to see what's missing

---

## Files Created:

- ✅ `supabase/migrations/006_setup_storage_and_cleanup.sql` - New migration
- ✅ `supabase/verify_migrations.sql` - Verification script
- ✅ `supabase/apply-migrations.md` - Updated with latest instructions
- ✅ File upload error handling improved in:
- `frontend/app/dashboard/teacher/create-homework/page.tsx`
- `frontend/app/dashboard/teacher/homework/[id]/page.tsx`
- `frontend/app/dashboard/student/homework/[id]/page.tsx`
28 changes: 24 additions & 4 deletions frontend/app/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,18 @@ export default function DashboardPage() {

if (loading) {
return (
<div className="flex items-center justify-center min-h-[calc(100vh-4rem)]">
<Loader2 className="w-8 h-8 animate-spin text-blue-600" />
<div className="flex items-center justify-center min-h-[calc(100vh-4rem)] bg-gradient-to-br from-blue-50 via-purple-50 to-pink-50 dark:from-zinc-950 dark:via-purple-950/20 dark:to-blue-950/20 relative overflow-hidden">
<div className="absolute inset-0 overflow-hidden pointer-events-none">
<div className="absolute top-20 left-10 w-72 h-72 bg-gradient-to-r from-blue-400/20 to-purple-400/20 rounded-full blur-3xl animate-pulse" style={{ animationDuration: '3s' }} />
<div className="absolute bottom-20 right-10 w-80 h-80 bg-gradient-to-r from-pink-400/20 to-orange-400/20 rounded-full blur-3xl animate-pulse" style={{ animationDuration: '4s' }} />
</div>
<div className="relative z-10">
<div className="relative">
<Loader2 className="w-16 h-16 animate-spin text-transparent bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text" />
<div className="absolute -inset-4 bg-gradient-to-r from-blue-500/20 to-purple-500/20 rounded-full blur-xl animate-pulse" />
</div>
<p className="mt-4 text-zinc-600 dark:text-zinc-400 font-medium">Loading your dashboard...</p>
</div>
</div>
);
}
Expand All @@ -77,8 +87,18 @@ export default function DashboardPage() {

// Show loading while redirecting
return (
<div className="flex items-center justify-center min-h-[calc(100vh-4rem)]">
<Loader2 className="w-8 h-8 animate-spin text-blue-600" />
<div className="flex items-center justify-center min-h-[calc(100vh-4rem)] bg-gradient-to-br from-blue-50 via-purple-50 to-pink-50 dark:from-zinc-950 dark:via-purple-950/20 dark:to-blue-950/20 relative overflow-hidden">
<div className="absolute inset-0 overflow-hidden pointer-events-none">
<div className="absolute top-20 left-10 w-72 h-72 bg-gradient-to-r from-blue-400/20 to-purple-400/20 rounded-full blur-3xl animate-pulse" style={{ animationDuration: '3s' }} />
<div className="absolute bottom-20 right-10 w-80 h-80 bg-gradient-to-r from-pink-400/20 to-orange-400/20 rounded-full blur-3xl animate-pulse" style={{ animationDuration: '4s' }} />
</div>
<div className="relative z-10">
<div className="relative">
<Loader2 className="w-16 h-16 animate-spin text-transparent bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text" />
<div className="absolute -inset-4 bg-gradient-to-r from-blue-500/20 to-purple-500/20 rounded-full blur-xl animate-pulse" />
</div>
<p className="mt-4 text-zinc-600 dark:text-zinc-400 font-medium">Redirecting...</p>
</div>
</div>
);
}
82 changes: 65 additions & 17 deletions frontend/app/dashboard/student/homework/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@ import { Upload, FileText, CheckCircle, MessageCircle, Loader2, ArrowLeft, Downl
import { Input } from '@/components/ui/input';
import Link from 'next/link';
import { createSupabaseBrowserClient } from '@/lib/supabase/client';
import { useToast } from '@/components/ui/toast';
import { LoadingPage } from '@/components/ui/loading-spinner';

const supabase = createSupabaseBrowserClient();

export default function StudentHomeworkPage() {
const { address, isConnected } = useAccount();
const router = useRouter();
const params = useParams();
const toast = useToast();
const homeworkId = params.id as string;

const [profile, setProfile] = useState<any>(null);
Expand Down Expand Up @@ -74,7 +77,7 @@ export default function StudentHomeworkPage() {
});

if (enrollmentsData.length === 0) {
alert('You are not enrolled in this task');
toast.warning('Not enrolled', 'You are not enrolled in this task');
router.push('/dashboard/student');
return;
}
Expand Down Expand Up @@ -105,15 +108,15 @@ export default function StudentHomeworkPage() {
setTaskResources(resourcesData);
} catch (error: any) {
console.error('Error loading data:', error);
alert('Error loading task details');
toast.error('Loading failed', 'Error loading task details');
router.push('/dashboard/student');
} finally {
setLoading(false);
}
}

loadData();
}, [address, isConnected, router, homeworkId]);
}, [address, isConnected, router, homeworkId, toast]);

async function handleSubmitSolution() {
if (!enrollment || !submissionText.trim()) return;
Expand All @@ -139,10 +142,10 @@ export default function StudentHomeworkPage() {
});

setEnrollment(enrollmentsData[0]);
alert('Solution submitted successfully! ✅ Your teacher will review it soon.');
toast.success('Solution submitted!', 'Your teacher will review it soon.');
} catch (error: any) {
console.error('Error submitting solution:', error);
alert('Error submitting solution. Please try again.');
toast.error('Submission failed', 'Error submitting solution. Please try again.');
} finally {
setSubmitting(false);
}
Expand All @@ -155,14 +158,28 @@ export default function StudentHomeworkPage() {
try {
// Upload file to Supabase Storage
const fileExt = uploadedFile.name.split('.').pop();
const fileName = `${profile.id}/${enrollment.id}/${Date.now()}.${fileExt}`;
const fileName = `${profile.id}/${enrollment.id}/${Date.now()}_${Math.random().toString(36).substring(7)}.${fileExt}`;

const { data: uploadData, error: uploadError } = await supabase
.storage
.from('submissions')
.upload(fileName, uploadedFile);

if (uploadError) throw uploadError;
if (uploadError) {
console.error('Storage upload error:', uploadError);
let errorMessage = 'Failed to upload file to storage.';

if (uploadError.message.includes('row-level security')) {
errorMessage = 'Storage access denied. Please check your permissions.';
} else if (uploadError.message.includes('size')) {
errorMessage = 'File is too large. Maximum file size is 50MB.';
} else if (uploadError.message) {
errorMessage = `Upload failed: ${uploadError.message}`;
}

alert(`❌ ${errorMessage}\n\nFile: ${uploadedFile.name}\n\nPlease try again or contact your teacher if the problem persists.`);
return;
}

// Get public URL
const { data: { publicUrl } } = supabase
Expand All @@ -171,14 +188,22 @@ export default function StudentHomeworkPage() {
.getPublicUrl(fileName);

// Create submission record
await createSubmission({
enrollment_id: enrollment.id,
student_id: profile.id,
homework_id: homeworkId,
file_url: publicUrl,
file_name: uploadedFile.name,
file_type: uploadedFile.type,
});
try {
await createSubmission({
enrollment_id: enrollment.id,
student_id: profile.id,
homework_id: homeworkId,
file_url: publicUrl,
file_name: uploadedFile.name,
file_type: uploadedFile.type,
});
} catch (dbError: any) {
console.error('Database error:', dbError);
// If DB insert fails, try to clean up the uploaded file
await supabase.storage.from('submissions').remove([fileName]);
alert(`❌ Failed to save submission to database.\n\nFile: ${uploadedFile.name}\nError: ${dbError.message || 'Unknown error'}\n\nPlease try again or contact your teacher.`);
return;
}

// Reload submissions
const submissionsData = await getSubmissions({
Expand All @@ -187,10 +212,33 @@ export default function StudentHomeworkPage() {
setSubmissions(submissionsData);

setUploadedFile(null);
alert('File uploaded successfully! ✅');
<<<<<<< HEAD
<<<<<<< HEAD
toast.success('File uploaded!', 'Your file has been uploaded successfully.');
} catch (error: any) {
console.error('Error uploading file:', error);
toast.error('Upload failed', 'Error uploading file. Please try again.');
=======
=======
>>>>>>> c89133b (css & animations)
// Reset file input
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
if (fileInput) fileInput.value = '';

alert(`✅ File uploaded successfully!\n\n${uploadedFile.name}\n\nYour teacher will be able to see this file when reviewing your submission.`);
} catch (error: any) {
console.error('Unexpected error uploading file:', error);
alert(`❌ Unexpected error uploading file.\n\nFile: ${uploadedFile.name}\nPlease try again or contact your teacher if the problem persists.`);
<<<<<<< HEAD
>>>>>>> 71a7763 (versiune care merge)
=======
=======
toast.success('File uploaded!', 'Your file has been uploaded successfully.');
} catch (error: any) {
console.error('Error uploading file:', error);
alert('Error uploading file. Please try again.');
toast.error('Upload failed', 'Error uploading file. Please try again.');
>>>>>>> 8ec33f1 (css & animations)
>>>>>>> c89133b (css & animations)
} finally {
setUploading(false);
}
Expand Down
Loading