Skip to content

Commit 4c97d78

Browse files
committed
update
1 parent 7411e1a commit 4c97d78

File tree

5 files changed

+367
-122
lines changed

5 files changed

+367
-122
lines changed

app/models/course.server.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { prisma } from "~/db.server";
2+
import type { Course } from "@prisma/client";
23

34
export async function getCourseListItems({ userId }: { userId: string }) {
45
return prisma.course.findMany({
@@ -22,16 +23,19 @@ export async function createCourse({
2223
title,
2324
description,
2425
userId,
26+
structure,
2527
}: {
2628
title: string;
2729
description?: string;
2830
userId: string;
29-
}) {
31+
structure?: string | null; // Explicitly define structure type as string or null
32+
}): Promise<Course> { // Explicitly define return type as Course
3033
return prisma.course.create({
3134
data: {
3235
title,
3336
description: description || "",
3437
userId,
38+
structure, // Include structure in the data
3539
},
3640
});
3741
}

app/routes/courses.new.tsx

Lines changed: 128 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { json, redirect } from "@remix-run/node";
33
import { Form, useActionData, useNavigation } from "@remix-run/react";
44
import { requireUserId } from "~/session.server";
55
import { createCourse } from "~/models/course.server";
6-
import React from 'react';
6+
import React, { useState } from 'react';
7+
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
78

89
export const loader = async ({ request }: LoaderFunctionArgs) => {
910
await requireUserId(request);
@@ -15,62 +16,175 @@ export const action = async ({ request }: ActionFunctionArgs) => {
1516
const formData = await request.formData();
1617
const title = formData.get("title") as string;
1718
const description = formData.get("description") as string | null;
18-
19+
const structure = JSON.parse(formData.get("structure") as string) || [];
20+
console.log('Structure from form data:', structure);
21+
1922
if (!title) {
2023
return json({ error: "Title is required" }, { status: 400 });
2124
}
2225

2326
try {
24-
await createCourse({ title, description: description || "", userId });
27+
console.log('Structure being passed to createCourse:', structure);
28+
console.log('Structure being passed to createCourse:', structure);
29+
await createCourse({ title, description: description || "", userId, structure });
2530
return redirect("/courses");
2631
} catch (error) {
2732
return json({ error: "Failed to create course" }, { status: 500 });
2833
}
2934
};
3035

36+
type CourseItem = {
37+
id: string;
38+
content: string;
39+
type: 'topic' | 'lesson' | 'quiz';
40+
};
41+
3142
export default function NewCoursePage() {
3243
const actionData = useActionData<typeof action>();
3344
const navigation = useNavigation();
45+
const [courseTitle, setCourseTitle] = useState('');
46+
const [courseDescription, setCourseDescription] = useState('');
47+
const [courseItems, setCourseItems] = useState<CourseItem[]>([]);
48+
const [points, setPoints] = useState(0); // Gamification: Points for actions
49+
50+
const addItem = (type: CourseItem['type']) => {
51+
const newItem: CourseItem = {
52+
id: `item-${Date.now()}`,
53+
content: `New ${type.charAt(0).toUpperCase() + type.slice(1)}`,
54+
type,
55+
};
56+
setCourseItems([...courseItems, newItem]);
57+
setPoints(points + 10); // Add points for creating new content
58+
};
59+
60+
const onDragEnd = (result: any) => {
61+
if (!result.destination) return;
62+
63+
const newItems = Array.from(courseItems);
64+
const [reorderedItem] = newItems.splice(result.source.index, 1);
65+
newItems.splice(result.destination.index, 0, reorderedItem);
66+
67+
setCourseItems(newItems);
68+
setPoints(points + 5); // Add points for reordering
69+
};
3470

3571
return (
36-
<div className="flex-1 p-6">
37-
<h1 className="text-2xl font-bold mb-4">New Course</h1>
38-
<Form method="post" className="space-y-4 max-w-md">
72+
<div className="flex-1 p-6 bg-gray-100 min-h-screen">
73+
<h1 className="text-3xl font-bold mb-6 text-gray-800">Create New Course</h1>
74+
<Form method="post" className="space-y-6 max-w-2xl bg-white p-6 rounded-lg shadow-md" onSubmit={() => {
75+
const formData = new FormData();
76+
formData.append('title', courseTitle);
77+
formData.append('description', courseDescription);
78+
formData.append('structure', JSON.stringify(courseItems));
79+
}}>
3980
<div>
40-
<label htmlFor="title" className="block text-sm font-medium text-gray-700">
41-
Title
81+
<label htmlFor="title" className="block text-lg font-medium text-gray-700 mb-2">
82+
Course Title
4283
</label>
4384
<input
4485
type="text"
4586
id="title"
4687
name="title"
4788
required
48-
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
49-
defaultValue=""
89+
value={courseTitle}
90+
onChange={(e) => setCourseTitle(e.target.value)}
91+
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 p-2"
92+
placeholder="Enter course title"
5093
/>
5194
</div>
5295

5396
<div>
54-
<label htmlFor="description" className="block text-sm font-medium text-gray-700">
97+
<label htmlFor="description" className="block text-lg font-medium text-gray-700 mb-2">
5598
Description (Optional)
5699
</label>
57100
<textarea
58101
id="description"
59102
name="description"
60103
rows={4}
61-
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
62-
defaultValue=""
104+
value={courseDescription}
105+
onChange={(e) => setCourseDescription(e.target.value)}
106+
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 p-2"
107+
placeholder="Enter course description"
63108
/>
64109
</div>
65110

111+
<div className="mb-6">
112+
<h2 className="text-xl font-semibold text-gray-700 mb-4">Course Structure</h2>
113+
<div className="space-y-4">
114+
<button
115+
type="button"
116+
onClick={() => addItem('topic')}
117+
className="bg-green-500 text-white py-2 px-4 rounded hover:bg-green-600"
118+
>
119+
Add Topic
120+
</button>
121+
<button
122+
type="button"
123+
onClick={() => addItem('lesson')}
124+
className="bg-blue-500 text-white py-2 px-4 rounded hover:bg-blue-600 ml-2"
125+
>
126+
Add Lesson
127+
</button>
128+
<button
129+
type="button"
130+
onClick={() => addItem('quiz')}
131+
className="bg-purple-500 text-white py-2 px-4 rounded hover:bg-purple-600 ml-2"
132+
>
133+
Add Quiz
134+
</button>
135+
</div>
136+
137+
<DragDropContext onDragEnd={onDragEnd}>
138+
<Droppable droppableId="droppable">
139+
{(provided) => (
140+
<div
141+
{...provided.droppableProps}
142+
ref={provided.innerRef}
143+
className="mt-4 space-y-4"
144+
>
145+
{courseItems.map((item, index) => (
146+
<Draggable key={item.id} draggableId={item.id} index={index}>
147+
{(provided) => (
148+
<div
149+
ref={provided.innerRef}
150+
{...provided.draggableProps}
151+
{...provided.dragHandleProps}
152+
className="p-4 bg-gray-200 rounded shadow-md flex justify-between items-center"
153+
>
154+
<span>{item.content}</span>
155+
<button
156+
type="button"
157+
onClick={() => setCourseItems(courseItems.filter(i => i.id !== item.id))}
158+
className="text-red-500 hover:text-red-700"
159+
>
160+
Remove
161+
</button>
162+
</div>
163+
)}
164+
</Draggable>
165+
))}
166+
{provided.placeholder}
167+
</div>
168+
)}
169+
</Droppable>
170+
</DragDropContext>
171+
</div>
172+
173+
{/* Gamification Display */}
174+
<div className="mt-6 p-4 bg-yellow-100 rounded-lg">
175+
<h3 className="text-lg font-semibold text-gray-800">Your Progress</h3>
176+
<p className="text-gray-700">Points: {points}</p>
177+
{points >= 50 && <span className="text-green-600">Badge Unlocked: Course Creator!</span>}
178+
</div>
179+
66180
{actionData?.error && (
67181
<p className="text-red-500 text-sm">{actionData.error}</p>
68182
)}
69183

70184
<button
71185
type="submit"
72186
disabled={navigation.state === "submitting"}
73-
className="w-full py-2 px-4 bg-blue-500 text-white rounded-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
187+
className="w-full py-3 px-6 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition duration-300"
74188
>
75189
{navigation.state === "submitting" ? "Creating..." : "Create Course"}
76190
</button>

0 commit comments

Comments
 (0)