RES-1995 rework groups page to have a map of groups#744
Open
edwh wants to merge 77 commits into
Open
Conversation
# Conflicts: # package-lock.json
This reverts commit 263e367.
This reverts commit e2cd10a.
This reverts commit 6022db1.
This reverts commit 35044ad.
This reverts commit 3c65b95.
# Conflicts: # package.json
# Conflicts: # app/Http/Controllers/API/GroupController.php # app/Http/Controllers/GroupController.php # app/User.php # resources/js/components/GroupsTable.vue # tests/Feature/Groups/APIv2GroupTest.php
When npm is backgrounded in docker_run.sh, neither public/hot nor public/build/manifest.json exists when php-fpm starts (~2min into startup). Every page request triggers a ViteManifestNotFoundException -> HTTP 500, so the health check never passes. Fix: run npm install + npm run build on the CI host BEFORE starting Docker. public/build/manifest.json then exists in the volume-mounted workspace from the moment php-fpm starts handling requests. Also add node_modules caching so subsequent runs are fast (seconds vs minutes). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MySQL 8.0 enables binary logging by default, which requires SUPER privilege to create triggers. The restarters user lacks SUPER, so migrate:fresh --seed was failing with SQLSTATE 1419, leaving the DB empty and causing HTTP 500. Moving log_bin_trust_function_creators=1 into mysql/my.cnf ensures it's applied at MySQL startup, before docker_run.sh runs migrations. Previously it was only set in the CI "Setup application" step — but that step runs after "Wait for services", which never completed due to the migration failure. Also capture storage/logs/laravel.log as a CI artifact for future debugging. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
In CI (PHP-FPM), the file cache was returning __PHP_Incomplete_Class for stdClass objects in waste_stats[0], causing E_WARNING → ErrorException → HTTP 500 on every health check request. Root fix: computeStats() now stores waste_stats and device_count_status as plain PHP arrays instead of DB stdClass objects. No class deserialization needed at all. isStatsValid() now also checks waste_stats[0] is an array, so any cached data in old format (stdClass) is treated as invalid and recomputed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixometer::loginRegisterStats(): if Cache::put() fails after the cold-start lock (disk full, backend down), Cache::get() returns null and decorateStats(null) would hit a TypeError. Now falls back to computing uncached. decorateStats(): add ?? [] guard on waste_stats[0] access so a corrupt cache entry returns zeros rather than a fatal error. Trigger migration: attempt SET GLOBAL log_bin_trust_function_creators = 1 before creating triggers; silently catches SUPER-privilege denial so the migration still runs on hosts where the flag is already set in my.cnf (e.g. production). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The runtime `patch` call in docker_run.sh was a workaround: cweagans/composer-patches already manages the other vendor patches, so this one should live there too. cweagans applies patches on fresh installs (cache misses in CI) and the cached vendor already carries the patch from prior builds. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
edwh/laravel-drip was a personal fork. TheRestartProject/laravel-drip is the org-owned fork, currently at the Laravel 10 support commit. The existing laravel-drip-l11 composer patch supplies the L11/12/13 constraint, so behaviour is unchanged — just a cleaner provenance. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Key conflict resolutions:
- package.json: merged leaflet/vue2-leaflet map deps with L13 sentry/popperjs upgrades
- app.js: kept map component registrations (GroupMapAndList, GroupMarker, GroupInfoModal)
in L13's reorganised initializeJQuery() block; dropped duplicate jQuery init from
map branch's end-of-file
- GroupsPage.vue: merged images mixin from L13 with map imports from HEAD
- GroupsTable.vue: merged L13's GroupsTableFilters UI and images mixin with map branch's
row-hover events for map synchronisation
- GroupsTableFilters.vue: restored (was deleted in map branch; needed for NC filter bar)
- NetworkController: kept mapBounds calculation AND merged L13's stats/tags/networkData
- User.groupsNearby(): kept null check from map branch, used safer selectRaw from L13;
added image eager-loading (load('groupImage.image')) inside the null-check block
- Group resource: adopted L13's includeStats=false default (opt-in for perf)
- header.blade.php: used Vite @Vite() directive, kept leaflet CSS links
- networks/show.blade.php: kept map branch layout (sidebar + GroupMapAndList);
added show-filters, can-manage-tags, available-tags props for Phase 2 filter bar
- webpack.mix.js: accepted deletion (L13 uses Vite)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- GroupMapAndList: accept showFilters/canManageTags/availableTags props, pass them through to GroupsTable as search/showTags/allGroupTags - GroupsTable: add search/networks/allGroupTags/showTags props; add searchName/Location/Network/Country/Tags state; add groups and filteredItems computed properties so filter bar controls the table - Add NetworkShowTest covering NC and admin scenarios plus map bounds Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The merge accidentally reverted to an older two-column blade layout that dropped NetworkPage.vue (which has tag management UI required by the grouptags Playwright tests). - networks/show.blade.php: restore to use NetworkPage + add :map-bounds - NetworkPage.vue: replace simple groups count/link with GroupMapAndList; add mapBounds prop and GroupMapAndList import/component registration - NetworkShowTest: update assertions to check NetworkPage props directly Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
||
| <!-- Styles --> | ||
| @vite(['resources/sass/app.scss', 'resources/global/css/app.scss']) | ||
| <link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css" type="text/css" /> |
| <!-- Styles --> | ||
| @vite(['resources/sass/app.scss', 'resources/global/css/app.scss']) | ||
| <link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css" type="text/css" /> | ||
| <link rel="stylesheet" href="//unpkg.com/leaflet-gesture-handling/dist/leaflet-gesture-handling.min.css" type="text/css"> |
Vite 4.x does not include .vue in default resolve.extensions, so extension-less imports of .vue files fail in production builds. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without this, CircleCI's fallback composer cache (from develop) is used as the starting point, and re-applying the guzzle nullable patch on an already-patched vendor causes inconsistent autoloader state. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…mposer plugins - composer.lock: declare Laravel 13 support in wouternl/laravel-drip require section (the fork's code works with L13; the constraint was just too conservative — cweagans patch updated this at runtime but not at build time) - Dockerfile.fly: set COMPOSER_ALLOW_SUPERUSER=1 so composer plugins (including cweagans/composer-patches) can run during the image build Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
cweagans/composer-patches needs patches/composer.patches.json to be present when composer install runs. Previously only composer.json and composer.lock were copied at that stage. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…packages Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The navbar renders a <div class="vue"> containing <Notifications>, which assertVueProperties picks up as props[0] on any logged-in page. Tests were checking props[0] for NetworkPage props but needed to check props[1]. Also remove two unused translation keys (groups_count, view_groups_link) from lang/en/networks.php — replaced by the NetworkPage Vue component. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
User.groupsNearby() was updated on this branch to return groups matching the user's country when lat/lng are null. The dashboard test expected 0 nearby groups for a null-location GB user, but the group created in setUp() has country_code='GB', so the country branch now returns 1. Add a separate newGroupCount parameter so nearby-groups and new-groups can have different expectations: null-location GB user gets 1 nearby group (country match) but 0 new groups (dashboard controller guards that call with an explicit null-lat/lng check). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The group index no longer server-renders :all-groups in the blade — groups are fetched via /api/v2/groups/summary by the Vue component. Rewrite testGroupIndexNextEventIsEagerLoaded to call that API endpoint with includeNextEvent=true and verify next_event is populated. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…lity GroupMapAndList shows a loading spinner while fetching groups via API, then initializes a Leaflet map which runs flyToBounds before GroupsTable renders. The previous fixed 2-second timeout was insufficient in CI. Now waits for .loader to be hidden (API complete) then uses a 15-second timeout on the table visibility assertion to allow for map initialization. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When currentTab was set in created(), BootstrapVue's lazy b-tab missed the initial activation event, leaving the Other Groups tab panel empty. Initialize currentTab directly in data() from the tab prop so the correct tab is active from the very first render cycle, allowing lazy tab content to render correctly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…{id}
BootstrapVue lazy tabs only render content when localActive transitions
from false to true. When the tab is initially active (currentTab=1 from
data()), localActive starts true and the transition watcher never fires,
leaving the tab panel permanently empty.
Removing lazy from the Other Groups tab ensures GroupMapAndList always
mounts. The map API call runs in the background but this is acceptable
since the map is the primary content for network group pages.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… bounds When Leaflet map bounds calculation doesn't fire (e.g. headless CI environment), groupidsInBounds stays empty and the table never renders. Add effectiveGroupIds computed that falls back to all store groups (filtered by network if set) so the table is always visible once the API call completes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
waitForLoadState('networkidle') hangs indefinitely on /group/network/ pages
because Leaflet tile requests prevent the network from going idle in headless
CI. Replace with waitForSelector('.loader', {state:'hidden'}) which resolves
once the groups API call completes — the actual signal we need.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…olving The /group page now has GroupMapAndList rendered in the background tab, which loads Leaflet tiles and prevents networkidle from ever resolving. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace networkidle on /networks/ pages with waitForSelector('.tags-management')
since NetworkPage.vue also uses GroupMapAndList with Leaflet tiles
- Increase tag-item assertion timeouts to 15s (API calls can be slow in CI)
- Fix strict mode violation in event test: .multiselect resolves to 2 elements,
use .first() to target the group member select
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…reateTag() The mounted() hook fetches existing tags asynchronously. If createTag() runs and pushes a new tag before the GET response arrives, the subsequent this.tags = response.data.data assignment clears the pushed tag (the GET was issued before the POST, so the server returns an empty array). Fix: merge fetched tags with any already present rather than overwriting. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The NetworkPage.vue mounted() GET for tags was incorrectly using a merge strategy that correlated with createTag() not being invoked in CI. Revert to plain assignment and instead fix the race in the test by waiting for the tags-management element and a short delay before filling the form, ensuring mounted()'s GET completes before the form is submitted.
Kill Vite BEFORE modifying .env in docker:test:playwright — Vite v4 crashes with ERR_INVALID_ARG_TYPE when it detects .env changes while running, which risks leaving the build in an inconsistent state. Switch --reporter=html to --reporter=list,html so each completed test prints a line to stdout, preventing CircleCI's no_output_timeout from killing the Playwright step mid-run. Add console.log tracing and 10s explicit timeouts to "NC can create a tag" to identify the exact step that hangs in CI.
page.fill() fails to update Vue's newTagName after a mounted() GET response triggers a re-render: the DOM value is set but the reactive state stays empty so the submit button remains disabled. pressSequentially() simulates real keystrokes (keydown/input/keyup per char), which reliably drives BootstrapVue's onInput handler regardless of prior re-renders. Also remove the waitForTimeout(1000) that was masking the issue by ensuring the re-render always happened first.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.




Summary
GroupMapAndListcomponent that shows all groups in the network on an interactive Leaflet map, with a sortable table belowWhat changed
Backend
NetworkController::show(): computes lat/lng bounding box for map, fetches tags for NCs/admins, returnscanManageTagsflagGroupController::indexVariations(): returns role-scoped tags (admin sees all, NC sees network tags only, others see none)app/Http/Resources/Group.php:includeStatsnow opt-in (defaults false) for performanceFrontend
GroupMapAndList.vue: acceptsshowFilters,canManageTags,availableTagsprops, passes them toGroupsTableGroupsTable.vue: acceptssearch/allGroupTags/showTagsprops; filter state drives afilteredItemscomputed propertyNetworkPage.vue: replaced "view groups" link with embeddedGroupMapAndList; acceptsmapBoundspropnetworks/show.blade.php: passesmapBoundsand tag-management props toNetworkPageTests
NetworkShowTest: PHPUnit checks NC and admin seecan-manage-tags=trueand correct tags; map bounds reflect group locationsgrouptags.test.js: Playwright E2E tests for full tag CRUD on the network pageCode Quality Review
GroupMapAndListprops have safe defaults so existing usages (GroupsPage) are unaffectedTest Plan
/group— "Nearby/All" tab still shows map, "Mine" tab still shows tableNetworkShowTestpasses in CIgrouptags.test.jspasses in CI