Function bodies 104 total
SetupScreen function · javascript · L8-L76 (69 LOC)src/App.jsx
function SetupScreen() {
return (
<div className="min-h-screen flex flex-col items-center justify-center bg-neutral-950 px-6">
<div className="bg-neutral-900 border border-neutral-800 rounded-2xl shadow-2xl p-8 w-full max-w-md animate-scale-in">
<div className="flex justify-center mb-6">
<img src="/logo.png" alt="Imperial Sporthorses" className="h-20 w-20 object-contain" />
</div>
<h1 className="text-2xl font-bold text-amber-400 mb-1 text-center">
Imperial Sporthorses
</h1>
<p className="text-neutral-400 text-sm mb-6 text-center">
Almost ready! Connect your Supabase project to get started.
</p>
<div className="space-y-4 text-sm">
<div className="flex gap-3 items-start bg-neutral-800 rounded-xl p-3">
<Database className="w-5 h-5 text-amber-400 shrink-0 mt-0.5" />
<div>
<p className="font-semibold text-neutral-100">1. Create a Supabase ProjectApp function · javascript · L78-L109 (32 LOC)src/App.jsx
export default function App() {
const { session, profile, loading } = useAuth()
if (!isConfigured) {
return <SetupScreen />
}
if (loading) {
return (
<div className="min-h-screen flex items-center justify-center bg-neutral-950">
<div className="text-center animate-fade-in">
<img src="/logo.png" alt="Imperial Sporthorses" className="h-16 w-16 object-contain mx-auto mb-4 opacity-80" />
<RefreshCw className="w-8 h-8 text-amber-400 animate-spin mx-auto mb-3" />
<p className="text-sm text-neutral-400">Loading...</p>
</div>
</div>
)
}
// Not logged in
if (!session) {
return <LoginPage />
}
// Logged in but no display name -> complete profile
if (!profile?.display_name) {
return <CompleteProfilePage />
}
// Fully authenticated
return <Layout />
}HandOffModal function · javascript · L5-L38 (34 LOC)src/components/HandOffModal.jsx
export default function HandOffModal({ shift, dateStr, users, currentUserId, onClose, onSubmit }) {
const otherUsers = users.filter((u) => u.id !== currentUserId)
return (
<div className="fixed inset-0 bg-black/40 z-50 flex items-end justify-center animate-fade-in">
<div className="bg-neutral-900 border-t border-neutral-700 rounded-t-2xl w-full max-w-lg p-5 pb-8 animate-slide-up safe-area-bottom">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-2">
<ArrowRightLeft className="w-4 h-4 text-amber-400" />
<h3 className="font-semibold text-neutral-100">Hand Off Shift</h3>
</div>
<button onClick={onClose} className="p-2">
<X className="w-5 h-5 text-neutral-500" />
</button>
</div>
<p className="text-sm text-neutral-400 mb-4">
Hand off your <span className="font-medium text-neutral-200">{SHIFT_LABELS[shift] || shift}</span>Layout function · javascript · L28-L139 (112 LOC)src/components/Layout.jsx
export default function Layout() {
const [activeTab, setActiveTab] = useState('tasks')
const [tasksView, setTasksView] = useState('weekly') // 'weekly' or 'board'
const { isAdmin } = useAuth()
function renderTab() {
switch (activeTab) {
case 'tasks':
return (
<div>
{/* Sub-tabs for Tasks */}
<div className="flex items-center gap-2 px-4 pt-5 pb-2">
<button
onClick={() => setTasksView('weekly')}
className={`flex items-center gap-1.5 text-sm font-semibold px-4 py-2 rounded-xl transition ${
tasksView === 'weekly'
? 'bg-amber-500/20 text-amber-400'
: 'bg-neutral-800/60 text-neutral-400 hover:text-neutral-300'
}`}
>
<CalendarDays className="w-4 h-4" />
Weekly
</button>
<button
onClick={() => setTasksView('board')}
renderTab function · javascript · L33-L88 (56 LOC)src/components/Layout.jsx
function renderTab() {
switch (activeTab) {
case 'tasks':
return (
<div>
{/* Sub-tabs for Tasks */}
<div className="flex items-center gap-2 px-4 pt-5 pb-2">
<button
onClick={() => setTasksView('weekly')}
className={`flex items-center gap-1.5 text-sm font-semibold px-4 py-2 rounded-xl transition ${
tasksView === 'weekly'
? 'bg-amber-500/20 text-amber-400'
: 'bg-neutral-800/60 text-neutral-400 hover:text-neutral-300'
}`}
>
<CalendarDays className="w-4 h-4" />
Weekly
</button>
<button
onClick={() => setTasksView('board')}
className={`flex items-center gap-1.5 text-sm font-semibold px-4 py-2 rounded-xl transition ${
tasksView === 'board'
? 'bg-amber-500/20 text-amber-400'
NoteEditor function · javascript · L5-L192 (188 LOC)src/components/NoteEditor.jsx
export default function NoteEditor({ note, onBack, onDelete, onSave, onTogglePin }) {
const [title, setTitle] = useState(note?.title || '')
const [body, setBody] = useState(note?.body || '')
const [saveStatus, setSaveStatus] = useState('saved') // 'saved' | 'saving' | 'unsaved'
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false)
const bodyRef = useRef(null)
const titleRef = useRef(null)
const isNew = !note?.title && !note?.body
// Auto-focus title if new note
useEffect(() => {
if (isNew && titleRef.current) {
titleRef.current.focus()
}
}, [])
function handleChange(newTitle, newBody) {
setTitle(newTitle)
setBody(newBody)
setSaveStatus('saving')
onSave(note.id, { title: newTitle, body: newBody }).then(() => {
setSaveStatus('saved')
})
}
function handleBack() {
onBack()
}
function insertChecklist() {
if (!bodyRef.current) return
const ta = bodyRef.current
const start = ta.selectionStarthandleChange function · javascript · L21-L28 (8 LOC)src/components/NoteEditor.jsx
function handleChange(newTitle, newBody) {
setTitle(newTitle)
setBody(newBody)
setSaveStatus('saving')
onSave(note.id, { title: newTitle, body: newBody }).then(() => {
setSaveStatus('saved')
})
}Repobility (the analyzer behind this table) · https://repobility.com
handleBack function · javascript · L30-L32 (3 LOC)src/components/NoteEditor.jsx
function handleBack() {
onBack()
}insertChecklist function · javascript · L34-L53 (20 LOC)src/components/NoteEditor.jsx
function insertChecklist() {
if (!bodyRef.current) return
const ta = bodyRef.current
const start = ta.selectionStart
const before = body.slice(0, start)
const after = body.slice(ta.selectionEnd)
// If at start of line or start of text, just insert. Otherwise add newline first.
const needsNewline = start > 0 && before[before.length - 1] !== '\n'
const insert = (needsNewline ? '\n' : '') + '- [ ] '
const newBody = before + insert + after
setBody(newBody)
setSaveStatus('saving')
onSave(note.id, { title, body: newBody }).then(() => setSaveStatus('saved'))
// Set cursor after the inserted text
requestAnimationFrame(() => {
const pos = start + insert.length
ta.setSelectionRange(pos, pos)
ta.focus()
})
}handleBodyKeyDown function · javascript · L56-L90 (35 LOC)src/components/NoteEditor.jsx
function handleBodyKeyDown(e) {
if (e.key === 'Enter') {
const ta = bodyRef.current
const pos = ta.selectionStart
const lines = body.slice(0, pos).split('\n')
const currentLine = lines[lines.length - 1]
// If current line is a checklist item, auto-continue
if (/^- \[[ x]\] /.test(currentLine)) {
e.preventDefault()
const before = body.slice(0, pos)
const after = body.slice(ta.selectionEnd)
// If the current checklist line is empty (just the prefix), remove it instead
if (/^- \[[ x]\] $/.test(currentLine)) {
const newBody = body.slice(0, pos - currentLine.length) + after
setBody(newBody)
setSaveStatus('saving')
onSave(note.id, { title, body: newBody }).then(() => setSaveStatus('saved'))
requestAnimationFrame(() => {
const newPos = pos - currentLine.length
ta.setSelectionRange(newPos, newPos)
})
} else {
conAuthProvider function · javascript · L8-L218 (211 LOC)src/contexts/AuthContext.jsx
export function AuthProvider({ children }) {
const [session, setSession] = useState(null)
const [profile, setProfile] = useState(null)
const [loading, setLoading] = useState(true)
const [authReady, setAuthReady] = useState(false)
const fetchingRef = useRef(false)
// Safety timeout: never stay loading for more than 8 seconds
useEffect(() => {
const timer = setTimeout(() => {
if (loading) {
console.warn('[Auth] Safety timeout — forcing loading=false')
setLoading(false)
}
}, 8000)
return () => clearTimeout(timer)
}, [loading])
// Effect 1: Track auth session ONLY. No database calls here.
useEffect(() => {
if (!isConfigured) {
setLoading(false)
return
}
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(event, currentSession) => {
console.log(`[Auth] event: ${event}`)
// Ignore SIGNED_OUT from failed token refreshes (network blips)
// Only clear session if handleVisibilityChange function · javascript · L60-L71 (12 LOC)src/contexts/AuthContext.jsx
function handleVisibilityChange() {
if (document.visibilityState === 'visible') {
supabase.auth.startAutoRefresh()
// Explicitly refresh session when app comes back to foreground
supabase.auth.getSession().then(({ data, error }) => {
if (data?.session) setSession(data.session)
if (error) console.warn('[Auth] Visibility refresh error:', error.message)
})
} else {
supabase.auth.stopAutoRefresh()
}
}loadProfile function · javascript · L106-L165 (60 LOC)src/contexts/AuthContext.jsx
async function loadProfile(attempt = 1) {
if (fetchingRef.current) return
fetchingRef.current = true
console.log(`[Auth] Fetching profile for: ${userId} (attempt ${attempt})`)
try {
const { data, error } = await supabase
.from('users')
.select('*')
.eq('id', userId)
.single()
if (error && error.code === 'PGRST116') {
console.log('[Auth] No profile found, upserting...')
// Use upsert to handle race condition with handle_new_user trigger
const { data: newProfile, error: upsertErr } = await supabase
.from('users')
.upsert({ id: userId, email: userEmail }, { onConflict: 'id' })
.select()
.single()
if (upsertErr) {
console.error('[Auth] Failed to upsert profile:', upsertErr.message)
// Retry: the trigger might have created it, try fetching again
if (attempt < 3) {
fetchupdateDisplayName function · javascript · L170-L187 (18 LOC)src/contexts/AuthContext.jsx
async function updateDisplayName(displayName) {
if (!session?.user) throw new Error('No session')
const { data, error } = await supabase
.from('users')
.update({ display_name: displayName })
.eq('id', session.user.id)
.select()
.single()
if (error) {
console.error('[Auth] updateDisplayName error:', error)
throw error
}
// Update local profile state to instantly reflect the change
setProfile(data)
return data
}signInWithGoogle function · javascript · L189-L197 (9 LOC)src/contexts/AuthContext.jsx
async function signInWithGoogle() {
const { error } = await supabase.auth.signInWithOAuth({
provider: 'google',
options: {
redirectTo: window.location.origin,
},
})
if (error) throw error
}Provenance: Repobility (https://repobility.com) — every score reproducible from /scan/
signOut function · javascript · L199-L203 (5 LOC)src/contexts/AuthContext.jsx
async function signOut() {
await supabase.auth.signOut()
// The onAuthStateChange listener will handle clearing session/profile
window.location.reload() // Force a full reload to clear all state
}useHandoffs function · javascript · L4-L125 (122 LOC)src/hooks/useHandoffs.js
export default function useHandoffs() {
const [handoffs, setHandoffs] = useState([])
const fetchHandoffs = useCallback(async () => {
// Try with joins first, fall back to plain select
const { data, error } = await supabase
.from('shift_handoffs')
.select(
'*, from_user:users!shift_handoffs_from_user_id_fkey(display_name), to_user:users!shift_handoffs_to_user_id_fkey(display_name)'
)
if (error) {
console.warn('[Handoffs] Join query failed, trying fallback:', error.message)
const { data: fallback } = await supabase
.from('shift_handoffs')
.select('*')
setHandoffs(fallback || [])
} else {
setHandoffs(data || [])
}
}, [])
useEffect(() => {
fetchHandoffs()
const channel = supabase
.channel('handoffs-realtime')
.on(
'postgres_changes',
{ event: '*', schema: 'public', table: 'shift_handoffs' },
() => fetchHandoffs()
)
.subscribe()
returgetHandoff function · javascript · L42-L49 (8 LOC)src/hooks/useHandoffs.js
function getHandoff(dateStr, shift, userId) {
return handoffs.find(
(h) =>
h.handoff_date === dateStr &&
h.shift === shift &&
h.from_user_id === userId
)
}getShiftHandoffs function · javascript · L52-L56 (5 LOC)src/hooks/useHandoffs.js
function getShiftHandoffs(dateStr, shift) {
return handoffs.filter(
(h) => h.handoff_date === dateStr && h.shift === shift
)
}createHandoff function · javascript · L59-L82 (24 LOC)src/hooks/useHandoffs.js
async function createHandoff(dateStr, shift, fromUserId, toUserId) {
const existing = handoffs.find(
(h) =>
h.handoff_date === dateStr &&
h.shift === shift &&
h.from_user_id === fromUserId &&
h.status === 'pending'
)
if (existing) return { error: 'A hand-off is already pending' }
const { data: insertData, error } = await supabase.from('shift_handoffs').insert({
handoff_date: dateStr,
shift,
from_user_id: fromUserId,
to_user_id: toUserId,
}).select()
if (error) {
console.error('[Handoffs] Error creating handoff:', error)
return { error: error.message }
}
await fetchHandoffs()
return { error: null }
}acceptHandoff function · javascript · L85-L122 (38 LOC)src/hooks/useHandoffs.js
async function acceptHandoff(handoffId) {
// Fetch the handoff details so we know which shift/date/user to reassign
const handoff = handoffs.find((h) => h.id === handoffId)
if (!handoff) {
console.error('[Handoffs] Handoff not found:', handoffId)
return { error: 'Handoff not found' }
}
const { error } = await supabase
.from('shift_handoffs')
.update({
status: 'accepted',
resolved_at: new Date().toISOString(),
})
.eq('id', handoffId)
.select()
if (error) {
console.error('[Handoffs] Error accepting:', error)
return { error: error.message }
}
// Reassign ALL tasks for this shift+date from the original user to the accepting user
const { error: reassignError } = await supabase
.from('tasks')
.update({ assigned_to: handoff.to_user_id })
.eq('task_date', handoff.handoff_date)
.eq('shift', handoff.shift)
.eq('assigned_to', handoff.from_user_id)
if (ruseScheduleNotes function · javascript · L4-L96 (93 LOC)src/hooks/useScheduleNotes.js
export default function useScheduleNotes(profileId) {
const [notes, setNotes] = useState([])
const [loading, setLoading] = useState(true)
const saveTimerRef = useRef(null)
const pendingSaveRef = useRef(null)
const fetchNotes = useCallback(async () => {
const { data, error } = await supabase
.from('schedule_notes')
.select('*, creator:created_by(display_name), updater:updated_by(display_name)')
.order('pinned', { ascending: false })
.order('updated_at', { ascending: false })
if (error) {
console.error('Error fetching notes:', error)
} else {
setNotes(data || [])
}
setLoading(false)
}, [])
useEffect(() => {
fetchNotes()
const channel = supabase
.channel('schedule-notes-realtime')
.on('postgres_changes', { event: '*', schema: 'public', table: 'schedule_notes' }, () =>
fetchNotes()
)
.subscribe()
return () => supabase.removeChannel(channel)
}, [fetchNotes])
async functcreateNote function · javascript · L37-L48 (12 LOC)src/hooks/useScheduleNotes.js
async function createNote() {
const { data, error } = await supabase
.from('schedule_notes')
.insert({ title: '', body: '', created_by: profileId, updated_by: profileId })
.select()
.single()
if (error) {
console.error('Error creating note:', error)
return null
}
return data
}Open data scored by Repobility · https://repobility.com
saveNote function · javascript · L50-L66 (17 LOC)src/hooks/useScheduleNotes.js
function saveNote(id, fields) {
// Cancel any pending save
if (saveTimerRef.current) clearTimeout(saveTimerRef.current)
pendingSaveRef.current = { id, fields }
return new Promise((resolve) => {
saveTimerRef.current = setTimeout(async () => {
const { error } = await supabase
.from('schedule_notes')
.update({ ...fields, updated_by: profileId, updated_at: new Date().toISOString() })
.eq('id', id)
pendingSaveRef.current = null
if (error) console.error('Error saving note:', error)
resolve(!error)
}, 500)
})
}flushSave function · javascript · L68-L79 (12 LOC)src/hooks/useScheduleNotes.js
async function flushSave() {
if (saveTimerRef.current) clearTimeout(saveTimerRef.current)
if (pendingSaveRef.current) {
const { id, fields } = pendingSaveRef.current
pendingSaveRef.current = null
const { error } = await supabase
.from('schedule_notes')
.update({ ...fields, updated_by: profileId, updated_at: new Date().toISOString() })
.eq('id', id)
if (error) console.error('Error flushing save:', error)
}
}deleteNote function · javascript · L81-L85 (5 LOC)src/hooks/useScheduleNotes.js
async function deleteNote(id) {
const { error } = await supabase.from('schedule_notes').delete().eq('id', id)
if (error) console.error('Error deleting note:', error)
return !error
}togglePin function · javascript · L87-L93 (7 LOC)src/hooks/useScheduleNotes.js
async function togglePin(id, currentPinned) {
const { error } = await supabase
.from('schedule_notes')
.update({ pinned: !currentPinned })
.eq('id', id)
if (error) console.error('Error toggling pin:', error)
}urlBase64ToUint8Array function · javascript · L5-L14 (10 LOC)src/lib/pushSubscription.js
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - (base64String.length % 4)) % 4)
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/')
const rawData = atob(base64)
const outputArray = new Uint8Array(rawData.length)
for (let i = 0; i < rawData.length; i++) {
outputArray[i] = rawData.charCodeAt(i)
}
return outputArray
}subscribeToPush function · javascript · L17-L78 (62 LOC)src/lib/pushSubscription.js
export async function subscribeToPush(userId) {
if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
console.log('[Push] Not supported in this browser')
return null
}
try {
const registration = await navigator.serviceWorker.ready
// Check for existing subscription
let subscription = await registration.pushManager.getSubscription()
// Unsubscribe old subscription if it exists (VAPID key may have changed)
if (subscription) {
try {
await subscription.unsubscribe()
console.log('[Push] Cleared old subscription')
} catch (e) {
console.warn('[Push] Failed to unsubscribe old:', e)
}
subscription = null
}
if (!subscription) {
// Request permission and subscribe
const permission = await Notification.requestPermission()
if (permission !== 'granted') {
console.log('[Push] Permission denied')
return null
}
subscription = await registration.pushsendPushAlert function · javascript · L81-L116 (36 LOC)src/lib/pushSubscription.js
export async function sendPushAlert(title, message) {
// Fetch all push subscriptions from Supabase
const { data: subs, error } = await supabase
.from('push_subscriptions')
.select('endpoint, p256dh, auth')
if (error || !subs || subs.length === 0) {
console.warn('[Push] No subscriptions found:', error)
return { sent: 0 }
}
// Format subscriptions for web-push
const subscriptions = subs.map((s) => ({
endpoint: s.endpoint,
keys: { p256dh: s.p256dh, auth: s.auth },
}))
// Call Netlify Function
const response = await fetch('/.netlify/functions/send-push', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title, message, subscriptions }),
})
const result = await response.json()
console.log('[Push] Send result:', result)
// Clean up expired subscriptions
if (result.expired && result.expired.length > 0) {
for (const endpoint of result.expired) {
await supabase.from('push_subsErrorBoundary class · javascript · L7-L40 (34 LOC)src/main.jsx
class ErrorBoundary extends React.Component {
constructor(props) {
super(props)
this.state = { hasError: false, error: null }
}
static getDerivedStateFromError(error) {
return { hasError: true, error }
}
componentDidCatch(error, info) {
console.error('[ErrorBoundary]', error, info)
}
render() {
if (this.state.hasError) {
return (
<div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', flexDirection: 'column', gap: 16, padding: 24, textAlign: 'center', background: '#0a0a0a' }}>
<img src="/logo.png" alt="Imperial Sporthorses" style={{ height: 80, width: 80, objectFit: 'contain', opacity: 0.8 }} />
<div style={{ color: '#fbbf24', fontSize: 16, fontWeight: 'bold' }}>Something went wrong</div>
<div style={{ color: '#a3a3a3', fontSize: 12, maxWidth: 280 }}>{this.state.error?.message || 'Unknown error'}</div>
<button
onClick={() => {
try Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
constructor method · javascript · L8-L11 (4 LOC)src/main.jsx
constructor(props) {
super(props)
this.state = { hasError: false, error: null }
}getDerivedStateFromError method · javascript · L12-L14 (3 LOC)src/main.jsx
static getDerivedStateFromError(error) {
return { hasError: true, error }
}componentDidCatch method · javascript · L15-L17 (3 LOC)src/main.jsx
componentDidCatch(error, info) {
console.error('[ErrorBoundary]', error, info)
}render method · javascript · L18-L39 (22 LOC)src/main.jsx
render() {
if (this.state.hasError) {
return (
<div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', flexDirection: 'column', gap: 16, padding: 24, textAlign: 'center', background: '#0a0a0a' }}>
<img src="/logo.png" alt="Imperial Sporthorses" style={{ height: 80, width: 80, objectFit: 'contain', opacity: 0.8 }} />
<div style={{ color: '#fbbf24', fontSize: 16, fontWeight: 'bold' }}>Something went wrong</div>
<div style={{ color: '#a3a3a3', fontSize: 12, maxWidth: 280 }}>{this.state.error?.message || 'Unknown error'}</div>
<button
onClick={() => {
try { localStorage.clear() } catch(e) {}
try { sessionStorage.clear() } catch(e) {}
window.location.href = window.location.origin
}}
style={{ background: '#f59e0b', color: '#000', border: 'none', padding: '10px 24px', borderRadius: 8, fontWeight: 'bold', fontSize: toggleUserRole function · javascript · L112-L116 (5 LOC)src/pages/AdminSettings.jsx
async function toggleUserRole(user) {
const newRole = user.role === 'Admin' ? 'Staff' : 'Admin'
await supabase.from('users').update({ role: newRole }).eq('id', user.id)
fetchAll()
}addLocation function · javascript · L119-L128 (10 LOC)src/pages/AdminSettings.jsx
async function addLocation(e) {
e.preventDefault()
if (!newLocation.name.trim()) return
await supabase.from('locations').insert({
name: newLocation.name.trim(),
type: newLocation.type,
})
setNewLocation({ name: '', type: 'Stall' })
fetchAll()
}deleteLocation function · javascript · L130-L134 (5 LOC)src/pages/AdminSettings.jsx
async function deleteLocation(id) {
if (!confirm('Delete this location?')) return
await supabase.from('locations').delete().eq('id', id)
fetchAll()
}syncMapLocations function · javascript · L141-L156 (16 LOC)src/pages/AdminSettings.jsx
async function syncMapLocations() {
const needed = [
...MAP_STALLS.map((name) => ({ name, type: 'Stall' })),
...MAP_PASTURES.map((name) => ({ name, type: 'Pasture' })),
]
const existing = locations.map((l) => l.name)
const toCreate = needed.filter((n) => !existing.includes(n.name))
if (toCreate.length === 0) return
const { error } = await supabase.from('locations').insert(toCreate)
if (error) {
console.error('Sync error:', error)
alert('Failed to sync: ' + error.message)
} else {
fetchAll()
}
}Repobility (the analyzer behind this table) · https://repobility.com
addHorse function · javascript · L159-L179 (21 LOC)src/pages/AdminSettings.jsx
async function addHorse(e) {
e.preventDefault()
if (!newHorse.name.trim()) return
await supabase.from('horses').insert({
name: newHorse.name.trim(),
owner_info: newHorse.owner_info || null,
home_stall: newHorse.home_stall || null,
assigned_pasture: newHorse.assigned_pasture || null,
current_location: newHorse.home_stall || null,
am_grain: newHorse.am_grain || null,
pm_grain: newHorse.pm_grain || null,
hay_type: newHorse.hay_type || null,
supplements: newHorse.supplements || null,
meds_notes: newHorse.meds_notes || null,
})
setNewHorse({
name: '', owner_info: '', home_stall: '', assigned_pasture: '',
am_grain: '', pm_grain: '', hay_type: '', supplements: '', meds_notes: '',
})
fetchAll()
}deleteHorse function · javascript · L181-L185 (5 LOC)src/pages/AdminSettings.jsx
async function deleteHorse(id) {
if (!confirm('Delete this horse?')) return
await supabase.from('horses').delete().eq('id', id)
fetchAll()
}addTemplate function · javascript · L188-L198 (11 LOC)src/pages/AdminSettings.jsx
async function addTemplate(e) {
e.preventDefault()
if (!newTemplate.title.trim()) return
await supabase.from('task_templates').insert({
title: newTemplate.title.trim(),
shift: newTemplate.shift,
sort_order: newTemplate.sort_order,
})
setNewTemplate({ title: '', shift: 'AM', sort_order: 0 })
fetchAll()
}deleteTemplate function · javascript · L200-L203 (4 LOC)src/pages/AdminSettings.jsx
async function deleteTemplate(id) {
await supabase.from('task_templates').delete().eq('id', id)
fetchAll()
}addScheduleEntry function · javascript · L206-L216 (11 LOC)src/pages/AdminSettings.jsx
async function addScheduleEntry(e) {
e.preventDefault()
if (!newScheduleEntry.user_id) return
await supabase.from('weekly_schedule').insert({
user_id: newScheduleEntry.user_id,
day_of_week: parseInt(newScheduleEntry.day_of_week),
shift: newScheduleEntry.shift,
})
setNewScheduleEntry({ user_id: '', day_of_week: 0, shift: 'AM' })
fetchAll()
}deleteScheduleEntry function · javascript · L218-L221 (4 LOC)src/pages/AdminSettings.jsx
async function deleteScheduleEntry(id) {
await supabase.from('weekly_schedule').delete().eq('id', id)
fetchAll()
}addBoardTask function · javascript · L224-L243 (20 LOC)src/pages/AdminSettings.jsx
async function addBoardTask(e) {
e.preventDefault()
if (!newBoardTask.title.trim()) return
const { format } = await import('date-fns')
const { error } = await supabase.from('tasks').insert({
title: newBoardTask.title.trim(),
shift: null,
status: 'Pending',
task_date: format(new Date(), 'yyyy-MM-dd'),
assigned_to: newBoardTask.assigned_to || null,
sort_order: 0,
})
if (error) {
console.error('[Admin] Failed to create board task:', error)
alert('Failed to create task: ' + error.message)
return
}
setNewBoardTask({ title: '', assigned_to: '' })
fetchAll()
}deleteBoardTask function · javascript · L245-L249 (5 LOC)src/pages/AdminSettings.jsx
async function deleteBoardTask(id) {
if (!confirm('Delete this board task?')) return
await supabase.from('tasks').delete().eq('id', id)
fetchAll()
}Provenance: Repobility (https://repobility.com) — every score reproducible from /scan/
Chat function · javascript · L8-L261 (254 LOC)src/pages/Chat.jsx
export default function Chat() {
const { profile, isAdmin } = useAuth()
const [messages, setMessages] = useState([])
const [newMessage, setNewMessage] = useState('')
const [loading, setLoading] = useState(true)
const [sending, setSending] = useState(false)
const messagesEndRef = useRef(null)
const inputRef = useRef(null)
const fetchMessages = useCallback(async () => {
try {
const { data, error } = await supabase
.from('messages')
.select('*, sender:users!messages_user_id_fkey(display_name)')
.order('created_at', { ascending: true })
.limit(200)
if (error) {
console.error('Error fetching messages:', error)
const { data: fallback } = await supabase
.from('messages')
.select('*')
.order('created_at', { ascending: true })
.limit(200)
setMessages(fallback || [])
} else {
setMessages(data || [])
}
} catch (err) {
console.error('fetchMsendNotification function · javascript · L52-L66 (15 LOC)src/pages/Chat.jsx
function sendNotification(title, body, tag) {
if ('Notification' in window && Notification.permission === 'granted') {
if ('serviceWorker' in navigator && navigator.serviceWorker.controller) {
navigator.serviceWorker.controller.postMessage({
type: 'SHOW_NOTIFICATION',
title,
body,
tag,
})
} else {
// Fallback to basic Notification API
new Notification(title, { body, icon: '/notif-icon.png', tag })
}
}
}scrollToBottom function · javascript · L97-L99 (3 LOC)src/pages/Chat.jsx
function scrollToBottom() {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
}page 1 / 3next ›