PageSourceSearch

https://class-act.netlify.app/assets/004_api_documentation-ehDJDYb0.js

js class-act.netlify.app collected 2026-10-03 10:34:57 UTC 21,230 bytes, 337 lines download raw bytes

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.