1import{j as e}from"./index-w8uym0Jr.js";import"./vendor-react-B5kyjNin.js";import"./vendor-auth-3IJ4rw9Z.js";import"./vendor-ui-zVkK_Prl.js";import"./vendor-payment-Cx_eeV9O.js";import"./vendor-framer-CGrNjTbd.js";function s(r){const n={blockquote:"blockquote",code:"code",h1:"h1",h2:"h2",h3:"h3",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",...r.components};return e.jsxs(e.Fragment,{children:[e.jsx(n.h1,{children:"API Documentation"}),` 2`,e.jsx(n.p,{children:"This document outlines the Supabase API endpoints and service functions used throughout the ClassAct application."}),` 3`,e.jsx(n.h2,{children:"Supabase Integration"}),` 4`,e.jsxs(n.p,{children:["ClassAct uses Supabase for authentication, database, and storage. The connection is established in ",e.jsx(n.code,{children:"src/services/supabaseClient.ts"}),":"]}),` 5`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-typescript",children:`import { createClient } from '@supabase/supabase-js';\r 6\r 7// IMPORTANT: Always use environment variables for sensitive credentials\r 8const supabaseUrl = import.meta.env.VITE_SUPABASE_URL; \r 9const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY; \r 10\r 11export const supabase = createClient(supabaseUrl, supabaseAnonKey); 12`})}),` 13`,e.jsxs("div",{className:"note-block",children:[e.jsx("div",{className:"note-icon",children:"â ï¸"}),e.jsxs("div",{className:"note-content",children:[e.jsx("p",{children:e.jsx("strong",{children:"Security Warning:"})}),e.jsx("p",{children:"Never hardcode Supabase credentials in your application code. Always use environment variables as shown above."}),e.jsx("p",{children:"In your .env file:"}),e.jsx("pre",{children:`VITE_SUPABASE_URL=your_supabase_url\r 14VITE_SUPABASE_ANON_KEY=your_supabase_anon_key`})]})]}),` 15`,e.jsx(n.h2,{children:"Database Schema"}),` 16`,e.jsxs("div",{className:"db-schema",children:[e.jsxs("div",{className:"db-table",children:[e.jsxs("div",{className:"table-header",children:[e.jsx("span",{children:"users"}),e.jsx("span",{children:"User accounts and profiles"})]}),e.jsx("div",{className:"table-body",children:e.jsxs("table",{className:"table-columns",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Column"}),e.jsx("th",{children:"Type"}),e.jsx("th",{children:"Description"})]})}),e.jsxs("tbody",{children:[e.jsxs("tr",{children:[e.jsx("td",{className:"column-name primary-key",children:"id"}),e.jsx("td",{className:"column-type",children:"uuid"}),e.jsx("td",{className:"column-description",children:"Unique identifier for the user"})]}),e.jsxs("tr",{children:[e.jsx("td",{className:"column-name",children:"email"}),e.jsx("td",{className:"column-type",children:"varchar"}),e.jsx("td",{className:"column-description",children:"User's email address (unique)"})]}),e.jsxs("tr",{children:[e.jsx("td",{className:"column-name",children:"full_name"}),e.jsx("td",{className:"column-type",children:"varchar"}),e.jsx("td",{className:"column-description",children:"User's full name"})]}),e.jsxs("tr",{children:[e.jsx("td",{className:"column-name",children:"role"}),e.jsx("td",{className:"column-type",children:"varchar"}),e.jsx("td",{className:"column-description",children:"User role (user, admin, super_admin)"})]}),e.jsxs("tr",{children:[e.jsx("td",{className:"column-name",children:"created_at"}),e.jsx("td",{className:"column-type",children:"timestamp"}),e.jsx("td",{className:"column-description",children:"When the user was created"})]})]})]})})]}),e.jsxs("div",{className:"db-table",children:[e.jsxs("div",{className:"table-header",children:[e.jsx("span",{children:"events"}),e.jsx("span",{children:"Performance events"})]}),e.jsx("div",{className:"table-body",children:e.jsxs("table",{className:"table-columns",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Column"}),e.jsx("th",{children:"Type"}),e.jsx("th",{children:"Description"})]})}),e.jsxs("tbody",{children:[e.jsxs("tr",{children:[e.jsx("td",{className:"column-name primary-key",children:"id"}),e.jsx("td",{className:"column-type",children:"uuid"}),e.jsx("td",{className:"column-description",children:"Unique identifier for the event"})]}),e.jsxs("tr",{children:[e.jsx("td",{className:"column-name",children:"title"}),e.jsx("td",{className:"column-type",children:"varchar"}),e.jsx("td",{className:"column-description",children:"Event title"})]}),e.jsxs("tr",{children:[e.jsx("td",{className:"column-name",children:"description"}),e.jsx("td",{className:"column-type",children:"text"}),e.jsx("td",{className:"column-description",children:"Full description of the event"})]}),e.jsxs("tr",{children:[e.jsx("td",{className:"column-name",children:"event_date"}),e.jsx("td",{className:"column-type",children:"timestamp"}),e.jsx("td",{className:"column-description",children:"Date and time of the event"})]}),e.jsxs("tr",{children:[e.jsx("td",{className:"column-name",children:"location"}),e.jsx("td",{className:"column-type",children:"varchar"}),e.jsx("td",{className:"column-description",children:"Physical location of the event"})]}),e.jsxs("tr",{children:[e.jsx("td",{className:"column-name",children:"max_capacity"}),e.jsx("td",{className:"column-type",children:"integer"}),e.jsx("td",{className:"column-description",children:"Maximum number of attendees"})]}),e.jsxs("tr",{children:[e.jsx("td",{className:"column-name foreign-key",children:"created_by"}),e.jsx("td",{className:"column-type",children:"uuid"}),e.jsx("td",{className:"column-description",children:"Reference to user who created the event"})]})]})]})})]}),e.jsxs("div",{className:"db-table",children:[e.jsxs("div",{className:"table-header",children:[e.jsx("span",{children:"tickets"}),e.jsx("span",{children:"Event tickets"})]}),e.jsx("div",{className:"table-body",children:e.jsxs("table",{className:"table-columns",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Column"}),e.jsx("th",{children:"Type"}),e.jsx("th",{children:"Description"})]})}),e.jsxs("tbody",{children:[e.jsxs("tr",{children:[e.jsx("td",{className:"column-name primary-key",children:"id"}),e.jsx("td",{className:"column-type",children:"uuid"}),e.jsx("td",{className:"column-description",children:"Unique identifier for the ticket"})]}),e.jsxs("tr",{children:[e.jsx("td",{className:"column-name foreign-key",children:"event_id"}),e.jsx("td",{className:"column-type",children:"uuid"}),e.jsx("td",{className:"column-description",children:"Reference to associated event"})]}),e.jsxs("tr",{children:[e.jsx("td",{className:"column-name foreign-key",children:"user_id"}),e.jsx("td",{className:"column-type",children:"uuid"}),e.jsx("td",{className:"column-description",children:"Reference to ticket owner"})]}),e.jsxs("tr",{children:[e.jsx("td",{className:"column-name",children:"price"}),e.jsx("td",{className:"column-type",children:"decimal"}),e.jsx("td",{className:"column-description",children:"Ticket price"})]}),e.jsxs("tr",{children:[e.jsx("td",{className:"column-name",children:"purchase_date"}),e.jsx("td",{className:"column-type",children:"timestamp"}),e.jsx("td",{className:"column-description",children:"When the ticket was purchased"})]}),e.jsxs("tr",{children:[e.jsx("td",{className:"column-name",children:"qr_code"}),e.jsx("td",{className:"column-type",children:"text"}),e.jsx("td",{className:"column-description",children:"Generated QR code data"})]}),e.jsxs("tr",{children:[e.jsx("td",{className:"column-name",children:"is_validated"}),e.jsx("td",{className:"column-type",children:"boolean"}),e.jsx("td",{className:"column-description",children:"Whether ticket was used/scanned"})]})]})]})})]})]}),` 17`,e.jsx(n.h2,{children:"Database Relationships"}),` 18`,e.jsxs("div",{className:"relationship-diagram",children:[e.jsxs("div",{className:"relationship-line",children:[e.jsx("div",{className:"table-name",children:"users"}),e.jsx("div",{className:"relationship-type",children:"one-to-many"}),e.jsx("div",{className:"relationship-arrow",children:"â"}),e.jsx("div",{className:"table-name",children:"events"})]}),e.jsxs("div",{className:"relationship-line",children:[e.jsx("div",{className:"table-name",children:"events"}),e.jsx("div",{className:"relationship-type",children:"one-to-many"}),e.jsx("div",{className:"relationship-arrow",children:"â"}),e.jsx("div",{className:"table-name",children:"tickets"})]}),e.jsxs("div",{className:"relationship-line",children:[e.jsx("div",{className:"table-name",children:"users"}),e.jsx("div",{className:"relationship-type",children:"one-to-many"}),e.jsx("div",{className:"relationship-arrow",children:"â"}),e.jsx("div",{className:"table-name",children:"tickets"})]})]}),` 19`,e.jsx(n.h2,{children:"API Endpoints"}),` 20`,e.jsx(n.h3,{children:"Authentication API"}),`
21`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-typescript",children:`// Register a new user\r 22const signUp = async (email: string, password: string, userData: UserData) => {\r 23 return await supabase.auth.signUp({\r 24 email,\r 25 password,\r 26 options: {\r 27 data: userData\r 28 }\r 29 });\r 30};\r 31\r 32// Sign in a user\r 33const signIn = async (email: string, password: string) => {\r 34 return await supabase.auth.signInWithPassword({\r 35 email,\r 36 password\r 37 });\r 38};\r 39\r 40// Sign out a user\r 41const signOut = async () => {\r 42 return await supabase.auth.signOut();\r 43};\r 44\r 45// Get current user session\r 46const getSession = async () => {\r 47 return await supabase.auth.getSession();\r 48}; 49`})}),` 50`,e.jsx(n.h3,{children:"Events API"}),` 51`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-typescript",children:`// Get all events\r 52const getAllEvents = async () => {\r 53 return await supabase\r 54 .from('events')\r 55 .select('*')\r 56 .order('date', { ascending: true });\r 57};\r 58\r 59// Get event by ID\r 60const getEventById = async (id: string) => {\r 61 return await supabase\r 62 .from('events')\r 63 .select('*')\r 64 .eq('id', id)\r 65 .single();\r 66};\r 67\r 68// Create a new event\r 69const createEvent = async (eventData: EventData) => {\r 70 return await supabase\r 71 .from('events')\r 72 .insert(eventData)\r 73 .select();\r 74};\r 75\r 76// Update an event\r 77const updateEvent = async (id: string, eventData: Partial<EventData>) => {\r 78 return await supabase\r 79 .from('events')\r 80 .update(eventData)\r 81 .eq('id', id)\r 82 .select();\r 83};\r 84\r 85// Delete an event\r 86const deleteEvent = async (id: string) => {\r 87 return await supabase\r 88 .from('events')\r 89 .delete()\r 90 .eq('id', id);\r 91}; 92`})}),` 93`,e.jsx(n.h3,{children:"Tickets API"}),` 94`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-typescript",children:`// Create a ticket\r 95const createTicket = async (ticketData: TicketData) => {\r 96 return await supabase\r 97 .from('tickets')\r 98 .insert(ticketData)\r 99 .select();\r 100};\r 101\r 102// Get tickets by user ID\r 103const getTicketsByUserId = async (userId: string) => {\r 104 return await supabase\r 105 .from('tickets')\r 106 .select(\`\r 107 *,\r 108 events (*)\r 109 \`)\r 110 .eq('user_id', userId);\r 111};\r 112\r 113// Verify ticket\r 114const verifyTicket = async (ticketId: string) => {\r 115 return await supabase\r 116 .from('tickets')\r 117 .select(\`\r 118 *,\r 119 events (*)\r 120 \`)\r 121 .eq('id', ticketId)\r 122 .single();\r 123};\r 124\r 125// Mark ticket as used\r 126const markTicketAsUsed = async (ticketId: string) => {\r 127 return await supabase\r 128 .from('tickets')\r 129 .update({ is_used: true })\r 130 .eq('id', ticketId)\r 131 .select();\r 132}; 133`})}),` 134`,e.jsx(n.h3,{children:"User Management API"}),` 135`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-typescript",children:`// Get all users (admin only)\r 136const getAllUsers = async () => {\r 137 return await supabase\r 138 .from('users')\r 139 .select('*');\r 140};\r 141\r 142// Update user role\r 143const updateUserRole = async (userId: string, role: number) => {\r 144 return await supabase\r 145 .from('users')\r 146 .update({ role })\r 147 .eq('id', userId)\r 148 .select();\r 149};\r 150\r 151// Get user profile\r 152const getUserProfile = async (userId: string) => {\r 153 return await supabase\r 154 .from('users')\r 155 .select('*')\r 156 .eq('id', userId)\r 157 .single();\r 158};\r 159\r 160// Update user profile\r 161const updateUserProfile = async (userId: string, profileData: Partial<UserProfile>) => {\r 162 return await supabase\r 163 .from('users')\r 164 .update(profileData)\r 165 .eq('id', userId)\r 166 .select();\r 167}; 168`})}),` 169`,e.jsx(n.h2,{children:"Storage API"}),` 170`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-typescript",children:`// Upload event image\r 171const uploadEventImage = async (file: File, path: string) => {\r 172 return await supabase.storage\r 173 .from('event-images')\r 174 .upload(path, file, {\r 175 cacheControl: '3600',\r 176 upsert: true\r 177 });\r 178};\r 179\r 180// Get public URL for event image\r 181const getEventImageUrl = (path: string) => {\r 182 return supabase.storage\r 183 .from('event-images')\r 184 .getPublicUrl(path).data.publicUrl;\r 185};\r 186\r 187// Delete event image\r 188const deleteEventImage = async (path: string) => {\r 189 return await supabase.storage\r 190 .from('event-images')\r 191 .remove([path]);\r 192}; 193`})}),` 194`,e.jsx(n.h2,{children:"Error Handling"}),` 195`,e.jsx(n.p,{children:"All API calls should implement proper error handling:"}),`
196`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-typescript",children:`try {\r 197 const { data, error } = await someApiCall();\r 198 \r 199 if (error) {\r 200 logger.error('API Error:', error);\r 201 throw error;\r 202 }\r 203 \r 204 return data;\r 205} catch (err) {\r 206 logger.error('Unexpected error:', err);\r 207 // Use ErrorContext to handle application-wide errors\r 208 errorContext.setError({\r 209 message: 'Failed to fetch data',\r 210 context: 'EventsService',\r 211 details: err.message\r 212 });\r 213 throw err;\r 214} 215`})}),` 216`,e.jsx(n.h2,{children:"Error Handling with Supabase"}),` 217`,e.jsxs("div",{className:"note-block",children:[e.jsx("div",{className:"note-icon",children:"â ï¸"}),e.jsxs("div",{className:"note-content",children:[e.jsx("p",{children:e.jsx("strong",{children:"Error Handling Standard:"})}),e.jsx("p",{children:"Always use the ErrorContext for handling Supabase errors to ensure consistent error reporting throughout the application."})]})]}),` 218`,e.jsx(n.p,{children:"When working with Supabase APIs, follow this error handling pattern:"}),` 219`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-tsx",children:`{\`import { useError } from '../context/ErrorContext';\r 220import { createLogger } from '../utils/logger';\r 221\r 222const logger = createLogger('ComponentName', true);\r 223const { handleError } = useError();\r 224\r 225// Within async functions\r 226try {\r 227 const { data, error } = await supabase.from('events').select('*');\r 228 if (error) throw error;\r 229 return data;\r 230} catch (err) {\r 231 logger.error('Error fetching events:', err);\r 232 handleError(err, 'ComponentName.functionName');\r 233 return [];\r 234}\`} 235`})}),` 236`,e.jsx(n.h2,{children:"Supabase Best Practices"}),` 237`,e.jsxs(n.ol,{children:[` 238`,e.jsxs(n.li,{children:[e.jsx(n.strong,{children:"Use prepared queries"})," to prevent SQL injection"]}),` 239`,e.jsxs(n.li,{children:[e.jsx(n.strong,{children:"Handle errors gracefully"})," to provide a good user experience"]}),` 240`,e.jsxs(n.li,{children:[e.jsx(n.strong,{children:"Use RLS policies"})," in Supabase to secure your data"]}),` 241`,e.jsxs(n.li,{children:[e.jsx(n.strong,{children:"Minimize data transfer"})," by only selecting needed fields"]}),` 242`,e.jsxs(n.li,{children:[e.jsx(n.strong,{children:"Use transactions"})," when performing multiple related operations"]}),` 243`]}),` 244`,e.jsx(n.h2,{children:"API Client Setup"}),` 245`,e.jsx(n.p,{children:"The Supabase client is configured in the following file:"}),` 246`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-typescript",children:`// src/services/supabaseClient.ts\r 247import { createClient } from '@supabase/supabase-js';\r 248\r 249const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;\r 250const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;\r 251\r 252const supabase = createClient(supabaseUrl, supabaseAnonKey);\r 253\r 254export default supabase; 255`})}),` 256`,e.jsx(n.h2,{children:"Example API Usage"}),` 257`,e.jsx(n.h3,{children:"Authentication"}),` 258`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-typescript",children:`// Login with email and password\r 259const loginUser = async (email: string, password: string) => {\r 260 try {\r 261 const { data, error } = await supabase.auth.signInWithPassword({\r 262 email,\r 263 password,\r 264 });\r 265 \r 266 if (error) throw error;\r 267 return data;\r 268 } catch (error) {\r 269 console.error('Error logging in:', error);\r 270 throw error;\r 271 }\r 272};\r 273\r 274// Sign up a new user\r 275const registerUser = async (email: string, password: string, fullName: string) => {\r 276 try {\r 277 const { data, error } = await supabase.auth.signUp({\r 278 email,\r 279 password,\r 280 options: {\r 281 data: {\r 282 full_name: fullName,\r 283 role: 'user',\r 284 },\r 285 },\r 286 });\r 287 \r 288 if (error) throw error;\r 289 return data;\r 290 } catch (error) {\r 291 console.error('Error registering user:', error);\r 292 throw error;\r 293 }\r 294}; 295`})}),` 296`,e.jsx(n.h3,{children:"Event Operations"}),` 297`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-typescript",children:`// Create a new event\r 298const createEvent = async (eventData) => {\r 299 const { data, error } = await supabase\r 300 .from('events')\r 301 .insert([eventData])\r 302 .select();\r 303 \r 304 if (error) throw error;\r 305 return data;\r 306};\r 307\r 308// Get events with pagination\r 309const getEvents = async (page = 1, limit = 10) => {\r 310 const from = (page - 1) * limit;\r 311 const to = from + limit - 1;\r 312 \r 313 const { data, error, count } = await supabase\r 314 .from('events')\r 315 .select('*', { count: 'exact' })\r 316 .order('event_date', { ascending: true })\r 317 .range(from, to);\r 318 \r 319 if (error) throw error;\r 320 return { data, count };\r 321}; 322`})}),` 323`,e.jsx(n.h2,{children:"Terminal Commands for API Testing"}),` 324`,e.jsxs("div",{className:"command-example",children:[e.jsxs("div",{className:"command-line",children:[e.jsx("span",{className:"prompt",children:"$"}),e.jsx("span",{className:"command",children:'curl -X GET "https://yourproject.supabase.co/rest/v1/events" \\'})]}),e.jsxs("div",{className:"command-line",children:[e.jsx("span",{className:"prompt"}),e.jsx("span",{className:"command",children:' -H "apikey: YOUR_ANON_KEY" \\'})]}),e.jsxs("div",{className:"command-line",children:[e.jsx("span",{className:"prompt"}),e.jsx("span",{className:"command",children:' -H "Authorization: Bearer USER_TOKEN"'})]}),e.jsx("div",{className:"output-line",children:e.jsx("pre",{children:'[{ "id": "uuid", "title": "Event Title", "description": "Event description" }]'})})]}),` 325`,e.jsx(n.h2,{children:"User Authentication Flow"}),` 326`,e.jsxs("div",{className:"flow-diagram",children:[e.jsx("div",{className:"flow-title",children:"Authentication Process"}),e.jsxs("div",{className:"flow-steps",children:[e.jsxs("div",{className:"flow-step",children:[e.jsx("div",{className:"step-number",children:"1"}),e.jsxs("div",{className:"step-content",children:[e.jsx("div",{className:"step-title",children:"User Sign-Up"}),e.jsx("div",{className:"step-description",children:"User enters email, password, and profile data"})]})]}),e.jsxs("div",{className:"flow-step",children:[e.jsx("div",{className:"step-number",children:"2"}),e.jsxs("div",{className:"step-content",children:[e.jsx("div",{className:"step-title",children:"Email Verification"}),e.jsx("div",{className:"step-description",children:"User clicks verification link in email"})]})]}),e.jsxs("div",{className:"flow-step",children:[e.jsx("div",{className:"step-number",children:"3"}),e.jsxs("div",{className:"step-content",children:[e.jsx("div",{className:"step-title",children:"Login"}),e.jsx("div",{className:"step-description",children:"User logs in with verified credentials"})]})]}),e.jsxs("div",{className:"flow-step",children:[e.jsx("div",{className:"step-number",children:"4"}),e.jsxs("div",{className:"step-content",children:[e.jsx("div",{className:"step-title",children:"Auth Check"}),e.jsx("div",{className:"step-description",children:"Token validation with each request"})]})]})]})]}),` 327`,e.jsxs(n.blockquote,{children:[` 328`,e.jsxs(n.p,{children:[e.jsx(n.strong,{children:"Important Note:"})," Always use environment variables for API keys and never expose them in client-side code. The Supabase client automatically handles token refresh and secure storage."]}),` 329`]}),` 330`,e.jsx(n.h2,{children:"API Best Practices"}),` 331`,e.jsxs(n.ol,{children:[` 332`,e.jsxs(n.li,{children:[e.jsx(n.strong,{children:"Always validate data"})," before sending it to the API"]}),` 333`,e.jsxs(n.li,{children:[e.jsx(n.strong,{children:"Handle errors gracefully"})," to provide a good user experience"]}),` 334`,e.jsxs(n.li,{children:[e.jsx(n.strong,{children:"Use RLS policies"})," in Supabase to secure your data"]}),` 335`,e.jsxs(n.li,{children:[e.jsx(n.strong,{children:"Minimize data transfer"})," by only selecting needed fields"]}),` 336`,e.jsxs(n.li,{children:[e.jsx(n.strong,{children:"Use transactions"})," when performing multiple related operations"]}),` 337`]})]})}function o(r={}){const{wrapper:n}=r.components||{};return n?e.jsx(n,{...r,children:e.jsx(s,{...r})}):s(r)}export{o as default};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.