Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .env.ios
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# iOS / Capacitor build environment
# Used by: VITE_IOS_BUILD=true npm run build
# or: vite build --mode ios (requires .env.ios to be named .env.ios)
#
# Supabase vars are intentionally absent — iOS build is local-only storage.
# No user data leaves the device, keeping App Store privacy declarations clean.

VITE_IOS_BUILD=true
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed
- Reverted Xcode project filename from `TimeTrackerPro.xcodeproj` back to `App.xcodeproj`
— `ios/App/App.xcodeproj/` (Capacitor CLI hardcodes `App.xcodeproj`; renaming it broke `npm run sync:ios` with ENOENT on `project.pbxproj`; the Xcode target/product name remains "TimeTrackerPro")
- Fixed `open:ios` npm script pointing to the old renamed project path
— `package.json` (updated from `TimeTrackerPro.xcodeproj/project.xcworkspace` to `App.xcodeproj/project.xcworkspace`)
- Fixed `vite-plugin-pwa` not disabling PWA during iOS builds due to wrong option name
— `vite.config.ts` (changed `disabled: isIosBuild` → `disable: isIosBuild`; plugin uses `disable`, not `disabled`, so `sw.js`, `workbox-*.js`, and `manifest.webmanifest` were being generated and synced into the iOS bundle on every build)
- Fixed outdated device capability declaration in iOS Info.plist
— `ios/App/App/Info.plist` (changed `UIRequiredDeviceCapabilities` from `armv7` to `arm64`; Apple dropped 32-bit support in iOS 11 and this stale Capacitor template value can fail App Store validation)
- Removed stale `-DCOCOAPODS` Swift compiler flag from Xcode Debug build settings
— `ios/App/App.xcodeproj/project.pbxproj` (project uses SPM, not CocoaPods; flag was a leftover from Capacitor's original CocoaPods template with no runtime effect)

### Added
- Capacitor iOS native app scaffolding (Phase 2)
— `capacitor.config.ts`, `ios/` Xcode project, `package.json` (appId `com.adamjolicoeur.timetrackerpro`, iOS 15+ minimum via SPM, `sync:ios` script combines `build:ios` + `cap sync ios`; `ios/App/App/public` gitignored and regenerated on every sync)
- Renamed Xcode target and product from "App" to "TimeTrackerPro"
— `ios/App/App.xcodeproj/project.pbxproj` (updated target name, productName, product path, and configuration list comments so Xcode shows "TimeTrackerPro"; project file itself remains `App.xcodeproj` per Capacitor's requirement)
- Capacitor iOS integration prep (Phase 1)
— `src/App.tsx`, `src/components/Navigation.tsx`, `src/pages/Settings.tsx`, `vite.config.ts`, `.env.ios`, `index.html`, `package.json` (BrowserRouter → HashRouter for filesystem loading; `VITE_IOS_BUILD` flag disables PWA SW and hides auth/sync UI in native builds; CSP updated with `capacitor://localhost`; `build:ios` npm script added)
- `PageLayout` shared layout component for consistent page chrome
— `src/components/PageLayout.tsx`, `src/components/PageLayout.test.tsx` (standardizes title + optional actions slot across all six pages; all page components migrated to use it)

### Fixed
- Carry over incomplete GFM checklist items as todo tasks when a day is archived
— `src/contexts/TimeTrackingContext.tsx`, `src/contexts/TimeTracking.test.tsx` (unchecked `- [ ]` items from task descriptions are now extracted and appended as new todo tasks on archive; unique IDs and safe functional setState ensure no data loss on rollback)

### Accessibility
- Added `aria-label` to all icon-only buttons whose visible text label is hidden on mobile viewports: Restore and Edit in `ArchiveItem`, Restore/Delete/Edit in `ArchiveEditDialog` header, per-task Edit/Delete in `ArchiveEditDialog` task table, and Edit/Delete in `ProjectManagement`
- Replaced `focus:outline-none` with `focus-visible:outline-none` + `focus-visible:ring-2 focus-visible:ring-ring` on Radix `TabsTrigger` elements in `ArchiveItem` — the browser focus ring was previously stripped for all input methods; it is now suppressed only for pointer clicks while remaining fully visible for keyboard navigation
Expand Down
43 changes: 40 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# CLAUDE.md - AI Assistant Codebase Guide

**Last Updated:** 2026-03-09
**Version:** 2.0.0
**Last Updated:** 2026-04-24
**Version:** 2.1.0

TimeTracker Pro is a React 18 + TypeScript time tracking PWA for freelancers and consultants, with dual storage (localStorage guest mode and optional Supabase cloud sync).
TimeTracker Pro is a React 18 + TypeScript time tracking PWA for freelancers and consultants, with dual storage (localStorage guest mode and optional Supabase cloud sync). A native iOS app is also available via Capacitor.

---

Expand All @@ -19,6 +19,7 @@ TimeTracker Pro is a React 18 + TypeScript time tracking PWA for freelancers and
| Forms | React Hook Form + Zod |
| Backend | Supabase (optional) or localStorage |
| PWA | Vite PWA Plugin + Workbox |
| Native iOS | Capacitor 8 (@capacitor/core + @capacitor/ios) |
| Testing | Vitest + React Testing Library + Playwright |

---
Expand Down Expand Up @@ -57,6 +58,42 @@ export const MyComponent = () => {
| `src/lib/supabase.ts` | Supabase client configuration and caching |
| `src/config/categories.ts` | Default category definitions |
| `src/config/projects.ts` | Default project definitions |
| `src/components/PageLayout.tsx` | Shared page chrome (title + optional actions slot) |
| `capacitor.config.ts` | Capacitor iOS configuration |
| `.env.ios` | iOS build env (VITE_IOS_BUILD=true, no Supabase) |

---

## Capacitor iOS Build

The app ships as both a PWA and a native iOS app via Capacitor 8.

**Key differences in iOS builds:**

- `VITE_IOS_BUILD=true` disables the Vite PWA service worker and hides auth/sync UI (UserMenu, AuthDialog, SyncStatus, InstallPrompt, UpdateNotification)
- Routing uses `HashRouter` (required — Capacitor loads from filesystem, not a server)
- CSP includes `capacitor://localhost` for WKWebView asset loading
- Data storage is localStorage-only (no Supabase keys in `.env.ios`)

**iOS npm scripts:**

```bash
npm run build:ios # vite build --mode ios (outputs to dist/)
npm run sync:ios # build:ios + npx cap sync ios (copies dist/ into ios/ project)
```

**Working with the Xcode project:**

- `ios/App/App/public` is gitignored — it is regenerated by `cap sync ios`
- Open `ios/App/App.xcodeproj/project.xcworkspace` in Xcode (or run `npm run open:ios`)
- Bundle ID: `com.adamjolicoeur.timetrackerpro`
- Minimum iOS version: 26 (enforced via Package.swift SPM)

**When adding new features:**

- Gate any web-only UI (PWA install, auth, sync) behind `import.meta.env.VITE_IOS_BUILD !== "true"`
- Avoid `window.location.reload()` in iOS paths — use `window.location.replace()` to avoid interrupting the Capacitor JS bridge
- Test localStorage-only flow (no Supabase) before marking iOS features complete

---

Expand Down
78 changes: 43 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

A modern, feature-rich Progressive Web App (PWA) for time tracking built with React, TypeScript, and Tailwind CSS. Installable on desktop and mobile devices with full offline support. Perfect for freelancers, consultants, and professionals who need to track time, manage projects, and generate invoices.

![React](https://img.shields.io/badge/react-%2320232a.svg?style=for-the-badge&logo=react&logoColor=%2361DAFB) ![TypeScript](https://img.shields.io/badge/typescript-%23007ACC.svg?style=for-the-badge&logo=typescript&logoColor=white) ![Vite](https://img.shields.io/badge/vite-%23646CFF.svg?style=for-the-badge&logo=vite&logoColor=white) ![TailwindCSS](https://img.shields.io/badge/tailwindcss-%2338B2AC.svg?style=for-the-badge&logo=tailwind-css&logoColor=white) ![Supabase](https://img.shields.io/badge/Supabase-3ECF8E?style=for-the-badge&logo=supabase&logoColor=white) ![PWA](https://img.shields.io/badge/PWA-Enabled-5A0FC8?style=for-the-badge&logo=pwa&logoColor=white)
![React](https://img.shields.io/badge/react-%2320232a.svg?style=for-the-badge&logo=react&logoColor=%2361DAFB) ![TypeScript](https://img.shields.io/badge/typescript-%23007ACC.svg?style=for-the-badge&logo=typescript&logoColor=white) ![Vite](https://img.shields.io/badge/vite-%23646CFF.svg?style=for-the-badge&logo=vite&logoColor=white) ![TailwindCSS](https://img.shields.io/badge/tailwindcss-%2338B2AC.svg?style=for-the-badge&logo=tailwind-css&logoColor=white) ![Supabase](https://img.shields.io/badge/Supabase-3ECF8E?style=for-the-badge&logo=supabase&logoColor=white) ![PWA](https://img.shields.io/badge/PWA-Enabled-5A0FC8?style=for-the-badge&logo=pwa&logoColor=white) ![Capacitor](https://img.shields.io/badge/Capacitor-119EFF?style=for-the-badge&logo=capacitor&logoColor=white)

## 📑 Table of Contents

Expand Down Expand Up @@ -89,6 +89,7 @@ TimeTracker Pro is a professional time tracking application that helps you monit
- **Mobile Optimized** - Touch-friendly with bottom navigation
- **Auto-Updates** - New versions install seamlessly
- **Cross-Platform** - Works on Windows, Mac, Linux, iOS, and Android
- **Native iOS App** - Distributed as a native iOS app via Capacitor (App Store / sideload)

---

Expand Down Expand Up @@ -150,9 +151,7 @@ That's it! No complex configuration required.
1. Click "Start Day" button to begin tracking
2. The timer starts automatically

**Throughout the Day:**
3. Click "New Task" to create a task
4. Fill in task details:
**Throughout the Day:** 3. Click "New Task" to create a task 4. Fill in task details:

- **Title** (required) - Brief description of the work
- **Description** (optional) - Detailed notes with markdown support
Expand All @@ -162,10 +161,7 @@ That's it! No complex configuration required.
1. Task duration calculates automatically
2. Create new tasks as you switch between different work items

**Evening:**
7. Click "End Day" when you're finished working
8. Review your day summary (total time, revenue, task breakdown)
9. Click "Post Time to Archive" to save permanently
**Evening:** 7. Click "End Day" when you're finished working 8. Review your day summary (total time, revenue, task breakdown) 9. Click "Post Time to Archive" to save permanently

**Ongoing:**

Expand Down Expand Up @@ -241,7 +237,7 @@ Task descriptions support **GitHub Flavored Markdown (GFM)** for rich formatting

**Supported Features:**

- **Bold** and *italic* text
- **Bold** and _italic_ text
- Lists (bulleted and numbered)
- Task lists with checkboxes
- Tables
Expand All @@ -259,11 +255,13 @@ Task descriptions support **GitHub Flavored Markdown (GFM)** for rich formatting
**Attendees:** John, Sarah, Mike

**Topics Discussed:**

1. Q1 project timeline
2. Budget approval
3. Design mockups

**Action Items:**

- [ ] Send meeting minutes
- [ ] Update project roadmap
- [ ] Schedule follow-up
Expand Down Expand Up @@ -293,7 +291,7 @@ This renders beautifully in the task view with proper formatting and styling.
2. Tap the Share button (□↑)
3. Scroll and tap "Add to Home Screen"
4. Tap "Add" to confirm
- Toggle "Open as Web App" to "On"
- Toggle "Open as Web App" to "On"
5. Find the app icon on your home screen

**Android (Chrome):**
Expand Down Expand Up @@ -409,6 +407,10 @@ npm run screenshots:install # Install Playwright browsers (first time)
npm run screenshots # Capture PWA screenshots (headless)
npm run screenshots:headed # Capture screenshots with visible browser

# iOS / Capacitor
npm run build:ios # Build for iOS (vite --mode ios, no PWA/auth UI)
npm run sync:ios # build:ios + cap sync ios (copies dist into Xcode project)

# CSV Import Testing
npm run test-csv-import # Test standard CSV import
npm run test-full-import # Test full CSV import functionality
Expand Down Expand Up @@ -444,6 +446,11 @@ npm run test-error-handling # Test CSV error handling
- **Local Storage** - Browser storage for offline data persistence
- **Supabase** (optional) - PostgreSQL database and authentication

**Native Mobile:**

- **Capacitor 8** - Native iOS app wrapper (appId: `com.adamjolicoeur.timetrackerpro`, iOS 15+ minimum)
- **iOS-only build mode** - `VITE_IOS_BUILD` flag disables PWA/auth UI for native distribution

**PWA & Performance:**

- **Vite PWA Plugin** - Service worker and manifest generation
Expand Down Expand Up @@ -476,15 +483,15 @@ Data persistence is abstracted through a service interface with two implementati

```typescript
interface DataService {
loadCurrentDay(): Promise<DayData>
saveCurrentDay(data: DayData): Promise<void>
loadArchivedDays(): Promise<ArchivedDay[]>
saveArchivedDays(days: ArchivedDay[]): Promise<void>
loadCurrentDay(): Promise<DayData>;
saveCurrentDay(data: DayData): Promise<void>;
loadArchivedDays(): Promise<ArchivedDay[]>;
saveArchivedDays(days: ArchivedDay[]): Promise<void>;
// ... other methods
}

// Factory pattern selects implementation
const service = createDataService(isAuthenticated)
const service = createDataService(isAuthenticated);
// Returns: LocalStorageService OR SupabaseService
```

Expand Down Expand Up @@ -958,14 +965,14 @@ export default {
color: '#333',
a: {
color: '#3182ce',
'&:hover': { color: '#2c5282' },
},
},
},
},
},
},
}
'&:hover': { color: '#2c5282' }
}
}
}
}
}
}
};
```

**3. Component-Specific Overrides:**
Expand All @@ -978,7 +985,7 @@ export default {
<a href={href} className="custom-link-style">
{children}
</a>
),
)
}}
>
{content}
Expand All @@ -995,7 +1002,7 @@ export default {
// 3. Use shadcn/ui components
// 4. Import in parent component

import { MyFeature } from "@/components/MyFeature";
import { MyFeature } from '@/components/MyFeature';
```

**Adding a New Page:**
Expand Down Expand Up @@ -1069,25 +1076,26 @@ const MyPage = lazy(() => import("./pages/MyPage"));
For a detailed list of changes, new features, and bug fixes, see [CHANGELOG.md](CHANGELOG.md).

**Recent Updates:**

- Native iOS app via Capacitor 8 — Xcode project scaffolded, iOS-specific build mode (`build:ios` / `sync:ios`) with auth/PWA UI disabled for native distribution
- `PageLayout` shared component standardizes page chrome (title + actions) across all pages
- Incomplete checklist items in task descriptions are now carried over as todo tasks when a day is archived
- Improved Weekly Report error messages to distinguish Gemini API failure modes (rate limit, quota, overload, key issues)
- Fixed Weekly Report for authenticated users (data now sourced from Supabase, not localStorage only)
- Native HTML5 time inputs for intuitive, accessible time selection
- Consistent UX with date inputs across all dialogs
- Mobile-optimized with browser-native time pickers
- Full keyboard navigation and screen reader support

---

## 📱 iOS Screenshots

| View | Image |
| -- | -- |
| Dashboard | <img src="screenshots/iOS/01TimeTrackerPro-iOS.png" width="200" /> |
| View | Image |
| --------------------- | ------------------------------------------------------------------ |
| Dashboard | <img src="screenshots/iOS/01TimeTrackerPro-iOS.png" width="200" /> |
| Time Entry - Markdown | <img src="screenshots/iOS/02TimeTrackerPro-iOS.png" width="200" /> |
| Time Entry - Preview | <img src="screenshots/iOS/03TimeTrackerPro-iOS.png" width="200" /> |
| Active Tasks | <img src="screenshots/iOS/04TimeTrackerPro-iOS.png" width="200" /> |
| Day Ended | <img src="screenshots/iOS/06TimeTrackerPro-iOS.png" width="200" /> |
| Archive | <img src="screenshots/iOS/07TimeTrackerPro-iOS.png" width="200" /> |
| Time Entry - Preview | <img src="screenshots/iOS/03TimeTrackerPro-iOS.png" width="200" /> |
| Active Tasks | <img src="screenshots/iOS/04TimeTrackerPro-iOS.png" width="200" /> |
| Day Ended | <img src="screenshots/iOS/06TimeTrackerPro-iOS.png" width="200" /> |
| Archive | <img src="screenshots/iOS/07TimeTrackerPro-iOS.png" width="200" /> |

---

Expand Down
33 changes: 33 additions & 0 deletions capacitor.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { CapacitorConfig } from "@capacitor/cli";

const config: CapacitorConfig = {
appId: "com.adamjolicoeur.timetrackerpro",
appName: "TimeTracker Pro",
webDir: "dist",

ios: {
// Respect safe-area insets (notch, home indicator) automatically
contentInset: "always",
// Match the app's background so there is no flash during launch
backgroundColor: "#ffffff",
// Prevent the WKWebView itself from scrolling — the app manages scroll internally
scrollEnabled: false,
// Restricts navigation to the app bundle; blocks accidental external loads
limitsNavigationsToAppBoundDomains: true,
},

experimental: {
ios: {
spm: {
// iOS 26 requires PackageDescription 6.2; without this cap sync defaults to 5.9
swiftToolsVersion: "6.2",
},
},
},

plugins: {
// Placeholder — native plugin config goes here in Phase 4 (widget bridge)
},
};

export default config;
13 changes: 7 additions & 6 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,16 @@
<meta name="application-name" content="TimeTracker Pro" />

<!-- Content Security Policy -->
<!-- capacitor://localhost is included for WKWebView (iOS Capacitor) -->
<meta
http-equiv="Content-Security-Policy"
content="
default-src 'self';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
connect-src 'self' https://*.supabase.co wss://*.supabase.co https://generativelanguage.googleapis.com;
font-src 'self' data:;
img-src 'self' data: blob:;
default-src 'self' capacitor://localhost;
script-src 'self' 'unsafe-inline' capacitor://localhost;
style-src 'self' 'unsafe-inline' capacitor://localhost;
connect-src 'self' capacitor://localhost https://*.supabase.co wss://*.supabase.co https://generativelanguage.googleapis.com;
font-src 'self' data: capacitor://localhost;
img-src 'self' data: blob: capacitor://localhost;
frame-src 'none';
object-src 'none';
base-uri 'self';
Expand Down
13 changes: 13 additions & 0 deletions ios/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
App/build
App/Pods
App/output
App/App/public
DerivedData
xcuserdata

# Cordova plugins for Capacitor
capacitor-cordova-ios-plugins

# Generated Config files
App/App/capacitor.config.json
App/App/config.xml
Loading
Loading