Skip to main content

Documentation 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.

PunctuOwlity stores all user and event data locally on the device or in the browser — no server or cloud backend is involved. The web application uses the browser’s Web Storage APIs, while the Android application uses a SQLite database managed through a custom SQLiteOpenHelper subclass. Both approaches keep the data entirely within the user’s environment, which makes the project suitable for static hosting and offline use but also means that data does not sync across devices or sessions.

Web Storage

The web app uses two browser storage mechanisms: localStorage for data that must survive between sessions, and sessionStorage for flags that should reset when the browser tab is closed.

localStorage Keys

KeyTypePurpose
punctuowlity-eventsJSON array of event objectsAll saved events; falls back to seed events if absent or unparseable
punctuowlity-usersJSON array of user objectsAll registered user accounts
punctuowlity-sms'allowed' | 'denied'The user’s SMS/notification preference; absence means the preference has not been set yet

sessionStorage Keys

KeyValuePurpose
punctuowlity-authenticated'true'Set after a successful login; cleared when the tab closes. Pages with data-page="main" redirect to index.html if this key is absent.
punctuowlity-notified-{id}-{date}'true'Deduplication flag for browser notifications. One key per event per calendar date; prevents duplicate alerts within the same session.

Event Object Schema

Each entry in the punctuowlity-events array conforms to the following shape. Fields are written by normalizeEvent before the record is saved or rendered.
id
string
required
Unique identifier. Set to a seed keyword (e.g. 'birthday') for built-in events; set to String(Date.now()) for user-created events.
day
string
required
Three-letter weekday abbreviation in uppercase, e.g. 'SAT', 'MON'. Derived from fullDate by normalizeEvent.
date
string
required
Two-digit day-of-month string, e.g. '05', '14'. Derived from fullDate by normalizeEvent.
fullDate
string
required
ISO 8601 date string in YYYY-MM-DD format, e.g. '2025-04-05'. Used as the canonical date for parsing and notification matching.
rawTime
string
required
24-hour time string in HH:MM format, e.g. '13:30'. Empty string for all-day events.
time
string
required
Display-formatted time in compact 12-hour format, e.g. '01:30PM'. Set to 'ALL DAY' when rawTime is empty.
title
string
required
The event title as entered by the user, e.g. 'Dentist', "Jessica's Wedding".
category
string
required
One of 'all', 'birthday', 'appointment', 'trip', or 'general'. Auto-detected from the title if not explicitly set.
alert
boolean
required
Whether the user enabled a reminder for this event. Controls which alarm icon is displayed on the card and whether a browser notification is fired.

User Object Schema

Each entry in the punctuowlity-users array has the following fields. The web version stores more profile fields than the Android version because the sign-up form collects them.
id
string
required
Unique identifier; set to String(Date.now()) at registration time.
firstName
string
required
User’s first name.
lastName
string
required
User’s last name.
email
string
required
Email address (lowercased). Accepted as an alternative login credential alongside username.
phone
string
Optional phone number, collected for potential SMS use but not transmitted anywhere in the static app.
username
string
required
Chosen username (lowercased for comparison). Must be unique across all stored users.
password
string
required
Password as entered. See the warning below regarding plain-text storage.

Data Normalization

The normalizeEvent function in app.js is called on every event before it is rendered or saved. It handles two categories of stored data that need conversion before display: raw date strings and raw time strings.

Date Parsing

normalizeEvent accepts two date input formats and converts both to YYYY-MM-DD for the canonical fullDate field:
  • YYYY-MM-DD — already in the correct format; parsed directly via new Date($T00:00:00) to avoid timezone drift.
  • MM/DD/YYYY — the format used by the Android Event model. Split on /, reassembled as YYYY-MM-DD, then parsed.
Once parsed, the weekday (day) and two-digit day-of-month (date) are derived from the Date object using toLocaleDateString('en-US', { weekday: 'short' }) and getDate() respectively. If parsing fails or produces an invalid date, the function falls back to whatever day and date values are already in the record, or to '---' and '00'.

Time Conversion

If rawTime matches the pattern HH:MM (24-hour), it is converted to the compact 12-hour display format used on the event cards:
13:30  →  01:30PM
18:30  →  06:30PM
00:00  →  12:00AM
An empty rawTime produces the string 'ALL DAY'.

Category Auto-Detection

If an event record has no category field set, normalizeEvent inspects the lowercased title and assigns one of the following categories automatically:
Keyword matchAssigned category
birthday'birthday'
appointment, dentist, dental, doctor'appointment'
trip, travel, vacation'trip'
(no match)'general'

Android SQLite

The Android app stores data in a SQLite database file named punctuowlity.db at database version 1. The DatabaseHelper class extends SQLiteOpenHelper and defines two tables.

Schema

CREATE TABLE users (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  username TEXT UNIQUE,
  password TEXT
);

CREATE TABLE events (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  title TEXT,
  date TEXT,
  time TEXT
);

Table Details

users table
ColumnTypeNotes
idINTEGERAuto-incrementing primary key
usernameTEXT UNIQUEUsed as the login credential; uniqueness is enforced at the database level
passwordTEXTPlain text; see warning below
events table
ColumnTypeNotes
idINTEGERAuto-incrementing primary key
titleTEXTEvent title string
dateTEXTStored as MM/dd/yyyy string; parsed by Event.getDayOfWeek() and Event.getDateDay() at display time
timeTEXTTime string as entered

onCreate and onUpgrade

DatabaseHelper.onCreate() executes both CREATE TABLE statements in sequence when the database is first created. onUpgrade() drops both tables with DROP TABLE IF EXISTS and then calls onCreate() again — all data is lost on a version upgrade.
Both the web app and the Android app store passwords as plain text. The users localStorage array in the web version and the users table in the SQLite database both record the password exactly as the user typed it. This is appropriate for a self-contained local demonstration, but any application handling real user accounts requires server-side password hashing (e.g. bcrypt), secure transport, and proper session management.

Build docs developers (and LLMs) love