Connect frontend to API and display events

This commit is contained in:
Media
2026-04-14 20:09:53 -05:00
parent 27c74e0f25
commit 4313714c5d
3 changed files with 46 additions and 162 deletions
+9 -5
View File
@@ -1,21 +1,25 @@
import Fastify from "fastify"; import Fastify from "fastify";
import { pool } from "./db/index.js"; import cors from "@fastify/cors";
import { calendarRoutes } from "./modules/calendar/routes.js"; import { pool } from "./db";
import { calendarRoutes } from "./modules/calendar/routes";
const app = Fastify(); const app = Fastify();
await app.register(cors, {
origin: true,
});
pool pool
.query("SELECT NOW()") .query("SELECT NOW()")
.then((res) => console.log("DB connected:", res.rows[0])) .then((res) => console.log("DB connected:", res.rows[0]))
.catch((err) => console.error("DB connection failed:", err)); .catch((err) => console.error("DB connection failed:", err));
app.register(calendarRoutes);
app.get("/health", async () => { app.get("/health", async () => {
return { ok: true }; return { ok: true };
}); });
app.register(calendarRoutes);
app.listen({ port: 3000, host: "0.0.0.0" }).then(() => { app.listen({ port: 3000, host: "0.0.0.0" }).then(() => {
console.log("API running on port 3000"); console.log("API running on port 3000");
}); });
+32 -135
View File
@@ -1,145 +1,42 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from "react";
import { createClient } from '@supabase/supabase-js' import { getEvents } from "./lib/api";
const supabase = createClient( type Event = {
import.meta.env.VITE_SUPABASE_URL, id: string;
import.meta.env.VITE_SUPABASE_ANON_KEY title: string;
) starts_at: string;
};
export default function App() { export default function CalendarPage() {
const [email, setEmail] = useState('') const [events, setEvents] = useState<Event[]>([]);
const [password, setPassword] = useState('') const [error, setError] = useState("");
const [events, setEvents] = useState<any[]>([])
const [user, setUser] = useState<any>(null)
const [profile, setProfile] = useState<any>(null)
const [status, setStatus] = useState('loading...')
const [error, setError] = useState<string | null>(null)
useEffect(() => { useEffect(() => {
void loadEvents() getEvents()
void loadSession() .then((data) => {
}, []) console.log("Fetched events:", data);
setEvents(data);
async function loadEvents() { })
const { data, error } = await supabase .catch((err) => {
.from('events') console.error("Fetch failed:", err);
.select('*') setError(String(err));
.order('created_at', { ascending: false }) });
}, []);
if (error) {
setError(error.message)
setStatus('error loading events')
return
}
setEvents(data || [])
setStatus('ready')
}
async function loadSession() {
const { data, error } = await supabase.auth.getUser()
if (error) {
setError(error.message)
return
}
setUser(data.user ?? null)
if (data.user) {
const { data: profileData, error: profileError } = await supabase
.from('profiles')
.select('*')
.eq('id', data.user.id)
.maybeSingle()
if (profileError) {
setError(profileError.message)
return
}
setProfile(profileData)
}
}
async function signIn() {
setError(null)
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
})
if (error) {
setError(error.message)
console.error('signIn error:', error)
return
}
console.log('signIn data:', data)
setUser(data.user ?? null)
if (data.user) {
const { data: profileData, error: profileError } = await supabase
.from('profiles')
.select('*')
.eq('id', data.user.id)
.maybeSingle()
if (profileError) {
setError(profileError.message)
console.error('profile error:', profileError)
return
}
setProfile(profileData)
}
}
async function signOut() {
await supabase.auth.signOut()
setUser(null)
setProfile(null)
}
return ( return (
<div style={{ padding: 20, color: 'white' }}> <div style={{ padding: "20px" }}>
<h1>Supabase Test</h1> <h1>Events</h1>
<p>Status: {status}</p>
{!user ? ( {error && <p>Error: {error}</p>}
<div>
<h2>Sign In</h2> <p>Event count: {events.length}</p>
<input
placeholder="Email" {events.map((event) => (
value={email} <div key={event.id}>
onChange={(e) => setEmail(e.target.value)} <strong>{event.title}</strong>
style={{ display: 'block', marginBottom: 12 }} <div>{new Date(event.starts_at).toLocaleString()}</div>
/>
<input
placeholder="Password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
style={{ display: 'block', marginBottom: 12 }}
/>
<button onClick={signIn}>Sign In</button>
</div> </div>
) : ( ))}
<div>
<h2>Signed In</h2>
<p>{user.email}</p>
<button onClick={signOut}>Sign Out</button>
</div>
)}
{error && <p style={{ color: 'red' }}>{error}</p>}
<h2>Profile</h2>
<pre>{JSON.stringify(profile, null, 2)}</pre>
<h2>Events</h2>
<pre>{JSON.stringify(events, null, 2)}</pre>
</div> </div>
) );
} }
+4 -21
View File
@@ -1,26 +1,9 @@
import type { CalendarEvent } from '../types/calendar' export async function getEvents() {
const res = await fetch("http://localhost:3000/events");
const API_BASE = 'http://localhost:8787'
export async function fetchPublicEvents(params: {
from: string
to: string
calendarId?: string
resourceId?: string
}): Promise<CalendarEvent[]> {
const search = new URLSearchParams({
from: params.from,
to: params.to,
})
if (params.calendarId) search.set('calendarId', params.calendarId)
if (params.resourceId) search.set('resourceId', params.resourceId)
const res = await fetch(`${API_BASE}/v1/public/events?${search.toString()}`)
if (!res.ok) { if (!res.ok) {
throw new Error(`Failed to fetch events: ${res.status}`) throw new Error("Failed to fetch events");
} }
return res.json() as Promise<CalendarEvent[]> return res.json();
} }