package application import ( "encoding/json" "fmt" "io" "net/http" "sort" "strings" "time" "git.coopgo.io/coopgo-apps/parcoursmob/core/utils/identification" groupstorage "git.coopgo.io/coopgo-platform/groups-management/storage" "github.com/gorilla/mux" "github.com/paulmach/orb/geojson" "github.com/rs/zerolog/log" ) func (h *Handler) OrganizedCarpoolOverviewHTTPHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // Parse form to get both query params and form data r.ParseForm() // Extract filter parameters tab := r.FormValue("tab") if tab == "" { tab = "carpoolService" // Default to showing current bookings } // Extract archived filter archivedFilter := false if archived := r.URL.Query().Get("archived"); archived == "true" { archivedFilter = true } // Apply filters conditionally based on tab var status, driverID, startDate, endDate, departureGeo, destinationGeo, passengerAddressGeo, driverAddressGeo string var histStatus, histDriverID, histStartDate, histEndDate, histDepartureGeo, histDestinationGeo, histPassengerAddressGeo string // Driver address geography filter (applies when on drivers tab) if tab == "drivers" { driverAddressGeo = r.FormValue("driver_address_geo") } if tab == "carpoolService" { status = r.FormValue("status") driverID = r.FormValue("driver_id") startDate = r.FormValue("date_start") endDate = r.FormValue("date_end") departureGeo = r.FormValue("departure_geo") destinationGeo = r.FormValue("destination_geo") passengerAddressGeo = r.FormValue("passenger_address_geo") } // History filters (apply when on carpoolHistory tab) if tab == "carpoolHistory" { histStatus = r.FormValue("status") histDriverID = r.FormValue("driver_id") histStartDate = r.FormValue("date_start") histEndDate = r.FormValue("date_end") histDepartureGeo = r.FormValue("departure_geo") histDestinationGeo = r.FormValue("destination_geo") histPassengerAddressGeo = r.FormValue("passenger_address_geo") } // Set default history dates if not provided if histStartDate == "" { histStartDate = time.Now().Add(-30 * 24 * time.Hour).Format("2006-01-02") } if histEndDate == "" { histEndDate = time.Now().Add(-24 * time.Hour).Format("2006-01-02") } // Parse geography parameters (format: "layer:code") departureGeoLayer, departureGeoCode := "", "" if departureGeo != "" { parts := strings.SplitN(departureGeo, ":", 2) if len(parts) == 2 { departureGeoLayer, departureGeoCode = parts[0], parts[1] } } destinationGeoLayer, destinationGeoCode := "", "" if destinationGeo != "" { parts := strings.SplitN(destinationGeo, ":", 2) if len(parts) == 2 { destinationGeoLayer, destinationGeoCode = parts[0], parts[1] } } passengerAddressGeoLayer, passengerAddressGeoCode := "", "" if passengerAddressGeo != "" { parts := strings.SplitN(passengerAddressGeo, ":", 2) if len(parts) == 2 { passengerAddressGeoLayer, passengerAddressGeoCode = parts[0], parts[1] } } histDepartureGeoLayer, histDepartureGeoCode := "", "" if histDepartureGeo != "" { parts := strings.SplitN(histDepartureGeo, ":", 2) if len(parts) == 2 { histDepartureGeoLayer, histDepartureGeoCode = parts[0], parts[1] } } histDestinationGeoLayer, histDestinationGeoCode := "", "" if histDestinationGeo != "" { parts := strings.SplitN(histDestinationGeo, ":", 2) if len(parts) == 2 { histDestinationGeoLayer, histDestinationGeoCode = parts[0], parts[1] } } histPassengerAddressGeoLayer, histPassengerAddressGeoCode := "", "" if histPassengerAddressGeo != "" { parts := strings.SplitN(histPassengerAddressGeo, ":", 2) if len(parts) == 2 { histPassengerAddressGeoLayer, histPassengerAddressGeoCode = parts[0], parts[1] } } // Parse driver address geography parameter driverAddressGeoLayer, driverAddressGeoCode := "", "" if driverAddressGeo != "" { parts := strings.SplitN(driverAddressGeo, ":", 2) if len(parts) == 2 { driverAddressGeoLayer, driverAddressGeoCode = parts[0], parts[1] } } result, err := h.applicationHandler.GetOrganizedCarpoolOverview(r.Context(), status, driverID, startDate, endDate, departureGeoLayer, departureGeoCode, destinationGeoLayer, destinationGeoCode, passengerAddressGeoLayer, passengerAddressGeoCode, histStatus, histDriverID, histStartDate, histEndDate, histDepartureGeoLayer, histDepartureGeoCode, histDestinationGeoLayer, histDestinationGeoCode, histPassengerAddressGeoLayer, histPassengerAddressGeoCode, archivedFilter, driverAddressGeoLayer, driverAddressGeoCode) if err != nil { log.Error().Err(err).Msg("error retrieving organized carpool overview") http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } // Build filters map for template filters := map[string]any{ "tab": tab, "date_start": startDate, "date_end": endDate, "status": status, "driver_id": driverID, "departure_geo": departureGeo, "destination_geo": destinationGeo, "passenger_address_geo": passengerAddressGeo, "driver_address_geo": driverAddressGeo, } histFilters := map[string]any{ "tab": tab, "date_start": histStartDate, "date_end": histEndDate, "status": histStatus, "driver_id": histDriverID, "departure_geo": histDepartureGeo, "destination_geo": histDestinationGeo, "passenger_address_geo": histPassengerAddressGeo, } // 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, // Default to code if name fetch fails } // Fetch name from geography service 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 by name sort.Slice(enrichedGeoFilters, func(i, j int) bool { return enrichedGeoFilters[i]["name"] < enrichedGeoFilters[j]["name"] }) } h.renderer.OrganizedCarpoolOverview(w, r, result.Accounts, result.AccountsMap, result.BeneficiariesMap, result.Bookings, result.BookingsHistory, filters, histFilters, tab, enrichedGeoFilters, archivedFilter) } } func (h *Handler) OrganizedCarpoolBookingDisplayHTTPHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) bookingID := vars["bookingid"] result, err := h.applicationHandler.GetOrganizedCarpoolBookingData(r.Context(), bookingID) if err != nil { log.Error().Err(err).Msg("error retrieving organized carpool booking") http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } h.renderer.OrganizedCarpoolBookingDisplay(w, r, result.Booking, result.Driver, result.Passenger, result.DriverDepartureAddress, result.DriverArrivalAddress) } } func (h *Handler) OrganizedCarpoolBookingStatusHTTPHandler(action string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) bookingID := vars["bookingid"] err := h.applicationHandler.UpdateOrganizedCarpoolBookingStatus(r.Context(), bookingID, action) if err != nil { log.Error().Err(err).Msg("error updating organized carpool booking status") http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } http.Redirect(w, r, fmt.Sprintf("/app/organized-carpool/bookings/%s", bookingID), http.StatusSeeOther) } } func (h *Handler) OrganizedCarpoolCreateDriverHTTPHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { // Parse form data firstName := r.PostFormValue("first_name") lastName := r.PostFormValue("last_name") email := r.PostFormValue("email") phoneNumber := r.PostFormValue("phone_number") fileNumber := r.PostFormValue("file_number") gender := r.PostFormValue("gender") var birthdate *time.Time if r.PostFormValue("birthdate") != "" { if d, err := time.Parse("2006-01-02", r.PostFormValue("birthdate")); err == nil { birthdate = &d } } // Parse JSON address fields var address, addressDestination any if r.PostFormValue("address") != "" { json.Unmarshal([]byte(r.PostFormValue("address")), &address) } if r.PostFormValue("address_destination") != "" { json.Unmarshal([]byte(r.PostFormValue("address_destination")), &addressDestination) } driverID, err := h.applicationHandler.CreateOrganizedCarpoolDriver( r.Context(), firstName, lastName, email, birthdate, phoneNumber, fileNumber, address, addressDestination, gender, ) if err != nil { log.Error().Err(err).Msg("error creating organized carpool driver") http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } http.Redirect(w, r, fmt.Sprintf("/app/organized-carpool/drivers/%s", driverID), http.StatusFound) return } h.renderer.OrganizedCarpoolCreateDriver(w, r) } } func (h *Handler) OrganizedCarpoolDriverDisplayHTTPHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) driverID := vars["driverid"] // Extract tab parameter tab := r.URL.Query().Get("tab") if tab == "" { tab = "documents" // Default tab } result, err := h.applicationHandler.GetOrganizedCarpoolDriverData(r.Context(), driverID) if err != nil { log.Error().Err(err).Msg("error retrieving organized carpool driver data") http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } h.renderer.OrganizedCarpoolDriverDisplay(w, r, result.Driver, result.Trips, result.Documents, result.Bookings, result.BeneficiariesMap, result.Stats, result.WalletBalance, tab) } } func (h *Handler) OrganizedCarpoolUpdateDriverHTTPHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) driverID := vars["driverid"] if r.Method == "POST" { // Parse form data firstName := r.PostFormValue("first_name") lastName := r.PostFormValue("last_name") email := r.PostFormValue("email") phoneNumber := r.PostFormValue("phone_number") fileNumber := r.PostFormValue("file_number") gender := r.PostFormValue("gender") var birthdate *time.Time if r.PostFormValue("birthdate") != "" { if d, err := time.Parse("2006-01-02", r.PostFormValue("birthdate")); err == nil { birthdate = &d } } // Parse JSON address fields var address, addressDestination any if r.PostFormValue("address") != "" { json.Unmarshal([]byte(r.PostFormValue("address")), &address) } if r.PostFormValue("address_destination") != "" { json.Unmarshal([]byte(r.PostFormValue("address_destination")), &addressDestination) } updatedDriverID, err := h.applicationHandler.UpdateOrganizedCarpoolDriver( r.Context(), driverID, firstName, lastName, email, birthdate, phoneNumber, fileNumber, address, addressDestination, gender, r.PostFormValue("other_properties"), ) if err != nil { log.Error().Err(err).Msg("error updating organized carpool driver") http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } http.Redirect(w, r, fmt.Sprintf("/app/organized-carpool/drivers/%s", updatedDriverID), http.StatusFound) return } result, err := h.applicationHandler.GetOrganizedCarpoolDriver(r.Context(), driverID) if err != nil { log.Error().Err(err).Msg("error retrieving organized carpool driver for update") http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } h.renderer.OrganizedCarpoolUpdateDriver(w, r, result.Driver) } } func (h *Handler) OrganizedCarpoolArchiveDriverHTTPHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) driverID := vars["driverid"] err := h.applicationHandler.ArchiveOrganizedCarpoolDriver(r.Context(), driverID) if err != nil { log.Error().Err(err).Msg("error archiving organized carpool driver") http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } http.Redirect(w, r, fmt.Sprintf("/app/organized-carpool/drivers/%s", driverID), http.StatusFound) } } func (h *Handler) OrganizedCarpoolUnarchiveDriverHTTPHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) driverID := vars["driverid"] err := h.applicationHandler.UnarchiveOrganizedCarpoolDriver(r.Context(), driverID) if err != nil { log.Error().Err(err).Msg("error unarchiving organized carpool driver") http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } http.Redirect(w, r, fmt.Sprintf("/app/organized-carpool/drivers/%s", driverID), http.StatusFound) } } func (h *Handler) OrganizedCarpoolDriverDocumentsHTTPHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) driverID := vars["driverid"] if err := r.ParseMultipartForm(100 * 1024 * 1024); err != nil { // 100 MB limit log.Error().Err(err).Msg("error parsing multipart form") http.Error(w, "Bad Request", http.StatusBadRequest) return } documentType := r.FormValue("type") documentName := r.FormValue("name") file, header, err := r.FormFile("file-upload") if err != nil { log.Error().Err(err).Msg("error retrieving file") http.Error(w, "Bad Request", http.StatusBadRequest) return } defer file.Close() if err := h.applicationHandler.AddOrganizedCarpoolDriverDocument(r.Context(), driverID, file, header.Filename, header.Size, documentType, documentName); err != nil { log.Error().Err(err).Msg("error adding organized carpool driver document") http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } http.Redirect(w, r, fmt.Sprintf("/app/organized-carpool/drivers/%s", driverID), http.StatusFound) } } func (h *Handler) OrganizedCarpoolDocumentDownloadHTTPHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) driverID := vars["driverid"] document := vars["document"] file, info, err := h.applicationHandler.GetOrganizedCarpoolDriverDocument(r.Context(), driverID, document) if err != nil { log.Error().Err(err).Msg("error retrieving organized carpool driver document") http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } w.Header().Set("Content-Type", info.ContentType) if _, err = io.Copy(w, file); err != nil { log.Error().Err(err).Msg("error copying file content") http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } } } func (h *Handler) OrganizedCarpoolDocumentDeleteHTTPHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) driverID := vars["driverid"] document := vars["document"] if err := h.applicationHandler.DeleteOrganizedCarpoolDriverDocument(r.Context(), driverID, document); err != nil { log.Error().Err(err).Msg("error deleting organized carpool driver document") http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } http.Redirect(w, r, fmt.Sprintf("/app/organized-carpool/drivers/%s", driverID), http.StatusFound) } } func (h *Handler) OrganizedCarpoolAddTripHTTPHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { log.Error().Msg("Wrong method") http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) return } if err := r.ParseForm(); err != nil { log.Error().Err(err).Msg("error parsing availabilities form") http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } vars := mux.Vars(r) driverID := vars["driverid"] // Parse form data outwardtime := r.PostFormValue("outwardtime") returntime := r.PostFormValue("returntime") // Parse GeoJSON features departure, err := geojson.UnmarshalFeature([]byte(r.PostFormValue("address_departure"))) if err != nil { log.Error().Err(err).Msg("failed parsing departure geojson") http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } destination, err := geojson.UnmarshalFeature([]byte(r.PostFormValue("address_destination"))) if err != nil { log.Error().Err(err).Msg("failed parsing destination geojson") http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } // Parse days days := map[string]bool{ "monday": r.PostFormValue("days.monday") == "on", "tuesday": r.PostFormValue("days.tuesday") == "on", "wednesday": r.PostFormValue("days.wednesday") == "on", "thursday": r.PostFormValue("days.thursday") == "on", "friday": r.PostFormValue("days.friday") == "on", "saturday": r.PostFormValue("days.saturday") == "on", "sunday": r.PostFormValue("days.sunday") == "on", } err = h.applicationHandler.AddOrganizedCarpoolTrip(r.Context(), driverID, outwardtime, returntime, departure, destination, days) if err != nil { log.Error().Err(err).Msg("error adding organized carpool trip") http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } http.Redirect(w, r, fmt.Sprintf("/app/organized-carpool/drivers/%s", driverID), http.StatusFound) } } func (h *Handler) OrganizedCarpoolDeleteTripHTTPHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) driverID := vars["driverid"] tripID := vars["tripid"] err := h.applicationHandler.DeleteOrganizedCarpoolTrip(r.Context(), tripID) if err != nil { log.Error().Err(err).Msg("error deleting organized carpool trip") http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } http.Redirect(w, r, fmt.Sprintf("/app/organized-carpool/drivers/%s", driverID), http.StatusFound) } } func (h *Handler) OrganizedCarpoolJourneyHTTPHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) driverID := vars["driverid"] journeyID := vars["journeyid"] passengerID := r.URL.Query().Get("passengerid") if r.Method == "POST" { // Parse form data motivation := r.PostFormValue("motivation") message := r.PostFormValue("message") doNotSend := r.PostFormValue("do_not_send") == "on" bookingID, err := h.applicationHandler.CreateOrganizedCarpoolJourneyBooking(r.Context(), driverID, journeyID, passengerID, motivation, message, doNotSend) if err != nil { log.Error().Err(err).Msg("error creating organized carpool journey booking") http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } log.Info().Str("booking_id", bookingID).Msg("Carpool booking created successfully") http.Redirect(w, r, fmt.Sprintf("/app/organized-carpool/"), http.StatusFound) return } // Get current user's group g := r.Context().Value(identification.GroupKey) if g == nil { log.Error().Msg("group not found in request context") http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } group := g.(groupstorage.Group) result, err := h.applicationHandler.GetOrganizedCarpoolJourneyData(r.Context(), driverID, journeyID, passengerID, group) if err != nil { log.Error().Err(err).Msg("error retrieving organized carpool journey data") http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } h.renderer.OrganizedCarpoolJourney(w, r, result.Journey, result.Driver, result.Passenger, result.Beneficiaries, result.PassengerWalletBalance, result.PricingResult) } }