PageSourceSearch

https://app.buildcities.com/assets/ChallengeCard-C_Kzwp5I.js

js buildcities.com collected 2026-09-28 07:34:25 UTC 14,144 bytes, 217 lines download raw bytes

1import { j as jsxRuntimeExports } from "./vendor-react-BoGG1wja.js";
2import { I as Image, A as Avatar, n as navigate, h as namedRoutes } from "./index-Cn8jK25t.js";
3import { u as useBreakpoint } from "./useMediaQuery-C69IUzRZ.js";
4import { F as ForwardRef } from "./PhotoIcon-CgxDqnvo.js";
5import { F as ForwardRef$1 } from "./ClockIcon-B6vIAdIF.js";
6import { F as ForwardRef$2 } from "./CurrencyDollarIcon-P1LKg5m9.js";
7const ChallengeCard = ({
8  challenge
9}) => {
10  const {
11    isMobile
12  } = useBreakpoint();
13  const {
14    slug,
15    title,
16    description,
17    coverImage,
18    goalAmount,
19    startDate,
20    endDate,
21    publisher,
22    publishAsNetwork,
23    networks = [],
24    cities = []
25  } = challenge;
26  const formatCurrency = (amount) => {
27    if (!amount) return "N/A";
28    return new Intl.NumberFormat("en-US", {
29      style: "currency",
30      currency: "USD",
31      minimumFractionDigits: 0,
32      maximumFractionDigits: 0
33    }).format(amount);
34  };
35  const formatDate = (timestamp) => {
36    if (!timestamp) return null;
37    const date = new Date(timestamp);
38    return date.toLocaleDateString("en-US", {
39      month: "short",
40      day: "numeric",
41      year: "numeric"
42    });
43  };
44  const getDaysRemaining = () => {
45    if (!endDate) return null;
46    const now = (/* @__PURE__ */ new Date()).getTime();
47    const end = new Date(endDate).getTime();
48    const diff = end - now;
49    const days = Math.ceil(diff / (1e3 * 60 * 60 * 24));
50    return days > 0 ? days : 0;
51  };
52  const daysRemaining = getDaysRemaining();
53  const publisherDisplay = publishAsNetwork && networks[0] ? {
54    name: networks[0].name,
55    icon: networks[0].img
56  } : publisher;
57  const getChallengeStatus = () => {
58    const now = (/* @__PURE__ */ new Date()).getTime();
59    if (!startDate && !endDate) return "unknown";
60    const start = startDate ? new Date(startDate).getTime() : null;
61    const end = endDate ? new Date(endDate).getTime() : null;
62    if (start && now < start) return "upcoming";
63    if (end && now > end) return "expired";
64    if (start && now >= start) return "ongoing";
65    return "unknown";
66  };
67  const challengeStatus = getChallengeStatus();
68  const getStatusConfig = () => {
69    switch (challengeStatus) {
70      case "upcoming":
71        return {
72          label: "Upcoming",
73          bgColor: "bg-brandYellow/20",
74          textColor: "text-brandYellow"
75        };
76      case "ongoing":
77        return {
78          label: "Ongoing",
79          bgColor: "bg-brandYellow/20",
80          textColor: "text-brandYellow"
81        };
82      case "expired":
83        return {
84          label: "Expired",
85          bgColor: "bg-brandNeutralMedium/20",
86          textColor: "text-brandNeutralMedium"
87        };
88      default:
89        return {
90          label: "No Timeline",
91          bgColor: "bg-brandBackgroundBorder/20",
92          textColor: "text-brandNeutralMedium"
93        };
94    }
95  };
96  const statusConfig = getStatusConfig();
97  const getDaysUntilStart = () => {
98    if (!startDate) return null;
99    const now = (/* @__PURE__ */ new Date()).getTime();
100    const start = new Date(startDate).getTime();
101    const diff = start - now;
102    const days = Math.ceil(diff / (1e3 * 60 * 60 * 24));
103    return days > 0 ? days : 0;
104  };
105  const daysUntilStart = getDaysUntilStart();
106  const handleViewChallenge = () => {
107    navigate(namedRoutes.challenge({
108      slug
109    }));
110  };
111  if (isMobile) {
112    return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { onClick: handleViewChallenge, className: "bg-brandBackgroundBlack hover:bg-brandBackgroundBlack/95 cursor-pointer rounded-2xl p-4 transition-colors", children: [
113      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-3", children: coverImage ? /* @__PURE__ */ jsxRuntimeExports.jsx(Image, { src: coverImage, alt: title, className: "h-40 w-full rounded-xl object-cover", showDefaultFallback: true }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-brandBackgroundBorder/50 text-brandNeutralMedium flex h-40 w-full flex-col items-center justify-center rounded-xl", children: [
114        /* @__PURE__ */ jsxRuntimeExports.jsx(ForwardRef, { className: "mb-1 h-10 w-10" }),
115        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs", children: "No image" })
116      ] }) }),
117      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx("h5", { className: "text-brandYellow text-lg font-semibold", children: title }) }),
118      /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-brandNeutralMedium mb-3 line-clamp-2 text-sm", children: description }),
119      publisherDisplay && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-3 flex items-center", children: [
120        /* @__PURE__ */ jsxRuntimeExports.jsx(Avatar, { src: publisherDisplay.icon, username: publisherDisplay.name, className: "mr-2 h-6 w-6" }),
121        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-brandNeutral text-sm", children: publisherDisplay.name })
122      ] }),
123      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
124        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between text-sm", children: [
125          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-brandNeutralMedium", children: "Funds Available:" }),
126          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `font-semibold ${goalAmount ? "text-brandSemanticSuccessStrong" : "text-brandNeutralMedium"}`, children: formatCurrency(goalAmount) })
127        ] }),
128        (startDate || endDate) && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-brandBackgroundBorder/30 rounded-lg p-2", children: [
129          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-2 flex items-center justify-between", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `inline-flex items-center gap-1 rounded-full px-2 py-0.5 ${statusConfig.bgColor} ${statusConfig.textColor} text-xs font-semibold`, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: statusConfig.label }) }) }),
130          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between text-xs", children: [
131            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
132              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-brandNeutralMedium mb-0.5", children: "Start" }),
133              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-brandNeutral font-medium", children: formatDate(startDate) || "TBD" })
134            ] }),
135            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border-brandBackgroundBorder mx-2 flex-1 border-t border-dashed" }),
136            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-right", children: [
137              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-brandNeutralMedium mb-0.5", children: "End" }),
138              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-brandNeutral font-medium", children: formatDate(endDate) || "TBD" })
139            ] })
140          ] }),
141          challengeStatus === "ongoing" && daysRemaining !== null && daysRemaining > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-brandYellow mt-2 flex items-center justify-center text-xs", children: [
142            /* @__PURE__ */ jsxRuntimeExports.jsx(ForwardRef$1, { className: "mr-1 h-3 w-3" }),
143            daysRemaining,
144            " days remaining"
145          ] }),
146          challengeStatus === "upcoming" && daysUntilStart !== null && daysUntilStart > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-brandYellow mt-2 flex items-center justify-center text-xs", children: [
147            /* @__PURE__ */ jsxRuntimeExports.jsx(ForwardRef$1, { className: "mr-1 h-3 w-3" }),
148            "Starts in ",
149            daysUntilStart,
150            " days"
151          ] })
152        ] })
153      ] }),
154      (networks.length > 0 || cities.length > 0) && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-3 flex flex-wrap gap-1", children: [
155        networks.slice(0, 2).map((network) => /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "bg-brandBackgroundBorder text-brandNeutralMedium rounded-full px-2 py-1 text-xs", children: network.name }, network.id)),
156        cities.slice(0, 2).map((city) => /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "bg-brandBackgroundBorder text-brandNeutralMedium rounded-full px-2 py-1 text-xs", children: city.name }, city.id))
157      ] })
158    ] });
159  }
160  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { onClick: handleViewChallenge, className: "bg-brandBackgroundBlack hover:bg-brandBackgroundBlack/95 cursor-pointer rounded-2xl p-5 transition-colors", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-5", children: [
161    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-shrink-0", children: coverImage ? /* @__P
161URE__ */ jsxRuntimeExports.jsx(Image, { src: coverImage, alt: title, className: "h-36 w-36 rounded-xl object-cover", showDefaultFallback: true }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-brandBackgroundBorder/50 text-brandNeutralMedium flex h-36 w-36 flex-col items-center justify-center rounded-xl", children: [
162      /* @__PURE__ */ jsxRuntimeExports.jsx(ForwardRef, { className: "mb-1 h-10 w-10" }),
163      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs", children: "No image" })
164    ] }) }),
165    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex-1", children: [
166      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-2", children: [
167        /* @__PURE__ */ jsxRuntimeExports.jsx("h5", { className: "text-brandYellow mb-1 text-xl font-semibold", children: title }),
168        /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-brandNeutralMedium line-clamp-1 text-sm", children: description })
169      ] }),
170      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-3 flex items-center justify-between", children: [
171        publisherDisplay && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center", children: [
172          /* @__PURE__ */ jsxRuntimeExports.jsx(Avatar, { src: publisherDisplay.icon, username: publisherDisplay.name, className: "mr-2 h-6 w-6" }),
173          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-brandNeutral text-sm font-medium", children: publisherDisplay.name })
174        ] }),
175        (networks.length > 0 || cities.length > 0) && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap gap-1.5", children: [
176          networks.slice(0, 2).map((network) => /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "bg-brandBackgroundBorder text-brandNeutralMedium rounded-full px-2.5 py-0.5 text-xs", children: network.name }, network.id)),
177          cities.slice(0, 2).map((city) => /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "bg-brandBackgroundBorder text-brandNeutralMedium rounded-full px-2.5 py-0.5 text-xs", children: city.name }, city.id))
178        ] })
179      ] }),
180      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start gap-6", children: [
181        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-shrink-0", children: [
182          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-brandNeutralMedium mb-1 flex items-center text-xs", children: [
183            /* @__PURE__ */ jsxRuntimeExports.jsx(ForwardRef$2, { className: "mr-1 h-4 w-4" }),
184            "Funds Available"
185          ] }),
186          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `text-lg font-semibold ${goalAmount ? "text-brandSemanticSuccessStrong" : "text-brandNeutralMedium"}`, children: formatCurrency(goalAmount) })
187        ] }),
188        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1", children: [
189          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-brandNeutralMedium mb-1 flex items-center justify-between text-xs", children: [
190            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center", children: [
191              /* @__PURE__ */ jsxRuntimeExports.jsx(ForwardRef$1, { className: "mr-1 h-4 w-4" }),
192              "Timeline"
193            ] }),
194            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `inline-flex items-center gap-1 rounded-full px-2 py-0.5 ${statusConfig.bgColor} ${statusConfig.textColor} text-xs font-semibold`, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: statusConfig.label }) })
195          ] }),
196          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "bg-brandBackgroundBorder/30 rounded-lg px-4 py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4 text-sm", children: [
197            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-brandNeutral whitespace-nowrap", children: formatDate(startDate) || "TBD" }),
198            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border-brandNeutralMedium/30 min-w-[40px] flex-1 border-t-2 border-dashed" }),
199            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-brandNeutral whitespace-nowrap", children: formatDate(endDate) || "TBD" }),
200            challengeStatus === "ongoing" && daysRemaining !== null && daysRemaining > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-brandYellow whitespace-nowrap text-sm font-medium", children: [
201              daysRemaining,
202              " days remaining"
203            ] }),
204            challengeStatus === "upcoming" && daysUntilStart !== null && daysUntilStart > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-brandYellow whitespace-nowrap text-sm font-medium", children: [
205              "Starts in ",
206              daysUntilStart,
207              " days"
208            ] })
209          ] }) })
210        ] })
211      ] })
212    ] })
213  ] }) });
214};
215export {
216  ChallengeCard as C
217};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.