570 lines
25 KiB
Go
570 lines
25 KiB
Go
package dto
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
"wucher/internal/domain/base"
|
|
"wucher/internal/shared/pkg/userctx"
|
|
"wucher/internal/shared/pkg/uuidv7"
|
|
)
|
|
|
|
// JSON:API Base DTOs
|
|
|
|
type BaseAttributes struct {
|
|
BaseName string `json:"base" example:"Lude"`
|
|
BaseAbbreviation string `json:"base_abbreviation,omitempty" example:"LOIG"`
|
|
BaseCategory string `json:"base_category,omitempty" example:"regular" enums:"regular,hems"`
|
|
Address string `json:"address,omitempty" example:"Lude Airfield"`
|
|
Latitude float64 `json:"latitude" example:"47.3769"`
|
|
Longitude float64 `json:"longitude" example:"8.5417"`
|
|
LandlineNumber string `json:"landline_number,omitempty" example:"+62-21-1234"`
|
|
MobileNumber string `json:"mobile_number,omitempty" example:"+62-812-1234"`
|
|
Email string `json:"email,omitempty" example:"lude@wucher.local"`
|
|
SortKey *int `json:"sortkey" example:"1"`
|
|
SMSAlert bool `json:"sms_alert" example:"false"`
|
|
Checklist bool `json:"checklist" example:"false"`
|
|
LegTime string `json:"leg_time,omitempty" example:"00:20"`
|
|
DefaultStartTimeType string `json:"default_start_time_type,omitempty" example:"FIXED" enums:"FIXED,BMCT,ECET"`
|
|
DefaultEndTimeType string `json:"default_end_time_type,omitempty" example:"FIXED" enums:"FIXED,BMCT,ECET"`
|
|
ShiftTime BaseShiftTime `json:"shift_time"`
|
|
UTC string `json:"utc" example:"0"`
|
|
Foto *BaseFoto `json:"foto,omitempty"`
|
|
Dry bool `json:"dry" example:"false"`
|
|
ControlCenter bool `json:"control_center" example:"false"`
|
|
DUL bool `json:"dul" example:"false"`
|
|
Notes string `json:"notes,omitempty" example:"Main base"`
|
|
HEMSEDCContactIDs []string `json:"hems_edc_contact_ids,omitempty"`
|
|
HEMSEDCs []BaseContact `json:"hems_edc,omitempty"`
|
|
MedPaxContactIDs []string `json:"med_pax_contact_ids,omitempty"`
|
|
MedPax []BaseContact `json:"med_pax,omitempty"`
|
|
ResponsiblePilotContactIDs []string `json:"responsible_pilot_contact_ids,omitempty"`
|
|
ResponsiblePilots []BaseContact `json:"responsible_pilots,omitempty"`
|
|
OperationalShiftTimes []BaseOperationalShiftTime `json:"operational_shift_times,omitempty"`
|
|
IsActive bool `json:"is_active" example:"true"`
|
|
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
|
UpdatedBy string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
|
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
|
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
|
DeletedAt string `json:"deleted_at,omitempty" example:"2026-03-12T03:04:05Z" swaggerignore:"true"`
|
|
DeletedBy string `json:"deleted_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" swaggerignore:"true"`
|
|
}
|
|
|
|
type BaseContact struct {
|
|
ID string `json:"id"`
|
|
FirstName string `json:"first_name,omitempty"`
|
|
LastName string `json:"last_name,omitempty"`
|
|
}
|
|
|
|
type BaseShiftTime struct {
|
|
Start string `json:"start,omitempty" example:"06:00"`
|
|
End string `json:"end,omitempty" example:"21:00"`
|
|
}
|
|
|
|
type BaseOperationalShiftTime struct {
|
|
DateStart string `json:"date_start" example:"2026-04-12"`
|
|
DateEnd string `json:"date_end" example:"2026-04-12"`
|
|
StartTimeType string `json:"start_time_type,omitempty" example:"FIXED" enums:"FIXED,BMCT,ECET"`
|
|
EndTimeType string `json:"end_time_type,omitempty" example:"FIXED" enums:"FIXED,BMCT,ECET"`
|
|
ShiftTime BaseShiftTime `json:"shift_time"`
|
|
}
|
|
|
|
type BaseFoto struct {
|
|
UUID string `json:"uuid" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
|
DownloadURL string `json:"download_url" example:"https://storage.example.com/base/lude.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=900&X-Amz-Signature=abc123"`
|
|
ThumbnailDownloadURL string `json:"thumbnail_download_url,omitempty" example:"https://storage.example.com/base/lude.thumb.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=900&X-Amz-Signature=abc123"`
|
|
OriginalSizeBytes *int64 `json:"original_size_bytes,omitempty" example:"1024000"`
|
|
}
|
|
|
|
type BaseResource struct {
|
|
Type string `json:"type" example:"base"`
|
|
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
|
Attributes BaseAttributes `json:"attributes"`
|
|
}
|
|
|
|
type BaseResponse struct {
|
|
Data BaseResource `json:"data"`
|
|
}
|
|
|
|
type BaseListResponse struct {
|
|
Data []BaseResource `json:"data"`
|
|
Meta struct {
|
|
PageNumber int `json:"page_number" example:"1"`
|
|
PageSize int `json:"page_size" example:"20"`
|
|
Total int64 `json:"total" example:"120"`
|
|
} `json:"meta"`
|
|
}
|
|
|
|
type BaseDataTableResponse struct {
|
|
Data []BaseResource `json:"data"`
|
|
Meta struct {
|
|
Draw int `json:"draw" example:"1"`
|
|
RecordsTotal int64 `json:"records_total" example:"120"`
|
|
RecordsFiltered int64 `json:"records_filtered" example:"12"`
|
|
} `json:"meta"`
|
|
}
|
|
|
|
type BaseCreateAttributes struct {
|
|
BaseName string `json:"base" validate:"required" example:"Lude"`
|
|
BaseAbbreviation *string `json:"base_abbreviation,omitempty" example:"LOIG"`
|
|
BaseCategory *string `json:"base_category,omitempty" example:"regular" enums:"regular,hems"`
|
|
Address *string `json:"address,omitempty" example:"Lude Airfield"`
|
|
Latitude *float64 `json:"latitude" validate:"required,gte=-90,lte=90" example:"47.3769"`
|
|
Longitude *float64 `json:"longitude" validate:"required,gte=-180,lte=180" example:"8.5417"`
|
|
LandlineNumber *string `json:"landline_number,omitempty" example:"+62-21-1234"`
|
|
MobileNumber *string `json:"mobile_number,omitempty" example:"+62-812-1234"`
|
|
Email *string `json:"email,omitempty" example:"lude@wucher.local"`
|
|
SortKey *int `json:"sortkey" example:"1"`
|
|
SMSAlert *bool `json:"sms_alert,omitempty" example:"false"`
|
|
Checklist *bool `json:"checklist,omitempty" example:"false"`
|
|
LegTime *string `json:"leg_time,omitempty" example:"00:20"`
|
|
DefaultStartTimeType *string `json:"default_start_time_type,omitempty" example:"FIXED" enums:"FIXED,BMCT,ECET"`
|
|
DefaultEndTimeType *string `json:"default_end_time_type,omitempty" example:"FIXED" enums:"FIXED,BMCT,ECET"`
|
|
ShiftTime *BaseShiftTime `json:"shift_time,omitempty"`
|
|
DefaultShiftStart *string `json:"default_shift_start,omitempty" example:"06:00:00"`
|
|
DefaultShiftEnd *string `json:"default_shift_end,omitempty" example:"21:00:00"`
|
|
DefaultShiftTime *string `json:"default_shift_time,omitempty" example:"12:00"`
|
|
UTC *string `json:"utc,omitempty" example:"0"`
|
|
FileUUID *string `json:"file_uuid,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e90"`
|
|
Dry *bool `json:"dry,omitempty" example:"false"`
|
|
ControlCenter *bool `json:"control_center,omitempty" example:"false"`
|
|
DUL *bool `json:"dul,omitempty" example:"false"`
|
|
Notes *string `json:"notes,omitempty" example:"Main base"`
|
|
HEMSEDCContactIDs []string `json:"hems_edc_contact_ids,omitempty"`
|
|
MedPaxContactIDs []string `json:"med_pax_contact_ids,omitempty"`
|
|
ResponsiblePilotContactIDs []string `json:"responsible_pilot_contact_ids,omitempty"`
|
|
OperationalShiftTimes []BaseOperationalShiftTime `json:"operational_shift_times,omitempty"`
|
|
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
|
CreatedBy *string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" swaggerignore:"true"`
|
|
UpdatedBy *string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" swaggerignore:"true"`
|
|
}
|
|
|
|
type BaseCreateData struct {
|
|
Type string `json:"type" validate:"required,oneof=base_create base hems_base" enums:"base" example:"base"`
|
|
Attributes BaseCreateAttributes `json:"attributes" validate:"required"`
|
|
}
|
|
|
|
type BaseCreateRequest struct {
|
|
Data BaseCreateData `json:"data" validate:"required"`
|
|
}
|
|
|
|
type BaseUpdateAttributes struct {
|
|
BaseName *string `json:"base,omitempty" example:"Lude"`
|
|
BaseAbbreviation *string `json:"base_abbreviation,omitempty" example:"LOIG"`
|
|
BaseCategory *string `json:"base_category,omitempty" example:"regular" enums:"regular,hems"`
|
|
Address *string `json:"address,omitempty" example:"Lude Airfield"`
|
|
Latitude *float64 `json:"latitude,omitempty" validate:"omitempty,gte=-90,lte=90" example:"47.3769"`
|
|
Longitude *float64 `json:"longitude,omitempty" validate:"omitempty,gte=-180,lte=180" example:"8.5417"`
|
|
LandlineNumber *string `json:"landline_number,omitempty" example:"+62-21-1234"`
|
|
MobileNumber *string `json:"mobile_number,omitempty" example:"+62-812-1234"`
|
|
Email *string `json:"email,omitempty" example:"lude@wucher.local"`
|
|
SortKey NullInt `json:"sortkey,omitempty" swaggertype:"integer" example:"1"`
|
|
SMSAlert *bool `json:"sms_alert,omitempty" example:"false"`
|
|
Checklist *bool `json:"checklist,omitempty" example:"false"`
|
|
LegTime *string `json:"leg_time,omitempty" example:"00:20"`
|
|
DefaultStartTimeType *string `json:"default_start_time_type,omitempty" example:"FIXED" enums:"FIXED,BMCT,ECET"`
|
|
DefaultEndTimeType *string `json:"default_end_time_type,omitempty" example:"FIXED" enums:"FIXED,BMCT,ECET"`
|
|
DefaultShiftStart *string `json:"default_shift_start,omitempty" example:"06:00:00"`
|
|
DefaultShiftEnd *string `json:"default_shift_end,omitempty" example:"21:00:00"`
|
|
DefaultShiftTime *string `json:"default_shift_time,omitempty" example:"12:00"`
|
|
ShiftTime *BaseShiftTime `json:"shift_time,omitempty"`
|
|
UTC *string `json:"utc,omitempty" example:"0"`
|
|
FileUUID *string `json:"file_uuid,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e90"`
|
|
Dry *bool `json:"dry,omitempty" example:"false"`
|
|
ControlCenter *bool `json:"control_center,omitempty" example:"false"`
|
|
DUL *bool `json:"dul,omitempty" example:"false"`
|
|
Notes *string `json:"notes,omitempty" example:"Main base"`
|
|
HEMSEDCContactIDs *[]string `json:"hems_edc_contact_ids,omitempty"`
|
|
MedPaxContactIDs *[]string `json:"med_pax_contact_ids,omitempty"`
|
|
ResponsiblePilotContactIDs *[]string `json:"responsible_pilot_contact_ids,omitempty"`
|
|
OperationalShiftTimes []BaseOperationalShiftTime `json:"operational_shift_times,omitempty"`
|
|
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
|
CreatedBy *string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" swaggerignore:"true"`
|
|
UpdatedBy *string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" swaggerignore:"true"`
|
|
}
|
|
|
|
type BaseUpdateData struct {
|
|
Type string `json:"type" validate:"required,oneof=base_update base hems_base" enums:"base_update" example:"base_update"`
|
|
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
|
Attributes BaseUpdateAttributes `json:"attributes" validate:"required"`
|
|
}
|
|
|
|
type BaseUpdateRequest struct {
|
|
Data BaseUpdateData `json:"data" validate:"required"`
|
|
}
|
|
|
|
type BaseDeleteAttributes struct {
|
|
Deleted bool `json:"deleted" example:"true"`
|
|
}
|
|
|
|
type BaseDeleteResource struct {
|
|
Type string `json:"type" example:"base_delete"`
|
|
ID string `json:"id" example:"019d6b10-a385-7e9e-a553-8e892bcec0ef"`
|
|
Attributes BaseDeleteAttributes `json:"attributes"`
|
|
}
|
|
|
|
type BaseDeleteResponse struct {
|
|
Data BaseDeleteResource `json:"data"`
|
|
}
|
|
|
|
type BaseResponseMappingInput struct {
|
|
ResourceType string
|
|
BaseCategory string
|
|
ID string
|
|
DefaultStartTimeType string
|
|
DefaultEndTimeType string
|
|
ShiftTime BaseShiftTime
|
|
Foto *BaseFoto
|
|
Latitude float64
|
|
Longitude float64
|
|
HEMSEDCContactIDs []string
|
|
HEMSEDCs []BaseContact
|
|
MedPaxContactIDs []string
|
|
MedPax []BaseContact
|
|
ResponsiblePilotContactIDs []string
|
|
ResponsiblePilots []BaseContact
|
|
OperationalShiftTimes []BaseOperationalShiftTime
|
|
}
|
|
|
|
func MapBaseCreateAttributesToDomain(attrs BaseCreateAttributes) *base.Base {
|
|
row := &base.Base{}
|
|
row.BaseName = strings.TrimSpace(attrs.BaseName)
|
|
row.BaseAbbreviation = strings.TrimSpace(stringOrDefault(attrs.BaseAbbreviation, ""))
|
|
row.Address = strings.TrimSpace(stringOrDefault(attrs.Address, ""))
|
|
row.Latitude = floatOrDefault(attrs.Latitude, 0)
|
|
row.Longitude = floatOrDefault(attrs.Longitude, 0)
|
|
row.LandlineNumber = strings.TrimSpace(stringOrDefault(attrs.LandlineNumber, ""))
|
|
row.MobileNumber = strings.TrimSpace(stringOrDefault(attrs.MobileNumber, ""))
|
|
row.Email = strings.TrimSpace(stringOrDefault(attrs.Email, ""))
|
|
row.SortKey = cloneIntPtr(attrs.SortKey)
|
|
row.SMSAlert = boolOrDefault(attrs.SMSAlert, false)
|
|
row.Checklist = boolOrDefault(attrs.Checklist, false)
|
|
row.LegTime = strings.TrimSpace(stringOrDefault(attrs.LegTime, ""))
|
|
row.DefaultStartTimeType = normalizeShiftTimeType(stringOrDefault(attrs.DefaultStartTimeType, ""))
|
|
row.DefaultEndTimeType = normalizeShiftTimeType(stringOrDefault(attrs.DefaultEndTimeType, ""))
|
|
row.UTC = normalizeUTC(attrs.UTC)
|
|
row.Dry = boolOrDefault(attrs.Dry, false)
|
|
row.ControlCenter = boolOrDefault(attrs.ControlCenter, false)
|
|
row.DUL = boolOrDefault(attrs.DUL, false)
|
|
row.Notes = strings.TrimSpace(stringOrDefault(attrs.Notes, ""))
|
|
row.IsActive = boolOrDefault(attrs.IsActive, true)
|
|
row.OperationalShiftTimes = mapBaseOperationalShiftTimesToDomain(attrs.OperationalShiftTimes)
|
|
return row
|
|
}
|
|
|
|
func ApplyBaseUpdateAttributesToDomain(row *base.Base, attrs BaseUpdateAttributes) {
|
|
if attrs.BaseName != nil {
|
|
row.BaseName = strings.TrimSpace(*attrs.BaseName)
|
|
}
|
|
if attrs.BaseAbbreviation != nil {
|
|
row.BaseAbbreviation = strings.TrimSpace(*attrs.BaseAbbreviation)
|
|
}
|
|
if attrs.Address != nil {
|
|
row.Address = strings.TrimSpace(*attrs.Address)
|
|
}
|
|
if attrs.Latitude != nil {
|
|
row.Latitude = *attrs.Latitude
|
|
}
|
|
if attrs.Longitude != nil {
|
|
row.Longitude = *attrs.Longitude
|
|
}
|
|
if attrs.LandlineNumber != nil {
|
|
row.LandlineNumber = strings.TrimSpace(*attrs.LandlineNumber)
|
|
}
|
|
if attrs.MobileNumber != nil {
|
|
row.MobileNumber = strings.TrimSpace(*attrs.MobileNumber)
|
|
}
|
|
if attrs.Email != nil {
|
|
row.Email = strings.TrimSpace(*attrs.Email)
|
|
}
|
|
if attrs.SortKey.Set {
|
|
if attrs.SortKey.Valid {
|
|
v := attrs.SortKey.Value
|
|
row.SortKey = &v
|
|
} else {
|
|
row.SortKey = nil
|
|
}
|
|
}
|
|
if attrs.SMSAlert != nil {
|
|
row.SMSAlert = *attrs.SMSAlert
|
|
}
|
|
if attrs.Checklist != nil {
|
|
row.Checklist = *attrs.Checklist
|
|
}
|
|
if attrs.LegTime != nil {
|
|
row.LegTime = strings.TrimSpace(*attrs.LegTime)
|
|
}
|
|
if attrs.DefaultStartTimeType != nil {
|
|
row.DefaultStartTimeType = normalizeShiftTimeType(*attrs.DefaultStartTimeType)
|
|
}
|
|
if attrs.DefaultEndTimeType != nil {
|
|
row.DefaultEndTimeType = normalizeShiftTimeType(*attrs.DefaultEndTimeType)
|
|
}
|
|
if attrs.UTC != nil {
|
|
row.UTC = normalizeUTC(attrs.UTC)
|
|
}
|
|
if attrs.Dry != nil {
|
|
row.Dry = *attrs.Dry
|
|
}
|
|
if attrs.ControlCenter != nil {
|
|
row.ControlCenter = *attrs.ControlCenter
|
|
}
|
|
if attrs.DUL != nil {
|
|
row.DUL = *attrs.DUL
|
|
}
|
|
if attrs.Notes != nil {
|
|
row.Notes = strings.TrimSpace(*attrs.Notes)
|
|
}
|
|
if attrs.IsActive != nil {
|
|
row.IsActive = *attrs.IsActive
|
|
}
|
|
}
|
|
|
|
func mapBaseOperationalShiftTimesToDomain(items []BaseOperationalShiftTime) []base.BaseOperationalShiftTime {
|
|
if len(items) == 0 {
|
|
return nil
|
|
}
|
|
out := make([]base.BaseOperationalShiftTime, 0, len(items))
|
|
for i := range items {
|
|
dateStart := parseBaseOperationDatePtr(items[i].DateStart)
|
|
dateEnd := parseBaseOperationDatePtr(items[i].DateEnd)
|
|
out = append(out, base.BaseOperationalShiftTime{
|
|
DateStart: dateStart,
|
|
DateEnd: dateEnd,
|
|
StartTimeType: normalizeShiftTimeType(items[i].StartTimeType),
|
|
EndTimeType: normalizeShiftTimeType(items[i].EndTimeType),
|
|
ShiftStart: normalizeBaseClockValue(items[i].ShiftTime.Start),
|
|
ShiftEnd: normalizeBaseClockValue(items[i].ShiftTime.End),
|
|
})
|
|
}
|
|
return out
|
|
}
|
|
|
|
func MapBaseOperationalShiftTimesToDTO(items []base.BaseOperationalShiftTime) []BaseOperationalShiftTime {
|
|
if len(items) == 0 {
|
|
return nil
|
|
}
|
|
out := make([]BaseOperationalShiftTime, 0, len(items))
|
|
for i := range items {
|
|
out = append(out, BaseOperationalShiftTime{
|
|
DateStart: timePtrToDateString(items[i].DateStart),
|
|
DateEnd: timePtrToDateString(items[i].DateEnd),
|
|
StartTimeType: normalizeShiftTimeType(items[i].StartTimeType),
|
|
EndTimeType: normalizeShiftTimeType(items[i].EndTimeType),
|
|
ShiftTime: BaseShiftTime{
|
|
Start: DisplayBaseShiftValue(items[i].StartTimeType, items[i].ShiftStart),
|
|
End: DisplayBaseShiftValue(items[i].EndTimeType, items[i].ShiftEnd),
|
|
},
|
|
})
|
|
}
|
|
return out
|
|
}
|
|
|
|
func parseBaseOperationDate(raw string) time.Time {
|
|
parsed, err := time.Parse("2006-01-02", strings.TrimSpace(raw))
|
|
if err != nil {
|
|
return time.Time{}
|
|
}
|
|
return parsed.UTC()
|
|
}
|
|
|
|
func parseBaseOperationDatePtr(raw string) *time.Time {
|
|
parsed := parseBaseOperationDate(raw)
|
|
if parsed.IsZero() {
|
|
return nil
|
|
}
|
|
return &parsed
|
|
}
|
|
|
|
func timePtrToDateString(v *time.Time) string {
|
|
if v == nil || v.IsZero() {
|
|
return ""
|
|
}
|
|
return v.UTC().Format("2006-01-02")
|
|
}
|
|
|
|
func normalizeBaseClockValue(raw string) string {
|
|
trimmed := strings.TrimSpace(raw)
|
|
if trimmed == "" {
|
|
return ""
|
|
}
|
|
if len(trimmed) == 5 {
|
|
trimmed += ":00"
|
|
}
|
|
parsed, err := time.Parse("15:04:05", trimmed)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return parsed.UTC().Format("15:04:05")
|
|
}
|
|
|
|
func normalizeBaseShiftDisplay(raw string) string {
|
|
s := strings.TrimSpace(raw)
|
|
if s == "" {
|
|
return s
|
|
}
|
|
layouts := []string{
|
|
"15:04:05",
|
|
"15:04",
|
|
"2006-01-02 15:04:05",
|
|
"2006-01-02 15:04",
|
|
time.RFC3339,
|
|
time.RFC3339Nano,
|
|
}
|
|
for _, layout := range layouts {
|
|
if parsed, err := time.Parse(layout, s); err == nil {
|
|
return parsed.UTC().Format("15:04")
|
|
}
|
|
}
|
|
if len(s) >= 5 && isDigitBaseClock(s[0]) && isDigitBaseClock(s[1]) && s[2] == ':' && isDigitBaseClock(s[3]) && isDigitBaseClock(s[4]) {
|
|
return s[:5]
|
|
}
|
|
return s
|
|
}
|
|
|
|
// DisplayBaseShiftValue returns the user-facing representation for a shift value.
|
|
// Fixed shifts are shown as HH:MM, while BMCT/ECET are shown as labels.
|
|
func DisplayBaseShiftValue(timeType, raw string) string {
|
|
switch normalizeShiftTimeType(timeType) {
|
|
case "BMCT", "ECET":
|
|
return normalizeShiftTimeType(timeType)
|
|
default:
|
|
return normalizeBaseShiftDisplay(raw)
|
|
}
|
|
}
|
|
|
|
func normalizeShiftTimeType(raw string) string {
|
|
switch strings.ToUpper(strings.TrimSpace(raw)) {
|
|
case "", "FIXED":
|
|
return "FIXED"
|
|
case "BMCT":
|
|
return "BMCT"
|
|
case "ECET":
|
|
return "ECET"
|
|
default:
|
|
return strings.ToUpper(strings.TrimSpace(raw))
|
|
}
|
|
}
|
|
|
|
func isDigitBaseClock(b byte) bool {
|
|
return b >= '0' && b <= '9'
|
|
}
|
|
|
|
func MapBaseToResource(row *base.Base, in BaseResponseMappingInput) BaseResource {
|
|
if row == nil {
|
|
return BaseResource{
|
|
Type: in.ResourceType,
|
|
Attributes: BaseAttributes{
|
|
BaseCategory: in.BaseCategory,
|
|
DefaultStartTimeType: normalizeShiftTimeType(in.DefaultStartTimeType),
|
|
DefaultEndTimeType: normalizeShiftTimeType(in.DefaultEndTimeType),
|
|
ShiftTime: in.ShiftTime,
|
|
OperationalShiftTimes: in.OperationalShiftTimes,
|
|
Latitude: in.Latitude,
|
|
Longitude: in.Longitude,
|
|
},
|
|
}
|
|
}
|
|
return BaseResource{
|
|
Type: in.ResourceType,
|
|
ID: in.ID,
|
|
Attributes: BaseAttributes{
|
|
BaseName: row.BaseName,
|
|
BaseAbbreviation: row.BaseAbbreviation,
|
|
BaseCategory: in.BaseCategory,
|
|
Address: row.Address,
|
|
Latitude: row.Latitude,
|
|
Longitude: row.Longitude,
|
|
LandlineNumber: row.LandlineNumber,
|
|
MobileNumber: row.MobileNumber,
|
|
Email: row.Email,
|
|
SortKey: cloneIntPtr(row.SortKey),
|
|
SMSAlert: row.SMSAlert,
|
|
Checklist: row.Checklist,
|
|
LegTime: row.LegTime,
|
|
DefaultStartTimeType: normalizeShiftTimeType(row.DefaultStartTimeType),
|
|
DefaultEndTimeType: normalizeShiftTimeType(row.DefaultEndTimeType),
|
|
ShiftTime: in.ShiftTime,
|
|
UTC: row.UTC,
|
|
Foto: in.Foto,
|
|
Dry: row.Dry,
|
|
ControlCenter: row.ControlCenter,
|
|
DUL: row.DUL,
|
|
Notes: row.Notes,
|
|
HEMSEDCContactIDs: in.HEMSEDCContactIDs,
|
|
HEMSEDCs: in.HEMSEDCs,
|
|
MedPaxContactIDs: in.MedPaxContactIDs,
|
|
MedPax: in.MedPax,
|
|
ResponsiblePilotContactIDs: in.ResponsiblePilotContactIDs,
|
|
ResponsiblePilots: in.ResponsiblePilots,
|
|
OperationalShiftTimes: in.OperationalShiftTimes,
|
|
IsActive: row.IsActive,
|
|
CreatedBy: bytesToUUID(row.CreatedBy),
|
|
UpdatedBy: bytesToUUID(row.UpdatedBy),
|
|
CreatedAt: row.CreatedAt.UTC().Format(time.RFC3339),
|
|
UpdatedAt: row.UpdatedAt.UTC().Format(time.RFC3339),
|
|
DeletedAt: timePtrToString(row.DeletedAt),
|
|
DeletedBy: bytesToUUID(row.DeletedBy),
|
|
},
|
|
}
|
|
}
|
|
|
|
func stringOrDefault(v *string, d string) string {
|
|
if v == nil {
|
|
return d
|
|
}
|
|
return *v
|
|
}
|
|
|
|
func boolOrDefault(v *bool, d bool) bool {
|
|
if v == nil {
|
|
return d
|
|
}
|
|
return *v
|
|
}
|
|
|
|
func normalizeUTC(v *string) string {
|
|
s := strings.TrimSpace(stringOrDefault(v, "0"))
|
|
if s == "" {
|
|
return "0"
|
|
}
|
|
return s
|
|
}
|
|
|
|
func floatOrDefault(v *float64, d float64) float64 {
|
|
if v == nil {
|
|
return d
|
|
}
|
|
return *v
|
|
}
|
|
|
|
func cloneIntPtr(v *int) *int {
|
|
if v == nil {
|
|
return nil
|
|
}
|
|
x := *v
|
|
return &x
|
|
}
|
|
|
|
func bytesToUUID(v []byte) string {
|
|
if len(v) == 0 {
|
|
return ""
|
|
}
|
|
if name := userctx.GetDisplayName(context.TODO(), v); name != "" {
|
|
return name
|
|
}
|
|
s, err := uuidv7.BytesToString(v)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return s
|
|
}
|
|
|
|
func timePtrToString(v *time.Time) string {
|
|
if v == nil {
|
|
return ""
|
|
}
|
|
return v.UTC().Format(time.RFC3339)
|
|
}
|