Function bodies 104 total
handleSend function · javascript · L101-L128 (28 LOC)src/pages/Chat.jsx
async function handleSend(e, isAlert = false) {
e.preventDefault()
const trimmed = newMessage.trim()
if (!trimmed || !profile) return
if (isAlert && !confirm('Send this as an ALERT to everyone?')) return
setSending(true)
setNewMessage('')
const row = { user_id: profile.id, content: trimmed }
if (isAlert) row.is_alert = true
const { error } = await supabase.from('messages').insert(row)
if (error) {
console.error('Error sending message:', error)
alert('Failed to send: ' + error.message)
setNewMessage(trimmed)
} else if (isAlert) {
// Send real push notification to all devices via Netlify Function
sendPushAlert('🚨 Barn Alert', trimmed).catch((err) =>
console.error('Push send error:', err)
)
}
setSending(false)
inputRef.current?.focus()
}formatTimestamp function · javascript · L130-L135 (6 LOC)src/pages/Chat.jsx
function formatTimestamp(dateStr) {
const date = new Date(dateStr)
if (isToday(date)) return format(date, 'h:mm a')
if (isYesterday(date)) return 'Yesterday ' + format(date, 'h:mm a')
return format(date, 'MMM d, h:mm a')
}getSenderName function · javascript · L137-L140 (4 LOC)src/pages/Chat.jsx
function getSenderName(msg) {
if (msg.sender?.display_name) return msg.sender.display_name
return 'Unknown'
}CompleteProfilePage function · javascript · L5-L119 (115 LOC)src/pages/CompleteProfilePage.jsx
export default function CompleteProfilePage() {
const { session, updateDisplayName, profile } = useAuth()
const [name, setName] = useState('')
const [error, setError] = useState('')
const [saving, setSaving] = useState(false)
const [retrying, setRetrying] = useState(true)
// Give auth context a few seconds to finish loading profile
useEffect(() => {
const timer = setTimeout(() => setRetrying(false), 3000)
return () => clearTimeout(timer)
}, [])
// If profile loads with a display_name, stop retrying immediately
useEffect(() => {
if (profile?.display_name) setRetrying(false)
}, [profile])
async function handleSubmit(e) {
e.preventDefault()
const trimmed = name.trim()
if (!trimmed) {
setError('Please enter your display name.')
return
}
if (trimmed.length < 2) {
setError('Name must be at least 2 characters.')
return
}
setSaving(true)
try {
const timeout = new Promise((_, reject) =>
handleSubmit function · javascript · L23-L48 (26 LOC)src/pages/CompleteProfilePage.jsx
async function handleSubmit(e) {
e.preventDefault()
const trimmed = name.trim()
if (!trimmed) {
setError('Please enter your display name.')
return
}
if (trimmed.length < 2) {
setError('Name must be at least 2 characters.')
return
}
setSaving(true)
try {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Request timed out')), 10000)
)
await Promise.race([updateDisplayName(trimmed), timeout])
} catch (err) {
setError(err.message === 'Request timed out'
? 'Request timed out. Check your connection and try again.'
: 'Failed to save. Please try again.')
console.error('[CompleteProfile] Error:', err)
} finally {
setSaving(false)
}
}generateMainBarnStalls function · javascript · L7-L17 (11 LOC)src/pages/FacilityMap.jsx
function generateMainBarnStalls() {
const stalls = []
const bx = 200, by = 340, sw = 27, sh = 35, gap = 2
for (let i = 0; i < 8; i++) {
stalls.push({ id: `mb-b${i+1}`, label: `${i+1}`, type: 'stall', x: bx+10+i*(sw+gap), y: by+170-sh-6, w: sw, h: sh, parent: 'main-barn', dbName: `Stall ${i+1}` })
}
for (let i = 0; i < 4; i++) {
stalls.push({ id: `mb-t${i+1}`, label: `${i+9}`, type: 'stall', x: bx+10+i*(sw+gap), y: by+22, w: sw, h: sh, parent: 'main-barn', dbName: `Stall ${i+9}` })
}
return stalls
}generateSixStallBarnStalls function · javascript · L20-L30 (11 LOC)src/pages/FacilityMap.jsx
function generateSixStallBarnStalls() {
const stalls = []
const bx = 10, by = 170, sw = 40, sh = 35, gap = 3
for (let i = 0; i < 3; i++) {
stalls.push({ id: `sb-t${i+1}`, label: `${i+13}`, type: 'stall', x: bx+10+i*(sw+gap), y: by+22, w: sw, h: sh, parent: '6stall', dbName: `Stall ${i+13}` })
}
for (let i = 0; i < 3; i++) {
stalls.push({ id: `sb-b${i+1}`, label: `${i+16}`, type: 'stall', x: bx+10+i*(sw+gap), y: by+130-sh-6, w: sw, h: sh, parent: '6stall', dbName: `Stall ${i+16}` })
}
return stalls
}Repobility · MCP-ready · https://repobility.com
generateFourStallBarnStalls function · javascript · L33-L43 (11 LOC)src/pages/FacilityMap.jsx
function generateFourStallBarnStalls() {
const stalls = []
const bx = 310, by = 130, sw = 38, sh = 32, gap = 3
for (let i = 0; i < 2; i++) {
stalls.push({ id: `fb-t${i+1}`, label: `${i+19}`, type: 'stall', x: bx+10+i*(sw+gap), y: by+22, w: sw, h: sh, parent: '4stall', dbName: `Stall ${i+19}` })
}
for (let i = 0; i < 2; i++) {
stalls.push({ id: `fb-b${i+1}`, label: `${i+21}`, type: 'stall', x: bx+10+i*(sw+gap), y: by+110-sh-6, w: sw, h: sh, parent: '4stall', dbName: `Stall ${i+21}` })
}
return stalls
}resetZoom function · javascript · L104-L106 (3 LOC)src/pages/FacilityMap.jsx
function resetZoom() {
setViewBox({ x: 0, y: 0, w: 1000, h: 700 })
}zoomBy function · javascript · L108-L120 (13 LOC)src/pages/FacilityMap.jsx
function zoomBy(factor) {
setViewBox((v) => {
const newW = Math.min(MAX_ZOOM_W, Math.max(MIN_ZOOM_W, v.w * factor))
const newH = newW * 0.7
const cx = v.x + v.w / 2
const cy = v.y + v.h / 2
return {
x: Math.max(-300, Math.min(1000 - newW + 300, cx - newW / 2)),
y: Math.max(-300, Math.min(700 - newH + 300, cy - newH / 2)),
w: newW, h: newH,
}
})
}getTouchDist function · javascript · L122-L124 (3 LOC)src/pages/FacilityMap.jsx
function getTouchDist(t1, t2) {
return Math.hypot(t1.clientX - t2.clientX, t1.clientY - t2.clientY)
}getTouchCenter function · javascript · L126-L128 (3 LOC)src/pages/FacilityMap.jsx
function getTouchCenter(t1, t2) {
return { x: (t1.clientX + t2.clientX) / 2, y: (t1.clientY + t2.clientY) / 2 }
}handleTouchStart function · javascript · L130-L140 (11 LOC)src/pages/FacilityMap.jsx
function handleTouchStart(e) {
if (editMode) return
if (e.touches.length === 2) {
e.preventDefault()
lastTouchDist.current = getTouchDist(e.touches[0], e.touches[1])
lastTouchCenter.current = getTouchCenter(e.touches[0], e.touches[1])
} else if (e.touches.length === 1) {
setIsPanning(true)
setPanStart({ x: e.touches[0].clientX, y: e.touches[0].clientY, vx: viewBox.x, vy: viewBox.y })
}
}handleTouchMove function · javascript · L142-L174 (33 LOC)src/pages/FacilityMap.jsx
function handleTouchMove(e) {
if (editMode) return
if (e.touches.length === 2 && lastTouchDist.current) {
e.preventDefault()
const newDist = getTouchDist(e.touches[0], e.touches[1])
const scale = lastTouchDist.current / newDist
lastTouchDist.current = newDist
setViewBox((v) => {
const newW = Math.min(MAX_ZOOM_W, Math.max(MIN_ZOOM_W, v.w * scale))
const newH = newW * 0.7
const cx = v.x + v.w / 2
const cy = v.y + v.h / 2
return {
x: Math.max(-200, Math.min(1000 - newW + 200, cx - newW / 2)),
y: Math.max(-200, Math.min(700 - newH + 200, cy - newH / 2)),
w: newW, h: newH,
}
})
} else if (e.touches.length === 1 && isPanning && panStart) {
const container = mapContainerRef.current
if (!container) return
const rect = container.getBoundingClientRect()
const scaleX = viewBox.w / rect.width
const scaleY = viewBox.h / rect.height
consthandleTouchEnd function · javascript · L176-L185 (10 LOC)src/pages/FacilityMap.jsx
function handleTouchEnd(e) {
if (e.touches.length < 2) {
lastTouchDist.current = null
lastTouchCenter.current = null
}
if (e.touches.length === 0) {
setIsPanning(false)
setPanStart(null)
}
}Hi, dataset curator — please cite Repobility (https://repobility.com) when reusing this data.
toSvg function · javascript · L221-L228 (8 LOC)src/pages/FacilityMap.jsx
function toSvg(cx, cy) {
const svg = svgRef.current
if (!svg) return { x: 0, y: 0 }
const pt = svg.createSVGPoint()
pt.x = cx; pt.y = cy
const p = pt.matrixTransform(svg.getScreenCTM().inverse())
return { x: p.x, y: p.y }
}onAreaPointerDown function · javascript · L231-L237 (7 LOC)src/pages/FacilityMap.jsx
function onAreaPointerDown(e, idx) {
if (!editMode) return
e.stopPropagation(); e.preventDefault()
const { x, y } = toSvg(e.clientX, e.clientY)
const a = mapAreas[idx]
setDragInfo({ idx, ox: x - a.x, oy: y - a.y, mode: 'move' })
}onHandleDown function · javascript · L240-L245 (6 LOC)src/pages/FacilityMap.jsx
function onHandleDown(e, idx, handle) {
if (!editMode) return
e.stopPropagation(); e.preventDefault()
const { x, y } = toSvg(e.clientX, e.clientY)
setDragInfo({ idx, sx: x, sy: y, mode: 'resize', handle, orig: { ...mapAreas[idx] } })
}onPointerMove function · javascript · L247-L267 (21 LOC)src/pages/FacilityMap.jsx
function onPointerMove(e) {
if (!dragInfo) return
const { x, y } = toSvg(e.clientX, e.clientY)
setMapAreas((prev) => {
const next = [...prev]
const a = { ...next[dragInfo.idx] }
if (dragInfo.mode === 'move') {
a.x = Math.round(x - dragInfo.ox)
a.y = Math.round(y - dragInfo.oy)
} else {
const dx = x - dragInfo.sx, dy = y - dragInfo.sy, o = dragInfo.orig, h = dragInfo.handle
if (h.includes('e')) a.w = Math.max(20, Math.round(o.w + dx))
if (h.includes('w')) { a.x = Math.round(o.x + dx); a.w = Math.max(20, Math.round(o.w - dx)) }
if (h.includes('s')) a.h = Math.max(15, Math.round(o.h + dy))
if (h.includes('n')) { a.y = Math.round(o.y + dy); a.h = Math.max(15, Math.round(o.h - dy)) }
}
next[dragInfo.idx] = a
return next
})
setHasChanges(true)
}saveLayout function · javascript · L272-L285 (14 LOC)src/pages/FacilityMap.jsx
async function saveLayout() {
const rows = mapAreas.map((a, i) => ({
area_key: a.id, label: a.label, area_type: a.type,
x: a.x, y: a.y, w: a.w, h: a.h,
location_id: a.locationId || null, parent_key: a.parent || null, sort_order: i,
}))
const { error } = await supabase.from('map_areas').upsert(rows, { onConflict: 'area_key' })
if (error) {
console.error('Save layout error:', error)
alert('Failed to save: ' + error.message)
} else {
setHasChanges(false)
}
}findDbLocation function · javascript · L288-L306 (19 LOC)src/pages/FacilityMap.jsx
function findDbLocation(area) {
if (area.locationId) return locations.find((l) => l.id === area.locationId)
if (area.dbName) {
// Exact match first
let loc = locations.find((l) => l.name === area.dbName)
if (loc) return loc
// Case-insensitive match
loc = locations.find((l) => l.name.toLowerCase() === area.dbName.toLowerCase())
if (loc) return loc
// Partial match (e.g. "Stall 1" matches "Stall 1" or "stall 1")
loc = locations.find((l) => l.name.toLowerCase().includes(area.dbName.toLowerCase()) || area.dbName.toLowerCase().includes(l.name.toLowerCase()))
return loc || null
}
// Try matching by label as last resort
if (area.label) {
return locations.find((l) => l.name.toLowerCase() === area.label.toLowerCase()) || null
}
return null
}getHorsesForArea function · javascript · L308-L317 (10 LOC)src/pages/FacilityMap.jsx
function getHorsesForArea(area) {
const loc = findDbLocation(area)
if (!loc) return []
// Show horse if it's currently here, OR this is their home stall, OR their assigned pasture
return horses.filter((h) =>
h.current_location === loc.id ||
h.home_stall === loc.id ||
h.assigned_pasture === loc.id
)
}moveHorse function · javascript · L319-L326 (8 LOC)src/pages/FacilityMap.jsx
async function moveHorse(horse, targetLocation) {
if (targetLocation.type === 'Pasture' && horse.assigned_pasture && horse.assigned_pasture !== targetLocation.id) {
const ap = locations.find((l) => l.id === horse.assigned_pasture)
setWarningModal({ horse, targetLocation, assignedPastureName: ap?.name || 'Unknown' })
return
}
await confirmMove(horse, targetLocation)
}Repobility analyzer · published findings · https://repobility.com
confirmMove function · javascript · L328-L332 (5 LOC)src/pages/FacilityMap.jsx
async function confirmMove(horse, targetLocation) {
const { error } = await supabase.from('horses').update({ current_location: targetLocation.id }).eq('id', horse.id)
if (error) console.error('Move error:', error)
setDraggingHorse(null); setWarningModal(null)
}handleAreaTap function · javascript · L335-L349 (15 LOC)src/pages/FacilityMap.jsx
function handleAreaTap(area) {
if (editMode) {
// In edit mode, select for resize handles only (no popup panel)
setSelectedArea(selectedArea?.id === area.id ? null : area)
return
}
if (draggingHorse) {
const loc = findDbLocation(area)
if (loc && isAdmin) moveHorse(draggingHorse, loc)
return
}
// Pastures always show horse names inline, no tap-to-expand
if (area.type === 'pasture') return
setSelectedArea(selectedArea?.id === area.id ? null : area)
}handleHorseTap function · javascript · L351-L354 (4 LOC)src/pages/FacilityMap.jsx
function handleHorseTap(e, horse) {
e.stopPropagation()
if (isAdmin && !editMode) setDraggingHorse(horse)
}updateLabel function · javascript · L357-L360 (4 LOC)src/pages/FacilityMap.jsx
function updateLabel(val) {
setMapAreas((p) => p.map((a) => a.id === selectedArea.id ? { ...a, label: val } : a))
setSelectedArea((p) => ({ ...p, label: val })); setEditLabel(val); setHasChanges(true)
}linkLocation function · javascript · L363-L366 (4 LOC)src/pages/FacilityMap.jsx
function linkLocation(locId) {
setMapAreas((p) => p.map((a) => a.id === selectedArea.id ? { ...a, locationId: locId || null } : a))
setSelectedArea((p) => ({ ...p, locationId: locId || null })); setEditLocationId(locId); setHasChanges(true)
}openHorseEdit function · javascript · L48-L57 (10 LOC)src/pages/FeedRoom.jsx
function openHorseEdit(horse) {
setEditForm({
am_grain: horse.am_grain || '',
pm_grain: horse.pm_grain || '',
hay_type: horse.hay_type || '',
supplements: horse.supplements || '',
meds_notes: horse.meds_notes || '',
})
setEditingHorse(horse)
}saveHorseEdit function · javascript · L59-L80 (22 LOC)src/pages/FeedRoom.jsx
async function saveHorseEdit(e) {
e.preventDefault()
if (!editingHorse) return
setSaving(true)
const { error } = await supabase
.from('horses')
.update({
am_grain: editForm.am_grain.trim() || null,
pm_grain: editForm.pm_grain.trim() || null,
hay_type: editForm.hay_type.trim() || null,
supplements: editForm.supplements.trim() || null,
meds_notes: editForm.meds_notes.trim() || null,
})
.eq('id', editingHorse.id)
setSaving(false)
if (error) {
console.error('Error updating horse:', error)
return
}
setEditingHorse(null)
fetchData()
}getExpirationStyle function · javascript · L115-L122 (8 LOC)src/pages/FeedRoom.jsx
function getExpirationStyle(dateStr) {
if (!dateStr) return { class: 'text-neutral-500', label: '' }
const days = differenceInDays(parseISO(dateStr), new Date())
if (days < 0) return { class: 'bg-red-900/40 text-red-400', label: 'Expired' }
if (days <= 14)
return { class: 'bg-yellow-900/40 text-yellow-400', label: `${days}d left` }
return { class: 'bg-green-900/40 text-green-400', label: `${days}d left` }
}Generated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
getSvgPoint function · javascript · L125-L133 (9 LOC)src/pages/FeedRoom.jsx
function getSvgPoint(e) {
const svg = bowlSvgRef.current
if (!svg) return { x: 0, y: 0 }
const pt = svg.createSVGPoint()
pt.x = e.clientX; pt.y = e.clientY
const ctm = svg.getScreenCTM().inverse()
const svgP = pt.matrixTransform(ctm)
return { x: svgP.x, y: svgP.y }
}onBowlPointerDown function · javascript · L135-L143 (9 LOC)src/pages/FeedRoom.jsx
function onBowlPointerDown(e, bowlId) {
if (!bowlEditMode) return
e.preventDefault()
e.stopPropagation()
const pt = getSvgPoint(e)
const bowl = bowls.find(b => b.id === bowlId)
if (!bowl) return
setDraggingBowl({ id: bowlId, offsetX: pt.x - bowl.x, offsetY: pt.y - bowl.y })
}onBowlPointerMove function · javascript · L145-L153 (9 LOC)src/pages/FeedRoom.jsx
function onBowlPointerMove(e) {
if (!draggingBowl) return
const pt = getSvgPoint(e)
setBowls(prev => prev.map(b =>
b.id === draggingBowl.id
? { ...b, x: Math.round(pt.x - draggingBowl.offsetX), y: Math.round(pt.y - draggingBowl.offsetY) }
: b
))
}onBowlPointerUp function · javascript · L155-L157 (3 LOC)src/pages/FeedRoom.jsx
function onBowlPointerUp() {
setDraggingBowl(null)
}saveBowlPositions function · javascript · L159-L164 (6 LOC)src/pages/FeedRoom.jsx
async function saveBowlPositions() {
for (const b of bowls) {
await supabase.from('feed_bowls').update({ x: b.x, y: b.y }).eq('id', b.id)
}
setBowlEditMode(false)
}handleAddDelivery function · javascript · L166-L189 (24 LOC)src/pages/FeedRoom.jsx
async function handleAddDelivery(e) {
e.preventDefault()
if (!newItem.feed_name.trim()) return
const { error } = await supabase.from('feed_inventory').insert({
feed_name: newItem.feed_name.trim(),
quantity: newItem.quantity.trim() || null,
delivery_date: newItem.delivery_date || null,
expiration_date: newItem.expiration_date || null,
})
if (error) {
console.error('Error adding delivery:', error)
return
}
setNewItem({
feed_name: '',
quantity: '',
delivery_date: format(new Date(), 'yyyy-MM-dd'),
expiration_date: '',
})
setShowAddModal(false)
}LoginPage function · javascript · L3-L43 (41 LOC)src/pages/LoginPage.jsx
export default function LoginPage() {
const { signInWithGoogle } = useAuth()
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-sm text-center">
<div className="flex justify-center mb-6">
<img src="/logo.png" alt="Imperial Sporthorses" className="h-28 w-28 object-contain" />
</div>
<h1 className="text-2xl font-bold text-amber-400 mb-1">Imperial Sporthorses</h1>
<p className="text-neutral-400 text-sm mb-8">
Barn management for Katherine Palmer
</p>
<button
onClick={signInWithGoogle}
className="w-full flex items-center justify-center gap-3 bg-neutral-800 border border-neutral-700 rounded-xl px-4 py-3 text-neutral-200 font-medium hover:bg-neutral-700 active:bg-neutral-600 transition shadow-sm"
>
<svg className="w-5 h-5" viewScheduleNotes function · javascript · L8-L138 (131 LOC)src/pages/ScheduleNotes.jsx
export default function ScheduleNotes() {
const { profile } = useAuth()
const { notes, loading, createNote, saveNote, flushSave, deleteNote, togglePin } =
useScheduleNotes(profile?.id)
const [selectedNote, setSelectedNote] = useState(null)
const [search, setSearch] = useState('')
async function handleCreate() {
const note = await createNote()
if (note) setSelectedNote(note)
}
async function handleDelete(id) {
const ok = await deleteNote(id)
if (ok) setSelectedNote(null)
}
async function handleBack() {
await flushSave()
setSelectedNote(null)
}
// Filter notes by search
const filtered = search.trim()
? notes.filter(
(n) =>
n.title.toLowerCase().includes(search.toLowerCase()) ||
n.body.toLowerCase().includes(search.toLowerCase())
)
: notes
const pinned = filtered.filter((n) => n.pinned)
const unpinned = filtered.filter((n) => !n.pinned)
if (loading) {
return (
<div classNameRepobility · MCP-ready · https://repobility.com
handleCreate function · javascript · L15-L18 (4 LOC)src/pages/ScheduleNotes.jsx
async function handleCreate() {
const note = await createNote()
if (note) setSelectedNote(note)
}handleDelete function · javascript · L20-L23 (4 LOC)src/pages/ScheduleNotes.jsx
async function handleDelete(id) {
const ok = await deleteNote(id)
if (ok) setSelectedNote(null)
}handleBack function · javascript · L25-L28 (4 LOC)src/pages/ScheduleNotes.jsx
async function handleBack() {
await flushSave()
setSelectedNote(null)
}NoteCard function · javascript · L140-L162 (23 LOC)src/pages/ScheduleNotes.jsx
function NoteCard({ note, onTap }) {
const title = note.title || 'New Note'
const preview = note.body ? note.body.slice(0, 80).replace(/\n/g, ' ') : 'No additional text'
const timeAgo = note.updated_at
? formatDistanceToNow(new Date(note.updated_at), { addSuffix: true })
: ''
return (
<button
onClick={onTap}
className="w-full text-left bg-neutral-900 border border-neutral-800 rounded-xl p-3 active:scale-[0.98] transition-all"
>
<div className="flex items-start justify-between gap-2">
<div className="flex-1 min-w-0">
<p className="text-sm font-semibold text-neutral-100 truncate">{title}</p>
<p className="text-xs text-neutral-500 truncate mt-0.5">{preview}</p>
<p className="text-[10px] text-neutral-600 mt-1">{timeAgo}</p>
</div>
{note.pinned && <Pin className="w-3.5 h-3.5 text-amber-500/60 shrink-0 mt-0.5" />}
</div>
</button>
)
}TaskBoard function · javascript · L47-L384 (338 LOC)src/pages/TaskBoard.jsx
export default function TaskBoard() {
const { profile, isAdmin } = useAuth()
const [tasks, setTasks] = useState([])
const [users, setUsers] = useState([])
const [loading, setLoading] = useState(true)
const [editingTask, setEditingTask] = useState(null)
const [filterTab, setFilterTab] = useState('active')
const fetchTasks = useCallback(async () => {
try {
console.log('[TaskBoard] Fetching board tasks...')
// Try with join first
const { data, error } = await supabase
.from('tasks')
.select('*, assigned_user:users!tasks_assigned_to_fkey(display_name)')
.is('shift', null)
.order('sort_order', { ascending: true })
if (error) {
console.warn('[TaskBoard] Join query failed:', error.message)
// Fallback without join
const { data: fallbackData, error: fbErr } = await supabase
.from('tasks')
.select('*')
.is('shift', null)
.order('sort_order', { ascending: trhandleTaskTap function · javascript · L113-L139 (27 LOC)src/pages/TaskBoard.jsx
async function handleTaskTap(task) {
let updates
if (task.status === 'Pending') {
updates = {
status: 'In Progress',
assigned_to: task.assigned_to || profile.id,
}
} else if (task.status === 'In Progress') {
updates = {
status: 'Done',
completed_at: new Date().toISOString(),
}
} else if (task.status === 'Done') {
// Undo — revert back to In Progress
updates = {
status: 'In Progress',
completed_at: null,
}
}
const { error } = await supabase
.from('tasks')
.update(updates)
.eq('id', task.id)
if (error) console.error('Error updating task:', error)
}handleReassign function · javascript · L141-L149 (9 LOC)src/pages/TaskBoard.jsx
async function handleReassign(taskId, userId) {
const { error } = await supabase
.from('tasks')
.update({ assigned_to: userId || null })
.eq('id', taskId)
if (error) console.error('Error reassigning task:', error)
setEditingTask(null)
}handleDeleteTask function · javascript · L151-L154 (4 LOC)src/pages/TaskBoard.jsx
async function handleDeleteTask(taskId) {
const { error } = await supabase.from('tasks').delete().eq('id', taskId)
if (error) console.error('Error deleting task:', error)
}Hi, dataset curator — please cite Repobility (https://repobility.com) when reusing this data.
getAssigneeName function · javascript · L156-L163 (8 LOC)src/pages/TaskBoard.jsx
function getAssigneeName(task) {
if (task.assigned_user?.display_name) return task.assigned_user.display_name
if (task.assigned_to) {
const user = users.find((u) => u.id === task.assigned_to)
return user?.display_name || 'Unknown'
}
return null
}groupByDate function · javascript · L117-L135 (19 LOC)src/pages/WeeklyPlanner.jsx
function groupByDate(tasks) {
const grouped = {}
for (const day of allDays) {
const key = format(day, 'yyyy-MM-dd')
grouped[key] = []
}
for (const task of tasks) {
if (grouped[task.task_date]) {
// Deduplicate: skip if same title+shift already exists for this date
const isDupe = grouped[task.task_date].some(
(t) => t.title === task.title && t.shift === task.shift
)
if (!isDupe) {
grouped[task.task_date].push(task)
}
}
}
setWeekTasks(grouped)
}generateTasksForDate function · javascript · L228-L272 (45 LOC)src/pages/WeeklyPlanner.jsx
async function generateTasksForDate(dateStr) {
if (templates.length === 0) return false
if (generatingRef.current) return false
generatingRef.current = true
try {
// Check if tasks already exist for this date (re-check from DB to avoid races)
const { data: existing } = await supabase
.from('tasks')
.select('id')
.eq('task_date', dateStr)
.not('shift', 'is', null)
.limit(1)
if (existing && existing.length > 0) return false
// Look up scheduled staff for this day
const date = new Date(dateStr + 'T12:00:00')
const dayOfWeek = date.getDay()
const staffByShift = {}
for (const entry of schedule) {
if (entry.day_of_week === dayOfWeek) {
if (!staffByShift[entry.shift]) staffByShift[entry.shift] = []
staffByShift[entry.shift].push(entry.user_id)
}
}
const rows = templates.map((t) => ({
title: t.title,
shift: t.shift,