The events dashboard is the core of PunctuOwlity. It displays every saved event as a card in a two-column grid and provides tools to add new events, edit or delete existing ones, search by title, and filter by category. On Android the dashboard isDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/apursley2012/punctuowlity/llms.txt
Use this file to discover all available pages before exploring further.
MainActivity; in the browser it is events.html, driven by app.js.
The Events Dashboard
Each event appears as a card containing the following information:| Position | Content | Example |
|---|---|---|
| Top-left | Three-letter day abbreviation | SAT |
| Centre-left | Two-digit day-of-month number | 05 |
| Body | Event title | Project Two Due |
| Footer | Formatted time, or ALL DAY if no time was set | 01:30PM |
| Top-right | Alarm icon (on or off) indicating reminder status | 🔔 / 🔕 |
| Actions | Edit and delete icon buttons | ✏️ 🗑 |
CardView layout (event_card.xml) for each Event returned by db.getAllEvents(), then adding each card to a GridLayout (eventsGrid). The getDayOfWeek() and getDateDay() helpers on the Event model parse the stored MM/dd/yyyy date string using SimpleDateFormat to derive the abbreviated day name and zero-padded day number respectively.
Browser renders cards dynamically. The render() function in app.js calls getEvents(), applies the active category filter and any search query, then maps the resulting array into HTML <article> elements which are written to the #eventsGrid section as a single innerHTML assignment.
Adding an Event
Tap the Add Event button
Android: Tap the floating action button (
fabAddEvent) on MainActivity. This starts AddEventActivity with no extras.Browser: Tap the floating action button (.fab) on events.html. This navigates to add-event.html with no URL parameters.Fill in the event details
Three fields are required on both platforms:
- Title — a free-text name for the event (
#editEventTitleon Android,#eventTitlein the browser). - Date — entered in
MM/dd/yyyyformat on Android (textEventDate); the browser renders an<input type="date">picker (#eventDate) which stores the value inYYYY-MM-DDformat internally. - Time — entered as free text on Android (
textEventTime); the browser renders an<input type="time">picker (#eventTime).
#eventAlert, a checkbox styled as a switch) labelled “Would you like to be reminded of this event?”Save the event
Android: The Browser: The
buttonSave click listener validates that none of the three fields are empty, then calls db.insertEvent(title, date, time) for a new event:DatabaseHelper.insertEvent writes a new row to the events SQLite table:#eventForm submit handler validates the fields, constructs a new event object (including the alert checkbox state), and pushes it to the events array in localStorage:Editing an Event
Android: Tapping the editImageButton on a card in MainActivity starts AddEventActivity with the event’s database ID attached as an extra:
AddEventActivity.onCreate, the presence of the event_id extra triggers a lookup via db.getEventById(eventId), and the returned values pre-populate the three form fields. On save, db.updateEvent(eventId, title, date, time) is called instead of insertEvent:
add-event.html?id=<event_id>. The add page handler reads the id URL parameter, finds the matching event with getEvents().find(e => e.id === id), and pre-populates the form fields (title, date, time, and the alert checkbox) with the existing values. On submit, events.findIndex(x => x.id === id) locates the record in the array and events[i] = item replaces it in place before saveEvents writes the updated array back to localStorage.
Deleting an Event
Android: Tapping the deleteImageButton on a card calls db.deleteEvent(event.getId()) and then loadEvents() to rebuild the grid:
DatabaseHelper.deleteEvent removes the matching row from the SQLite events table by primary key:
#eventsGrid. It identifies the card’s data-id attribute, filters the event out of the current array with getEvents().filter(x => x.id !== card.dataset.id), writes the result back via saveEvents, re-renders the grid, and shows a toast:
Searching and Filtering
A search input (#eventSearch) and four category tabs sit above the events grid on both platforms.
Search: The input event on #eventSearch calls render() immediately. Inside render(), events are filtered so that event.title.toLowerCase().includes(q) is true for the current lowercase query string. The filter is case-insensitive and matches any substring of the title.
Category tabs: The tabs are All Events, Birthdays, Appointments, and Trips, corresponding to the category values all, birthday, appointment, and trip. Clicking a tab updates the category variable and calls render(). The two filters are applied together — an event must satisfy both the active category and the search query to appear.
Auto-detection of categories (browser): When an event is normalised by normalizeEvent(), if the event object has no explicit category property, the function inspects the title to infer one:
Event Data Model
| Field | Type | Description |
|---|---|---|
id | string (browser) / integer (Android) | Unique identifier. Browser uses Date.now() as a string; Android uses SQLite AUTOINCREMENT. |
title | string | The event name as entered by the user. |
date | string | Zero-padded day-of-month number, e.g. "05". Derived from fullDate by normalizeEvent. |
time | string | Human-readable display time, e.g. "01:30PM" or "ALL DAY". Formatted by normalizeEvent from rawTime. |
day | string | Three-letter uppercase day abbreviation, e.g. "SAT". Derived from fullDate. |
fullDate | string | ISO 8601 date string (YYYY-MM-DD). Used for date comparisons and notification checks. |
rawTime | string | 24-hour time string as stored (HH:MM), or empty string for all-day events. |
category | string | One of birthday, appointment, trip, or general (default). The All Events tab shows every category; no event is stored with category all. |
alert | boolean | Whether a reminder notification is enabled for this event. |