parcoursmob/servers/web/application/organized_carpool.go

596 lines
20 KiB
Go

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)
}
}