PostHogAnalytics.shared.initialize() was calling the Kotlin actual object which
is a no-op on iOS. Replaced with AnalyticsManager.shared.configure() so the
PostHog SDK actually initializes. Also switched to anonymous-only person profiles.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
TokenManager.keychainDelegate was never set, so all Keychain reads/writes
failed silently. Tokens couldn't be persisted or loaded on app restart,
and TokenStorage.getToken() returned nil during task completion flows.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The createCompletionWithImages method used client.post() with a manual
MultiPartFormDataContent body, which can override the Authorization header.
Switch to submitFormWithBinaryData() (matching DocumentApi's working pattern)
which properly separates form data from request headers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Change ApiConfig.CURRENT_ENV from LOCAL to DEV
- Add "Google Sign-In Error" and "Sign in with Google" to Localizable.xcstrings
- Reorder Xcode project build settings (cosmetic)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
27 new tests covering SubscriptionCacheWrapper: currentTier derivation,
shouldShowUpgradePrompt with per-resource limits and boundary conditions,
canShareResidence/canShareContractor gating, and deprecated prompt property.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SKIE doesn't expose Kotlin default parameters to Swift, so isCacheValid calls
need explicit ttlMs argument. Renamed struct-based screen objects to avoid
ambiguity with class-based PageObjects (LoginScreenObject, RegisterScreenObject,
MainTabScreenObject).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create unit tests: DataLayerTests (27 tests for DATA-001–007), DataManagerExtendedTests
(20 tests for TASK-005, TASK-012, TCOMP-003, THEME-001, QA-002), plus ValidationHelpers,
TaskMetrics, StringExtensions, DoubleExtensions, DateUtils, DocumentHelpers, ErrorMessageParser
- Create UI tests: AuthenticationTests, PasswordResetTests, OnboardingTests, TaskIntegration,
ContractorIntegration, ResidenceIntegration, DocumentIntegration, DataLayer, Stability
- Add UI test framework: AuthenticatedTestCase, ScreenObjects, TestFlows, TestAccountManager,
TestAccountAPIClient, TestDataCleaner, TestDataSeeder
- Add accessibility identifiers to password reset views for UI test support
- Add greenfield test plan CSVs and update automated column for 27 test IDs
- All 297 unit tests pass across 60 suites
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remediate all P0-S priority findings from cross-platform architecture audit:
- Harden token storage with EncryptedSharedPreferences (Android) and Keychain (iOS)
- Add SSL pinning and certificate validation to API clients
- Fix subscription cache race conditions and add thread-safe access
- Add input validation for document uploads and file type restrictions
- Refactor DocumentApi to use proper multipart upload flow
- Add rate limiting awareness and retry logic to API layer
- Harden subscription tier enforcement in SubscriptionHelper
- Add biometric prompt for sensitive actions (Login, Onboarding)
- Fix notification permission handling and device registration
- Add UI test infrastructure (page objects, fixtures, smoke tests)
- Add CI workflow for mobile builds
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove old PostHogAnalytics singleton and replace with guide-based
two-file architecture: AnalyticsManager (singleton wrapper with super
properties, session replay, opt-out, subscription funnel) and
AnalyticsEvent (type-safe enum with associated values).
Key changes:
- New API key, self-hosted analytics endpoint
- All 19 events ported to type-safe AnalyticsEvent enum
- Screen tracking via AnalyticsManager.Screen enum + SwiftUI modifier
- Remove all identify() calls — fully anonymous analytics
- Add lifecycle hooks: flush on background, update super properties on foreground
- Add privacy opt-out toggle in Settings
- Subscription funnel methods ready for IAP integration
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Keep only implode, firework, starburst, and ripple animations.
Remove slide, fade, scale, flip, bounce, spring, rotation, morph,
confetti, and cascade animations for a more focused experience.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Animation Testing:
- Add AnimationTesting module with 14 animation types for task completion
- Include 4 celebration animations: Implode, Firework, Starburst, Ripple
- Card shrinks, shows checkmark with effect, then moves to next column
- Extended timing (2.2s) for celebration animations
Task Count Fix:
- Fix "7 Days" and "30 Days" counts on residence cards and dashboard
- Previously used API column membership (30-day "due soon" column)
- Now calculates actual days until due from task's effectiveDueDate
- Correctly counts tasks due within 7 days vs 8-30 days
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create BackgroundTaskManager that schedules refresh at random time
between 12:00 AM - 4:00 AM local time
- Fetches tasks from API, writes to DataManager and shared App Group
- Reloads widget timelines after refresh
- Register task in AppDelegate, schedule when app goes to background
- Add BGTaskSchedulerPermittedIdentifiers and fetch/processing modes to Info.plist
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add API column name constants to WidgetDataManager (overdueColumn,
dueWithin7DaysColumn, due8To30DaysColumn, etc.)
- Update DataManagerObservable to use WidgetDataManager column constants
- Remove duplicate ResidenceTaskStats struct, use TaskMetrics everywhere
- Delete TaskStatsCalculator.swift (consolidated into WidgetDataManager)
- Rename confusing flags: isUpcoming → isDueWithin7Days, isLater → isDue8To30Days
- Add comprehensive unit tests for TaskMetrics and WidgetTask
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The widget was calculating overdue count from due dates, which didn't
account for in-progress tasks. Now uses the isOverdue flag from kanban
categorization, ensuring the count matches the displayed tasks.
Before: 3 overdue (date-based calc included in-progress tasks)
After: 2 overdue (matches kanban which excludes in-progress)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The toDate() extension was only parsing "yyyy-MM-dd" format, causing
ISO datetime strings like "2025-01-02T00:00:00Z" to fail parsing and
display as raw strings. Now extracts the date part before the "T"
before parsing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix residence detail not updating after edit:
- DataManager.updateResidence() now updates both _residences and _myResidences
- ResidenceViewModel auto-updates selectedResidence when data changes
- No pull-to-refresh needed after editing
- Add widget theme support:
- Widgets now use user's selected theme via App Group UserDefaults
- ThemeManager has simplified version for widget extension context
- Added WIDGET_EXTENSION compiler flag to CaseraExtension target
- Redesign widget views with organic aesthetic:
- Updated FreeWidgetView, SmallWidgetView, MediumWidgetView, LargeWidgetView
- Created OrganicTaskRowView, OrganicStatsView, OrganicStatPillWidget
- Document patterns in CLAUDE.md:
- Added Mutation & Auto-Update Pattern section
- Added iOS Shared Components documentation
- Documented reusable buttons, forms, empty states, cards, modifiers
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- APILayer.getResidence() now checks both residences and myResidences caches
- Home screen loads myResidences, but getResidence() only checked residences,
causing unnecessary network calls on every residence detail visit
- Remove flawed iOS-side cache check in TaskViewModel that had race condition
with Kotlin StateFlow - let Kotlin handle all caching logic
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add SecureIconTextField component that includes the eye toggle button
inside the text field (matching RegisterView's OrganicSecureField).
Update LoginView to use SecureIconTextField instead of IconTextField
with an external button, ensuring consistent UI across auth screens.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update cost format string to use pre-formatted currency
- Add new localization entries for refactored components
- Remove unused "Pros" string (renamed to "Contractors")
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Create centralized shared utilities in iosApp/Shared/:
- Extensions: ViewExtensions, DateExtensions, StringExtensions, DoubleExtensions
- Components: FormComponents, SharedEmptyStateView, ButtonStyles
- Modifiers: CardModifiers
- Utilities: ValidationHelpers, ErrorMessages
Migrate existing views to use shared utilities:
- LoginView: Use IconTextField, FieldLabel, FieldError, OrganicPrimaryButton
- TaskFormView: Use .loadingOverlay() modifier
- TaskCard/DynamicTaskCard: Use .toFormattedDate() extension
- CompletionCardView: Use .toCurrency() (with KotlinDouble support)
- ResidenceDetailView: Use OrganicEmptyState, StandardLoadingView
- Profile views: Use .standardFormStyle(), .sectionBackground()
- Form views: Use consistent form styling modifiers
Benefits:
- Eliminates ~180 lines of duplicate code
- Consistent styling across all forms and components
- KotlinDouble extensions for seamless KMM interop
- Single source of truth for UI patterns
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create shared TaskStatsCalculator for consistent date bucket logic
- Fix widget stats to use exclusive buckets (overdue | 7 days | 30 days)
- Update labels: "This Week" → "Next 7 Days" / "7 Days"
- Large widget now shows Overdue, 7 Days, 30 Days (removed Total)
- Rename "Pros" tab to "Contractors"
- Remove red pulsing ring from residence card icons
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix TokenStorage.getToken() returning stale cached token after login/logout
- Add comprehensive ErrorMessageParser with 80+ error code mappings
- Add Suite9 and Suite10 UI test files for E2E integration testing
- Fix accessibility identifiers in RegisterView and ResidenceFormView
- Fix UITestHelpers logout to target alert button specifically
- Update various UI components with proper accessibility identifiers
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Compute task stats locally from kanban data for both summary card and residence cards
- Filter out completed_tasks and cancelled_tasks columns from counts
- Use startOfDay for accurate date comparisons (overdue, due this week, next 30 days)
- Add parseDate helper to DateUtils
- Make address tappable to open in Apple Maps
- Remove navigation title from residences list
- Update CLAUDE.md with Go backend references and DataManager architecture
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
New strings for redesigned views:
- "Welcome to Your Space" - empty residences view
- "Your Home Dashboard" - main home view title
- "Primary" - residence badge label
- "Overdue" - task status label
- Updated task templates count format
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The crash occurred when UIKit's didSelectRow callback fired during
SwiftUI view teardown, causing an array index out of bounds error.
Fixes:
- Use Identifiable struct for stable ForEach identity
- Hide picker before dismissing to prevent race condition
- Add .id() modifier for stable picker identity
- Disable interactive dismiss to prevent mid-scroll dismissal
- Add small delay before dismiss callbacks
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove invalid 'summary' parameter from TaskColumnsResponse calls
- Remove invalid 'totalDueSoon' parameter from TotalSummary call
- Fix TimePickerSheet crash when scrolling by properly initializing @State
with State(initialValue:) and pre-computing hours array
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove summary field from MyResidencesResponse and TaskColumnsResponse
- Update HomeScreen and ResidencesScreen to observe DataManager.totalSummary
- Load tasks when residence views appear to ensure accurate summary
- Add pull-to-refresh for tasks on ResidencesScreen
- Update iOS views to use client-side calculated summary
Summary is now calculated via refreshSummaryFromKanban() from cached
kanban data. This ensures summary is always up-to-date after CRUD
operations and when residence views are shown.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The setAllTasks() function was not calling refreshSummaryFromKanban()
after loading kanban data, so the summary statistics (totalTasks,
totalOverdue, etc.) were never calculated - they stayed at zero.
Also switch API environment back to DEV.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update ErrorResponse model to make detail and statusCode optional since
backend now returns simple {"error": "message"} format
- Update AuthApi to parse actual backend error messages instead of generic
"Registration failed"/"Login failed" strings
- Update ErrorParser to prioritize the "error" field and add fallback for
simple error map responses
- Update iOS ViewModels (Login, Register, AppleSignIn) to properly handle
400 and 409 status codes by displaying backend error messages
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Shows a subtle "Swipe to see your tasks" hint centered on the first
column (Overdue) when it's empty but other columns have tasks. The
hint uses the theme primary color and scrolls with the column.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Creates a reusable KeyboardDismissToolbar view modifier that adds a
"Done" button to dismiss keyboards that don't have a return key.
Applied to all numeric keyboards (numberPad, decimalPad, phonePad)
and multi-line text inputs (TextEditor, TextField with axis: .vertical).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add CompleteTaskScreen for full-screen task completion (matches iOS CompleteTaskView)
- Add ManageUsersScreen for full-screen user management (matches iOS ManageUsersView)
- Add PhotoViewerScreen with swipeable gallery and pinch-to-zoom
- Add UpgradeScreen for full-screen subscription flow (matches iOS UpgradeFeatureView)
- Add navigation routes for new screens (CompleteTaskRoute, ManageUsersRoute, PhotoViewerRoute, UpgradeRoute)
- Wire navigation callbacks into AllTasksScreen, ResidenceDetailScreen, ProfileScreen
- Add overdue count with red color to Summary Card
- Add pulsing animation to residence cards when overdue
- Add property type, isPrimary star, and street address to Residence Card
- Add Edit Profile, Privacy Policy, and App Version to ProfileScreen
- Add string resources for new UI elements and localization
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move Easy Share (.casera file) section above Share Code section in the
invite users dialog. Both platforms now have consistent UI with both
buttons using the same filled button style.
iOS changes:
- Move share functionality into ManageUsersView
- Remove share button from ResidenceDetailView toolbar
- Redesign ShareCodeCard with Easy Share on top
Android changes:
- Update ManageUsersDialog with matching layout
- Connect share package callback to existing share function
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add calculateSummaryFromKanban() to compute summary stats from cached kanban data
- Add refreshSummaryFromKanban() called after task CRUD operations
- Fix column name matching to use API format (e.g., "overdue_tasks" not "overdue")
- Fix tasksDueNextMonth to only include due_soon tasks (not upcoming)
- Update TaskResponse computed properties to resolve from DataManager cache
- Update iOS task cards to use computed properties for priority/frequency/category
- This enables API to skip preloading lookups for better performance
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Load contractors during initializeLookups() when authenticated
- Update getContractorsByResidence() to filter from cache instead of making network call
- Only fetch from API if cache is empty or stale
This eliminates the /contractors/by-residence/{id}/ call when visiting residences.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add customIntervalDays field to Kotlin models (TaskResponse, TaskCreateRequest, TaskUpdateRequest)
- Update Android AddTaskDialog to show interval field only for "Custom" frequency
- Update Android EditTaskScreen for custom frequency support
- Update iOS TaskFormView for custom frequency support
- Fix preview data in TaskCard and TasksSection to include new field
- Add customIntervalDays to OnboardingFirstTaskView
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add backgroundColor parameter to CaseraIconView and PulsingIconView
- Update ResidenceCard to pass Color.appPrimary for themed icon background
- Icon now matches the app's current theme like PropertyHeaderCard
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>