Skip to content

Commit aee6314

Browse files
committed
refactor(web): replace datetime inputs with date range picker for assignment dates
1 parent ae75e40 commit aee6314

File tree

2 files changed

+39
-47
lines changed

2 files changed

+39
-47
lines changed

apps/nextjs/src/app/teacher/classroom/[id]/assignment/[assignmentId]/page.tsx

Lines changed: 25 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use client";
22

33
import type { ColDef } from "ag-grid-community";
4+
import type { DateRange } from "react-day-picker";
45
import { use, useState } from "react";
56
import Link from "next/link";
67
import { useMutation, useQuery } from "convex/react";
@@ -34,17 +35,15 @@ import { Spinner } from "@package/ui/spinner";
3435

3536
import { getSubmissionDownload } from "~/app/app/actions";
3637
import { AppDataGrid } from "~/components/app-data-grid";
38+
import {
39+
DatePickerWithRange,
40+
defaultDateRange,
41+
} from "~/components/date-picker-with-range";
3742
import { Editor } from "~/components/editor";
3843
import { Authenticated, AuthLoading, Unauthenticated } from "~/lib/auth";
3944
import { triggerDownload } from "~/lib/download";
4045
import { StarterCodeCard } from "./starter-code-card";
4146

42-
function formatDateForInput(timestamp: number) {
43-
const date = new Date(timestamp);
44-
const offset = date.getTimezoneOffset() * 60_000;
45-
return new Date(date.getTime() - offset).toISOString().slice(0, 16);
46-
}
47-
4847
function AssignmentContent({
4948
classroomId,
5049
assignmentId,
@@ -71,8 +70,7 @@ function AssignmentContent({
7170
interface AssignmentDraft {
7271
name: NonNullable<UpdateAssignmentArgs["name"]>;
7372
description: NonNullable<UpdateAssignmentArgs["description"]>;
74-
releaseDate: string;
75-
dueDate: string;
73+
dateRange: DateRange;
7674
}
7775

7876
const [isEditing, setIsEditing] = useState(false);
@@ -95,8 +93,10 @@ function AssignmentContent({
9593
setDraft({
9694
name: assignment.name,
9795
description: assignment.description ?? "",
98-
releaseDate: formatDateForInput(assignment.releaseDate),
99-
dueDate: formatDateForInput(assignment.dueDate),
96+
dateRange: {
97+
from: new Date(assignment.releaseDate),
98+
to: new Date(assignment.dueDate),
99+
},
100100
});
101101
setIsEditing(true);
102102
};
@@ -120,8 +120,11 @@ function AssignmentContent({
120120
setIsSavingAssignment(true);
121121

122122
try {
123-
const parsedReleaseDate = Date.parse(draft.releaseDate);
124-
const parsedDueDate = Date.parse(draft.dueDate);
123+
if (!draft.dateRange.from || !draft.dateRange.to) {
124+
throw new Error("Please select a date range");
125+
}
126+
const parsedReleaseDate = draft.dateRange.from.getTime();
127+
const parsedDueDate = draft.dateRange.to.getTime();
125128

126129
if (Number.isNaN(parsedReleaseDate) || Number.isNaN(parsedDueDate)) {
127130
throw new Error("Invalid date values");
@@ -391,30 +394,11 @@ function AssignmentContent({
391394
}
392395
/>
393396
</div>
394-
<div className="space-y-2">
395-
<Label htmlFor="assignment-release-date">
396-
Release Date
397-
</Label>
398-
<Input
399-
id="assignment-release-date"
400-
type="datetime-local"
401-
value={draft?.releaseDate ?? ""}
402-
onChange={(event) =>
403-
updateDraft({ releaseDate: event.target.value })
404-
}
405-
/>
406-
</div>
407-
<div className="space-y-2">
408-
<Label htmlFor="assignment-due-date">Due Date</Label>
409-
<Input
410-
id="assignment-due-date"
411-
type="datetime-local"
412-
value={draft?.dueDate ?? ""}
413-
onChange={(event) =>
414-
updateDraft({ dueDate: event.target.value })
415-
}
416-
/>
417-
</div>
397+
<DatePickerWithRange
398+
date={draft?.dateRange ?? defaultDateRange()}
399+
onDateChange={(dateRange) => updateDraft({ dateRange })}
400+
size="narrow"
401+
/>
418402
<div className="flex gap-2">
419403
<Button
420404
className="flex-1"
@@ -450,25 +434,25 @@ function AssignmentContent({
450434
</div>
451435
<div>
452436
<p className="text-muted-foreground text-xs uppercase">
453-
Due Date
437+
Release Date
454438
</p>
455439
<p className="font-medium">
456-
{new Date(assignment.dueDate).toLocaleString()}
440+
{new Date(assignment.releaseDate).toLocaleString()}
457441
</p>
458442
</div>
459443
<div>
460444
<p className="text-muted-foreground text-xs uppercase">
461-
Release Date
445+
Due Date
462446
</p>
463447
<p className="font-medium">
464-
{new Date(assignment.releaseDate).toLocaleString()}
448+
{new Date(assignment.dueDate).toLocaleString()}
465449
</p>
466450
</div>
467451
<div>
468452
<p className="text-muted-foreground text-xs uppercase">
469453
Created
470454
</p>
471-
<p className="text-sm">
455+
<p className="font-medium">
472456
{new Date(assignment._creationTime).toLocaleString()}
473457
</p>
474458
</div>

apps/nextjs/src/components/date-picker-with-range.tsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,47 @@
22

33
import type { DateRange } from "react-day-picker";
44

5+
import { cn } from "@package/ui";
56
import { Calendar } from "@package/ui/calendar";
67
import { Input } from "@package/ui/input";
78
import { Label } from "@package/ui/label";
89

10+
type DatePickerSize = "wide" | "narrow";
11+
912
export function DatePickerWithRange({
1013
className,
1114
date,
1215
onDateChange,
1316
required,
17+
size = "wide",
1418
}: {
1519
className?: string;
1620
date: DateRange;
1721
onDateChange: (dateRange: DateRange) => void;
1822
required?: boolean;
23+
size?: DatePickerSize;
1924
}) {
25+
const numberOfMonths = size === "narrow" ? 1 : 2;
26+
const gridCols = size === "narrow" ? "grid-cols-1" : "md:grid-cols-2";
27+
2028
return (
21-
<div className={className}>
29+
<div className={cn("space-y-4", className)}>
2230
<Label aria-label="availability-period">Availability Period</Label>
2331
<Calendar
24-
className="w-full"
32+
className="w-full p-0"
2533
mode="range"
2634
defaultMonth={date.from}
2735
selected={date}
2836
onSelect={(newDateRange: DateRange | undefined) =>
2937
onDateChange(updateDateRange(date, newDateRange))
3038
}
31-
numberOfMonths={2}
39+
numberOfMonths={numberOfMonths}
3240
showOutsideDays={false}
3341
required={required}
3442
/>
35-
<div className="grid gap-4 md:grid-cols-2">
43+
<div className={cn("grid gap-4", gridCols)}>
3644
<div className="space-y-2">
37-
<Label htmlFor="release-time">Start Time</Label>
45+
<Label htmlFor="release-time">Release Time</Label>
3846
<Input
3947
id="release-time"
4048
type="time"
@@ -51,7 +59,7 @@ export function DatePickerWithRange({
5159
/>
5260
</div>
5361
<div className="space-y-2">
54-
<Label htmlFor="due-time">End Time</Label>
62+
<Label htmlFor="due-time">Due Time</Label>
5563
<Input
5664
id="due-time"
5765
type="time"

0 commit comments

Comments
 (0)