0x0x230x

Untitled

May 12th, 2025
35
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 32.64 KB | None | 0 0
  1. import { env } from '@/lib/config/env';
  2. import { gigsService } from '@/lib/services/GigsService';
  3. import { ImageResponse } from 'next/og';
  4. import { formatUnits } from 'viem';
  5. import { coinGeckoService } from '@/lib/services/price-feed/CoinGeckoService';
  6. import { ETH_ADDRESS } from '@/lib/constants/common';
  7.  
  8. const width = 1200;
  9. const height = 800;
  10. const gigbotLogo = `${env.NEXT_PUBLIC_APP_BASE_URL}gigbot-logo.svg`;
  11. const bgImage = `${env.NEXT_PUBLIC_APP_BASE_URL}og-bg-image.svg`;
  12. const baseLogo = `https://raw.githubusercontent.com/base/brand-kit/refs/heads/main/logo/in-product/Base_Network_Logo.svg`;
  13.  
  14. const ethLogo = `${env.NEXT_PUBLIC_APP_BASE_URL}eth.svg`;
  15.  
  16. function formatTokenAmount(amount: number, decimals: number): string {
  17. const formatted = formatUnits(BigInt(amount), decimals);
  18. return formatted.toLocaleString();
  19. }
  20.  
  21. // Helper function to render embedded content for OG image
  22. function renderEmbeddedContent(gig: any) {
  23. const actionParams = gig.action_params || {};
  24. // Post
  25. if (actionParams.post) {
  26. const post = actionParams.post;
  27. const postText = (post.text || '').replace(/\n/g, ' ');
  28. return (
  29. <div
  30. style={{
  31. display: 'flex',
  32. flexDirection: 'column',
  33. alignItems: 'flex-start',
  34. width: '100%',
  35. minHeight: 120,
  36. padding: 0,
  37. height: 220,
  38. // background: 'yellow',
  39. }}
  40. >
  41. <div
  42. style={{
  43. display: 'flex',
  44. flexDirection: 'row',
  45. justifyContent: 'flex-start',
  46. alignItems: 'center',
  47. gap: 18,
  48. marginBottom: 12,
  49. width: '100%',
  50. }}
  51. >
  52. {post.author?.pfp ? (
  53. <img
  54. src={post.author?.pfp}
  55. alt={post.author?.username}
  56. width={48}
  57. height={48}
  58. style={{ borderRadius: 12, objectFit: 'cover', flexShrink: 0 }}
  59. />
  60. ) : (
  61. <div
  62. style={{
  63. width: 48,
  64. height: 48,
  65. borderRadius: '50%',
  66. background: '#232136',
  67. display: 'flex',
  68. alignItems: 'center',
  69. justifyContent: 'center',
  70. fontSize: 32,
  71. fontWeight: 900,
  72. color: '#A78BFA', // nice purple
  73. flexShrink: 0,
  74. border: '2px solid #393552',
  75. }}
  76. >
  77. {post.author?.username?.[0]?.toUpperCase() || 'U'}
  78. </div>
  79. )}
  80. <div
  81. style={{
  82. display: 'flex',
  83. flexDirection: 'column',
  84. maxWidth: 300,
  85. minWidth: 0,
  86. }}
  87. >
  88. <span
  89. style={{
  90. fontWeight: 900,
  91. fontSize: 32,
  92. color: '#e5e7eb',
  93. letterSpacing: 0.5,
  94. maxWidth: 300,
  95. overflow: 'hidden',
  96. textOverflow: 'ellipsis',
  97. whiteSpace: 'nowrap',
  98. background: 'rgba(37,35,59,0.18)',
  99. borderRadius: 6,
  100. padding: '2px 8px',
  101. }}
  102. >
  103. {post.author?.displayName || post.author?.username}
  104. </span>
  105. <span
  106. style={{
  107. fontSize: 18,
  108. color: '#e5e7eb',
  109. maxWidth: 300,
  110. overflow: 'hidden',
  111. textOverflow: 'ellipsis',
  112. whiteSpace: 'nowrap',
  113. marginTop: 2,
  114. }}
  115. >
  116. @{post.author?.username}
  117. </span>
  118. </div>
  119. </div>
  120. <span
  121. style={{
  122. color: '#e5e7eb',
  123. fontSize: 30,
  124. fontWeight: 500,
  125. wordBreak: 'break-word',
  126. whiteSpace: 'pre-line',
  127. lineHeight: 1.4,
  128. maxWidth: '100%',
  129. overflowWrap: 'break-word',
  130. height: 170,
  131. overflow: 'hidden',
  132. display: '-webkit-box',
  133. WebkitLineClamp: 3,
  134. WebkitBoxOrient: 'vertical',
  135. textOverflow: 'ellipsis',
  136. }}
  137. >
  138. {postText}
  139. </span>
  140. </div>
  141. );
  142. }
  143. // Profile
  144. if (actionParams.profile) {
  145. const profile = actionParams.profile;
  146. const bioText = (profile.bio || '').replace(/\n/g, ' ');
  147. return (
  148. <div
  149. style={{
  150. display: 'flex',
  151. flexDirection: 'row',
  152. alignItems: 'flex-start',
  153. width: '100%',
  154. minHeight: 220,
  155. padding: 0,
  156. height: 220,
  157. gap: 12,
  158. }}
  159. >
  160. <img
  161. src={profile.pfp}
  162. alt={profile.username}
  163. width={160}
  164. height={160}
  165. style={{ borderRadius: 28, objectFit: 'cover', marginBottom: 18 }}
  166. />
  167. <div style={{ display: 'flex', flexDirection: 'column' }}>
  168. <span
  169. style={{
  170. fontWeight: 800,
  171. fontSize: 34,
  172. color: '#e5e7eb',
  173. letterSpacing: 0.5,
  174. marginBottom: 6,
  175. textAlign: 'center',
  176. maxWidth: 400,
  177. overflow: 'hidden',
  178. textOverflow: 'ellipsis',
  179. whiteSpace: 'nowrap',
  180. }}
  181. >
  182. @{profile.username}
  183. </span>
  184. <span
  185. style={{
  186. fontSize: 32,
  187. color: '#e5e7eb',
  188. fontWeight: 500,
  189. textAlign: 'center',
  190. maxWidth: 400,
  191. overflow: 'hidden',
  192. textOverflow: 'ellipsis',
  193. whiteSpace: 'nowrap',
  194. marginBottom: 18,
  195. }}
  196. >
  197. {profile.displayName}
  198. </span>
  199. </div>
  200. </div>
  201. );
  202. }
  203. // Channel
  204. if (actionParams.channel) {
  205. const channel = actionParams.channel;
  206. const descText = (channel.description || '').replace(/\n/g, ' ');
  207. return (
  208. <div
  209. style={{
  210. display: 'flex',
  211. flexDirection: 'column',
  212. alignItems: 'flex-start',
  213. width: '100%',
  214. minHeight: 120,
  215. padding: 0,
  216. height: 340,
  217. }}
  218. >
  219. <div
  220. style={{
  221. display: 'flex',
  222. flexDirection: 'row',
  223. alignItems: 'center',
  224. gap: 18,
  225. marginBottom: 12,
  226. width: '100%',
  227. }}
  228. >
  229. {channel.image_url && (
  230. <img
  231. src={channel.image_url}
  232. alt={channel.name}
  233. width={48}
  234. height={48}
  235. style={{ borderRadius: 12, objectFit: 'cover', flexShrink: 0 }}
  236. />
  237. )}
  238. <div
  239. style={{
  240. display: 'flex',
  241. flexDirection: 'column',
  242. maxWidth: 300,
  243. minWidth: 0,
  244. }}
  245. >
  246. <span
  247. style={{
  248. fontWeight: 900,
  249. fontSize: 32,
  250. color: '#e5e7eb',
  251. letterSpacing: 0.5,
  252. maxWidth: 300,
  253. overflow: 'hidden',
  254. textOverflow: 'ellipsis',
  255. whiteSpace: 'nowrap',
  256. background: 'rgba(37,35,59,0.18)',
  257. borderRadius: 6,
  258. padding: '2px 8px',
  259. }}
  260. >
  261. {channel.name}
  262. </span>
  263. </div>
  264. </div>
  265. {channel.description && (
  266. <span
  267. style={{
  268. color: '#e5e7eb',
  269. fontSize: 30,
  270. fontWeight: 500,
  271. wordBreak: 'break-word',
  272. whiteSpace: 'pre-line',
  273. lineHeight: 1.4,
  274. maxWidth: '100%',
  275. overflowWrap: 'break-word',
  276. height: 200,
  277. overflow: 'hidden',
  278. display: '-webkit-box',
  279. WebkitLineClamp: 5,
  280. WebkitBoxOrient: 'vertical',
  281. textOverflow: 'ellipsis',
  282. }}
  283. >
  284. {descText}
  285. </span>
  286. )}
  287. </div>
  288. );
  289. }
  290. return null;
  291. }
  292.  
  293. export async function GET(
  294. request: Request,
  295. { params }: { params: Promise<{ id: string }> },
  296. ) {
  297. const { id } = await params;
  298. const gigId = Number(id);
  299. const gig = await gigsService.getGig(gigId);
  300.  
  301. if (!gig || !gig.production) {
  302. return new ImageResponse(
  303. (
  304. <div
  305. style={{
  306. width,
  307. height,
  308. position: 'relative',
  309. display: 'flex',
  310. flexDirection: 'column',
  311. alignItems: 'center',
  312. justifyContent: 'center',
  313. fontFamily: 'Inter',
  314. color: 'white',
  315. }}
  316. >
  317. <img
  318. src={bgImage}
  319. width={width}
  320. height={height}
  321. style={{
  322. position: 'absolute',
  323. top: 0,
  324. left: 0,
  325. width: '100%',
  326. height: '100%',
  327. objectFit: 'cover',
  328. zIndex: 0,
  329. }}
  330. />
  331.  
  332. <div
  333. style={{
  334. position: 'absolute',
  335. top: 32,
  336. left: 32,
  337. display: 'flex',
  338. flexDirection: 'row',
  339. alignItems: 'center',
  340. zIndex: 2,
  341. }}
  342. >
  343. <img
  344. src={gigbotLogo}
  345. alt="Gigbot Logo"
  346. width={64}
  347. height={64}
  348. style={{ display: 'block' }}
  349. />
  350. <div
  351. style={{
  352. display: 'flex',
  353. fontSize: 40,
  354. fontWeight: 900,
  355. letterSpacing: 2,
  356. marginLeft: 12,
  357. }}
  358. >
  359. <span style={{ color: '#855DCD', marginRight: 4 }}>GIG</span>
  360. <span style={{ color: '#fff' }}>.BOT</span>
  361. </div>
  362. </div>
  363.  
  364. <div
  365. style={{
  366. zIndex: 2,
  367. backgroundColor: 'rgba(163, 110, 253, 0.20)',
  368. padding: 48,
  369. borderRadius: 24,
  370. fontSize: 40,
  371. fontWeight: 600,
  372. color: '#855DCD',
  373. border: '1px solid #594798',
  374. }}
  375. >
  376. Gig not found
  377. </div>
  378. </div>
  379. ),
  380. { width, height },
  381. );
  382. }
  383.  
  384. // Fetch USD price for the token
  385. let usdPrice = 0;
  386. try {
  387. const prices = await coinGeckoService.getTokensPrices();
  388. const price =
  389. prices[gig.token.coingecko_id || ''] ||
  390. prices[gig.token.pair_address || ''];
  391. if (price) {
  392. usdPrice = Number(price);
  393. }
  394. } catch (e) {
  395. console.error('Failed to fetch USD price', e);
  396. }
  397.  
  398. const formattedAmount = formatTokenAmount(gig.amount, gig.token.decimals);
  399. const durationInDays = Math.ceil(
  400. (gig.end_time_ms - gig.start_time_ms) / (1000 * 60 * 60 * 24),
  401. );
  402. const howToEarn = gig.how_to_earn?.split('\n').filter(Boolean).join(' ');
  403.  
  404. // Calculate USD value for the split info
  405. const amountInUnits = Number(
  406. formatUnits(BigInt(gig.amount), gig.token.decimals),
  407. );
  408.  
  409. const usdTotal = usdPrice * amountInUnits;
  410.  
  411. const hasEmbeddedContent = !!renderEmbeddedContent(gig);
  412.  
  413. const mockedHowToEarn = howToEarn + howToEarn + howToEarn + howToEarn;
  414.  
  415. const tokenLogo =
  416. gig.token.address.toLowerCase() === ETH_ADDRESS
  417. ? ethLogo
  418. : gig.token.image_url;
  419.  
  420. return new ImageResponse(
  421. (
  422. <div
  423. style={{
  424. width,
  425. height,
  426. position: 'relative',
  427. display: 'flex',
  428. flexDirection: 'column',
  429. fontFamily: 'Inter',
  430. color: 'white',
  431. }}
  432. >
  433. <img
  434. src={bgImage}
  435. width={width}
  436. height={height}
  437. style={{
  438. position: 'absolute',
  439. top: 0,
  440. left: 0,
  441. width: '100%',
  442. height: '100%',
  443. objectFit: 'cover',
  444. zIndex: 0,
  445. }}
  446. />
  447.  
  448. <div
  449. style={{
  450. position: 'absolute',
  451. top: 32,
  452. left: 32,
  453. display: 'flex',
  454. flexDirection: 'row',
  455. alignItems: 'center',
  456. zIndex: 2,
  457. }}
  458. >
  459. <img
  460. src={env.NEXT_PUBLIC_APP_BASE_URL + 'gigbot-logo.svg'}
  461. alt="Gigbot Logo"
  462. width={64}
  463. height={64}
  464. style={{ display: 'block' }}
  465. />
  466. <div
  467. style={{
  468. display: 'flex',
  469. fontSize: 48,
  470. fontWeight: 900,
  471. letterSpacing: 2,
  472. marginLeft: 12,
  473. }}
  474. >
  475. <span style={{ color: '#855DCD', marginRight: 4 }}>GIG</span>
  476. <span style={{ color: '#fff' }}>.BOT</span>
  477. </div>
  478. </div>
  479.  
  480. <div
  481. style={{
  482. zIndex: 2,
  483. display: 'flex',
  484. flexDirection: 'column',
  485. alignItems: 'center',
  486. justifyContent: 'center',
  487. flexGrow: 1,
  488. padding: '48px 64px',
  489. marginTop: 70,
  490. }}
  491. >
  492. <div
  493. style={{
  494. display: 'flex',
  495. flexDirection: 'column',
  496. backgroundColor: 'rgba(163, 110, 253, 0.20)',
  497. backdropFilter: 'blur(12px)',
  498. WebkitBackdropFilter: 'blur(12px)',
  499. padding: 20,
  500. borderRadius: 24,
  501. width: 1150,
  502. height: 650,
  503. alignItems: 'flex-start',
  504. border: '1px solid #594798',
  505. gap: 32,
  506. }}
  507. >
  508. {/* Row 1: Token + Base logos and Title (combined) */}
  509. <div
  510. style={{
  511. display: 'flex',
  512. flexDirection: 'row',
  513. justifyContent: 'flex-start',
  514. alignItems: 'center',
  515. position: 'relative',
  516. width: '100%',
  517. gap: 70,
  518. }}
  519. >
  520. {/* Token + Base logos */}
  521. <div
  522. style={{
  523. display: 'flex',
  524. position: 'relative',
  525. width: 96,
  526. height: 96,
  527. justifyContent: 'flex-start',
  528. alignItems: 'center',
  529. }}
  530. >
  531. <img
  532. src={baseLogo}
  533. alt="Base logo"
  534. width={100}
  535. height={100}
  536. style={{
  537. position: 'absolute',
  538. left: 56,
  539. top: 0,
  540. zIndex: 1,
  541. }}
  542. />
  543. <img
  544. src={tokenLogo}
  545. alt="Token logo"
  546. width={100}
  547. height={100}
  548. style={{
  549. position: 'absolute',
  550. left: 0,
  551. top: 0,
  552. zIndex: 2,
  553. borderRadius: '50%',
  554. }}
  555. />
  556. </div>
  557.  
  558. {/* Title */}
  559. <div
  560. style={{
  561. display: 'flex',
  562. flexDirection: 'column',
  563. justifyContent: 'flex-start',
  564. alignItems: 'flex-start',
  565. flexWrap: 'wrap',
  566. gap: 0,
  567. color: '#e5e7eb',
  568. fontFamily: 'Inter',
  569. fontSize: 62,
  570. fontStyle: 'normal',
  571. fontWeight: 600,
  572. lineHeight: 'normal',
  573. letterSpacing: 0.353,
  574. textAlign: 'left',
  575. }}
  576. >
  577. <div
  578. style={{
  579. display: 'flex',
  580. gap: 8,
  581. alignItems: 'center',
  582. textShadow:
  583. '1px 0 #fff, -1px 0 #fff, 0 1px #fff, 0 -1px #fff',
  584. }}
  585. >
  586. <span>
  587. Earn {formattedAmount} ${gig.token.symbol}
  588. </span>
  589. </div>
  590. <span
  591. style={{
  592. marginTop: 4,
  593. fontSize: 49,
  594. fontWeight: 500,
  595. color: '#e5e7eb',
  596. letterSpacing: 0.5,
  597. textShadow:
  598. '1px 0 #fff, -1px 0 #fff, 0 1px #fff, 0 -1px #fff',
  599. }}
  600. >
  601. {`~$${usdTotal.toLocaleString(undefined, {
  602. maximumFractionDigits: 2,
  603. })} split to all earners`}
  604. </span>
  605. </div>
  606. </div>
  607. {/* Row with two 50% containers */}
  608. <div
  609. style={{
  610. display: 'flex',
  611. flexDirection: 'row',
  612. width: '100%',
  613. gap: 0,
  614. marginTop: 10,
  615. position: 'relative',
  616. minHeight: 300,
  617. }}
  618. >
  619. {!hasEmbeddedContent ? (
  620. <div
  621. style={{
  622. position: 'absolute',
  623. left: '50%',
  624. top: '65%',
  625. transform: 'translate(-50%, -50%)',
  626. width: '100%',
  627. borderRadius: 24,
  628. display: 'flex',
  629. flexDirection: 'column',
  630. alignItems: 'flex-start',
  631. zIndex: 2,
  632. height: 450,
  633. textShadow:
  634. '1px 0 #fff, -1px 0 #fff, 0 1px #fff, 0 -1px #fff',
  635. }}
  636. >
  637. <div
  638. style={{
  639. height: 300,
  640. display: 'flex',
  641. flexDirection: 'column',
  642. alignItems: 'flex-start',
  643. justifyContent: 'flex-start',
  644. gap: 12,
  645. padding: 12,
  646. width: '100%',
  647. marginTop: 40,
  648. }}
  649. >
  650. <span
  651. style={{
  652. color: '#e5e7eb',
  653. fontSize: 36,
  654. fontWeight: 900,
  655. marginBottom: 12,
  656. letterSpacing: 0.5,
  657. fontFamily: 'Inter',
  658. textShadow:
  659. '1px 0 #855DCD, -1px 0 #855DCD, 0 1px #855DCD, 0 -1px #855DCD',
  660. }}
  661. >
  662. HOW TO EARN
  663. </span>
  664.  
  665. <span
  666. style={{
  667. color: '#e5e7eb',
  668. fontSize: 30,
  669. fontWeight: 500,
  670. wordBreak: 'break-word',
  671. whiteSpace: 'pre-line',
  672. lineHeight: 1.4,
  673. maxWidth: '100%',
  674. overflowWrap: 'break-word',
  675. height: 170,
  676. display: '-webkit-box',
  677. WebkitLineClamp: 4,
  678. WebkitBoxOrient: 'vertical',
  679. textOverflow: 'ellipsis',
  680. zIndex: 100,
  681. letterSpacing: 1,
  682. }}
  683. >
  684. {howToEarn && howToEarn.trim().length > 0 ? (
  685. howToEarn
  686. ) : (
  687. <span style={{ color: '#b6b6b6', fontStyle: 'italic' }}>
  688. No instructions provided
  689. </span>
  690. )}
  691. </span>
  692. </div>
  693.  
  694. <div
  695. style={{
  696. display: 'flex',
  697. flexDirection: 'row',
  698. justifyContent: 'space-between',
  699. alignItems: 'flex-end',
  700. width: '100%',
  701. marginTop: 0,
  702. }}
  703. >
  704. {/* GIG TYPE */}
  705. <div
  706. style={{
  707. display: 'flex',
  708. flexDirection: 'column',
  709. alignItems: 'flex-start',
  710. minWidth: 0,
  711. }}
  712. >
  713. <span
  714. style={{
  715. color: '#e5e7eb',
  716. fontSize: 36,
  717. fontWeight: 900,
  718. marginBottom: 10,
  719. letterSpacing: 0.5,
  720. }}
  721. >
  722. GIG TYPE
  723. </span>
  724. <div
  725. style={{
  726. display: 'flex',
  727. alignItems: 'center',
  728. gap: 14,
  729. }}
  730. >
  731. {gig.gig_type?.display?.[gig.platform]?.icon && (
  732. <img
  733. src={gig.gig_type.display[gig.platform].icon}
  734. alt="gig type icon"
  735. width={34}
  736. height={34}
  737. style={{ borderRadius: 6, background: '#232136' }}
  738. />
  739. )}
  740. <span
  741. style={{
  742. color: '#e5e7eb',
  743. fontSize: 32,
  744. fontWeight: 600,
  745. textTransform: 'uppercase',
  746. letterSpacing: 0.5,
  747. }}
  748. >
  749. {gig.gig_type?.display?.[gig.platform]?.label || ''}
  750. </span>
  751. </div>
  752. </div>
  753. {/* OFFER ENDS */}
  754. <div
  755. style={{
  756. display: 'flex',
  757. flexDirection: 'column',
  758. alignItems: 'flex-end',
  759. minWidth: 0,
  760. textAlign: 'right',
  761. }}
  762. >
  763. <span
  764. style={{
  765. color: '#e5e7eb',
  766. fontSize: 36,
  767. fontWeight: 900,
  768. marginBottom: 10,
  769. letterSpacing: 0.5,
  770. textShadow:
  771. '1px 0 #fff, -1px 0 #fff, 0 1px #fff, 0 -1px #fff',
  772. }}
  773. >
  774. OFFER ENDS
  775. </span>
  776. <span
  777. style={{
  778. color: '#e5e7eb',
  779. fontSize: 32,
  780. fontWeight: 600,
  781. letterSpacing: 0.5,
  782. }}
  783. >
  784. {new Date(gig.end_time_ms).toLocaleString('en-US', {
  785. month: 'short',
  786. day: 'numeric',
  787. year: 'numeric',
  788. hour: '2-digit',
  789. minute: '2-digit',
  790. hour12: true,
  791. })}
  792. </span>
  793. </div>
  794. </div>
  795. </div>
  796. ) : (
  797. <>
  798. <div
  799. style={{
  800. width: '45%',
  801. borderRadius: 16,
  802. display: 'flex',
  803. flexDirection: 'column',
  804. alignItems: 'flex-start',
  805. boxSizing: 'border-box',
  806. gap: 0,
  807. padding: '8px 0 8px 8px',
  808. // background: 'red',
  809. }}
  810. >
  811. {/* HOW TO EARN section */}
  812. <div
  813. style={{
  814. // background: 'yellow',
  815. display: 'flex',
  816. flexDirection: 'column',
  817. height: 200,
  818. }}
  819. >
  820. <span
  821. style={{
  822. color: '#e5e7eb',
  823. fontSize: 36,
  824. fontWeight: 900,
  825. marginBottom: 12,
  826. letterSpacing: 0.5,
  827. textShadow:
  828. '1px 0 #fff, -1px 0 #fff, 0 1px #fff, 0 -1px #fff',
  829. }}
  830. >
  831. HOW TO EARN
  832. </span>
  833. {howToEarn ? (
  834. <span
  835. style={{
  836. color: '#e5e7eb',
  837. fontSize: 30,
  838. fontWeight: 500,
  839. wordBreak: 'break-word',
  840. whiteSpace: 'pre-line',
  841. lineHeight: 1.4,
  842. maxWidth: '100%',
  843. overflowWrap: 'break-word',
  844. minHeight: 60,
  845. overflow: 'hidden',
  846. display: '-webkit-box',
  847. WebkitLineClamp: 3,
  848. WebkitBoxOrient: 'vertical',
  849. textOverflow: 'ellipsis',
  850. marginBottom: 4,
  851. marginTop: 28,
  852. height: 260,
  853. }}
  854. >
  855. {howToEarn}
  856. </span>
  857. ) : (
  858. <span
  859. style={{
  860. color: '#b6b6b6',
  861. fontSize: 32,
  862. fontStyle: 'italic',
  863. minHeight: 60,
  864. marginBottom: 32,
  865. display: 'flex',
  866. alignItems: 'center',
  867. }}
  868. >
  869. No instructions provided
  870. </span>
  871. )}
  872. </div>
  873. {/* GIG TYPE only, now at the bottom left */}
  874. <div
  875. style={{
  876. marginTop: 105,
  877. display: 'flex',
  878. flexDirection: 'row',
  879. justifyContent: 'space-between',
  880. alignItems: 'flex-end',
  881. width: '100%',
  882. gap: 32,
  883. // background: 'blue',
  884. }}
  885. >
  886. {/* GIG TYPE */}
  887. <div
  888. style={{
  889. display: 'flex',
  890. flexDirection: 'column',
  891. alignItems: 'flex-start',
  892. minWidth: 0,
  893. }}
  894. >
  895. <span
  896. style={{
  897. color: '#e5e7eb',
  898. fontSize: 36,
  899. fontWeight: 900,
  900. marginBottom: 10,
  901. letterSpacing: 0.5,
  902. textShadow:
  903. '1px 0 #fff, -1px 0 #fff, 0 1px #fff, 0 -1px #fff',
  904. }}
  905. >
  906. GIG TYPE
  907. </span>
  908. <div
  909. style={{
  910. display: 'flex',
  911. alignItems: 'center',
  912. gap: 14,
  913. }}
  914. >
  915. {gig.gig_type?.display?.[gig.platform]?.icon && (
  916. <img
  917. src={gig.gig_type.display[gig.platform].icon}
  918. alt="gig type icon"
  919. width={34}
  920. height={34}
  921. style={{ borderRadius: 6, background: '#232136' }}
  922. />
  923. )}
  924. <span
  925. style={{
  926. color: '#e5e7eb',
  927. fontSize: 32,
  928. fontWeight: 600,
  929. textTransform: 'uppercase',
  930. letterSpacing: 0.5,
  931. }}
  932. >
  933. {gig.gig_type?.display?.[gig.platform]?.label || ''}
  934. </span>
  935. </div>
  936. </div>
  937. </div>
  938. </div>
  939. {/* Spacer for visible gap */}
  940. <div style={{ width: '10%' }} />
  941. {/* Right: Embedded Content (post/profile/channel) if present */}
  942. <div
  943. style={{
  944. width: '45%',
  945. borderRadius: 16,
  946. display: 'flex',
  947. flexDirection: 'column',
  948. alignItems: 'flex-start',
  949. justifyContent: 'flex-start',
  950. boxSizing: 'border-box',
  951. gap: 0,
  952. padding: '8px 0 8px 8px',
  953. marginTop: 0,
  954. height: '100%',
  955. // background: 'red',
  956. }}
  957. >
  958. {renderEmbeddedContent(gig)}
  959.  
  960. {/* OFFER ENDS now at the bottom right */}
  961. <div
  962. style={{
  963. marginTop: 85,
  964. // background: 'blue',
  965. display: 'flex',
  966. flexDirection: 'column',
  967. alignItems: 'flex-end',
  968. minWidth: 0,
  969. textAlign: 'right',
  970. width: '100%',
  971. }}
  972. >
  973. <span
  974. style={{
  975. color: '#e5e7eb',
  976. fontSize: 36,
  977. fontWeight: 900,
  978. marginBottom: 10,
  979. letterSpacing: 0.5,
  980. textShadow:
  981. '1px 0 #fff, -1px 0 #fff, 0 1px #fff, 0 -1px #fff',
  982. }}
  983. >
  984. OFFER ENDS
  985. </span>
  986. <span
  987. style={{
  988. color: '#e5e7eb',
  989. fontSize: 32,
  990. fontWeight: 600,
  991. letterSpacing: 0.5,
  992. }}
  993. >
  994. {new Date(gig.end_time_ms).toLocaleString('en-US', {
  995. month: 'short',
  996. day: 'numeric',
  997. year: 'numeric',
  998. hour: '2-digit',
  999. minute: '2-digit',
  1000. hour12: true,
  1001. })}
  1002. </span>
  1003. </div>
  1004. </div>
  1005. </>
  1006. )}
  1007. </div>
  1008. </div>
  1009. </div>
  1010. </div>
  1011. ),
  1012. {
  1013. width,
  1014. height,
  1015. },
  1016. );
  1017. }
  1018.  
Advertisement
Add Comment
Please, Sign In to add comment