feat: enhance draft reset functionality to include deletion of draft timers
This commit is contained in:
parent
4aafd5853b
commit
e7baea319e
2 changed files with 97 additions and 29 deletions
|
|
@ -13,6 +13,7 @@ import { findAllSportsSeasons } from "~/models/sports-season";
|
|||
import { findDraftSlotsBySeasonId, setDraftOrder, randomizeDraftOrder } from "~/models/draft-slot";
|
||||
import { deleteAllDraftPicks } from "~/models/draft-pick";
|
||||
import { clearAllQueuesForSeason } from "~/models/draft-queue";
|
||||
import { deleteSeasonTimers } from "~/models/draft-timer";
|
||||
import type { Route } from "./+types/$leagueId.settings";
|
||||
import { Button } from "~/components/ui/button";
|
||||
import { Input } from "~/components/ui/input";
|
||||
|
|
@ -284,21 +285,24 @@ export async function action(args: Route.ActionArgs) {
|
|||
return { error: "Only admins can reset the draft" };
|
||||
}
|
||||
|
||||
// Check if season status is draft or completed
|
||||
if (season.status !== "draft" && season.status !== "completed" && season.status !== "active") {
|
||||
return { error: "Draft can only be reset after it has started" };
|
||||
}
|
||||
|
||||
try {
|
||||
// Delete all draft picks
|
||||
await deleteAllDraftPicks(season.id);
|
||||
|
||||
|
||||
// Clear all draft queues
|
||||
await clearAllQueuesForSeason(season.id);
|
||||
|
||||
// Set season status back to pre_draft (keeps draft order intact)
|
||||
await updateSeason(season.id, { status: "pre_draft" });
|
||||
|
||||
|
||||
// Delete all draft timers
|
||||
await deleteSeasonTimers(season.id);
|
||||
|
||||
// Set season status back to pre_draft and reset draft state (keeps draft order intact)
|
||||
await updateSeason(season.id, {
|
||||
status: "pre_draft",
|
||||
currentPickNumber: null,
|
||||
draftStartedAt: null,
|
||||
draftPaused: false,
|
||||
});
|
||||
|
||||
return { success: true, message: "Draft has been reset successfully. Draft order preserved." };
|
||||
} catch (error) {
|
||||
console.error("Error resetting draft:", error);
|
||||
|
|
@ -676,7 +680,6 @@ export default function LeagueSettings({ loaderData, actionData }: Route.Compone
|
|||
mode="single"
|
||||
selected={draftDate}
|
||||
onSelect={setDraftDate}
|
||||
initialFocus
|
||||
disabled={(date) => date < new Date(new Date().setHours(0, 0, 0, 0))}
|
||||
/>
|
||||
</PopoverContent>
|
||||
|
|
@ -1017,11 +1020,11 @@ export default function LeagueSettings({ loaderData, actionData }: Route.Compone
|
|||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{/* Reset Draft - Admin Only */}
|
||||
{isAdmin && season && (season.status === "draft" || season.status === "completed" || season.status === "active") && (
|
||||
{isAdmin && season && (
|
||||
<div className="space-y-2">
|
||||
<h3 className="font-medium">Reset Draft</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Delete all draft picks and reset the season to pre-draft status. The draft order will be preserved.
|
||||
Delete all draft picks, queues, and timers, and reset the season to pre-draft status. The draft order will be preserved.
|
||||
</p>
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
|
|
@ -1033,7 +1036,7 @@ export default function LeagueSettings({ loaderData, actionData }: Route.Compone
|
|||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Reset the draft?</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
This will delete all draft picks and draft queues, and set the season back to pre-draft status.
|
||||
This will delete all draft picks, draft queues, and draft timers, and set the season back to pre-draft status.
|
||||
The draft order will remain intact. This action cannot be undone.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { deleteAllDraftPicks } from '~/models/draft-pick';
|
||||
import { clearAllQueuesForSeason } from '~/models/draft-queue';
|
||||
import { deleteSeasonTimers } from '~/models/draft-timer';
|
||||
import { updateSeason } from '~/models/season';
|
||||
import { isUserAdminByClerkId } from '~/models/user';
|
||||
|
||||
|
|
@ -38,6 +39,10 @@ vi.mock('~/models/draft-queue', () => ({
|
|||
clearAllQueuesForSeason: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('~/models/draft-timer', () => ({
|
||||
deleteSeasonTimers: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('~/models/season', () => ({
|
||||
updateSeason: vi.fn(),
|
||||
findCurrentSeasonWithSports: vi.fn(),
|
||||
|
|
@ -119,6 +124,17 @@ describe('Draft Reset - Delete Operations', () => {
|
|||
expect(clearAllQueuesForSeason).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should delete all draft timers for a season', async () => {
|
||||
const seasonId = 'season-1';
|
||||
|
||||
vi.mocked(deleteSeasonTimers).mockResolvedValue(undefined);
|
||||
|
||||
await deleteSeasonTimers(seasonId);
|
||||
|
||||
expect(deleteSeasonTimers).toHaveBeenCalledWith(seasonId);
|
||||
expect(deleteSeasonTimers).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should handle errors when deleting draft picks fails', async () => {
|
||||
const seasonId = 'season-1';
|
||||
const error = new Error('Database error');
|
||||
|
|
@ -136,6 +152,15 @@ describe('Draft Reset - Delete Operations', () => {
|
|||
|
||||
await expect(clearAllQueuesForSeason(seasonId)).rejects.toThrow('Database error');
|
||||
});
|
||||
|
||||
it('should handle errors when deleting timers fails', async () => {
|
||||
const seasonId = 'season-1';
|
||||
const error = new Error('Database error');
|
||||
|
||||
vi.mocked(deleteSeasonTimers).mockRejectedValue(error);
|
||||
|
||||
await expect(deleteSeasonTimers(seasonId)).rejects.toThrow('Database error');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Draft Reset - Season Status Update', () => {
|
||||
|
|
@ -172,30 +197,30 @@ describe('Draft Reset - Status Validation', () => {
|
|||
|
||||
it('should allow reset when season status is draft', () => {
|
||||
const seasonStatus = 'draft';
|
||||
const allowedStatuses = ['draft', 'completed', 'active'];
|
||||
const validStatuses = ['pre_draft', 'draft', 'completed', 'active'];
|
||||
|
||||
expect(allowedStatuses.includes(seasonStatus)).toBe(true);
|
||||
expect(validStatuses.includes(seasonStatus)).toBe(true);
|
||||
});
|
||||
|
||||
it('should allow reset when season status is completed', () => {
|
||||
const seasonStatus = 'completed';
|
||||
const allowedStatuses = ['draft', 'completed', 'active'];
|
||||
const validStatuses = ['pre_draft', 'draft', 'completed', 'active'];
|
||||
|
||||
expect(allowedStatuses.includes(seasonStatus)).toBe(true);
|
||||
expect(validStatuses.includes(seasonStatus)).toBe(true);
|
||||
});
|
||||
|
||||
it('should allow reset when season status is active', () => {
|
||||
const seasonStatus = 'active';
|
||||
const allowedStatuses = ['draft', 'completed', 'active'];
|
||||
const validStatuses = ['pre_draft', 'draft', 'completed', 'active'];
|
||||
|
||||
expect(allowedStatuses.includes(seasonStatus)).toBe(true);
|
||||
expect(validStatuses.includes(seasonStatus)).toBe(true);
|
||||
});
|
||||
|
||||
it('should not allow reset when season status is pre_draft', () => {
|
||||
it('should allow reset when season status is pre_draft', () => {
|
||||
const seasonStatus = 'pre_draft';
|
||||
const allowedStatuses = ['draft', 'completed', 'active'];
|
||||
const validStatuses = ['pre_draft', 'draft', 'completed', 'active'];
|
||||
|
||||
expect(allowedStatuses.includes(seasonStatus)).toBe(false);
|
||||
expect(validStatuses.includes(seasonStatus)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -223,14 +248,29 @@ describe('Draft Reset - Complete Workflow', () => {
|
|||
await clearAllQueuesForSeason(seasonId);
|
||||
expect(clearAllQueuesForSeason).toHaveBeenCalledWith(seasonId);
|
||||
|
||||
// Step 4: Update season status
|
||||
// Step 4: Delete draft timers
|
||||
vi.mocked(deleteSeasonTimers).mockResolvedValue(undefined);
|
||||
await deleteSeasonTimers(seasonId);
|
||||
expect(deleteSeasonTimers).toHaveBeenCalledWith(seasonId);
|
||||
|
||||
// Step 5: Update season status and reset draft state
|
||||
const mockUpdatedSeason = createMockSeason({ id: seasonId, status: 'pre_draft' });
|
||||
|
||||
vi.mocked(updateSeason).mockResolvedValue(mockUpdatedSeason);
|
||||
const result = await updateSeason(seasonId, { status: 'pre_draft' });
|
||||
const result = await updateSeason(seasonId, {
|
||||
status: 'pre_draft',
|
||||
currentPickNumber: null,
|
||||
draftStartedAt: null,
|
||||
draftPaused: false,
|
||||
});
|
||||
|
||||
expect(result.status).toBe('pre_draft');
|
||||
expect(updateSeason).toHaveBeenCalledWith(seasonId, { status: 'pre_draft' });
|
||||
expect(updateSeason).toHaveBeenCalledWith(seasonId, {
|
||||
status: 'pre_draft',
|
||||
currentPickNumber: null,
|
||||
draftStartedAt: null,
|
||||
draftPaused: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should not proceed with reset if admin check fails', async () => {
|
||||
|
|
@ -244,6 +284,7 @@ describe('Draft Reset - Complete Workflow', () => {
|
|||
// Should not call any reset functions
|
||||
expect(deleteAllDraftPicks).not.toHaveBeenCalled();
|
||||
expect(clearAllQueuesForSeason).not.toHaveBeenCalled();
|
||||
expect(deleteSeasonTimers).not.toHaveBeenCalled();
|
||||
expect(updateSeason).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
|
@ -385,6 +426,10 @@ describe('Draft Reset - Data Integrity', () => {
|
|||
callOrder.push('clearAllQueuesForSeason');
|
||||
});
|
||||
|
||||
vi.mocked(deleteSeasonTimers).mockImplementation(async () => {
|
||||
callOrder.push('deleteSeasonTimers');
|
||||
});
|
||||
|
||||
vi.mocked(updateSeason).mockImplementation(async () => {
|
||||
callOrder.push('updateSeason');
|
||||
return createMockSeason({ id: seasonId, status: 'pre_draft' });
|
||||
|
|
@ -393,12 +438,19 @@ describe('Draft Reset - Data Integrity', () => {
|
|||
// Execute in correct order
|
||||
await deleteAllDraftPicks(seasonId);
|
||||
await clearAllQueuesForSeason(seasonId);
|
||||
await updateSeason(seasonId, { status: 'pre_draft' });
|
||||
await deleteSeasonTimers(seasonId);
|
||||
await updateSeason(seasonId, {
|
||||
status: 'pre_draft',
|
||||
currentPickNumber: null,
|
||||
draftStartedAt: null,
|
||||
draftPaused: false,
|
||||
});
|
||||
|
||||
// Verify order
|
||||
expect(callOrder).toEqual([
|
||||
'deleteAllDraftPicks',
|
||||
'clearAllQueuesForSeason',
|
||||
'deleteSeasonTimers',
|
||||
'updateSeason',
|
||||
]);
|
||||
});
|
||||
|
|
@ -409,17 +461,30 @@ describe('Draft Reset - Data Integrity', () => {
|
|||
// All operations reference the same season
|
||||
vi.mocked(deleteAllDraftPicks).mockResolvedValue(undefined);
|
||||
vi.mocked(clearAllQueuesForSeason).mockResolvedValue(undefined);
|
||||
vi.mocked(deleteSeasonTimers).mockResolvedValue(undefined);
|
||||
vi.mocked(updateSeason).mockResolvedValue(
|
||||
createMockSeason({ id: seasonId, status: 'pre_draft' })
|
||||
);
|
||||
|
||||
await deleteAllDraftPicks(seasonId);
|
||||
await clearAllQueuesForSeason(seasonId);
|
||||
await updateSeason(seasonId, { status: 'pre_draft' });
|
||||
await deleteSeasonTimers(seasonId);
|
||||
await updateSeason(seasonId, {
|
||||
status: 'pre_draft',
|
||||
currentPickNumber: null,
|
||||
draftStartedAt: null,
|
||||
draftPaused: false,
|
||||
});
|
||||
|
||||
// All operations used the same season ID
|
||||
expect(deleteAllDraftPicks).toHaveBeenCalledWith(seasonId);
|
||||
expect(clearAllQueuesForSeason).toHaveBeenCalledWith(seasonId);
|
||||
expect(updateSeason).toHaveBeenCalledWith(seasonId, { status: 'pre_draft' });
|
||||
expect(deleteSeasonTimers).toHaveBeenCalledWith(seasonId);
|
||||
expect(updateSeason).toHaveBeenCalledWith(seasonId, {
|
||||
status: 'pre_draft',
|
||||
currentPickNumber: null,
|
||||
draftStartedAt: null,
|
||||
draftPaused: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue