Commit graph

12 commits

Author SHA1 Message Date
e2e4d7aef7 feat: add goal conversions section to dashboard
Query event:goal dimension with visitors, events, and conversion_rate
metrics via the existing POST /api/v2/query endpoint. Shows goal name,
unique conversions, and conversion rate percentage in a dedicated card.

- Add GoalConversion model with name, visitors, events, conversionRate
- Add 9th concurrent query in StatsRepository (try/catch for graceful
  degradation when no goals are configured)
- Add GoalConversionsSection composable; hidden when no goals exist
- Add goalConversions to DashboardData with default emptyList() for
  backward cache compatibility

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 15:31:54 +01:00
26467d9047 feat: add favicons, drag-to-reorder sites, and SVG support
- Load site favicons (svg → png → ico fallback) via Coil 3 with
  SubcomposeAsyncImage; globe icon as final fallback
- Register SvgDecoder in ImplausiblyApp for SVG favicon support
- Add drag-to-reorder via sh.calvin.reorderable library with a
  drag handle per site row; order persisted to sort_order column
- Add sort_order column to stored_sites with schema migration (1.sqm)
- New SiteRepository methods: reorderSites(), deleteSitesForInstance(),
  getAllSites() now includes sort_order
- Dependencies: coil-compose, coil-network-okhttp, coil-svg,
  reorderable (all Apache 2.0)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 14:52:06 +01:00
9110af7b8f feat: unified site picker with validation and instance management
Replace the two-step instance/site switching flow with a single Site
Picker screen that shows all sites grouped by instance. One tap to
switch to any site regardless of which instance it belongs to.

- Add SitePickerScreen with grouped LazyColumn (instance headers +
  site rows), inline add/edit/delete, current selection highlight
- Validate new site IDs against the Plausible API before adding
- Support deleting instances with cascading site removal
- Simplify Dashboard to single switch button
- Simplify Setup to add-only (no instance picker), with back arrow
- Remove old SiteListScreen/SiteListViewModel (replaced by SitePicker)
- Add selectAll query to StoredSite.sq, getAllSites/deleteSitesForInstance
  to SiteRepository

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 13:59:52 +01:00
ebe1d18123 feat: add open-in-browser button to dashboard
Opens {baseUrl}/{siteId} in the default browser so users can quickly
access the full Plausible web interface for their site.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 17:53:53 +01:00
8d92720d63 fix: UX improvements from audit
- Remove dead-end back arrow from dashboard; sites and instances
  are accessible via globe and swap icons in the top bar
- Replace gear icon with swap icon (SwapHoriz) for switching instances
- Require successful connection test before enabling Save
- Add API key visibility toggle on setup form
- Add delete confirmation dialog on site list
- Fix chart double-modifier bug (height applied twice)
- Add empty states for chart ("No visitor data") and dimension
  sections ("No data for this period") instead of silently hiding

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 17:34:08 +01:00
3e0ad6c350 fix: clone navigation uses single setup route with optional params
Previous approach had two separate composable entries for setup and
setup-with-clone-params, which Jetpack Navigation couldn't distinguish.
Now uses one route with nullable query param arguments and defaultValue
null, so navigating to "setup" opens fresh and "setup?cloneInstanceId=
...&cloneSiteId=..." opens pre-filled.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 17:23:56 +01:00
1434f2f842 feat: clone instance with full details for editing
Clone button on site list now navigates to setup screen pre-filled
with the source instance's URL, API key, and site ID. User can edit
all fields and save as a new instance. API key is resolved from
encrypted storage via the ViewModel, never passed through nav args.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 17:20:28 +01:00
4eef2ac168 feat: add clone button to site list
Copies the site ID into the add field so users can duplicate an
existing site and just change the ID. Useful when autodiscovery is
unavailable and multiple sites share the same instance credentials.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 17:13:57 +01:00
ebdf72e2c1 fix: smart navigation waits for DataStore before routing
Use flow.first() instead of collectAsState(initial = null) to
determine start destination. The previous approach fired immediately
with the initial null value before DataStore loaded from disk, always
routing to Setup. Now the loading screen suspends until preferences
are actually read.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 17:02:16 +01:00
54a5b38fc6 feat: add countries, devices, browsers, OS dashboard sections
Add 4 new dimension queries (concurrent with existing 4, total 8) to
the dashboard: countries, devices, browsers, and operating systems.
All reuse the existing DimensionSection component with proportional
progress bars.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 16:57:38 +01:00
d9e4b18a52 fix: API compatibility, smart navigation, and dashboard controls
- Fix Plausible API v2 compatibility: move `limit` into `pagination`
  object, add `expectSuccess = true` to Ktor client so API errors are
  surfaced instead of "Illegal input" serialization errors, configure
  `explicitNulls = false` to avoid sending null fields
- Add smart start destination: app checks DataStore on launch and
  navigates directly to dashboard if an instance/site was previously
  selected, skipping the setup screen
- Add settings and sites icons to dashboard top bar for navigating
  back to instance management and site list
- Add site editing (inline rename) to site list screen
- Fix DateRangeSelector crash caused by sealed class data object
  initialization issue in Compose lambda captures

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 16:50:06 +01:00
aa66172d58 feat: implement Phase 1 MVP of Implausibly
Working Android dashboard app for self-hosted Plausible Analytics CE.
Connects to Plausible API v2 (POST /api/v2/query), displays top stats,
visitor chart, top sources, and top pages with date range selection.

Architecture: Kotlin + Jetpack Compose + Material 3 + Hilt + Ktor +
SQLDelight + EncryptedSharedPreferences. Single :app module, four-layer
unidirectional data flow (UI → ViewModel → Repository → Data).

Includes: instance management, site list, caching with TTL per date
range, encrypted API key storage, custom Canvas visitor chart,
pull-to-refresh, and unit tests for API, cache, repository, and
domain model layers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 16:46:08 +01:00