Honor your time.
A personal appointment intelligence PWA — a calm, premium app that helps you become more reliable and intentional with your time, with optional Family and Workspace modes for sharing that reliability with the people around you.
Built by Afolaola.
This zip contains only the marketing site (index.html) plus what it needs to be self-contained: shared branding, icons, manifest, robots.txt, sitemap.xml, and the Privacy/Terms/documentation pages it links to.
Cross-package links are handled automatically, not just documented. Every "Install OlaTime" and "Try Insights" call-to-action on this page links to app.html and olatime-insights.html — those live in the separate App package. branding/peer-check.js checks at page load whether those pages are actually reachable — if the App package isn't deployed alongside this one, those specific links are automatically disabled (dimmed, aria-disabled) and show a short friendly message instead of ever letting anyone click through to a real 404. Deploy both packages together in the same directory and this becomes a non-issue — the links just work.
Both packages ship a manifest.webmanifest with the same filename, but different start_url values (this package's points at ./, the App package's points at app.html). If you deploy both together in the same directory, whichever file gets copied last silently wins. Keep this package's manifest (start_url: "./") for a combined deployment — it's the correct one, since index.html genuinely exists at that root here.
GitHub Pages always serves index.html as a directory's default page — there's no way to configure a different default document on static hosting. To make the Overview/Insights landing experience load first (not the app), file roles were swapped:
index.html — the landing page (Overview, Insights, "How it's built"). This is what loads at your bare site URL and what an installed PWA icon opens to.app.html — the actual scheduling app (this used to be index.html).olatime-insights.html — unchanged, the organization analytics dashboard.Every internal link was updated to match (the landing page's "Open app" buttons point to app.html; the app's own home icon and Settings link point back to index.html; the insights dashboard's Home button too).
Bugs fixed:
sw.js served same-origin requests cache-first, including page navigations. Once index.html/app.html were cached, they kept being served from that cache regardless of new deploys, until you manually bypassed the service worker with a hard refresh. Pages are now network-first: always fetch the latest HTML first, falling back to cache only when actually offline. Static assets (icons, etc.) stay cache-first, since those rarely change.signOutSync() just toasted "Signed out" and re-rendered. A full farewell screen now exists (see below).github.com/Olasunkanmi-hub/olasunkanmi-analytics (the raw repo) instead of the deployed page, in both index.html and olatime-insights.html's footer (the second one was missed on the first pass). Both now correctly point to https://olasunkanmi-hub.github.io/olasunkanmi-analytics/.index.html and olatime-insights.html. The source code is no longer linked from the live site.Features added:
guest.html) — invite someone with a secure link; they can accept/decline/request reschedule with no install and no account.mailto: Send Reminder button, and push reminders that target the *specific assignee's device* (via OneSignal's External ID feature) instead of broadcasting to everyone.visibilitychange listener so habits/streaks never show stale state if the app is left open across midnight.Deliberately not done — and why: full personal analytics/habits/achievements *per workspace member* (the real ask is closer to replicating the whole personal app once per teammate — a much bigger effort than a polish pass); scheduled future emails (EmailJS is client-triggered only, can't fire itself later with nothing open — what's real is an *immediate* assignment email); a ground-up visual redesign (already substantially in place; redoing it risks the exact kind of regression this project has been actively avoiding).
/ (or the search icon) to fuzzy-search appointments, family members, workspaces, and run commandsStill a deliberate no-build progressive web app — plain HTML, CSS, and JavaScript. No framework, no bundler, no dependencies to install.
index.html — the landing page (Overview / Insights / How it's built)app.html — the actual scheduling appolatime-insights.html — the organization analytics dashboardguest.html — the no-account Guest Portalsw.js — service worker (network-first HTML, cache-first static assets)manifest.webmanifest — install metadatafirebase-sync.js — optional cloud sync + Workspace/Guest backend (Firebase)firestore.rules — security rules for personal, Family, and Workspace data — must be deployed via the Firebase CLI, see that file's own headerOneSignalSDKWorker.js — required at the site root for OneSignal pushicons/ — app icons (192, 512, maskable, apple-touch, favicon)CLOUD-SYNC.md — what cloud sync does and how it worksEverything below is optional — the app works fully without any of it.
OneSignal (real background push): free account at onesignal.com → new Web Push app → paste your App ID and REST API Key into the two blank ONESIGNAL_* values near the top of app.html's script.
EmailJS (workspace invite/assignment emails): free account at emailjs.com (200 emails/month, 2 templates) → connect an email provider → one template with {{to_email}} {{subject}} {{message}} {{workspace_name}} → paste your Service ID, Template ID, and Public Key into the EMAILJS_* values in app.html.
Firestore security rules (required before trusting Workspace Mode with real data):
npm install -g firebase-tools
firebase login
firebase init firestore # point it at the existing firestore.rules file
firebase deploy --only firestore:rules
Test with the Firebase Local Emulator Suite before using real data — these rules were reasoned through carefully but not run against a live project from where they were written.
npx serve .
# or
python3 -m http.server 8000
Then open the URL it prints. To test install + offline, use Chrome DevTools → Application → Service Workers.
> Opening files directly via file:// won't register the service worker. Serve it over http(s)://.
GitHub Pages — push to GitHub, then Settings → Pages → deploy from the main branch, root folder.
Netlify — drag this folder onto app.netlify.com, or connect the GitHub repo. No build command; publish directory is the repo root.
Both serve over HTTPS, which is required for PWA install and notifications.
Four layers, in order of reliability: in-app (Smart Reminder Engine, always works), browser Notification API (while the tab/installed app is open), real background push via OneSignal (works even fully closed, needs setup above), and email via EmailJS (needs setup above). On iPhone, add OlaTime to your Home Screen (Share → Add to Home Screen) for the most reliable in-app/browser reminders — iOS restricts web notifications for uninstalled sites regardless of the above.
MIT © Afolaola
branding/ contains the actual shared AFOLAOLA branding package (company
identity JSON + the AFOLAOLA company mark), copied in from
afolaola-shared-package/branding/, plus brand-loader.js, a small
runtime loader written for this integration.
How it works: brand-loader.js fetches company.json,
social-links.json, contact-information.json, and urls.json directly
at runtime and builds window.AFOLAOLA from them. There is no generated
or hand-copied duplicate of this data anywhere — the JSON files themselves
are the only source of truth, so editing one takes effect immediately with
nothing to keep in sync and nothing that can silently drift.
Any page that needs the resolved values awaits window.AFOLAOLA_READY
(a promise); window.AFOLAOLA itself holds safe, generic fallback values
until that resolves, so nothing breaks or shows "undefined" if the fetch
is slow or the JSON is briefly unreachable. All three pages (index.html,
app.html, olatime-insights.html) load it the same way:
<script src="branding/brand-loader.js"></script>
The JSON files and the loader script are precached by the service worker,
so this keeps working offline after the first successful load — consistent
with the rest of the app.
Deliberately not changed: OlaTime's own app icons, favicons, manifest,
and color theme. The shared package's own README is explicit that the
AFOLAOLA mark is for ecosystem attribution, not a replacement for a
product's own visual identity — so OlaTime's PWA icon, installed home
screen identity, and brand colors are untouched.
Not integrated: feedback-module/ (a Supabase-backed feedback +
testimonials widget) is a real, separate feature, not a branding asset —
it wasn't added here since that's a product decision (new external
dependency, needs its own Supabase credentials) rather than something
implied by a branding refactor.
The shared package today only contains company-identity JSON and one mark
image. When it eventually gains real shared favicons, PWA icons, OG/Twitter
images, splash screens, shared CSS, or shared components, here is exactly
what adopting each looks like — deliberately small, one-time, mechanical
changes rather than another refactor:
contact info, and similarly any future data-only addition): add it to
the relevant JSON file, or add a new key to a new
branding/assets-manifest.json (this file doesn't exist yet — creating
it is itself the "adoption" step for asset *paths* like a favicon file
location). brand-loader.js already fetches assets-manifest.json if
present and exposes its contents as window.AFOLAOLA.assets — no
loader code changes needed when that day comes.
<link rel="icon"> / apple-touch-icon: browsers readthese directly from each HTML file's <head>, before and independent of
our JavaScript running, so they can't be made dynamic through the loader.
Once real shared files exist at e.g. branding/icons/..., update the
<link> tags in each HTML file's <head> to point there — one line per
file, no logic changes.
manifest.webmanifest icons: same reasoning — the browser'sinstallability check reads this file as static JSON, not through our
JS. Update the icons array's src values directly if/when shared PWA
icons replace OlaTime's own.
execute JavaScript, so <meta property="og:image"> must be a static tag
in each page's <head>, not injected at runtime. Add the tag once a
real shared OG image exists.
product-agnostic (not the case today — see the shared package's own
branding README on why product-specific styling isn't shared), the
cleanest fit for this codebase would be an additional branding/shared.css
loaded the same way as brand-loader.js, scoped to whatever it exports
(custom properties, utility classes) so it can be adopted incrementally
without touching OlaTime's own stylesheet.
None of this exists yet, and nothing here should be built ahead of time
against assets that don't exist — this section exists so the *next* person
touching this doesn't have to rediscover the shape of the problem.