Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/app/baseball/actions/lift-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ const saveLiftSessionPlanSchema = z.object({
* Optional subset of player IDs to target within the resolved group/team.
* When present, the resolved athlete list is intersected with these IDs —
* callers cannot expand scope beyond what the group/team already grants.
* An explicitly-provided empty array targets nobody (count: 0); only an
* omitted (undefined) value falls back to the whole resolved scope.
*/
playerIds: z.array(uuid).max(200).optional(),
});
Expand Down Expand Up @@ -321,8 +323,9 @@ export const saveLiftSessionPlan = withBaseballAction(

// If the caller supplied a player-ID subset, restrict to the intersection
// with the resolved scope. Callers cannot expand scope beyond what the
// group/team membership already grants.
if (input.playerIds && input.playerIds.length > 0) {
// group/team membership already grants. An explicitly-provided empty set
// means "nobody" (count: 0) — it must NOT fall back to the whole team/group.
if (input.playerIds) {
const requested = new Set(input.playerIds);
playerIds = playerIds.filter((id) => requested.has(id));
}
Expand Down
14 changes: 12 additions & 2 deletions src/components/baseball/performance/LiftBuilderClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,13 @@ export function LiftBuilderClient({
});
return;
}
if (selectedPlayerIds.size === 0) {
setSaveMsg({
text: 'Select at least one athlete before saving.',
tone: 'error',
});
return;
}
setSaveMsg(null);
startSave(async () => {
try {
Expand All @@ -259,7 +266,9 @@ export function LiftBuilderClient({
date: sessionDate,
title: sessionTitle.trim(),
blocks: draftBlocks,
playerIds: selectedPlayerIds.size > 0 ? [...selectedPlayerIds] : undefined,
// Always send the explicit per-player selection. An empty set means
// "nobody" — never fall back to the whole team/group.
playerIds: [...selectedPlayerIds],
});
if (res.success) {
setSaveMsg({
Expand Down Expand Up @@ -519,7 +528,8 @@ export function LiftBuilderClient({
onClick={handleSave}
isLoading={saving}
>
Save — {selectedGroupName}
Save — {selectedPlayerIds.size} selected athlete
{selectedPlayerIds.size === 1 ? '' : 's'}
</Button>
</div>
</Card>
Expand Down
Loading