diff --git a/agent.md b/agent.md
index 6c40b66..f1d4630 100644
--- a/agent.md
+++ b/agent.md
@@ -8,13 +8,23 @@ This file tracks the progress of the project.
- [x] **Member Profile Modal**: Display player profiles in a modal dialog instead of a separate page.
- [x] **Refine Profile Modal**: Update the profile modal to remove the bio and include `joined_date`, `matches_played`, `wins`, and `losses`.
- [x] **Implement Register/Login**: Build user registration and login functionality. This will be a prerequisite for features that require user authentication.
-
-## In Progress
-
-- [ ] **Training Game Interest Form**: Create a form for players to register their interest in training games. The form will allow users to look up their account with their phone number. If an account doesn't exist, submitting the form will create one.
+- [x] **Backend for Player Data**: Create a backend endpoint to serve the list of players, replacing the mock data in `index.php`.
+- [x] **Refactor Team Handling (Many-to-Many)**
+ - [x] **DB Schema:** Drop `team_a`/`team_b` from `matches` table.
+ - [x] **DB Schema:** Create `teams` table.
+ - [x] **DB Schema:** Create `match_teams` join table.
+ - [x] **Refactor UI:** Update "Create Match" form for multiple teams.
+ - [x] **Refactor Backend:** Update `api/matches.php` to handle multiple teams.
+ - [x] **Refactor UI:** Update match card to display a list of teams.
+- [x] **Story: Training Game Interest and Scheduling**
+ - [x] **Matches Table**: Create a `matches` table in the database to store event details, including date, time, and location.
+ - [x] **`match_votes` Table**: Create a join table between `users` and `matches` to store individual votes.
+ - [x] **Admin Match Management**: Develop a UI for admins to create, update, and delete matches.
+ - [x] **Match Interest Form**: For each match, create a unique interest form for users to vote on their availability.
+ - [x] **Voting Logic**: Implement the backend functionality to record and manage user votes for each match.
+ - [x] **Voter Display**: On each match's page, display the users who have voted, along with their responses.
## Backlog
-- [ ] **Backend for Player Data**: Create a backend endpoint to serve the list of players, replacing the mock data in `index.php`.
- [ ] **Player Dashboard**: Build a personalized dashboard for logged-in players to see their own detailed stats, track progress over time, and manage their information.
- [ ] **Branded Landing Page**: Develop a more formal, public-facing homepage for the application that explains its purpose and value.
\ No newline at end of file
diff --git a/api/matches.php b/api/matches.php
new file mode 100644
index 0000000..d5da4a2
--- /dev/null
+++ b/api/matches.php
@@ -0,0 +1,221 @@
+prepare('
+ SELECT t.name
+ FROM teams t
+ JOIN match_teams mt ON t.id = mt.team_id
+ WHERE mt.match_id = ?
+ ');
+ $stmt->execute([$match['id']]);
+ $teams = $stmt->fetchAll(PDO::FETCH_COLUMN);
+
+ $match_date = new DateTime($match['match_datetime']);
+ $formatted_date = $match_date->format('D, M j, Y ');
+ $formatted_time = $match_date->format('g:i A');
+
+ $teams_html = '';
+ if (count($teams) > 0) {
+ $teams_html = '
Teams: ' . htmlspecialchars(implode(', ', $teams)) . '
';
+ }
+
+ return '
+
+
' . htmlspecialchars($match['match_type']) . '
+
' . htmlspecialchars($match['location']) . '
+
' . $formatted_date . ' at ' . $formatted_time . '
'
+ . $teams_html .
+ '
View Details
+
+
';
+}
+
+header('Content-Type: application/json');
+
+$action = $_GET['action'] ?? '';
+
+switch ($action) {
+ case 'create':
+ $pdo = db();
+ try {
+ $pdo->beginTransaction();
+
+ // Insert match
+ $stmt = $pdo->prepare("INSERT INTO matches (match_datetime, location, match_type) VALUES (?, ?, ?)");
+ $stmt->execute([
+ $_POST['match_datetime'],
+ $_POST['location'],
+ $_POST['match_type']
+ ]);
+ $match_id = $pdo->lastInsertId();
+
+ // Handle teams
+ $teams = $_POST['teams'] ?? [];
+ foreach ($teams as $team_input) {
+ $team_id = null;
+ if (is_numeric($team_input)) {
+ // Existing team
+ $team_id = (int)$team_input;
+ } else {
+ // New team, check if it exists
+ $stmt = $pdo->prepare("SELECT id FROM teams WHERE name = ?");
+ $stmt->execute([$team_input]);
+ $existing_team = $stmt->fetch();
+ if ($existing_team) {
+ $team_id = $existing_team['id'];
+ } else {
+ // Insert new team
+ $stmt = $pdo->prepare("INSERT INTO teams (name) VALUES (?)");
+ $stmt->execute([$team_input]);
+ $team_id = $pdo->lastInsertId();
+ }
+ }
+
+ if ($team_id) {
+ $stmt = $pdo->prepare("INSERT INTO match_teams (match_id, team_id) VALUES (?, ?)");
+ $stmt->execute([$match_id, $team_id]);
+ }
+ }
+
+ $pdo->commit();
+ echo json_encode(['success' => true]);
+ } catch (PDOException $e) {
+ $pdo->rollBack();
+ echo json_encode(['success' => false, 'error' => $e->getMessage()]);
+ }
+ break;
+
+ case 'load_more':
+ $section = $_GET['section'] ?? '';
+ $offset = (int)($_GET['offset'] ?? 0);
+ $limit = 10;
+ $sql = '';
+
+ switch ($section) {
+ case 'upcoming':
+ $sql = "SELECT * FROM matches WHERE match_datetime > NOW() ORDER BY match_datetime ASC LIMIT ? OFFSET ?";
+ break;
+ case 'recent':
+ $sql = "SELECT * FROM matches WHERE match_datetime <= NOW() AND match_datetime >= NOW() - INTERVAL 2 WEEK ORDER BY match_datetime DESC LIMIT ? OFFSET ?";
+ break;
+ case 'past':
+ $sql = "SELECT * FROM matches WHERE match_datetime < NOW() - INTERVAL 2 WEEK ORDER BY match_datetime DESC LIMIT ? OFFSET ?";
+ break;
+ }
+
+ if ($sql) {
+ $pdo = db();
+ $stmt = $pdo->prepare($sql);
+ $load_limit = $limit + 1;
+ $stmt->bindParam(1, $load_limit, PDO::PARAM_INT);
+ $stmt->bindParam(2, $offset, PDO::PARAM_INT);
+ $stmt->execute();
+ $matches = $stmt->fetchAll();
+
+ $has_more = count($matches) > $limit;
+ if ($has_more) {
+ array_pop($matches); // Remove the extra match
+ }
+
+ $rendered_matches = [];
+ foreach($matches as $match) {
+ $rendered_matches[] = render_match_card($match);
+ }
+
+ echo json_encode(['matches' => $rendered_matches, 'has_more' => $has_more]);
+ } else {
+ echo json_encode(['matches' => [], 'has_more' => false]);
+ }
+ break;
+
+ case 'get':
+ try {
+ $pdo = db();
+ $stmt = $pdo->prepare("SELECT * FROM matches WHERE id = ?");
+ $stmt->execute([$_GET['id']]);
+ $match = $stmt->fetch(PDO::FETCH_ASSOC);
+
+ $stmt = $pdo->prepare("SELECT team_id FROM match_teams WHERE match_id = ?");
+ $stmt->execute([$_GET['id']]);
+ $teams = $stmt->fetchAll(PDO::FETCH_COLUMN);
+ $match['teams'] = $teams;
+
+ echo json_encode(['success' => true, 'match' => $match]);
+ } catch (PDOException $e) {
+ echo json_encode(['success' => false, 'error' => $e->getMessage()]);
+ }
+ break;
+
+ case 'update':
+ $pdo = db();
+ try {
+ $pdo->beginTransaction();
+
+ // Update match
+ $stmt = $pdo->prepare("UPDATE matches SET match_datetime = ?, location = ?, match_type = ? WHERE id = ?");
+ $stmt->execute([
+ $_POST['match_datetime'],
+ $_POST['location'],
+ $_POST['match_type'],
+ $_POST['match_id']
+ ]);
+
+ $match_id = $_POST['match_id'];
+
+ // Delete old team associations
+ $stmt = $pdo->prepare("DELETE FROM match_teams WHERE match_id = ?");
+ $stmt->execute([$match_id]);
+
+ // Handle teams
+ $teams = $_POST['teams'] ?? [];
+ foreach ($teams as $team_input) {
+ $team_id = null;
+ if (is_numeric($team_input)) {
+ // Existing team
+ $team_id = (int)$team_input;
+ } else {
+ // New team, check if it exists
+ $stmt = $pdo->prepare("SELECT id FROM teams WHERE name = ?");
+ $stmt->execute([$team_input]);
+ $existing_team = $stmt->fetch();
+ if ($existing_team) {
+ $team_id = $existing_team['id'];
+ } else {
+ // Insert new team
+ $stmt = $pdo->prepare("INSERT INTO teams (name) VALUES (?)");
+ $stmt->execute([$team_input]);
+ $team_id = $pdo->lastInsertId();
+ }
+ }
+
+ if ($team_id) {
+ $stmt = $pdo->prepare("INSERT INTO match_teams (match_id, team_id) VALUES (?, ?)");
+ $stmt->execute([$match_id, $team_id]);
+ }
+ }
+
+ $pdo->commit();
+ echo json_encode(['success' => true]);
+ } catch (PDOException $e) {
+ $pdo->rollBack();
+ echo json_encode(['success' => false, 'error' => $e->getMessage()]);
+ }
+ break;
+
+ case 'delete':
+ try {
+ $pdo = db();
+ $stmt = $pdo->prepare("DELETE FROM matches WHERE id = ?");
+ $stmt->execute([$_GET['id']]);
+ echo json_encode(['success' => true]);
+ } catch (PDOException $e) {
+ echo json_encode(['success' => false, 'error' => $e->getMessage()]);
+ }
+ break;
+
+ default:
+ echo json_encode(['success' => false, 'error' => 'Invalid action']);
+ break;
+}
diff --git a/controllers/index_controller.php b/controllers/index_controller.php
new file mode 100644
index 0000000..47ae497
--- /dev/null
+++ b/controllers/index_controller.php
@@ -0,0 +1,49 @@
+query("SELECT *, DATE_FORMAT(joined_date, '%M %e, %Y') as formatted_joined_date FROM users WHERE role = 'player' ORDER BY points DESC, name ASC");
+ $players = $stmt->fetchAll();
+
+ foreach ($players as $key => &$player) {
+ $player['rank'] = $key + 1;
+ }
+ unset($player);
+
+} catch (PDOException $e) {
+ die("DB ERROR: " . $e->getMessage());
+}
+
+// Prepare data for the view
+$podium = array_slice($players, 0, 3);
+$rest_of_players = array_slice($players, 3);
+
+// Helper functions for the view. In a larger application, these might
+// go into a separate 'helpers.php' file.
+function get_trend_icon($trend) {
+ switch ($trend) {
+ case 'up':
+ return ' ';
+ case 'down':
+ return ' ';
+ default:
+ return ' ';
+ }
+}
+
+function get_trend_icon_modal($trend) {
+ switch ($trend) {
+ case 'up':
+ return ' ';
+ case 'down':
+ return ' ';
+ default:
+ return ' ';
+ }
+}
\ No newline at end of file
diff --git a/db/migrations/005_create_matches_table.php b/db/migrations/005_create_matches_table.php
new file mode 100644
index 0000000..04f105e
--- /dev/null
+++ b/db/migrations/005_create_matches_table.php
@@ -0,0 +1,18 @@
+exec($sql);
+ echo "Table 'matches' created successfully." . PHP_EOL;
+} catch (PDOException $e) {
+ die("DB ERROR: " . $e->getMessage());
+}
diff --git a/db/migrations/006_create_match_votes_table.php b/db/migrations/006_create_match_votes_table.php
new file mode 100644
index 0000000..a1840ce
--- /dev/null
+++ b/db/migrations/006_create_match_votes_table.php
@@ -0,0 +1,21 @@
+exec($sql);
+ echo "Table 'match_votes' created successfully." . PHP_EOL;
+} catch (PDOException $e) {
+ die("DB ERROR: " . $e->getMessage());
+}
diff --git a/db/migrations/007_add_teams_to_matches.php b/db/migrations/007_add_teams_to_matches.php
new file mode 100644
index 0000000..be082c3
--- /dev/null
+++ b/db/migrations/007_add_teams_to_matches.php
@@ -0,0 +1,11 @@
+exec($sql);
+ echo "Table 'matches' updated successfully with 'team_a' and 'team_b' columns." . PHP_EOL;
+} catch (PDOException $e) {
+ die("Could not update matches table: " . $e->getMessage());
+}
diff --git a/db/migrations/008_drop_teams_from_matches.php b/db/migrations/008_drop_teams_from_matches.php
new file mode 100644
index 0000000..105f3d8
--- /dev/null
+++ b/db/migrations/008_drop_teams_from_matches.php
@@ -0,0 +1,14 @@
+exec('ALTER TABLE matches DROP COLUMN team_a, DROP COLUMN team_b;');
+ echo "Migration 008 successful: Dropped team_a and team_b from matches table." . PHP_EOL;
+ } catch (PDOException $e) {
+ echo "Migration 008 failed: " . $e->getMessage() . PHP_EOL;
+ }
+}
+
+migrate_008_drop_teams_from_matches();
diff --git a/db/migrations/009_create_teams_table.php b/db/migrations/009_create_teams_table.php
new file mode 100644
index 0000000..fa3eed1
--- /dev/null
+++ b/db/migrations/009_create_teams_table.php
@@ -0,0 +1,20 @@
+exec('
+ CREATE TABLE IF NOT EXISTS teams (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ name VARCHAR(255) NOT NULL UNIQUE,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+ );
+ ');
+ echo "Migration 009 successful: Created teams table." . PHP_EOL;
+ } catch (PDOException $e) {
+ echo "Migration 009 failed: " . $e->getMessage() . PHP_EOL;
+ }
+}
+
+migrate_009_create_teams_table();
diff --git a/db/migrations/010_create_match_teams_table.php b/db/migrations/010_create_match_teams_table.php
new file mode 100644
index 0000000..d15792b
--- /dev/null
+++ b/db/migrations/010_create_match_teams_table.php
@@ -0,0 +1,22 @@
+exec('
+ CREATE TABLE IF NOT EXISTS match_teams (
+ match_id INT NOT NULL,
+ team_id INT NOT NULL,
+ PRIMARY KEY (match_id, team_id),
+ FOREIGN KEY (match_id) REFERENCES matches(id) ON DELETE CASCADE,
+ FOREIGN KEY (team_id) REFERENCES teams(id) ON DELETE CASCADE
+ );
+ ');
+ echo "Migration 010 successful: Created match_teams table." . PHP_EOL;
+ } catch (PDOException $e) {
+ echo "Migration 010 failed: " . $e->getMessage() . PHP_EOL;
+ }
+}
+
+migrate_010_create_match_teams_table();
diff --git a/db/migrations/011_add_match_type_to_matches.php b/db/migrations/011_add_match_type_to_matches.php
new file mode 100644
index 0000000..66b20a2
--- /dev/null
+++ b/db/migrations/011_add_match_type_to_matches.php
@@ -0,0 +1,14 @@
+exec($sql);
+ echo "Column 'match_type' added to 'matches' table successfully." . PHP_EOL;
+} catch (PDOException $e) {
+ die("DB ERROR: " . $e->getMessage());
+}
diff --git a/includes/header.php b/includes/header.php
new file mode 100644
index 0000000..9a5b006
--- /dev/null
+++ b/includes/header.php
@@ -0,0 +1,55 @@
+prepare("SELECT * FROM users WHERE id = ?");
+ $stmt->execute([$_SESSION['user_id']]);
+ $user = $stmt->fetch();
+ } catch (PDOException $e) {
+ // Log error or handle it gracefully
+ }
+}
+
+
+
+$current_page = basename($_SERVER['PHP_SELF']);
+?>
+
+
+
diff --git a/index.php b/index.php
index f8335b7..de7542b 100644
--- a/index.php
+++ b/index.php
@@ -1,236 +1,15 @@
query("SELECT *, DATE_FORMAT(joined_date, '%M %e, %Y') as formatted_joined_date FROM users WHERE role = 'player' ORDER BY points DESC, name ASC");
- $players = $stmt->fetchAll();
-
- foreach ($players as $key => &$player) {
- $player['rank'] = $key + 1;
- }
- unset($player);
-
-} catch (PDOException $e) {
- die("DB ERROR: " . $e->getMessage());
-}
-
-// --- Data Simulation ---
-
-$podium = array_slice($players, 0, 3);
-$rest_of_players = array_slice($players, 3);
-
-function get_trend_icon($trend) {
- switch ($trend) {
- case 'up':
- return ' ';
- case 'down':
- return ' ';
- default:
- return ' ';
- }
-}
-
-function get_trend_icon_modal($trend) {
- switch ($trend) {
- case 'up':
- return ' ';
- case 'down':
- return ' ';
- default:
- return ' ';
- }
-}
-?>
-
-
-
-
-
- B3SC Leaderboard
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Welcome, !
-
Logout
-
-
Login / Register
-
-
-
- Season 1 - Premier Division
-
-
-
-
-
- $player): ?>
-
-
-
-
-
-
-
PTS
-
-
-
-
-
-
-
-
-
-
-
-
- Rank
- Player
- Points
- Trend
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+// 3. Load the view.
+// The view uses the variables prepared by the controller to render the HTML.
+require_once 'views/index_view.php';
\ No newline at end of file
diff --git a/login.php b/login.php
index a76437e..e2efd8e 100644
--- a/login.php
+++ b/login.php
@@ -46,11 +46,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['register'])) {
$stmt->execute();
$user_id = $pdoconn->lastInsertId();
- $sql = "SELECT * FROM users WHERE id = :id";
- $stmt = $pdoconn->prepare($sql);
- $stmt->bindParam(':id', $user_id);
- $stmt->execute();
- $_SESSION['user'] = $stmt->fetch(PDO::FETCH_ASSOC);
+ $_SESSION['user_id'] = $user_id;
header("Location: index.php");
exit();
@@ -78,7 +74,7 @@ else if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['phone'])) {
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user) {
- $_SESSION['user'] = $user;
+ $_SESSION['user_id'] = $user['id'];
header("Location: index.php");
exit();
} else {
diff --git a/logout.php b/logout.php
index 2f45770..248c535 100644
--- a/logout.php
+++ b/logout.php
@@ -2,6 +2,6 @@
session_start();
session_unset();
session_destroy();
-header("Location: index.php");
+header("Location: login.php");
exit();
?>
\ No newline at end of file
diff --git a/match_view.php b/match_view.php
new file mode 100644
index 0000000..e6729fd
--- /dev/null
+++ b/match_view.php
@@ -0,0 +1,143 @@
+prepare("SELECT * FROM matches WHERE id = ?");
+$stmt->execute([$match_id]);
+$match = $stmt->fetch();
+
+if (!$match) {
+ header('Location: matches.php');
+ exit;
+}
+
+// Fetch teams for the match
+$stmt = $pdo->prepare('SELECT t.name FROM teams t JOIN match_teams mt ON t.id = mt.team_id WHERE mt.match_id = ?');
+$stmt->execute([$match_id]);
+$teams = $stmt->fetchAll(PDO::FETCH_COLUMN);
+
+// Fetch user's current vote
+$stmt = $pdo->prepare("SELECT vote FROM match_votes WHERE match_id = ? AND user_id = ?");
+$stmt->execute([$match_id, $user_id]);
+$current_vote = $stmt->fetchColumn();
+
+// Handle vote submission
+if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['vote'])) {
+ $vote = $_POST['vote'];
+
+ if ($current_vote) {
+ // Update vote
+ $stmt = $pdo->prepare("UPDATE match_votes SET vote = ? WHERE match_id = ? AND user_id = ?");
+ $stmt->execute([$vote, $match_id, $user_id]);
+ } else {
+ // Insert new vote
+ $stmt = $pdo->prepare("INSERT INTO match_votes (match_id, user_id, vote) VALUES (?, ?, ?)");
+ $stmt->execute([$match_id, $user_id, $vote]);
+ }
+ // Refresh the page to show the updated vote status
+ header('Location: match_view.php?id=' . $match_id);
+ exit;
+}
+
+// Fetch all votes for this match to display who has voted
+$stmt = $pdo->prepare('
+ SELECT u.nickname, mv.vote
+ FROM users u
+ JOIN match_votes mv ON u.id = mv.user_id
+ WHERE mv.match_id = ?
+ ORDER BY mv.created_at DESC
+');
+$stmt->execute([$match_id]);
+$votes = $stmt->fetchAll();
+
+$match_date = new DateTime($match['match_datetime']);
+$formatted_date = $match_date->format('D, M j, Y ');
+$formatted_time = $match_date->format('g:i A');
+
+?>
+
+
+
+
+
+ View Match
+
+
+
+
+
+
+
+
+
+
+
Location:
+
Date:
+
Time:
+ 0): ?>
+
Teams:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No one has voted yet.
+
+
+
+
+
+
+
+
+
diff --git a/matches.php b/matches.php
new file mode 100644
index 0000000..549caaa
--- /dev/null
+++ b/matches.php
@@ -0,0 +1,422 @@
+prepare('
+ SELECT t.name
+ FROM teams t
+ JOIN match_teams mt ON t.id = mt.team_id
+ WHERE mt.match_id = ?
+ ');
+ $stmt->execute([$match['id']]);
+ $teams = $stmt->fetchAll(PDO::FETCH_COLUMN);
+
+ $match_date = new DateTime($match['match_datetime']);
+ $formatted_date = $match_date->format('D, M j, Y ');
+ $formatted_time = $match_date->format('g:i A');
+
+ $teams_html = '';
+ if (count($teams) > 0) {
+ $teams_html = 'Teams: ' . htmlspecialchars(implode(', ', $teams)) . '
';
+ }
+
+
+ return '
+
+
' . htmlspecialchars($match['match_type']) . '
+
' . htmlspecialchars($match['location']) . '
+
' . $formatted_date . ' at ' . $formatted_time . '
'
+ . $teams_html .
+ '
View Details
+
Edit
+
Delete
+
+
';
+}
+
+?>
+
+
+
+
+
+ Matches
+
+
+
+
+
+
+
+
+
Matches
+
+ Create Match
+
+
+
+
+ Upcoming
+
+ prepare("SELECT * FROM matches WHERE match_datetime > NOW() ORDER BY match_datetime ASC LIMIT 5");
+ $stmt->execute();
+ $upcoming_matches = $stmt->fetchAll();
+ if (count($upcoming_matches) == 0) {
+ echo '
No upcoming matches.
';
+ } else {
+ foreach ($upcoming_matches as $match) {
+ echo render_match_card($match);
+ }
+ }
+ ?>
+
+
+
+
+ prepare("SELECT * FROM matches WHERE match_datetime <= NOW() AND match_datetime >= NOW() - INTERVAL 2 WEEK ORDER BY match_datetime DESC LIMIT 5");
+ $stmt->execute();
+ $recent_matches = $stmt->fetchAll();
+ if (count($recent_matches) > 0):
+ ?>
+
+
+
+ prepare("SELECT * FROM matches WHERE match_datetime < NOW() - INTERVAL 2 WEEK ORDER BY match_datetime DESC LIMIT 5");
+ $stmt->execute();
+ $past_matches = $stmt->fetchAll();
+ if (count($past_matches) > 0):
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/views/index_view.php b/views/index_view.php
new file mode 100644
index 0000000..ba76a29
--- /dev/null
+++ b/views/index_view.php
@@ -0,0 +1,182 @@
+
+
+
+
+
+ B3SC Leaderboard
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Season 1 - Premier Division
+
+
+
+
+
+ $player): ?>
+
+
+
+
+
+
+
PTS
+
+
+
+
+
+
+
+
+
+
+
+
+ Rank
+ Player
+ Points
+ Trend
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file