updated
This commit is contained in:
parent
3554698475
commit
bacac4f701
484
src/App.js
484
src/App.js
@ -1,9 +1,10 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { BrowserRouter as Router, Route, Routes, Navigate, Link } from 'react-router-dom';
|
||||
import './App.css';
|
||||
import { useParams,useNavigate,useLocation } from 'react-router-dom';
|
||||
import { useParams, useNavigate, useLocation } from 'react-router-dom';
|
||||
import Home from './pages/Home';
|
||||
import TeamResults from './pages/TeamResult';
|
||||
|
||||
// Auth Context
|
||||
const AuthContext = React.createContext();
|
||||
|
||||
@ -37,8 +38,6 @@ const apiService = {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ accessKey, password })
|
||||
});
|
||||
console.log(accessKey);
|
||||
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Login failed');
|
||||
@ -49,7 +48,7 @@ const apiService = {
|
||||
|
||||
getTeams: async (token) => {
|
||||
const response = await fetch(`${API_URL}/api/teams`, {
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
headers: token ? { 'Authorization': `Bearer ${token}` } : {}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
@ -60,7 +59,7 @@ const apiService = {
|
||||
},
|
||||
|
||||
createTeam: async (teamData, token) => {
|
||||
const response = await fetch(`${API_URL}/api/teams`, {
|
||||
const response = await fetch(`${API_URL}/admin/teams`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@ -77,7 +76,7 @@ const apiService = {
|
||||
},
|
||||
|
||||
updateTeam: async (id, teamData, token) => {
|
||||
const response = await fetch(`${API_URL}/api/teams/${id}`, {
|
||||
const response = await fetch(`${API_URL}/admin/teams/${id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@ -94,7 +93,7 @@ const apiService = {
|
||||
},
|
||||
|
||||
deleteTeam: async (id, token) => {
|
||||
const response = await fetch(`${API_URL}/api/teams/${id}`, {
|
||||
const response = await fetch(`${API_URL}/admin/teams/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
});
|
||||
@ -107,6 +106,9 @@ const apiService = {
|
||||
},
|
||||
|
||||
publishResult: async (resultData, token) => {
|
||||
console.log("Publishing result with data:", resultData);
|
||||
console.log("Using token:", token);
|
||||
|
||||
const response = await fetch(`${API_URL}/admin/results`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@ -115,9 +117,44 @@ const apiService = {
|
||||
},
|
||||
body: JSON.stringify(resultData)
|
||||
});
|
||||
// In your publishResult function
|
||||
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
console.error("Server responded with error:", response.status, errorData);
|
||||
console.log("Sending exact payload:", JSON.stringify(resultData));
|
||||
throw new Error(`Failed to publish result: ${response.status}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
},
|
||||
|
||||
updateResult: async (id, resultData, token) => {
|
||||
const response = await fetch(`${API_URL}/admin/results/${id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify(resultData)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to publish result');
|
||||
throw new Error('Failed to update result');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
},
|
||||
|
||||
deleteResult: async (id, token) => {
|
||||
const response = await fetch(`${API_URL}/admin/results/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to delete result');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
@ -125,13 +162,97 @@ const apiService = {
|
||||
|
||||
getDailyResults: async (date, token) => {
|
||||
const response = await fetch(`${API_URL}/api/results/daily?date=${date}`, {
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
headers: token ? { 'Authorization': `Bearer ${token}` } : {}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch daily results');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
},
|
||||
|
||||
getMonthlyResults: async (team, month) => {
|
||||
const response = await fetch(`${API_URL}/api/results/monthly`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ team, month })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch monthly results');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
},
|
||||
|
||||
getTodayResults: async () => {
|
||||
const response = await fetch(`${API_URL}/api/today`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch today\'s results');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
},
|
||||
|
||||
// Scheduled games API endpoints
|
||||
getScheduledGames: async (date, token) => {
|
||||
const response = await fetch(`${API_URL}/api/schedule?date=${date}`, {
|
||||
headers: token ? { 'Authorization': `Bearer ${token}` } : {}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch scheduled games');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
},
|
||||
|
||||
createScheduledGame: async (gameData, token) => {
|
||||
const response = await fetch(`${API_URL}/admin/schedule`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify(gameData)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to create scheduled game');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
},
|
||||
|
||||
updateScheduledGame: async (id, gameData, token) => {
|
||||
const response = await fetch(`${API_URL}/admin/schedule/${id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify(gameData)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to update scheduled game');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
},
|
||||
|
||||
deleteScheduledGame: async (id, token) => {
|
||||
const response = await fetch(`${API_URL}/admin/schedule/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to delete scheduled game');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
};
|
||||
@ -142,13 +263,17 @@ const Login = () => {
|
||||
const [password, setPassword] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const { login } = React.useContext(AuthContext);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
const data = await apiService.login(accessKey, password);
|
||||
console.log("Login successful, token:", data.token);
|
||||
login(data.token);
|
||||
navigate('/teams');
|
||||
} catch (err) {
|
||||
console.error("Login failed:", err);
|
||||
setError('Invalid credentials');
|
||||
}
|
||||
};
|
||||
@ -193,6 +318,7 @@ const TeamList = () => {
|
||||
try {
|
||||
const data = await apiService.getTeams(token);
|
||||
setTeams(data);
|
||||
// alert(teams)
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
setError('Failed to fetch teams');
|
||||
@ -352,6 +478,17 @@ const ResultCalendar = () => {
|
||||
setDate(e.target.value);
|
||||
};
|
||||
|
||||
const handleDeleteResult = async (id) => {
|
||||
if (window.confirm('Are you sure you want to delete this result?')) {
|
||||
try {
|
||||
await apiService.deleteResult(id, token);
|
||||
setResults(results.filter(result => result.id !== id));
|
||||
} catch (err) {
|
||||
setError('Failed to delete result');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) return <div>Loading...</div>;
|
||||
if (error) return <div className="error">{error}</div>;
|
||||
|
||||
@ -379,6 +516,7 @@ const ResultCalendar = () => {
|
||||
<tr>
|
||||
<th>Team</th>
|
||||
<th>Result</th>
|
||||
<th>Announcement Time</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -387,8 +525,15 @@ const ResultCalendar = () => {
|
||||
<tr key={result.id}>
|
||||
<td>{result.team}</td>
|
||||
<td>{result.result}</td>
|
||||
<td>{result.announcement_time}</td>
|
||||
<td>
|
||||
<Link to={`/results/edit/${result.id}?date=${date}`} className="btn-secondary">Edit</Link>
|
||||
<Link to={`/admin/results/edit/${result.id}?date=${date}`} className="btn-secondary">Edit</Link>
|
||||
<button
|
||||
onClick={() => handleDeleteResult(result.id)}
|
||||
className="btn-danger"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
@ -402,9 +547,10 @@ const ResultCalendar = () => {
|
||||
|
||||
const ResultForm = ({ isEdit = false }) => {
|
||||
const [formData, setFormData] = useState({
|
||||
team_id: '',
|
||||
team: '',
|
||||
result: '',
|
||||
result_date: new Date().toISOString().split('T')[0]
|
||||
date: new Date().toISOString().split('T')[0],
|
||||
announcement_time: '12:00:00'
|
||||
});
|
||||
const [teams, setTeams] = useState([]);
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
@ -415,7 +561,7 @@ const ResultForm = ({ isEdit = false }) => {
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
const fetchTeams = async () => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const teamsData = await apiService.getTeams(token);
|
||||
setTeams(teamsData);
|
||||
@ -424,19 +570,19 @@ const ResultForm = ({ isEdit = false }) => {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const dateParam = params.get('date');
|
||||
if (dateParam) {
|
||||
setFormData(prev => ({ ...prev, result_date: dateParam }));
|
||||
setFormData(prev => ({ ...prev, date: dateParam }));
|
||||
}
|
||||
|
||||
// If editing, fetch the result details
|
||||
if (isEdit && id) {
|
||||
// This is a simplified approach. In a real app, you'd have an API endpoint to fetch a specific result
|
||||
const results = await apiService.getDailyResults(dateParam, token);
|
||||
const results = await apiService.getDailyResults(dateParam || formData.date, token);
|
||||
const result = results.find(r => r.id === parseInt(id));
|
||||
if (result) {
|
||||
setFormData({
|
||||
team_id: result.team_id,
|
||||
team: result.team_id.toString(),
|
||||
result: result.result,
|
||||
result_date: result.result_date
|
||||
date: result.result_date,
|
||||
announcement_time: result.announcement_time
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -445,8 +591,8 @@ const ResultForm = ({ isEdit = false }) => {
|
||||
}
|
||||
};
|
||||
|
||||
fetchTeams();
|
||||
}, [isEdit, id, token, location.search]);
|
||||
fetchData();
|
||||
}, [isEdit, id, token, location.search, formData.date]);
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
@ -458,10 +604,22 @@ const ResultForm = ({ isEdit = false }) => {
|
||||
setSubmitting(true);
|
||||
|
||||
try {
|
||||
await apiService.publishResult(formData, token);
|
||||
navigate(`/results?date=${formData.result_date}`);
|
||||
const payload = {
|
||||
team: formData.team, // This should be the team ID
|
||||
date: formData.date,
|
||||
result: formData.result,
|
||||
announcement_time: formData.announcement_time
|
||||
};
|
||||
|
||||
if (isEdit) {
|
||||
await apiService.updateResult(id, payload, token);
|
||||
} else {
|
||||
await apiService.publishResult(payload, token);
|
||||
}
|
||||
navigate(`/admin/results?date=${formData.date}`);
|
||||
} catch (err) {
|
||||
setError('Failed to publish result');
|
||||
console.error("Error submitting form:", err);
|
||||
setError(isEdit ? 'Failed to update result' : 'Failed to publish result');
|
||||
setSubmitting(false);
|
||||
}
|
||||
};
|
||||
@ -474,14 +632,14 @@ const ResultForm = ({ isEdit = false }) => {
|
||||
<div className="form-group">
|
||||
<label>Team</label>
|
||||
<select
|
||||
name="team_id"
|
||||
value={formData.team_id}
|
||||
name="team"
|
||||
value={formData.team}
|
||||
onChange={handleChange}
|
||||
required
|
||||
>
|
||||
<option value="">Select a team</option>
|
||||
{teams.map(team => (
|
||||
<option key={team.id} value={team.id}>{team.name}</option>
|
||||
<option key={team.name} value={team.name}>{team.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
@ -492,7 +650,7 @@ const ResultForm = ({ isEdit = false }) => {
|
||||
name="result"
|
||||
value={formData.result}
|
||||
onChange={handleChange}
|
||||
placeholder="e.g., Win 3-2"
|
||||
placeholder="e.g., 45"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
@ -500,8 +658,18 @@ const ResultForm = ({ isEdit = false }) => {
|
||||
<label>Date</label>
|
||||
<input
|
||||
type="date"
|
||||
name="result_date"
|
||||
value={formData.result_date}
|
||||
name="date"
|
||||
value={formData.date}
|
||||
onChange={handleChange}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Announcement Time</label>
|
||||
<input
|
||||
type="time"
|
||||
name="announcement_time"
|
||||
value={formData.announcement_time}
|
||||
onChange={handleChange}
|
||||
required
|
||||
/>
|
||||
@ -513,7 +681,256 @@ const ResultForm = ({ isEdit = false }) => {
|
||||
>
|
||||
{submitting ? 'Saving...' : (isEdit ? 'Update Result' : 'Publish Result')}
|
||||
</button>
|
||||
<Link to={`/results?date=${formData.result_date}`} className="btn-secondary">Cancel</Link>
|
||||
<Link to={`/admin/results?date=${formData.date}`} className="btn-secondary">Cancel</Link>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Scheduled Games Components
|
||||
const ScheduleCalendar = () => {
|
||||
const [date, setDate] = useState(new Date().toISOString().split('T')[0]);
|
||||
const [scheduledGames, setScheduledGames] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState('');
|
||||
const { token } = React.useContext(AuthContext);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchScheduledGames = async () => {
|
||||
try {
|
||||
const data = await apiService.getScheduledGames(date, token);
|
||||
setScheduledGames(data);
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
setError('Failed to fetch scheduled games');
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchScheduledGames();
|
||||
}, [date, token]);
|
||||
|
||||
const handleDateChange = (e) => {
|
||||
setDate(e.target.value);
|
||||
};
|
||||
|
||||
const handleDeleteScheduledGame = async (id) => {
|
||||
if (window.confirm('Are you sure you want to delete this scheduled game?')) {
|
||||
try {
|
||||
await apiService.deleteScheduledGame(id, token);
|
||||
setScheduledGames(scheduledGames.filter(game => game.id !== id));
|
||||
} catch (err) {
|
||||
setError('Failed to delete scheduled game');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) return <div>Loading...</div>;
|
||||
if (error) return <div className="error">{error}</div>;
|
||||
|
||||
return (
|
||||
<div className="schedule-container">
|
||||
<h2>Scheduled Games</h2>
|
||||
<div className="date-picker">
|
||||
<label>Select Date: </label>
|
||||
<input
|
||||
type="date"
|
||||
value={date}
|
||||
onChange={handleDateChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="scheduled-games-container">
|
||||
<h3>Games scheduled for {date}</h3>
|
||||
<Link to={`/admin/schedule/new?date=${date}`} className="btn-primary">Schedule New Game</Link>
|
||||
|
||||
{scheduledGames.length === 0 ? (
|
||||
<p>No games scheduled for this date.</p>
|
||||
) : (
|
||||
<table className="schedule-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Home Team</th>
|
||||
<th>Away Team</th>
|
||||
<th>Time</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{scheduledGames.map(game => (
|
||||
<tr key={game.id}>
|
||||
<td>{game.home_team_name}</td>
|
||||
<td>{game.away_team_name}</td>
|
||||
<td>{game.game_time}</td>
|
||||
<td>{game.status}</td>
|
||||
<td>
|
||||
<Link to={`/admin/schedule/edit/${game.id}?date=${date}`} className="btn-secondary">Edit</Link>
|
||||
<button
|
||||
onClick={() => handleDeleteScheduledGame(game.id)}
|
||||
className="btn-danger"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ScheduleForm = ({ isEdit = false }) => {
|
||||
const [formData, setFormData] = useState({
|
||||
home_team: '',
|
||||
away_team: '',
|
||||
game_date: new Date().toISOString().split('T')[0],
|
||||
game_time: '12:00:00',
|
||||
status: 'SCHEDULED'
|
||||
});
|
||||
const [teams, setTeams] = useState([]);
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
const { token } = React.useContext(AuthContext);
|
||||
const { id } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const teamsData = await apiService.getTeams(token);
|
||||
setTeams(teamsData);
|
||||
|
||||
// Set date from query params if available
|
||||
const params = new URLSearchParams(location.search);
|
||||
const dateParam = params.get('date');
|
||||
if (dateParam) {
|
||||
setFormData(prev => ({ ...prev, game_date: dateParam }));
|
||||
}
|
||||
|
||||
// If editing, fetch the scheduled game details
|
||||
if (isEdit && id) {
|
||||
const games = await apiService.getScheduledGames(dateParam || formData.game_date, token);
|
||||
const game = games.find(g => g.id === parseInt(id));
|
||||
if (game) {
|
||||
setFormData({
|
||||
home_team: game.home_team_id.toString(),
|
||||
away_team: game.away_team_id.toString(),
|
||||
game_date: game.game_date,
|
||||
game_time: game.game_time,
|
||||
status: game.status
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
setError('Failed to fetch data');
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, [isEdit, id, token, location.search, formData.game_date]);
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData(prev => ({ ...prev, [name]: value }));
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setSubmitting(true);
|
||||
|
||||
try {
|
||||
if (isEdit) {
|
||||
await apiService.updateScheduledGame(id, formData, token);
|
||||
} else {
|
||||
await apiService.createScheduledGame(formData, token);
|
||||
}
|
||||
navigate(`/admin/schedule?date=${formData.game_date}`);
|
||||
} catch (err) {
|
||||
setError(isEdit ? 'Failed to update scheduled game' : 'Failed to create scheduled game');
|
||||
setSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="schedule-form-container">
|
||||
<h2>{isEdit ? 'Edit Scheduled Game' : 'Schedule New Game'}</h2>
|
||||
{error && <div className="error">{error}</div>}
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="form-group">
|
||||
<label>Home Team</label>
|
||||
<select
|
||||
name="home_team"
|
||||
value={formData.home_team}
|
||||
onChange={handleChange}
|
||||
required
|
||||
>
|
||||
<option value="">Select home team</option>
|
||||
{teams.map(team => (
|
||||
<option key={team.id} value={team.id}>{team.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Away Team</label>
|
||||
<select
|
||||
name="away_team"
|
||||
value={formData.away_team}
|
||||
onChange={handleChange}
|
||||
required
|
||||
>
|
||||
<option value="">Select away team</option>
|
||||
{teams.map(team => (
|
||||
<option key={team.id} value={team.id}>{team.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Date</label>
|
||||
<input
|
||||
type="date"
|
||||
name="game_date"
|
||||
value={formData.game_date}
|
||||
onChange={handleChange}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Time</label>
|
||||
<input
|
||||
type="time"
|
||||
name="game_time"
|
||||
value={formData.game_time}
|
||||
onChange={handleChange}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Status</label>
|
||||
<select
|
||||
name="status"
|
||||
value={formData.status}
|
||||
onChange={handleChange}
|
||||
required
|
||||
>
|
||||
<option value="SCHEDULED">Scheduled</option>
|
||||
<option value="LIVE">Live</option>
|
||||
<option value="COMPLETED">Completed</option>
|
||||
<option value="CANCELLED">Cancelled</option>
|
||||
</select>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
className="btn-primary"
|
||||
disabled={submitting}
|
||||
>
|
||||
{submitting ? 'Saving...' : (isEdit ? 'Update Game' : 'Schedule Game')}
|
||||
</button>
|
||||
<Link to={`/admin/schedule?date=${formData.game_date}`} className="btn-secondary">Cancel</Link>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
@ -532,6 +949,7 @@ const Dashboard = () => {
|
||||
<nav className="dashboard-nav">
|
||||
<Link to="/teams" className="nav-link">Teams</Link>
|
||||
<Link to="/results" className="nav-link">Results</Link>
|
||||
<Link to="/schedule" className="nav-link">Schedule</Link>
|
||||
</nav>
|
||||
|
||||
<div className="dashboard-content">
|
||||
@ -542,6 +960,9 @@ const Dashboard = () => {
|
||||
<Route path="/results" element={<ResultCalendar />} />
|
||||
<Route path="/results/new" element={<ResultForm />} />
|
||||
<Route path="/results/edit/:id" element={<ResultForm isEdit={true} />} />
|
||||
<Route path="/schedule" element={<ScheduleCalendar />} />
|
||||
<Route path="/schedule/new" element={<ScheduleForm />} />
|
||||
<Route path="/schedule/edit/:id" element={<ScheduleForm isEdit={true} />} />
|
||||
<Route path="/" element={<Navigate to="/teams" />} />
|
||||
</Routes>
|
||||
</div>
|
||||
@ -549,6 +970,7 @@ const Dashboard = () => {
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
// Protected Route
|
||||
const ProtectedRoute = ({ children }) => {
|
||||
const { isAuthenticated } = React.useContext(AuthContext);
|
||||
@ -568,7 +990,7 @@ const App = () => {
|
||||
<Routes>
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route
|
||||
path="/admin"
|
||||
path="/*"
|
||||
element={
|
||||
<ProtectedRoute>
|
||||
<Dashboard />
|
||||
|
||||
@ -1,387 +1,591 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { PlusCircle, Trash2, Edit, BarChart2 } from 'lucide-react';
|
||||
// import dataService from '../services/dataService';
|
||||
import dataService from '../services/DataService';
|
||||
import { BrowserRouter as Router, Route, Routes, Navigate, Link } from 'react-router-dom';
|
||||
import './App.css';
|
||||
import { useParams,useNavigate,useLocation } from 'react-router-dom';
|
||||
import Home from './pages/Home';
|
||||
import TeamResults from './pages/TeamResult';
|
||||
// Auth Context
|
||||
const AuthContext = React.createContext();
|
||||
|
||||
const AdminPanel = () => {
|
||||
const AuthProvider = ({ children }) => {
|
||||
const [token, setToken] = useState(localStorage.getItem('token'));
|
||||
|
||||
const login = (newToken) => {
|
||||
localStorage.setItem('token', newToken);
|
||||
setToken(newToken);
|
||||
};
|
||||
|
||||
const logout = () => {
|
||||
localStorage.removeItem('token');
|
||||
setToken(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={{ token, login, logout, isAuthenticated: !!token }}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
// API Service
|
||||
const API_URL = 'http://localhost:5500';
|
||||
|
||||
const apiService = {
|
||||
login: async (accessKey, password) => {
|
||||
const response = await fetch(`${API_URL}/admin/login`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ accessKey, password })
|
||||
});
|
||||
console.log(accessKey);
|
||||
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Login failed');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
},
|
||||
|
||||
getTeams: async (token) => {
|
||||
const response = await fetch(`${API_URL}/api/teams`, {
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch teams');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
},
|
||||
|
||||
createTeam: async (teamData, token) => {
|
||||
const response = await fetch(`${API_URL}/api/teams`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify(teamData)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to create team');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
},
|
||||
|
||||
updateTeam: async (id, teamData, token) => {
|
||||
const response = await fetch(`${API_URL}/api/teams/${id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify(teamData)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to update team');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
},
|
||||
|
||||
deleteTeam: async (id, token) => {
|
||||
const response = await fetch(`${API_URL}/api/teams/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to delete team');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
},
|
||||
|
||||
publishResult: async (resultData, token) => {
|
||||
const response = await fetch(`${API_URL}/admin/results`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify(resultData)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to publish result');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
},
|
||||
|
||||
getDailyResults: async (date, token) => {
|
||||
const response = await fetch(`${API_URL}/api/results/daily?date=${date}`, {
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch daily results');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
};
|
||||
|
||||
// Components
|
||||
const Login = () => {
|
||||
const [accessKey, setAccessKey] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const { login } = React.useContext(AuthContext);
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
const data = await apiService.login(accessKey, password);
|
||||
login(data.token);
|
||||
// redirection
|
||||
|
||||
} catch (err) {
|
||||
setError('Invalid credentials');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="login-container">
|
||||
<h2>Admin Login</h2>
|
||||
{error && <div className="error">{error}</div>}
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="form-group">
|
||||
<label>Access Key</label>
|
||||
<input
|
||||
type="text"
|
||||
value={accessKey}
|
||||
onChange={(e) => setAccessKey(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Password</label>
|
||||
<input
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<button type="submit" className="btn-primary">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const TeamList = () => {
|
||||
const [teams, setTeams] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
const [error, setError] = useState('');
|
||||
const { token } = React.useContext(AuthContext);
|
||||
|
||||
const [selectedTeam, setSelectedTeam] = useState(null);
|
||||
const [showAddForm, setShowAddForm] = useState(false);
|
||||
const [showEditForm, setShowEditForm] = useState(false);
|
||||
const [showChartView, setShowChartView] = useState(false);
|
||||
const [formData, setFormData] = useState({ name: '', time: '', result: '' });
|
||||
const [dates] = useState(['2025-03-11', '2025-03-12']);
|
||||
const [currentDate, setCurrentDate] = useState('2025-03-12');
|
||||
|
||||
// Fetch teams on component mount
|
||||
useEffect(() => {
|
||||
const fetchTeams = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const data = await dataService.getTeams();
|
||||
const data = await apiService.getTeams(token);
|
||||
setTeams(data);
|
||||
setError(null);
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
setError('Failed to load teams data');
|
||||
console.error(err);
|
||||
} finally {
|
||||
setError('Failed to fetch teams');
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
fetchTeams();
|
||||
|
||||
// Subscribe to real-time updates
|
||||
const unsubscribe = dataService.subscribeToUpdates((updatedTeams) => {
|
||||
setTeams(updatedTeams);
|
||||
});
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Handle input changes
|
||||
const handleInputChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData({ ...formData, [name]: value });
|
||||
}, [token]);
|
||||
|
||||
const handleDelete = async (id) => {
|
||||
if (window.confirm('Are you sure you want to delete this team?')) {
|
||||
try {
|
||||
await apiService.deleteTeam(id, token);
|
||||
setTeams(teams.filter(team => team.id !== id));
|
||||
} catch (err) {
|
||||
setError('Failed to delete team');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) return <div>Loading...</div>;
|
||||
if (error) return <div className="error">{error}</div>;
|
||||
|
||||
return (
|
||||
<div className="team-list-container">
|
||||
<h2>Team Management</h2>
|
||||
<Link to="/teams/new" className="btn-primary">Add New Team</Link>
|
||||
<table className="team-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{teams.map(team => (
|
||||
<tr key={team.id}>
|
||||
<td>{team.id}</td>
|
||||
<td>{team.name}</td>
|
||||
<td>
|
||||
<Link to={`/teams/edit/${team.id}`} className="btn-secondary">Edit</Link>
|
||||
<button
|
||||
onClick={() => handleDelete(team.id)}
|
||||
className="btn-danger"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Add new team
|
||||
const handleAddTeam = async () => {
|
||||
try {
|
||||
const newTeamData = {
|
||||
name: formData.name,
|
||||
time: formData.time,
|
||||
results: {
|
||||
[dates[0]]: '',
|
||||
[dates[1]]: ''
|
||||
const TeamForm = ({ isEdit = false }) => {
|
||||
const [name, setName] = useState('');
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
const { token } = React.useContext(AuthContext);
|
||||
const { id } = useParams();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (isEdit && id) {
|
||||
const fetchTeam = async () => {
|
||||
try {
|
||||
const teams = await apiService.getTeams(token);
|
||||
const team = teams.find(t => t.id === parseInt(id));
|
||||
if (team) {
|
||||
setName(team.name);
|
||||
}
|
||||
} catch (err) {
|
||||
setError('Failed to fetch team details');
|
||||
}
|
||||
};
|
||||
|
||||
await dataService.addTeam(newTeamData);
|
||||
setFormData({ name: '', time: '', result: '' });
|
||||
setShowAddForm(false);
|
||||
} catch (err) {
|
||||
setError('Failed to add team');
|
||||
console.error(err);
|
||||
fetchTeam();
|
||||
}
|
||||
};
|
||||
|
||||
// Delete team
|
||||
const handleDeleteTeam = async (id) => {
|
||||
}, [isEdit, id, token]);
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setSubmitting(true);
|
||||
|
||||
try {
|
||||
await dataService.deleteTeam(id);
|
||||
if (isEdit) {
|
||||
await apiService.updateTeam(id, { name }, token);
|
||||
} else {
|
||||
await apiService.createTeam({ name }, token);
|
||||
}
|
||||
navigate('/teams');
|
||||
} catch (err) {
|
||||
setError('Failed to delete team');
|
||||
console.error(err);
|
||||
setError(isEdit ? 'Failed to update team' : 'Failed to create team');
|
||||
setSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Select team for editing
|
||||
const handleSelectTeam = (team) => {
|
||||
setSelectedTeam(team);
|
||||
setFormData({
|
||||
name: team.name,
|
||||
time: team.time,
|
||||
result: team.results[currentDate] || ''
|
||||
});
|
||||
setShowEditForm(true);
|
||||
setShowChartView(false);
|
||||
};
|
||||
|
||||
// Update team
|
||||
const handleUpdateTeam = async () => {
|
||||
try {
|
||||
if (!selectedTeam) return;
|
||||
|
||||
const updatedResults = { ...selectedTeam.results };
|
||||
updatedResults[currentDate] = formData.result;
|
||||
|
||||
const updatedTeamData = {
|
||||
name: formData.name,
|
||||
time: formData.time,
|
||||
results: updatedResults
|
||||
};
|
||||
|
||||
await dataService.updateTeam(selectedTeam.id, updatedTeamData);
|
||||
setShowEditForm(false);
|
||||
setSelectedTeam(null);
|
||||
setFormData({ name: '', time: '', result: '' });
|
||||
} catch (err) {
|
||||
setError('Failed to update team');
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
// Show chart for selected team
|
||||
const handleViewChart = (team) => {
|
||||
setSelectedTeam(team);
|
||||
setShowChartView(true);
|
||||
setShowEditForm(false);
|
||||
};
|
||||
|
||||
// Generate mock chart data for the selected team
|
||||
const generateChartData = () => {
|
||||
if (!selectedTeam) return [];
|
||||
|
||||
// Generate some random data for demonstration
|
||||
const mockData = [];
|
||||
const currentDate = new Date();
|
||||
|
||||
for (let i = 0; i < 30; i++) {
|
||||
const date = new Date(currentDate);
|
||||
date.setDate(date.getDate() - i);
|
||||
const dateStr = date.toISOString().split('T')[0];
|
||||
|
||||
mockData.unshift({
|
||||
date: dateStr,
|
||||
result: Math.floor(Math.random() * 100).toString().padStart(2, '0')
|
||||
});
|
||||
}
|
||||
|
||||
return mockData;
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return <div className="flex justify-center items-center h-screen">Loading...</div>;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <div className="flex justify-center items-center h-screen text-red-500">{error}</div>;
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className="bg-gray-100 min-h-screen p-4">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<div className="bg-emerald-400 p-4 text-white text-center text-xl font-bold rounded-t-lg">
|
||||
Bikaner Super Satta Result Admin Panel
|
||||
<div className="team-form-container">
|
||||
<h2>{isEdit ? 'Edit Team' : 'Add New Team'}</h2>
|
||||
{error && <div className="error">{error}</div>}
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="form-group">
|
||||
<label>Team Name</label>
|
||||
<input
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
className="btn-primary"
|
||||
disabled={submitting}
|
||||
>
|
||||
{submitting ? 'Saving...' : (isEdit ? 'Update Team' : 'Create Team')}
|
||||
</button>
|
||||
<Link to="/teams" className="btn-secondary">Cancel</Link>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ResultCalendar = () => {
|
||||
const [date, setDate] = useState(new Date().toISOString().split('T')[0]);
|
||||
const [results, setResults] = useState([]);
|
||||
const [teams, setTeams] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState('');
|
||||
const { token } = React.useContext(AuthContext);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const [teamsData, resultsData] = await Promise.all([
|
||||
apiService.getTeams(token),
|
||||
apiService.getDailyResults(date, token)
|
||||
]);
|
||||
setTeams(teamsData);
|
||||
setResults(resultsData);
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
setError('Failed to fetch data');
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, [date, token]);
|
||||
|
||||
const handleDateChange = (e) => {
|
||||
setDate(e.target.value);
|
||||
};
|
||||
|
||||
if (loading) return <div>Loading...</div>;
|
||||
if (error) return <div className="error">{error}</div>;
|
||||
|
||||
return (
|
||||
<div className="calendar-container">
|
||||
<h2>Results Calendar</h2>
|
||||
<div className="date-picker">
|
||||
<label>Select Date: </label>
|
||||
<input
|
||||
type="date"
|
||||
value={date}
|
||||
onChange={handleDateChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="results-container">
|
||||
<h3>Results for {date}</h3>
|
||||
<Link to={`/results/new?date=${date}`} className="btn-primary">Add New Result</Link>
|
||||
|
||||
{/* Controls */}
|
||||
<div className="bg-white p-4 mb-4 flex justify-between items-center">
|
||||
<button
|
||||
className="bg-green-500 text-white px-4 py-2 rounded flex items-center gap-2"
|
||||
onClick={() => {
|
||||
setShowAddForm(true);
|
||||
setShowEditForm(false);
|
||||
setShowChartView(false);
|
||||
setFormData({ name: '', time: '', result: '' });
|
||||
}}
|
||||
>
|
||||
<PlusCircle size={16} />
|
||||
Add Team
|
||||
</button>
|
||||
|
||||
<div>
|
||||
<select
|
||||
className="border p-2 rounded"
|
||||
value={currentDate}
|
||||
onChange={(e) => setCurrentDate(e.target.value)}
|
||||
>
|
||||
{dates.map(date => (
|
||||
<option key={date} value={date}>
|
||||
{new Date(date).toLocaleDateString()}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Add Form */}
|
||||
{showAddForm && (
|
||||
<div className="bg-white p-4 mb-4 rounded shadow">
|
||||
<h2 className="text-lg font-semibold mb-4">Add New Team</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">Team Name</label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
value={formData.name}
|
||||
onChange={handleInputChange}
|
||||
className="w-full p-2 border rounded"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">Time</label>
|
||||
<input
|
||||
type="text"
|
||||
name="time"
|
||||
value={formData.time}
|
||||
onChange={handleInputChange}
|
||||
className="w-full p-2 border rounded"
|
||||
placeholder="e.g. 02:20 AM"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4 flex justify-end gap-2">
|
||||
<button
|
||||
className="bg-gray-300 px-4 py-2 rounded"
|
||||
onClick={() => setShowAddForm(false)}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
className="bg-green-500 text-white px-4 py-2 rounded"
|
||||
onClick={handleAddTeam}
|
||||
>
|
||||
Add Team
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Edit Form */}
|
||||
{showEditForm && selectedTeam && (
|
||||
<div className="bg-white p-4 mb-4 rounded shadow">
|
||||
<h2 className="text-lg font-semibold mb-4">Edit Team: {selectedTeam.name}</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">Team Name</label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
value={formData.name}
|
||||
onChange={handleInputChange}
|
||||
className="w-full p-2 border rounded"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">Time</label>
|
||||
<input
|
||||
type="text"
|
||||
name="time"
|
||||
value={formData.time}
|
||||
onChange={handleInputChange}
|
||||
className="w-full p-2 border rounded"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">Result for {new Date(currentDate).toLocaleDateString()}</label>
|
||||
<input
|
||||
type="text"
|
||||
name="result"
|
||||
value={formData.result}
|
||||
onChange={handleInputChange}
|
||||
className="w-full p-2 border rounded"
|
||||
placeholder="e.g. 61"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4 flex justify-end gap-2">
|
||||
<button
|
||||
className="bg-gray-300 px-4 py-2 rounded"
|
||||
onClick={() => setShowEditForm(false)}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
className="bg-blue-500 text-white px-4 py-2 rounded"
|
||||
onClick={handleUpdateTeam}
|
||||
>
|
||||
Update Team
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Chart View */}
|
||||
{showChartView && selectedTeam && (
|
||||
<div className="bg-white p-4 mb-4 rounded shadow">
|
||||
<h2 className="text-lg font-semibold mb-4">Monthly Chart: {selectedTeam.name}</h2>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full border-collapse">
|
||||
<thead>
|
||||
<tr className="bg-gray-100">
|
||||
<th className="border p-2 text-left">Date</th>
|
||||
<th className="border p-2 text-right">Result</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{generateChartData().map((item, index) => (
|
||||
<tr key={index} className={index % 2 === 0 ? 'bg-gray-50' : 'bg-white'}>
|
||||
<td className="border p-2">{new Date(item.date).toLocaleDateString()}</td>
|
||||
<td className="border p-2 text-right font-bold">{item.result}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="mt-4 flex justify-end">
|
||||
<button
|
||||
className="bg-gray-300 px-4 py-2 rounded"
|
||||
onClick={() => setShowChartView(false)}
|
||||
>
|
||||
Back to List
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Teams Table */}
|
||||
<div className="bg-white rounded-b-lg overflow-hidden">
|
||||
<table className="w-full border-collapse">
|
||||
{results.length === 0 ? (
|
||||
<p>No results for this date.</p>
|
||||
) : (
|
||||
<table className="results-table">
|
||||
<thead>
|
||||
<tr className="bg-gray-800 text-white">
|
||||
<th className="p-3 text-left">Games List</th>
|
||||
<th className="p-3 text-center">
|
||||
{new Date(dates[0]).toLocaleDateString()} <br/>
|
||||
{new Date(dates[0]).toLocaleDateString("en-US", {weekday: 'short'})}
|
||||
</th>
|
||||
<th className="p-3 text-center">
|
||||
{new Date(dates[1]).toLocaleDateString()} <br/>
|
||||
{new Date(dates[1]).toLocaleDateString("en-US", {weekday: 'short'})}
|
||||
</th>
|
||||
<th className="p-3 text-center">Actions</th>
|
||||
<tr>
|
||||
<th>Team</th>
|
||||
<th>Result</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{teams.map(team => (
|
||||
<tr key={team.id} className="border-b hover:bg-gray-50">
|
||||
<td className="p-3">
|
||||
<div className="font-semibold">{team.name}</div>
|
||||
<div className="text-sm text-gray-500">at {team.time}</div>
|
||||
</td>
|
||||
<td className="p-3 text-center text-2xl font-bold">{team.results[dates[0]] || 'XX'}</td>
|
||||
<td className="p-3 text-center text-2xl font-bold">{team.results[dates[1]] || 'XX'}</td>
|
||||
<td className="p-3">
|
||||
<div className="flex justify-center gap-2">
|
||||
<button
|
||||
className="p-2 bg-blue-100 text-blue-600 rounded"
|
||||
onClick={() => handleSelectTeam(team)}
|
||||
title="Edit Team"
|
||||
>
|
||||
<Edit size={16} />
|
||||
</button>
|
||||
<button
|
||||
className="p-2 bg-red-100 text-red-600 rounded"
|
||||
onClick={() => handleDeleteTeam(team.id)}
|
||||
title="Delete Team"
|
||||
>
|
||||
<Trash2 size={16} />
|
||||
</button>
|
||||
<button
|
||||
className="p-2 bg-green-100 text-green-600 rounded"
|
||||
onClick={() => handleViewChart(team)}
|
||||
title="View Monthly Chart"
|
||||
>
|
||||
<BarChart2 size={16} />
|
||||
</button>
|
||||
</div>
|
||||
{results.map(result => (
|
||||
<tr key={result.id}>
|
||||
<td>{result.team}</td>
|
||||
<td>{result.result}</td>
|
||||
<td>
|
||||
<Link to={`/results/edit/${result.id}?date=${date}`} className="btn-secondary">Edit</Link>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AdminPanel;
|
||||
const ResultForm = ({ isEdit = false }) => {
|
||||
const [formData, setFormData] = useState({
|
||||
team: '',
|
||||
result: '',
|
||||
date: new Date().toISOString().split('T')[0]
|
||||
});
|
||||
const [teams, setTeams] = useState([]);
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
const { token } = React.useContext(AuthContext);
|
||||
const { id } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
const fetchTeams = async () => {
|
||||
try {
|
||||
const teamsData = await apiService.getTeams(token);
|
||||
setTeams(teamsData);
|
||||
|
||||
// Set date from query params if available
|
||||
const params = new URLSearchParams(location.search);
|
||||
const dateParam = params.get('date');
|
||||
if (dateParam) {
|
||||
setFormData(prev => ({ ...prev, date: dateParam }));
|
||||
}
|
||||
|
||||
// If editing, fetch the result details
|
||||
if (isEdit && id) {
|
||||
// This is a simplified approach. In a real app, you'd have an API endpoint to fetch a specific result
|
||||
const results = await apiService.getDailyResults(dateParam, token);
|
||||
const result = results.find(r => r.id === parseInt(id));
|
||||
console.log(result.team);
|
||||
if (result) {
|
||||
console.log(result)
|
||||
setFormData({
|
||||
team: result.team,
|
||||
result: result.result,
|
||||
date: result.date
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
setError('Failed to fetch data');
|
||||
}
|
||||
};
|
||||
|
||||
fetchTeams();
|
||||
}, [isEdit, id, token, location.search]);
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData(prev => ({ ...prev, [name]: value }));
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setSubmitting(true);
|
||||
|
||||
try {
|
||||
await apiService.publishResult(formData, token);
|
||||
navigate(`/results?date=${formData.date}`);
|
||||
} catch (err) {
|
||||
setError('Failed to publish result');
|
||||
setSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="result-form-container">
|
||||
<h2>{isEdit ? 'Edit Result' : 'Add New Result'}</h2>
|
||||
{error && <div className="error">{error}</div>}
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="form-group">
|
||||
<label>Team</label>
|
||||
<select
|
||||
name="team"
|
||||
value={formData.team}
|
||||
onChange={handleChange}
|
||||
required
|
||||
>
|
||||
<option value="">Select a team</option>
|
||||
{teams.map(team => (
|
||||
<option key={team.id} value={team.id}>{team.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Result</label>
|
||||
<input
|
||||
type="text"
|
||||
name="result"
|
||||
value={formData.result}
|
||||
onChange={handleChange}
|
||||
placeholder="e.g., Win 3-2"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Date</label>
|
||||
<input
|
||||
type="date"
|
||||
name="date"
|
||||
value={formData.date}
|
||||
onChange={handleChange}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
className="btn-primary"
|
||||
disabled={submitting}
|
||||
>
|
||||
{submitting ? 'Saving...' : (isEdit ? 'Update Result' : 'Publish Result')}
|
||||
</button>
|
||||
<Link to={`/results?date=${formData.date}`} className="btn-secondary">Cancel</Link>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Dashboard = () => {
|
||||
const { logout } = React.useContext(AuthContext);
|
||||
|
||||
return (
|
||||
<div className="dashboard-container">
|
||||
<header className="dashboard-header">
|
||||
<h1>Admin Dashboard</h1>
|
||||
<button onClick={logout} className="btn-danger">Logout</button>
|
||||
</header>
|
||||
|
||||
<nav className="dashboard-nav">
|
||||
<Link to="/teams" className="nav-link">Teams</Link>
|
||||
<Link to="/results" className="nav-link">Results</Link>
|
||||
</nav>
|
||||
|
||||
<div className="dashboard-content">
|
||||
<Routes>
|
||||
<Route path="/teams" element={<TeamList />} />
|
||||
<Route path="/teams/new" element={<TeamForm />} />
|
||||
<Route path="/teams/edit/:id" element={<TeamForm isEdit={true} />} />
|
||||
<Route path="/results" element={<ResultCalendar />} />
|
||||
<Route path="/results/new" element={<ResultForm />} />
|
||||
<Route path="/results/edit/:id" element={<ResultForm isEdit={true} />} />
|
||||
<Route path="/" element={<Navigate to="/teams" />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Protected Route
|
||||
const ProtectedRoute = ({ children }) => {
|
||||
const { isAuthenticated } = React.useContext(AuthContext);
|
||||
|
||||
if (!isAuthenticated) {
|
||||
return <Navigate to="/login" />;
|
||||
}
|
||||
|
||||
return children;
|
||||
};
|
||||
|
||||
// App
|
||||
const App = () => {
|
||||
return (
|
||||
<Router>
|
||||
<AuthProvider>
|
||||
<Routes>
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route
|
||||
path="/*"
|
||||
element={
|
||||
<ProtectedRoute>
|
||||
<Dashboard />
|
||||
</ProtectedRoute>
|
||||
}
|
||||
/>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/res" element={<TeamResults />} />
|
||||
</Routes>
|
||||
</AuthProvider>
|
||||
</Router>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
474
src/pages/Home2.js
Normal file
474
src/pages/Home2.js
Normal file
@ -0,0 +1,474 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { BarChart2, Calendar, RefreshCw } from 'lucide-react';
|
||||
import axios from 'axios';
|
||||
|
||||
const Home2 = () => {
|
||||
const [teams, setTeams] = useState([]);
|
||||
const [dates, setDates] = useState([]);
|
||||
const [selectedTeam, setSelectedTeam] = useState(null);
|
||||
const [showChartView, setShowChartView] = useState(false);
|
||||
const [showCalendar, setShowCalendar] = useState(false);
|
||||
const [currentTime, setCurrentTime] = useState("");
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
const [calendarData, setCalendarData] = useState([]);
|
||||
const [currentMonth, setCurrentMonth] = useState(new Date());
|
||||
|
||||
// API URL
|
||||
const API_URL = 'http://localhost:5500/api';
|
||||
|
||||
// Fetch teams data
|
||||
useEffect(() => {
|
||||
|
||||
const fetchTeams = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
// Get all teams
|
||||
const teamsResponse = await axios.get(`${API_URL}/teams`);
|
||||
alert("teamsResponse");
|
||||
|
||||
// Get today's date and format it
|
||||
const today = new Date();
|
||||
const todayFormatted = today.toISOString().split('T')[0];
|
||||
// Get yesterday's date and format it
|
||||
const yesterday = new Date();
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
const yesterdayFormatted = yesterday.toISOString().split('T')[0];
|
||||
|
||||
// Set dates for display
|
||||
setDates([yesterdayFormatted, todayFormatted]);
|
||||
|
||||
// Get today's results
|
||||
const todayResultsResponse = await axios.get(`${API_URL}/today`);
|
||||
|
||||
// Get yesterday's results for each team
|
||||
const yesterdayResultsPromises = teamsResponse.data.map(team =>
|
||||
axios.get(`${API_URL}/results?team=${team.name}&date=${yesterdayFormatted}`)
|
||||
.then(response => response.data)
|
||||
.catch(() => null) // If no result, return null
|
||||
);
|
||||
|
||||
const yesterdayResults = await Promise.all(yesterdayResultsPromises);
|
||||
|
||||
// Combine team data with results
|
||||
const teamsWithResults = teamsResponse.data.map((team, index) => {
|
||||
const results = {};
|
||||
|
||||
// Add yesterday's result if available
|
||||
if (yesterdayResults[index]) {
|
||||
results[yesterdayFormatted] = yesterdayResults[index].result;
|
||||
}
|
||||
|
||||
// Add today's result if available
|
||||
const todayResult = todayResultsResponse.data.find(r => r.team === team.name);
|
||||
if (todayResult) {
|
||||
results[todayFormatted] = todayResult.result;
|
||||
}
|
||||
|
||||
// Extract time from team name or use default
|
||||
let time = "XX:XX";
|
||||
const timePart = team.name.match(/\d{2}:\d{2}\s*(?:AM|PM)/i);
|
||||
if (timePart) {
|
||||
time = timePart[0];
|
||||
}
|
||||
|
||||
return {
|
||||
id: team.id,
|
||||
name: team.name,
|
||||
time: time,
|
||||
results: results
|
||||
};
|
||||
});
|
||||
|
||||
setTeams(teamsWithResults);
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
console.error("Error fetching data:", err);
|
||||
setError("Failed to load team data. Please try again later.");
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchTeams();
|
||||
|
||||
// Update current time every minute
|
||||
const interval = setInterval(() => {
|
||||
const now = new Date();
|
||||
const formattedTime = now.toLocaleString("en-IN", { timeZone: "Asia/Kolkata" });
|
||||
setCurrentTime(formattedTime);
|
||||
}, 60000);
|
||||
|
||||
// Set initial time
|
||||
const now = new Date();
|
||||
const formattedTime = now.toLocaleString("en-IN", { timeZone: "Asia/Kolkata" });
|
||||
setCurrentTime(formattedTime);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
// Show chart for selected team
|
||||
const handleViewChart = async (team) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
// Get monthly results for the selected team
|
||||
const currentDate = new Date();
|
||||
const month = currentDate.getMonth() + 1;
|
||||
const year = currentDate.getFullYear();
|
||||
|
||||
const response = await axios.post(`${API_URL}/results/monthly`, {
|
||||
team: team.name,
|
||||
month: `${year}-${month.toString().padStart(2, '0')}`
|
||||
});
|
||||
|
||||
setSelectedTeam({
|
||||
...team,
|
||||
chartData: response.data
|
||||
});
|
||||
|
||||
setShowChartView(true);
|
||||
setShowCalendar(false);
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
console.error("Error fetching chart data:", err);
|
||||
setError("Failed to load chart data. Please try again later.");
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Load calendar data
|
||||
const loadCalendarData = async (year, month) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
// Calculate first and last day of month
|
||||
const firstDay = new Date(year, month, 1).toISOString().split('T')[0];
|
||||
const lastDay = new Date(year, month + 1, 0).toISOString().split('T')[0];
|
||||
|
||||
// Get results for each day in the month
|
||||
const dailyResultsPromises = [];
|
||||
const currentDate = new Date(year, month, 1);
|
||||
const lastDate = new Date(year, month + 1, 0);
|
||||
|
||||
while (currentDate <= lastDate) {
|
||||
const dateString = currentDate.toISOString().split('T')[0];
|
||||
dailyResultsPromises.push(
|
||||
axios.get(`${API_URL}/results/daily?date=${dateString}`)
|
||||
.then(response => ({
|
||||
date: dateString,
|
||||
results: response.data
|
||||
}))
|
||||
.catch(() => ({
|
||||
date: dateString,
|
||||
results: []
|
||||
}))
|
||||
);
|
||||
currentDate.setDate(currentDate.getDate() + 1);
|
||||
}
|
||||
|
||||
const allResults = await Promise.all(dailyResultsPromises);
|
||||
|
||||
// Format calendar data
|
||||
const calendarDays = [];
|
||||
const firstDayOfMonth = new Date(year, month, 1);
|
||||
const firstDayWeekday = firstDayOfMonth.getDay();
|
||||
|
||||
// Add empty cells for days before the first of the month
|
||||
for (let i = 0; i < firstDayWeekday; i++) {
|
||||
calendarDays.push(null);
|
||||
}
|
||||
|
||||
// Add days with results
|
||||
for (let i = 1; i <= lastDate.getDate(); i++) {
|
||||
const dateObj = new Date(year, month, i);
|
||||
const dateStr = dateObj.toISOString().split('T')[0];
|
||||
|
||||
const dayData = allResults.find(r => r.date === dateStr);
|
||||
let teamResults = {};
|
||||
|
||||
if (dayData && dayData.results.length > 0) {
|
||||
dayData.results.forEach(result => {
|
||||
teamResults[result.team] = result.result;
|
||||
});
|
||||
}
|
||||
|
||||
calendarDays.push({
|
||||
day: i,
|
||||
date: dateStr,
|
||||
results: teamResults
|
||||
});
|
||||
}
|
||||
|
||||
setCalendarData(calendarDays);
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
console.error("Error loading calendar data:", err);
|
||||
setError("Failed to load calendar data. Please try again later.");
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Handle calendar view button click
|
||||
const handleCalendarView = () => {
|
||||
const now = new Date();
|
||||
setCurrentMonth(now);
|
||||
loadCalendarData(now.getFullYear(), now.getMonth());
|
||||
setShowCalendar(true);
|
||||
setShowChartView(false);
|
||||
};
|
||||
|
||||
// Handle month change in calendar
|
||||
const handleMonthChange = (increment) => {
|
||||
const newMonth = new Date(currentMonth);
|
||||
newMonth.setMonth(newMonth.getMonth() + increment);
|
||||
setCurrentMonth(newMonth);
|
||||
loadCalendarData(newMonth.getFullYear(), newMonth.getMonth());
|
||||
};
|
||||
|
||||
// Refresh data
|
||||
const handleRefresh = () => {
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-gray-100 min-h-screen p-4">
|
||||
<div className="w-full bg-gray-100 p-4 text-center">
|
||||
{/* Header */}
|
||||
<h1 className="text-3xl font-bold text-black uppercase">SATTA-KING-FAST.com</h1>
|
||||
|
||||
{/* Advertisement Banner */}
|
||||
<div className="mt-4 flex justify-center items-center">
|
||||
<img
|
||||
src="/api/placeholder/800/120"
|
||||
alt="Advertisement"
|
||||
className="w-full max-w-4xl"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Informational Text */}
|
||||
<p className="mt-4 text-gray-700 text-sm p-1 w-full lg:w-3/4 m-auto">
|
||||
Delhi Diamond Satta Result And Monthly Satta Chart of March 2025 With Combined Chart of Gali, Desawar, Ghaziabad, Faridabad And Shri Ganesh from Satta King Fast, Satta King Result, Satta King Chart, Black Satta King and Satta King 786.
|
||||
</p>
|
||||
|
||||
{/* Disclaimer */}
|
||||
<p className="mt-2 text-blue-600 text-sm font-medium bg-white p-1 w-full lg:w-3/4 m-auto">
|
||||
Satta-King-Fast.com is the most popular gaming discussion forum for players to use freely and we are not in partnership with any gaming company.
|
||||
</p>
|
||||
|
||||
{/* Warning Message */}
|
||||
<p className="mt-2 text-red-600 font-bold bg-white p-1 w-full lg:w-3/4 m-auto">
|
||||
कृपया ध्यान दें, लीक गेम के नाम पर किसी को कोई पैसा न दें, ना पहले ना बाद में - धन्यवाद
|
||||
</p>
|
||||
|
||||
{/* Contact Link */}
|
||||
<p className="mt-2 text-green-600 font-medium bg-white p-1 w-full lg:w-3/4 m-auto">
|
||||
हमसे संपर्क करने के लिए ➡ <a href="#" className="underline">यहाँ क्लिक करें</a>
|
||||
</p>
|
||||
|
||||
{/* Timestamp */}
|
||||
<p className="mt-2 text-gray-600 text-xs">
|
||||
Updated: {currentTime} IST.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="max-w-6xl mx-auto">
|
||||
{error && (
|
||||
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
|
||||
<p>{error}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="bg-emerald-400 p-4 text-white text-center text-xl font-bold rounded-t-lg">
|
||||
{teams.length > 0 && teams[0].name} Satta Result of {dates.length > 1 && new Date(dates[1]).toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' })} & {dates.length > 0 && new Date(dates[0]).toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' })}
|
||||
</div>
|
||||
|
||||
{/* Controls */}
|
||||
<div className="bg-white p-4 mb-4 flex justify-between items-center">
|
||||
<div className="text-lg font-semibold">Latest Results</div>
|
||||
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
className="bg-blue-500 text-white px-4 py-2 rounded flex items-center gap-2"
|
||||
onClick={handleCalendarView}
|
||||
disabled={loading}
|
||||
>
|
||||
<Calendar size={16} />
|
||||
Calendar View
|
||||
</button>
|
||||
<button
|
||||
className="bg-green-500 text-white px-4 py-2 rounded flex items-center gap-2"
|
||||
onClick={handleRefresh}
|
||||
disabled={loading}
|
||||
>
|
||||
<RefreshCw size={16} />
|
||||
Refresh
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Loading indicator */}
|
||||
{loading && (
|
||||
<div className="bg-white p-8 mb-4 rounded shadow flex justify-center items-center">
|
||||
<div className="flex items-center gap-2">
|
||||
<RefreshCw size={24} className="animate-spin text-blue-500" />
|
||||
<span>Loading data...</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Chart View */}
|
||||
{!loading && showChartView && selectedTeam && (
|
||||
<div className="bg-white p-4 mb-4 rounded shadow">
|
||||
<h2 className="text-lg font-semibold mb-4">Monthly Chart: {selectedTeam.name}</h2>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full border-collapse">
|
||||
<thead>
|
||||
<tr className="bg-gray-100">
|
||||
<th className="border p-2 text-left">Date</th>
|
||||
<th className="border p-2 text-right">Result</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{selectedTeam.chartData && selectedTeam.chartData.map((item, index) => (
|
||||
<tr key={index} className={index % 2 === 0 ? 'bg-gray-50' : 'bg-white'}>
|
||||
<td className="border p-2">{new Date(item.result_date).toLocaleDateString()}</td>
|
||||
<td className="border p-2 text-right font-bold">{item.result}</td>
|
||||
</tr>
|
||||
))}
|
||||
{(!selectedTeam.chartData || selectedTeam.chartData.length === 0) && (
|
||||
<tr>
|
||||
<td colSpan="2" className="border p-2 text-center">No chart data available</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="mt-4 flex justify-end">
|
||||
<button
|
||||
className="bg-gray-300 px-4 py-2 rounded"
|
||||
onClick={() => setShowChartView(false)}
|
||||
>
|
||||
Back to Results
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Calendar View */}
|
||||
{!loading && showCalendar && (
|
||||
<div className="bg-white p-4 mb-4 rounded shadow">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<button
|
||||
className="bg-gray-200 p-2 rounded"
|
||||
onClick={() => handleMonthChange(-1)}
|
||||
>
|
||||
Previous Month
|
||||
</button>
|
||||
|
||||
<h2 className="text-lg font-semibold">
|
||||
{currentMonth.toLocaleDateString('en-US', { month: 'long', year: 'numeric' })}
|
||||
</h2>
|
||||
|
||||
<button
|
||||
className="bg-gray-200 p-2 rounded"
|
||||
onClick={() => handleMonthChange(1)}
|
||||
>
|
||||
Next Month
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-7 gap-2 mb-2">
|
||||
{['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map(day => (
|
||||
<div key={day} className="text-center font-semibold">{day}</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-7 gap-2">
|
||||
{calendarData.map((day, index) => (
|
||||
<div key={index} className={`border rounded p-2 min-h-16 ${day ? 'bg-white' : ''}`}>
|
||||
{day && (
|
||||
<>
|
||||
<div className="text-right text-sm text-gray-500">{day.day}</div>
|
||||
{teams.map(team => (
|
||||
<div key={team.id} className="text-xs mt-1">
|
||||
{day.results[team.name] && (
|
||||
<>
|
||||
<span className="font-semibold">{team.name.split(' ')[0]}:</span> {day.results[team.name]}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-4 flex justify-end">
|
||||
<button
|
||||
className="bg-gray-300 px-4 py-2 rounded"
|
||||
onClick={() => setShowCalendar(false)}
|
||||
>
|
||||
Back to Results
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Teams Table */}
|
||||
{!loading && !showCalendar && !showChartView && (
|
||||
<div className="bg-white rounded-b-lg overflow-hidden">
|
||||
<table className="w-full border-collapse">
|
||||
<thead>
|
||||
<tr className="bg-gray-800 text-white">
|
||||
<th className="p-3 text-left">Games List</th>
|
||||
<th className="p-3 text-center">
|
||||
{dates.length > 0 && new Date(dates[0]).toLocaleDateString('en-US', { weekday: 'short' })} {dates.length > 0 && new Date(dates[0]).getDate()}th
|
||||
</th>
|
||||
<th className="p-3 text-center">
|
||||
{dates.length > 1 && new Date(dates[1]).toLocaleDateString('en-US', { weekday: 'short' })} {dates.length > 1 && new Date(dates[1]).getDate()}th
|
||||
</th>
|
||||
<th className="p-3 text-center">Chart</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{teams.map(team => (
|
||||
<tr key={team.id} className="border-b hover:bg-gray-50">
|
||||
<td className="p-3">
|
||||
<div className="font-semibold">{team.name}</div>
|
||||
<div className="text-sm text-gray-500">at {team.time}</div>
|
||||
<div className="text-xs text-blue-500 underline mt-1 cursor-pointer" onClick={() => handleViewChart(team)}>Record Chart</div>
|
||||
</td>
|
||||
<td className="p-3 text-center text-2xl font-bold">{dates.length > 0 && team.results[dates[0]] || 'XX'}</td>
|
||||
<td className="p-3 text-center text-2xl font-bold">{dates.length > 1 && team.results[dates[1]] || 'XX'}</td>
|
||||
<td className="p-3">
|
||||
<div className="flex justify-center">
|
||||
<button
|
||||
className="p-2 bg-green-100 text-green-600 rounded"
|
||||
onClick={() => handleViewChart(team)}
|
||||
title="View Monthly Chart"
|
||||
>
|
||||
<BarChart2 size={16} />
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
{teams.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan="4" className="p-4 text-center">No teams found</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div className="bg-gray-700 text-white text-center p-3">
|
||||
<button className="hover:underline" onClick={handleCalendarView}>Click here for all games results.</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home2;
|
||||
Loading…
x
Reference in New Issue
Block a user