|
1 | 1 | import { useDocuments } from "@sanity/sdk-react"; |
2 | | -import { useState } from "react"; |
| 2 | +import { useEffect, useState } from "react"; |
3 | 3 |
|
4 | | -type WorkspaceTab = "crm" | "bookings" | "billing"; |
| 4 | +type WorkspaceTab = "crm" | "bookings" | "billing" | "syndication"; |
5 | 5 |
|
6 | 6 | interface SeededGmailDraft { |
7 | 7 | companyName: string; |
@@ -111,6 +111,33 @@ export function App() { |
111 | 111 | const sponsorLeads = useDocuments({ documentType: "sponsorLead" }); |
112 | 112 | const sponsorPool = useDocuments({ documentType: "sponsorPool" }); |
113 | 113 | const bookings = useDocuments({ documentType: "booking" }); |
| 114 | + const syndications = useDocuments({ documentType: "syndication" }); |
| 115 | + const [syndicatedContent, setSyndicatedContent] = useState<Array<any>>([]); |
| 116 | + |
| 117 | + useEffect(() => { |
| 118 | + const query = encodeURIComponent( |
| 119 | + `*[_type in ["post", "podcast"] && !(_id in path("drafts.**"))] | order(coalesce(date, _createdAt) desc)[0...40] { |
| 120 | + _id, |
| 121 | + _type, |
| 122 | + title, |
| 123 | + "slug": slug.current, |
| 124 | + "date": coalesce(date, _createdAt), |
| 125 | + youtube, |
| 126 | + dailydev, |
| 127 | + devto, |
| 128 | + hashnode, |
| 129 | + awsbuilder, |
| 130 | + medium, |
| 131 | + reddit |
| 132 | + }`, |
| 133 | + ); |
| 134 | + fetch(`https://${projectId}.apicdn.sanity.io/v2025-09-30/data/query/${dataset}?query=${query}`) |
| 135 | + .then((r) => r.json()) |
| 136 | + .then((d) => { |
| 137 | + if (Array.isArray(d?.result)) setSyndicatedContent(d.result); |
| 138 | + }) |
| 139 | + .catch(() => {}); |
| 140 | + }, [projectId, dataset]); |
114 | 141 |
|
115 | 142 | const handleDiscoverOutbound = async () => { |
116 | 143 | setDiscovering(true); |
@@ -185,6 +212,21 @@ export function App() { |
185 | 212 | > |
186 | 213 | 3. Stripe 50/50 Billing & Deal Rooms |
187 | 214 | </button> |
| 215 | + <button |
| 216 | + type="button" |
| 217 | + onClick={() => setActiveTab("syndication")} |
| 218 | + style={{ |
| 219 | + padding: "8px 16px", |
| 220 | + borderRadius: 8, |
| 221 | + border: "1px solid #ccc", |
| 222 | + background: activeTab === "syndication" ? "#111" : "#fff", |
| 223 | + color: activeTab === "syndication" ? "#fff" : "#111", |
| 224 | + fontWeight: 600, |
| 225 | + cursor: "pointer", |
| 226 | + }} |
| 227 | + > |
| 228 | + 4. Syndication & Platform Links ({syndications.data?.length || syndicatedContent.length}) |
| 229 | + </button> |
188 | 230 | </div> |
189 | 231 | </header> |
190 | 232 |
|
@@ -318,6 +360,133 @@ export function App() { |
318 | 360 | </ul> |
319 | 361 | </section> |
320 | 362 | )} |
| 363 | + |
| 364 | + {activeTab === "syndication" && ( |
| 365 | + <section style={{ padding: 20, border: "1px solid #ddd", borderRadius: 12 }}> |
| 366 | + <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 16 }}> |
| 367 | + <div> |
| 368 | + <h2 style={{ margin: 0, fontSize: 18 }}>Cross-Platform Syndication & YouTube Funnel Tracker</h2> |
| 369 | + <p style={{ margin: "4px 0 0", color: "#666", fontSize: 13 }}> |
| 370 | + Tracks canonical <code>codingcat.dev</code> URLs and outbound links across YouTube, daily.dev Squad, Dev.to, Hashnode, AWS Builder Center, Medium, and Reddit. |
| 371 | + </p> |
| 372 | + </div> |
| 373 | + <div style={{ display: "flex", gap: 8 }}> |
| 374 | + <a |
| 375 | + href="https://daily.dev/squads/codingcatdev" |
| 376 | + target="_blank" |
| 377 | + rel="noreferrer" |
| 378 | + style={{ padding: "8px 12px", border: "1px solid #ccc", borderRadius: 6, textDecoration: "none", color: "#111", fontSize: 12, fontWeight: 600 }} |
| 379 | + > |
| 380 | + daily.dev Squad ↗ |
| 381 | + </a> |
| 382 | + <a |
| 383 | + href="https://dev.to/codingcatdev" |
| 384 | + target="_blank" |
| 385 | + rel="noreferrer" |
| 386 | + style={{ padding: "8px 12px", border: "1px solid #ccc", borderRadius: 6, textDecoration: "none", color: "#111", fontSize: 12, fontWeight: 600 }} |
| 387 | + > |
| 388 | + Dev.to Org ↗ |
| 389 | + </a> |
| 390 | + </div> |
| 391 | + </div> |
| 392 | + <div style={{ overflowX: "auto" }}> |
| 393 | + <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 13 }}> |
| 394 | + <thead> |
| 395 | + <tr style={{ textAlign: "left", borderBottom: "2px solid #eee" }}> |
| 396 | + <th style={{ padding: "8px 6px" }}>Date</th> |
| 397 | + <th style={{ padding: "8px 6px" }}>Title (Canonical)</th> |
| 398 | + <th style={{ padding: "8px 6px" }}>YouTube</th> |
| 399 | + <th style={{ padding: "8px 6px" }}>daily.dev Squad</th> |
| 400 | + <th style={{ padding: "8px 6px" }}>Dev.to</th> |
| 401 | + <th style={{ padding: "8px 6px" }}>Hashnode</th> |
| 402 | + <th style={{ padding: "8px 6px" }}>AWS Builder</th> |
| 403 | + <th style={{ padding: "8px 6px" }}>Medium / Reddit</th> |
| 404 | + </tr> |
| 405 | + </thead> |
| 406 | + <tbody> |
| 407 | + {syndicatedContent.map((item) => { |
| 408 | + const canonical = `${siteOrigin}/${item._type}/${item.slug}`; |
| 409 | + const hnUrl = item.hashnode |
| 410 | + ? item.hashnode.startsWith("http") |
| 411 | + ? item.hashnode |
| 412 | + : `https://hashnode.codingcat.dev/${item.hashnode}` |
| 413 | + : null; |
| 414 | + return ( |
| 415 | + <tr key={item._id} style={{ borderBottom: "1px solid #f0f0f0" }}> |
| 416 | + <td style={{ padding: "8px 6px", whiteSpace: "nowrap", color: "#666" }}> |
| 417 | + {(item.date || "").slice(0, 10)} |
| 418 | + </td> |
| 419 | + <td style={{ padding: "8px 6px", fontWeight: 600 }}> |
| 420 | + <a href={canonical} target="_blank" rel="noreferrer" style={{ color: "#111" }}> |
| 421 | + {item.title} |
| 422 | + </a> |
| 423 | + </td> |
| 424 | + <td style={{ padding: "8px 6px" }}> |
| 425 | + {item.youtube ? ( |
| 426 | + <a href={item.youtube} target="_blank" rel="noreferrer" style={{ color: "#c00", fontWeight: 600 }}> |
| 427 | + Watch ↗ |
| 428 | + </a> |
| 429 | + ) : ( |
| 430 | + <span style={{ color: "#aaa" }}>-</span> |
| 431 | + )} |
| 432 | + </td> |
| 433 | + <td style={{ padding: "8px 6px" }}> |
| 434 | + {item.dailydev ? ( |
| 435 | + <a href={item.dailydev} target="_blank" rel="noreferrer" style={{ color: "#7c3aed", fontWeight: 600 }}> |
| 436 | + Squad ↗ |
| 437 | + </a> |
| 438 | + ) : ( |
| 439 | + <span style={{ color: "#aaa" }}>-</span> |
| 440 | + )} |
| 441 | + </td> |
| 442 | + <td style={{ padding: "8px 6px" }}> |
| 443 | + {item.devto ? ( |
| 444 | + <a href={item.devto} target="_blank" rel="noreferrer" style={{ color: "#2563eb", fontWeight: 600 }}> |
| 445 | + Dev.to ↗ |
| 446 | + </a> |
| 447 | + ) : ( |
| 448 | + <span style={{ color: "#aaa" }}>-</span> |
| 449 | + )} |
| 450 | + </td> |
| 451 | + <td style={{ padding: "8px 6px" }}> |
| 452 | + {hnUrl ? ( |
| 453 | + <a href={hnUrl} target="_blank" rel="noreferrer" style={{ color: "#2563eb", fontWeight: 600 }}> |
| 454 | + Hashnode ↗ |
| 455 | + </a> |
| 456 | + ) : ( |
| 457 | + <span style={{ color: "#aaa" }}>-</span> |
| 458 | + )} |
| 459 | + </td> |
| 460 | + <td style={{ padding: "8px 6px" }}> |
| 461 | + {item.awsbuilder ? ( |
| 462 | + <a href={item.awsbuilder} target="_blank" rel="noreferrer" style={{ color: "#d97706", fontWeight: 600 }}> |
| 463 | + AWS ↗ |
| 464 | + </a> |
| 465 | + ) : ( |
| 466 | + <span style={{ color: "#aaa" }}>-</span> |
| 467 | + )} |
| 468 | + </td> |
| 469 | + <td style={{ padding: "8px 6px" }}> |
| 470 | + {item.medium && ( |
| 471 | + <a href={item.medium} target="_blank" rel="noreferrer" style={{ marginRight: 8 }}> |
| 472 | + Medium ↗ |
| 473 | + </a> |
| 474 | + )} |
| 475 | + {item.reddit && ( |
| 476 | + <a href={item.reddit} target="_blank" rel="noreferrer" style={{ color: "#ea580c" }}> |
| 477 | + Reddit ↗ |
| 478 | + </a> |
| 479 | + )} |
| 480 | + {!item.medium && !item.reddit && <span style={{ color: "#aaa" }}>-</span>} |
| 481 | + </td> |
| 482 | + </tr> |
| 483 | + ); |
| 484 | + })} |
| 485 | + </tbody> |
| 486 | + </table> |
| 487 | + </div> |
| 488 | + </section> |
| 489 | + )} |
321 | 490 | </div> |
322 | 491 | ); |
323 | 492 | } |
0 commit comments