Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@ CREATE TABLE quests (
name TEXT NOT NULL,
description TEXT,
points_reward INTEGER DEFAULT 100,
difficulty TEXT DEFAULT 'medium', -- easy, medium, hard
estimated_time INTEGER DEFAULT 120, -- in minutes
category TEXT DEFAULT 'exploration',
requirements TEXT,
instructions TEXT,
max_participants INTEGER DEFAULT 100,
start_date DATE,
end_date DATE,
location_area TEXT,
tags TEXT,
is_active BOOLEAN DEFAULT TRUE,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
Expand All @@ -86,12 +96,14 @@ CREATE TABLE quest_steps (
id INTEGER PRIMARY KEY AUTOINCREMENT,
quest_id INTEGER NOT NULL,
step_number INTEGER NOT NULL,
title TEXT NOT NULL,
description TEXT,
step_type TEXT NOT NULL, -- checkin, photo, social_share, business_visit
description TEXT NOT NULL,
step_type TEXT NOT NULL, -- photo, checkin, question, task
target_location_id INTEGER,
target_business_id INTEGER,
points_reward INTEGER DEFAULT 10,
points_reward INTEGER DEFAULT 15,
question TEXT,
answer TEXT,
task_instructions TEXT,
FOREIGN KEY (quest_id) REFERENCES quests(id),
FOREIGN KEY (target_location_id) REFERENCES locations(id),
FOREIGN KEY (target_business_id) REFERENCES business_partners(id)
Expand Down
45 changes: 42 additions & 3 deletions src/react-app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ import {
Users,
Crown,
Gem,
Sparkles
Sparkles,
Settings
} from "lucide-react";
import { LocationMap } from "./components/LocationMap";
import { CheckInCard } from "./components/CheckInCard";
import { QuestCard } from "./components/QuestCard";
import { AdminPanel } from "./components/AdminPanel";

interface User {
id: number;
Expand All @@ -47,6 +49,7 @@ interface Location {
longitude: number;
category: string;
points_reward: number;
radius_meters: number;
distance_meters?: number;
difficulty?: 'easy' | 'medium' | 'hard';
estimated_time?: number;
Expand Down Expand Up @@ -86,22 +89,30 @@ function App() {
const [userQuests, setUserQuests] = useState<any[]>([]);
const [selectedLocation, setSelectedLocation] = useState<Location | null>(null);
const [selectedBusiness, setSelectedBusiness] = useState<BusinessPartner | null>(null);
const [activeTab, setActiveTab] = useState<'explore' | 'quests' | 'profile' | 'leaderboard'>('explore');
const [activeTab, setActiveTab] = useState<'explore' | 'quests' | 'profile' | 'leaderboard' | 'admin'>('explore');
const [isLoading, setIsLoading] = useState(true);
const [showWelcome, setShowWelcome] = useState(true);
const [isAdmin, setIsAdmin] = useState(false);
const [adminLocations, setAdminLocations] = useState<Location[]>([]);

useEffect(() => {
// Check for auth success redirect
const urlParams = new URLSearchParams(window.location.search);
const userId = urlParams.get('user_id');
const authenticated = urlParams.get('authenticated');
const admin = urlParams.get('admin');

if (userId && authenticated === 'true') {
fetchUser(userId);
// Clean up URL
window.history.replaceState({}, document.title, window.location.pathname);
}

// Check if user is admin
if (admin === 'true') {
setIsAdmin(true);
}

// Get user location
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
Expand Down Expand Up @@ -129,11 +140,16 @@ function App() {
if (user) {
fetchUserQuests(user.id);
}

if (isAdmin) {
fetchAllLocations();
}

setIsLoading(false);

// Hide welcome screen after 3 seconds
setTimeout(() => setShowWelcome(false), 3000);
}, [user]);
}, [user, isAdmin]);

const fetchUser = async (userId: string) => {
try {
Expand Down Expand Up @@ -282,6 +298,18 @@ function App() {
}
};

const fetchAllLocations = async () => {
try {
const response = await fetch('/api/locations');
if (response.ok) {
const data = await response.json();
setAdminLocations(data);
}
} catch (error) {
console.error('Error fetching all locations:', error);
}
};

const handleCheckIn = async (lat: number, lng: number, locationId?: number, businessId?: number) => {
if (!user) {
alert('Please log in to check in');
Expand Down Expand Up @@ -453,6 +481,7 @@ function App() {
{ id: 'quests', label: 'Quests', icon: Trophy, color: 'text-green-600' },
{ id: 'leaderboard', label: 'Leaderboard', icon: TrendingUp, color: 'text-orange-600' },
{ id: 'profile', label: 'Profile', icon: User, color: 'text-blue-600' },
...(isAdmin ? [{ id: 'admin', label: 'Admin', icon: Settings, color: 'text-purple-600' }] : []),
].map(({ id, label, icon: Icon, color }) => (
<button
key={id}
Expand Down Expand Up @@ -792,6 +821,16 @@ function App() {
)}
</div>
)}

{/* Admin Panel */}
{activeTab === 'admin' && (
<div className="max-w-6xl mx-auto px-4 py-8">
<AdminPanel
locations={adminLocations}
onRefresh={fetchAllLocations}
/>
</div>
)}
</main>
</div>
);
Expand Down
Loading