Spaces:
Sleeping
Sleeping
fixing MCP quiz
Browse files- app/api/data/route.ts +48 -3
- app/components/Calendar.tsx +51 -49
- app/components/Messages.tsx +1 -1
app/api/data/route.ts
CHANGED
|
@@ -53,13 +53,25 @@ export async function GET(request: NextRequest) {
|
|
| 53 |
const stats = await stat(itemPath)
|
| 54 |
const relativePath = path.relative(userDir, itemPath)
|
| 55 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
return {
|
| 57 |
name: item,
|
| 58 |
type: stats.isDirectory() ? 'folder' : 'file',
|
| 59 |
size: stats.size,
|
| 60 |
modified: stats.mtime.toISOString(),
|
| 61 |
path: relativePath,
|
| 62 |
-
extension: path.extname(item).substring(1)
|
|
|
|
| 63 |
}
|
| 64 |
}))
|
| 65 |
|
|
@@ -72,6 +84,39 @@ export async function GET(request: NextRequest) {
|
|
| 72 |
|
| 73 |
export async function POST(request: NextRequest) {
|
| 74 |
try {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
const formData = await request.formData()
|
| 76 |
const file = formData.get('file') as File
|
| 77 |
const key = formData.get('key') as string
|
|
@@ -99,8 +144,8 @@ export async function POST(request: NextRequest) {
|
|
| 99 |
|
| 100 |
return NextResponse.json({ success: true })
|
| 101 |
} catch (error) {
|
| 102 |
-
console.error('Error
|
| 103 |
-
return NextResponse.json({ error: '
|
| 104 |
}
|
| 105 |
}
|
| 106 |
|
|
|
|
| 53 |
const stats = await stat(itemPath)
|
| 54 |
const relativePath = path.relative(userDir, itemPath)
|
| 55 |
|
| 56 |
+
// Read content for JSON files (like quiz.json, quiz_answers.json)
|
| 57 |
+
let content = undefined
|
| 58 |
+
if (!stats.isDirectory() && path.extname(item).toLowerCase() === '.json') {
|
| 59 |
+
try {
|
| 60 |
+
const fileContent = await fs.promises.readFile(itemPath, 'utf-8')
|
| 61 |
+
content = fileContent
|
| 62 |
+
} catch (err) {
|
| 63 |
+
console.error(`Error reading ${item}:`, err)
|
| 64 |
+
}
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
return {
|
| 68 |
name: item,
|
| 69 |
type: stats.isDirectory() ? 'folder' : 'file',
|
| 70 |
size: stats.size,
|
| 71 |
modified: stats.mtime.toISOString(),
|
| 72 |
path: relativePath,
|
| 73 |
+
extension: path.extname(item).substring(1),
|
| 74 |
+
...(content !== undefined && { content })
|
| 75 |
}
|
| 76 |
}))
|
| 77 |
|
|
|
|
| 84 |
|
| 85 |
export async function POST(request: NextRequest) {
|
| 86 |
try {
|
| 87 |
+
const contentType = request.headers.get('content-type')
|
| 88 |
+
|
| 89 |
+
// Handle JSON body (for save_file action)
|
| 90 |
+
if (contentType?.includes('application/json')) {
|
| 91 |
+
const body = await request.json()
|
| 92 |
+
const { passkey, action, fileName, content, folder = '' } = body
|
| 93 |
+
|
| 94 |
+
if (!passkey) {
|
| 95 |
+
return NextResponse.json({ error: 'Passkey is required' }, { status: 400 })
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
if (action === 'save_file') {
|
| 99 |
+
if (!fileName || !content) {
|
| 100 |
+
return NextResponse.json({ error: 'fileName and content are required' }, { status: 400 })
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
const sanitizedKey = passkey.replace(/[^a-zA-Z0-9_-]/g, '')
|
| 104 |
+
const userDir = path.join(DATA_DIR, sanitizedKey)
|
| 105 |
+
const targetDir = path.join(userDir, folder)
|
| 106 |
+
|
| 107 |
+
// Ensure directories exist
|
| 108 |
+
await mkdir(targetDir, { recursive: true })
|
| 109 |
+
|
| 110 |
+
const filePath = path.join(targetDir, fileName)
|
| 111 |
+
await writeFile(filePath, content, 'utf-8')
|
| 112 |
+
|
| 113 |
+
return NextResponse.json({ success: true })
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
return NextResponse.json({ error: 'Unknown action' }, { status: 400 })
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
// Handle FormData (for file uploads)
|
| 120 |
const formData = await request.formData()
|
| 121 |
const file = formData.get('file') as File
|
| 122 |
const key = formData.get('key') as string
|
|
|
|
| 144 |
|
| 145 |
return NextResponse.json({ success: true })
|
| 146 |
} catch (error) {
|
| 147 |
+
console.error('Error in POST handler:', error)
|
| 148 |
+
return NextResponse.json({ error: 'Operation failed' }, { status: 500 })
|
| 149 |
}
|
| 150 |
}
|
| 151 |
|
app/components/Calendar.tsx
CHANGED
|
@@ -235,9 +235,9 @@ export function Calendar({ onClose, onMinimize, onMaximize, onFocus, zIndex }: C
|
|
| 235 |
{['Home', 'Work', 'Holidays', 'Birthdays'].map((cal, i) => (
|
| 236 |
<div key={cal} className="flex items-center gap-3 px-2 py-1.5 rounded-md hover:bg-white/5 cursor-pointer group">
|
| 237 |
<div className={`w-3 h-3 rounded-full border-2 ${i === 0 ? 'border-blue-500 bg-blue-500' :
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
}`} />
|
| 242 |
<span className="text-sm text-gray-300">{cal}</span>
|
| 243 |
<Check size={12} className="ml-auto opacity-0 group-hover:opacity-100 text-gray-500" />
|
|
@@ -308,61 +308,63 @@ export function Calendar({ onClose, onMinimize, onMaximize, onFocus, zIndex }: C
|
|
| 308 |
animate={{ opacity: 1, scale: 1 }}
|
| 309 |
exit={{ opacity: 0, scale: 1.02 }}
|
| 310 |
transition={{ duration: 0.2 }}
|
| 311 |
-
className="h-full flex flex-col"
|
| 312 |
>
|
| 313 |
-
<div className="
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
{day}
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
|
|
|
| 330 |
border-b border-r border-white/5 p-1 relative group transition-colors
|
| 331 |
${!dateObj.isCurrentMonth ? 'bg-black/20 text-gray-600' : 'hover:bg-white/5'}
|
| 332 |
`}
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
text-sm font-medium w-7 h-7 flex items-center justify-center rounded-full
|
| 342 |
${isCurrentDay
|
| 343 |
-
|
| 344 |
-
|
| 345 |
`}
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
|
| 355 |
text-[10px] px-1.5 py-0.5 rounded-sm truncate cursor-pointer hover:opacity-80
|
| 356 |
${event.color || 'bg-blue-500'} text-white font-medium shadow-sm backdrop-blur-sm bg-opacity-80
|
| 357 |
`}
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
|
|
|
| 362 |
</div>
|
| 363 |
-
|
| 364 |
-
)
|
| 365 |
-
|
| 366 |
</div>
|
| 367 |
</motion.div>
|
| 368 |
)}
|
|
@@ -373,9 +375,9 @@ export function Calendar({ onClose, onMinimize, onMaximize, onFocus, zIndex }: C
|
|
| 373 |
initial={{ opacity: 0 }}
|
| 374 |
animate={{ opacity: 1 }}
|
| 375 |
exit={{ opacity: 0 }}
|
| 376 |
-
className="h-full overflow-y-auto p-8 [&::-webkit-scrollbar-thumb]:bg-white/20 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar]:w-2"
|
| 377 |
>
|
| 378 |
-
<div className="grid grid-cols-4 gap-x-8 gap-y-12">
|
| 379 |
{monthNames.map((month, monthIndex) => {
|
| 380 |
const days = getDaysInMonth(currentDate.getFullYear(), monthIndex)
|
| 381 |
return (
|
|
|
|
| 235 |
{['Home', 'Work', 'Holidays', 'Birthdays'].map((cal, i) => (
|
| 236 |
<div key={cal} className="flex items-center gap-3 px-2 py-1.5 rounded-md hover:bg-white/5 cursor-pointer group">
|
| 237 |
<div className={`w-3 h-3 rounded-full border-2 ${i === 0 ? 'border-blue-500 bg-blue-500' :
|
| 238 |
+
i === 1 ? 'border-purple-500 bg-purple-500' :
|
| 239 |
+
i === 2 ? 'border-green-500 bg-green-500' :
|
| 240 |
+
'border-red-500 bg-red-500'
|
| 241 |
}`} />
|
| 242 |
<span className="text-sm text-gray-300">{cal}</span>
|
| 243 |
<Check size={12} className="ml-auto opacity-0 group-hover:opacity-100 text-gray-500" />
|
|
|
|
| 308 |
animate={{ opacity: 1, scale: 1 }}
|
| 309 |
exit={{ opacity: 0, scale: 1.02 }}
|
| 310 |
transition={{ duration: 0.2 }}
|
| 311 |
+
className="h-full flex flex-col overflow-auto"
|
| 312 |
>
|
| 313 |
+
<div className="min-w-[600px] h-full flex flex-col">
|
| 314 |
+
<div className="grid grid-cols-7 border-b border-white/5 bg-black/10">
|
| 315 |
+
{weekDays.map(day => (
|
| 316 |
+
<div key={day} className="py-2 text-right pr-4 text-xs font-medium text-gray-500 uppercase tracking-wider">
|
| 317 |
+
{day}
|
| 318 |
+
</div>
|
| 319 |
+
))}
|
| 320 |
+
</div>
|
| 321 |
+
<div className="flex-1 grid grid-cols-7 grid-rows-6">
|
| 322 |
+
{calendarDays.map((dateObj, index) => {
|
| 323 |
+
const dateStr = `${dateObj.year}-${String(dateObj.month + 1).padStart(2, '0')}-${String(dateObj.day).padStart(2, '0')}`
|
| 324 |
+
const dayEvents = getEventsForDate(dateStr)
|
| 325 |
+
const isCurrentDay = isToday(dateObj.day, dateObj.month, dateObj.year)
|
| 326 |
+
|
| 327 |
+
return (
|
| 328 |
+
<div
|
| 329 |
+
key={index}
|
| 330 |
+
className={`
|
| 331 |
border-b border-r border-white/5 p-1 relative group transition-colors
|
| 332 |
${!dateObj.isCurrentMonth ? 'bg-black/20 text-gray-600' : 'hover:bg-white/5'}
|
| 333 |
`}
|
| 334 |
+
onClick={() => {
|
| 335 |
+
setCurrentDate(new Date(dateObj.year, dateObj.month, dateObj.day))
|
| 336 |
+
setView('day')
|
| 337 |
+
}}
|
| 338 |
+
>
|
| 339 |
+
<div className="flex justify-end mb-1">
|
| 340 |
+
<span
|
| 341 |
+
className={`
|
| 342 |
text-sm font-medium w-7 h-7 flex items-center justify-center rounded-full
|
| 343 |
${isCurrentDay
|
| 344 |
+
? 'bg-red-500 text-white shadow-lg shadow-red-900/50'
|
| 345 |
+
: dateObj.isCurrentMonth ? 'text-gray-300' : 'text-gray-600'}
|
| 346 |
`}
|
| 347 |
+
>
|
| 348 |
+
{dateObj.day}
|
| 349 |
+
</span>
|
| 350 |
+
</div>
|
| 351 |
+
<div className="space-y-1 overflow-y-auto max-h-[calc(100%-2rem)] [&::-webkit-scrollbar]:hidden">
|
| 352 |
+
{dayEvents.map((event, i) => (
|
| 353 |
+
<div
|
| 354 |
+
key={i}
|
| 355 |
+
className={`
|
| 356 |
text-[10px] px-1.5 py-0.5 rounded-sm truncate cursor-pointer hover:opacity-80
|
| 357 |
${event.color || 'bg-blue-500'} text-white font-medium shadow-sm backdrop-blur-sm bg-opacity-80
|
| 358 |
`}
|
| 359 |
+
>
|
| 360 |
+
{event.title}
|
| 361 |
+
</div>
|
| 362 |
+
))}
|
| 363 |
+
</div>
|
| 364 |
</div>
|
| 365 |
+
)
|
| 366 |
+
})}
|
| 367 |
+
</div>
|
| 368 |
</div>
|
| 369 |
</motion.div>
|
| 370 |
)}
|
|
|
|
| 375 |
initial={{ opacity: 0 }}
|
| 376 |
animate={{ opacity: 1 }}
|
| 377 |
exit={{ opacity: 0 }}
|
| 378 |
+
className="h-full overflow-y-auto p-4 md:p-8 [&::-webkit-scrollbar-thumb]:bg-white/20 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar]:w-2"
|
| 379 |
>
|
| 380 |
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-x-8 gap-y-12">
|
| 381 |
{monthNames.map((month, monthIndex) => {
|
| 382 |
const days = getDaysInMonth(currentDate.getFullYear(), monthIndex)
|
| 383 |
return (
|
app/components/Messages.tsx
CHANGED
|
@@ -115,7 +115,7 @@ export function Messages({ onClose, onMinimize, onMaximize, onFocus, zIndex }: M
|
|
| 115 |
width={800}
|
| 116 |
height={600}
|
| 117 |
x={100}
|
| 118 |
-
y={
|
| 119 |
className="messages-window !bg-[#1e1e1e]/80 !backdrop-blur-2xl border border-white/10 shadow-2xl !rounded-xl overflow-hidden"
|
| 120 |
contentClassName="!bg-transparent"
|
| 121 |
headerClassName="!bg-transparent border-b border-white/5"
|
|
|
|
| 115 |
width={800}
|
| 116 |
height={600}
|
| 117 |
x={100}
|
| 118 |
+
y={50}
|
| 119 |
className="messages-window !bg-[#1e1e1e]/80 !backdrop-blur-2xl border border-white/10 shadow-2xl !rounded-xl overflow-hidden"
|
| 120 |
contentClassName="!bg-transparent"
|
| 121 |
headerClassName="!bg-transparent border-b border-white/5"
|