️ feat: fix accessibility warnings, PWA icons, and form field attributes

- add DialogDescription (sr-only) to EventDialog to satisfy Radix UI
  aria-describedby requirement and silence the React console warning
- add htmlFor/id pairing to the all-day checkbox label in EventDialog
- add id and name attributes to all raw form fields (textarea, checkbox)
  in event-dialog to resolve biome a11y lint warnings (4 fields fixed)
- add name attribute to hidden file inputs in image-picker and ics-file-picker
- generate public/icon-192x192.png and public/icon-512x512.png so the PWA
  manifest no longer returns 404 for the app icons
This commit is contained in:
2026-04-07 23:08:48 -04:00
parent cbd2559169
commit d002bbc29e
5 changed files with 17 additions and 1 deletions

BIN
public/icon-192x192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

BIN
public/icon-512x512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@@ -3,6 +3,7 @@ import { Button } from "@/components/ui/button";
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
DialogDescription,
DialogFooter, DialogFooter,
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
@@ -66,32 +67,45 @@ export const EventDialog = ({
<DialogContent> <DialogContent>
<DialogHeader> <DialogHeader>
<DialogTitle>{editingId ? "Edit Event" : "Add Event"}</DialogTitle> <DialogTitle>{editingId ? "Edit Event" : "Add Event"}</DialogTitle>
<DialogDescription className="sr-only">
Fill in the event details below. Title and start date are required.
</DialogDescription>
</DialogHeader> </DialogHeader>
<Input <Input
id="event-title"
name="title"
placeholder="Title" placeholder="Title"
value={title} value={title}
onChange={(e) => setTitle(e.target.value)} onChange={(e) => setTitle(e.target.value)}
/> />
<textarea <textarea
id="event-description"
name="description"
className="border rounded p-2 w-full" className="border rounded p-2 w-full"
placeholder="Description" placeholder="Description"
value={description} value={description}
onChange={(e) => setDescription(e.target.value)} onChange={(e) => setDescription(e.target.value)}
/> />
<Input <Input
id="event-location"
name="location"
placeholder="Location" placeholder="Location"
value={location} value={location}
onChange={(e) => setLocation(e.target.value)} onChange={(e) => setLocation(e.target.value)}
/> />
<Input <Input
id="event-url"
name="url"
placeholder="URL" placeholder="URL"
value={url} value={url}
onChange={(e) => setUrl(e.target.value)} onChange={(e) => setUrl(e.target.value)}
/> />
<RecurrencePicker value={recurrenceRule} onChange={setRecurrenceRule} /> <RecurrencePicker value={recurrenceRule} onChange={setRecurrenceRule} />
<label className="flex items-center gap-2 mt-2"> <label className="flex items-center gap-2 mt-2" htmlFor="event-all-day">
<input <input
id="event-all-day"
name="allDay"
type="checkbox" type="checkbox"
checked={allDay} checked={allDay}
onChange={(e) => setAllDay(e.target.checked)} onChange={(e) => setAllDay(e.target.checked)}

View File

@@ -44,6 +44,7 @@ export function IcsFilePicker({
<input <input
ref={fileInputRef} ref={fileInputRef}
type="file" type="file"
name="ics-file"
accept=".ics" accept=".ics"
onChange={handleFileChange} onChange={handleFileChange}
className="hidden" className="hidden"

View File

@@ -42,6 +42,7 @@ export function ImagePicker({
<input <input
ref={fileInputRef} ref={fileInputRef}
type="file" type="file"
name="image-upload"
accept="image/png,image/jpeg,image/webp" accept="image/png,image/jpeg,image/webp"
onChange={handleFileChange} onChange={handleFileChange}
className="hidden" className="hidden"