@@ -119,6 +119,12 @@ interface SponsorLeadDoc {
119119 intent ?: string ;
120120 threadId ?: string ;
121121 notes ?: string ;
122+ preCallNotes ?: string ;
123+ runOfShowNotes ?: string ;
124+ guestQuestions ?: string ;
125+ runOfShowUpdatedAt ?: string ;
126+ runOfShowUpdatedBy ?: string ;
127+ legacyPortalToken ?: string ;
122128 deliverables ?: DeliverableItem [ ] ;
123129 sharedAssets ?: SharedAssetItem [ ] ;
124130}
@@ -141,6 +147,11 @@ interface BookingDoc {
141147 preMeetingGoogleEventId ?: string ;
142148 preMeetingMeetUrl ?: string ;
143149 topics ?: string ;
150+ preCallNotes ?: string ;
151+ runOfShowNotes ?: string ;
152+ guestQuestions ?: string ;
153+ runOfShowUpdatedAt ?: string ;
154+ runOfShowUpdatedBy ?: string ;
144155 appointmentScheduleUrl ?: string ;
145156 googleCalendarEventId ?: string ;
146157 passionfrootCollaborationId ?: string ;
@@ -150,6 +161,7 @@ interface BookingDoc {
150161 signatureData ?: string ;
151162 signedAt ?: string ;
152163 portalToken ?: string ;
164+ legacyPortalToken ?: string ;
153165 sharedAssets ?: SharedAssetItem [ ] ;
154166}
155167
@@ -338,6 +350,16 @@ export function App() {
338350 const [ vaultUrlInput , setVaultUrlInput ] = useState < Record < string , string > > (
339351 { } ,
340352 ) ;
353+ const [ rosDrafts , setRosDrafts ] = useState <
354+ Record <
355+ string ,
356+ {
357+ preCallNotes : string ;
358+ runOfShowNotes : string ;
359+ guestQuestions : string ;
360+ }
361+ >
362+ > ( { } ) ;
341363 const [ busyActionId , setBusyActionId ] = useState < string | null > ( null ) ;
342364
343365 const showFlash = useCallback ( ( msg : string ) => {
@@ -527,6 +549,39 @@ export function App() {
527549 }
528550 } ;
529551
552+ const handleSaveRunOfShow = async (
553+ docId : string ,
554+ values : {
555+ preCallNotes : string ;
556+ runOfShowNotes : string ;
557+ guestQuestions : string ;
558+ } ,
559+ ) => {
560+ setBusyActionId ( `${ docId } :ros` ) ;
561+ try {
562+ const nowIso = new Date ( ) . toISOString ( ) ;
563+ const patchData = {
564+ preCallNotes : values . preCallNotes ,
565+ runOfShowNotes : values . runOfShowNotes ,
566+ guestQuestions : values . guestQuestions ,
567+ runOfShowUpdatedAt : nowIso ,
568+ runOfShowUpdatedBy : "Alex Patterson (Ops App)" ,
569+ } ;
570+ await patchDocWithFallback ( docId , patchData ) ;
571+ setBookings ( ( prev ) =>
572+ prev . map ( ( b ) => ( b . _id === docId ? { ...b , ...patchData } : b ) ) ,
573+ ) ;
574+ setLeads ( ( prev ) =>
575+ prev . map ( ( l ) => ( l . _id === docId ? { ...l , ...patchData } : l ) ) ,
576+ ) ;
577+ showFlash ( "✓ Saved Run-of-Show & Pre-Call Notes to Sanity (live on Guest Portal)" ) ;
578+ } catch ( err : any ) {
579+ showFlash ( `Error saving Run-of-Show: ${ err ?. message || "Unknown error" } ` ) ;
580+ } finally {
581+ setBusyActionId ( null ) ;
582+ }
583+ } ;
584+
530585 const handleCreateLead = async ( e : React . FormEvent ) => {
531586 e . preventDefault ( ) ;
532587 if ( ! newCompany . trim ( ) || ! newContactEmail . trim ( ) ) return ;
@@ -3349,10 +3404,10 @@ export function App() {
33493404 }
33503405 style = { primaryBtnStyle }
33513406 >
3352- { isExpanded
3353- ? "Hide Details"
3354- : `Topics , E-SIGN & Vault (${ assetCount } )` }
3355- </ button >
3407+ { isExpanded
3408+ ? "Hide Details"
3409+ : `Run-of-Show , E-SIGN & Vault (${ assetCount } )` }
3410+ </ button >
33563411 < a
33573412 href = { vaultUrl }
33583413 target = "_blank"
@@ -3691,6 +3746,283 @@ export function App() {
36913746 </ div >
36923747 </ div >
36933748 </ div >
3749+
3750+ { /* Shared 2-Way Run-of-Show & Pre-Call Notes Co-Editor */ }
3751+ { ( ( ) => {
3752+ const currentRos = rosDrafts [ b . _id ] || {
3753+ preCallNotes : b . preCallNotes || "" ,
3754+ runOfShowNotes : b . runOfShowNotes || "" ,
3755+ guestQuestions : b . guestQuestions || "" ,
3756+ } ;
3757+ return (
3758+ < div
3759+ style = { {
3760+ marginTop : 16 ,
3761+ padding : 16 ,
3762+ borderRadius : 10 ,
3763+ background : "#fff" ,
3764+ border : "2px solid #c4b5fd" ,
3765+ } }
3766+ >
3767+ < div
3768+ style = { {
3769+ display : "flex" ,
3770+ justifyContent : "space-between" ,
3771+ alignItems : "center" ,
3772+ flexWrap : "wrap" ,
3773+ gap : 8 ,
3774+ marginBottom : 12 ,
3775+ } }
3776+ >
3777+ < div >
3778+ < h4
3779+ style = { {
3780+ margin : 0 ,
3781+ fontSize : 14 ,
3782+ color : "#4B2E83" ,
3783+ } }
3784+ >
3785+ 📝 2-Way Run-of-Show & Pre-Call
3786+ Coordination Notes (Syncs Live with{ " " }
3787+ < code > { portalUrl } #run-of-show</ code > )
3788+ </ h4 >
3789+ < div
3790+ style = { {
3791+ fontSize : 11 ,
3792+ color : "#6b7280" ,
3793+ marginTop : 2 ,
3794+ } }
3795+ >
3796+ Both you and{ " " }
3797+ < strong > { b . guestName } </ strong > can
3798+ edit these notes before and during the
3799+ session.
3800+ { b . runOfShowUpdatedAt
3801+ ? ` Last saved ${ formatDateTime ( b . runOfShowUpdatedAt ) } by ${ b . runOfShowUpdatedBy || "Host" } .`
3802+ : "" }
3803+ </ div >
3804+ </ div >
3805+ < div
3806+ style = { {
3807+ display : "flex" ,
3808+ gap : 6 ,
3809+ flexWrap : "wrap" ,
3810+ } }
3811+ >
3812+ < button
3813+ type = "button"
3814+ onClick = { ( ) =>
3815+ setRosDrafts ( ( prev ) => ( {
3816+ ...prev ,
3817+ [ b . _id ] : {
3818+ preCallNotes :
3819+ currentRos . preCallNotes ||
3820+ [
3821+ `• Target Audience Takeaway: Practical mental model + live demo of ${ b . company || b . guestName } ` ,
3822+ `• Screen-Share Check: 1080p editor/browser zoomed to 125%-150%` ,
3823+ `• Audio/Video Check: Headphones on (no echo in StreamYard)` ,
3824+ ] . join ( "\n" ) ,
3825+ guestQuestions :
3826+ currentRos . guestQuestions ||
3827+ [
3828+ `• Must-Ask #1: What developer pain point sparked ${ b . company || "this project" } ?` ,
3829+ `• Must-Ask #2: Walk us through the architecture & live code` ,
3830+ `• Lower-Third Links: ${ b . socialUrl || "https://codingcat.dev" } ` ,
3831+ ] . join ( "\n" ) ,
3832+ runOfShowNotes : [
3833+ `00:00 - 03:00 | 1. Cold Open & Origin Story` ,
3834+ ` • Welcome ${ b . guestName } (${ b . company || "Guest" } ) to CodingCat.dev` ,
3835+ ` • The 30-second elevator pitch: What problem were you tired of hitting in production?` ,
3836+ `` ,
3837+ `03:00 - 12:00 | 2. Architecture Mental Model` ,
3838+ ` • How developers used to wire this up vs. how ${ b . company || b . guestName } approaches it` ,
3839+ ` • Under-the-hood primitives, state flow, and developer ergonomics` ,
3840+ `` ,
3841+ `12:00 - 28:00 | 3. Show Me the Code (Live Screen-Share / Demo)` ,
3842+ ` • Step-by-step walkthrough in real code (zero slideware)` ,
3843+ ` • Key workflows, CLI/UI ergonomics, and live inspection` ,
3844+ `` ,
3845+ `28:00 - 38:00 | 4. Production Trade-Offs & Sharp Edges` ,
3846+ ` • Where this shines vs. when teams should use something else` ,
3847+ ` • Security, performance, and scaling gotchas` ,
3848+ `` ,
3849+ `38:00 - 45:00 | 5. Verdict, Roadmap & Where to Try It` ,
3850+ ` • What is shipping next on the roadmap` ,
3851+ ` • Links to docs, GitHub repo, and starter templates` ,
3852+ ] . join ( "\n" ) ,
3853+ } ,
3854+ } ) )
3855+ }
3856+ style = { btnStyle }
3857+ >
3858+ Load 5-Part Episode Template
3859+ </ button >
3860+ < button
3861+ type = "button"
3862+ onClick = { ( ) =>
3863+ handleCopy (
3864+ `${ portalUrl } #run-of-show` ,
3865+ `ros-link-${ b . _id } ` ,
3866+ )
3867+ }
3868+ style = { btnStyle }
3869+ >
3870+ { copiedId === `ros-link-${ b . _id } `
3871+ ? "✓ Copied #run-of-show URL"
3872+ : "Copy #run-of-show URL" }
3873+ </ button >
3874+ < button
3875+ type = "button"
3876+ disabled = {
3877+ busyActionId === `${ b . _id } :ros`
3878+ }
3879+ onClick = { ( ) =>
3880+ handleSaveRunOfShow (
3881+ b . _id ,
3882+ currentRos ,
3883+ )
3884+ }
3885+ style = { primaryBtnStyle }
3886+ >
3887+ { busyActionId === `${ b . _id } :ros`
3888+ ? "Saving..."
3889+ : "💾 Save Run-of-Show to Sanity" }
3890+ </ button >
3891+ </ div >
3892+ </ div >
3893+
3894+ < div
3895+ style = { {
3896+ display : "grid" ,
3897+ gridTemplateColumns :
3898+ "repeat(auto-fit, minmax(280px, 1fr))" ,
3899+ gap : 12 ,
3900+ } }
3901+ >
3902+ < div
3903+ style = { {
3904+ display : "flex" ,
3905+ flexDirection : "column" ,
3906+ gap : 8 ,
3907+ } }
3908+ >
3909+ < div >
3910+ < label
3911+ style = { {
3912+ display : "block" ,
3913+ fontSize : 11 ,
3914+ fontWeight : 700 ,
3915+ color : "#374151" ,
3916+ marginBottom : 4 ,
3917+ } }
3918+ >
3919+ 1. 15m Pre-Call Alignment & Tech
3920+ Check Notes:
3921+ </ label >
3922+ < textarea
3923+ rows = { 5 }
3924+ value = { currentRos . preCallNotes }
3925+ onChange = { ( e ) =>
3926+ setRosDrafts ( ( prev ) => ( {
3927+ ...prev ,
3928+ [ b . _id ] : {
3929+ ...currentRos ,
3930+ preCallNotes : e . target . value ,
3931+ } ,
3932+ } ) )
3933+ }
3934+ placeholder = "Pre-call takeaways, screen-share setup, bio/pronunciation notes..."
3935+ style = { {
3936+ width : "100%" ,
3937+ padding : 8 ,
3938+ borderRadius : 6 ,
3939+ border : "1px solid #d1d5db" ,
3940+ fontFamily : "monospace" ,
3941+ fontSize : 12 ,
3942+ lineHeight : 1.4 ,
3943+ } }
3944+ />
3945+ </ div >
3946+ < div >
3947+ < label
3948+ style = { {
3949+ display : "block" ,
3950+ fontSize : 11 ,
3951+ fontWeight : 700 ,
3952+ color : "#374151" ,
3953+ marginBottom : 4 ,
3954+ } }
3955+ >
3956+ 2. Guest Prompts, Links &
3957+ Lower-Third Callouts:
3958+ </ label >
3959+ < textarea
3960+ rows = { 4 }
3961+ value = { currentRos . guestQuestions }
3962+ onChange = { ( e ) =>
3963+ setRosDrafts ( ( prev ) => ( {
3964+ ...prev ,
3965+ [ b . _id ] : {
3966+ ...currentRos ,
3967+ guestQuestions :
3968+ e . target . value ,
3969+ } ,
3970+ } ) )
3971+ }
3972+ placeholder = "Must-ask questions, GitHub repo URL, docs links..."
3973+ style = { {
3974+ width : "100%" ,
3975+ padding : 8 ,
3976+ borderRadius : 6 ,
3977+ border : "1px solid #d1d5db" ,
3978+ fontFamily : "monospace" ,
3979+ fontSize : 12 ,
3980+ lineHeight : 1.4 ,
3981+ } }
3982+ />
3983+ </ div >
3984+ </ div >
3985+ < div >
3986+ < label
3987+ style = { {
3988+ display : "block" ,
3989+ fontSize : 11 ,
3990+ fontWeight : 700 ,
3991+ color : "#374151" ,
3992+ marginBottom : 4 ,
3993+ } }
3994+ >
3995+ 3. 45-Minute Episode Run-of-Show
3996+ Outline (5-Part Arc):
3997+ </ label >
3998+ < textarea
3999+ rows = { 11 }
4000+ value = { currentRos . runOfShowNotes }
4001+ onChange = { ( e ) =>
4002+ setRosDrafts ( ( prev ) => ( {
4003+ ...prev ,
4004+ [ b . _id ] : {
4005+ ...currentRos ,
4006+ runOfShowNotes : e . target . value ,
4007+ } ,
4008+ } ) )
4009+ }
4010+ placeholder = "00:00 - 03:00 | 1. Cold Open & Origin Story..."
4011+ style = { {
4012+ width : "100%" ,
4013+ padding : 8 ,
4014+ borderRadius : 6 ,
4015+ border : "1px solid #d1d5db" ,
4016+ fontFamily : "monospace" ,
4017+ fontSize : 12 ,
4018+ lineHeight : 1.4 ,
4019+ } }
4020+ />
4021+ </ div >
4022+ </ div >
4023+ </ div >
4024+ ) ;
4025+ } ) ( ) }
36944026 </ td >
36954027 </ tr >
36964028 ) }
0 commit comments