Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
3e1c260
chore: status toggle badge
luckspt Feb 6, 2026
236dc55
chore: date-picker component
luckspt Feb 6, 2026
3748641
chore: calendar component
luckspt Feb 6, 2026
059eddf
chore: native select
luckspt Feb 6, 2026
3136ab9
chore: remove unneeded text
luckspt Feb 6, 2026
dcc238b
chore: accordion collapsed by default on mobile
luckspt Feb 6, 2026
57abf9d
chore: mutate async for await loading
luckspt Feb 6, 2026
ccffe13
chore: info/tasks tabs on company
luckspt Feb 6, 2026
6ec47ca
chore: billing and logos task
luckspt Feb 6, 2026
ad0023c
chore: confirmation task
luckspt Feb 6, 2026
a1f29e0
chore: corlief task
luckspt Feb 6, 2026
e3b45e3
chore: contract task
luckspt Feb 6, 2026
839dcd3
chore: logistics task
luckspt Feb 6, 2026
ce31ce2
chore: session titles task
luckspt Feb 6, 2026
96b7e0d
feat: tasks prototype
luckspt Feb 6, 2026
c44f5a8
feat: refactor task components to support entity type differentiation
Francisca105 Feb 28, 2026
2a38de8
feat: add task components for flights, hotel, and materials
Francisca105 Feb 28, 2026
25144bc
feat: add speaker-specific fields and coverage task components
Francisca105 Feb 28, 2026
d9efbe1
feat: update task components to include LinkedIn fields and enhance h…
Francisca105 Feb 28, 2026
b11c8ee
feat: add contact prop to TaskConfirmation and Tasks components for s…
Francisca105 Feb 28, 2026
68a2b9d
feat: implement task management for companies and speakers with assoc…
Francisca105 Mar 1, 2026
b520551
feat: implement task management for speakers and companies
Francisca105 Mar 1, 2026
4e043d7
feat: enhance task models with new fields and BSON handling for Linke…
Francisca105 Mar 1, 2026
6c2c606
feat: add public image upload functionality for companies and speakers
Francisca105 Mar 1, 2026
a74decf
feat: add image handling for companies and speakers in Tasks component
Francisca105 Mar 1, 2026
1937061
feat: add initialUrl prop to ImageUpload component for preview seeding
Francisca105 Mar 1, 2026
c259a8b
feat: enhance tab management in CompanyView component with dynamic ro…
Francisca105 Mar 1, 2026
7ef8b88
feat: add public image upload functionality for companies and speaker…
Francisca105 Mar 1, 2026
f08adee
feat: add PO number input and update handling in TaskBillingLogos com…
Francisca105 Mar 1, 2026
e62068f
feat: add 'Company has paid' checkbox and update contract state handl…
Francisca105 Mar 1, 2026
f9c7734
feat: enhance speaker contact details management with editable fields…
Francisca105 Mar 1, 2026
7a24f98
feat: replace checkboxes with select inputs for media coverage
Francisca105 Mar 1, 2026
7665fca
feat: add needs flights selector and conditional flight details displ…
Francisca105 Mar 1, 2026
3e25e15
feat: add needs hotel selector and conditional display of hotel detai…
Francisca105 Mar 1, 2026
b12f411
feat: add image upload functionality for company and speaker in TaskI…
Francisca105 Mar 1, 2026
3676f67
feat: remove test schedule and completion fields from TaskMaterials c…
Francisca105 Mar 1, 2026
47479f9
feat: implement auto-save functionality for tasks and update UI indic…
Francisca105 Mar 1, 2026
b2b8f57
feat: add Test Schedule component with scheduling and completion func…
Francisca105 Mar 1, 2026
cd6d2aa
feat: add loading indicator for saving state in TaskTimelineItem comp…
Francisca105 Mar 1, 2026
9ac4054
Merge branch 'staging' into dev-luckspt-company-tasks
Francisca105 Mar 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 20 additions & 18 deletions backend/src/models/company.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,31 +43,33 @@ type CompanyParticipation struct {
// These are used to sync communications with Gmail.
GmailThreadIds []string `json:"gmailThreadIds,omitempty" bson:"gmailThreadIds,omitempty"`

// Stand details
StandDetails StandDetails `json:"standDetails,omitempty" bson:"standDetails,omitempty"`
// Stand details
StandDetails StandDetails `json:"standDetails,omitempty" bson:"standDetails,omitempty"`

// Stand and days at the venue
Stands []Stand `json:"stands,omitempty" bson:"stands,omitempty"`
// Stand and days at the venue
Stands []Stand `json:"stands,omitempty" bson:"stands,omitempty"`

// Tasks tracks the company's task pipeline progress.
Tasks CompanyTasks `json:"tasks" bson:"tasks"`
}

type Stand struct {
// Stand identifier
StandID string `json:"standId" bson:"standId"`
// Stand identifier
StandID string `json:"standId" bson:"standId"`

// Day at the venue
Date *time.Time `json:"date,omitempty" bson:"date,omitempty"`
// Day at the venue
Date *time.Time `json:"date,omitempty" bson:"date,omitempty"`
}

type StandDetails struct {
// Number of chairs required by the company
Chairs int `json:"chairs" bson:"chairs"`
// Number of chairs required by the company
Chairs int `json:"chairs" bson:"chairs"`

// Require front table
Table bool `json:"table" bson:"table"`
// Require front table
Table bool `json:"table" bson:"table"`

// Require lettering
Lettering bool `json:"lettering" bson:"lettering"`
// Require lettering
Lettering bool `json:"lettering" bson:"lettering"`
}

// CompanyBillingInfo of company
Expand Down Expand Up @@ -132,11 +134,11 @@ type CompanyParticipationPublic struct {
// Participation's package is a Package _id (see models.Package).
Package PackagePublic `json:"package,omitempty"`

// Stand details
StandDetails StandDetails `json:"standDetails,omitempty"`
// Stand details
StandDetails StandDetails `json:"standDetails,omitempty"`

// Days at the venue
Stands []Stand `json:"stands,omitempty"`
// Days at the venue
Stands []Stand `json:"stands,omitempty"`
}

// CompanyPublic represents a company to be contacted by the team, that will hopefully participate
Expand Down
15 changes: 9 additions & 6 deletions backend/src/models/speaker.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ type SpeakerParticipation struct {
// GmailThreadIds is an array of Gmail thread IDs linked to this participation.
// These are used to sync communications with Gmail.
GmailThreadIds []string `json:"gmailThreadIds,omitempty" bson:"gmailThreadIds,omitempty"`

// Tasks tracks the speaker's task pipeline progress.
Tasks SpeakerTasks `json:"tasks" bson:"tasks"`
}

type SpeakerImages struct {
Expand All @@ -68,17 +71,17 @@ type Speaker struct {
Name string `json:"name" bson:"name"`

// Contact is an _id of Contact (see models.Contact).
Contact *primitive.ObjectID `json:"contact,omitempty" bson:"contact"`
ContactObject *Contact `json:"contactObject,omitempty" bson:"contactObject"`
Contact *primitive.ObjectID `json:"contact,omitempty" bson:"contact"`
ContactObject *Contact `json:"contactObject,omitempty" bson:"contactObject"`

// Title of the speaker (CEO @ HugeCorportation, for example).
Title string `json:"title" bson:"title"`

// Bio of the speaker. Careful, this will be visible on our website!
Bio string `json:"bio" bson:"bio"`

// Company name
CompanyName string `json:"companyName" bson:"companyName"`
// Company name
CompanyName string `json:"companyName" bson:"companyName"`

// This is only visible by the team. Praise and trash talk at will.
Notes string `json:"notes" bson:"notes"`
Expand Down Expand Up @@ -120,8 +123,8 @@ type SpeakerPublic struct {
// Bio of the speaker. Careful, this will be visible on our website!
Bio string `json:"bio" bson:"bio"`

// Company name
CompanyName string `json:"companyName,omitempty" bson:"companyName"`
// Company name
CompanyName string `json:"companyName,omitempty" bson:"companyName"`

Images SpeakerImagesPublic `json:"imgs" bson:"imgs"`
Participations []SpeakerParticipationPublic `json:"participation" bson:"participations"`
Expand Down
265 changes: 265 additions & 0 deletions backend/src/models/task.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
package models

import (
"time"

"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/bsontype"
)

// ============================================================
// Shared task fields (used by both companies and speakers)
// ============================================================

// TaskLogos tracks logo delivery status.
type TaskLogos struct {
Received bool `json:"received" bson:"received"`
NeedsReviewing bool `json:"needsReviewing" bson:"needsReviewing"`
}

// ============================================================
// Company-specific task types
// ============================================================

// CompanyTaskConfirmation tracks confirmation-related progress for a company.
type CompanyTaskConfirmation struct {
AskedForInfo bool `json:"askedForInfo" bson:"askedForInfo"`
}

// CompanyTaskContract tracks contract lifecycle.
type CompanyTaskContract struct {
Sent bool `json:"sent" bson:"sent"`
Created bool `json:"created" bson:"created"`
Signed bool `json:"signed" bson:"signed"`
ReceiptSent bool `json:"receiptSent" bson:"receiptSent"`
Paid bool `json:"paid" bson:"paid"`
}

// CompanyTaskSessionTitles tracks session / workshop titles.
type CompanyTaskSessionTitles struct {
PresentationTitle string `json:"presentationTitle" bson:"presentationTitle"`
WorkshopTitle string `json:"workshopTitle" bson:"workshopTitle"`
}

// CompanyTaskCorlief tracks Corlief-related steps.
type CompanyTaskCorlief struct {
PreNotice bool `json:"preNotice" bson:"preNotice"`
Scheduled bool `json:"scheduled" bson:"scheduled"`
Reserved bool `json:"reserved" bson:"reserved"`
}

// CompanyTaskLogistics tracks logistics info.
type CompanyTaskLogistics struct {
RequestedInfo bool `json:"requestedInfo" bson:"requestedInfo"`
CarStatus string `json:"carStatus" bson:"carStatus"` // "not_responded", "wants", "not_wants"
LicensePlate string `json:"licensePlate" bson:"licensePlate"`
}

// CompanyTasks is the top-level task object embedded in CompanyParticipation.
type CompanyTasks struct {
Confirmation CompanyTaskConfirmation `json:"confirmation" bson:"confirmation"`
Logos TaskLogos `json:"logos" bson:"logos"`
Contract CompanyTaskContract `json:"contract" bson:"contract"`
SessionTitles CompanyTaskSessionTitles `json:"sessionTitles" bson:"sessionTitles"`
Corlief CompanyTaskCorlief `json:"corlief" bson:"corlief"`
Logistics CompanyTaskLogistics `json:"logistics" bson:"logistics"`
PO string `json:"po" bson:"po"`
}

// ============================================================
// Speaker-specific task types
// ============================================================

// SpeakerTaskConfirmation tracks confirmation-related progress for a speaker.
type SpeakerTaskConfirmation struct {
Phone string `json:"phone" bson:"phone"`
LinkedIn string `json:"linkedin" bson:"linkedin"`
WantsLinkedIn string `json:"wantsLinkedinTag" bson:"wantsLinkedinTag"` // "not_responded", "yes", "no"
Observations string `json:"observations" bson:"observations"`
}

// UnmarshalBSON handles legacy bool values stored for WantsLinkedIn.
func (s *SpeakerTaskConfirmation) UnmarshalBSON(data []byte) error {
var raw bson.Raw
if err := bson.Unmarshal(data, &raw); err != nil {
return err
}

decodeString := func(key string) string {
val, err := raw.LookupErr(key)
if err != nil {
return ""
}
if val.Type == bsontype.String {
v, _ := val.StringValueOK()
return v
}
return ""
}

decodeLinkedIn := func(key string) string {
val, err := raw.LookupErr(key)
if err != nil {
return "not_responded"
}
switch val.Type {
case bsontype.Boolean:
b, _ := val.BooleanOK()
return boolToString(b)
case bsontype.String:
v, _ := val.StringValueOK()
return v
default:
return "not_responded"
}
}

s.Phone = decodeString("phone")
s.LinkedIn = decodeString("linkedin")
s.WantsLinkedIn = decodeLinkedIn("wantsLinkedinTag")
s.Observations = decodeString("observations")
return nil
}

// MarshalBSON always writes string values.
func (s SpeakerTaskConfirmation) MarshalBSON() ([]byte, error) {
return bson.Marshal(bson.D{
{Key: "phone", Value: s.Phone},
{Key: "linkedin", Value: s.LinkedIn},
{Key: "wantsLinkedinTag", Value: s.WantsLinkedIn},
{Key: "observations", Value: s.Observations},
})
}

// SpeakerTaskFlightLeg stores one leg (arrival or departure).
type SpeakerTaskFlightLeg struct {
Airport string `json:"airport" bson:"airport"`
FlightNumber string `json:"flightNumber" bson:"flightNumber"`
Date *time.Time `json:"date,omitempty" bson:"date,omitempty"`
Time string `json:"time" bson:"time"`
}

// SpeakerTaskFlightDetails stores pricing / status / booking info.
type SpeakerTaskFlightDetails struct {
Price string `json:"price" bson:"price"`
Status string `json:"status" bson:"status"` // "pending", "received", "approved", "bought"
Link string `json:"link" bson:"link"`
BookingRef string `json:"bookingRef" bson:"bookingRef"`
}

// SpeakerTaskFlightRefund stores refund info.
type SpeakerTaskFlightRefund struct {
Amount string `json:"amount" bson:"amount"`
Method string `json:"method" bson:"method"`
InfoNeeded string `json:"infoNeeded" bson:"infoNeeded"`
Status string `json:"status" bson:"status"` // "not_started", "receipt_requested", "info_requested", "done"
}

// SpeakerTaskFlights stores everything flight-related.
type SpeakerTaskFlights struct {
NeedsFlights string `json:"needsFlights" bson:"needsFlights"` // "not_responded", "yes", "no"
Requested bool `json:"requested" bson:"requested"`
Arrival SpeakerTaskFlightLeg `json:"arrival" bson:"arrival"`
Departure SpeakerTaskFlightLeg `json:"departure" bson:"departure"`
Details SpeakerTaskFlightDetails `json:"details" bson:"details"`
Refund SpeakerTaskFlightRefund `json:"refund" bson:"refund"`
}

// SpeakerTaskCoverage tracks video/photo coverage confirmation.
type SpeakerTaskCoverage struct {
Video string `json:"video" bson:"video"` // "not_responded", "yes", "no"
Streaming string `json:"streaming" bson:"streaming"` // "not_responded", "yes", "no"
Photo string `json:"photo" bson:"photo"` // "not_responded", "yes", "no"
}

// boolToString converts legacy boolean values to the new string format.
func boolToString(v interface{}) string {
switch b := v.(type) {
case bool:
if b {
return "yes"
}
return "no"
case string:
return b
default:
return "not_responded"
}
}

// UnmarshalBSON handles legacy boolean fields being decoded into string fields.
func (c *SpeakerTaskCoverage) UnmarshalBSON(data []byte) error {
var raw bson.Raw
if err := bson.Unmarshal(data, &raw); err != nil {
return err
}

decodeField := func(key string) string {
val, err := raw.LookupErr(key)
if err != nil {
return "not_responded"
}
switch val.Type {
case bsontype.Boolean:
b, _ := val.BooleanOK()
return boolToString(b)
case bsontype.String:
s, _ := val.StringValueOK()
return s
default:
return "not_responded"
}
}

c.Video = decodeField("video")
c.Streaming = decodeField("streaming")
c.Photo = decodeField("photo")
return nil
}

// MarshalBSON always writes string values.
func (c SpeakerTaskCoverage) MarshalBSON() ([]byte, error) {
return bson.Marshal(bson.D{
{Key: "video", Value: c.Video},
{Key: "streaming", Value: c.Streaming},
{Key: "photo", Value: c.Photo},
})
}

// SpeakerTaskMaterials tracks talk info and materials delivery.
type SpeakerTaskMaterials struct {
Requested bool `json:"requested" bson:"requested"`
TalkTitle string `json:"talkTitle" bson:"talkTitle"`
TalkDescription string `json:"talkDescription" bson:"talkDescription"`
Received bool `json:"received" bson:"received"`
TestSchedule string `json:"testSchedule" bson:"testSchedule"`
TestDone bool `json:"testDone" bson:"testDone"`
}

// SpeakerTaskHotel stores hotel / booking / payment info.
type SpeakerTaskHotel struct {
NeedsHotel string `json:"needsHotel" bson:"needsHotel"` // "not_responded", "yes", "no"
Requested bool `json:"requested" bson:"requested"`
HotelName string `json:"hotelName" bson:"hotelName"`
RoomType string `json:"roomType" bson:"roomType"`
Price string `json:"price" bson:"price"`
CheckIn *time.Time `json:"checkIn,omitempty" bson:"checkIn,omitempty"`
CheckOut *time.Time `json:"checkOut,omitempty" bson:"checkOut,omitempty"`
NumNights string `json:"numNights" bson:"numNights"`
NumGuests string `json:"numGuests" bson:"numGuests"`
GuestNames string `json:"guestNames" bson:"guestNames"`
Invoice bool `json:"invoice" bson:"invoice"`
Paid bool `json:"paid" bson:"paid"`
Notes string `json:"notes" bson:"notes"`
}

// SpeakerTasks is the top-level task object embedded in SpeakerParticipation.
type SpeakerTasks struct {
Confirmation SpeakerTaskConfirmation `json:"confirmation" bson:"confirmation"`
Logos TaskLogos `json:"logos" bson:"logos"`
AskedForInfo bool `json:"askedForInfo" bson:"askedForInfo"`
Flights SpeakerTaskFlights `json:"flights" bson:"flights"`
Coverage SpeakerTaskCoverage `json:"coverage" bson:"coverage"`
Materials SpeakerTaskMaterials `json:"materials" bson:"materials"`
Hotel SpeakerTaskHotel `json:"hotel" bson:"hotel"`
}
Loading
Loading