@@ -350,68 +350,77 @@ export function App() {
350350 const fetchAllData = useCallback ( async ( ) => {
351351 setLoading ( true ) ;
352352 try {
353- const res = await client . fetch < {
354- leads : SponsorLeadDoc [ ] ;
355- bookings : BookingDoc [ ] ;
356- syndicatedContent : SyndicatedContentItem [ ] ;
357- syndicationDocCount : number ;
358- draftItems : DraftContentItem [ ] ;
359- trendSignals : TrendSignalDoc [ ] ;
360- } > (
361- `{
362- "leads": *[_type == "sponsorLead" && !(_id in path("drafts.**"))] | order(coalesce(lastEmailAt, _updatedAt, _createdAt) desc),
363- "bookings": *[_type == "booking" && !(_id in path("drafts.**"))] | order(coalesce(slotStartAt, _createdAt) desc),
364- "syndicatedContent": *[_type in ["post", "podcast"] && !(_id in path("drafts.**"))] | order(coalesce(date, _createdAt) desc)[0...50] {
365- _id,
366- _type,
367- title,
368- "slug": slug.current,
369- "date": coalesce(date, _createdAt),
370- youtube,
371- dailydev,
372- devto,
373- hashnode,
374- awsbuilder,
375- medium,
376- reddit
377- },
378- "syndicationDocCount": count(*[_type == "syndication"]),
379- "draftItems": *[_type in ["post", "podcast", "course", "lesson"] && _id in path("drafts.**")] | order(_updatedAt desc) {
380- _id,
381- _type,
382- title,
383- "slug": slug.current,
384- _updatedAt,
385- _createdAt,
386- excerpt,
387- "contentLen": count(content),
388- "hasCover": defined(coverImage.asset)
389- },
390- "trendSignals": *[_type == "trendSignal"] | order(coalesce(discoveredAt, _createdAt) desc)[0...40] {
391- _id,
392- title,
393- "slug": slug.current,
394- source,
395- sourceUrl,
396- score,
397- status,
398- summary,
399- rawMetrics,
400- discoveredAt,
401- socialDrafts
402- }
403- }` ,
404- { } ,
405- { perspective : "raw" } ,
406- ) ;
353+ let loadedFromSanityClient = false ;
354+ try {
355+ const res = await client . fetch < {
356+ leads : SponsorLeadDoc [ ] ;
357+ bookings : BookingDoc [ ] ;
358+ syndicatedContent : SyndicatedContentItem [ ] ;
359+ syndicationDocCount : number ;
360+ draftItems : DraftContentItem [ ] ;
361+ trendSignals : TrendSignalDoc [ ] ;
362+ } > (
363+ `{
364+ "leads": *[_type == "sponsorLead" && !(_id in path("drafts.**"))] | order(coalesce(lastEmailAt, _updatedAt, _createdAt) desc),
365+ "bookings": *[_type == "booking" && !(_id in path("drafts.**"))] | order(coalesce(slotStartAt, _createdAt) desc),
366+ "syndicatedContent": *[_type in ["post", "podcast"] && !(_id in path("drafts.**"))] | order(coalesce(date, _createdAt) desc)[0...50] {
367+ _id,
368+ _type,
369+ title,
370+ "slug": slug.current,
371+ "date": coalesce(date, _createdAt),
372+ youtube,
373+ dailydev,
374+ devto,
375+ hashnode,
376+ awsbuilder,
377+ medium,
378+ reddit
379+ },
380+ "syndicationDocCount": count(*[_type == "syndication"]),
381+ "draftItems": *[_type in ["post", "podcast", "course", "lesson"] && _id in path("drafts.**")] | order(_updatedAt desc) {
382+ _id,
383+ _type,
384+ title,
385+ "slug": slug.current,
386+ _updatedAt,
387+ _createdAt,
388+ excerpt,
389+ "contentLen": count(content),
390+ "hasCover": defined(coverImage.asset)
391+ },
392+ "trendSignals": *[_type == "trendSignal"] | order(coalesce(discoveredAt, _createdAt) desc)[0...40] {
393+ _id,
394+ title,
395+ "slug": slug.current,
396+ source,
397+ sourceUrl,
398+ score,
399+ status,
400+ summary,
401+ rawMetrics,
402+ discoveredAt,
403+ socialDrafts
404+ }
405+ }` ,
406+ { } ,
407+ { perspective : "raw" } ,
408+ ) ;
407409
408- if ( res ) {
409- setLeads ( res . leads || [ ] ) ;
410- setBookings ( res . bookings || [ ] ) ;
411- setSyndicatedContent ( res . syndicatedContent || [ ] ) ;
412- setSyndicationDocCount ( res . syndicationDocCount || 0 ) ;
413- setDraftItems ( res . draftItems || [ ] ) ;
414- setTrendSignals ( res . trendSignals || [ ] ) ;
410+ if ( res && ( res . leads ?. length || res . bookings ?. length ) ) {
411+ loadedFromSanityClient = true ;
412+ setLeads ( res . leads || [ ] ) ;
413+ setBookings ( res . bookings || [ ] ) ;
414+ setSyndicatedContent ( res . syndicatedContent || [ ] ) ;
415+ setSyndicationDocCount ( res . syndicationDocCount || 0 ) ;
416+ setDraftItems ( res . draftItems || [ ] ) ;
417+ setTrendSignals ( res . trendSignals || [ ] ) ;
418+ }
419+ } catch ( err ) {
420+ console . warn (
421+ "Direct Sanity client fetch blocked by CORS, falling back to codingcat.dev API:" ,
422+ err ,
423+ ) ;
415424 }
416425
417426 try {
@@ -421,6 +430,44 @@ export function App() {
421430 if ( Array . isArray ( calData . calendarEvents ) ) {
422431 setMasterCalendarEvents ( calData . calendarEvents ) ;
423432 }
433+ if ( ! loadedFromSanityClient ) {
434+ if ( Array . isArray ( calData . allLeads ) ) {
435+ setLeads ( calData . allLeads ) ;
436+ } else if ( Array . isArray ( calData . actionLeads ) ) {
437+ setLeads ( calData . actionLeads ) ;
438+ }
439+ if ( Array . isArray ( calData . allBookings ) ) {
440+ setBookings ( calData . allBookings ) ;
441+ } else if (
442+ Array . isArray ( calData . pendingHolds ) ||
443+ Array . isArray ( calData . upcoming72hBookings )
444+ ) {
445+ const seen = new Set < string > ( ) ;
446+ const mergedBookings : BookingDoc [ ] = [ ] ;
447+ for ( const b of [
448+ ...( calData . pendingHolds || [ ] ) ,
449+ ...( calData . upcoming72hBookings || [ ] ) ,
450+ ] ) {
451+ if ( b && b . _id && ! seen . has ( b . _id ) ) {
452+ seen . add ( b . _id ) ;
453+ mergedBookings . push ( b ) ;
454+ }
455+ }
456+ setBookings ( mergedBookings ) ;
457+ }
458+ if ( Array . isArray ( calData . syndicatedContent ) ) {
459+ setSyndicatedContent ( calData . syndicatedContent ) ;
460+ }
461+ if ( typeof calData . syndicationDocCount === "number" ) {
462+ setSyndicationDocCount ( calData . syndicationDocCount ) ;
463+ }
464+ if ( Array . isArray ( calData . draftItems ) ) {
465+ setDraftItems ( calData . draftItems ) ;
466+ }
467+ if ( Array . isArray ( calData . trendSignals ) ) {
468+ setTrendSignals ( calData . trendSignals ) ;
469+ }
470+ }
424471 }
425472 } catch {
426473 // Ignore calendar digest fetch errors
@@ -442,6 +489,26 @@ export function App() {
442489 setTimeout ( ( ) => setCopiedId ( null ) , 2000 ) ;
443490 } ;
444491
492+ const patchDocWithFallback = async (
493+ docId : string ,
494+ patchData : Record < string , any > ,
495+ ) => {
496+ try {
497+ await client . patch ( docId ) . set ( patchData ) . commit ( ) ;
498+ } catch {
499+ const res = await fetch ( `${ siteOrigin } /api/ops/daily-digest` , {
500+ method : "POST" ,
501+ headers : { "Content-Type" : "application/json" } ,
502+ body : JSON . stringify ( {
503+ action : "patch_doc" ,
504+ docId,
505+ patch : patchData ,
506+ } ) ,
507+ } ) ;
508+ if ( ! res . ok ) throw new Error ( "Failed to patch document" ) ;
509+ }
510+ } ;
511+
445512 const handleUpdateLeadStatus = async ( leadId : string , newStatus : string ) => {
446513 try {
447514 const patchData : Record < string , any > = { status : newStatus } ;
@@ -450,7 +517,7 @@ export function App() {
450517 } else if ( newStatus === "paid" ) {
451518 patchData . balancePaidAt = new Date ( ) . toISOString ( ) ;
452519 }
453- await client . patch ( leadId ) . set ( patchData ) . commit ( ) ;
520+ await patchDocWithFallback ( leadId , patchData ) ;
454521 setLeads ( ( prev ) =>
455522 prev . map ( ( l ) => ( l . _id === leadId ? { ...l , ...patchData } : l ) ) ,
456523 ) ;
@@ -528,7 +595,7 @@ export function App() {
528595 newStatus : string ,
529596 ) => {
530597 try {
531- await client . patch ( bookingId ) . set ( { status : newStatus } ) . commit ( ) ;
598+ await patchDocWithFallback ( bookingId , { status : newStatus } ) ;
532599 setBookings ( ( prev ) =>
533600 prev . map ( ( b ) =>
534601 b . _id === bookingId ? { ...b , status : newStatus } : b ,
0 commit comments