|
1 | 1 | "use client"; |
2 | 2 | import { useEffect, useState, useCallback } from "react"; |
3 | 3 | import { useRouter } from "next/navigation"; |
4 | | -import { roomsApi, usersApi, privateRoomsApi, TonightRooms, PrivateRoom } from "@/lib/api"; |
| 4 | +import { roomsApi, usersApi, privateRoomsApi, friendsApi, TonightRooms, PrivateRoom, Friend } from "@/lib/api"; |
5 | 5 | import { useAuthStore } from "@/lib/auth"; |
6 | 6 | import CountdownTimer from "@/components/CountdownTimer"; |
7 | 7 | import RoomCard from "@/components/RoomCard"; |
@@ -31,6 +31,11 @@ export default function DashboardPage() { |
31 | 31 | const [showCreateModal, setShowCreateModal] = useState(false); |
32 | 32 | const [createForm, setCreateForm] = useState({ rakats: 8, juz_number: 1, juz_per_night: 1.0 }); |
33 | 33 | const [creating, setCreating] = useState(false); |
| 34 | + // Post-creation invite step |
| 35 | + const [createdRoom, setCreatedRoom] = useState<{ id: string; room_url: string } | null>(null); |
| 36 | + const [friends, setFriends] = useState<Friend[]>([]); |
| 37 | + const [inviteBusy, setInviteBusy] = useState<Record<string, boolean>>({}); |
| 38 | + const [inviteDone, setInviteDone] = useState<Record<string, boolean>>({}); |
34 | 39 |
|
35 | 40 | const handleUnauth = useCallback(() => { |
36 | 41 | clearAuth(); |
@@ -308,81 +313,161 @@ export default function DashboardPage() { |
308 | 313 | {showCreateModal && ( |
309 | 314 | <div className="fixed inset-0 z-50 flex items-center justify-center px-4 bg-black/60 backdrop-blur-sm"> |
310 | 315 | <div className="w-full max-w-sm glass-card p-6 mosque-glow space-y-4"> |
311 | | - <div className="flex items-center justify-between"> |
312 | | - <h2 className="font-bold text-white">Create Private Room</h2> |
313 | | - <button onClick={() => setShowCreateModal(false)} className="text-gray-500 hover:text-gray-300">✕</button> |
314 | | - </div> |
315 | 316 |
|
316 | | - <div className="space-y-3"> |
317 | | - <div> |
318 | | - <label className="text-xs text-gray-400 mb-1 block">Rakats</label> |
319 | | - <div className="flex gap-2"> |
320 | | - {[8, 20].map((r) => ( |
321 | | - <button |
322 | | - key={r} |
323 | | - onClick={() => setCreateForm((f) => ({ ...f, rakats: r }))} |
324 | | - className={`flex-1 py-2 rounded-lg border text-sm font-medium transition-colors ${ |
325 | | - createForm.rakats === r |
326 | | - ? "border-mosque-gold bg-mosque-gold/10 text-mosque-gold" |
327 | | - : "border-gray-700 text-gray-400" |
328 | | - }`} |
| 317 | + {/* Step 1 — configure room */} |
| 318 | + {!createdRoom ? ( |
| 319 | + <> |
| 320 | + <div className="flex items-center justify-between"> |
| 321 | + <h2 className="font-bold text-white">Create Private Room</h2> |
| 322 | + <button onClick={() => setShowCreateModal(false)} className="text-gray-500 hover:text-gray-300">✕</button> |
| 323 | + </div> |
| 324 | + |
| 325 | + <div className="space-y-3"> |
| 326 | + <div> |
| 327 | + <label className="text-xs text-gray-400 mb-1 block">Rakats</label> |
| 328 | + <div className="flex gap-2"> |
| 329 | + {[8, 20].map((r) => ( |
| 330 | + <button |
| 331 | + key={r} |
| 332 | + onClick={() => setCreateForm((f) => ({ ...f, rakats: r }))} |
| 333 | + className={`flex-1 py-2 rounded-lg border text-sm font-medium transition-colors ${ |
| 334 | + createForm.rakats === r |
| 335 | + ? "border-mosque-gold bg-mosque-gold/10 text-mosque-gold" |
| 336 | + : "border-gray-700 text-gray-400" |
| 337 | + }`} |
| 338 | + > |
| 339 | + {r} Rakats |
| 340 | + </button> |
| 341 | + ))} |
| 342 | + </div> |
| 343 | + </div> |
| 344 | + |
| 345 | + <div> |
| 346 | + <label className="text-xs text-gray-400 mb-1 block">Juz</label> |
| 347 | + <select |
| 348 | + value={createForm.juz_number} |
| 349 | + onChange={(e) => setCreateForm((f) => ({ ...f, juz_number: Number(e.target.value) }))} |
| 350 | + className="w-full bg-mosque-darkest border border-white/10 rounded-lg px-3 py-2 text-white text-sm outline-none focus:border-mosque-gold/50" |
329 | 351 | > |
330 | | - {r} Rakats |
331 | | - </button> |
332 | | - ))} |
| 352 | + {Array.from({ length: 30 }, (_, i) => i + 1).map((n) => ( |
| 353 | + <option key={n} value={n}>Juz {n}</option> |
| 354 | + ))} |
| 355 | + </select> |
| 356 | + </div> |
| 357 | + |
| 358 | + <div> |
| 359 | + <label className="text-xs text-gray-400 mb-1 block">Amount</label> |
| 360 | + <div className="flex gap-2"> |
| 361 | + {[1.0, 0.5].map((j) => ( |
| 362 | + <button |
| 363 | + key={j} |
| 364 | + onClick={() => setCreateForm((f) => ({ ...f, juz_per_night: j }))} |
| 365 | + className={`flex-1 py-2 rounded-lg border text-sm font-medium transition-colors ${ |
| 366 | + createForm.juz_per_night === j |
| 367 | + ? "border-mosque-gold bg-mosque-gold/10 text-mosque-gold" |
| 368 | + : "border-gray-700 text-gray-400" |
| 369 | + }`} |
| 370 | + > |
| 371 | + {j === 1.0 ? "Full Juz" : "Half Juz"} |
| 372 | + </button> |
| 373 | + ))} |
| 374 | + </div> |
| 375 | + </div> |
333 | 376 | </div> |
334 | | - </div> |
335 | 377 |
|
336 | | - <div> |
337 | | - <label className="text-xs text-gray-400 mb-1 block">Juz</label> |
338 | | - <select |
339 | | - value={createForm.juz_number} |
340 | | - onChange={(e) => setCreateForm((f) => ({ ...f, juz_number: Number(e.target.value) }))} |
341 | | - className="w-full bg-mosque-darkest border border-white/10 rounded-lg px-3 py-2 text-white text-sm outline-none focus:border-mosque-gold/50" |
| 378 | + <button |
| 379 | + onClick={async () => { |
| 380 | + setCreating(true); |
| 381 | + try { |
| 382 | + const res = await privateRoomsApi.create(createForm); |
| 383 | + setCreatedRoom({ id: res.data.id, room_url: res.data.room_url }); |
| 384 | + // Reload private rooms list in background |
| 385 | + privateRoomsApi.list().then((r) => setPrivateRooms(r.data)).catch(() => {}); |
| 386 | + // Load friends for invite step |
| 387 | + friendsApi.getAll().then((r) => setFriends(r.data.friends)).catch(() => {}); |
| 388 | + } finally { |
| 389 | + setCreating(false); |
| 390 | + } |
| 391 | + }} |
| 392 | + disabled={creating} |
| 393 | + className="w-full py-3 bg-mosque-gold text-mosque-dark font-bold rounded-xl hover:bg-mosque-gold-light transition-colors disabled:opacity-50" |
342 | 394 | > |
343 | | - {Array.from({ length: 30 }, (_, i) => i + 1).map((n) => ( |
344 | | - <option key={n} value={n}>Juz {n}</option> |
345 | | - ))} |
346 | | - </select> |
347 | | - </div> |
| 395 | + {creating ? "Creating…" : "Create Room"} |
| 396 | + </button> |
| 397 | + </> |
| 398 | + ) : ( |
| 399 | + /* Step 2 — invite friends */ |
| 400 | + <> |
| 401 | + <div className="flex items-center justify-between"> |
| 402 | + <h2 className="font-bold text-white">Invite Friends</h2> |
| 403 | + <button |
| 404 | + onClick={() => { |
| 405 | + setShowCreateModal(false); |
| 406 | + setCreatedRoom(null); |
| 407 | + setInviteBusy({}); |
| 408 | + setInviteDone({}); |
| 409 | + }} |
| 410 | + className="text-gray-500 hover:text-gray-300" |
| 411 | + > |
| 412 | + ✕ |
| 413 | + </button> |
| 414 | + </div> |
348 | 415 |
|
349 | | - <div> |
350 | | - <label className="text-xs text-gray-400 mb-1 block">Amount</label> |
351 | | - <div className="flex gap-2"> |
352 | | - {[1.0, 0.5].map((j) => ( |
353 | | - <button |
354 | | - key={j} |
355 | | - onClick={() => setCreateForm((f) => ({ ...f, juz_per_night: j }))} |
356 | | - className={`flex-1 py-2 rounded-lg border text-sm font-medium transition-colors ${ |
357 | | - createForm.juz_per_night === j |
358 | | - ? "border-mosque-gold bg-mosque-gold/10 text-mosque-gold" |
359 | | - : "border-gray-700 text-gray-400" |
360 | | - }`} |
361 | | - > |
362 | | - {j === 1.0 ? "Full Juz" : "Half Juz"} |
363 | | - </button> |
364 | | - ))} |
| 416 | + {/* Share link */} |
| 417 | + <div className="bg-mosque-darkest border border-white/10 rounded-lg px-3 py-2"> |
| 418 | + <p className="text-xs text-gray-400 mb-1">Room link</p> |
| 419 | + <p className="text-xs text-mosque-gold break-all">{createdRoom.room_url}</p> |
365 | 420 | </div> |
366 | | - </div> |
367 | | - </div> |
368 | 421 |
|
369 | | - <button |
370 | | - onClick={async () => { |
371 | | - setCreating(true); |
372 | | - try { |
373 | | - await privateRoomsApi.create(createForm); |
374 | | - const res = await privateRoomsApi.list(); |
375 | | - setPrivateRooms(res.data); |
376 | | - setShowCreateModal(false); |
377 | | - } finally { |
378 | | - setCreating(false); |
379 | | - } |
380 | | - }} |
381 | | - disabled={creating} |
382 | | - className="w-full py-3 bg-mosque-gold text-mosque-dark font-bold rounded-xl hover:bg-mosque-gold-light transition-colors disabled:opacity-50" |
383 | | - > |
384 | | - {creating ? "Creating…" : "Create Room"} |
385 | | - </button> |
| 422 | + {/* Friends list */} |
| 423 | + {friends.length === 0 ? ( |
| 424 | + <p className="text-sm text-gray-500 text-center py-2"> |
| 425 | + No friends yet.{" "} |
| 426 | + <Link href="/friends" className="text-mosque-gold underline" onClick={() => { setShowCreateModal(false); setCreatedRoom(null); }}> |
| 427 | + Add friends |
| 428 | + </Link> |
| 429 | + </p> |
| 430 | + ) : ( |
| 431 | + <div className="space-y-2 max-h-48 overflow-y-auto pr-1"> |
| 432 | + {friends.map((f) => ( |
| 433 | + <div key={f.id} className="flex items-center justify-between"> |
| 434 | + <div> |
| 435 | + <p className="text-sm text-white">{f.name || f.email}</p> |
| 436 | + {f.name && <p className="text-xs text-gray-500">{f.email}</p>} |
| 437 | + </div> |
| 438 | + <button |
| 439 | + disabled={inviteBusy[f.id] || inviteDone[f.id]} |
| 440 | + onClick={async () => { |
| 441 | + setInviteBusy((p) => ({ ...p, [f.id]: true })); |
| 442 | + try { |
| 443 | + await privateRoomsApi.invite(createdRoom.id, f.id); |
| 444 | + setInviteDone((p) => ({ ...p, [f.id]: true })); |
| 445 | + } finally { |
| 446 | + setInviteBusy((p) => ({ ...p, [f.id]: false })); |
| 447 | + } |
| 448 | + }} |
| 449 | + className={`text-xs px-3 py-1.5 rounded-lg font-medium transition-colors ${ |
| 450 | + inviteDone[f.id] |
| 451 | + ? "bg-green-900/40 text-green-400 cursor-default" |
| 452 | + : "bg-mosque-gold/10 border border-mosque-gold/30 text-mosque-gold hover:bg-mosque-gold/20 disabled:opacity-50" |
| 453 | + }`} |
| 454 | + > |
| 455 | + {inviteDone[f.id] ? "Invited" : inviteBusy[f.id] ? "…" : "Invite"} |
| 456 | + </button> |
| 457 | + </div> |
| 458 | + ))} |
| 459 | + </div> |
| 460 | + )} |
| 461 | + |
| 462 | + <Link |
| 463 | + href={`/room/${createdRoom.id}`} |
| 464 | + className="block w-full py-3 bg-mosque-gold text-mosque-dark font-bold rounded-xl hover:bg-mosque-gold-light transition-colors text-center" |
| 465 | + onClick={() => { setShowCreateModal(false); setCreatedRoom(null); }} |
| 466 | + > |
| 467 | + Go to Room |
| 468 | + </Link> |
| 469 | + </> |
| 470 | + )} |
386 | 471 | </div> |
387 | 472 | </div> |
388 | 473 | )} |
|
0 commit comments