lot of new functionalities

This commit is contained in:
Arnaud Delcasse
2025-10-14 18:11:13 +02:00
parent a6f70a6e85
commit d992a7984f
164 changed files with 15113 additions and 9442 deletions

View File

@@ -0,0 +1,247 @@
package application
import (
"net/http"
"time"
"github.com/gorilla/mux"
"github.com/rs/zerolog/log"
)
func (h *Handler) AdministrationHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Call business logic handler
result, err := h.applicationHandler.GetAdministrationData(r.Context())
if err != nil {
log.Error().Err(err).Msg("error retrieving administration data")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Render HTTP response
h.renderer.Administration(
w, r,
result.Accounts,
result.Beneficiaries,
result.Groups,
result.Bookings,
result.Events,
)
}
}
func (h *Handler) AdministrationCreateGroupHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
if err := r.ParseForm(); err != nil {
log.Error().Err(err).Msg("error parsing form")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
// Extract and validate form parameters
name := r.FormValue("name")
if name == "" {
log.Error().Str("name", name).Msg("Invalid name")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
// Extract modules configuration
modules := map[string]any{
"beneficiaries": r.FormValue("modules.beneficiaries") == "on",
"journeys": r.FormValue("modules.journeys") == "on",
"vehicles": r.FormValue("modules.vehicles") == "on",
"vehicles_management": r.FormValue("modules.vehicles_management") == "on",
"events": r.FormValue("modules.events") == "on",
"agenda": r.FormValue("modules.agenda") == "on",
"groups": r.FormValue("modules.groups") == "on",
"administration": r.FormValue("modules.administration") == "on",
"support": r.FormValue("modules.support") == "on",
"group_module": r.FormValue("modules.group_module") == "on",
"organized_carpool": r.FormValue("modules.organized_carpool") == "on",
"solidarity_transport": r.FormValue("modules.solidarity_transport") == "on",
}
// Call business logic handler
groupID, err := h.applicationHandler.CreateAdministrationGroup(r.Context(), name, modules)
if err != nil {
log.Error().Err(err).Msg("error creating administration group")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Redirect to group display
http.Redirect(w, r, "/app/administration/groups/"+groupID, http.StatusFound)
return
}
// For GET requests, render the create group form
h.renderer.AdministrationCreateGroup(w, r)
}
}
func (h *Handler) AdministrationGroupDisplayHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
groupID := vars["groupid"]
// Call business logic handler
result, err := h.applicationHandler.GetAdministrationGroupData(r.Context(), groupID)
if err != nil {
log.Error().Err(err).Msg("error retrieving administration group data")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Render HTTP response
h.renderer.AdministrationGroupDisplay(w, r, result.Group, result.Members, result.Admins)
}
}
func (h *Handler) AdministrationGroupInviteAdminHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
groupID := vars["groupid"]
if r.Method == "POST" {
if err := r.ParseForm(); err != nil {
log.Error().Err(err).Msg("error parsing form")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
// Extract form parameters
username := r.FormValue("username")
if username == "" {
log.Error().Str("username", username).Msg("Invalid username")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
// Call business logic handler
err := h.applicationHandler.InviteAdministrationGroupAdmin(r.Context(), groupID, username)
if err != nil {
log.Error().Err(err).Msg("error inviting group admin")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Redirect back to group display
http.Redirect(w, r, "/app/administration/groups/"+groupID, http.StatusFound)
return
}
// For GET requests, redirect to group display (no separate form)
http.Redirect(w, r, "/app/administration/groups/"+groupID, http.StatusFound)
}
}
func (h *Handler) AdministrationGroupInviteMemberHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
groupID := vars["groupid"]
if r.Method == "POST" {
if err := r.ParseForm(); err != nil {
log.Error().Err(err).Msg("error parsing form")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
// Extract form parameters
username := r.FormValue("username")
if username == "" {
log.Error().Str("username", username).Msg("Invalid username")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
// Call business logic handler
err := h.applicationHandler.InviteAdministrationGroupMember(r.Context(), groupID, username)
if err != nil {
log.Error().Err(err).Msg("error inviting group member")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Redirect back to group display
http.Redirect(w, r, "/app/administration/groups/"+groupID, http.StatusFound)
return
}
// For GET requests, redirect to group display (no separate form)
http.Redirect(w, r, "/app/administration/groups/"+groupID, http.StatusFound)
}
}
func (h *Handler) AdminStatsVehiclesHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
result, err := h.applicationHandler.GetVehiclesStats()
if err != nil {
log.Error().Err(err).Msg("error retrieving vehicles stats")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.AdminStatVehicles(w, r, result.Vehicles, result.Bookings, result.Groups)
}
}
func (h *Handler) AdminStatsBookingsHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Extract filter parameters from query
status := r.URL.Query().Get("status")
dateStart := r.URL.Query().Get("date_start")
dateEnd := r.URL.Query().Get("date_end")
// Default to last month if no dates specified
if dateStart == "" {
dateStart = time.Now().AddDate(0, -1, 0).Format("2006-01-02")
}
if dateEnd == "" {
dateEnd = time.Now().Format("2006-01-02")
}
result, err := h.applicationHandler.GetBookingsStats(status, dateStart, dateEnd)
if err != nil {
log.Error().Err(err).Msg("error retrieving bookings stats")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Prepare filters map for template
filters := map[string]string{
"status": status,
"date_start": dateStart,
"date_end": dateEnd,
}
h.renderer.AdminStatBookings(w, r, result.Vehicles, result.Bookings, result.Groups, result.BeneficiariesMap, filters)
}
}
func (h *Handler) AdminStatsBeneficiariesHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
result, err := h.applicationHandler.GetBeneficiariesStats()
if err != nil {
log.Error().Err(err).Msg("error retrieving beneficiaries stats")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.AdminStatBeneficaires(w, r, result.Beneficiaries, result.CacheID)
}
}
func (h *Handler) AdminStatsEventsHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
result, err := h.applicationHandler.GetEventsStats()
if err != nil {
log.Error().Err(err).Msg("error retrieving events stats")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.AdminStatEvents(w, r, result.Events, result.Groups)
}
}

View File

@@ -0,0 +1,469 @@
package application
import (
"encoding/json"
"fmt"
"io"
"net/http"
"strconv"
"strings"
"time"
formvalidators "git.coopgo.io/coopgo-apps/parcoursmob/core/utils/form-validators"
"github.com/gorilla/mux"
"github.com/rs/zerolog/log"
)
type EventsForm struct {
Name string `json:"name" validate:"required"`
Type string `json:"type" validate:"required"`
Description string `json:"description"`
Address any `json:"address,omitempty"`
Allday bool `json:"allday"`
Startdate *time.Time `json:"startdate"`
Enddate *time.Time `json:"enddate"`
Starttime string `json:"starttime"`
Endtime string `json:"endtime"`
MaxSubscribers int `json:"max_subscribers"`
}
func parseEventsForm(r *http.Request) (*EventsForm, error) {
if err := r.ParseForm(); err != nil {
return nil, err
}
var startdate *time.Time
var enddate *time.Time
if r.PostFormValue("startdate") != "" {
d, err := time.Parse("2006-01-02", r.PostFormValue("startdate"))
if err != nil {
return nil, err
}
startdate = &d
}
if r.PostFormValue("enddate") != "" {
d, err := time.Parse("2006-01-02", r.PostFormValue("enddate"))
if err != nil {
return nil, err
}
enddate = &d
}
max_subscribers, err := strconv.Atoi(r.PostFormValue("max_subscribers"))
if err != nil {
return nil, err
}
formData := &EventsForm{
Name: r.PostFormValue("name"),
Type: r.PostFormValue("type"),
Description: r.PostFormValue("description"),
Startdate: startdate,
Enddate: enddate,
Starttime: r.PostFormValue("starttime"),
Endtime: r.PostFormValue("endtime"),
MaxSubscribers: max_subscribers,
}
if r.PostFormValue("allday") == "true" {
formData.Allday = true
}
if r.PostFormValue("address") != "" {
var a any
json.Unmarshal([]byte(r.PostFormValue("address")), &a)
formData.Address = a
}
validate := formvalidators.New()
if err := validate.Struct(formData); err != nil {
return nil, err
}
return formData, nil
}
func (h *Handler) AgendaHomeHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
today := time.Now().Truncate(24 * time.Hour)
result, err := h.applicationHandler.GetAgendaEvents(r.Context(), &today, nil)
if err != nil {
log.Error().Err(err).Msg("error retrieving agenda events")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.AgendaHome(w, r, result.Events, result.Groups)
}
}
func (h *Handler) AgendaHistoryHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
result, err := h.applicationHandler.GetAgendaEvents(r.Context(), nil, nil)
if err != nil {
log.Error().Err(err).Msg("error retrieving agenda events")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.AgendaHistory(w, r, result.Events, result.Groups)
}
}
func (h *Handler) AgendaCreateEventHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
// Parse form data
eventForm, err := parseEventsForm(r)
if err != nil {
log.Error().Err(err).Msg("error parsing event form")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
// Handle file upload if present
var file io.Reader
var filename string
var fileSize int64
var documentType, documentName string
contentType := r.Header.Get("Content-Type")
if strings.HasPrefix(contentType, "multipart/form-data") {
err = r.ParseMultipartForm(100 * 1024 * 1024) // 100 MB limit
if err != nil {
log.Error().Err(err).Msg("error parsing multipart form")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
uploadedFile, header, err := r.FormFile("file-upload")
if err == nil {
defer uploadedFile.Close()
file = uploadedFile
filename = header.Filename
fileSize = header.Size
documentType = r.FormValue("file_type")
documentName = r.FormValue("file_name")
} else if err != http.ErrMissingFile {
log.Error().Err(err).Msg("error retrieving file")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
eventID, err := h.applicationHandler.CreateAgendaEvent(
r.Context(),
eventForm.Name,
eventForm.Type,
eventForm.Description,
eventForm.Address,
eventForm.Allday,
eventForm.Startdate,
eventForm.Enddate,
eventForm.Starttime,
eventForm.Endtime,
eventForm.MaxSubscribers,
file,
filename,
fileSize,
documentType,
documentName,
)
if err != nil {
log.Error().Err(err).Msg("error creating agenda event")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/agenda/%s", eventID), http.StatusFound)
return
}
// For GET requests, render the create event form with config data
documentTypes := h.cfg.GetStringSlice("modules.agenda.documents_types")
fileTypes := h.cfg.GetStringMapString("storage.files.file_types")
h.renderer.AgendaCreateEvent(w, r, documentTypes, fileTypes, nil)
}
}
func (h *Handler) AgendaDisplayEventHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
eventID := vars["eventid"]
result, err := h.applicationHandler.GetAgendaEvent(r.Context(), eventID)
if err != nil {
log.Error().Err(err).Msg("error retrieving agenda event")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
documentTypes := h.cfg.GetStringSlice("modules.agenda.documents_types")
fileTypes := h.cfg.GetStringMapString("storage.files.file_types")
h.renderer.AgendaDisplayEvent(w, r, result.Event, result.Group, documentTypes, fileTypes, result.Documents, result.Subscribers, result.Accounts)
}
}
func (h *Handler) AgendaUpdateEventHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
eventID := vars["eventid"]
if r.Method == "POST" {
// Parse form data
eventForm, err := parseEventsForm(r)
if err != nil {
log.Error().Err(err).Msg("error parsing event form")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
updatedEventID, err := h.applicationHandler.UpdateAgendaEvent(
r.Context(),
eventID,
eventForm.Name,
eventForm.Type,
eventForm.Description,
eventForm.Address,
eventForm.Allday,
eventForm.Startdate,
eventForm.Enddate,
eventForm.Starttime,
eventForm.Endtime,
eventForm.MaxSubscribers,
)
if err != nil {
log.Error().Err(err).Msg("error updating agenda event")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/agenda/%s", updatedEventID), http.StatusFound)
return
}
// For GET requests, render the update event form
result, err := h.applicationHandler.GetAgendaEvent(r.Context(), eventID)
if err != nil {
log.Error().Err(err).Msg("error retrieving agenda event")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.AgendaUpdateEvent(w, r, result.Event)
}
}
func (h *Handler) AgendaDeleteEventHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
eventID := vars["eventid"]
if r.Method == "POST" {
if err := h.applicationHandler.DeleteAgendaEvent(r.Context(), eventID); err != nil {
log.Error().Err(err).Msg("error deleting agenda event")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/app/agenda/", http.StatusFound)
return
}
// For GET requests, render the delete confirmation form
result, err := h.applicationHandler.GetAgendaEvent(r.Context(), eventID)
if err != nil {
log.Error().Err(err).Msg("error retrieving agenda event")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.AgendaDeleteEvent(w, r, result.Event)
}
}
func (h *Handler) AgendaSubscribeEventHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
eventID := vars["eventid"]
if r.Method == "POST" {
if err := r.ParseForm(); err != nil {
log.Error().Err(err).Msg("error parsing form")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
subscriber := r.FormValue("subscriber")
// Get current user and group information
current_group, err := h.currentGroup(r)
if err != nil {
log.Error().Err(err).Msg("error getting current group")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
current_user_token, current_user_claims, err := h.currentUser(r)
if err != nil {
log.Error().Err(err).Msg("error getting current user")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
subscriptionData := map[string]any{
"subscribed_by": map[string]any{
"user": map[string]any{
"id": current_user_token.Subject,
"display_name": current_user_claims["first_name"].(string) + " " + current_user_claims["last_name"].(string),
"email": current_user_claims["email"].(string),
},
"group": map[string]any{
"id": current_group.ID,
"name": current_group.Data["name"],
},
},
}
if err := h.applicationHandler.SubscribeToAgendaEvent(r.Context(), eventID, subscriber, subscriptionData); err != nil {
log.Error().Err(err).Msg("error subscribing to agenda event")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/agenda/%s", eventID), http.StatusFound)
return
}
// For GET requests, redirect to event display
http.Redirect(w, r, fmt.Sprintf("/app/agenda/%s", eventID), http.StatusFound)
}
}
func (h *Handler) AgendaDeleteSubscribeEventHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
eventID := vars["eventid"]
subscribeID := vars["subscribeid"]
if r.Method == "POST" {
if err := r.ParseForm(); err != nil {
log.Error().Err(err).Msg("error parsing form")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
motif := r.FormValue("motif")
// Get current user and group information
current_group, err := h.currentGroup(r)
if err != nil {
log.Error().Err(err).Msg("error getting current group")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
current_user_token, current_user_claims, err := h.currentUser(r)
if err != nil {
log.Error().Err(err).Msg("error getting current user")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
if err := h.applicationHandler.UnsubscribeFromAgendaEvent(
r.Context(),
eventID,
subscribeID,
motif,
current_user_token.Subject,
current_user_claims["first_name"].(string)+" "+current_user_claims["last_name"].(string),
current_user_claims["email"].(string),
current_group.ID,
current_group.Data["name"].(string),
); err != nil {
log.Error().Err(err).Msg("error unsubscribing from agenda event")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/agenda/%s", eventID), http.StatusFound)
return
}
// For GET requests, render the delete subscription form
h.renderer.AgendaDeleteSubscribeEvent(w, r, eventID)
}
}
func (h *Handler) AgendaHistoryEventHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
eventID := vars["eventid"]
result, err := h.applicationHandler.GetAgendaEventHistory(r.Context(), eventID)
if err != nil {
log.Error().Err(err).Msg("error retrieving agenda event history")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.AgendaHistoryEvent(w, r, result.Event, result.Group, result.Subscribers, result.Accounts)
}
}
func (h *Handler) EventDocumentsHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
eventID := vars["eventid"]
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.AddEventDocument(r.Context(), eventID, file, header.Filename, header.Size, documentType, documentName); err != nil {
log.Error().Err(err).Msg("error adding event document")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/agenda/%s", eventID), http.StatusFound)
}
}
func (h *Handler) EventDocumentDownloadHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
eventID := vars["eventid"]
document := vars["document"]
file, info, err := h.applicationHandler.GetEventDocument(r.Context(), eventID, document)
if err != nil {
log.Error().Err(err).Msg("error retrieving event 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
}
http.Redirect(w, r, fmt.Sprintf("/app/agenda/%s", eventID), http.StatusFound)
}
}

View File

@@ -0,0 +1,324 @@
package application
import (
"encoding/json"
"fmt"
"io"
"net/http"
"strconv"
"time"
formvalidators "git.coopgo.io/coopgo-apps/parcoursmob/core/utils/form-validators"
"github.com/gorilla/mux"
"github.com/rs/zerolog/log"
)
type BeneficiariesForm struct {
FirstName string `json:"first_name" validate:"required"`
LastName string `json:"last_name" validate:"required"`
Email string `json:"email" validate:"required,email"`
Birthdate *time.Time `json:"birthdate" validate:"required"`
PhoneNumber string `json:"phone_number" validate:"required,phoneNumber"`
FileNumber string `json:"file_number"`
Address any `json:"address,omitempty"`
Gender string `json:"gender"`
OtherProperties any `json:"other_properties,omitempty"`
}
func parseBeneficiariesForm(r *http.Request) (*BeneficiariesForm, error) {
if err := r.ParseForm(); err != nil {
return nil, err
}
var date *time.Time
if r.PostFormValue("birthdate") != "" {
d, err := time.Parse("2006-01-02", r.PostFormValue("birthdate"))
if err != nil {
return nil, err
}
date = &d
}
formData := &BeneficiariesForm{
FirstName: r.PostFormValue("first_name"),
LastName: r.PostFormValue("last_name"),
Email: r.PostFormValue("email"),
Birthdate: date,
PhoneNumber: r.PostFormValue("phone_number"),
FileNumber: r.PostFormValue("file_number"),
Gender: r.PostFormValue("gender"),
}
if r.PostFormValue("address") != "" {
var a any
json.Unmarshal([]byte(r.PostFormValue("address")), &a)
formData.Address = a
}
if r.PostFormValue("other_properties") != "" {
var a any
json.Unmarshal([]byte(r.PostFormValue("other_properties")), &a)
formData.OtherProperties = a
}
validate := formvalidators.New()
if err := validate.Struct(formData); err != nil {
return nil, err
}
return formData, nil
}
func (h *Handler) BeneficiariesListHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Extract search and archived filters from query parameters
searchFilter := ""
if search := r.URL.Query().Get("search"); search != "" {
searchFilter = search
}
archivedFilter := false
if archived := r.URL.Query().Get("archived"); archived == "true" {
archivedFilter = true
}
result, err := h.applicationHandler.GetBeneficiaries(r.Context(), searchFilter, archivedFilter)
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)
}
}
func (h *Handler) BeneficiaryCreateHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
beneficiaryForm, err := parseBeneficiariesForm(r)
if err != nil {
log.Error().Err(err).Msg("error parsing beneficiary form")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
beneficiaryID, err := h.applicationHandler.CreateBeneficiary(
r.Context(),
beneficiaryForm.FirstName,
beneficiaryForm.LastName,
beneficiaryForm.Email,
beneficiaryForm.Birthdate,
beneficiaryForm.PhoneNumber,
beneficiaryForm.FileNumber,
beneficiaryForm.Address,
beneficiaryForm.Gender,
beneficiaryForm.OtherProperties,
)
if err != nil {
log.Error().Err(err).Msg("error creating beneficiary")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/beneficiaries/%s", beneficiaryID), http.StatusFound)
return
}
h.renderer.BeneficiaryCreate(w, r)
}
}
func (h *Handler) BeneficiaryDisplayHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
beneficiaryID := vars["beneficiaryid"]
// Extract tab parameter
tab := r.URL.Query().Get("tab")
if tab == "" {
tab = "documents" // Default tab
}
result, err := h.applicationHandler.GetBeneficiaryData(r.Context(), beneficiaryID)
if err != nil {
log.Error().Err(err).Msg("error retrieving beneficiary data")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
beneficiariesFileTypes := h.cfg.GetStringSlice("modules.beneficiaries.documents_types")
fileTypesMap := h.cfg.GetStringMapString("storage.files.file_types")
h.renderer.BeneficiaryDisplay(w, r, result.Account, result.Bookings, result.Organizations, beneficiariesFileTypes, fileTypesMap, result.Documents, result.EventsList, result.SolidarityTransportStats, result.SolidarityTransportBookings, result.SolidarityDriversMap, result.OrganizedCarpoolStats, result.OrganizedCarpoolBookings, result.OrganizedCarpoolDriversMap, result.WalletBalance, tab)
}
}
func (h *Handler) BeneficiaryUpdateHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
beneficiaryID := vars["beneficiaryid"]
if r.Method == "POST" {
beneficiaryForm, err := parseBeneficiariesForm(r)
if err != nil {
log.Error().Err(err).Msg("error parsing beneficiary form")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
updatedBeneficiaryID, err := h.applicationHandler.UpdateBeneficiary(
r.Context(),
beneficiaryID,
beneficiaryForm.FirstName,
beneficiaryForm.LastName,
beneficiaryForm.Email,
beneficiaryForm.Birthdate,
beneficiaryForm.PhoneNumber,
beneficiaryForm.FileNumber,
beneficiaryForm.Address,
beneficiaryForm.Gender,
beneficiaryForm.OtherProperties,
)
if err != nil {
log.Error().Err(err).Msg("error updating beneficiary")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/beneficiaries/%s", updatedBeneficiaryID), http.StatusFound)
return
}
// For GET requests, just get the basic beneficiary account data
result, err := h.applicationHandler.GetBeneficiary(r.Context(), beneficiaryID)
if err != nil {
log.Error().Err(err).Msg("error retrieving beneficiary for update")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.BeneficiaryUpdate(w, r, result.Account)
}
}
func (h *Handler) BeneficiaryArchiveHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
beneficiaryID := vars["beneficiaryid"]
if err := h.applicationHandler.ArchiveBeneficiary(r.Context(), beneficiaryID); err != nil {
log.Error().Err(err).Msg("error archiving beneficiary")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/beneficiaries/%s", beneficiaryID), http.StatusFound)
}
}
func (h *Handler) BeneficiaryUnarchiveHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
beneficiaryID := vars["beneficiaryid"]
if err := h.applicationHandler.UnarchiveBeneficiary(r.Context(), beneficiaryID); err != nil {
log.Error().Err(err).Msg("error unarchiving beneficiary")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/beneficiaries/%s", beneficiaryID), http.StatusFound)
}
}
func (h *Handler) BeneficiaryPictureHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
beneficiaryID := vars["beneficiaryid"]
imageData, contentType, err := h.applicationHandler.GetBeneficiaryPicture(r.Context(), beneficiaryID)
if err != nil {
log.Error().Err(err).Msg("error generating beneficiary picture")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", contentType)
w.Header().Set("Content-Length", strconv.Itoa(len(imageData)))
if _, err := w.Write(imageData); err != nil {
log.Error().Err(err).Msg("unable to write image")
}
}
}
func (h *Handler) BeneficiaryDocumentsHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
beneficiaryID := vars["beneficiaryid"]
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.AddBeneficiaryDocument(r.Context(), beneficiaryID, file, header.Filename, header.Size, documentType, documentName); err != nil {
log.Error().Err(err).Msg("error adding beneficiary document")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/beneficiaries/%s", beneficiaryID), http.StatusFound)
}
}
func (h *Handler) BeneficiaryDocumentDownloadHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
beneficiaryID := vars["beneficiaryid"]
document := vars["document"]
file, info, err := h.applicationHandler.GetBeneficiaryDocument(r.Context(), beneficiaryID, document)
if err != nil {
log.Error().Err(err).Msg("error retrieving beneficiary 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) BeneficiaryDocumentDeleteHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
beneficiaryID := vars["beneficiaryid"]
document := vars["document"]
if err := h.applicationHandler.DeleteBeneficiaryDocument(r.Context(), beneficiaryID, document); err != nil {
log.Error().Err(err).Msg("error deleting beneficiary document")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/beneficiaries/%s", beneficiaryID), http.StatusFound)
}
}

View File

@@ -0,0 +1,74 @@
package application
import (
"net/http"
"sort"
"strings"
"github.com/rs/zerolog/log"
)
func (h *Handler) DashboardHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Parse driver address geography filter
driverAddressGeo := r.URL.Query().Get("driver_address_geo")
driverAddressGeoLayer, driverAddressGeoCode := "", ""
if driverAddressGeo != "" {
parts := strings.SplitN(driverAddressGeo, ":", 2)
if len(parts) == 2 {
driverAddressGeoLayer, driverAddressGeoCode = parts[0], parts[1]
}
}
result, err := h.applicationHandler.GetDashboardData(r.Context(), driverAddressGeoLayer, driverAddressGeoCode)
if err != nil {
log.Error().Err(err).Msg("error retrieving dashboard data")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// 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.Dashboard(w, r, result.Accounts, len(result.Accounts), len(result.Members), result.Events, result.Bookings, result.SolidarityDrivers, result.OrganizedCarpoolDrivers, driverAddressGeo, enrichedGeoFilters)
}
}

View File

@@ -0,0 +1,11 @@
package application
import (
"net/http"
)
func (h *Handler) DirectoryHomeHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
h.renderer.DirectoryHome(w, r)
}
}

View File

@@ -0,0 +1,54 @@
package application
import (
"net/http"
"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/identification"
"git.coopgo.io/coopgo-platform/groups-management/storage"
"github.com/rs/zerolog/log"
)
func (h *Handler) GroupSettingsDisplayHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
g := r.Context().Value(identification.GroupKey)
if g == nil {
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
group := g.(storage.Group)
result, err := h.applicationHandler.GetGroupSettings(r.Context(), group.ID)
if err != nil {
log.Error().Err(err).Msg("error getting group settings")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.GroupSettingsDisplay(w, r, result.Group, result.GroupMembers, result.Admins)
}
}
func (h *Handler) GroupSettingsInviteMemberHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
g := r.Context().Value(identification.GroupKey)
if g == nil {
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
group := g.(storage.Group)
r.ParseForm()
username := r.FormValue("username")
err := h.applicationHandler.InviteMemberToGroup(r.Context(), group.ID, username)
if err != nil {
log.Error().Err(err).Msg("error inviting member to group")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/app/group/settings", http.StatusFound)
}
}

View File

@@ -0,0 +1,101 @@
package application
import (
"net/http"
"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/identification"
"git.coopgo.io/coopgo-platform/groups-management/storage"
"github.com/gorilla/mux"
"github.com/rs/zerolog/log"
)
func (h *Handler) GroupsHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
result, err := h.applicationHandler.GetGroups(r.Context())
if err != nil {
log.Error().Err(err).Msg("error getting groups")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.Groups(w, r, result.Groups)
}
}
func (h *Handler) CreateGroupModuleHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
r.ParseForm()
name := r.FormValue("name")
groupType := r.FormValue("type")
description := r.FormValue("description")
address := r.PostFormValue("address")
result, err := h.applicationHandler.CreateGroupModule(r.Context(), name, groupType, description, address)
if err != nil {
log.Error().Err(err).Msg("error creating group module")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/app/group_module/groups/"+result.GroupID, http.StatusFound)
return
}
result, err := h.applicationHandler.GetGroupModuleCreateData(r.Context())
if err != nil {
log.Error().Err(err).Msg("error getting group module create data")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.CreateGroupModule(w, r, result.GroupTypes)
}
}
func (h *Handler) DisplayGroupModuleHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
groupID := vars["groupid"]
if r.Method == "POST" || r.FormValue("beneficiaryid") != "" {
r.ParseForm()
beneficiaryID := r.FormValue("beneficiaryid")
err := h.applicationHandler.SubscribeBeneficiaryToGroup(r.Context(), groupID, beneficiaryID)
if err != nil {
log.Error().Err(err).Msg("error subscribing beneficiary to group")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/app/group_module/groups/"+groupID, http.StatusFound)
return
}
// Get current user's group from context
g := r.Context().Value(identification.GroupKey)
if g == nil {
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
currentUserGroup := g.(storage.Group)
// Parse search filter
searchFilter := ""
if searchFilters, ok := r.URL.Query()["search"]; ok && len(searchFilters) > 0 {
searchFilter = searchFilters[0]
}
result, err := h.applicationHandler.DisplayGroupModule(r.Context(), groupID, searchFilter, currentUserGroup)
if err != nil {
log.Error().Err(err).Msg("error displaying group module")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.DisplayGroupModule(w, r, result.GroupID, result.Accounts, result.CacheID,
result.Searched, result.Beneficiary, result.Group, result.AccountsBeneficiaire)
}
}

View File

@@ -0,0 +1,60 @@
package application
import (
"errors"
"net/http"
"git.coopgo.io/coopgo-apps/parcoursmob/core/application"
"git.coopgo.io/coopgo-apps/parcoursmob/renderer"
"git.coopgo.io/coopgo-apps/parcoursmob/services"
"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/identification"
"git.coopgo.io/coopgo-platform/groups-management/storage"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/spf13/viper"
)
type Handler struct {
cfg *viper.Viper
renderer *renderer.Renderer
applicationHandler *application.ApplicationHandler
idp *identification.IdentificationProvider
services *services.ServicesHandler
}
func NewHandler(cfg *viper.Viper, renderer *renderer.Renderer, applicationHandler *application.ApplicationHandler, idp *identification.IdentificationProvider, services *services.ServicesHandler) *Handler {
return &Handler{
cfg: cfg,
renderer: renderer,
applicationHandler: applicationHandler,
idp: idp,
services: services,
}
}
func (h *Handler) currentGroup(r *http.Request) (current_group storage.Group, err error) {
g := r.Context().Value(identification.GroupKey)
if g == nil {
return storage.Group{}, errors.New("current group not found")
}
current_group = g.(storage.Group)
return current_group, nil
}
func (h *Handler) currentUser(r *http.Request) (current_user_token *oidc.IDToken, current_user_claims map[string]any, err error) {
// Get current user ID
u := r.Context().Value(identification.IdtokenKey)
if u == nil {
return nil, nil, errors.New("current user not found")
}
current_user_token = u.(*oidc.IDToken)
// Get current user claims
c := r.Context().Value(identification.ClaimsKey)
if c == nil {
return nil, nil, errors.New("current user claims not found")
}
current_user_claims = c.(map[string]any)
return current_user_token, current_user_claims, nil
}

View File

@@ -0,0 +1,418 @@
package application
import (
"encoding/json"
"net/http"
"time"
"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/identification"
groupstorage "git.coopgo.io/coopgo-platform/groups-management/storage"
mobilityaccountsstorage "git.coopgo.io/coopgo-platform/mobility-accounts/storage"
savedsearchtypes "git.coopgo.io/coopgo-platform/saved-search/data/types"
"github.com/gorilla/mux"
"github.com/paulmach/orb/geojson"
"github.com/rs/zerolog/log"
)
func (h *Handler) JourneysSearchHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
log.Error().Err(err).Msg("error parsing form")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
// Extract and convert HTTP parameters
departureDate := r.FormValue("departuredate")
departureTime := r.FormValue("departuretime")
departure := r.FormValue("departure")
destination := r.FormValue("destination")
passengerID := r.FormValue("passengerid")
solidarityTransportExcludeDriver := r.FormValue("solidarity_transport_exclude_driver")
// Parse timezone and datetime
locTime, err := time.LoadLocation("Europe/Paris")
if err != nil {
log.Error().Err(err).Msg("timezone error")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
var departureDateTime time.Time
if departureDate != "" && departureTime != "" {
departureDateTime, err = time.ParseInLocation("2006-01-02 15:04", departureDate+" "+departureTime, locTime)
if err != nil {
log.Error().Err(err).Msg("error parsing datetime")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
log.Info().
Str("departureDate", departureDate).
Str("departureTime", departureTime).
Time("departureDateTime", departureDateTime).
Str("timezone", departureDateTime.Location().String()).
Str("RFC3339", departureDateTime.Format(time.RFC3339)).
Msg("Journey search - parsed departure datetime")
}
// Parse departure location
var departureGeo *geojson.Feature
if departure == "" && passengerID != "" {
// Get passenger address
p, err := h.services.GetAccount(passengerID)
if err != nil {
log.Error().Err(err).Msg("could not retrieve passenger")
http.Error(w, "Not Found", http.StatusNotFound)
return
}
departureBytes, err := json.Marshal(p.Data["address"])
if err != nil {
log.Error().Err(err).Any("address", p.Data["address"]).Msg("could not marshal address")
} else {
departureGeo, err = geojson.UnmarshalFeature(departureBytes)
if err != nil {
log.Error().Err(err).Msg("error unmarshalling passenger departure")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
} else if departure != "" {
departureGeo, err = geojson.UnmarshalFeature([]byte(departure))
if err != nil {
log.Error().Err(err).Msg("error unmarshalling departure")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
// Parse destination location
var destinationGeo *geojson.Feature
if destination != "" {
destinationGeo, err = geojson.UnmarshalFeature([]byte(destination))
if err != nil {
log.Error().Err(err).Msg("error unmarshalling destination")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
// Call business logic handler
result, err := h.applicationHandler.SearchJourneys(
r.Context(),
departureDateTime,
departureGeo,
destinationGeo,
passengerID,
solidarityTransportExcludeDriver,
)
if err != nil {
log.Error().Err(err).Msg("error in journey search")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Get beneficiaries for rendering
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)
beneficiaries, err := h.services.GetBeneficiariesInGroup(group)
if err != nil {
log.Error().Err(err).Msg("issue retrieving beneficiaries")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Get saved searches for this group when no search has been performed
var savedSearches []*savedsearchtypes.SavedSearch
var beneficiariesMap map[string]mobilityaccountsstorage.Account
if !result.Searched {
savedSearches, err = h.applicationHandler.GetSavedSearchesByOwner(r.Context(), group.ID)
if err != nil {
log.Error().Err(err).Msg("issue retrieving saved searches")
// Don't fail the request, just log the error
savedSearches = []*savedsearchtypes.SavedSearch{}
}
// Create beneficiaries map for template lookup
beneficiariesMap = make(map[string]mobilityaccountsstorage.Account)
for _, b := range beneficiaries {
beneficiariesMap[b.ID] = b
}
}
// Render HTTP response
h.renderer.JourneysSearch(
w, r,
result.CarpoolResults,
result.TransitResults,
result.VehicleResults,
result.Searched,
departureGeo,
destinationGeo,
departureDate,
departureTime,
result.DriverJourneys,
result.Drivers,
result.OrganizedCarpools,
beneficiaries,
result.KnowledgeBaseResults,
passengerID,
savedSearches,
beneficiariesMap,
)
}
}
func (h *Handler) SaveSearchHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
log.Error().Err(err).Msg("error parsing form")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
// Extract search parameters from URL query
query := r.URL.Query()
departureDate := query.Get("departuredate")
departureTime := query.Get("departuretime")
departure := query.Get("departure")
destination := query.Get("destination")
passengerID := query.Get("passengerid")
// Debug logging
log.Debug().
Str("departuredate", departureDate).
Str("departuretime", departureTime).
Str("departure", departure).
Str("destination", destination).
Str("passengerid", passengerID).
Str("query", r.URL.RawQuery).
Msg("SaveSearch request parameters")
// Parse timezone and datetime
locTime, err := time.LoadLocation("Europe/Paris")
if err != nil {
log.Error().Err(err).Msg("timezone error")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
var departureDateTime time.Time
if departureDate != "" && departureTime != "" {
departureDateTime, err = time.ParseInLocation("2006-01-02 15:04", departureDate+" "+departureTime, locTime)
if err != nil {
log.Error().Err(err).Msg("error parsing datetime")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
// Parse departure location
var departureGeo *geojson.Feature
if departure != "" {
departureGeo, err = geojson.UnmarshalFeature([]byte(departure))
if err != nil {
log.Error().Err(err).Msg("error unmarshalling departure")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
// Parse destination location
var destinationGeo *geojson.Feature
if destination != "" {
destinationGeo, err = geojson.UnmarshalFeature([]byte(destination))
if err != nil {
log.Error().Err(err).Msg("error unmarshalling destination")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
// Get group ID from session/context
g := r.Context().Value(identification.GroupKey)
if g == nil {
log.Error().Msg("group not found in request context")
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
group := g.(groupstorage.Group)
// Prepare additional data
additionalData := map[string]interface{}{}
if passengerID != "" {
additionalData["passenger_id"] = passengerID
}
// Save the search using the business logic
err = h.applicationHandler.SaveSearch(
r.Context(),
group.ID,
departureDateTime,
departureGeo,
destinationGeo,
additionalData,
)
if err != nil {
log.Error().Err(err).Msg("error saving search")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Redirect back to search with success message
http.Redirect(w, r, "/app/journeys/", http.StatusSeeOther)
}
}
func (h *Handler) DeleteSavedSearchHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Get search ID from URL parameters
vars := mux.Vars(r)
searchID := vars["id"]
if searchID == "" {
log.Error().Msg("search ID not provided")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
// Get group ID from session/context for authorization
g := r.Context().Value(identification.GroupKey)
if g == nil {
log.Error().Msg("group not found in request context")
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
group := g.(groupstorage.Group)
// Delete the saved search using the business logic
err := h.applicationHandler.DeleteSavedSearch(r.Context(), searchID, group.ID)
if err != nil {
log.Error().Err(err).Str("search_id", searchID).Msg("error deleting saved search")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
log.Info().Str("search_id", searchID).Str("group_id", group.ID).Msg("saved search deleted successfully")
// Redirect back to journeys page
http.Redirect(w, r, "/app/journeys/?deleted=1", http.StatusSeeOther)
}
}
func (h *Handler) JourneysSearchCompactHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
log.Error().Err(err).Msg("error parsing form")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
// Extract and convert HTTP parameters (same as JourneysSearchHTTPHandler)
departureDate := r.FormValue("departuredate")
departureTime := r.FormValue("departuretime")
departure := r.FormValue("departure")
destination := r.FormValue("destination")
passengerID := r.FormValue("passengerid")
solidarityTransportExcludeDriver := r.FormValue("solidarity_transport_exclude_driver")
// Parse timezone and datetime
locTime, err := time.LoadLocation("Europe/Paris")
if err != nil {
log.Error().Err(err).Msg("timezone error")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
var departureDateTime time.Time
if departureDate != "" && departureTime != "" {
departureDateTime, err = time.ParseInLocation("2006-01-02 15:04", departureDate+" "+departureTime, locTime)
if err != nil {
log.Error().Err(err).Msg("error parsing datetime")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
// Parse departure location
var departureGeo *geojson.Feature
if departure == "" && passengerID != "" {
// Get passenger address
p, err := h.services.GetAccount(passengerID)
if err != nil {
log.Error().Err(err).Msg("could not retrieve passenger")
http.Error(w, "Not Found", http.StatusNotFound)
return
}
departureBytes, err := json.Marshal(p.Data["address"])
if err != nil {
log.Error().Err(err).Any("address", p.Data["address"]).Msg("could not marshal address")
} else {
departureGeo, err = geojson.UnmarshalFeature(departureBytes)
if err != nil {
log.Error().Err(err).Msg("error unmarshalling passenger departure")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
} else if departure != "" {
departureGeo, err = geojson.UnmarshalFeature([]byte(departure))
if err != nil {
log.Error().Err(err).Msg("error unmarshalling departure")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
// Parse destination location
var destinationGeo *geojson.Feature
if destination != "" {
destinationGeo, err = geojson.UnmarshalFeature([]byte(destination))
if err != nil {
log.Error().Err(err).Msg("error unmarshalling destination")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
// Call business logic handler
result, err := h.applicationHandler.SearchJourneys(
r.Context(),
departureDateTime,
departureGeo,
destinationGeo,
passengerID,
solidarityTransportExcludeDriver,
)
if err != nil {
log.Error().Err(err).Msg("error in journey search")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Render compact HTTP response
h.renderer.JourneysSearchCompact(
w, r,
result.CarpoolResults,
result.TransitResults,
result.VehicleResults,
result.Searched,
departureGeo,
destinationGeo,
departureDate,
departureTime,
result.DriverJourneys,
result.Drivers,
result.OrganizedCarpools,
result.KnowledgeBaseResults,
passengerID,
)
}
}

View File

@@ -0,0 +1,72 @@
package application
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
"github.com/rs/zerolog/log"
)
func (h *Handler) MembersListHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
result, err := h.applicationHandler.GetMembers(r.Context())
if err != nil {
log.Error().Err(err).Msg("error retrieving members")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.MembersList(w, r, result.Accounts, result.CacheID, result.GroupsNames)
}
}
func (h *Handler) MemberDisplayHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
memberID := vars["adminid"]
result, err := h.applicationHandler.GetMemberData(r.Context(), memberID)
if err != nil {
log.Error().Err(err).Msg("error retrieving member data")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.MemberDisplay(w, r, result.Account, result.GroupsNames)
}
}
func (h *Handler) MemberUpdateHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
memberID := vars["adminid"]
if r.Method == "POST" {
firstName := r.PostFormValue("first_name")
lastName := r.PostFormValue("last_name")
email := r.PostFormValue("email")
phoneNumber := r.PostFormValue("phone_number")
gender := r.PostFormValue("gender")
updatedMemberID, err := h.applicationHandler.UpdateMember(r.Context(), memberID, firstName, lastName, email, phoneNumber, gender)
if err != nil {
log.Error().Err(err).Msg("error updating member")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/members/%s", updatedMemberID), http.StatusFound)
return
}
result, err := h.applicationHandler.GetMember(r.Context(), memberID)
if err != nil {
log.Error().Err(err).Msg("error retrieving member for update")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.MemberUpdate(w, r, result.Account)
}
}

View File

@@ -0,0 +1,596 @@
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)
}
}

View File

@@ -0,0 +1,36 @@
package application
import (
"net/http"
"github.com/rs/zerolog/log"
)
func (h *Handler) SendSMSHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
referer := r.Referer()
if err := r.ParseForm(); err != nil {
log.Error().Err(err).Msg("Bad request")
w.WriteHeader(http.StatusBadRequest)
return
}
message := r.PostFormValue("message")
beneficiaryID := r.PostFormValue("beneficiaryid")
err := h.applicationHandler.SendSMS(r.Context(), beneficiaryID, message)
if err != nil {
log.Error().Err(err).Msg("error sending SMS")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, referer, http.StatusFound)
}
}

View File

@@ -0,0 +1,636 @@
package application
import (
"encoding/json"
"fmt"
"io"
"net/http"
"sort"
"strings"
"time"
groupstorage "git.coopgo.io/coopgo-platform/groups-management/storage"
"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/identification"
"github.com/gorilla/mux"
"github.com/rs/zerolog/log"
)
func (h *Handler) SolidarityTransportOverviewHTTPHandler() 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 using the same field names as the old handler
tab := r.FormValue("tab")
if tab == "" {
tab = "solidarityService" // 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 (matching old handler logic)
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 == "solidarityService" {
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 solidarityHistory tab)
if tab == "solidarityHistory" {
histStatus = r.FormValue("status") // Note: history uses same field names as current
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.GetSolidarityTransportOverview(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 solidarity transport overview")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Build filters map for template (matching old handler format)
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.SolidarityTransportOverview(w, r, result.Accounts, result.AccountsMap, result.BeneficiariesMap, result.Bookings, result.BookingsHistory, filters, histFilters, tab, enrichedGeoFilters, archivedFilter)
}
}
func (h *Handler) SolidarityTransportCreateDriverHTTPHandler() 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")
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 field
var address any
if r.PostFormValue("address") != "" {
if err := json.Unmarshal([]byte(r.PostFormValue("address")), &address); err != nil {
log.Error().Err(err).Msg("failed parsing address JSON")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
// Parse JSON other_properties field
var otherProperties any
if r.PostFormValue("other_properties") != "" {
if err := json.Unmarshal([]byte(r.PostFormValue("other_properties")), &otherProperties); err != nil {
log.Error().Err(err).Msg("failed parsing other_properties JSON")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
driverID, err := h.applicationHandler.CreateSolidarityTransportDriver(
r.Context(),
firstName,
lastName,
email,
birthdate,
phoneNumber,
address,
gender,
otherProperties,
)
if err != nil {
log.Error().Err(err).Msg("error creating solidarity transport driver")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/solidarity-transport/drivers/%s", driverID), http.StatusFound)
return
}
h.renderer.SolidarityTransportCreateDriver(w, r)
}
}
func (h *Handler) SolidarityTransportUpdateDriverHTTPHandler() 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")
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 field
var address any
if r.PostFormValue("address") != "" {
if err := json.Unmarshal([]byte(r.PostFormValue("address")), &address); err != nil {
log.Error().Err(err).Msg("failed parsing address JSON")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
// Parse JSON other_properties field
var otherProperties any
if r.PostFormValue("other_properties") != "" {
if err := json.Unmarshal([]byte(r.PostFormValue("other_properties")), &otherProperties); err != nil {
log.Error().Err(err).Msg("failed parsing other_properties JSON")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
updatedDriverID, err := h.applicationHandler.UpdateSolidarityTransportDriver(
r.Context(),
driverID,
firstName,
lastName,
email,
birthdate,
phoneNumber,
address,
gender,
otherProperties,
)
if err != nil {
log.Error().Err(err).Msg("error updating solidarity transport driver")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/solidarity-transport/drivers/%s", updatedDriverID), http.StatusFound)
return
}
result, err := h.applicationHandler.GetSolidarityTransportDriver(r.Context(), driverID)
if err != nil {
log.Error().Err(err).Msg("error retrieving solidarity transport driver for update")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.SolidarityTransportUpdateDriver(w, r, result.Driver)
}
}
func (h *Handler) SolidarityTransportDriverDisplayHTTPHandler() 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.GetSolidarityTransportDriverData(r.Context(), driverID)
if err != nil {
log.Error().Err(err).Msg("error retrieving solidarity transport driver data")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.SolidarityTransportDriverDisplay(w, r, result.Driver, result.Availabilities, result.Documents, result.Bookings, result.BeneficiariesMap, result.Stats, result.WalletBalance, tab)
}
}
func (h *Handler) SolidarityTransportAddAvailabilityHTTPHandler() 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
starttime := r.PostFormValue("starttime")
endtime := r.PostFormValue("endtime")
// Parse JSON address field
var address any
if r.PostFormValue("address") != "" {
if err := json.Unmarshal([]byte(r.PostFormValue("address")), &address); err != nil {
log.Error().Err(err).Msg("failed parsing address JSON")
http.Error(w, "Bad Request", http.StatusBadRequest)
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.AddSolidarityTransportAvailability(r.Context(), driverID, starttime, endtime, address, days)
if err != nil {
log.Error().Err(err).Msg("error adding solidarity transport availability")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/solidarity-transport/drivers/%s", driverID), http.StatusFound)
}
}
func (h *Handler) SolidarityTransportArchiveDriverHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
driverID := vars["driverid"]
err := h.applicationHandler.ArchiveSolidarityTransportDriver(r.Context(), driverID)
if err != nil {
log.Error().Err(err).Msg("error archiving solidarity transport driver")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/solidarity-transport/drivers/%s", driverID), http.StatusFound)
}
}
func (h *Handler) SolidarityTransportUnarchiveDriverHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
driverID := vars["driverid"]
err := h.applicationHandler.UnarchiveSolidarityTransportDriver(r.Context(), driverID)
if err != nil {
log.Error().Err(err).Msg("error unarchiving solidarity transport driver")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/solidarity-transport/drivers/%s", driverID), http.StatusFound)
}
}
func (h *Handler) SolidarityTransportDriverDocumentsHTTPHandler() 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.AddSolidarityTransportDriverDocument(r.Context(), driverID, file, header.Filename, header.Size, documentType, documentName); err != nil {
log.Error().Err(err).Msg("error adding solidarity transport driver document")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/solidarity-transport/drivers/%s", driverID), http.StatusFound)
}
}
func (h *Handler) SolidarityTransportDocumentDownloadHTTPHandler() 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.GetSolidarityTransportDriverDocument(r.Context(), driverID, document)
if err != nil {
log.Error().Err(err).Msg("error retrieving solidarity transport 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) SolidarityTransportDeleteAvailabilityHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
driverID := vars["driverid"]
availabilityID := vars["availabilityid"]
err := h.applicationHandler.DeleteSolidarityTransportAvailability(r.Context(), driverID, availabilityID)
if err != nil {
log.Error().Err(err).Msg("error deleting solidarity transport availability")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/solidarity-transport/drivers/%s", driverID), http.StatusFound)
}
}
func (h *Handler) SolidarityTransportDriverJourneyHTTPHandler() 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"
returnWaitingTimeMinutes := 0
if r.PostFormValue("return_waiting_time") != "" {
fmt.Sscanf(r.PostFormValue("return_waiting_time"), "%d", &returnWaitingTimeMinutes)
}
bookingID, err := h.applicationHandler.CreateSolidarityTransportJourneyBooking(r.Context(), driverID, journeyID, passengerID, motivation, message, doNotSend, returnWaitingTimeMinutes)
if err != nil {
log.Error().Err(err).Msg("error creating solidarity transport journey booking")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
log.Info().Str("booking_id", bookingID).Msg("Solidarity transport booking created successfully")
http.Redirect(w, r, fmt.Sprintf("/app/solidarity-transport/"), 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.GetSolidarityTransportJourneyData(r.Context(), driverID, journeyID, passengerID, group)
if err != nil {
log.Error().Err(err).Msg("error retrieving solidarity transport journey data")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.SolidarityTransportDriverJourney(w, r, result.Journey, result.Driver, result.Passenger, result.Beneficiaries, result.PassengerWalletBalance, result.PricingResult)
}
}
func (h *Handler) SolidarityTransportDriverJourneyToggleNoreturnHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
driverID := vars["driverid"]
journeyID := vars["journeyid"]
err := h.applicationHandler.ToggleSolidarityTransportJourneyNoreturn(r.Context(), driverID, journeyID)
if err != nil {
log.Error().Err(err).Msg("error toggling solidarity transport journey noreturn")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/solidarity-transport/drivers/%s/journeys/%s", driverID, journeyID), http.StatusFound)
}
}
func (h *Handler) SolidarityTransportBookingDisplayHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
bookingID := vars["bookingid"]
result, err := h.applicationHandler.GetSolidarityTransportBookingData(r.Context(), bookingID)
if err != nil {
log.Error().Err(err).Msg("error retrieving solidarity transport booking")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.SolidarityTransportBookingDisplay(w, r, result.Booking, result.Driver, result.Passenger, result.PassengerWalletBalance)
}
}
func (h *Handler) SolidarityTransportDocumentDeleteHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
driverID := vars["driverid"]
document := vars["document"]
if err := h.applicationHandler.DeleteSolidarityTransportDriverDocument(r.Context(), driverID, document); err != nil {
log.Error().Err(err).Msg("error deleting solidarity transport driver document")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/solidarity-transport/drivers/%s", driverID), http.StatusFound)
}
}
func (h *Handler) SolidarityTransportBookingStatusHTTPHandler(action string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
bookingID := vars["bookingid"]
// Extract reason from form data for cancellations
reason := ""
if action == "cancel" && r.Method == "POST" {
r.ParseForm()
reason = r.PostFormValue("reason")
}
err := h.applicationHandler.UpdateSolidarityTransportBookingStatus(r.Context(), bookingID, action, reason, "", false)
if err != nil {
log.Error().Err(err).Msg("error updating solidarity transport booking status")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/solidarity-transport/bookings/%s", bookingID), http.StatusSeeOther)
}
}

View File

@@ -0,0 +1,40 @@
package application
import (
"net/http"
"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/identification"
"github.com/rs/zerolog/log"
)
func (h *Handler) SupportSendHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
c := r.Context().Value(identification.ClaimsKey)
if c == nil {
log.Error().Msg("no current user claims")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
currentUserClaims := c.(map[string]any)
if r.Method == "POST" {
// Parse form data
comment := r.PostFormValue("comment")
userEmail := currentUserClaims["email"].(string)
err := h.applicationHandler.SendSupportMessage(r.Context(), comment, userEmail)
if err != nil {
log.Error().Err(err).Msg("error sending support message")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/app/", http.StatusFound)
return
}
// GET request - show form
comment := r.FormValue("comment")
h.renderer.SupportSend(w, r, comment, currentUserClaims)
}
}

View File

@@ -0,0 +1,161 @@
package application
import (
"fmt"
"io"
"net/http"
"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/identification"
"git.coopgo.io/coopgo-platform/groups-management/storage"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/gorilla/mux"
"github.com/rs/zerolog/log"
)
func (h *Handler) VehiclesSearchHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
// Extract parameters
beneficiaryID := r.FormValue("beneficiaryid")
startDate := r.FormValue("startdate")
endDate := r.FormValue("enddate")
vehicleType := r.FormValue("type")
automatic := r.FormValue("automatic") == "on"
// Call business logic
result, err := h.applicationHandler.SearchVehicles(r.Context(), beneficiaryID, startDate, endDate, vehicleType, automatic)
if err != nil {
log.Error().Err(err).Msg("error searching vehicles")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Render response
h.renderer.VehiclesSearch(w, r, result.Beneficiaries, result.Searched, result.Vehicles, result.Beneficiary,
result.StartDate, result.EndDate, result.MandatoryDocuments, result.FileTypesMap,
result.BeneficiaryDocuments, result.VehicleType, result.Automatic, result.VehicleTypes, result.Groups)
}
}
func (h *Handler) BookVehicleHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Extract URL parameters
vars := mux.Vars(r)
vehicleID := vars["vehicleid"]
beneficiaryID := vars["beneficiaryid"]
// Extract user context
currentUserToken := r.Context().Value(identification.IdtokenKey).(*oidc.IDToken)
currentUserClaims := r.Context().Value(identification.ClaimsKey).(map[string]any)
currentGroup := r.Context().Value(identification.GroupKey)
// Parse multipart form
if err := r.ParseMultipartForm(100 * 1024 * 1024); err != nil {
log.Error().Err(err).Msg("error parsing multipart form")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
// Extract form data
startDate := r.FormValue("startdate")
endDate := r.FormValue("enddate")
// Extract documents
documents := make(map[string]io.Reader)
documentHeaders := make(map[string]string)
existingDocs := make(map[string]string)
// Get mandatory document types from config
mandatoryDocTypes := h.cfg.GetStringSlice("modules.fleets.booking_documents.mandatory")
for _, docType := range mandatoryDocTypes {
existingFile := r.FormValue("type-" + docType)
if existingFile != "" {
existingDocs[docType] = existingFile
} else {
file, header, err := r.FormFile("doc-" + docType)
if err != nil {
log.Error().Err(err).Msg("missing required document: " + docType)
http.Error(w, "Document manquant : "+docType, http.StatusBadRequest)
return
}
documents[docType] = file
documentHeaders[docType] = header.Filename
}
}
// Call business logic
result, err := h.applicationHandler.BookVehicle(r.Context(), vehicleID, beneficiaryID, startDate, endDate,
documents, documentHeaders, existingDocs, currentUserToken.Subject, currentUserClaims, currentGroup)
if err != nil {
log.Error().Err(err).Msg("error booking vehicle")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Redirect to booking details
http.Redirect(w, r, fmt.Sprintf("/app/vehicles/bookings/%s", result.BookingID), http.StatusFound)
}
}
func (h *Handler) VehicleBookingDisplayHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
bookingID := vars["bookingid"]
result, err := h.applicationHandler.GetVehicleBookingDetails(r.Context(), bookingID)
if err != nil {
log.Error().Err(err).Msg("error getting booking details")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.VehicleBookingDisplay(w, r, result.Booking, result.Vehicle, result.Beneficiary,
result.Group, result.Documents, result.FileTypesMap)
}
}
func (h *Handler) VehiclesBookingsListHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Get group from context
g := r.Context().Value(identification.GroupKey)
if g == nil {
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
group := g.(storage.Group)
result, err := h.applicationHandler.GetVehicleBookingsList(r.Context(), group.ID)
if err != nil {
log.Error().Err(err).Msg("error getting bookings list")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.VehicleBookingsList(w, r, result.Bookings, result.VehiclesMap, result.GroupsMap)
}
}
func (h *Handler) BookingDocumentDownloadHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
bookingID := vars["bookingid"]
document := vars["document"]
fileReader, contentType, err := h.applicationHandler.GetBookingDocument(r.Context(), bookingID, document)
if err != nil {
log.Error().Err(err).Msg("error getting booking document")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", contentType)
if _, err = io.Copy(w, fileReader); err != nil {
log.Error().Err(err).Msg("error writing document to response")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
}
}

View File

@@ -0,0 +1,408 @@
package application
import (
"encoding/json"
"fmt"
"net/http"
"time"
"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/identification"
"git.coopgo.io/coopgo-platform/groups-management/storage"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/gorilla/mux"
"github.com/rs/zerolog/log"
)
func (h *Handler) VehiclesManagementOverviewHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Extract filter parameters from request
groupID := ""
if g := r.Context().Value(identification.GroupKey); g != nil {
group := g.(storage.Group)
groupID = group.ID
}
result, err := h.applicationHandler.GetVehiclesManagementOverview(r.Context(), groupID)
if err != nil {
log.Error().Err(err).Msg("error retrieving vehicles management overview")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.VehiclesManagementOverview(w, r, result.Vehicles, result.VehiclesMap, result.DriversMap, result.Bookings)
}
}
func (h *Handler) VehiclesManagementBookingsListHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Extract filter parameters from request
groupID := ""
if g := r.Context().Value(identification.GroupKey); g != nil {
group := g.(storage.Group)
groupID = group.ID
}
// Extract filter parameters from query
status := r.URL.Query().Get("status")
dateStart := r.URL.Query().Get("date_start")
dateEnd := r.URL.Query().Get("date_end")
// Default to last month if no dates specified
if dateStart == "" {
dateStart = time.Now().AddDate(0, -1, 0).Format("2006-01-02")
}
if dateEnd == "" {
dateEnd = time.Now().Format("2006-01-02")
}
result, err := h.applicationHandler.GetVehiclesManagementBookingsList(r.Context(), groupID, status, dateStart, dateEnd)
if err != nil {
log.Error().Err(err).Msg("error retrieving vehicles management bookings list")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Prepare filters map for template
filters := map[string]string{
"status": status,
"date_start": dateStart,
"date_end": dateEnd,
}
h.renderer.VehiclesManagementBookingsList(w, r, result.VehiclesMap, result.DriversMap, result.Bookings, result.CacheID, filters)
}
}
func (h *Handler) VehiclesFleetAddHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
// Get group from context
g := r.Context().Value(identification.GroupKey)
if g == nil {
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
// Parse form data
if err := r.ParseForm(); err != nil {
log.Error().Err(err).Msg("error parsing form")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
// Parse address JSON
var address map[string]any
if addressStr := r.FormValue("address"); addressStr != "" {
if err := json.Unmarshal([]byte(addressStr), &address); err != nil {
log.Error().Err(err).Msg("error parsing address JSON")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
}
// Parse other_properties from form fields
otherProperties := make(map[string]any)
vehicleOptionalFields := h.cfg.Get("modules.fleets.vehicle_optional_fields")
log.Debug().Msgf("CREATE: vehicleOptionalFields type: %T, value: %+v", vehicleOptionalFields, vehicleOptionalFields)
if fields, ok := vehicleOptionalFields.([]map[string]any); ok {
log.Debug().Msgf("CREATE: Successfully cast to []map[string]any, length: %d", len(fields))
for _, field := range fields {
log.Debug().Any("field", field).Msg("vehicle other properties field handling")
if fieldName, ok := field["name"].(string); ok {
value := r.FormValue(fieldName)
log.Debug().Msgf("CREATE: Field %s: value='%s'", fieldName, value)
if value != "" {
otherProperties[fieldName] = value
}
}
}
} else {
log.Debug().Msg("CREATE: Failed to cast vehicleOptionalFields to []map[string]any")
}
log.Debug().Msgf("CREATE: Final otherProperties: %+v", otherProperties)
vehicleID, err := h.applicationHandler.CreateVehicle(r.Context(),
r.FormValue("name"),
r.FormValue("type"),
r.FormValue("informations"),
r.FormValue("licence_plate"),
r.FormValue("kilometers"),
r.FormValue("automatic") == "on",
address,
otherProperties,
)
if err != nil {
log.Error().Err(err).Msg("error creating vehicle")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/vehicles-management/fleet/%s", vehicleID), http.StatusFound)
return
}
// GET request - show form
vehicleTypes, err := h.applicationHandler.GetVehicleTypes(r.Context())
if err != nil {
log.Error().Err(err).Msg("error getting vehicle types")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.VehiclesFleetAdd(w, r, vehicleTypes)
}
}
func (h *Handler) VehiclesFleetDisplayHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
vehicleID := vars["vehicleid"]
result, err := h.applicationHandler.GetVehicleDisplay(r.Context(), vehicleID)
if err != nil {
log.Error().Err(err).Msg("error retrieving vehicle display")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.VehiclesFleetDisplay(w, r, result.Vehicle, result.Beneficiaries)
}
}
func (h *Handler) VehicleManagementBookingDisplayHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
bookingID := vars["bookingid"]
if r.Method == "POST" {
// Parse form data
r.ParseForm()
err := h.applicationHandler.UpdateBooking(r.Context(), bookingID,
r.FormValue("startdate"),
r.FormValue("enddate"),
r.FormValue("unavailablefrom"),
r.FormValue("unavailableto"),
)
if err != nil {
log.Error().Err(err).Msg("error updating booking")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
}
result, err := h.applicationHandler.GetBookingDisplay(r.Context(), bookingID)
if err != nil {
log.Error().Err(err).Msg("error retrieving booking display")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.VehicleManagementBookingDisplay(w, r, result.Booking, result.Vehicle, result.Beneficiary, result.Group, result.Documents, result.FileTypesMap, result.Alternatives)
}
}
func (h *Handler) VehicleManagementBookingChangeVehicleHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
bookingID := vars["bookingid"]
r.ParseForm()
newVehicle := r.FormValue("vehicle")
err := h.applicationHandler.ChangeBookingVehicle(r.Context(), bookingID, newVehicle)
if err != nil {
log.Error().Err(err).Msg("error changing booking vehicle")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/vehicles-management/bookings/%s", bookingID), http.StatusFound)
}
}
func (h *Handler) VehiclesFleetMakeUnavailableHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Get context values
g := r.Context().Value(identification.GroupKey)
if g == nil {
log.Error().Msg("no current group")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
u := r.Context().Value(identification.IdtokenKey)
if u == nil {
log.Error().Msg("no current user")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
currentUserToken := u.(*oidc.IDToken)
c := r.Context().Value(identification.ClaimsKey)
if c == nil {
log.Error().Msg("no current user claims")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
currentUserClaims := c.(map[string]any)
vars := mux.Vars(r)
vehicleID := vars["vehicleid"]
r.ParseForm()
err := h.applicationHandler.MakeVehicleUnavailable(r.Context(), vehicleID,
r.FormValue("unavailablefrom"),
r.FormValue("unavailableto"),
r.FormValue("comment"),
currentUserToken.Subject,
currentUserClaims,
)
if err != nil {
log.Error().Err(err).Msg("error making vehicle unavailable")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/vehicles-management/fleet/%s", vehicleID), http.StatusFound)
}
}
func (h *Handler) DeleteBookingHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
bookingID := vars["bookingid"]
// Get form data
motif := r.FormValue("motif")
// Get current user information
currentUserToken := r.Context().Value(identification.IdtokenKey).(*oidc.IDToken)
currentUserClaims := r.Context().Value(identification.ClaimsKey).(map[string]any)
currentGroup := r.Context().Value(identification.GroupKey)
err := h.applicationHandler.UnbookVehicle(r.Context(), bookingID, motif, currentUserToken.Subject, currentUserClaims, currentGroup)
if err != nil {
log.Error().Err(err).Msg("error unbooking vehicle")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/app/vehicles-management/bookings/", http.StatusSeeOther)
}
}
func (h *Handler) UnbookingVehicleHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
bookingID := vars["bookingid"]
if r.Method == "POST" {
// Extract user and group info from context
currentUserToken := r.Context().Value(identification.IdtokenKey).(*oidc.IDToken)
currentUserClaims := r.Context().Value(identification.ClaimsKey).(map[string]any)
currentGroup := r.Context().Value(identification.GroupKey)
motif := r.FormValue("motif")
err := h.applicationHandler.UnbookVehicle(r.Context(), bookingID, motif,
currentUserToken.Subject, currentUserClaims, currentGroup)
if err != nil {
log.Error().Err(err).Msg("error unbooking vehicle")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/app/vehicles-management/", http.StatusFound)
return
}
// GET request - show form
booking, err := h.applicationHandler.GetBookingForUnbooking(r.Context(), bookingID)
if err != nil {
log.Error().Err(err).Msg("error retrieving booking for unbooking")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.UnbookingVehicle(w, r, booking)
}
}
func (h *Handler) VehiclesFleetUpdateHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
vehicleID := vars["vehicleid"]
if r.Method == "POST" {
if err := r.ParseForm(); err != nil {
log.Error().Err(err).Msg("error parsing form")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
// Parse address JSON
var address map[string]any
if addressStr := r.FormValue("address"); addressStr != "" {
if err := json.Unmarshal([]byte(addressStr), &address); err != nil {
log.Error().Err(err).Msg("error parsing address JSON")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
}
// Parse other_properties from form fields
otherProperties := make(map[string]any)
vehicleOptionalFields := h.cfg.Get("modules.fleets.vehicle_optional_fields")
log.Debug().Msgf("UPDATE: vehicleOptionalFields type: %T, value: %+v", vehicleOptionalFields, vehicleOptionalFields)
if fields, ok := vehicleOptionalFields.([]any); ok {
log.Debug().Msgf("UPDATE: Successfully cast to []map[string]any, length: %d", len(fields))
for _, f := range fields {
if field, ok := f.(map[string]any); ok {
log.Debug().Any("field", field).Msg("vehicle other properties field handling (update)")
if fieldName, ok := field["name"].(string); ok {
value := r.FormValue(fieldName)
log.Debug().Msgf("UPDATE: Field %s: value='%s'", fieldName, value)
if value != "" {
otherProperties[fieldName] = value
}
}
}
}
} else {
log.Debug().Msg("UPDATE: Failed to cast vehicleOptionalFields to []map[string]any")
}
log.Debug().Msgf("UPDATE: Final otherProperties: %+v", otherProperties)
updatedVehicleID, err := h.applicationHandler.UpdateVehicle(r.Context(), vehicleID,
r.FormValue("name"),
r.FormValue("type"),
r.FormValue("informations"),
r.FormValue("licence_plate"),
r.FormValue("kilometers"),
r.FormValue("automatic") == "on",
address,
otherProperties,
)
if err != nil {
log.Error().Err(err).Msg("error updating vehicle")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, fmt.Sprintf("/app/vehicles-management/fleet/%s", updatedVehicleID), http.StatusFound)
return
}
// GET request - show form
result, err := h.applicationHandler.GetVehicleForUpdate(r.Context(), vehicleID)
if err != nil {
log.Error().Err(err).Msg("error retrieving vehicle for update")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.VehiclesFleetUpdate(w, r, result.Vehicle, result.VehicleTypes)
}
}

View File

@@ -0,0 +1,58 @@
package application
import (
"net/http"
"net/url"
"strconv"
"github.com/gorilla/mux"
"github.com/rs/zerolog/log"
)
func (h *Handler) CreditWalletHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
userid := vars["userid"]
if r.Method != "POST" {
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
return
}
r.ParseForm()
amountStr := r.FormValue("amount")
paymentMethod := r.FormValue("payment_method")
description := r.FormValue("description")
amount, err := strconv.ParseFloat(amountStr, 64)
if err != nil {
log.Error().Err(err).Msg("could not read amount")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
if paymentMethod == "" {
paymentMethod = "Paiement en espèce (MMS)"
}
err = h.applicationHandler.CreditWallet(r.Context(), userid, amount, paymentMethod, description)
if err != nil {
log.Error().Err(err).Msg("could not credit wallet")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
refererURL, err := url.Parse(r.Referer())
if err != nil {
http.Redirect(w, r, r.Referer(), http.StatusFound)
return
}
query := refererURL.Query()
query.Set("tab", "wallet")
refererURL.RawQuery = query.Encode()
http.Redirect(w, r, refererURL.String(), http.StatusFound)
}
}