Skip to content

Commit 5ce5d6f

Browse files
authored
feat: company and speaker tasks
Draft: Company Tasks
2 parents bc849d5 + 9ac4054 commit 5ce5d6f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+4787
-71
lines changed

backend/src/models/company.go

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -43,31 +43,33 @@ type CompanyParticipation struct {
4343
// These are used to sync communications with Gmail.
4444
GmailThreadIds []string `json:"gmailThreadIds,omitempty" bson:"gmailThreadIds,omitempty"`
4545

46-
// Stand details
47-
StandDetails StandDetails `json:"standDetails,omitempty" bson:"standDetails,omitempty"`
46+
// Stand details
47+
StandDetails StandDetails `json:"standDetails,omitempty" bson:"standDetails,omitempty"`
4848

49-
// Stand and days at the venue
50-
Stands []Stand `json:"stands,omitempty" bson:"stands,omitempty"`
49+
// Stand and days at the venue
50+
Stands []Stand `json:"stands,omitempty" bson:"stands,omitempty"`
5151

52+
// Tasks tracks the company's task pipeline progress.
53+
Tasks CompanyTasks `json:"tasks" bson:"tasks"`
5254
}
5355

5456
type Stand struct {
55-
// Stand identifier
56-
StandID string `json:"standId" bson:"standId"`
57+
// Stand identifier
58+
StandID string `json:"standId" bson:"standId"`
5759

58-
// Day at the venue
59-
Date *time.Time `json:"date,omitempty" bson:"date,omitempty"`
60+
// Day at the venue
61+
Date *time.Time `json:"date,omitempty" bson:"date,omitempty"`
6062
}
6163

6264
type StandDetails struct {
63-
// Number of chairs required by the company
64-
Chairs int `json:"chairs" bson:"chairs"`
65+
// Number of chairs required by the company
66+
Chairs int `json:"chairs" bson:"chairs"`
6567

66-
// Require front table
67-
Table bool `json:"table" bson:"table"`
68+
// Require front table
69+
Table bool `json:"table" bson:"table"`
6870

69-
// Require lettering
70-
Lettering bool `json:"lettering" bson:"lettering"`
71+
// Require lettering
72+
Lettering bool `json:"lettering" bson:"lettering"`
7173
}
7274

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

135-
// Stand details
136-
StandDetails StandDetails `json:"standDetails,omitempty"`
137+
// Stand details
138+
StandDetails StandDetails `json:"standDetails,omitempty"`
137139

138-
// Days at the venue
139-
Stands []Stand `json:"stands,omitempty"`
140+
// Days at the venue
141+
Stands []Stand `json:"stands,omitempty"`
140142
}
141143

142144
// CompanyPublic represents a company to be contacted by the team, that will hopefully participate

backend/src/models/speaker.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ type SpeakerParticipation struct {
4444
// GmailThreadIds is an array of Gmail thread IDs linked to this participation.
4545
// These are used to sync communications with Gmail.
4646
GmailThreadIds []string `json:"gmailThreadIds,omitempty" bson:"gmailThreadIds,omitempty"`
47+
48+
// Tasks tracks the speaker's task pipeline progress.
49+
Tasks SpeakerTasks `json:"tasks" bson:"tasks"`
4750
}
4851

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

7073
// Contact is an _id of Contact (see models.Contact).
71-
Contact *primitive.ObjectID `json:"contact,omitempty" bson:"contact"`
72-
ContactObject *Contact `json:"contactObject,omitempty" bson:"contactObject"`
74+
Contact *primitive.ObjectID `json:"contact,omitempty" bson:"contact"`
75+
ContactObject *Contact `json:"contactObject,omitempty" bson:"contactObject"`
7376

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

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

80-
// Company name
81-
CompanyName string `json:"companyName" bson:"companyName"`
83+
// Company name
84+
CompanyName string `json:"companyName" bson:"companyName"`
8285

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

123-
// Company name
124-
CompanyName string `json:"companyName,omitempty" bson:"companyName"`
126+
// Company name
127+
CompanyName string `json:"companyName,omitempty" bson:"companyName"`
125128

126129
Images SpeakerImagesPublic `json:"imgs" bson:"imgs"`
127130
Participations []SpeakerParticipationPublic `json:"participation" bson:"participations"`

backend/src/models/task.go

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
package models
2+
3+
import (
4+
"time"
5+
6+
"go.mongodb.org/mongo-driver/bson"
7+
"go.mongodb.org/mongo-driver/bson/bsontype"
8+
)
9+
10+
// ============================================================
11+
// Shared task fields (used by both companies and speakers)
12+
// ============================================================
13+
14+
// TaskLogos tracks logo delivery status.
15+
type TaskLogos struct {
16+
Received bool `json:"received" bson:"received"`
17+
NeedsReviewing bool `json:"needsReviewing" bson:"needsReviewing"`
18+
}
19+
20+
// ============================================================
21+
// Company-specific task types
22+
// ============================================================
23+
24+
// CompanyTaskConfirmation tracks confirmation-related progress for a company.
25+
type CompanyTaskConfirmation struct {
26+
AskedForInfo bool `json:"askedForInfo" bson:"askedForInfo"`
27+
}
28+
29+
// CompanyTaskContract tracks contract lifecycle.
30+
type CompanyTaskContract struct {
31+
Sent bool `json:"sent" bson:"sent"`
32+
Created bool `json:"created" bson:"created"`
33+
Signed bool `json:"signed" bson:"signed"`
34+
ReceiptSent bool `json:"receiptSent" bson:"receiptSent"`
35+
Paid bool `json:"paid" bson:"paid"`
36+
}
37+
38+
// CompanyTaskSessionTitles tracks session / workshop titles.
39+
type CompanyTaskSessionTitles struct {
40+
PresentationTitle string `json:"presentationTitle" bson:"presentationTitle"`
41+
WorkshopTitle string `json:"workshopTitle" bson:"workshopTitle"`
42+
}
43+
44+
// CompanyTaskCorlief tracks Corlief-related steps.
45+
type CompanyTaskCorlief struct {
46+
PreNotice bool `json:"preNotice" bson:"preNotice"`
47+
Scheduled bool `json:"scheduled" bson:"scheduled"`
48+
Reserved bool `json:"reserved" bson:"reserved"`
49+
}
50+
51+
// CompanyTaskLogistics tracks logistics info.
52+
type CompanyTaskLogistics struct {
53+
RequestedInfo bool `json:"requestedInfo" bson:"requestedInfo"`
54+
CarStatus string `json:"carStatus" bson:"carStatus"` // "not_responded", "wants", "not_wants"
55+
LicensePlate string `json:"licensePlate" bson:"licensePlate"`
56+
}
57+
58+
// CompanyTasks is the top-level task object embedded in CompanyParticipation.
59+
type CompanyTasks struct {
60+
Confirmation CompanyTaskConfirmation `json:"confirmation" bson:"confirmation"`
61+
Logos TaskLogos `json:"logos" bson:"logos"`
62+
Contract CompanyTaskContract `json:"contract" bson:"contract"`
63+
SessionTitles CompanyTaskSessionTitles `json:"sessionTitles" bson:"sessionTitles"`
64+
Corlief CompanyTaskCorlief `json:"corlief" bson:"corlief"`
65+
Logistics CompanyTaskLogistics `json:"logistics" bson:"logistics"`
66+
PO string `json:"po" bson:"po"`
67+
}
68+
69+
// ============================================================
70+
// Speaker-specific task types
71+
// ============================================================
72+
73+
// SpeakerTaskConfirmation tracks confirmation-related progress for a speaker.
74+
type SpeakerTaskConfirmation struct {
75+
Phone string `json:"phone" bson:"phone"`
76+
LinkedIn string `json:"linkedin" bson:"linkedin"`
77+
WantsLinkedIn string `json:"wantsLinkedinTag" bson:"wantsLinkedinTag"` // "not_responded", "yes", "no"
78+
Observations string `json:"observations" bson:"observations"`
79+
}
80+
81+
// UnmarshalBSON handles legacy bool values stored for WantsLinkedIn.
82+
func (s *SpeakerTaskConfirmation) UnmarshalBSON(data []byte) error {
83+
var raw bson.Raw
84+
if err := bson.Unmarshal(data, &raw); err != nil {
85+
return err
86+
}
87+
88+
decodeString := func(key string) string {
89+
val, err := raw.LookupErr(key)
90+
if err != nil {
91+
return ""
92+
}
93+
if val.Type == bsontype.String {
94+
v, _ := val.StringValueOK()
95+
return v
96+
}
97+
return ""
98+
}
99+
100+
decodeLinkedIn := func(key string) string {
101+
val, err := raw.LookupErr(key)
102+
if err != nil {
103+
return "not_responded"
104+
}
105+
switch val.Type {
106+
case bsontype.Boolean:
107+
b, _ := val.BooleanOK()
108+
return boolToString(b)
109+
case bsontype.String:
110+
v, _ := val.StringValueOK()
111+
return v
112+
default:
113+
return "not_responded"
114+
}
115+
}
116+
117+
s.Phone = decodeString("phone")
118+
s.LinkedIn = decodeString("linkedin")
119+
s.WantsLinkedIn = decodeLinkedIn("wantsLinkedinTag")
120+
s.Observations = decodeString("observations")
121+
return nil
122+
}
123+
124+
// MarshalBSON always writes string values.
125+
func (s SpeakerTaskConfirmation) MarshalBSON() ([]byte, error) {
126+
return bson.Marshal(bson.D{
127+
{Key: "phone", Value: s.Phone},
128+
{Key: "linkedin", Value: s.LinkedIn},
129+
{Key: "wantsLinkedinTag", Value: s.WantsLinkedIn},
130+
{Key: "observations", Value: s.Observations},
131+
})
132+
}
133+
134+
// SpeakerTaskFlightLeg stores one leg (arrival or departure).
135+
type SpeakerTaskFlightLeg struct {
136+
Airport string `json:"airport" bson:"airport"`
137+
FlightNumber string `json:"flightNumber" bson:"flightNumber"`
138+
Date *time.Time `json:"date,omitempty" bson:"date,omitempty"`
139+
Time string `json:"time" bson:"time"`
140+
}
141+
142+
// SpeakerTaskFlightDetails stores pricing / status / booking info.
143+
type SpeakerTaskFlightDetails struct {
144+
Price string `json:"price" bson:"price"`
145+
Status string `json:"status" bson:"status"` // "pending", "received", "approved", "bought"
146+
Link string `json:"link" bson:"link"`
147+
BookingRef string `json:"bookingRef" bson:"bookingRef"`
148+
}
149+
150+
// SpeakerTaskFlightRefund stores refund info.
151+
type SpeakerTaskFlightRefund struct {
152+
Amount string `json:"amount" bson:"amount"`
153+
Method string `json:"method" bson:"method"`
154+
InfoNeeded string `json:"infoNeeded" bson:"infoNeeded"`
155+
Status string `json:"status" bson:"status"` // "not_started", "receipt_requested", "info_requested", "done"
156+
}
157+
158+
// SpeakerTaskFlights stores everything flight-related.
159+
type SpeakerTaskFlights struct {
160+
NeedsFlights string `json:"needsFlights" bson:"needsFlights"` // "not_responded", "yes", "no"
161+
Requested bool `json:"requested" bson:"requested"`
162+
Arrival SpeakerTaskFlightLeg `json:"arrival" bson:"arrival"`
163+
Departure SpeakerTaskFlightLeg `json:"departure" bson:"departure"`
164+
Details SpeakerTaskFlightDetails `json:"details" bson:"details"`
165+
Refund SpeakerTaskFlightRefund `json:"refund" bson:"refund"`
166+
}
167+
168+
// SpeakerTaskCoverage tracks video/photo coverage confirmation.
169+
type SpeakerTaskCoverage struct {
170+
Video string `json:"video" bson:"video"` // "not_responded", "yes", "no"
171+
Streaming string `json:"streaming" bson:"streaming"` // "not_responded", "yes", "no"
172+
Photo string `json:"photo" bson:"photo"` // "not_responded", "yes", "no"
173+
}
174+
175+
// boolToString converts legacy boolean values to the new string format.
176+
func boolToString(v interface{}) string {
177+
switch b := v.(type) {
178+
case bool:
179+
if b {
180+
return "yes"
181+
}
182+
return "no"
183+
case string:
184+
return b
185+
default:
186+
return "not_responded"
187+
}
188+
}
189+
190+
// UnmarshalBSON handles legacy boolean fields being decoded into string fields.
191+
func (c *SpeakerTaskCoverage) UnmarshalBSON(data []byte) error {
192+
var raw bson.Raw
193+
if err := bson.Unmarshal(data, &raw); err != nil {
194+
return err
195+
}
196+
197+
decodeField := func(key string) string {
198+
val, err := raw.LookupErr(key)
199+
if err != nil {
200+
return "not_responded"
201+
}
202+
switch val.Type {
203+
case bsontype.Boolean:
204+
b, _ := val.BooleanOK()
205+
return boolToString(b)
206+
case bsontype.String:
207+
s, _ := val.StringValueOK()
208+
return s
209+
default:
210+
return "not_responded"
211+
}
212+
}
213+
214+
c.Video = decodeField("video")
215+
c.Streaming = decodeField("streaming")
216+
c.Photo = decodeField("photo")
217+
return nil
218+
}
219+
220+
// MarshalBSON always writes string values.
221+
func (c SpeakerTaskCoverage) MarshalBSON() ([]byte, error) {
222+
return bson.Marshal(bson.D{
223+
{Key: "video", Value: c.Video},
224+
{Key: "streaming", Value: c.Streaming},
225+
{Key: "photo", Value: c.Photo},
226+
})
227+
}
228+
229+
// SpeakerTaskMaterials tracks talk info and materials delivery.
230+
type SpeakerTaskMaterials struct {
231+
Requested bool `json:"requested" bson:"requested"`
232+
TalkTitle string `json:"talkTitle" bson:"talkTitle"`
233+
TalkDescription string `json:"talkDescription" bson:"talkDescription"`
234+
Received bool `json:"received" bson:"received"`
235+
TestSchedule string `json:"testSchedule" bson:"testSchedule"`
236+
TestDone bool `json:"testDone" bson:"testDone"`
237+
}
238+
239+
// SpeakerTaskHotel stores hotel / booking / payment info.
240+
type SpeakerTaskHotel struct {
241+
NeedsHotel string `json:"needsHotel" bson:"needsHotel"` // "not_responded", "yes", "no"
242+
Requested bool `json:"requested" bson:"requested"`
243+
HotelName string `json:"hotelName" bson:"hotelName"`
244+
RoomType string `json:"roomType" bson:"roomType"`
245+
Price string `json:"price" bson:"price"`
246+
CheckIn *time.Time `json:"checkIn,omitempty" bson:"checkIn,omitempty"`
247+
CheckOut *time.Time `json:"checkOut,omitempty" bson:"checkOut,omitempty"`
248+
NumNights string `json:"numNights" bson:"numNights"`
249+
NumGuests string `json:"numGuests" bson:"numGuests"`
250+
GuestNames string `json:"guestNames" bson:"guestNames"`
251+
Invoice bool `json:"invoice" bson:"invoice"`
252+
Paid bool `json:"paid" bson:"paid"`
253+
Notes string `json:"notes" bson:"notes"`
254+
}
255+
256+
// SpeakerTasks is the top-level task object embedded in SpeakerParticipation.
257+
type SpeakerTasks struct {
258+
Confirmation SpeakerTaskConfirmation `json:"confirmation" bson:"confirmation"`
259+
Logos TaskLogos `json:"logos" bson:"logos"`
260+
AskedForInfo bool `json:"askedForInfo" bson:"askedForInfo"`
261+
Flights SpeakerTaskFlights `json:"flights" bson:"flights"`
262+
Coverage SpeakerTaskCoverage `json:"coverage" bson:"coverage"`
263+
Materials SpeakerTaskMaterials `json:"materials" bson:"materials"`
264+
Hotel SpeakerTaskHotel `json:"hotel" bson:"hotel"`
265+
}

0 commit comments

Comments
 (0)