fixed a bit

This commit is contained in:
bobbert committed 2025-03-11 17:46:24 +00:00
1 parent 55e93b54c4
commit 524533ce35
12 files changed
+11166 -11

No files matched your search

+113
View File
@@ -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>
);
}
+9 -3
View File
@@ -1,5 +1,5 @@
'use client'; // Mark as a Client Component
import { useState } from 'react';
import { useState, useEffect } from 'react';
import { useSession } from 'next-auth/react';
import { useRouter } from 'next/navigation';
import { joinTeam } from '../../actions/auth';
@@ -11,17 +11,18 @@ export default function TeamSelection() {
const [isLoading, setIsLoading] = useState(false);
const router = useRouter();
// Use useEffect for navigation instead of doing it in render
useEffect(() => {
// 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;
}
}, [status, session, router]);
const handleJoinTeam = async (e) => {
e.preventDefault();
@@ -48,6 +49,11 @@ export default function TeamSelection() {
return <div className="loading">Loading...</div>;
}
// Don't render anything if redirecting
if (status === 'unauthenticated' || (status === 'authenticated' && session?.user?.role === 'admin')) {
return null;
}
return (
<div className="team-selection-container">
<div className="team-selection-card">
+31
View File
@@ -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 });
}
}
+51
View File
@@ -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
});
}
}
+25
View File
@@ -740,3 +740,28 @@ p {
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
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
View File
@@ -1,12 +1,10 @@
import './globals.css';
import { NextAuthProvider } from './providers';
import TeamHeader from '../../components/TeamHeader';
export default function RootLayout({ children }) {
export default function MainLayout({ children }) {
return (
<html lang="en">
<body>
<NextAuthProvider>{children}</NextAuthProvider>
</body>
</html>
<div className="main-layout">
<TeamHeader />
{children}
</div>
);
}
+1
View File
@@ -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 &quot;C:/Users/mudab/WebstormProjects/pe-two/clue-hunt-app/initialise-databse.sql&quot; (not supported by this version) --</sql><current_tab id="0"/></tab_sql></sqlb_project>
+38
View File
@@ -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>
);
}
View File
Whitespace-only changes.
View File
Whitespace-only changes.
+7169
View File
File diff suppressed because it is too large. Load diff
+3723
View File
File diff suppressed because it is too large. Load diff