diff --git a/api.php b/api.php
new file mode 100644
index 0000000..43cc3ae
--- /dev/null
+++ b/api.php
@@ -0,0 +1,454 @@
+prepare("""
+ SELECT COUNT(DISTINCT m.id) AS unique_members
+ FROM members m
+ JOIN attendance a ON m.id = a.member_id
+ WHERE m.community_id = ?
+ AND a.attendance_date BETWEEN ? AND ?
+ """);
+ $stmt->execute([$community_id, $start_date, $end_date]);
+ return $stmt->fetch();
+}
+
+function get_unique_members_drilldown($community_id, $start_date, $end_date) {
+ $pdo = db();
+ $stmt = $pdo->prepare("""
+ SELECT m.name, m.email, m.join_date, MAX(a.attendance_date) as last_visit
+ FROM members m
+ JOIN attendance a ON m.id = a.member_id
+ WHERE m.community_id = ?
+ AND a.attendance_date BETWEEN ? AND ?
+ GROUP BY m.id, m.name, m.email, m.join_date
+ ORDER BY m.name
+ """);
+ $stmt->execute([$community_id, $start_date, $end_date]);
+ return $stmt->fetchAll();
+}
+
+if (isset($_GET['kpi'])) {
+ header('Content-Type: application/json');
+ $community_id = $_GET['community_id'] ?? 1;
+ $start_date = $_GET['start_date'] ?? date('Y-m-01');
+ $end_date = $_GET['end_date'] ?? date('Y-m-t');
+
+ switch ($_GET['kpi']) {
+ case 'unique_members':
+ echo json_encode(get_unique_members($community_id, $start_date, $end_date));
+ break;
+ case 'unique_members_drilldown':
+ echo json_encode(get_unique_members_drilldown($community_id, $start_date, $end_date));
+ break;
+ case 'avg_class_attendance':
+ echo json_encode(get_avg_class_attendance($community_id, $start_date, $end_date));
+ break;
+ case 'avg_class_attendance_drilldown':
+ echo json_encode(get_avg_class_attendance_drilldown($community_id, $start_date, $end_date));
+ break;
+ case 'classes_lightly_attended':
+ echo json_encode(get_classes_lightly_attended($community_id, $start_date, $end_date));
+ break;
+ case 'classes_lightly_attended_drilldown':
+ echo json_encode(get_classes_lightly_attended_drilldown($community_id, $start_date, $end_date));
+ break;
+ case 'attendance_trend':
+ echo json_encode(get_attendance_trend($community_id, $start_date, $end_date));
+ break;
+ case 'avg_visits_per_member':
+ echo json_encode(get_avg_visits_per_member($community_id, $start_date, $end_date));
+ break;
+ case 'avg_visits_per_member_drilldown':
+ echo json_encode(get_avg_visits_per_member_drilldown($community_id, $start_date, $end_date));
+ break;
+ case 'high_frequency_members':
+ echo json_encode(get_high_frequency_members($community_id, $start_date, $end_date));
+ break;
+ case 'high_frequency_members_drilldown':
+ echo json_encode(get_high_frequency_members_drilldown($community_id, $start_date, $end_date));
+ break;
+ case 'total_revenue':
+ echo json_encode(get_total_revenue($community_id, $start_date, $end_date));
+ break;
+ case 'total_revenue_drilldown':
+ echo json_encode(get_total_revenue_drilldown($community_id, $start_date, $end_date));
+ break;
+ case 'revenue_per_active_member':
+ echo json_encode(get_revenue_per_active_member($community_id, $start_date, $end_date));
+ break;
+ case 'revenue_per_active_member_drilldown':
+ echo json_encode(get_revenue_per_active_member_drilldown($community_id, $start_date, $end_date));
+ break;
+ case 'inactive_members':
+ echo json_encode(get_inactive_members($community_id, $start_date, $end_date));
+ break;
+ case 'inactive_members_drilldown':
+ echo json_encode(get_inactive_members_drilldown($community_id, $start_date, $end_date));
+ break;
+ case 'visits_dropped':
+ echo json_encode(get_visits_dropped($community_id, $start_date, $end_date));
+ break;
+ case 'attendance_trend_drilldown':
+ echo json_encode(get_attendance_trend_drilldown($community_id, $start_date, $end_date));
+ break;
+ case 'attendance_over_time':
+ echo json_encode(get_attendance_over_time($community_id, $start_date, $end_date));
+ break;
+ case 'visit_frequency_distribution':
+ echo json_encode(get_visit_frequency_distribution($community_id, $start_date, $end_date));
+ break;
+ }
+ exit;
+}
+
+function get_avg_class_attendance($community_id, $start_date, $end_date) {
+ $pdo = db();
+ $stmt = $pdo->prepare("""
+ SELECT AVG(daily_attendance) as avg_attendance
+ FROM (
+ SELECT COUNT(member_id) as daily_attendance
+ FROM attendance a
+ JOIN members m ON a.member_id = m.id
+ WHERE m.community_id = ?
+ AND a.attendance_date BETWEEN ? AND ?
+ GROUP BY a.class_id, a.attendance_date
+ ) as daily_counts
+ """);
+ $stmt->execute([$community_id, $start_date, $end_date]);
+ $result = $stmt->fetch();
+ return ['avg_class_attendance' => round($result['avg_attendance'], 2)];
+}
+
+function get_avg_class_attendance_drilldown($community_id, $start_date, $end_date) {
+ $pdo = db();
+ $stmt = $pdo->prepare("""
+ SELECT c.name as class_name, DATE(a.attendance_date) as date, COUNT(a.member_id) as attendance_count
+ FROM attendance a
+ JOIN classes c ON a.class_id = c.id
+ JOIN members m ON a.member_id = m.id
+ WHERE m.community_id = ?
+ AND a.attendance_date BETWEEN ? AND ?
+ GROUP BY c.name, DATE(a.attendance_date)
+ ORDER BY date, class_name
+ """);
+ $stmt->execute([$community_id, $start_date, $end_date]);
+ return $stmt->fetchAll();
+}
+
+function get_classes_lightly_attended($community_id, $start_date, $end_date, $threshold = 5) {
+ $pdo = db();
+ $stmt = $pdo->prepare("""
+ SELECT (CAST(SUM(CASE WHEN attendance_count < ? THEN 1 ELSE 0 END) AS DECIMAL(10,2)) / COUNT(*)) * 100 as percentage
+ FROM (
+ SELECT COUNT(a.member_id) as attendance_count
+ FROM attendance a
+ JOIN members m ON a.member_id = m.id
+ WHERE m.community_id = ?
+ AND a.attendance_date BETWEEN ? AND ?
+ GROUP BY a.class_id, a.attendance_date
+ ) as class_counts
+ """);
+ $stmt->execute([$threshold, $community_id, $start_date, $end_date]);
+ $result = $stmt->fetch();
+ return ['classes_lightly_attended' => round($result['percentage'], 2)];
+}
+
+function get_classes_lightly_attended_drilldown($community_id, $start_date, $end_date, $threshold = 5) {
+ $pdo = db();
+ $stmt = $pdo->prepare("""
+ SELECT c.name as class_name, DATE(a.attendance_date) as date, COUNT(a.member_id) as attendance_count
+ FROM attendance a
+ JOIN classes c ON a.class_id = c.id
+ JOIN members m ON a.member_id = m.id
+ WHERE m.community_id = ?
+ AND a.attendance_date BETWEEN ? AND ?
+ GROUP BY c.name, DATE(a.attendance_date)
+ HAVING COUNT(a.member_id) < ?
+ ORDER BY date, class_name
+ """);
+ $stmt->execute([$community_id, $start_date, $end_date, $threshold]);
+ return $stmt->fetchAll();
+}
+
+function get_attendance_trend($community_id, $start_date, $end_date) {
+ $pdo = db();
+
+ $current_period_stmt = $pdo->prepare("SELECT COUNT(*) as count FROM attendance a JOIN members m ON m.id = a.member_id WHERE m.community_id = ? AND a.attendance_date BETWEEN ? AND ?");
+ $current_period_stmt->execute([$community_id, $start_date, $end_date]);
+ $current_period_count = $current_period_stmt->fetchColumn();
+
+ $period_days = (new DateTime($end_date))->diff(new DateTime($start_date))->days;
+ $previous_start_date = (new DateTime($start_date))->modify("-" . ($period_days + 1) . " days")->format('Y-m-d');
+ $previous_end_date = (new DateTime($end_date))->modify("-" . ($period_days + 1) . " days")->format('Y-m-d');
+
+ $previous_period_stmt = $pdo->prepare("SELECT COUNT(*) as count FROM attendance a JOIN members m ON m.id = a.member_id WHERE m.community_id = ? AND a.attendance_date BETWEEN ? AND ?");
+ $previous_period_stmt->execute([$community_id, $previous_start_date, $previous_end_date]);
+ $previous_period_count = $previous_period_stmt->fetchColumn();
+
+ if ($previous_period_count == 0) {
+ $trend = $current_period_count > 0 ? 100 : 0;
+ } else {
+ $trend = (($current_period_count - $previous_period_count) / $previous_period_count) * 100;
+ }
+
+ return ['attendance_trend' => round($trend, 2)];
+}
+
+function get_avg_visits_per_member($community_id, $start_date, $end_date) {
+ $pdo = db();
+ $stmt = $pdo->prepare("""
+ SELECT AVG(visit_count) as avg_visits
+ FROM (
+ SELECT COUNT(a.id) as visit_count
+ FROM members m
+ JOIN attendance a ON m.id = a.member_id
+ WHERE m.community_id = ?
+ AND a.attendance_date BETWEEN ? AND ?
+ GROUP BY m.id
+ ) as member_visits
+ """);
+ $stmt->execute([$community_id, $start_date, $end_date]);
+ $result = $stmt->fetch();
+ return ['avg_visits_per_member' => round($result['avg_visits'], 2)];
+}
+
+function get_avg_visits_per_member_drilldown($community_id, $start_date, $end_date) {
+ $pdo = db();
+ $stmt = $pdo->prepare("""
+ SELECT m.name, m.email, COUNT(a.id) as visit_count
+ FROM members m
+ JOIN attendance a ON m.id = a.member_id
+ WHERE m.community_id = ?
+ AND a.attendance_date BETWEEN ? AND ?
+ GROUP BY m.id, m.name, m.email
+ ORDER BY visit_count DESC
+ """);
+ $stmt->execute([$community_id, $start_date, $end_date]);
+ return $stmt->fetchAll();
+}
+
+function get_high_frequency_members($community_id, $start_date, $end_date, $frequency_threshold = 5) {
+ $pdo = db();
+ $stmt = $pdo->prepare("""
+ SELECT (CAST(SUM(CASE WHEN visit_count >= ? THEN 1 ELSE 0 END) AS DECIMAL(10,2)) / COUNT(*)) * 100 as percentage
+ FROM (
+ SELECT COUNT(a.id) as visit_count
+ FROM members m
+ JOIN attendance a ON m.id = a.member_id
+ WHERE m.community_id = ?
+ AND a.attendance_date BETWEEN ? AND ?
+ GROUP BY m.id
+ ) as member_visits
+ """);
+ $stmt->execute([$frequency_threshold, $community_id, $start_date, $end_date]);
+ $result = $stmt->fetch();
+ return ['high_frequency_members' => round($result['percentage'], 2)];
+}
+
+function get_high_frequency_members_drilldown($community_id, $start_date, $end_date, $frequency_threshold = 5) {
+ $pdo = db();
+ $stmt = $pdo->prepare("""
+ SELECT m.name, m.email, COUNT(a.id) as visit_count
+ FROM members m
+ JOIN attendance a ON m.id = a.member_id
+ WHERE m.community_id = ?
+ AND a.attendance_date BETWEEN ? AND ?
+ GROUP BY m.id, m.name, m.email
+ HAVING visit_count >= ?
+ ORDER BY visit_count DESC
+ """);
+ $stmt->execute([$community_id, $start_date, $end_date, $frequency_threshold]);
+ return $stmt->fetchAll();
+}
+
+function get_total_revenue($community_id, $start_date, $end_date) {
+ $pdo = db();
+ $stmt = $pdo->prepare("""
+ SELECT SUM(r.amount) as total_revenue
+ FROM revenue r
+ JOIN members m ON r.member_id = m.id
+ WHERE m.community_id = ?
+ AND r.transaction_date BETWEEN ? AND ?
+ """);
+ $stmt->execute([$community_id, $start_date, $end_date]);
+ $result = $stmt->fetch();
+ return ['total_revenue' => round($result['total_revenue'], 2)];
+}
+
+function get_total_revenue_drilldown($community_id, $start_date, $end_date) {
+ $pdo = db();
+ $stmt = $pdo->prepare("""
+ SELECT m.name, m.email, r.amount, r.service_category, r.transaction_date
+ FROM revenue r
+ JOIN members m ON r.member_id = m.id
+ WHERE m.community_id = ?
+ AND r.transaction_date BETWEEN ? AND ?
+ ORDER BY r.transaction_date DESC
+ """);
+ $stmt->execute([$community_id, $start_date, $end_date]);
+ return $stmt->fetchAll();
+}
+
+function get_revenue_per_active_member($community_id, $start_date, $end_date) {
+ $pdo = db();
+ $stmt = $pdo->prepare("""
+ SELECT SUM(r.amount) / COUNT(DISTINCT r.member_id) as revenue_per_active_member
+ FROM revenue r
+ JOIN members m ON r.member_id = m.id
+ WHERE m.community_id = ?
+ AND r.transaction_date BETWEEN ? AND ?
+ """);
+ $stmt->execute([$community_id, $start_date, $end_date]);
+ $result = $stmt->fetch();
+ return ['revenue_per_active_member' => round($result['revenue_per_active_member'], 2)];
+}
+
+function get_revenue_per_active_member_drilldown($community_id, $start_date, $end_date) {
+ $pdo = db();
+ $stmt = $pdo->prepare("""
+ SELECT m.name, m.email, SUM(r.amount) as total_revenue, COUNT(r.id) as transaction_count
+ FROM revenue r
+ JOIN members m ON r.member_id = m.id
+ WHERE m.community_id = ?
+ AND r.transaction_date BETWEEN ? AND ?
+ GROUP BY m.id, m.name, m.email
+ ORDER BY total_revenue DESC
+ """);
+ $stmt->execute([$community_id, $start_date, $end_date]);
+ return $stmt->fetchAll();
+}
+
+function get_inactive_members($community_id, $start_date, $end_date) {
+ $pdo = db();
+ $stmt = $pdo->prepare("""
+ SELECT
+ (1 - (COUNT(DISTINCT a.member_id) / COUNT(DISTINCT m.id))) * 100 as inactive_percentage
+ FROM members m
+ LEFT JOIN attendance a ON m.id = a.member_id AND a.attendance_date BETWEEN ? AND ?
+ WHERE m.community_id = ?
+ """);
+ $stmt->execute([$start_date, $end_date, $community_id]);
+ $result = $stmt->fetch();
+ return ['inactive_members' => round($result['inactive_percentage'], 2)];
+}
+
+function get_inactive_members_drilldown($community_id, $start_date, $end_date) {
+ $pdo = db();
+ $stmt = $pdo->prepare("""
+ SELECT m.name, m.email, m.join_date
+ FROM members m
+ LEFT JOIN attendance a ON m.id = a.member_id AND a.attendance_date BETWEEN ? AND ?
+ WHERE m.community_id = ?
+ GROUP BY m.id
+ HAVING COUNT(a.id) = 0
+ """);
+ $stmt->execute([$start_date, $end_date, $community_id]);
+ return $stmt->fetchAll();
+}
+
+function get_visits_dropped($community_id, $start_date, $end_date) {
+ $pdo = db();
+
+ $current_period_stmt = $pdo->prepare("SELECT COUNT(*) as count FROM attendance a JOIN members m ON m.id = a.member_id WHERE m.community_id = ? AND a.attendance_date BETWEEN ? AND ?");
+ $current_period_stmt->execute([$community_id, $start_date, $end_date]);
+ $current_period_count = $current_period_stmt->fetchColumn();
+
+ $period_days = (new DateTime($end_date))->diff(new DateTime($start_date))->days;
+ $previous_start_date = (new DateTime($start_date))->modify("-" . ($period_days + 1) . " days")->format('Y-m-d');
+ $previous_end_date = (new DateTime($end_date))->modify("-" . ($period_days + 1) . " days")->format('Y-m-d');
+
+ $previous_period_stmt = $pdo->prepare("SELECT COUNT(*) as count FROM attendance a JOIN members m ON m.id = a.member_id WHERE m.community_id = ? AND a.attendance_date BETWEEN ? AND ?");
+ $previous_period_stmt->execute([$community_id, $previous_start_date, $previous_end_date]);
+ $previous_period_count = $previous_period_stmt->fetchColumn();
+
+ if ($previous_period_count == 0) {
+ $trend = $current_period_count > 0 ? 100 : 0;
+ } else {
+ $trend = (($current_period_count - $previous_period_count) / $previous_period_count) * 100;
+ }
+
+ return ['visits_dropped' => round($trend, 2)];
+}
+
+function get_attendance_trend_drilldown($community_id, $start_date, $end_date) {
+ $pdo = db();
+
+ $current_period_stmt = $pdo->prepare("SELECT COUNT(*) as count FROM attendance a JOIN members m ON m.id = a.member_id WHERE m.community_id = ? AND a.attendance_date BETWEEN ? AND ?");
+ $current_period_stmt->execute([$community_id, $start_date, $end_date]);
+ $current_period_count = $current_period_stmt->fetchColumn();
+
+ $period_days = (new DateTime($end_date))->diff(new DateTime($start_date))->days;
+ $previous_start_date = (new DateTime($start_date))->modify("-" . ($period_days + 1) . " days")->format('Y-m-d');
+ $previous_end_date = (new DateTime($end_date))->modify("-" . ($period_days + 1) . " days")->format('Y-m-d');
+
+ $previous_period_stmt = $pdo->prepare("SELECT COUNT(*) as count FROM attendance a JOIN members m ON m.id = a.member_id WHERE m.community_id = ? AND a.attendance_date BETWEEN ? AND ?");
+ $previous_period_stmt->execute([$community_id, $previous_start_date, $previous_end_date]);
+ $previous_period_count = $previous_period_stmt->fetchColumn();
+
+ return [
+ ['period' => 'Current Period', 'visits' => $current_period_count],
+ ['period' => 'Previous Period', 'visits' => $previous_period_count]
+ ];
+}
+
+function get_attendance_over_time($community_id, $start_date, $end_date) {
+ $pdo = db();
+ $stmt = $pdo->prepare("""
+ SELECT DATE(a.attendance_date) as date, COUNT(a.id) as attendance_count
+ FROM attendance a
+ JOIN members m ON a.member_id = m.id
+ WHERE m.community_id = ?
+ AND a.attendance_date BETWEEN ? AND ?
+ GROUP BY DATE(a.attendance_date)
+ ORDER BY date
+ """);
+ $stmt->execute([$community_id, $start_date, $end_date]);
+ $data = $stmt->fetchAll();
+
+ $labels = [];
+ $values = [];
+ foreach ($data as $row) {
+ $labels[] = $row['date'];
+ $values[] = $row['attendance_count'];
+ }
+
+ return ['labels' => $labels, 'data' => $values];
+}
+
+function get_visit_frequency_distribution($community_id, $start_date, $end_date) {
+ $pdo = db();
+ $stmt = $pdo->prepare("""
+ SELECT
+ CASE
+ WHEN visit_count = 1 THEN '1 visit'
+ WHEN visit_count BETWEEN 2 AND 4 THEN '2-4 visits'
+ WHEN visit_count BETWEEN 5 AND 9 THEN '5-9 visits'
+ ELSE '10+ visits'
+ END as frequency_bucket,
+ COUNT(*) as member_count
+ FROM (
+ SELECT COUNT(a.id) as visit_count
+ FROM members m
+ JOIN attendance a ON m.id = a.member_id
+ WHERE m.community_id = ?
+ AND a.attendance_date BETWEEN ? AND ?
+ GROUP BY m.id
+ ) as member_visits
+ GROUP BY frequency_bucket
+ """);
+ $stmt->execute([$community_id, $start_date, $end_date]);
+ $data = $stmt->fetchAll();
+
+ $labels = ['1 visit', '2-4 visits', '5-9 visits', '10+ visits'];
+ $values = [0, 0, 0, 0];
+ foreach ($data as $row) {
+ $index = array_search($row['frequency_bucket'], $labels);
+ if ($index !== false) {
+ $values[$index] = $row['member_count'];
+ }
+ }
+
+ return ['labels' => $labels, 'data' => $values];
+}
diff --git a/assets/css/custom.css b/assets/css/custom.css
new file mode 100644
index 0000000..5a7a479
--- /dev/null
+++ b/assets/css/custom.css
@@ -0,0 +1,33 @@
+body {
+ background-color: #f8f9fa;
+}
+
+.kpi-card {
+ border-left-width: 5px;
+}
+
+.border-left-primary {
+ border-left-color: #007bff;
+}
+
+.border-left-success {
+ border-left-color: #28a745;
+}
+
+.border-left-danger {
+ border-left-color: #dc3545;
+}
+
+.border-left-info {
+ border-left-color: #17a2b8;
+}
+
+.kpi-value {
+ font-size: 2.5rem;
+ font-weight: bold;
+}
+
+.kpi-label {
+ font-weight: 500;
+ color: #6c757d;
+}
diff --git a/assets/pasted-20251230-211133-cc45faf9.png b/assets/pasted-20251230-211133-cc45faf9.png
new file mode 100644
index 0000000..af012e5
Binary files /dev/null and b/assets/pasted-20251230-211133-cc45faf9.png differ
diff --git a/db/setup.php b/db/setup.php
new file mode 100644
index 0000000..e7847f8
--- /dev/null
+++ b/db/setup.php
@@ -0,0 +1,112 @@
+exec("""
+ CREATE TABLE IF NOT EXISTS communities (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ name VARCHAR(255) NOT NULL
+ )
+ """);
+
+ // Create members table
+ $pdo->exec("""
+ CREATE TABLE IF NOT EXISTS members (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ community_id INT NOT NULL,
+ name VARCHAR(255) NOT NULL,
+ email VARCHAR(255) NOT NULL,
+ join_date DATE NOT NULL,
+ FOREIGN KEY (community_id) REFERENCES communities(id)
+ )
+ """);
+
+ // Create classes table
+ $pdo->exec("""
+ CREATE TABLE IF NOT EXISTS classes (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ name VARCHAR(255) NOT NULL,
+ instructor VARCHAR(255) NOT NULL
+ )
+ """);
+
+ // Create attendance table
+ $pdo->exec("""
+ CREATE TABLE IF NOT EXISTS attendance (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ member_id INT NOT NULL,
+ class_id INT NOT NULL,
+ attendance_date DATE NOT NULL,
+ FOREIGN KEY (member_id) REFERENCES members(id),
+ FOREIGN KEY (class_id) REFERENCES classes(id)
+ )
+ """);
+
+ // Create revenue table
+ $pdo->exec("""
+ CREATE TABLE IF NOT EXISTS revenue (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ member_id INT NOT NULL,
+ amount DECIMAL(10, 2) NOT NULL,
+ service_category VARCHAR(255) NOT NULL,
+ transaction_date DATE NOT NULL,
+ FOREIGN KEY (member_id) REFERENCES members(id)
+ )
+ """);
+
+ // Seed data
+ $stmt = $pdo->query("SELECT COUNT(*) FROM communities");
+ if ($stmt->fetchColumn() == 0) {
+ // Seed communities
+ $communities = ['Valencia Sound', 'Valencia Reserve', 'Valencia Cove'];
+ $stmt = $pdo->prepare("INSERT INTO communities (name) VALUES (?)");
+ foreach ($communities as $community) {
+ $stmt->execute([$community]);
+ }
+
+ // Seed members
+ $members = [
+ [1, 'John Doe', 'john.doe@email.com', '2025-01-15'],
+ [1, 'Jane Smith', 'jane.smith@email.com', '2025-02-20'],
+ [2, 'Peter Jones', 'peter.jones@email.com', '2025-03-10'],
+ [2, 'Mary Johnson', 'mary.johnson@email.com', '2025-04-05'],
+ [3, 'David Williams', 'david.williams@email.com', '2025-05-12'],
+ [3, 'Susan Brown', 'susan.brown@email.com', '2025-06-18'],
+ ];
+ $stmt = $pdo->prepare("INSERT INTO members (community_id, name, email, join_date) VALUES (?, ?, ?, ?)");
+ foreach ($members as $member) {
+ $stmt->execute($member);
+ }
+
+ // Seed classes
+ $classes = ['Yoga', 'Pilates', 'Zumba', 'Spin'];
+ $stmt = $pdo->prepare("INSERT INTO classes (name, instructor) VALUES (?, ?)");
+ foreach ($classes as $class) {
+ $stmt->execute([$class, 'Instructor ' . rand(1, 3)]);
+ }
+
+ // Seed attendance
+ for ($i = 1; $i <= 500; $i++) {
+ $member_id = rand(1, 6);
+ $class_id = rand(1, 4);
+ $date = date('Y-m-d', strtotime('-' . rand(0, 180) . ' days'));
+ $pdo->prepare("INSERT INTO attendance (member_id, class_id, attendance_date) VALUES (?, ?, ?)")->execute([$member_id, $class_id, $date]);
+ }
+
+ // Seed revenue
+ $service_categories = ['Group', 'PT', 'Massage', 'Events'];
+ for ($i = 1; $i <= 200; $i++) {
+ $member_id = rand(1, 6);
+ $amount = rand(20, 200);
+ $service_category = $service_categories[rand(0, 3)];
+ $date = date('Y-m-d', strtotime('-' . rand(0, 180) . ' days'));
+ $pdo->prepare("INSERT INTO revenue (member_id, amount, service_category, transaction_date) VALUES (?, ?, ?, ?)")->execute([$member_id, $amount, $service_category, $date]);
+ }
+ }
+}
+
+setup_database();
+echo "Database setup complete.";
diff --git a/index.php b/index.php
index 7205f3d..4a3ee21 100644
--- a/index.php
+++ b/index.php
@@ -1,150 +1,367 @@
-
-
+
-
-
- New Style
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ Community Command Center
+
+
+
+
+
+
-
-
-
Analyzing your requirements and generating your website…
-
- Loading…
-
-
= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.
-
This page will update automatically as the plan is implemented.
-
Runtime: PHP = htmlspecialchars($phpVersion) ?> — UTC = htmlspecialchars($now) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
12
+
Avg Class Attendance
+
+
+
15%
+
Classes Lightly Attended
+
+
+
+5%
+
Attendance Trend
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
4.2
+
Avg Visits per Member
+
+
+
45%
+
High Frequency Members
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
$22,450
+
Total Revenue
+
+
+
$65.64
+
Revenue per Active Member
+
+
+
+
+
+
+
+
+
+
+
+
+
+
0%
+
Inactive Members
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Top Praises
+
+ - "Love the new yoga class!"
+ - "Instructor was fantastic."
+
+
+
+
Top Requests
+
+ - "More evening classes."
+ - "Wish there was a smoothie bar."
+
+
+
+
Top Frictions
+
+ - "Locker room needs attention."
+ - "Difficulty booking online."
+
+
+
+
+
+
+
+
+
+
+
+
+
Noticeable Shift
+
Risk: While revenue is up, the drop in visit frequency and recent negative feedback on facilities could impact retention next period.
+
Opportunity: High praise for specific instructors suggests promoting them more could boost attendance.
+
+
+
+
+
+
+
+
+ CAUTION: Visits Dropped 10%
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+