diff --git a/core/application/beneficiaries.go b/core/application/beneficiaries.go index 2a81358..264a636 100644 --- a/core/application/beneficiaries.go +++ b/core/application/beneficiaries.go @@ -31,6 +31,8 @@ import ( solidaritytransformers "git.coopgo.io/coopgo-platform/solidarity-transport/servers/grpc/transformers" solidaritytypes "git.coopgo.io/coopgo-platform/solidarity-transport/types" "github.com/google/uuid" + "github.com/paulmach/orb" + "github.com/paulmach/orb/geojson" "github.com/rs/zerolog/log" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" @@ -41,12 +43,38 @@ type BeneficiariesResult struct { CacheID string } -func (h *ApplicationHandler) GetBeneficiaries(ctx context.Context, searchFilter string, archivedFilter bool) (*BeneficiariesResult, error) { +func (h *ApplicationHandler) GetBeneficiaries(ctx context.Context, searchFilter string, archivedFilter bool, addressGeoLayer, addressGeoCode string) (*BeneficiariesResult, error) { accounts, err := h.getBeneficiariesWithFilters(ctx, searchFilter, archivedFilter) if err != nil { return nil, err } + // Apply address geography filtering + if addressGeoLayer != "" && addressGeoCode != "" { + addressPolygons, err := h.loadGeographyPolygon(addressGeoLayer, addressGeoCode) + if err != nil { + log.Warn().Err(err).Msg("failed to load beneficiary address geography filter") + } else { + filtered := []mobilityaccountsstorage.Account{} + for _, account := range accounts { + if addr, ok := account.Data["address"]; ok { + jsonAddr, err := json.Marshal(addr) + if err == nil { + addrGeojson, err := geojson.UnmarshalFeature(jsonAddr) + if err == nil && addrGeojson.Geometry != nil { + if point, ok := addrGeojson.Geometry.(orb.Point); ok { + if isPointInGeographies(point, addressPolygons) { + filtered = append(filtered, account) + } + } + } + } + } + } + accounts = filtered + } + } + sort.Sort(sorting.BeneficiariesByName(accounts)) cacheID := uuid.NewString() diff --git a/renderer/beneficiaries.go b/renderer/beneficiaries.go index ffb7361..912604e 100755 --- a/renderer/beneficiaries.go +++ b/renderer/beneficiaries.go @@ -30,15 +30,25 @@ func (s BeneficiariesListState) JSONWithLimits(a int, b int) template.JS { return s.JSON() } -func (renderer *Renderer) BeneficiariesList(w http.ResponseWriter, r *http.Request, accounts []mobilityaccountsstorage.Account, cacheid string, archived bool) { +func (renderer *Renderer) BeneficiariesList(w http.ResponseWriter, r *http.Request, accounts []mobilityaccountsstorage.Account, cacheid string, archived bool, enrichedGeoFilters []map[string]string, selectedAddressGeo string) { files := renderer.ThemeConfig.GetStringSlice("views.beneficiaries.list.files") + geoFiltersEnabled := len(enrichedGeoFilters) > 0 + state := NewState(r, renderer.ThemeConfig, beneficiariesMenu) - state.ViewState = BeneficiariesListState{ - Count: len(accounts), - CacheId: cacheid, - Beneficiaries: accounts, - Archived: archived, + state.ViewState = map[string]any{ + "list": BeneficiariesListState{ + Count: len(accounts), + CacheId: cacheid, + Beneficiaries: accounts, + Archived: archived, + }, + "geography_filters_enabled": geoFiltersEnabled, + "geography_filters_list": enrichedGeoFilters, + "archived": archived, + "filters": map[string]any{ + "beneficiary_address_geo": selectedAddressGeo, + }, } renderer.Render("beneficiaries_list", w, r, files, state) diff --git a/servers/web/application/beneficiaries.go b/servers/web/application/beneficiaries.go index 07e8d6d..b063d7c 100644 --- a/servers/web/application/beneficiaries.go +++ b/servers/web/application/beneficiaries.go @@ -5,7 +5,9 @@ import ( "fmt" "io" "net/http" + "sort" "strconv" + "strings" "time" formvalidators "git.coopgo.io/coopgo-apps/parcoursmob/core/utils/form-validators" @@ -83,14 +85,64 @@ func (h *Handler) BeneficiariesListHTTPHandler() http.HandlerFunc { archivedFilter = true } - result, err := h.applicationHandler.GetBeneficiaries(r.Context(), searchFilter, archivedFilter) + // Extract beneficiary address geography filter + beneficiaryAddressGeo := r.URL.Query().Get("beneficiary_address_geo") + addressGeoLayer, addressGeoCode := "", "" + if beneficiaryAddressGeo != "" { + parts := strings.SplitN(beneficiaryAddressGeo, ":", 2) + if len(parts) == 2 { + addressGeoLayer, addressGeoCode = parts[0], parts[1] + } + } + + result, err := h.applicationHandler.GetBeneficiaries(r.Context(), searchFilter, archivedFilter, addressGeoLayer, addressGeoCode) if err != nil { log.Error().Err(err).Msg("error retrieving beneficiaries") http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } - h.renderer.BeneficiariesList(w, r, result.Accounts, result.CacheID, archivedFilter) + // Enrich geography filters with names from geography service + var enrichedGeoFilters []map[string]string + if h.cfg.GetBool("geography.filters.enabled") { + geoFilters := h.cfg.Get("geography.filters.geographies") + if geoList, ok := geoFilters.([]any); ok { + for _, geoItem := range geoList { + if geoMap, ok := geoItem.(map[string]any); ok { + layer := "" + code := "" + if l, ok := geoMap["layer"].(string); ok { + layer = l + } + if c, ok := geoMap["code"].(string); ok { + code = c + } + + enrichedGeo := map[string]string{ + "layer": layer, + "code": code, + "name": code, + } + + if layer != "" && code != "" { + if geoFeature, err := h.services.Geography.Find(layer, code); err == nil { + if name := geoFeature.Properties.MustString("nom"); name != "" { + enrichedGeo["name"] = name + } + } + } + + enrichedGeoFilters = append(enrichedGeoFilters, enrichedGeo) + } + } + } + + sort.Slice(enrichedGeoFilters, func(i, j int) bool { + return enrichedGeoFilters[i]["name"] < enrichedGeoFilters[j]["name"] + }) + } + + h.renderer.BeneficiariesList(w, r, result.Accounts, result.CacheID, archivedFilter, enrichedGeoFilters, beneficiaryAddressGeo) } }