Connect frontend to API and display events
This commit is contained in:
+10
-6
@@ -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
@@ -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
@@ -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();
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user