Overview
BoxApp’s scheduling system combines class timetables with WOD programming to create a unified view of your gym’s weekly operations.
Weekly Calendar
Monday-Saturday view with all scheduled sessions
WOD Assignment
Link programmed WODs to specific class sessions
Session Types
Color-coded sessions for easy identification
Member Feedback
Athletes can provide session feedback and RPE
Weekly Schedule View
The schedule displays Monday through Saturday (Schedule.tsx:200-207):
// Schedule.tsx:200-207
const weekDays = [
t('common.mon', { defaultValue: 'MON' }),
t('common.tue', { defaultValue: 'TUE' }),
t('common.wed', { defaultValue: 'WED' }),
t('common.thu', { defaultValue: 'THU' }),
t('common.fri', { defaultValue: 'FRI' }),
t('common.sat', { defaultValue: 'SAT' })
];
Week Navigation
Navigate between weeks using the date picker (Schedule.tsx:234-253):
- Previous/Next Week: Arrow buttons
- Today Button: Jump to current week
- Date Range Display: Shows Monday-Saturday dates
// Schedule.tsx:185-198
const getDatesOfWeek = () => {
const start = new Date(viewDate);
const day = start.getDay();
const diff = day === 0 ? -6 : 1 - day; // Adjust to Monday
start.setDate(start.getDate() + diff);
start.setHours(0, 0, 0, 0);
return Array.from({ length: 6 }, (_, i) => {
const d = new Date(start);
d.setDate(d.getDate() + i);
return d;
});
};
Programming Viewer
The Programming Viewer shows all WODs assigned to each day across all tracks (Schedule.tsx:266-429).
Opening the Viewer
Click “Program” button to open the assignment interface.
Day Selection
Select which day to view/edit (Schedule.tsx:286-316):
// Day selector within modal
{getDatesOfWeek().map((date, i) => {
const isSelected = selectedViewerDate.toDateString() === date.toDateString();
const isToday = date.toDateString() === new Date().toDateString();
const hasWod = wods.some(w => w.date?.split('T')[0] === localDateStr);
return (
<button
onClick={() => setSelectedViewerDate(date)}
className={isSelected ? "bg-primary" : "hover:bg-muted"}
>
<span>{weekDays[i]}</span>
<span>{date.getDate()}</span>
{hasWod && <div className="indicator" />}
</button>
);
})}
Days with programmed WODs show a small indicator dot for quick identification.
Track Cards
Each track displays its assigned WOD (Schedule.tsx:319-424):
- Shows assigned WOD title
- “View Workout” link to see full details
- Replace or unlink options
- Marked with primary color badge
- Beginner-focused programming
- Same assignment controls as CrossFit
- Green color coding
- Hypertrophy workouts
- Blue color coding
- Conditioning focus
- Orange color coding
Assigning WODs to Sessions
Automatic Matching
BoxApp automatically links WODs to sessions based on track and date (Schedule.tsx:209-223):
// Schedule.tsx:209-223
const getMatchedWod = (session: any, date: Date) => {
const localDateStr = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
return wods.find(w => {
if (!w.date || !w.track) return false;
const wodDateStr = w.date.split('T')[0];
const sessionTypeName = (session.session_types?.name || '').toLowerCase();
const trackName = w.track.toLowerCase();
return wodDateStr === localDateStr && (
sessionTypeName.includes(trackName) ||
(trackName === 'crossfit' && sessionTypeName.includes('functional'))
);
});
};
Manual Assignment
- Click “Assign Existing” on a track card
- Select from recent WODs
- Confirm assignment
Assigning a WOD overwrites any existing assignment for that track on that date.
Unlinking WODs
// Schedule.tsx:170-183
const handleUnlinkWod = async (wodId: string) => {
const { error } = await supabase
.from('wods')
.update({ date: undefined, track: undefined })
.eq('id', wodId);
if (!error) {
showNotification('success', 'PROGRAMMING UNLINKED');
fetchWods();
}
};
Session Details
Click any session card to view details (Schedule.tsx:550-617):
- Time: Start time (e.g., “6:00 AM”)
- Type: Session category with color badge
- Title: Custom session name
- Coach: Assigned coach name
Linked Programming
If a WOD matches the session (Schedule.tsx:574-593):
{(() => {
const matchedWod = getMatchedWod(session, date);
if (matchedWod) {
return (
<div className="wod-preview"
onClick={(e) => {
e.stopPropagation();
setSelectedWod(matchedWod);
}}
>
<p>Programmed</p>
<p>{matchedWod.title}</p>
</div>
);
}
return null;
})()}
Click the WOD preview to view full workout details without closing the session view.
Daily Programming Summary
Each day card shows a programming summary (Schedule.tsx:507-540):
const dayWods = wods.filter(w => w.date?.split('T')[0] === localDateStr);
if (dayWods.length > 0) {
return (
<div className="daily-programming">
<p>Daily Programming</p>
{dayWods.slice(0, 3).map((wod, idx) => (
<div key={idx} onClick={() => setSelectedWod(wod)}>
<span>{wod.track}</span>
<span>{wod.title}</span>
<ChevronRight />
</div>
))}
</div>
);
}
WOD Viewer Modal
The WOD viewer shows complete workout details (Schedule.tsx:645-763):
- Track badge
- Date
- WOD title
Workout Structure
If using structured blocks:
{selectedWod?.structure.map((block: any) => (
<div key={block.id}>
<p>{block.title}</p>
{block.items.map((item: any) => (
<div>
<p>{item.movementName}</p>
<p>{item.reps} {item.weight && `@ ${item.weight}`}</p>
</div>
))}
</div>
))}
Stimulus & Scaling
- Target stimulus description
- General scaling options
- Level-specific scaling (beginner/intermediate/advanced)
Modalities
Displays all modalities present in the workout as badges.
Mobile Day Selector
On mobile devices, a horizontal scrollable day picker appears (Schedule.tsx:435-473):
// Schedule.tsx:436-473
<div className="mobile-day-selector">
{getDatesOfWeek().map((date, i) => {
const isSelected = selectedDayIndex === i;
const isToday = date.toDateString() === new Date().toDateString();
const hasSessions = sessions.some(s =>
new Date(s.start_time).toDateString() === date.toDateString()
);
return (
<button
data-day-index={i}
onClick={() => setSelectedDayIndex(i)}
className={isSelected ? "selected" : ""}
>
<span>{weekDays[i]}</span>
<span>{date.getDate()}</span>
{hasSessions && <div className="indicator" />}
</button>
);
})}
</div>
The selected day scrolls into view automatically for better mobile UX.
Member Feedback
Athletes can submit session feedback (Schedule.tsx:602-615):
<Button
onClick={(e) => {
e.stopPropagation();
setIsFeedbackOpen(session.id);
}}
>
<MessageSquare className="h-2.5 w-2.5" />
Feedback
</Button>
Feedback typically includes:
- Effort level (RPE)
- Fatigue rating
- Satisfaction emoji
- Text notes
Best Practices
- Program ahead: Assign WODs at least 24 hours before class
- Match tracks carefully: Ensure WOD track matches session type
- Review daily summaries: Check each day has complete programming
- Use color coding: Train members to recognize session types by color
- Monitor feedback: Use athlete feedback to adjust programming intensity
- Coach assignments: Always assign a coach to ensure accountability