Function bodies 262 total
fetchPerformanceHistory function · typescript · L501-L532 (32 LOC)src/app/(app)/workout/[id]/page.tsx
async function fetchPerformanceHistory(userId: string, exerciseId: string): Promise<PerformanceSet[]> {
const pageSize = 1000;
let from = 0;
const history: PerformanceSet[] = [];
while (!cancelled) {
const to = from + pageSize - 1;
const { data, error } = await supabase
.from('sets')
.select('id, weight, reps, left_weight, left_reps, right_weight, right_reps, is_split_lr, set_number, workouts!inner(user_id, date)')
.eq('exercise_id', exerciseId)
.eq('workouts.user_id', userId)
.eq('is_completed', true)
.eq('is_warmup', false)
.order('workouts(date)', { ascending: false })
.order('set_number', { ascending: true })
.order('id', { ascending: true })
.range(from, to);
if (error || !data || data.length === 0) break;
for (const row of data as unknown as PreviousPerformanceRow[]) {
const normalized = normalizePreviousPerfoloadPrevious function · typescript · L534-L546 (13 LOC)src/app/(app)/workout/[id]/page.tsx
async function loadPrevious() {
const { data: { user } } = await supabase.auth.getUser();
if (!user) return;
const missingExercises = exercises.filter((ex) => previousPerformance[ex.exerciseId] === undefined);
for (const ex of missingExercises) {
const history = await fetchPerformanceHistory(user.id, ex.exerciseId);
if (cancelled) return;
setPreviousPerformance(ex.exerciseId, history);
prefillEmptySetsWithPreviousPerformance(ex.exerciseId, history);
}
}openTrainerTips function · typescript · L765-L780 (16 LOC)src/app/(app)/workout/[id]/page.tsx
async function openTrainerTips(exerciseId: string, exerciseName: string) {
setTipsModal({ exerciseId, exerciseName });
setLoadingTips(true);
setExerciseDetails(null);
const { data } = await supabase
.from('exercises')
.select('id, name, instructions, equipment, level, primary_muscles, secondary_muscles')
.eq('id', exerciseId)
.single();
if (data) {
setExerciseDetails(data as ExerciseDetails);
}
setLoadingTips(false);
}openHistory function · typescript · L782-L835 (54 LOC)src/app/(app)/workout/[id]/page.tsx
async function openHistory(exerciseId: string, exerciseName: string) {
setHistoryModal({ exerciseId, exerciseName });
setLoadingHistory(true);
setHistoryData([]);
const { data: { user } } = await supabase.auth.getUser();
if (!user) {
setLoadingHistory(false);
return;
}
const { data } = await supabase
.from('sets')
.select('weight, reps, left_weight, left_reps, right_weight, right_reps, is_split_lr, set_number, is_warmup, workouts!inner(id, date, user_id)')
.eq('exercise_id', exerciseId)
.eq('workouts.user_id', user.id)
.eq('is_completed', true)
.order('workouts(date)', { ascending: false });
if (data) {
const workoutMap = new Map<string, { date: string; sets: typeof data }>();
for (const row of data) {
const workout = row.workouts as unknown as { id: string; date: string };
const workoutId = workout.id;
const date = workout.date;
if (!workoutMap.has(workoutIdhandleDragStart function · typescript · L975-L981 (7 LOC)src/app/(app)/workout/[id]/page.tsx
function handleDragStart(index: number, clientY: number) {
setDragIndex(index);
setDragOverIndex(index);
touchStartY.current = clientY;
// Haptic feedback if available
if (navigator.vibrate) navigator.vibrate(30);
}handleDragMove function · typescript · L983-L995 (13 LOC)src/app/(app)/workout/[id]/page.tsx
function handleDragMove(clientY: number) {
if (dragIndex === null) return;
// Find which exercise card the touch is over
for (let i = 0; i < exerciseRefs.current.length; i++) {
const el = exerciseRefs.current[i];
if (!el) continue;
const rect = el.getBoundingClientRect();
if (clientY >= rect.top && clientY <= rect.bottom) {
setDragOverIndex(i);
return;
}
}
}handleDragEnd function · typescript · L997-L1008 (12 LOC)src/app/(app)/workout/[id]/page.tsx
function handleDragEnd() {
if (dragIndex !== null && dragOverIndex !== null && dragIndex !== dragOverIndex) {
store.moveExercise(dragIndex, dragOverIndex);
}
clearMouseDragListeners();
setDragIndex(null);
setDragOverIndex(null);
if (longPressTimer.current) {
clearTimeout(longPressTimer.current);
longPressTimer.current = null;
}
}Hi, dataset curator — please cite Repobility (https://repobility.com) when reusing this data.
cancelDrag function · typescript · L1010-L1018 (9 LOC)src/app/(app)/workout/[id]/page.tsx
function cancelDrag() {
clearMouseDragListeners();
setDragIndex(null);
setDragOverIndex(null);
if (longPressTimer.current) {
clearTimeout(longPressTimer.current);
longPressTimer.current = null;
}
}pickBestSet function · typescript · L67-L70 (4 LOC)src/app/(app)/workout/summary/[id]/page.tsx
function pickBestSet(
sets: Array<{ weight: number; reps: number }>,
isAssistanceExercise: boolean,
): { weight: number; reps: number } {normalizeLegacyWorkoutData function · typescript · L90-L110 (21 LOC)src/app/(app)/workout/summary/[id]/page.tsx
function normalizeLegacyWorkoutData(data: LegacyWorkoutData): WorkoutData {
return {
...data,
sets: data.sets.map((set) => {
const exerciseRef = Array.isArray(set.exercises) ? set.exercises[0] : set.exercises;
return {
exercise_id: set.exercise_id,
weight: set.weight,
reps: set.reps,
set_number: set.set_number,
is_warmup: set.is_warmup,
exercises: { name: exerciseRef?.name || 'Exercise' },
left_weight: null,
left_reps: null,
right_weight: null,
right_reps: null,
is_split_lr: false,
};
}),
};
}WorkoutSummaryPage function · typescript · L112-L118 (7 LOC)src/app/(app)/workout/summary/[id]/page.tsx
export default function WorkoutSummaryPage() {
return (
<Suspense fallback={<div className="flex items-center justify-center min-h-dvh text-muted-foreground">Loading...</div>}>
<WorkoutSummaryContent />
</Suspense>
);
}load function · typescript · L135-L157 (23 LOC)src/app/(app)/workout/summary/[id]/page.tsx
async function load() {
const { data, error } = await supabase
.from('workouts')
.select('id, name, start_time, end_time, sets(exercise_id, weight, reps, left_weight, left_reps, right_weight, right_reps, is_split_lr, set_number, is_warmup, exercises(name))')
.eq('id', params.id as string)
.single();
if (data && !error) {
setWorkout(data as unknown as WorkoutData);
return;
}
if (!isMissingSplitSetColumnsError(error)) return;
const { data: legacyData, error: legacyError } = await supabase
.from('workouts')
.select('id, name, start_time, end_time, sets(exercise_id, weight, reps, set_number, is_warmup, exercises(name))')
.eq('id', params.id as string)
.single();
if (legacyData && !legacyError) {
setWorkout(normalizeLegacyWorkoutData(legacyData as unknown as LegacyWorkoutData));
}
}generateNotes function · typescript · L166-L280 (115 LOC)src/app/(app)/workout/summary/[id]/page.tsx
async function generateNotes() {
setLoadingNotes(true);
const { data: { user } } = await supabase.auth.getUser();
if (!user) {
setLoadingNotes(false);
return;
}
// Get unique exercise IDs from this workout
const exerciseIds = [...new Set(workoutData.sets.map((s) => s.exercise_id))];
// Fetch previous performance for each exercise (excluding this workout)
const comparisons: {
exerciseName: string;
isAssistanceExercise: boolean;
currentBest: { weight: number; reps: number };
previousBest: { weight: number; reps: number } | null;
}[] = [];
for (const exId of exerciseIds) {
const exerciseSets = workoutData.sets.filter((s) => s.exercise_id === exId && !s.is_warmup);
if (exerciseSets.length === 0) continue;
const exerciseName = exerciseSets[0].exercises.name;
const isAssistanceExercise = isAssistanceExerciseName(exerciseName);
// Find bhandleSaveTemplate function · typescript · L285-L327 (43 LOC)src/app/(app)/workout/summary/[id]/page.tsx
async function handleSaveTemplate() {
if (!workout) return;
setSaving(true);
const name = workout.name || 'My Template';
const { data: { user } } = await supabase.auth.getUser();
if (!user) {
setSaving(false);
return;
}
const { data: template, error: tErr } = await supabase
.from('workout_templates')
.insert({ user_id: user.id, name })
.select()
.single();
if (tErr || !template) {
setSaving(false);
return;
}
// Get unique exercises in order of first appearance
const exerciseOrder: string[] = [];
for (const s of workout.sets) {
if (!exerciseOrder.includes(s.exercise_id)) {
exerciseOrder.push(s.exercise_id);
}
}
const templateExercises = exerciseOrder.map((exId, idx) => ({
template_id: template.id,
exercise_id: exId,
order_index: idx,
default_sets: workout.sets.filter((s) => s.exercise_id === exId && !s.is_warmup).length || 3,
}))handleUpdateTemplate function · typescript · L329-L365 (37 LOC)src/app/(app)/workout/summary/[id]/page.tsx
async function handleUpdateTemplate() {
if (!workout || !templateId) return;
setSaving(true);
// Delete existing template exercises
await supabase
.from('template_exercises')
.delete()
.eq('template_id', templateId);
// Get unique exercises in order of first appearance
const exerciseOrder: string[] = [];
for (const s of workout.sets) {
if (!exerciseOrder.includes(s.exercise_id)) {
exerciseOrder.push(s.exercise_id);
}
}
// Insert new template exercises
const templateExercises = exerciseOrder.map((exId, idx) => ({
template_id: templateId,
exercise_id: exId,
order_index: idx,
default_sets: workout.sets.filter((s) => s.exercise_id === exId && !s.is_warmup).length || 3,
}));
await supabase.from('template_exercises').insert(templateExercises);
// Update template updated_at
await supabase
.from('workout_templates')
.update({ updated_at: new Date().toISOStrinRepobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
handleDiscardWorkout function · typescript · L367-L376 (10 LOC)src/app/(app)/workout/summary/[id]/page.tsx
async function handleDiscardWorkout() {
if (!workout) return;
if (!window.confirm('Discard this workout? It will be permanently deleted.')) return;
setDiscarding(true);
// Delete sets first (foreign key), then the workout
await supabase.from('sets').delete().eq('workout_id', workout.id);
await supabase.from('workouts').delete().eq('id', workout.id);
router.push('/dashboard');
}AuthLayout function · typescript · L5-L20 (16 LOC)src/app/(auth)/layout.tsx
export default function AuthLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<DesktopMobileFrame>
<div className="relative flex min-h-dvh items-center justify-center overflow-hidden bg-background px-4 py-10 lg:min-h-full">
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_top,_color-mix(in_srgb,var(--primary)_16%,transparent)_0%,transparent_55%)]" />
<div className="relative w-full max-w-md">
{children}
</div>
</div>
</DesktopMobileFrame>
);
}LoginPage function · typescript · L20-L180 (161 LOC)src/app/(auth)/login/page.tsx
export default function LoginPage() {
const router = useRouter();
const [formData, setFormData] = useState<LoginInput>({
email: '',
password: '',
});
const [errors, setErrors] = useState<Partial<Record<keyof LoginInput, string>>>({});
const [generalError, setGeneralError] = useState('');
const [loading, setLoading] = useState(false);
const [demoLoading, setDemoLoading] = useState(false);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFormData((prev) => ({ ...prev, [name]: value }));
if (errors[name as keyof LoginInput]) {
setErrors((prev) => ({ ...prev, [name]: undefined }));
}
if (generalError) setGeneralError('');
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setErrors({});
setGeneralError('');
const result = loginSchema.safeParse(formData);
if (!result.success) {
const fieldErrors: Partial<Record<keyof LoginInput, sRegisterPage function · typescript · L18-L130 (113 LOC)src/app/(auth)/register/page.tsx
export default function RegisterPage() {
const router = useRouter();
const [formData, setFormData] = useState<RegisterInput>({
email: '',
password: '',
});
const [errors, setErrors] = useState<Partial<Record<keyof RegisterInput, string>>>({});
const [generalError, setGeneralError] = useState('');
const [loading, setLoading] = useState(false);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFormData((prev) => ({ ...prev, [name]: value }));
if (errors[name as keyof RegisterInput]) {
setErrors((prev) => ({ ...prev, [name]: undefined }));
}
if (generalError) setGeneralError('');
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setErrors({});
setGeneralError('');
const result = registerSchema.safeParse(formData);
if (!result.success) {
const fieldErrors: Partial<Record<keyof RegisterInput, string>> = {};
for (const issue ofRootLayout function · typescript · L16-L29 (14 LOC)src/app/layout.tsx
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<head />
<body className="antialiased bg-background text-foreground">
{children}
</body>
</html>
);
}HomePage function · typescript · L3-L5 (3 LOC)src/app/page.tsx
export default function HomePage() {
redirect('/login');
}getTimeGroup function · typescript · L12-L28 (17 LOC)src/components/chat/ChatSidebar.tsx
function getTimeGroup(dateStr: string): string {
const date = new Date(dateStr);
const now = new Date();
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
const yesterday = new Date(today);
yesterday.setDate(yesterday.getDate() - 1);
const weekAgo = new Date(today);
weekAgo.setDate(weekAgo.getDate() - 7);
const monthAgo = new Date(today);
monthAgo.setDate(monthAgo.getDate() - 30);
if (date >= today) return 'Today';
if (date >= yesterday) return 'Yesterday';
if (date >= weekAgo) return 'Last 7 Days';
if (date >= monthAgo) return 'Last 30 Days';
return 'Older';
}ChatSidebar function · typescript · L32-L180 (149 LOC)src/components/chat/ChatSidebar.tsx
export default function ChatSidebar({ isOpen, onClose }: ChatSidebarProps) {
const conversations = useChatStore((s) => s.conversations);
const activeConversationId = useChatStore((s) => s.activeConversationId);
const createConversation = useChatStore((s) => s.createConversation);
const switchConversation = useChatStore((s) => s.switchConversation);
const deleteConversation = useChatStore((s) => s.deleteConversation);
// Lock body scroll when open
useEffect(() => {
if (isOpen) {
document.body.style.overflow = 'hidden';
}
return () => {
document.body.style.overflow = '';
};
}, [isOpen]);
// Escape key to close
const handleKeyDown = useCallback(
(e: KeyboardEvent) => {
if (e.key === 'Escape') onClose();
},
[onClose]
);
useEffect(() => {
if (isOpen) document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}, [isOpen, handleKeyDown]);
functioRepobility · severity-and-effort ranking · https://repobility.com
handleNewChat function · typescript · L62-L65 (4 LOC)src/components/chat/ChatSidebar.tsx
function handleNewChat() {
createConversation();
onClose();
}handleSelectConversation function · typescript · L67-L70 (4 LOC)src/components/chat/ChatSidebar.tsx
function handleSelectConversation(id: string) {
switchConversation(id);
onClose();
}handleDeleteConversation function · typescript · L72-L75 (4 LOC)src/components/chat/ChatSidebar.tsx
function handleDeleteConversation(e: React.MouseEvent, id: string) {
e.stopPropagation();
deleteConversation(id);
}FileUploadButton function · typescript · L13-L84 (72 LOC)src/components/chat/FileUploadButton.tsx
export default function FileUploadButton({
onFileContent,
accept = '.csv,.json,.txt',
disabled = false,
}: FileUploadButtonProps) {
const inputRef = useRef<HTMLInputElement>(null);
function handleClick() {
inputRef.current?.click();
}
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
const file = e.target.files?.[0];
if (!file) return;
// Check file size (max 500KB)
if (file.size > 500 * 1024) {
alert('File too large. Maximum size is 500KB.');
return;
}
const reader = new FileReader();
reader.onload = (event) => {
const content = event.target?.result as string;
onFileContent(content, file.name);
// Reset input so same file can be selected again
if (inputRef.current) {
inputRef.current.value = '';
}
};
reader.onerror = () => {
alert('Failed to read file. Please try again.');
};
reader.readAsText(file);
}
return (
<>
<Input
ref={inhandleClick function · typescript · L20-L22 (3 LOC)src/components/chat/FileUploadButton.tsx
function handleClick() {
inputRef.current?.click();
}handleChange function · typescript · L24-L47 (24 LOC)src/components/chat/FileUploadButton.tsx
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
const file = e.target.files?.[0];
if (!file) return;
// Check file size (max 500KB)
if (file.size > 500 * 1024) {
alert('File too large. Maximum size is 500KB.');
return;
}
const reader = new FileReader();
reader.onload = (event) => {
const content = event.target?.result as string;
onFileContent(content, file.name);
// Reset input so same file can be selected again
if (inputRef.current) {
inputRef.current.value = '';
}
};
reader.onerror = () => {
alert('Failed to read file. Please try again.');
};
reader.readAsText(file);
}normalizeWorkoutSet function · typescript · L89-L110 (22 LOC)src/components/history/HistoryOverlay.tsx
function normalizeWorkoutSet(row: WorkoutSetRow): WorkoutSet {
const exerciseRef = Array.isArray(row.exercises) ? row.exercises[0] : row.exercises;
return {
id: row.id,
exercise_id: row.exercise_id,
set_number: row.set_number,
weight: row.weight,
reps: row.reps,
time: row.time,
distance: row.distance ?? null,
is_warmup: !!row.is_warmup,
is_completed: row.is_completed !== false,
is_split_lr: !!row.is_split_lr,
left_weight: row.left_weight ?? null,
left_reps: row.left_reps ?? null,
right_weight: row.right_weight ?? null,
right_reps: row.right_reps ?? null,
exercises: {
name: exerciseRef?.name || 'Exercise',
},
};
}normalizeWorkoutRows function · typescript · L112-L121 (10 LOC)src/components/history/HistoryOverlay.tsx
function normalizeWorkoutRows(rows: HistoryWorkoutRow[]): HistoryWorkout[] {
return rows.map((row) => ({
id: row.id,
name: row.name,
date: row.date,
start_time: row.start_time,
end_time: row.end_time,
sets: (row.sets ?? []).map((set) => normalizeWorkoutSet(set)),
}));
}Want this analysis on your repo? https://repobility.com/scan/
setInitialMonthFromWorkouts function · typescript · L154-L161 (8 LOC)src/components/history/HistoryOverlay.tsx
function setInitialMonthFromWorkouts(rows: HistoryWorkout[]) {
if (monthInitializedRef.current || rows.length === 0) return;
const latestDate = new Date(rows[0].date);
if (!Number.isNaN(latestDate.getTime())) {
setCurrentMonth(new Date(latestDate.getFullYear(), latestDate.getMonth(), 1));
}
monthInitializedRef.current = true;
}load function · typescript · L244-L247 (4 LOC)src/components/history/HistoryOverlay.tsx
async function load() {
await loadWorkouts();
setLoading(false);
}makeLocalSetId function · typescript · L282-L284 (3 LOC)src/components/history/HistoryOverlay.tsx
function makeLocalSetId(exerciseId: string): string {
return `new-${exerciseId}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
}makeTemporaryExerciseId function · typescript · L286-L288 (3 LOC)src/components/history/HistoryOverlay.tsx
function makeTemporaryExerciseId(): string {
return `new-exercise-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
}addExerciseGroup function · typescript · L290-L313 (24 LOC)src/components/history/HistoryOverlay.tsx
function addExerciseGroup(initialName?: string) {
const promptedName = initialName ?? window.prompt('Exercise name');
const exerciseName = (promptedName || '').trim();
if (!exerciseName) return;
const exerciseId = makeTemporaryExerciseId();
const newDraft: EditSetDraft = {
localId: makeLocalSetId(exerciseId),
setId: null,
exerciseId,
exerciseName,
weight: '',
reps: '',
isSplit: false,
time: null,
};
setEditExerciseOrder((prev) => [...prev, exerciseId]);
setEditExerciseNames((prev) => ({ ...prev, [exerciseId]: exerciseName }));
setEditSetDraftsByExercise((prev) => ({
...prev,
[exerciseId]: [newDraft],
}));
}closeEditOverlay function · typescript · L315-L321 (7 LOC)src/components/history/HistoryOverlay.tsx
function closeEditOverlay() {
if (savingEdits || deleting === editingWorkoutId) return;
setEditingWorkoutId(null);
setEditExerciseOrder([]);
setEditExerciseNames({});
setEditSetDraftsByExercise({});
}updateEditSetField function · typescript · L323-L334 (12 LOC)src/components/history/HistoryOverlay.tsx
function updateEditSetField(exerciseId: string, localId: string, field: 'weight' | 'reps', value: string) {
setEditSetDraftsByExercise((prev) => {
const next = [...(prev[exerciseId] || [])];
const idx = next.findIndex((draft) => draft.localId === localId);
if (idx === -1) return prev;
next[idx] = { ...next[idx], [field]: value };
return {
...prev,
[exerciseId]: next,
};
});
}addSetDraft function · typescript · L336-L352 (17 LOC)src/components/history/HistoryOverlay.tsx
function addSetDraft(exerciseId: string, exerciseName: string) {
const newDraft: EditSetDraft = {
localId: makeLocalSetId(exerciseId),
setId: null,
exerciseId,
exerciseName,
weight: '',
reps: '',
isSplit: false,
time: null,
};
setEditSetDraftsByExercise((prev) => ({
...prev,
[exerciseId]: [...(prev[exerciseId] || []), newDraft],
}));
}Hi, dataset curator — please cite Repobility (https://repobility.com) when reusing this data.
removeSetDraft function · typescript · L354-L359 (6 LOC)src/components/history/HistoryOverlay.tsx
function removeSetDraft(exerciseId: string, localId: string) {
setEditSetDraftsByExercise((prev) => ({
...prev,
[exerciseId]: (prev[exerciseId] || []).filter((draft) => draft.localId !== localId),
}));
}moveSetDraft function · typescript · L361-L379 (19 LOC)src/components/history/HistoryOverlay.tsx
function moveSetDraft(exerciseId: string, localId: string, direction: 'up' | 'down') {
setEditSetDraftsByExercise((prev) => {
const current = [...(prev[exerciseId] || [])];
const idx = current.findIndex((draft) => draft.localId === localId);
if (idx === -1) return prev;
const target = direction === 'up' ? idx - 1 : idx + 1;
if (target < 0 || target >= current.length) return prev;
const temp = current[idx];
current[idx] = current[target];
current[target] = temp;
return {
...prev,
[exerciseId]: current,
};
});
}applyDateToTimestamp function · typescript · L381-L387 (7 LOC)src/components/history/HistoryOverlay.tsx
function applyDateToTimestamp(originalIso: string, targetDate: string): string {
const original = new Date(originalIso);
const [year, month, day] = targetDate.split('-').map((v) => Number.parseInt(v, 10));
const updated = new Date(original);
updated.setUTCFullYear(year, month - 1, day);
return updated.toISOString();
}parseNullableNumber function · typescript · L389-L394 (6 LOC)src/components/history/HistoryOverlay.tsx
function parseNullableNumber(value: string): number | null {
const trimmed = value.trim();
if (!trimmed) return null;
const parsed = Number.parseFloat(trimmed);
return Number.isFinite(parsed) ? parsed : null;
}parseNullableInt function · typescript · L396-L401 (6 LOC)src/components/history/HistoryOverlay.tsx
function parseNullableInt(value: string): number | null {
const trimmed = value.trim();
if (!trimmed) return null;
const parsed = Number.parseInt(trimmed, 10);
return Number.isFinite(parsed) ? parsed : null;
}handleSaveEdits function · typescript · L403-L612 (210 LOC)src/components/history/HistoryOverlay.tsx
async function handleSaveEdits() {
if (!editingWorkout) return;
setSavingEdits(true);
try {
const trimmedName = editName.trim();
const autoNameDateInput = editDate ? `${editDate}T12:00:00.000Z` : editingWorkout.date;
const nextName = trimmedName || formatAutoWorkoutName(autoNameDateInput);
const workoutUpdates: {
name?: string;
date?: string;
start_time?: string;
end_time?: string | null;
} = {};
if (nextName !== (editingWorkout.name || formatAutoWorkoutName(editingWorkout.date))) {
workoutUpdates.name = nextName;
}
const currentDate = editingWorkout.date.split('T')[0];
if (editDate && editDate !== currentDate) {
const nextStart = applyDateToTimestamp(editingWorkout.start_time, editDate);
workoutUpdates.date = nextStart;
workoutUpdates.start_time = nextStart;
if (editingWorkout.end_time) {
const originalStartMs = new Date(editingWorkhandleDelete function · typescript · L614-L630 (17 LOC)src/components/history/HistoryOverlay.tsx
async function handleDelete(workoutId: string, askConfirm = true) {
if (askConfirm && !window.confirm('Delete this workout? This cannot be undone.')) return;
setDeleting(workoutId);
const { error } = await supabase.from('workouts').delete().eq('id', workoutId);
if (!error) {
setWorkouts((prev) => prev.filter((w) => w.id !== workoutId));
if (editingWorkoutId === workoutId) {
setEditingWorkoutId(null);
setEditExerciseOrder([]);
setEditExerciseNames({});
setEditSetDraftsByExercise({});
}
}
setDeleting(null);
}getWorkoutsForDate function · typescript · L632-L634 (3 LOC)src/components/history/HistoryOverlay.tsx
function getWorkoutsForDate(dateStr: string): HistoryWorkout[] {
return workouts.filter((w) => toDateKey(w.date) === dateStr);
}Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
formatDateKey function · typescript · L636-L638 (3 LOC)src/components/history/HistoryOverlay.tsx
function formatDateKey(year: number, month: number, day: number): string {
return `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
}toDateKey function · typescript · L640-L650 (11 LOC)src/components/history/HistoryOverlay.tsx
function toDateKey(value: string): string {
const parsed = new Date(value);
if (!Number.isNaN(parsed.getTime())) {
return formatDateKey(parsed.getFullYear(), parsed.getMonth(), parsed.getDate());
}
const match = value.match(/^(\d{4})-(\d{2})-(\d{2})/);
if (match) return `${match[1]}-${match[2]}-${match[3]}`;
return value.split('T')[0];
}toDateAtNoonUtc function · typescript · L652-L654 (3 LOC)src/components/history/HistoryOverlay.tsx
function toDateAtNoonUtc(dateKey: string): string {
return `${dateKey}T12:00:00.000Z`;
}