mirror of
https://github.com/mudabbir-ahmad/peworkshop.git
synced 2026-10-07 19:50:20 +00:00
fixed a bit
This commit is contained in:
1 parent
55e93b54c4
commit
524533ce35
12 files changed
+11174
-19
No files matched your search
@@ -0,0 +1,113 @@
|
|||||||
|
'use client'; // Mark as a Client Component
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { useSession } from 'next-auth/react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
export default function CreateTeam() {
|
||||||
|
const { data: session, status } = useSession();
|
||||||
|
const [teamName, setTeamName] = useState('');
|
||||||
|
const [error, setError] = useState('');
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
// Redirect if not authenticated
|
||||||
|
if (status === 'unauthenticated') {
|
||||||
|
router.push('/login');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect if user is admin
|
||||||
|
if (status === 'authenticated' && session?.user?.role === 'admin') {
|
||||||
|
router.push('/admin');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCreateTeam = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsLoading(true);
|
||||||
|
setError('');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/teams/create', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
name: teamName,
|
||||||
|
userId: session.user.id
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
router.push(`/team/${result.teamId}`);
|
||||||
|
} else {
|
||||||
|
setError(result.error || 'Failed to create team');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
setError('An unexpected error occurred');
|
||||||
|
console.error(err);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (status === 'loading') {
|
||||||
|
return <div className="loading">Loading...</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="team-selection-container">
|
||||||
|
<div className="page-header">
|
||||||
|
<Link href="/team-selection" className="back-button">←</Link>
|
||||||
|
<div className="page-title">Create Team</div>
|
||||||
|
<div className="placeholder"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="team-selection-card">
|
||||||
|
<h1>Create a New Team</h1>
|
||||||
|
<form onSubmit={handleCreateTeam}>
|
||||||
|
<div className="form-group">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Team Name"
|
||||||
|
value={teamName}
|
||||||
|
onChange={(e) => setTeamName(e.target.value)}
|
||||||
|
required
|
||||||
|
className="team-input"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="team-button"
|
||||||
|
disabled={isLoading}
|
||||||
|
>
|
||||||
|
{isLoading ? 'Creating...' : 'Create Team'}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
{error && <p className="error">{error}</p>}
|
||||||
|
<div className="team-options">
|
||||||
|
<Link href="/team-selection" className="create-team-link">Back to Team Selection</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="nav-bar">
|
||||||
|
<div className="nav-item">
|
||||||
|
<div className="nav-icon">🏠</div>
|
||||||
|
</div>
|
||||||
|
<div className="nav-item">
|
||||||
|
<div className="nav-icon">📊</div>
|
||||||
|
</div>
|
||||||
|
<div className="nav-item">
|
||||||
|
<div className="nav-icon">🏆</div>
|
||||||
|
</div>
|
||||||
|
<div className="nav-item">
|
||||||
|
<div className="nav-icon">👤</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
'use client'; // Mark as a Client Component
|
'use client'; // Mark as a Client Component
|
||||||
import { useState } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useSession } from 'next-auth/react';
|
import { useSession } from 'next-auth/react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { joinTeam } from '../../actions/auth';
|
import { joinTeam } from '../../actions/auth';
|
||||||
@@ -11,17 +11,18 @@ export default function TeamSelection() {
|
|||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
// Redirect if not authenticated
|
// Use useEffect for navigation instead of doing it in render
|
||||||
if (status === 'unauthenticated') {
|
useEffect(() => {
|
||||||
router.push('/login');
|
// Redirect if not authenticated
|
||||||
return null;
|
if (status === 'unauthenticated') {
|
||||||
}
|
router.push('/login');
|
||||||
|
}
|
||||||
|
|
||||||
// Redirect if user is admin
|
// Redirect if user is admin
|
||||||
if (status === 'authenticated' && session?.user?.role === 'admin') {
|
if (status === 'authenticated' && session?.user?.role === 'admin') {
|
||||||
router.push('/admin');
|
router.push('/admin');
|
||||||
return null;
|
}
|
||||||
}
|
}, [status, session, router]);
|
||||||
|
|
||||||
const handleJoinTeam = async (e) => {
|
const handleJoinTeam = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -48,6 +49,11 @@ export default function TeamSelection() {
|
|||||||
return <div className="loading">Loading...</div>;
|
return <div className="loading">Loading...</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't render anything if redirecting
|
||||||
|
if (status === 'unauthenticated' || (status === 'authenticated' && session?.user?.role === 'admin')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="team-selection-container">
|
<div className="team-selection-container">
|
||||||
<div className="team-selection-card">
|
<div className="team-selection-card">
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { queryOne } from '../../../../lib/db';
|
||||||
|
import { getServerSession } from 'next-auth/next';
|
||||||
|
import { authOptions } from '../../auth/[...nextauth]/route';
|
||||||
|
|
||||||
|
export async function GET(request, { params }) {
|
||||||
|
const session = await getServerSession(authOptions);
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
return Response.json({ error: 'Unauthorized' }, { status: 401 });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { teamId } = params;
|
||||||
|
|
||||||
|
// Ensure the user is requesting their own team or is an admin
|
||||||
|
if (session.user.teamId != teamId && session.user.role !== 'admin') {
|
||||||
|
return Response.json({ error: 'Not authorized to view this team' }, { status: 403 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const team = await queryOne('SELECT * FROM teams WHERE id = ?', [teamId]);
|
||||||
|
|
||||||
|
if (!team) {
|
||||||
|
return Response.json({ error: 'Team not found' }, { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.json(team);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Team fetch error:', error);
|
||||||
|
return Response.json({ error: 'Failed to fetch team details' }, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { query, queryOne, run } from '../../../../lib/db';
|
||||||
|
import { getServerSession } from 'next-auth/next';
|
||||||
|
import { authOptions } from '../../auth/[...nextauth]/route';
|
||||||
|
import { nanoid } from 'nanoid';
|
||||||
|
|
||||||
|
export async function POST(request) {
|
||||||
|
const session = await getServerSession(authOptions);
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
return Response.json({ success: false, error: 'Unauthorized' }, { status: 401 });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { name, userId } = await request.json();
|
||||||
|
|
||||||
|
if (!name || name.trim() === '') {
|
||||||
|
return Response.json({ success: false, error: 'Team name is required' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a unique team code (6 characters)
|
||||||
|
const teamCode = nanoid(6).toUpperCase();
|
||||||
|
|
||||||
|
// Insert the new team
|
||||||
|
const result = await run(
|
||||||
|
'INSERT INTO teams (name, code, created_by) VALUES (?, ?, ?)',
|
||||||
|
[name, teamCode, userId]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!result || !result.lastID) {
|
||||||
|
return Response.json({ success: false, error: 'Failed to create team' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the user's team_id
|
||||||
|
await run(
|
||||||
|
'UPDATE users SET team_id = ? WHERE id = ?',
|
||||||
|
[result.lastID, userId]
|
||||||
|
);
|
||||||
|
|
||||||
|
return Response.json({
|
||||||
|
success: true,
|
||||||
|
teamId: result.lastID,
|
||||||
|
teamCode
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Create team error:', error);
|
||||||
|
return Response.json({
|
||||||
|
success: false,
|
||||||
|
error: 'Failed to create team: ' + error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -739,4 +739,29 @@ p {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Team Header */
|
||||||
|
.team-header {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
background-color: #63503d;
|
||||||
|
color: white;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
z-index: 100;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-name {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adjust the main layout to account for sticky header */
|
||||||
|
.main-layout {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
+6
-8
@@ -1,12 +1,10 @@
|
|||||||
import './globals.css';
|
import TeamHeader from '../../components/TeamHeader';
|
||||||
import { NextAuthProvider } from './providers';
|
|
||||||
|
|
||||||
export default function RootLayout({ children }) {
|
export default function MainLayout({ children }) {
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<div className="main-layout">
|
||||||
<body>
|
<TeamHeader />
|
||||||
<NextAuthProvider>{children}</NextAuthProvider>
|
{children}
|
||||||
</body>
|
</div>
|
||||||
</html>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?><sqlb_project><db path="clue_hunt.db" readonly="0" foreign_keys="1" case_sensitive_like="0" temp_store="0" wal_autocheckpoint="1000" synchronous="2"/><attached/><window><main_tabs open="structure browser pragmas query" current="0"/></window><tab_structure><column_width id="0" width="300"/><column_width id="1" width="0"/><column_width id="2" width="100"/><column_width id="3" width="1504"/><column_width id="4" width="0"/><expanded_item id="0" parent="1"/><expanded_item id="1" parent="1"/><expanded_item id="2" parent="1"/><expanded_item id="3" parent="1"/></tab_structure><tab_browse><table title="users" custom_title="0" dock_id="1" table="4,5:mainusers"/><dock_state state="000000ff00000000fd0000000100000002000005a80000031dfc0100000001fb000000160064006f0063006b00420072006f00770073006500310100000000000005a80000011e00ffffff000002690000000000000004000000040000000800000008fc00000000"/><default_encoding codec=""/><browse_table_settings><table schema="main" name="clues" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths><column index="1" value="212"/><column index="2" value="315"/><column index="3" value="133"/><column index="4" value="164"/><column index="5" value="296"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table><table schema="main" name="sqlite_sequence" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths><column index="1" value="47"/><column index="2" value="35"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table><table schema="main" name="teams" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths><column index="1" value="35"/><column index="2" value="87"/><column index="3" value="71"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table><table schema="main" name="users" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths><column index="1" value="35"/><column index="2" value="65"/><column index="3" value="127"/><column index="4" value="47"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table></browse_table_settings></tab_browse><tab_sql><sql name="initialise-databse.sql*" filename="C:/Users/mudab/WebstormProjects/pe-two/clue-hunt-app/initialise-databse.sql">-- Reference to file "C:/Users/mudab/WebstormProjects/pe-two/clue-hunt-app/initialise-databse.sql" (not supported by this version) --</sql><current_tab id="0"/></tab_sql></sqlb_project>
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
'use client';
|
||||||
|
import { useSession } from 'next-auth/react';
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
|
||||||
|
export default function TeamHeader() {
|
||||||
|
const { data: session } = useSession();
|
||||||
|
const [teamName, setTeamName] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Only fetch team info if user is logged in and not an admin
|
||||||
|
if (session?.user?.teamId && session?.user?.role !== 'admin') {
|
||||||
|
fetchTeamInfo();
|
||||||
|
}
|
||||||
|
}, [session]);
|
||||||
|
|
||||||
|
const fetchTeamInfo = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/teams/${session.user.teamId}`);
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
setTeamName(data.name);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch team info:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Don't render anything if no team name or user is admin
|
||||||
|
if (!teamName || session?.user?.role === 'admin') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="team-header">
|
||||||
|
<div className="team-name">{teamName}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Whitespace-only changes.
Whitespace-only changes.
Generated
+7169
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user