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
218 changes: 218 additions & 0 deletions app/api-test/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
"use client";

import React, { useState } from "react";
import {
healthCheck,
getUniversities,
createUniversity,
} from "@/lib/api/services";

export default function APITestPage() {
const [results, setResults] = useState<any[]>([]);
const [loading, setLoading] = useState(false);

const addResult = (test: string, status: "✅" | "❌", message: string) => {
setResults((prev) => [
...prev,
{ test, status, message, time: new Date() },
]);
};

const clearResults = () => setResults([]);

const testHealthCheck = async () => {
setLoading(true);
try {
const response = await healthCheck();
addResult(
"Health Check",
"✅",
`API is online: ${JSON.stringify(response)}`,
);
} catch (error: any) {
addResult("Health Check", "❌", error?.message || "Failed to connect");
}
setLoading(false);
};

const testGetUniversities = async () => {
setLoading(true);
try {
const response = await getUniversities();
addResult(
"Get Universities",
"✅",
`Retrieved ${response?.length || 0} universities: ${JSON.stringify(response).substring(0, 100)}...`,
);
} catch (error: any) {
addResult("Get Universities", "❌", error?.message || "Failed to fetch");
}
setLoading(false);
};

const testCreateUniversity = async () => {
setLoading(true);
try {
const testData = {
name: `Test University ${Date.now()}`,
location: "Test Location",
address: "123 Test Street",
city: "Test City",
country: "Nigeria",
latitude: 6.5244,
longitude: 3.3957,
};

const response = await createUniversity(testData);
addResult(
"Create University",
"✅",
`Created university: ${JSON.stringify(response).substring(0, 100)}...`,
);
} catch (error: any) {
addResult(
"Create University",
"❌",
error?.response?.data?.message || error?.message || "Failed to create",
);
}
setLoading(false);
};

return (
<div className="min-h-screen bg-gradient-to-br from-purple-50 to-blue-50 p-8">
<div className="max-w-4xl mx-auto">
{/* Header */}
<div className="mb-8">
<h1 className="text-4xl font-bold text-gray-900 mb-2">
Campus Guide API Test
</h1>
<p className="text-gray-600">
Test whether the backend API is working correctly
</p>
</div>

{/* Test Buttons */}
<div className="bg-white rounded-lg shadow-lg p-6 mb-6">
<h2 className="text-xl font-semibold text-gray-900 mb-4">
Run Tests
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<button
onClick={testHealthCheck}
disabled={loading}
className="bg-blue-500 hover:bg-blue-600 disabled:bg-gray-400 text-white px-4 py-2 rounded-lg font-medium transition"
>
🏥 Health Check
</button>
<button
onClick={testGetUniversities}
disabled={loading}
className="bg-green-500 hover:bg-green-600 disabled:bg-gray-400 text-white px-4 py-2 rounded-lg font-medium transition"
>
📚 Get Universities
</button>
<button
onClick={testCreateUniversity}
disabled={loading}
className="bg-purple-500 hover:bg-purple-600 disabled:bg-gray-400 text-white px-4 py-2 rounded-lg font-medium transition"
>
➕ Create Test University
</button>
<button
onClick={clearResults}
disabled={loading}
className="bg-red-500 hover:bg-red-600 disabled:bg-gray-400 text-white px-4 py-2 rounded-lg font-medium transition col-span-full"
>
🗑️ Clear Results
</button>
</div>
</div>

{/* Results */}
<div className="bg-white rounded-lg shadow-lg p-6">
<h2 className="text-xl font-semibold text-gray-900 mb-4">
Results ({results.length})
</h2>

{results.length === 0 ? (
<p className="text-gray-500 text-center py-8">
No tests run yet. Click a button above to start testing!
</p>
) : (
<div className="space-y-3">
{results.map((result, index) => (
<div
key={index}
className={`border-l-4 p-4 rounded ${
result.status === "✅"
? "border-green-500 bg-green-50"
: "border-red-500 bg-red-50"
}`}
>
<div className="flex items-start justify-between">
<div className="flex-1">
<p className="font-semibold text-gray-900">
{result.status} {result.test}
</p>
<p className="text-sm text-gray-700 mt-1 break-all">
{result.message}
</p>
</div>
<p className="text-xs text-gray-500 ml-2">
{result.time.toLocaleTimeString()}
</p>
</div>
</div>
))}
</div>
)}
</div>

{/* Info Section */}
<div className="bg-purple-50 border border-purple-200 rounded-lg p-6 mt-6">
<h3 className="text-lg font-semibold text-gray-900 mb-3">
🔍 What Each Test Does
</h3>
<ul className="space-y-2 text-gray-700">
<li>
<strong>Health Check:</strong> Verifies the API server is running
</li>
<li>
<strong>Get Universities:</strong> Retrieves all universities from
the database
</li>
<li>
<strong>Create Test University:</strong> Attempts to create a new
university entry
</li>
</ul>
</div>

{/* Status Indicator */}
<div className="mt-6 p-4 bg-white rounded-lg border border-gray-200">
<p className="text-sm text-gray-600">
<strong>API Endpoint:</strong>{" "}
<code className="bg-gray-100 px-2 py-1 rounded">
{process.env.NEXT_PUBLIC_CAMPUS_GUIDE_API_URL}
</code>
</p>
<p className="text-sm text-gray-600 mt-2">
<strong>Mode:</strong>{" "}
<span
className={
process.env.NEXT_PUBLIC_USE_MOCK_API === "true"
? "text-yellow-600 font-semibold"
: "text-green-600 font-semibold"
}
>
{process.env.NEXT_PUBLIC_USE_MOCK_API === "true"
? "🧪 Mock Mode (Using Local Responses)"
: "🚀 Production Mode (Real Backend)"}
</span>
</p>
</div>
</div>
</div>
);
}
65 changes: 57 additions & 8 deletions app/contribute/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,48 @@ import ContributeForm, {
ContributeFormData,
} from "@/component/Navigation/sections/ContributeForm";
import ContributeSuccess from "@/component/Navigation/sections/contribute/ContributeSuccess";
import { submitCampusContribution } from "@/lib/api/services";

export default function ContributePage() {
const [showSuccessPage, setShowSuccessPage] = useState(false);
const [submittedFormData, setSubmittedFormData] =
useState<ContributeFormData | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string>("");

const handleContributeSubmit = (formData: ContributeFormData) => {
console.log("Contribute form submitted:", formData);
setSubmittedFormData(formData);
setShowSuccessPage(true);
// Toggle between real API and mock mode (change to false when backend is fully ready)
const USE_MOCK_MODE = process.env.NEXT_PUBLIC_USE_MOCK_API === "true";

const handleContributeSubmit = async (formData: ContributeFormData) => {
setIsLoading(true);
setError("");

try {
console.log("🚀 Submitting contribution...", formData);

// Call the API
const response = await submitCampusContribution(formData, USE_MOCK_MODE);

console.log("✅ Submission successful:", response);

// Set the data for success page
setSubmittedFormData(formData);
setShowSuccessPage(true);
} catch (err: any) {
const errorMessage =
err?.response?.data?.message ||
err?.message ||
"Failed to submit contribution. Please try again.";
console.error("❌ Submission failed:", err);
setError(errorMessage);

// Show error but keep form visible for retry
setTimeout(() => {
setError("");
}, 5000);
} finally {
setIsLoading(false);
}
};

if (showSuccessPage && submittedFormData) {
Expand All @@ -27,9 +59,26 @@ export default function ContributePage() {
}

return (
<ContributeForm
onClose={() => (window.location.href = "/")}
onSubmit={handleContributeSubmit}
/>
<>
<ContributeForm
onClose={() => (window.location.href = "/")}
onSubmit={handleContributeSubmit}
isLoading={isLoading}
error={error}
/>
{isLoading && (
<div className="fixed inset-0 bg-black bg-opacity-40 flex items-center justify-center z-50">
<div className="bg-white rounded-lg p-8 text-center">
<div className="animate-spin rounded-full h-16 w-16 border-4 border-gray-300 border-t-purple-600 mx-auto mb-4"></div>
<p className="text-gray-700 font-medium">
Submitting your contribution...
</p>
<p className="text-gray-500 text-sm mt-2">
{USE_MOCK_MODE ? "🧪 (Mock Mode)" : "Sending to API..."}
</p>
</div>
</div>
)}
</>
);
}
22 changes: 20 additions & 2 deletions component/Navigation/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Link from "next/link";
import Image from "next/image";
import { COLORS } from "@/constant/design";
import { useHideOnScroll } from "@/lib/hooks/useHideOnScroll";
import { MapIcon, Hamburger } from "@/assets/icons";
import { MapIcon, ContributeBg, Hamburger } from "@/assets/icons";

interface HeaderProps {
onMenuClick?: () => void;
Expand Down Expand Up @@ -60,7 +60,25 @@ export const Header: React.FC<HeaderProps> = ({
<span className="text-sm font-medium">Web map</span>
</Link>

{/* Contribute Button - Currently disabled */}
{/* Contribute Button */}
<Link
href="/contribute"
className="hidden md:flex items-center gap-2 px-5 py-2 rounded-full font-light text-white transition-all duration-300 hover:shadow-lg hover:scale-105"
style={{
backgroundColor: COLORS.primary,
}}
onMouseEnter={(e) => {
(e.currentTarget as HTMLElement).style.backgroundColor =
COLORS.primaryLight;
}}
onMouseLeave={(e) => {
(e.currentTarget as HTMLElement).style.backgroundColor =
COLORS.primary;
}}
>
<span className="text-sm">Contribute</span>
<ContributeBg size={13} color="white" />
</Link>

{/* Mobile Menu Button */}
<button
Expand Down
Loading