lot of new functionalities
This commit is contained in:
@@ -100,7 +100,7 @@ func (renderer *Renderer) AdminStatVehicles(w http.ResponseWriter, r *http.Reque
|
||||
renderer.Render("vehicles_state", w, r, files, state)
|
||||
}
|
||||
|
||||
func (renderer *Renderer) AdminStatBookings(w http.ResponseWriter, r *http.Request, vehicles map[string]fleetsstorage.Vehicle, bookings []fleetsstorage.Booking, admingroups map[string]any, beneficiaries map[string]any) {
|
||||
func (renderer *Renderer) AdminStatBookings(w http.ResponseWriter, r *http.Request, vehicles map[string]fleetsstorage.Vehicle, bookings []fleetsstorage.Booking, admingroups map[string]any, beneficiaries map[string]any, filters map[string]string) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.administration.bookings_list.files")
|
||||
state := NewState(r, renderer.ThemeConfig, administrationMenu)
|
||||
state.ViewState = map[string]any{
|
||||
@@ -108,6 +108,7 @@ func (renderer *Renderer) AdminStatBookings(w http.ResponseWriter, r *http.Reque
|
||||
"bookings": bookings,
|
||||
"admingroups": admingroups,
|
||||
"beneficiaries_map": beneficiaries,
|
||||
"filters": filters,
|
||||
}
|
||||
|
||||
renderer.Render("bookings_stats", w, r, files, state)
|
||||
|
||||
@@ -15,6 +15,7 @@ type BeneficiariesListState struct {
|
||||
Count int `json:"count"`
|
||||
CacheId string `json:"cache_id"`
|
||||
Beneficiaries []mobilityaccountsstorage.Account `json:"beneficiaries"`
|
||||
Archived bool `json:"archived"`
|
||||
}
|
||||
|
||||
func (s BeneficiariesListState) JSON() template.JS {
|
||||
@@ -29,7 +30,7 @@ func (s BeneficiariesListState) JSONWithLimits(a int, b int) template.JS {
|
||||
return s.JSON()
|
||||
}
|
||||
|
||||
func (renderer *Renderer) BeneficiariesList(w http.ResponseWriter, r *http.Request, accounts []mobilityaccountsstorage.Account, cacheid string) {
|
||||
func (renderer *Renderer) BeneficiariesList(w http.ResponseWriter, r *http.Request, accounts []mobilityaccountsstorage.Account, cacheid string, archived bool) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.beneficiaries.list.files")
|
||||
|
||||
state := NewState(r, renderer.ThemeConfig, beneficiariesMenu)
|
||||
@@ -37,6 +38,7 @@ func (renderer *Renderer) BeneficiariesList(w http.ResponseWriter, r *http.Reque
|
||||
Count: len(accounts),
|
||||
CacheId: cacheid,
|
||||
Beneficiaries: accounts,
|
||||
Archived: archived,
|
||||
}
|
||||
|
||||
renderer.Render("beneficiaries_list", w, r, files, state)
|
||||
@@ -58,23 +60,29 @@ type BeneficiariesDisplayState struct {
|
||||
Beneficiary any
|
||||
}
|
||||
|
||||
func (renderer *Renderer) BeneficiaryDisplay(w http.ResponseWriter, r *http.Request, beneficiary any, bookings []fleetsstorage.Booking, organizations []any, beneficiaries_file_types []string, file_types_map map[string]string, documents any, event interface{}, diags []any, solidarityTransportStats any, walletBalance float64) {
|
||||
func (renderer *Renderer) BeneficiaryDisplay(w http.ResponseWriter, r *http.Request, beneficiary any, bookings []fleetsstorage.Booking, organizations []any, beneficiaries_file_types []string, file_types_map map[string]string, documents any, event interface{}, solidarityTransportStats any, solidarityTransportBookings any, solidarityDriversMap any, organizedCarpoolStats any, organizedCarpoolBookings any, organizedCarpoolDriversMap any, walletBalance float64, tab string) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.beneficiaries.display.files")
|
||||
profileFields := renderer.GlobalConfig.Get("modules.beneficiaries.profile_optional_fields")
|
||||
|
||||
state := NewState(r, renderer.ThemeConfig, beneficiariesMenu)
|
||||
state.ViewState = map[string]any{
|
||||
"beneficiary": beneficiary,
|
||||
"bookings": bookings,
|
||||
"beneficiaries_file_types": beneficiaries_file_types,
|
||||
"file_types_map": file_types_map,
|
||||
"documents": documents,
|
||||
"organizations": organizations,
|
||||
"event": event,
|
||||
"diags": diags,
|
||||
"solidarity_transport_stats": solidarityTransportStats,
|
||||
"profile_optional_fields": profileFields,
|
||||
"wallet_balance": walletBalance,
|
||||
"beneficiary": beneficiary,
|
||||
"bookings": bookings,
|
||||
"beneficiaries_file_types": beneficiaries_file_types,
|
||||
"file_types_map": file_types_map,
|
||||
"documents": documents,
|
||||
"organizations": organizations,
|
||||
"event": event,
|
||||
"solidarity_transport_stats": solidarityTransportStats,
|
||||
"solidarity_transport_bookings": solidarityTransportBookings,
|
||||
"solidarity_transport_drivers_map": solidarityDriversMap,
|
||||
"organized_carpool_stats": organizedCarpoolStats,
|
||||
"organized_carpool_bookings": organizedCarpoolBookings,
|
||||
"organized_carpool_drivers_map": organizedCarpoolDriversMap,
|
||||
"profile_optional_fields": profileFields,
|
||||
"wallet_balance": walletBalance,
|
||||
"tab": tab,
|
||||
"search_view": renderer.GlobalConfig.GetString("modules.journeys.search_view"),
|
||||
}
|
||||
renderer.Render("beneficiaries_display", w, r, files, state)
|
||||
}
|
||||
|
||||
@@ -5,11 +5,12 @@ import (
|
||||
|
||||
agendastorage "git.coopgo.io/coopgo-platform/agenda/storage"
|
||||
fleetstorage "git.coopgo.io/coopgo-platform/fleets/storage"
|
||||
mobilityaccountsstorage "git.coopgo.io/coopgo-platform/mobility-accounts/storage"
|
||||
)
|
||||
|
||||
const dashboardMenu = "dashboard"
|
||||
|
||||
func (renderer *Renderer) Dashboard(w http.ResponseWriter, r *http.Request, accounts []any, nbaccounts int, count_members int, events []agendastorage.Event, fleets []fleetstorage.Booking) {
|
||||
func (renderer *Renderer) Dashboard(w http.ResponseWriter, r *http.Request, accounts any, nbaccounts int, count_members int, events []agendastorage.Event, fleets []fleetstorage.Booking, solidarityDrivers []mobilityaccountsstorage.Account, organizedCarpoolDrivers []mobilityaccountsstorage.Account, driverAddressGeo string, enrichedGeoFilters []map[string]string) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.dashboard.files")
|
||||
state := NewState(r, renderer.ThemeConfig, dashboardMenu)
|
||||
state.ViewState = map[string]any{
|
||||
@@ -17,9 +18,16 @@ func (renderer *Renderer) Dashboard(w http.ResponseWriter, r *http.Request, acco
|
||||
"count": nbaccounts,
|
||||
"latest": accounts,
|
||||
},
|
||||
"count_members": count_members,
|
||||
"events": events,
|
||||
"fleets": fleets,
|
||||
"count_members": count_members,
|
||||
"events": events,
|
||||
"fleets": fleets,
|
||||
"solidarity_drivers": solidarityDrivers,
|
||||
"organized_carpool_drivers": organizedCarpoolDrivers,
|
||||
"geography_filters_enabled": renderer.GlobalConfig.GetBool("geography.filters.enabled"),
|
||||
"geography_filters_list": enrichedGeoFilters,
|
||||
"filters": map[string]any{
|
||||
"driver_address_geo": driverAddressGeo,
|
||||
},
|
||||
}
|
||||
|
||||
renderer.Render("dashboard", w, r, files, state)
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
package renderer
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
diagsstorage "git.coopgo.io/coopgo-platform/diags/storage"
|
||||
)
|
||||
|
||||
const diagsMenu = "diags"
|
||||
|
||||
func (renderer *Renderer) DiagsHome(w http.ResponseWriter, r *http.Request, diags []diagsstorage.Diag, groups map[string]any) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.diags.list.files")
|
||||
state := NewState(r, renderer.ThemeConfig, diagsMenu)
|
||||
state.ViewState = map[string]any{
|
||||
"diags": diags,
|
||||
"groups": groups,
|
||||
}
|
||||
|
||||
renderer.Render("diags home", w, r, files, state)
|
||||
}
|
||||
|
||||
func (renderer *Renderer) DiagsHistory(w http.ResponseWriter, r *http.Request, diags []diagsstorage.Diag) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.diags.history.files")
|
||||
state := NewState(r, renderer.ThemeConfig, diagsMenu)
|
||||
state.ViewState = map[string]any{
|
||||
"diags": diags,
|
||||
}
|
||||
|
||||
renderer.Render("diags history", w, r, files, state)
|
||||
}
|
||||
|
||||
func (renderer *Renderer) BeneficiariesCreateDiag(w http.ResponseWriter, r *http.Request, beneficiary string, diags_file_types []string, file_types_map map[string]string, documents any) {
|
||||
state := NewState(r, renderer.ThemeConfig, diagsMenu)
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.beneficiaries.create_diag.files")
|
||||
state.ViewState = map[string]any{
|
||||
"beneficiary": beneficiary,
|
||||
"diags_file_types": diags_file_types,
|
||||
"file_types_map": file_types_map,
|
||||
"documents": documents,
|
||||
}
|
||||
renderer.Render("diag create for beneficiary", w, r, files, state)
|
||||
}
|
||||
|
||||
func (renderer *Renderer) VehiclesCreateDiag(w http.ResponseWriter, r *http.Request, vehicle string) {
|
||||
state := NewState(r, renderer.ThemeConfig, diagsMenu)
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.vehicles_management.create_vehicle_diag.files")
|
||||
state.ViewState = map[string]any{
|
||||
"vehicle": vehicle,
|
||||
}
|
||||
renderer.Render("diag create for vehicle", w, r, files, state)
|
||||
}
|
||||
|
||||
func (renderer *Renderer) BookingsCreateDiag(w http.ResponseWriter, r *http.Request, booking string) {
|
||||
state := NewState(r, renderer.ThemeConfig, diagsMenu)
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.vehicles_management.create_booking_diag.files")
|
||||
state.ViewState = map[string]any{
|
||||
"booking": booking,
|
||||
}
|
||||
renderer.Render("diag create for booking", w, r, files, state)
|
||||
}
|
||||
|
||||
func (renderer *Renderer) VehicleBookingsCreateDiag(w http.ResponseWriter, r *http.Request, booking string) {
|
||||
state := NewState(r, renderer.ThemeConfig, diagsMenu)
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.vehicles.create_booking_diag.files")
|
||||
state.ViewState = map[string]any{
|
||||
"booking": booking,
|
||||
}
|
||||
renderer.Render("diag create for booking", w, r, files, state)
|
||||
}
|
||||
|
||||
func (renderer *Renderer) DiagsDisplayDiag(w http.ResponseWriter, r *http.Request, diag any, diags_file_types []string, file_types_map map[string]string, documents any) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.diags.display_diag.files")
|
||||
state := NewState(r, renderer.ThemeConfig, diagsMenu)
|
||||
|
||||
state.ViewState = map[string]any{
|
||||
"diag": diag,
|
||||
"diags_file_types": diags_file_types,
|
||||
"file_types_map": file_types_map,
|
||||
"documents": documents,
|
||||
}
|
||||
|
||||
renderer.Render("diags create diag", w, r, files, state)
|
||||
}
|
||||
|
||||
func (renderer *Renderer) DiagsHistoryDiag(w http.ResponseWriter, r *http.Request, diag any) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.diags.history_diag.files")
|
||||
state := NewState(r, renderer.ThemeConfig, diagsMenu)
|
||||
state.ViewState = map[string]any{
|
||||
"diag": diag,
|
||||
}
|
||||
|
||||
renderer.Render("diags history diag", w, r, files, state)
|
||||
}
|
||||
|
||||
func (renderer *Renderer) DiagUpdate(w http.ResponseWriter, r *http.Request, diag any) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.diags.update.files")
|
||||
state := NewState(r, renderer.ThemeConfig, diagsMenu)
|
||||
|
||||
state.ViewState = map[string]any{
|
||||
"diag": diag,
|
||||
}
|
||||
|
||||
renderer.Render("diag_update", w, r, files, state)
|
||||
}
|
||||
|
||||
func (renderer *Renderer) DiagDelete(w http.ResponseWriter, r *http.Request, diag any) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.diags.delete.files")
|
||||
state := NewState(r, renderer.ThemeConfig, diagsMenu)
|
||||
|
||||
state.ViewState = map[string]any{
|
||||
"diag": diag,
|
||||
}
|
||||
|
||||
renderer.Render("diag_deleteDiag", w, r, files, state)
|
||||
}
|
||||
@@ -5,12 +5,13 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"math"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
filestorage "git.coopgo.io/coopgo-apps/parcoursmob/utils/storage"
|
||||
validatedprofile "git.coopgo.io/coopgo-apps/parcoursmob/utils/validated-profile"
|
||||
filestorage "git.coopgo.io/coopgo-apps/parcoursmob/core/utils/storage"
|
||||
validatedprofile "git.coopgo.io/coopgo-apps/parcoursmob/core/utils/validated-profile"
|
||||
groupsstorage "git.coopgo.io/coopgo-platform/groups-management/storage"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/viper"
|
||||
@@ -151,6 +152,24 @@ func strval(v interface{}) string {
|
||||
}
|
||||
}
|
||||
|
||||
// JSEscape escapes a string for safe use in JavaScript
|
||||
func JSEscape(s string) template.JS {
|
||||
return template.JS(template.JSEscapeString(s))
|
||||
}
|
||||
|
||||
// IsGuaranteedTripMotivation checks if a motivation is a guaranteed trip
|
||||
func IsGuaranteedTripMotivation(globalConfig *viper.Viper) func(string) bool {
|
||||
return func(motivation string) bool {
|
||||
guaranteedMotivations := globalConfig.GetStringSlice("modules.solidarity_transport.guaranteed_trip_motivations")
|
||||
for _, m := range guaranteedMotivations {
|
||||
if m == motivation {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// GetTemplateFuncMap returns the common template functions for rendering
|
||||
func GetTemplateFuncMap(group groupsstorage.Group, globalConfig *viper.Viper, fileStorage filestorage.FileStorage) template.FuncMap {
|
||||
return template.FuncMap{
|
||||
@@ -162,14 +181,17 @@ func GetTemplateFuncMap(group groupsstorage.Group, globalConfig *viper.Viper, fi
|
||||
"json": JSON,
|
||||
"rawjson": RawJSON,
|
||||
"unescapeHTML": UnescapeHTML,
|
||||
"jsEscape": JSEscape,
|
||||
"walkingLength": WalkingLength,
|
||||
"divideFloat64": Divide[float64],
|
||||
"divideInt": Divide[int],
|
||||
"typeOf": reflect.TypeOf,
|
||||
"shortDuration": ShortDuration,
|
||||
"round2": Round2,
|
||||
"beneficiaryValidatedProfile": validatedprofile.ValidateProfile(globalConfig.Sub("modules.beneficiaries.validated_profile")),
|
||||
"solidarityDriverValidatedProfile": validatedprofile.ValidateProfile(globalConfig.Sub("modules.solidarity_transport.drivers.validated_profile")),
|
||||
"carpoolDriverValidatedProfile": validatedprofile.ValidateProfile(globalConfig.Sub("modules.organized_carpool.drivers.validated_profile")),
|
||||
"isGuaranteedTripMotivation": IsGuaranteedTripMotivation(globalConfig),
|
||||
"beneficiaryDocuments": func(id string) []filestorage.FileInfo {
|
||||
return fileStorage.List(filestorage.PREFIX_BENEFICIARIES + "/" + id)
|
||||
},
|
||||
@@ -211,3 +233,25 @@ func ShortDuration(d interface{}) string {
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Round2 rounds a float64 to 2 decimal places to avoid floating point issues
|
||||
func Round2(value interface{}) float64 {
|
||||
var f float64
|
||||
switch v := value.(type) {
|
||||
case float64:
|
||||
f = v
|
||||
case *float64:
|
||||
if v != nil {
|
||||
f = *v
|
||||
}
|
||||
case float32:
|
||||
f = float64(v)
|
||||
case int:
|
||||
f = float64(v)
|
||||
case int64:
|
||||
f = float64(v)
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
return math.Round(f*100) / 100
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ func (s BeneficiariesCovoiturage) JSONWithLimits(a int, b int) template.JS {
|
||||
return s.JSON()
|
||||
}
|
||||
|
||||
func (renderer *Renderer) JourneysSearch(w http.ResponseWriter, r *http.Request, carpools []*geojson.FeatureCollection, transitjourneys any, vehicles any, searched bool, departure any, destination any, departuredate string, departuretime string, driverJourneys any, solidarityDrivers any, organizedCarpools any, beneficiaries any, kbData any, passengerid string) {
|
||||
func (renderer *Renderer) JourneysSearch(w http.ResponseWriter, r *http.Request, carpools []*geojson.FeatureCollection, transitjourneys any, vehicles any, searched bool, departure any, destination any, departuredate string, departuretime string, driverJourneys any, solidarityDrivers any, organizedCarpools any, beneficiaries any, kbData any, passengerid string, savedSearches any, beneficiariesMap any) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.journeys.search.files")
|
||||
state := NewState(r, renderer.ThemeConfig, journeysMenu)
|
||||
journeyTabs := renderer.ThemeConfig.Get("journey_tabs")
|
||||
@@ -53,10 +53,13 @@ func (renderer *Renderer) JourneysSearch(w http.ResponseWriter, r *http.Request,
|
||||
"driver_journeys": driverJourneys,
|
||||
"solidarity_drivers": solidarityDrivers,
|
||||
"querystring": r.URL.RawQuery,
|
||||
"beneficiaries": beneficiaries,
|
||||
"beneficiaries": beneficiariesMap,
|
||||
"beneficiaries_list": beneficiaries,
|
||||
"kb_data": kbData,
|
||||
"passengerid": passengerid,
|
||||
"journey_tabs": journeyTabs,
|
||||
"saved_searches": savedSearches,
|
||||
"search_view": renderer.GlobalConfig.GetString("modules.journeys.search_view"),
|
||||
}
|
||||
|
||||
renderer.Render("journeys", w, r, files, state)
|
||||
@@ -156,3 +159,28 @@ func (renderer *Renderer) UpdateGroupCovoiturage(w http.ResponseWriter, r *http.
|
||||
}
|
||||
renderer.Render("journeys", w, r, files, state)
|
||||
}
|
||||
|
||||
func (renderer *Renderer) JourneysSearchCompact(w http.ResponseWriter, r *http.Request, carpools []*geojson.FeatureCollection, transitjourneys any, vehicles any, searched bool, departure any, destination any, departuredate string, departuretime string, driverJourneys any, solidarityDrivers any, organizedCarpools any, kbData any, passengerid string) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.journeys.search_compact.files")
|
||||
vehicleOptionalFields := renderer.GlobalConfig.Get("modules.fleets.vehicle_optional_fields")
|
||||
|
||||
state := NewState(r, renderer.ThemeConfig, journeysMenu)
|
||||
state.ViewState = map[string]any{
|
||||
"searched": searched,
|
||||
"departuredate": departuredate,
|
||||
"departuretime": departuretime,
|
||||
"departure": departure,
|
||||
"destination": destination,
|
||||
"journeys": transitjourneys,
|
||||
"carpools": carpools,
|
||||
"organized_carpools": organizedCarpools,
|
||||
"vehicles": vehicles,
|
||||
"vehicle_optional_fields": vehicleOptionalFields,
|
||||
"driver_journeys": driverJourneys,
|
||||
"solidarity_drivers": solidarityDrivers,
|
||||
"kb_data": kbData,
|
||||
"passengerid": passengerid,
|
||||
}
|
||||
|
||||
renderer.Render("journeys", w, r, files, state)
|
||||
}
|
||||
|
||||
@@ -4,18 +4,33 @@ import (
|
||||
"net/http"
|
||||
|
||||
mobilityaccountsstorage "git.coopgo.io/coopgo-platform/mobility-accounts/storage"
|
||||
"git.coopgo.io/coopgo-platform/payments/pricing"
|
||||
)
|
||||
|
||||
const organizedCarpoolMenu = "organized_carpool"
|
||||
|
||||
func (renderer *Renderer) OrganizedCarpoolOverview(w http.ResponseWriter, r *http.Request, drivers any, driversMap any, passengersMap any, bookings any) {
|
||||
func (renderer *Renderer) OrganizedCarpoolOverview(w http.ResponseWriter, r *http.Request, drivers any, driversMap any, passengersMap any, bookings any, bookingsHistory any, filters map[string]any, histFilters map[string]any, tab string, enrichedGeoFilters []map[string]string, archived bool) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.organized_carpool.overview.files")
|
||||
tripsItemsPerPage := renderer.GlobalConfig.GetInt("modules.organized_carpool.pagination.trips_items_per_page")
|
||||
driversItemsPerPage := renderer.GlobalConfig.GetInt("modules.organized_carpool.pagination.drivers_items_per_page")
|
||||
|
||||
geoFiltersEnabled := len(enrichedGeoFilters) > 0
|
||||
|
||||
state := NewState(r, renderer.ThemeConfig, organizedCarpoolMenu)
|
||||
state.ViewState = map[string]any{
|
||||
"drivers": drivers,
|
||||
"drivers_map": driversMap,
|
||||
"passengers_map": passengersMap,
|
||||
"bookings": bookings,
|
||||
"drivers": drivers,
|
||||
"drivers_map": driversMap,
|
||||
"passengers_map": passengersMap,
|
||||
"bookings": bookings,
|
||||
"bookings_history": bookingsHistory,
|
||||
"filters": filters,
|
||||
"hist_filters": histFilters,
|
||||
"tab": tab,
|
||||
"trips_items_per_page": tripsItemsPerPage,
|
||||
"drivers_items_per_page": driversItemsPerPage,
|
||||
"geography_filters_enabled": geoFiltersEnabled,
|
||||
"geography_filters_list": enrichedGeoFilters,
|
||||
"archived": archived,
|
||||
}
|
||||
|
||||
renderer.Render("organized carpool overview", w, r, files, state)
|
||||
@@ -39,7 +54,7 @@ func (renderer *Renderer) OrganizedCarpoolUpdateDriver(w http.ResponseWriter, r
|
||||
renderer.Render("organized carpool driver update", w, r, files, state)
|
||||
}
|
||||
|
||||
func (renderer *Renderer) OrganizedCarpoolDriverDisplay(w http.ResponseWriter, r *http.Request, driver mobilityaccountsstorage.Account, trips any, documents any) {
|
||||
func (renderer *Renderer) OrganizedCarpoolDriverDisplay(w http.ResponseWriter, r *http.Request, driver mobilityaccountsstorage.Account, trips any, documents any, bookings any, beneficiariesMap any, stats map[string]any, walletBalance float64, tab string) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.organized_carpool.driver_display.files")
|
||||
state := NewState(r, renderer.ThemeConfig, organizedCarpoolMenu)
|
||||
|
||||
@@ -50,22 +65,31 @@ func (renderer *Renderer) OrganizedCarpoolDriverDisplay(w http.ResponseWriter, r
|
||||
"driver": driver,
|
||||
"trips": trips,
|
||||
"documents": documents,
|
||||
"bookings": bookings,
|
||||
"beneficiaries_map": beneficiariesMap,
|
||||
"stats": stats,
|
||||
"drivers_file_types": drivers_file_types,
|
||||
"file_types_map": file_types_map,
|
||||
"wallet_balance": walletBalance,
|
||||
"tab": tab,
|
||||
}
|
||||
|
||||
renderer.Render("organized carpool driver display", w, r, files, state)
|
||||
}
|
||||
|
||||
func (renderer *Renderer) OrganizedCarpoolJourney(w http.ResponseWriter, r *http.Request, journey any, driver any, passenger any, beneficiaries any) {
|
||||
func (renderer *Renderer) OrganizedCarpoolJourney(w http.ResponseWriter, r *http.Request, journey any, driver any, passenger any, beneficiaries any, passengerWalletBalance float64, pricingResult map[string]pricing.Price) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.organized_carpool.journey.files")
|
||||
state := NewState(r, renderer.ThemeConfig, solidarityTransportMenu)
|
||||
bookingMotivations := renderer.GlobalConfig.Get("modules.organized_carpool.booking_motivations")
|
||||
state := NewState(r, renderer.ThemeConfig, organizedCarpoolMenu)
|
||||
state.ViewState = map[string]any{
|
||||
"driver": driver,
|
||||
"passenger": passenger,
|
||||
"beneficiaries": beneficiaries,
|
||||
"journey": journey,
|
||||
"config": renderer.GlobalConfig,
|
||||
"driver": driver,
|
||||
"passenger": passenger,
|
||||
"beneficiaries": beneficiaries,
|
||||
"journey": journey,
|
||||
"config": renderer.GlobalConfig,
|
||||
"booking_motivations": bookingMotivations,
|
||||
"passenger_wallet_balance": passengerWalletBalance,
|
||||
"pricing_result": pricingResult,
|
||||
}
|
||||
|
||||
renderer.Render("organized carpool journey", w, r, files, state)
|
||||
@@ -75,11 +99,11 @@ func (renderer *Renderer) OrganizedCarpoolBookingDisplay(w http.ResponseWriter,
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.organized_carpool.booking_display.files")
|
||||
state := NewState(r, renderer.ThemeConfig, organizedCarpoolMenu)
|
||||
state.ViewState = map[string]any{
|
||||
"driver": driver,
|
||||
"passenger": passenger,
|
||||
"booking": booking,
|
||||
"driverDepartureAddress": driverDepartureAddress,
|
||||
"driverArrivalAddress": driverArrivalAddress,
|
||||
"driver": driver,
|
||||
"passenger": passenger,
|
||||
"booking": booking,
|
||||
"driverDepartureAddress": driverDepartureAddress,
|
||||
"driverArrivalAddress": driverArrivalAddress,
|
||||
}
|
||||
|
||||
renderer.Render("organized carpool booking display", w, r, files, state)
|
||||
|
||||
@@ -5,9 +5,10 @@ import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"git.coopgo.io/coopgo-apps/parcoursmob/utils/icons"
|
||||
"git.coopgo.io/coopgo-apps/parcoursmob/utils/identification"
|
||||
cache "git.coopgo.io/coopgo-apps/parcoursmob/utils/storage"
|
||||
"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/icons"
|
||||
"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/identification"
|
||||
cache "git.coopgo.io/coopgo-apps/parcoursmob/core/utils/storage"
|
||||
xlsxrenderer "git.coopgo.io/coopgo-apps/parcoursmob/renderer/xlsx"
|
||||
"git.coopgo.io/coopgo-platform/emailing"
|
||||
"git.coopgo.io/coopgo-platform/groups-management/storage"
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
@@ -21,6 +22,7 @@ type Renderer struct {
|
||||
ThemeConfig *viper.Viper
|
||||
Mailer *emailing.Mailer
|
||||
FileStorage cache.FileStorage
|
||||
XLSX *xlsxrenderer.XLSXRenderer
|
||||
}
|
||||
|
||||
func NewRenderer(global *viper.Viper, templates_dir string, filestorage cache.FileStorage) *Renderer {
|
||||
@@ -36,6 +38,7 @@ func NewRenderer(global *viper.Viper, templates_dir string, filestorage cache.Fi
|
||||
GlobalConfig: global,
|
||||
ThemeConfig: theme,
|
||||
FileStorage: filestorage,
|
||||
XLSX: xlsxrenderer.NewXLSXRenderer(global),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +141,8 @@ func NewState(r *http.Request, themeConfig *viper.Viper, menuState string) Rende
|
||||
Active: menuState == administrationMenu,
|
||||
},
|
||||
|
||||
Menu: menu,
|
||||
Menu: menu,
|
||||
ActiveMenu: menuState,
|
||||
|
||||
// DEPRECATED
|
||||
MenuItems: []MenuItem{
|
||||
@@ -232,14 +236,6 @@ func NewState(r *http.Request, themeConfig *viper.Viper, menuState string) Rende
|
||||
Icon: "hero:outline/document-text",
|
||||
})
|
||||
}
|
||||
/*if modules["diags"] != nil && modules["diags"].(bool) {
|
||||
ls.MenuItems = append(ls.MenuItems, MenuItem{
|
||||
Title: "Diagnostics",
|
||||
Link: "/app/diags/",
|
||||
Active: menuState == diagsMenu,
|
||||
Icon: "hero:outline/document-text",
|
||||
})
|
||||
}*/
|
||||
|
||||
return RenderState{
|
||||
IconSet: icons.NewIconSet(iconset),
|
||||
|
||||
@@ -4,22 +4,37 @@ import (
|
||||
"net/http"
|
||||
|
||||
mobilityaccountsstorage "git.coopgo.io/coopgo-platform/mobility-accounts/storage"
|
||||
"git.coopgo.io/coopgo-platform/payments/pricing"
|
||||
)
|
||||
|
||||
const solidarityTransportMenu = "solidarity_transport"
|
||||
|
||||
func (renderer *Renderer) SolidarityTransportOverview(w http.ResponseWriter, r *http.Request, drivers any, driversMap any, passengersMap any, bookings any, bookingsHistory any, filters any, hist_filters any, tab string) {
|
||||
func (renderer *Renderer) SolidarityTransportOverview(w http.ResponseWriter, r *http.Request, drivers any, driversMap any, passengersMap any, bookings any, bookingsHistory any, filters any, hist_filters any, tab string, enrichedGeoFilters []map[string]string, archived bool) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.solidarity_transport.overview.files")
|
||||
tripsItemsPerPage := renderer.GlobalConfig.GetInt("modules.solidarity_transport.pagination.trips_items_per_page")
|
||||
driversItemsPerPage := renderer.GlobalConfig.GetInt("modules.solidarity_transport.pagination.drivers_items_per_page")
|
||||
|
||||
guaranteedMotivations := renderer.GlobalConfig.GetStringSlice("modules.solidarity_transport.guaranteed_trip_motivations")
|
||||
|
||||
// Geography filters
|
||||
geoFiltersEnabled := len(enrichedGeoFilters) > 0
|
||||
|
||||
state := NewState(r, renderer.ThemeConfig, solidarityTransportMenu)
|
||||
state.ViewState = map[string]any{
|
||||
"drivers": drivers,
|
||||
"drivers_map": driversMap,
|
||||
"passengers_map": passengersMap,
|
||||
"bookings": bookings,
|
||||
"bookings_history": bookingsHistory,
|
||||
"filters": filters,
|
||||
"hist_filters": hist_filters,
|
||||
"tab": tab,
|
||||
"drivers": drivers,
|
||||
"drivers_map": driversMap,
|
||||
"passengers_map": passengersMap,
|
||||
"bookings": bookings,
|
||||
"bookings_history": bookingsHistory,
|
||||
"filters": filters,
|
||||
"hist_filters": hist_filters,
|
||||
"tab": tab,
|
||||
"trips_items_per_page": tripsItemsPerPage,
|
||||
"drivers_items_per_page": driversItemsPerPage,
|
||||
"guaranteed_trip_motivations": guaranteedMotivations,
|
||||
"geography_filters_enabled": geoFiltersEnabled,
|
||||
"geography_filters_list": enrichedGeoFilters,
|
||||
"archived": archived,
|
||||
}
|
||||
|
||||
renderer.Render("solidarity transport overview", w, r, files, state)
|
||||
@@ -50,7 +65,7 @@ func (renderer *Renderer) SolidarityTransportUpdateDriver(w http.ResponseWriter,
|
||||
renderer.Render("solidarity transport driver update", w, r, files, state)
|
||||
}
|
||||
|
||||
func (renderer *Renderer) SolidarityTransportDriverDisplay(w http.ResponseWriter, r *http.Request, driver mobilityaccountsstorage.Account, availabilities any, documents any, bookings any, stats map[string]any, walletBalance float64) {
|
||||
func (renderer *Renderer) SolidarityTransportDriverDisplay(w http.ResponseWriter, r *http.Request, driver mobilityaccountsstorage.Account, availabilities any, documents any, bookings any, beneficiariesMap any, stats map[string]any, walletBalance float64, tab string) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.solidarity_transport.driver_display.files")
|
||||
profileFields := renderer.GlobalConfig.Get("modules.solidarity_transport.drivers.profile_optional_fields")
|
||||
state := NewState(r, renderer.ThemeConfig, solidarityTransportMenu)
|
||||
@@ -62,19 +77,22 @@ func (renderer *Renderer) SolidarityTransportDriverDisplay(w http.ResponseWriter
|
||||
"driver": driver,
|
||||
"availabilities": availabilities,
|
||||
"bookings": bookings,
|
||||
"beneficiaries_map": beneficiariesMap,
|
||||
"documents": documents,
|
||||
"drivers_file_types": drivers_file_types,
|
||||
"file_types_map": file_types_map,
|
||||
"stats": stats,
|
||||
"profile_optional_fields": profileFields,
|
||||
"wallet_balance": walletBalance,
|
||||
"tab": tab,
|
||||
}
|
||||
|
||||
renderer.Render("solidarity transport driver creation", w, r, files, state)
|
||||
}
|
||||
|
||||
func (renderer *Renderer) SolidarityTransportDriverJourney(w http.ResponseWriter, r *http.Request, driverJourney any, driver any, passenger any, beneficiaries any, passengerWalletBalance float64) {
|
||||
func (renderer *Renderer) SolidarityTransportDriverJourney(w http.ResponseWriter, r *http.Request, driverJourney any, driver any, passenger any, beneficiaries any, passengerWalletBalance float64, pricingResult map[string]pricing.Price) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.solidarity_transport.driver_journey.files")
|
||||
bookingMotivations := renderer.GlobalConfig.Get("modules.solidarity_transport.booking_motivations")
|
||||
state := NewState(r, renderer.ThemeConfig, solidarityTransportMenu)
|
||||
state.ViewState = map[string]any{
|
||||
"driver": driver,
|
||||
@@ -83,6 +101,8 @@ func (renderer *Renderer) SolidarityTransportDriverJourney(w http.ResponseWriter
|
||||
"driver_journey": driverJourney,
|
||||
"config": renderer.GlobalConfig,
|
||||
"passenger_wallet_balance": passengerWalletBalance,
|
||||
"pricing_result": pricingResult,
|
||||
"booking_motivations": bookingMotivations,
|
||||
}
|
||||
|
||||
renderer.Render("solidarity transport driver creation", w, r, files, state)
|
||||
@@ -90,12 +110,14 @@ func (renderer *Renderer) SolidarityTransportDriverJourney(w http.ResponseWriter
|
||||
|
||||
func (renderer *Renderer) SolidarityTransportBookingDisplay(w http.ResponseWriter, r *http.Request, booking any, driver any, passenger any, passengerWalletBalance float64) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.solidarity_transport.booking_display.files")
|
||||
bookingMotivations := renderer.GlobalConfig.Get("modules.solidarity_transport.booking_motivations")
|
||||
state := NewState(r, renderer.ThemeConfig, solidarityTransportMenu)
|
||||
state.ViewState = map[string]any{
|
||||
"driver": driver,
|
||||
"passenger": passenger,
|
||||
"booking": booking,
|
||||
"passenger_wallet_balance": passengerWalletBalance,
|
||||
"booking_motivations": bookingMotivations,
|
||||
}
|
||||
|
||||
renderer.Render("booking display", w, r, files, state)
|
||||
|
||||
@@ -3,7 +3,7 @@ package renderer
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
filestorage "git.coopgo.io/coopgo-apps/parcoursmob/utils/storage"
|
||||
filestorage "git.coopgo.io/coopgo-apps/parcoursmob/core/utils/storage"
|
||||
fleetsstorage "git.coopgo.io/coopgo-platform/fleets/storage"
|
||||
mobilityaccountsstorage "git.coopgo.io/coopgo-platform/mobility-accounts/storage"
|
||||
)
|
||||
@@ -23,7 +23,7 @@ func (renderer *Renderer) VehiclesManagementOverview(w http.ResponseWriter, r *h
|
||||
renderer.Render("fleet overview", w, r, files, state)
|
||||
}
|
||||
|
||||
func (renderer *Renderer) VehiclesManagementBookingsList(w http.ResponseWriter, r *http.Request, vehiclesMap map[string]fleetsstorage.Vehicle, driversMap map[string]mobilityaccountsstorage.Account, bookings []fleetsstorage.Booking, cacheid string) {
|
||||
func (renderer *Renderer) VehiclesManagementBookingsList(w http.ResponseWriter, r *http.Request, vehiclesMap map[string]fleetsstorage.Vehicle, driversMap map[string]mobilityaccountsstorage.Account, bookings []fleetsstorage.Booking, cacheid string, filters map[string]string) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.vehicles_management.bookings_list.files")
|
||||
state := NewState(r, renderer.ThemeConfig, vehiclesmanagementMenu)
|
||||
state.ViewState = map[string]any{
|
||||
@@ -31,6 +31,7 @@ func (renderer *Renderer) VehiclesManagementBookingsList(w http.ResponseWriter,
|
||||
"vehicles_map": vehiclesMap,
|
||||
"drivers_map": driversMap,
|
||||
"cacheid": cacheid,
|
||||
"filters": filters,
|
||||
}
|
||||
|
||||
renderer.Render("fleet overview", w, r, files, state)
|
||||
@@ -38,21 +39,26 @@ func (renderer *Renderer) VehiclesManagementBookingsList(w http.ResponseWriter,
|
||||
|
||||
func (renderer *Renderer) VehiclesFleetAdd(w http.ResponseWriter, r *http.Request, vehicle_types []string) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.vehicles_management.fleet_add.files")
|
||||
vehicleOptionalFields := renderer.GlobalConfig.Get("modules.fleets.vehicle_optional_fields")
|
||||
|
||||
state := NewState(r, renderer.ThemeConfig, vehiclesmanagementMenu)
|
||||
state.ViewState = map[string]any{
|
||||
"vehicle_types": vehicle_types,
|
||||
"vehicle_types": vehicle_types,
|
||||
"vehicle_optional_fields": vehicleOptionalFields,
|
||||
}
|
||||
|
||||
renderer.Render("fleet add vehicle", w, r, files, state)
|
||||
}
|
||||
|
||||
func (renderer *Renderer) VehiclesFleetDisplay(w http.ResponseWriter, r *http.Request, vehicle any, beneficiaries any, diags []any) {
|
||||
func (renderer *Renderer) VehiclesFleetDisplay(w http.ResponseWriter, r *http.Request, vehicle any, beneficiaries any) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.vehicles_management.fleet_display.files")
|
||||
vehicleOptionalFields := renderer.GlobalConfig.Get("modules.fleets.vehicle_optional_fields")
|
||||
|
||||
state := NewState(r, renderer.ThemeConfig, vehiclesmanagementMenu)
|
||||
state.ViewState = map[string]any{
|
||||
"vehicle": vehicle,
|
||||
"beneficiaries": beneficiaries,
|
||||
"diags": diags,
|
||||
"vehicle": vehicle,
|
||||
"beneficiaries": beneficiaries,
|
||||
"vehicle_optional_fields": vehicleOptionalFields,
|
||||
}
|
||||
|
||||
renderer.Render("fleet display vehicle", w, r, files, state)
|
||||
@@ -60,16 +66,19 @@ func (renderer *Renderer) VehiclesFleetDisplay(w http.ResponseWriter, r *http.Re
|
||||
|
||||
func (renderer *Renderer) VehiclesFleetUpdate(w http.ResponseWriter, r *http.Request, vehicle any, vehicle_types []string) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.vehicles_management.fleet_update.files")
|
||||
vehicleOptionalFields := renderer.GlobalConfig.Get("modules.fleets.vehicle_optional_fields")
|
||||
|
||||
state := NewState(r, renderer.ThemeConfig, vehiclesmanagementMenu)
|
||||
state.ViewState = map[string]any{
|
||||
"vehicle": vehicle,
|
||||
"vehicle_types": vehicle_types,
|
||||
"vehicle": vehicle,
|
||||
"vehicle_types": vehicle_types,
|
||||
"vehicle_optional_fields": vehicleOptionalFields,
|
||||
}
|
||||
|
||||
renderer.Render("fleet display vehicle", w, r, files, state)
|
||||
}
|
||||
|
||||
func (renderer *Renderer) VehicleManagementBookingDisplay(w http.ResponseWriter, r *http.Request, booking any, vehicle any, beneficiary any, group any, documents []filestorage.FileInfo, file_types_map map[string]string, alternative_vehicles []any, diags []any) {
|
||||
func (renderer *Renderer) VehicleManagementBookingDisplay(w http.ResponseWriter, r *http.Request, booking any, vehicle any, beneficiary any, group any, documents []filestorage.FileInfo, file_types_map map[string]string, alternative_vehicles []any) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.vehicles_management.booking_display.files")
|
||||
state := NewState(r, renderer.ThemeConfig, vehiclesmanagementMenu)
|
||||
state.ViewState = map[string]any{
|
||||
@@ -80,7 +89,6 @@ func (renderer *Renderer) VehicleManagementBookingDisplay(w http.ResponseWriter,
|
||||
"documents": documents,
|
||||
"file_types_map": file_types_map,
|
||||
"alternative_vehicles": alternative_vehicles,
|
||||
"diags": diags,
|
||||
}
|
||||
|
||||
renderer.Render("vehicles search", w, r, files, state)
|
||||
|
||||
@@ -3,7 +3,7 @@ package renderer
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
filestorage "git.coopgo.io/coopgo-apps/parcoursmob/utils/storage"
|
||||
filestorage "git.coopgo.io/coopgo-apps/parcoursmob/core/utils/storage"
|
||||
"git.coopgo.io/coopgo-platform/fleets/storage"
|
||||
fleetsstorage "git.coopgo.io/coopgo-platform/fleets/storage"
|
||||
mobilityaccountsstorage "git.coopgo.io/coopgo-platform/mobility-accounts/storage"
|
||||
@@ -53,7 +53,7 @@ func (renderer *Renderer) VehiclesSearch(w http.ResponseWriter, r *http.Request,
|
||||
renderer.Render("vehicles search", w, r, files, state)
|
||||
}
|
||||
|
||||
func (renderer *Renderer) VehicleBookingDisplay(w http.ResponseWriter, r *http.Request, booking any, vehicle any, beneficiary any, group any, documents []filestorage.FileInfo, file_types_map map[string]string, diags []any) {
|
||||
func (renderer *Renderer) VehicleBookingDisplay(w http.ResponseWriter, r *http.Request, booking any, vehicle any, beneficiary any, group any, documents []filestorage.FileInfo, file_types_map map[string]string) {
|
||||
files := renderer.ThemeConfig.GetStringSlice("views.vehicles.booking_display.files")
|
||||
state := NewState(r, renderer.ThemeConfig, vehiclesMenu)
|
||||
state.ViewState = map[string]any{
|
||||
@@ -63,7 +63,6 @@ func (renderer *Renderer) VehicleBookingDisplay(w http.ResponseWriter, r *http.R
|
||||
"group": group,
|
||||
"documents": documents,
|
||||
"file_types_map": file_types_map,
|
||||
"diags": diags,
|
||||
}
|
||||
|
||||
renderer.Render("vehicles search", w, r, files, state)
|
||||
|
||||
273
renderer/xlsx/organized-carpool.go
Normal file
273
renderer/xlsx/organized-carpool.go
Normal file
@@ -0,0 +1,273 @@
|
||||
package xlsx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"git.coopgo.io/coopgo-apps/parcoursmob/core/application"
|
||||
"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/gender"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (r *XLSXRenderer) OrganizedCarpoolBookings(w http.ResponseWriter, result *application.OrganizedCarpoolBookingsResult) {
|
||||
// Create Excel spreadsheet
|
||||
spreadsheet := r.NewSpreadsheet("Covoiturage solidaire")
|
||||
|
||||
// Build headers dynamically based on configuration
|
||||
headers := []string{
|
||||
"ID Réservation",
|
||||
"Statut",
|
||||
"Motif de réservation",
|
||||
"Date de prise en charge",
|
||||
"Heure de prise en charge",
|
||||
}
|
||||
|
||||
// Add driver fields from config
|
||||
driverOptionalFields := r.Config.Get("modules.organized_carpool.profile_optional_fields")
|
||||
driverFields := []string{"last_name", "first_name", "email", "phone_number"}
|
||||
driverHeaders := []string{"Conducteur - Nom", "Conducteur - Prénom", "Conducteur - Email", "Conducteur - Téléphone"}
|
||||
|
||||
if driverOptionalFieldsList, ok := driverOptionalFields.([]interface{}); ok {
|
||||
for _, field := range driverOptionalFieldsList {
|
||||
if fieldMap, ok := field.(map[string]interface{}); ok {
|
||||
if name, ok := fieldMap["name"].(string); ok {
|
||||
driverFields = append(driverFields, name)
|
||||
label := name
|
||||
if labelVal, ok := fieldMap["label"].(string); ok {
|
||||
label = labelVal
|
||||
}
|
||||
driverHeaders = append(driverHeaders, fmt.Sprintf("Conducteur - %s", label))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
headers = append(headers, driverHeaders...)
|
||||
|
||||
// Add beneficiary fields from config
|
||||
beneficiaryOptionalFields := r.Config.Get("modules.beneficiaries.profile_optional_fields")
|
||||
beneficiaryFields := []string{"last_name", "first_name", "email", "phone_number"}
|
||||
beneficiaryHeaders := []string{"Bénéficiaire - Nom", "Bénéficiaire - Prénom", "Bénéficiaire - Email", "Bénéficiaire - Téléphone"}
|
||||
|
||||
if beneficiaryOptionalFieldsList, ok := beneficiaryOptionalFields.([]interface{}); ok {
|
||||
for _, field := range beneficiaryOptionalFieldsList {
|
||||
if fieldMap, ok := field.(map[string]interface{}); ok {
|
||||
if name, ok := fieldMap["name"].(string); ok {
|
||||
beneficiaryFields = append(beneficiaryFields, name)
|
||||
label := name
|
||||
if labelVal, ok := fieldMap["label"].(string); ok {
|
||||
label = labelVal
|
||||
}
|
||||
beneficiaryHeaders = append(beneficiaryHeaders, fmt.Sprintf("Bénéficiaire - %s", label))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
headers = append(headers, beneficiaryHeaders...)
|
||||
|
||||
// Add journey information headers
|
||||
headers = append(headers,
|
||||
"Lieu de départ - Adresse",
|
||||
"Destination - Adresse",
|
||||
"Distance passager (km)",
|
||||
"Durée trajet (minutes)",
|
||||
"Prix passager",
|
||||
"Devise prix passager",
|
||||
"Compensation conducteur",
|
||||
"Devise compensation",
|
||||
)
|
||||
|
||||
spreadsheet.SetHeaders(headers)
|
||||
|
||||
// Add data rows
|
||||
for _, booking := range result.Bookings {
|
||||
driver := result.DriversMap[booking.Driver.Id]
|
||||
beneficiary := result.BeneficiariesMap[booking.Passenger.Id]
|
||||
|
||||
row := []interface{}{}
|
||||
|
||||
// Booking information
|
||||
row = append(row, booking.Id)
|
||||
row = append(row, booking.Status.String())
|
||||
|
||||
// Motivation
|
||||
motivation := ""
|
||||
if booking.Motivation != nil {
|
||||
motivation = *booking.Motivation
|
||||
}
|
||||
row = append(row, motivation)
|
||||
|
||||
// Journey date and time
|
||||
row = append(row, booking.PassengerPickupDate.AsTime().Format("2006-01-02"))
|
||||
row = append(row, booking.PassengerPickupDate.AsTime().Format("15:04"))
|
||||
|
||||
// Driver data
|
||||
for _, field := range driverFields {
|
||||
row = append(row, getAccountFieldValue(driver.Data, field))
|
||||
}
|
||||
|
||||
// Beneficiary data
|
||||
for _, field := range beneficiaryFields {
|
||||
row = append(row, getAccountFieldValue(beneficiary.Data, field))
|
||||
}
|
||||
|
||||
// Journey information
|
||||
if booking.PassengerPickupAddress != nil {
|
||||
row = append(row, *booking.PassengerPickupAddress)
|
||||
} else {
|
||||
row = append(row, "")
|
||||
}
|
||||
|
||||
if booking.PassengerDropAddress != nil {
|
||||
row = append(row, *booking.PassengerDropAddress)
|
||||
} else {
|
||||
row = append(row, "")
|
||||
}
|
||||
|
||||
// Distance
|
||||
if booking.Distance != nil {
|
||||
row = append(row, *booking.Distance)
|
||||
} else {
|
||||
row = append(row, "")
|
||||
}
|
||||
|
||||
// Duration
|
||||
if booking.Duration != nil {
|
||||
row = append(row, *booking.Duration)
|
||||
} else {
|
||||
row = append(row, "")
|
||||
}
|
||||
|
||||
// Pricing
|
||||
if booking.Price != nil && booking.Price.Amount != nil {
|
||||
row = append(row, fmt.Sprintf("%.2f", *booking.Price.Amount))
|
||||
if booking.Price.Currency != nil {
|
||||
row = append(row, *booking.Price.Currency)
|
||||
} else {
|
||||
row = append(row, "")
|
||||
}
|
||||
} else {
|
||||
row = append(row, "", "")
|
||||
}
|
||||
|
||||
// Driver compensation
|
||||
if booking.DriverCompensationAmount != nil {
|
||||
row = append(row, fmt.Sprintf("%.2f", *booking.DriverCompensationAmount))
|
||||
if booking.DriverCompensationCurrency != nil {
|
||||
row = append(row, *booking.DriverCompensationCurrency)
|
||||
} else {
|
||||
row = append(row, "")
|
||||
}
|
||||
} else {
|
||||
row = append(row, "", "")
|
||||
}
|
||||
|
||||
spreadsheet.AddRow(row)
|
||||
}
|
||||
|
||||
// Write Excel to response
|
||||
w.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
w.Header().Set("Content-Disposition", "attachment; filename=\"export-covoiturage-solidaire.xlsx\"")
|
||||
|
||||
if err := spreadsheet.GetFile().Write(w); err != nil {
|
||||
log.Error().Err(err).Msg("Error generating Excel file")
|
||||
http.Error(w, "Error generating Excel file", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (r *XLSXRenderer) OrganizedCarpoolDrivers(w http.ResponseWriter, result *application.OrganizedCarpoolOverviewResult) {
|
||||
// Create Excel spreadsheet
|
||||
spreadsheet := r.NewSpreadsheet("Covoitureurs solidaires")
|
||||
|
||||
// Build headers dynamically based on configuration
|
||||
driverOptionalFields := r.Config.Get("modules.organized_carpool.drivers.profile_optional_fields")
|
||||
driverFields := []string{"last_name", "first_name", "email", "phone_number", "birthdate", "gender", "file_number"}
|
||||
headers := []string{"ID", "Nom", "Prénom", "Email", "Téléphone", "Date de naissance", "Genre", "Numéro de dossier"}
|
||||
|
||||
if driverOptionalFieldsList, ok := driverOptionalFields.([]interface{}); ok {
|
||||
for _, field := range driverOptionalFieldsList {
|
||||
if fieldMap, ok := field.(map[string]interface{}); ok {
|
||||
if name, ok := fieldMap["name"].(string); ok {
|
||||
driverFields = append(driverFields, name)
|
||||
label := name
|
||||
if labelVal, ok := fieldMap["label"].(string); ok {
|
||||
label = labelVal
|
||||
}
|
||||
headers = append(headers, label)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add address columns
|
||||
headers = append(headers, "Adresse départ", "Adresse destination", "Archivé")
|
||||
|
||||
spreadsheet.SetHeaders(headers)
|
||||
|
||||
// Add data rows
|
||||
for _, driver := range result.Accounts {
|
||||
row := []interface{}{}
|
||||
|
||||
// Driver ID
|
||||
row = append(row, driver.ID)
|
||||
|
||||
// Driver data
|
||||
for _, field := range driverFields {
|
||||
value := getAccountFieldValue(driver.Data, field)
|
||||
// Convert gender code to text
|
||||
if field == "gender" && value != "" {
|
||||
value = gender.ISO5218ToString(value)
|
||||
}
|
||||
row = append(row, value)
|
||||
}
|
||||
|
||||
// Address departure
|
||||
addressDeparture := ""
|
||||
if addr, ok := driver.Data["address"]; ok {
|
||||
if addrMap, ok := addr.(map[string]interface{}); ok {
|
||||
if props, ok := addrMap["properties"]; ok {
|
||||
if propsMap, ok := props.(map[string]interface{}); ok {
|
||||
if label, ok := propsMap["label"].(string); ok {
|
||||
addressDeparture = label
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
row = append(row, addressDeparture)
|
||||
|
||||
// Address destination
|
||||
addressDestination := ""
|
||||
if addr, ok := driver.Data["address_destination"]; ok {
|
||||
if addrMap, ok := addr.(map[string]interface{}); ok {
|
||||
if props, ok := addrMap["properties"]; ok {
|
||||
if propsMap, ok := props.(map[string]interface{}); ok {
|
||||
if label, ok := propsMap["label"].(string); ok {
|
||||
addressDestination = label
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
row = append(row, addressDestination)
|
||||
|
||||
// Archived status
|
||||
archived := "Non"
|
||||
if archivedVal, ok := driver.Data["archived"].(bool); ok && archivedVal {
|
||||
archived = "Oui"
|
||||
}
|
||||
row = append(row, archived)
|
||||
|
||||
spreadsheet.AddRow(row)
|
||||
}
|
||||
|
||||
// Write Excel to response
|
||||
w.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
w.Header().Set("Content-Disposition", "attachment; filename=\"export-covoitureurs-solidaires.xlsx\"")
|
||||
|
||||
if err := spreadsheet.GetFile().Write(w); err != nil {
|
||||
log.Error().Err(err).Msg("Error generating Excel file")
|
||||
http.Error(w, "Error generating Excel file", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
320
renderer/xlsx/solidarity-transport.go
Normal file
320
renderer/xlsx/solidarity-transport.go
Normal file
@@ -0,0 +1,320 @@
|
||||
package xlsx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"git.coopgo.io/coopgo-apps/parcoursmob/core/application"
|
||||
"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/gender"
|
||||
"github.com/paulmach/orb/geojson"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (r *XLSXRenderer) SolidarityTransportBookings(w http.ResponseWriter, result *application.SolidarityTransportBookingsResult) {
|
||||
// Create Excel spreadsheet
|
||||
spreadsheet := r.NewSpreadsheet("Transport solidaire")
|
||||
|
||||
// Build headers dynamically based on configuration
|
||||
headers := []string{
|
||||
"ID Réservation",
|
||||
"Statut",
|
||||
"Motif de réservation",
|
||||
"Raison d'annulation",
|
||||
"Date de prise en charge",
|
||||
"Heure de prise en charge",
|
||||
}
|
||||
|
||||
// Add driver fields from config
|
||||
driverOptionalFields := r.Config.Get("modules.solidarity_transport.profile_optional_fields")
|
||||
driverFields := []string{"last_name", "first_name", "email", "phone_number"}
|
||||
driverHeaders := []string{"Conducteur - Nom", "Conducteur - Prénom", "Conducteur - Email", "Conducteur - Téléphone"}
|
||||
|
||||
if driverOptionalFieldsList, ok := driverOptionalFields.([]interface{}); ok {
|
||||
for _, field := range driverOptionalFieldsList {
|
||||
if fieldMap, ok := field.(map[string]interface{}); ok {
|
||||
if name, ok := fieldMap["name"].(string); ok {
|
||||
driverFields = append(driverFields, name)
|
||||
label := name
|
||||
if labelVal, ok := fieldMap["label"].(string); ok {
|
||||
label = labelVal
|
||||
}
|
||||
driverHeaders = append(driverHeaders, fmt.Sprintf("Conducteur - %s", label))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
headers = append(headers, driverHeaders...)
|
||||
|
||||
// Add beneficiary fields from config
|
||||
beneficiaryOptionalFields := r.Config.Get("modules.beneficiaries.profile_optional_fields")
|
||||
beneficiaryFields := []string{"last_name", "first_name", "email", "phone_number"}
|
||||
beneficiaryHeaders := []string{"Bénéficiaire - Nom", "Bénéficiaire - Prénom", "Bénéficiaire - Email", "Bénéficiaire - Téléphone"}
|
||||
|
||||
if beneficiaryOptionalFieldsList, ok := beneficiaryOptionalFields.([]interface{}); ok {
|
||||
for _, field := range beneficiaryOptionalFieldsList {
|
||||
if fieldMap, ok := field.(map[string]interface{}); ok {
|
||||
if name, ok := fieldMap["name"].(string); ok {
|
||||
beneficiaryFields = append(beneficiaryFields, name)
|
||||
label := name
|
||||
if labelVal, ok := fieldMap["label"].(string); ok {
|
||||
label = labelVal
|
||||
}
|
||||
beneficiaryHeaders = append(beneficiaryHeaders, fmt.Sprintf("Bénéficiaire - %s", label))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
headers = append(headers, beneficiaryHeaders...)
|
||||
|
||||
// Add journey information headers
|
||||
headers = append(headers,
|
||||
"Lieu de départ - Adresse",
|
||||
"Lieu de départ - Latitude",
|
||||
"Lieu de départ - Longitude",
|
||||
"Destination - Adresse",
|
||||
"Destination - Latitude",
|
||||
"Destination - Longitude",
|
||||
"Distance passager (km)",
|
||||
"Distance conducteur totale (km)",
|
||||
"Durée trajet (minutes)",
|
||||
"Prix passager",
|
||||
"Devise prix passager",
|
||||
"Compensation conducteur",
|
||||
"Devise compensation",
|
||||
"Temps d'attente retour",
|
||||
"Aller simple",
|
||||
"Départ conducteur - Adresse",
|
||||
"Départ conducteur - Latitude",
|
||||
"Départ conducteur - Longitude",
|
||||
"Arrivée conducteur - Adresse",
|
||||
"Arrivée conducteur - Latitude",
|
||||
"Arrivée conducteur - Longitude",
|
||||
)
|
||||
|
||||
spreadsheet.SetHeaders(headers)
|
||||
|
||||
// Add data rows
|
||||
for _, booking := range result.Bookings {
|
||||
driver := result.DriversMap[booking.DriverId]
|
||||
beneficiary := result.BeneficiariesMap[booking.PassengerId]
|
||||
|
||||
row := []interface{}{}
|
||||
|
||||
// Booking information
|
||||
row = append(row, booking.Id)
|
||||
row = append(row, booking.Status)
|
||||
|
||||
// Motivation (from booking.Data)
|
||||
motivation := ""
|
||||
if booking.Data != nil {
|
||||
if motivationVal, ok := booking.Data["motivation"]; ok && motivationVal != nil {
|
||||
motivation = fmt.Sprint(motivationVal)
|
||||
}
|
||||
}
|
||||
row = append(row, motivation)
|
||||
|
||||
// Cancellation reason (from booking.Data)
|
||||
cancellationReason := ""
|
||||
if booking.Data != nil {
|
||||
if reasonVal, ok := booking.Data["reason"]; ok && reasonVal != nil {
|
||||
cancellationReason = fmt.Sprint(reasonVal)
|
||||
}
|
||||
}
|
||||
row = append(row, cancellationReason)
|
||||
|
||||
// Journey date and time
|
||||
if booking.Journey != nil {
|
||||
row = append(row, booking.Journey.PassengerPickupDate.Format("2006-01-02"))
|
||||
row = append(row, booking.Journey.PassengerPickupDate.Format("15:04"))
|
||||
} else {
|
||||
row = append(row, "", "")
|
||||
}
|
||||
|
||||
// Driver data
|
||||
for _, field := range driverFields {
|
||||
row = append(row, getAccountFieldValue(driver.Data, field))
|
||||
}
|
||||
|
||||
// Beneficiary data
|
||||
for _, field := range beneficiaryFields {
|
||||
row = append(row, getAccountFieldValue(beneficiary.Data, field))
|
||||
}
|
||||
|
||||
// Journey locations and details
|
||||
if booking.Journey != nil {
|
||||
// Passenger pickup
|
||||
pickupAddr, pickupLat, pickupLon := getLocationData(booking.Journey.PassengerPickup)
|
||||
row = append(row, pickupAddr, pickupLat, pickupLon)
|
||||
|
||||
// Passenger drop
|
||||
dropAddr, dropLat, dropLon := getLocationData(booking.Journey.PassengerDrop)
|
||||
row = append(row, dropAddr, dropLat, dropLon)
|
||||
|
||||
// Distances and duration
|
||||
row = append(row, booking.Journey.PassengerDistance)
|
||||
row = append(row, booking.Journey.DriverDistance)
|
||||
row = append(row, int64(booking.Journey.Duration.Minutes()))
|
||||
|
||||
// Pricing
|
||||
row = append(row, fmt.Sprintf("%.2f", booking.Journey.Price.Amount))
|
||||
row = append(row, booking.Journey.Price.Currency)
|
||||
|
||||
// Driver compensation
|
||||
if booking.DriverCompensationAmount > 0 {
|
||||
row = append(row, fmt.Sprintf("%.2f", booking.DriverCompensationAmount))
|
||||
row = append(row, booking.DriverCompensationCurrency)
|
||||
} else {
|
||||
row = append(row, "", "")
|
||||
}
|
||||
|
||||
// Return wait time
|
||||
row = append(row, "")
|
||||
|
||||
// One way trip (Noreturn field)
|
||||
if booking.Journey.Noreturn {
|
||||
row = append(row, "Oui")
|
||||
} else {
|
||||
row = append(row, "Non")
|
||||
}
|
||||
|
||||
// Driver departure
|
||||
driverDepartAddr, driverDepartLat, driverDepartLon := getLocationData(booking.Journey.DriverDeparture)
|
||||
row = append(row, driverDepartAddr, driverDepartLat, driverDepartLon)
|
||||
|
||||
// Driver arrival
|
||||
driverArrivalAddr, driverArrivalLat, driverArrivalLon := getLocationData(booking.Journey.DriverArrival)
|
||||
row = append(row, driverArrivalAddr, driverArrivalLat, driverArrivalLon)
|
||||
} else {
|
||||
// No journey data - fill with empty values
|
||||
for i := 0; i < 21; i++ {
|
||||
row = append(row, "")
|
||||
}
|
||||
}
|
||||
|
||||
spreadsheet.AddRow(row)
|
||||
}
|
||||
|
||||
// Write Excel to response
|
||||
w.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
w.Header().Set("Content-Disposition", "attachment; filename=\"export-transport-solidaire.xlsx\"")
|
||||
|
||||
if err := spreadsheet.GetFile().Write(w); err != nil {
|
||||
log.Error().Err(err).Msg("Error generating Excel file")
|
||||
http.Error(w, "Error generating Excel file", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getAccountFieldValue(data map[string]interface{}, field string) string {
|
||||
// First check direct field
|
||||
if val, ok := data[field]; ok && val != nil {
|
||||
return fmt.Sprint(val)
|
||||
}
|
||||
|
||||
// Check in other_properties
|
||||
if otherProps, ok := data["other_properties"]; ok {
|
||||
if otherPropsMap, ok := otherProps.(map[string]interface{}); ok {
|
||||
if val, ok := otherPropsMap[field]; ok && val != nil {
|
||||
return fmt.Sprint(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func getLocationData(feature *geojson.Feature) (address string, lat interface{}, lon interface{}) {
|
||||
if feature != nil && feature.Properties != nil {
|
||||
if label, ok := feature.Properties["label"].(string); ok {
|
||||
address = label
|
||||
}
|
||||
if feature.Geometry != nil {
|
||||
coords := feature.Geometry.Bound().Center()
|
||||
lat = fmt.Sprintf("%.6f", coords.Lat())
|
||||
lon = fmt.Sprintf("%.6f", coords.Lon())
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r *XLSXRenderer) SolidarityTransportDrivers(w http.ResponseWriter, result *application.SolidarityTransportOverviewResult) {
|
||||
// Create Excel spreadsheet
|
||||
spreadsheet := r.NewSpreadsheet("Conducteurs solidaires")
|
||||
|
||||
// Build headers dynamically based on configuration
|
||||
driverOptionalFields := r.Config.Get("modules.solidarity_transport.drivers.profile_optional_fields")
|
||||
driverFields := []string{"last_name", "first_name", "email", "phone_number", "birthdate", "gender", "file_number"}
|
||||
headers := []string{"ID", "Nom", "Prénom", "Email", "Téléphone", "Date de naissance", "Genre", "Numéro de dossier"}
|
||||
|
||||
if driverOptionalFieldsList, ok := driverOptionalFields.([]interface{}); ok {
|
||||
for _, field := range driverOptionalFieldsList {
|
||||
if fieldMap, ok := field.(map[string]interface{}); ok {
|
||||
if name, ok := fieldMap["name"].(string); ok {
|
||||
driverFields = append(driverFields, name)
|
||||
label := name
|
||||
if labelVal, ok := fieldMap["label"].(string); ok {
|
||||
label = labelVal
|
||||
}
|
||||
headers = append(headers, label)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add address and archived columns
|
||||
headers = append(headers, "Adresse", "Archivé")
|
||||
|
||||
spreadsheet.SetHeaders(headers)
|
||||
|
||||
// Add data rows
|
||||
for _, driver := range result.Accounts {
|
||||
row := []interface{}{}
|
||||
|
||||
// Driver ID
|
||||
row = append(row, driver.ID)
|
||||
|
||||
// Driver data
|
||||
for _, field := range driverFields {
|
||||
value := getAccountFieldValue(driver.Data, field)
|
||||
// Convert gender code to text
|
||||
if field == "gender" && value != "" {
|
||||
value = gender.ISO5218ToString(value)
|
||||
}
|
||||
row = append(row, value)
|
||||
}
|
||||
|
||||
// Address
|
||||
address := ""
|
||||
if addr, ok := driver.Data["address"]; ok {
|
||||
if addrMap, ok := addr.(map[string]interface{}); ok {
|
||||
if props, ok := addrMap["properties"]; ok {
|
||||
if propsMap, ok := props.(map[string]interface{}); ok {
|
||||
if label, ok := propsMap["label"].(string); ok {
|
||||
address = label
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
row = append(row, address)
|
||||
|
||||
// Archived status
|
||||
archived := "Non"
|
||||
if archivedVal, ok := driver.Data["archived"].(bool); ok && archivedVal {
|
||||
archived = "Oui"
|
||||
}
|
||||
row = append(row, archived)
|
||||
|
||||
spreadsheet.AddRow(row)
|
||||
}
|
||||
|
||||
// Write Excel to response
|
||||
w.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
w.Header().Set("Content-Disposition", "attachment; filename=\"export-conducteurs-solidaires.xlsx\"")
|
||||
|
||||
if err := spreadsheet.GetFile().Write(w); err != nil {
|
||||
log.Error().Err(err).Msg("Error generating Excel file")
|
||||
http.Error(w, "Error generating Excel file", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
350
renderer/xlsx/vehicle-bookings.go
Normal file
350
renderer/xlsx/vehicle-bookings.go
Normal file
@@ -0,0 +1,350 @@
|
||||
package xlsx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
fleetsstorage "git.coopgo.io/coopgo-platform/fleets/storage"
|
||||
groupsstorage "git.coopgo.io/coopgo-platform/groups-management/storage"
|
||||
mobilityaccountsstorage "git.coopgo.io/coopgo-platform/mobility-accounts/storage"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (r *XLSXRenderer) VehicleBookings(w http.ResponseWriter, bookings []fleetsstorage.Booking, vehiclesMap map[string]fleetsstorage.Vehicle, driversMap map[string]mobilityaccountsstorage.Account) {
|
||||
// Create Excel spreadsheet
|
||||
spreadsheet := r.NewSpreadsheet("Réservations véhicules")
|
||||
|
||||
// Build headers
|
||||
headers := []string{
|
||||
"ID Réservation",
|
||||
"Statut",
|
||||
"Type de véhicule",
|
||||
"Nom du véhicule",
|
||||
"Immatriculation",
|
||||
}
|
||||
|
||||
// Add beneficiary fields from config
|
||||
beneficiaryOptionalFields := r.Config.Get("modules.beneficiaries.profile_optional_fields")
|
||||
beneficiaryFields := []string{"last_name", "first_name", "email", "phone_number"}
|
||||
beneficiaryHeaders := []string{"Bénéficiaire - Nom", "Bénéficiaire - Prénom", "Bénéficiaire - Email", "Bénéficiaire - Téléphone"}
|
||||
|
||||
if beneficiaryOptionalFieldsList, ok := beneficiaryOptionalFields.([]interface{}); ok {
|
||||
for _, field := range beneficiaryOptionalFieldsList {
|
||||
if fieldMap, ok := field.(map[string]interface{}); ok {
|
||||
if name, ok := fieldMap["name"].(string); ok {
|
||||
beneficiaryFields = append(beneficiaryFields, name)
|
||||
label := name
|
||||
if labelVal, ok := fieldMap["label"].(string); ok {
|
||||
label = labelVal
|
||||
}
|
||||
beneficiaryHeaders = append(beneficiaryHeaders, fmt.Sprintf("Bénéficiaire - %s", label))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
headers = append(headers, beneficiaryHeaders...)
|
||||
|
||||
// Add booking date headers
|
||||
headers = append(headers,
|
||||
"Date de début",
|
||||
"Date de fin",
|
||||
"Durée (jours)",
|
||||
"Commentaire",
|
||||
"Raison d'annulation",
|
||||
)
|
||||
|
||||
spreadsheet.SetHeaders(headers)
|
||||
|
||||
// Add data rows
|
||||
for _, booking := range bookings {
|
||||
vehicle := vehiclesMap[booking.Vehicleid]
|
||||
beneficiary := driversMap[booking.Driver]
|
||||
|
||||
row := []interface{}{}
|
||||
|
||||
// Booking information
|
||||
row = append(row, booking.ID)
|
||||
|
||||
// Status
|
||||
status := ""
|
||||
if booking.Deleted {
|
||||
status = "Annulé"
|
||||
} else {
|
||||
switch booking.Status() {
|
||||
case 1:
|
||||
status = "A venir"
|
||||
case 0:
|
||||
status = "En cours"
|
||||
case -1:
|
||||
status = "Terminé"
|
||||
}
|
||||
}
|
||||
row = append(row, status)
|
||||
|
||||
// Vehicle information
|
||||
row = append(row, vehicle.Type)
|
||||
if vehicleName, ok := vehicle.Data["name"].(string); ok {
|
||||
row = append(row, vehicleName)
|
||||
} else {
|
||||
row = append(row, "")
|
||||
}
|
||||
if licencePlate, ok := vehicle.Data["licence_plate"].(string); ok {
|
||||
row = append(row, licencePlate)
|
||||
} else {
|
||||
row = append(row, "")
|
||||
}
|
||||
|
||||
// Beneficiary data (including other_properties)
|
||||
for _, field := range beneficiaryFields {
|
||||
value := ""
|
||||
// First check direct field
|
||||
if val, ok := beneficiary.Data[field]; ok && val != nil {
|
||||
value = fmt.Sprint(val)
|
||||
} else {
|
||||
// Check in other_properties
|
||||
if otherProps, ok := beneficiary.Data["other_properties"]; ok {
|
||||
if otherPropsMap, ok := otherProps.(map[string]interface{}); ok {
|
||||
if val, ok := otherPropsMap[field]; ok && val != nil {
|
||||
value = fmt.Sprint(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
row = append(row, value)
|
||||
}
|
||||
|
||||
// Booking dates
|
||||
row = append(row, booking.Startdate.Format("2006-01-02"))
|
||||
row = append(row, booking.Enddate.Format("2006-01-02"))
|
||||
|
||||
// Duration in days
|
||||
duration := booking.Enddate.Sub(booking.Startdate).Hours() / 24
|
||||
row = append(row, fmt.Sprintf("%.0f", duration))
|
||||
|
||||
// Comment
|
||||
comment := ""
|
||||
if booking.Data != nil {
|
||||
if commentVal, ok := booking.Data["comment"]; ok && commentVal != nil {
|
||||
comment = fmt.Sprint(commentVal)
|
||||
}
|
||||
}
|
||||
row = append(row, comment)
|
||||
|
||||
// Cancellation reason
|
||||
cancellationReason := ""
|
||||
if booking.Deleted && booking.Data != nil {
|
||||
if reasonVal, ok := booking.Data["reason"]; ok && reasonVal != nil {
|
||||
cancellationReason = fmt.Sprint(reasonVal)
|
||||
}
|
||||
}
|
||||
row = append(row, cancellationReason)
|
||||
|
||||
spreadsheet.AddRow(row)
|
||||
}
|
||||
|
||||
// Write Excel to response
|
||||
w.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
w.Header().Set("Content-Disposition", "attachment; filename=\"export-reservations-vehicules.xlsx\"")
|
||||
|
||||
if err := spreadsheet.GetFile().Write(w); err != nil {
|
||||
log.Error().Err(err).Msg("Error generating Excel file")
|
||||
http.Error(w, "Error generating Excel file", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (r *XLSXRenderer) VehicleBookingsAdmin(w http.ResponseWriter, bookings []fleetsstorage.Booking, vehiclesMap map[string]interface{}, driversMap map[string]interface{}, groupsMap map[string]any) {
|
||||
// Create Excel spreadsheet
|
||||
spreadsheet := r.NewSpreadsheet("Réservations véhicules")
|
||||
|
||||
// Build headers
|
||||
headers := []string{
|
||||
"ID Réservation",
|
||||
"Statut",
|
||||
"Date de début",
|
||||
"Date de fin",
|
||||
"Durée (jours)",
|
||||
"Commentaire",
|
||||
"Raison d'annulation",
|
||||
"Type de véhicule",
|
||||
"Nom du véhicule",
|
||||
"Immatriculation",
|
||||
"Gestionnaire véhicule",
|
||||
}
|
||||
|
||||
// Add vehicle optional fields from config
|
||||
vehicleOptionalFields := r.Config.Get("modules.fleets.vehicle_optional_fields")
|
||||
vehicleFields := []string{}
|
||||
vehicleHeaders := []string{}
|
||||
|
||||
if vehicleOptionalFieldsList, ok := vehicleOptionalFields.([]interface{}); ok {
|
||||
for _, field := range vehicleOptionalFieldsList {
|
||||
if fieldMap, ok := field.(map[string]interface{}); ok {
|
||||
if name, ok := fieldMap["name"].(string); ok {
|
||||
vehicleFields = append(vehicleFields, name)
|
||||
label := name
|
||||
if labelVal, ok := fieldMap["label"].(string); ok {
|
||||
label = labelVal
|
||||
}
|
||||
vehicleHeaders = append(vehicleHeaders, fmt.Sprintf("Véhicule - %s", label))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
headers = append(headers, vehicleHeaders...)
|
||||
|
||||
// Add beneficiary fields from config
|
||||
beneficiaryOptionalFields := r.Config.Get("modules.beneficiaries.profile_optional_fields")
|
||||
beneficiaryFields := []string{"last_name", "first_name", "email", "phone_number"}
|
||||
beneficiaryHeaders := []string{"Bénéficiaire - Nom", "Bénéficiaire - Prénom", "Bénéficiaire - Email", "Bénéficiaire - Téléphone"}
|
||||
|
||||
if beneficiaryOptionalFieldsList, ok := beneficiaryOptionalFields.([]interface{}); ok {
|
||||
for _, field := range beneficiaryOptionalFieldsList {
|
||||
if fieldMap, ok := field.(map[string]interface{}); ok {
|
||||
if name, ok := fieldMap["name"].(string); ok {
|
||||
beneficiaryFields = append(beneficiaryFields, name)
|
||||
label := name
|
||||
if labelVal, ok := fieldMap["label"].(string); ok {
|
||||
label = labelVal
|
||||
}
|
||||
beneficiaryHeaders = append(beneficiaryHeaders, fmt.Sprintf("Bénéficiaire - %s", label))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
headers = append(headers, beneficiaryHeaders...)
|
||||
|
||||
spreadsheet.SetHeaders(headers)
|
||||
|
||||
// Add data rows
|
||||
for _, booking := range bookings {
|
||||
// Get vehicle from map
|
||||
var vehicle fleetsstorage.Vehicle
|
||||
if v, ok := vehiclesMap[booking.Vehicleid]; ok {
|
||||
if vTyped, ok := v.(fleetsstorage.Vehicle); ok {
|
||||
vehicle = vTyped
|
||||
}
|
||||
}
|
||||
|
||||
// Get beneficiary from map
|
||||
var beneficiary mobilityaccountsstorage.Account
|
||||
if d, ok := driversMap[booking.Driver]; ok {
|
||||
if dTyped, ok := d.(mobilityaccountsstorage.Account); ok {
|
||||
beneficiary = dTyped
|
||||
}
|
||||
}
|
||||
|
||||
row := []interface{}{}
|
||||
|
||||
// Booking information
|
||||
row = append(row, booking.ID)
|
||||
|
||||
// Status
|
||||
status := ""
|
||||
if booking.Deleted {
|
||||
status = "Annulé"
|
||||
} else {
|
||||
switch booking.Status() {
|
||||
case 1:
|
||||
status = "A venir"
|
||||
case 0:
|
||||
status = "En cours"
|
||||
case -1:
|
||||
status = "Terminé"
|
||||
}
|
||||
}
|
||||
row = append(row, status)
|
||||
|
||||
// Booking dates
|
||||
row = append(row, booking.Startdate.Format("2006-01-02"))
|
||||
row = append(row, booking.Enddate.Format("2006-01-02"))
|
||||
|
||||
// Duration in days
|
||||
duration := booking.Enddate.Sub(booking.Startdate).Hours() / 24
|
||||
row = append(row, fmt.Sprintf("%.0f", duration))
|
||||
|
||||
// Comment
|
||||
comment := ""
|
||||
if booking.Data != nil {
|
||||
if commentVal, ok := booking.Data["comment"]; ok && commentVal != nil {
|
||||
comment = fmt.Sprint(commentVal)
|
||||
}
|
||||
}
|
||||
row = append(row, comment)
|
||||
|
||||
// Cancellation reason
|
||||
cancellationReason := ""
|
||||
if booking.Deleted && booking.Data != nil {
|
||||
if reasonVal, ok := booking.Data["reason"]; ok && reasonVal != nil {
|
||||
cancellationReason = fmt.Sprint(reasonVal)
|
||||
}
|
||||
}
|
||||
row = append(row, cancellationReason)
|
||||
|
||||
// Vehicle information
|
||||
row = append(row, vehicle.Type)
|
||||
if vehicleName, ok := vehicle.Data["name"].(string); ok {
|
||||
row = append(row, vehicleName)
|
||||
} else {
|
||||
row = append(row, "")
|
||||
}
|
||||
if licencePlate, ok := vehicle.Data["licence_plate"].(string); ok {
|
||||
row = append(row, licencePlate)
|
||||
} else {
|
||||
row = append(row, "")
|
||||
}
|
||||
|
||||
// Vehicle administrator (group name)
|
||||
administratorName := ""
|
||||
if len(vehicle.Administrators) > 0 {
|
||||
if group, ok := groupsMap[vehicle.Administrators[0]]; ok {
|
||||
if groupTyped, ok := group.(groupsstorage.Group); ok {
|
||||
if name, ok := groupTyped.Data["name"]; ok {
|
||||
administratorName = fmt.Sprint(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
row = append(row, administratorName)
|
||||
|
||||
// Vehicle optional fields
|
||||
for _, field := range vehicleFields {
|
||||
value := ""
|
||||
if val, ok := vehicle.Data[field]; ok && val != nil {
|
||||
value = fmt.Sprint(val)
|
||||
}
|
||||
row = append(row, value)
|
||||
}
|
||||
|
||||
// Beneficiary data (including other_properties)
|
||||
for _, field := range beneficiaryFields {
|
||||
value := ""
|
||||
// First check direct field
|
||||
if val, ok := beneficiary.Data[field]; ok && val != nil {
|
||||
value = fmt.Sprint(val)
|
||||
} else {
|
||||
// Check in other_properties
|
||||
if otherProps, ok := beneficiary.Data["other_properties"]; ok {
|
||||
if otherPropsMap, ok := otherProps.(map[string]interface{}); ok {
|
||||
if val, ok := otherPropsMap[field]; ok && val != nil {
|
||||
value = fmt.Sprint(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
row = append(row, value)
|
||||
}
|
||||
|
||||
spreadsheet.AddRow(row)
|
||||
}
|
||||
|
||||
// Write Excel to response
|
||||
w.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
w.Header().Set("Content-Disposition", "attachment; filename=\"all_bookings.xlsx\"")
|
||||
|
||||
if err := spreadsheet.GetFile().Write(w); err != nil {
|
||||
log.Error().Err(err).Msg("Error generating Excel file")
|
||||
http.Error(w, "Error generating Excel file", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
60
renderer/xlsx/xlsx.go
Normal file
60
renderer/xlsx/xlsx.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package xlsx
|
||||
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
"github.com/xuri/excelize/v2"
|
||||
)
|
||||
|
||||
// XLSXRenderer handles rendering data to Excel XLSX format
|
||||
type XLSXRenderer struct {
|
||||
Config *viper.Viper
|
||||
}
|
||||
|
||||
func NewXLSXRenderer(config *viper.Viper) *XLSXRenderer {
|
||||
return &XLSXRenderer{
|
||||
Config: config,
|
||||
}
|
||||
}
|
||||
|
||||
// Spreadsheet represents an Excel spreadsheet
|
||||
type Spreadsheet struct {
|
||||
file *excelize.File
|
||||
sheetName string
|
||||
rowIndex int
|
||||
}
|
||||
|
||||
// NewSpreadsheet creates a new Excel spreadsheet
|
||||
func (r *XLSXRenderer) NewSpreadsheet(sheetName string) *Spreadsheet {
|
||||
f := excelize.NewFile()
|
||||
// Rename default sheet
|
||||
f.SetSheetName("Sheet1", sheetName)
|
||||
|
||||
return &Spreadsheet{
|
||||
file: f,
|
||||
sheetName: sheetName,
|
||||
rowIndex: 1,
|
||||
}
|
||||
}
|
||||
|
||||
// SetHeaders sets the header row
|
||||
func (s *Spreadsheet) SetHeaders(headers []string) {
|
||||
for i, header := range headers {
|
||||
cell, _ := excelize.CoordinatesToCellName(i+1, s.rowIndex)
|
||||
s.file.SetCellValue(s.sheetName, cell, header)
|
||||
}
|
||||
s.rowIndex++
|
||||
}
|
||||
|
||||
// AddRow adds a data row
|
||||
func (s *Spreadsheet) AddRow(values []interface{}) {
|
||||
for i, value := range values {
|
||||
cell, _ := excelize.CoordinatesToCellName(i+1, s.rowIndex)
|
||||
s.file.SetCellValue(s.sheetName, cell, value)
|
||||
}
|
||||
s.rowIndex++
|
||||
}
|
||||
|
||||
// GetFile returns the underlying excelize File
|
||||
func (s *Spreadsheet) GetFile() *excelize.File {
|
||||
return s.file
|
||||
}
|
||||
Reference in New Issue
Block a user