@rcnr/theme 2.0.4 → 2.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +19 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.js +246 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +243 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -41,4 +41,22 @@ interface RCNRMountainLogoProps {
|
|
|
41
41
|
}
|
|
42
42
|
declare function RCNRMountainLogo({ href, className, }: RCNRMountainLogoProps): react_jsx_runtime.JSX.Element;
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
interface ReportIssueModalProps {
|
|
45
|
+
isOpen: boolean;
|
|
46
|
+
onClose: () => void;
|
|
47
|
+
toolName: string;
|
|
48
|
+
apiBaseUrl?: string;
|
|
49
|
+
userEmail?: string;
|
|
50
|
+
}
|
|
51
|
+
declare function ReportIssueModal({ isOpen, onClose, toolName, apiBaseUrl, userEmail, }: ReportIssueModalProps): react_jsx_runtime.JSX.Element | null;
|
|
52
|
+
|
|
53
|
+
interface RequestToolModalProps {
|
|
54
|
+
isOpen: boolean;
|
|
55
|
+
onClose: () => void;
|
|
56
|
+
toolName: string;
|
|
57
|
+
apiBaseUrl?: string;
|
|
58
|
+
userEmail?: string;
|
|
59
|
+
}
|
|
60
|
+
declare function RequestToolModal({ isOpen, onClose, toolName, apiBaseUrl, userEmail, }: RequestToolModalProps): react_jsx_runtime.JSX.Element | null;
|
|
61
|
+
|
|
62
|
+
export { RCNRFooter, type RCNRFooterProps, RCNRHeader, type RCNRHeaderProps, RCNRMountainLogo, RCNRSubNav, type RCNRSubNavProps, ReportIssueModal, RequestToolModal };
|
package/dist/index.d.ts
CHANGED
|
@@ -41,4 +41,22 @@ interface RCNRMountainLogoProps {
|
|
|
41
41
|
}
|
|
42
42
|
declare function RCNRMountainLogo({ href, className, }: RCNRMountainLogoProps): react_jsx_runtime.JSX.Element;
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
interface ReportIssueModalProps {
|
|
45
|
+
isOpen: boolean;
|
|
46
|
+
onClose: () => void;
|
|
47
|
+
toolName: string;
|
|
48
|
+
apiBaseUrl?: string;
|
|
49
|
+
userEmail?: string;
|
|
50
|
+
}
|
|
51
|
+
declare function ReportIssueModal({ isOpen, onClose, toolName, apiBaseUrl, userEmail, }: ReportIssueModalProps): react_jsx_runtime.JSX.Element | null;
|
|
52
|
+
|
|
53
|
+
interface RequestToolModalProps {
|
|
54
|
+
isOpen: boolean;
|
|
55
|
+
onClose: () => void;
|
|
56
|
+
toolName: string;
|
|
57
|
+
apiBaseUrl?: string;
|
|
58
|
+
userEmail?: string;
|
|
59
|
+
}
|
|
60
|
+
declare function RequestToolModal({ isOpen, onClose, toolName, apiBaseUrl, userEmail, }: RequestToolModalProps): react_jsx_runtime.JSX.Element | null;
|
|
61
|
+
|
|
62
|
+
export { RCNRFooter, type RCNRFooterProps, RCNRHeader, type RCNRHeaderProps, RCNRMountainLogo, RCNRSubNav, type RCNRSubNavProps, ReportIssueModal, RequestToolModal };
|
package/dist/index.js
CHANGED
|
@@ -23,7 +23,9 @@ __export(index_exports, {
|
|
|
23
23
|
RCNRFooter: () => RCNRFooter_default,
|
|
24
24
|
RCNRHeader: () => RCNRHeader_default,
|
|
25
25
|
RCNRMountainLogo: () => RCNRMountainLogo_default,
|
|
26
|
-
RCNRSubNav: () => RCNRSubNav_default
|
|
26
|
+
RCNRSubNav: () => RCNRSubNav_default,
|
|
27
|
+
ReportIssueModal: () => ReportIssueModal,
|
|
28
|
+
RequestToolModal: () => RequestToolModal
|
|
27
29
|
});
|
|
28
30
|
module.exports = __toCommonJS(index_exports);
|
|
29
31
|
|
|
@@ -251,11 +253,253 @@ function RCNRFooter({
|
|
|
251
253
|
) });
|
|
252
254
|
}
|
|
253
255
|
var RCNRFooter_default = RCNRFooter;
|
|
256
|
+
|
|
257
|
+
// src/ReportIssueModal.tsx
|
|
258
|
+
var import_react = require("react");
|
|
259
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
260
|
+
function ReportIssueModal({
|
|
261
|
+
isOpen,
|
|
262
|
+
onClose,
|
|
263
|
+
toolName,
|
|
264
|
+
apiBaseUrl = "https://api.rcnr.net",
|
|
265
|
+
userEmail
|
|
266
|
+
}) {
|
|
267
|
+
const [description, setDescription] = (0, import_react.useState)("");
|
|
268
|
+
const [submitting, setSubmitting] = (0, import_react.useState)(false);
|
|
269
|
+
const [submitted, setSubmitted] = (0, import_react.useState)(false);
|
|
270
|
+
const [error, setError] = (0, import_react.useState)("");
|
|
271
|
+
const handleClose = (0, import_react.useCallback)(() => {
|
|
272
|
+
setDescription("");
|
|
273
|
+
setError("");
|
|
274
|
+
setSubmitted(false);
|
|
275
|
+
onClose();
|
|
276
|
+
}, [onClose]);
|
|
277
|
+
(0, import_react.useEffect)(() => {
|
|
278
|
+
if (!isOpen) return;
|
|
279
|
+
const onKey = (e) => {
|
|
280
|
+
if (e.key === "Escape") handleClose();
|
|
281
|
+
};
|
|
282
|
+
document.addEventListener("keydown", onKey);
|
|
283
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
284
|
+
}, [isOpen, handleClose]);
|
|
285
|
+
if (!isOpen) return null;
|
|
286
|
+
const handleSubmit = async (e) => {
|
|
287
|
+
e.preventDefault();
|
|
288
|
+
if (!description.trim()) return;
|
|
289
|
+
setSubmitting(true);
|
|
290
|
+
setError("");
|
|
291
|
+
try {
|
|
292
|
+
const res = await fetch(`${apiBaseUrl}/api/feedback/report`, {
|
|
293
|
+
method: "POST",
|
|
294
|
+
headers: { "Content-Type": "application/json" },
|
|
295
|
+
body: JSON.stringify({ tool_name: toolName, description, user_email: userEmail })
|
|
296
|
+
});
|
|
297
|
+
if (res.ok) {
|
|
298
|
+
setSubmitted(true);
|
|
299
|
+
setTimeout(handleClose, 2e3);
|
|
300
|
+
} else {
|
|
301
|
+
setError("Something went wrong. Please try again.");
|
|
302
|
+
}
|
|
303
|
+
} catch {
|
|
304
|
+
setError("Could not send report. Check your connection.");
|
|
305
|
+
} finally {
|
|
306
|
+
setSubmitting(false);
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
310
|
+
"div",
|
|
311
|
+
{
|
|
312
|
+
className: "fixed inset-0 z-50 flex items-center justify-center p-4",
|
|
313
|
+
style: { background: "rgba(0,12,23,0.85)", backdropFilter: "blur(6px)" },
|
|
314
|
+
onClick: handleClose,
|
|
315
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
316
|
+
"div",
|
|
317
|
+
{
|
|
318
|
+
className: "relative glass-card rounded-2xl w-full max-w-md p-8",
|
|
319
|
+
style: { animation: "fadeIn 0.15s ease" },
|
|
320
|
+
onClick: (e) => e.stopPropagation(),
|
|
321
|
+
children: [
|
|
322
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
323
|
+
"button",
|
|
324
|
+
{
|
|
325
|
+
onClick: handleClose,
|
|
326
|
+
className: "absolute top-4 right-4 p-2 text-brand/50 hover:text-brand hover:bg-white/5 rounded-lg transition-colors",
|
|
327
|
+
"aria-label": "Close",
|
|
328
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M18 6L6 18M6 6l12 12" }) })
|
|
329
|
+
}
|
|
330
|
+
),
|
|
331
|
+
submitted ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "text-center py-6", children: [
|
|
332
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "w-12 h-12 rounded-full bg-emerald-500/15 flex items-center justify-center mx-auto mb-4", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", className: "text-emerald-400", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "20 6 9 17 4 12" }) }) }),
|
|
333
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-white font-semibold", children: "Report sent. We'll fix it fast." })
|
|
334
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
335
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { className: "text-xl font-bold text-white font-serif mb-1", children: "Report an Issue" }),
|
|
336
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-brand/50 text-sm mb-6", children: "Found a bug or something broken? Let us know." }),
|
|
337
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
338
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
|
|
339
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { className: "block text-xs font-semibold text-brand/60 uppercase tracking-wider mb-2", children: "Tool" }),
|
|
340
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
341
|
+
"input",
|
|
342
|
+
{
|
|
343
|
+
type: "text",
|
|
344
|
+
value: toolName,
|
|
345
|
+
disabled: true,
|
|
346
|
+
className: "w-full px-4 py-2.5 rounded-xl bg-white/5 border border-brand/10 text-brand/40 text-sm"
|
|
347
|
+
}
|
|
348
|
+
)
|
|
349
|
+
] }),
|
|
350
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
|
|
351
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { className: "block text-xs font-semibold text-brand/60 uppercase tracking-wider mb-2", children: "What went wrong?" }),
|
|
352
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
353
|
+
"textarea",
|
|
354
|
+
{
|
|
355
|
+
value: description,
|
|
356
|
+
onChange: (e) => setDescription(e.target.value),
|
|
357
|
+
placeholder: "Describe the issue...",
|
|
358
|
+
rows: 4,
|
|
359
|
+
required: true,
|
|
360
|
+
className: "w-full px-4 py-3 rounded-xl bg-white/5 border border-brand/15 text-white placeholder-brand/30 text-sm resize-none focus:outline-none focus:border-brand/40 transition-colors"
|
|
361
|
+
}
|
|
362
|
+
)
|
|
363
|
+
] }),
|
|
364
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-red-400 text-sm", children: error }),
|
|
365
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
366
|
+
"button",
|
|
367
|
+
{
|
|
368
|
+
type: "submit",
|
|
369
|
+
disabled: submitting || !description.trim(),
|
|
370
|
+
className: "btn-ice w-full py-3 text-sm font-semibold rounded-xl disabled:opacity-40 disabled:cursor-not-allowed",
|
|
371
|
+
children: submitting ? "Sending..." : "Submit Report"
|
|
372
|
+
}
|
|
373
|
+
)
|
|
374
|
+
] })
|
|
375
|
+
] })
|
|
376
|
+
]
|
|
377
|
+
}
|
|
378
|
+
)
|
|
379
|
+
}
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// src/RequestToolModal.tsx
|
|
384
|
+
var import_react2 = require("react");
|
|
385
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
386
|
+
function RequestToolModal({
|
|
387
|
+
isOpen,
|
|
388
|
+
onClose,
|
|
389
|
+
toolName,
|
|
390
|
+
apiBaseUrl = "https://api.rcnr.net",
|
|
391
|
+
userEmail
|
|
392
|
+
}) {
|
|
393
|
+
const [description, setDescription] = (0, import_react2.useState)("");
|
|
394
|
+
const [submitting, setSubmitting] = (0, import_react2.useState)(false);
|
|
395
|
+
const [submitted, setSubmitted] = (0, import_react2.useState)(false);
|
|
396
|
+
const [error, setError] = (0, import_react2.useState)("");
|
|
397
|
+
const handleClose = (0, import_react2.useCallback)(() => {
|
|
398
|
+
setDescription("");
|
|
399
|
+
setError("");
|
|
400
|
+
setSubmitted(false);
|
|
401
|
+
onClose();
|
|
402
|
+
}, [onClose]);
|
|
403
|
+
(0, import_react2.useEffect)(() => {
|
|
404
|
+
if (!isOpen) return;
|
|
405
|
+
const onKey = (e) => {
|
|
406
|
+
if (e.key === "Escape") handleClose();
|
|
407
|
+
};
|
|
408
|
+
document.addEventListener("keydown", onKey);
|
|
409
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
410
|
+
}, [isOpen, handleClose]);
|
|
411
|
+
if (!isOpen) return null;
|
|
412
|
+
const handleSubmit = async (e) => {
|
|
413
|
+
e.preventDefault();
|
|
414
|
+
if (!description.trim()) return;
|
|
415
|
+
setSubmitting(true);
|
|
416
|
+
setError("");
|
|
417
|
+
try {
|
|
418
|
+
const res = await fetch(`${apiBaseUrl}/api/feedback/request`, {
|
|
419
|
+
method: "POST",
|
|
420
|
+
headers: { "Content-Type": "application/json" },
|
|
421
|
+
body: JSON.stringify({ tool_name: toolName, description, user_email: userEmail })
|
|
422
|
+
});
|
|
423
|
+
if (res.ok) {
|
|
424
|
+
setSubmitted(true);
|
|
425
|
+
setTimeout(handleClose, 2e3);
|
|
426
|
+
} else {
|
|
427
|
+
setError("Something went wrong. Please try again.");
|
|
428
|
+
}
|
|
429
|
+
} catch {
|
|
430
|
+
setError("Could not send request. Check your connection.");
|
|
431
|
+
} finally {
|
|
432
|
+
setSubmitting(false);
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
436
|
+
"div",
|
|
437
|
+
{
|
|
438
|
+
className: "fixed inset-0 z-50 flex items-center justify-center p-4",
|
|
439
|
+
style: { background: "rgba(0,12,23,0.85)", backdropFilter: "blur(6px)" },
|
|
440
|
+
onClick: handleClose,
|
|
441
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
442
|
+
"div",
|
|
443
|
+
{
|
|
444
|
+
className: "relative glass-card rounded-2xl w-full max-w-md p-8",
|
|
445
|
+
style: { animation: "fadeIn 0.15s ease" },
|
|
446
|
+
onClick: (e) => e.stopPropagation(),
|
|
447
|
+
children: [
|
|
448
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
449
|
+
"button",
|
|
450
|
+
{
|
|
451
|
+
onClick: handleClose,
|
|
452
|
+
className: "absolute top-4 right-4 p-2 text-brand/50 hover:text-brand hover:bg-white/5 rounded-lg transition-colors",
|
|
453
|
+
"aria-label": "Close",
|
|
454
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M18 6L6 18M6 6l12 12" }) })
|
|
455
|
+
}
|
|
456
|
+
),
|
|
457
|
+
submitted ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "text-center py-6", children: [
|
|
458
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "w-12 h-12 rounded-full bg-brand/10 flex items-center justify-center mx-auto mb-4", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", className: "text-brand", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("polyline", { points: "20 6 9 17 4 12" }) }) }),
|
|
459
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-white font-semibold", children: "Request received. Thanks!" })
|
|
460
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
461
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h2", { className: "text-xl font-bold text-white font-serif mb-1", children: "Request a Tool" }),
|
|
462
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-brand/50 text-sm mb-6", children: "Have an idea for something we should build?" }),
|
|
463
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
464
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
|
|
465
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "block text-xs font-semibold text-brand/60 uppercase tracking-wider mb-2", children: "Describe what you need" }),
|
|
466
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
467
|
+
"textarea",
|
|
468
|
+
{
|
|
469
|
+
value: description,
|
|
470
|
+
onChange: (e) => setDescription(e.target.value),
|
|
471
|
+
placeholder: "What problem would this tool solve? What would it do?",
|
|
472
|
+
rows: 5,
|
|
473
|
+
required: true,
|
|
474
|
+
className: "w-full px-4 py-3 rounded-xl bg-white/5 border border-brand/15 text-white placeholder-brand/30 text-sm resize-none focus:outline-none focus:border-brand/40 transition-colors"
|
|
475
|
+
}
|
|
476
|
+
)
|
|
477
|
+
] }),
|
|
478
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-red-400 text-sm", children: error }),
|
|
479
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
480
|
+
"button",
|
|
481
|
+
{
|
|
482
|
+
type: "submit",
|
|
483
|
+
disabled: submitting || !description.trim(),
|
|
484
|
+
className: "btn-ice w-full py-3 text-sm font-semibold rounded-xl disabled:opacity-40 disabled:cursor-not-allowed",
|
|
485
|
+
children: submitting ? "Sending..." : "Submit Request"
|
|
486
|
+
}
|
|
487
|
+
)
|
|
488
|
+
] })
|
|
489
|
+
] })
|
|
490
|
+
]
|
|
491
|
+
}
|
|
492
|
+
)
|
|
493
|
+
}
|
|
494
|
+
);
|
|
495
|
+
}
|
|
254
496
|
// Annotate the CommonJS export names for ESM import in node:
|
|
255
497
|
0 && (module.exports = {
|
|
256
498
|
RCNRFooter,
|
|
257
499
|
RCNRHeader,
|
|
258
500
|
RCNRMountainLogo,
|
|
259
|
-
RCNRSubNav
|
|
501
|
+
RCNRSubNav,
|
|
502
|
+
ReportIssueModal,
|
|
503
|
+
RequestToolModal
|
|
260
504
|
});
|
|
261
505
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/RCNRMountainLogo.tsx","../src/RCNRHeader.tsx","../src/RCNRSubNav.tsx","../src/RCNRFooter.tsx"],"sourcesContent":["export { default as RCNRHeader } from './RCNRHeader'\r\nexport { default as RCNRSubNav } from './RCNRSubNav'\r\nexport { default as RCNRFooter } from './RCNRFooter'\r\nexport { default as RCNRMountainLogo } from './RCNRMountainLogo'\r\n\r\nexport type {\r\n RCNRHeaderProps,\r\n RCNRSubNavProps,\r\n RCNRFooterProps,\r\n} from './types'\r\n","interface RCNRMountainLogoProps {\r\n href?: string\r\n className?: string\r\n}\r\n\r\nfunction RCNRMountainLogo({\r\n href = 'https://teacher.rcnr.net',\r\n className = '',\r\n}: RCNRMountainLogoProps) {\r\n return (\r\n <a\r\n href={href}\r\n className={`flex items-center justify-center w-10 h-10 rounded-lg bg-brand/10 hover:bg-brand/20 transition-colors ${className}`}\r\n title=\"Back to Dashboard\"\r\n >\r\n <svg\r\n width=\"28\"\r\n height=\"24\"\r\n viewBox=\"0 0 120 100\"\r\n fill=\"none\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n >\r\n <path\r\n d=\"M10,90l26.48-39.76h.31s4.54,5.02,4.54,5.02l.32-.16,23.06-37.14,17.34,28.63,3.65-4.61,30.22,48.03h-8.43c-7.17-12.26-15.51-24.35-23.06-36.26-1.43.52-2.03,3.6-3.49,3.97l-16.31-25.93-21.87,34.36-5.09-5.01-19.48,28.87h-8.19Z\"\r\n fill=\"#99D9D9\"\r\n />\r\n <path\r\n d=\"M89.84,90h-5.73c-6.34-8.18-12.93-16.89-19.64-24.65-6.66,8.19-13.04,16.6-19.89,24.65h-5.81l25.69-39.76,25.37,39.76Z\"\r\n fill=\"#99D9D9\"\r\n />\r\n </svg>\r\n </a>\r\n )\r\n}\r\n\r\nexport default RCNRMountainLogo\r\n","import type { ReactNode } from 'react'\r\nimport type { RCNRHeaderProps } from './types'\r\nimport RCNRMountainLogo from './RCNRMountainLogo'\r\n\r\nfunction NavButton({\r\n onClick,\r\n icon,\r\n label,\r\n title,\r\n}: {\r\n onClick: () => void\r\n icon: ReactNode\r\n label: string\r\n title?: string\r\n}) {\r\n return (\r\n <button\r\n onClick={onClick}\r\n className=\"flex items-center gap-2 px-3 py-2 text-brand/70 hover:text-brand hover:bg-brand/5 rounded-lg transition-colors\"\r\n title={title ?? label}\r\n >\r\n {icon}\r\n <span className=\"hidden md:inline text-sm\">{label}</span>\r\n </button>\r\n )\r\n}\r\n\r\nconst defaultReportIssue = () =>\r\n window.open('mailto:support@rcnr.net?subject=Bug%20Report', '_blank')\r\n\r\nconst defaultRequestTool = () =>\r\n window.open('mailto:support@rcnr.net?subject=Feature%20Request', '_blank')\r\n\r\nfunction RCNRHeader({\r\n toolName,\r\n dashboardUrl = 'https://teacher.rcnr.net',\r\n extraNavItems,\r\n userAvatar,\r\n onHowItWorks,\r\n onReportIssue = defaultReportIssue,\r\n onRequestTool = defaultRequestTool,\r\n}: RCNRHeaderProps) {\r\n return (\r\n <header className=\"glass-card border-b border-brand/15 px-6 py-4\">\r\n <div className=\"flex items-center justify-between\">\r\n {/* Left: Logo + Tool Name */}\r\n <div className=\"flex items-center gap-4\">\r\n <RCNRMountainLogo href={dashboardUrl} />\r\n <span className=\"text-xl font-serif text-brand\">{toolName}</span>\r\n </div>\r\n\r\n {/* Right: Nav Actions */}\r\n <div className=\"flex items-center gap-2\">\r\n {/* Tool-specific nav items first */}\r\n {extraNavItems?.map((item) => (\r\n <NavButton\r\n key={item.label}\r\n onClick={item.onClick}\r\n icon={item.icon}\r\n label={item.label}\r\n />\r\n ))}\r\n\r\n {/* Standard nav items in fixed order */}\r\n {onHowItWorks && (\r\n <NavButton\r\n onClick={onHowItWorks}\r\n label=\"How It Works\"\r\n icon={\r\n <svg\r\n className=\"w-[18px] h-[18px]\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n strokeWidth={2}\r\n >\r\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\r\n <path\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n d=\"M9.09 9a3 3 0 015.83 1c0 2-3 3-3 3\"\r\n />\r\n <line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\" />\r\n </svg>\r\n }\r\n />\r\n )}\r\n\r\n <NavButton\r\n onClick={onReportIssue}\r\n label=\"Report Issue\"\r\n icon={\r\n <svg\r\n className=\"w-[18px] h-[18px]\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n strokeWidth={2}\r\n >\r\n <path\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\r\n />\r\n </svg>\r\n }\r\n />\r\n\r\n <NavButton\r\n onClick={onRequestTool}\r\n label=\"Request Tool\"\r\n icon={\r\n <svg\r\n className=\"w-[18px] h-[18px]\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n strokeWidth={2}\r\n >\r\n <path\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n d=\"M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z\"\r\n />\r\n </svg>\r\n }\r\n />\r\n\r\n {userAvatar}\r\n </div>\r\n </div>\r\n </header>\r\n )\r\n}\r\n\r\nexport default RCNRHeader\r\n","import type { RCNRSubNavProps } from './types'\r\n\r\nfunction RCNRSubNav({ tabs }: RCNRSubNavProps) {\r\n return (\r\n <nav className=\"flex items-center gap-1 px-6 py-2 border-b border-brand/10 bg-surface/50\">\r\n {tabs.map((tab) => {\r\n const isWarning = tab.variant === 'warning'\r\n const activeClass = isWarning\r\n ? 'bg-warning/10 text-warning border border-warning/20'\r\n : 'bg-brand/15 text-brand'\r\n const inactiveClass = isWarning\r\n ? 'text-warning/70 hover:text-warning hover:bg-warning/5 border border-warning/10'\r\n : 'text-brand/50 hover:text-brand hover:bg-brand/5'\r\n\r\n const className = `flex items-center gap-2 px-4 py-2 rounded-lg text-sm transition-colors ${\r\n tab.active ? activeClass : inactiveClass\r\n }`\r\n\r\n if (tab.href) {\r\n return (\r\n <a key={tab.label} href={tab.href} className={className}>\r\n {tab.icon}\r\n <span>{tab.label}</span>\r\n </a>\r\n )\r\n }\r\n\r\n return (\r\n <button\r\n key={tab.label}\r\n onClick={tab.onClick}\r\n className={className}\r\n >\r\n {tab.icon}\r\n <span>{tab.label}</span>\r\n </button>\r\n )\r\n })}\r\n </nav>\r\n )\r\n}\r\n\r\nexport default RCNRSubNav\r\n","import type { RCNRFooterProps } from './types'\r\n\r\nfunction RCNRFooter({\r\n toolName,\r\n linkUrl = 'https://rcnr.net',\r\n}: RCNRFooterProps) {\r\n return (\r\n <footer className=\"mt-auto py-4 text-center text-sm text-brand-dark/50\">\r\n <a\r\n href={linkUrl}\r\n className=\"hover:text-brand transition-colors\"\r\n >\r\n {toolName} — Part of the RCNR Teacher Toolbox\r\n </a>\r\n </footer>\r\n )\r\n}\r\n\r\nexport default RCNRFooter\r\n"],"mappings":"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","names":["import_jsx_runtime","import_jsx_runtime","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/RCNRMountainLogo.tsx","../src/RCNRHeader.tsx","../src/RCNRSubNav.tsx","../src/RCNRFooter.tsx","../src/ReportIssueModal.tsx","../src/RequestToolModal.tsx"],"sourcesContent":["export { default as RCNRHeader } from './RCNRHeader'\r\nexport { default as RCNRSubNav } from './RCNRSubNav'\r\nexport { default as RCNRFooter } from './RCNRFooter'\r\nexport { default as RCNRMountainLogo } from './RCNRMountainLogo'\r\nexport { ReportIssueModal } from './ReportIssueModal'\r\nexport { RequestToolModal } from './RequestToolModal'\r\n\r\nexport type {\r\n RCNRHeaderProps,\r\n RCNRSubNavProps,\r\n RCNRFooterProps,\r\n} from './types'\r\n","interface RCNRMountainLogoProps {\r\n href?: string\r\n className?: string\r\n}\r\n\r\nfunction RCNRMountainLogo({\r\n href = 'https://teacher.rcnr.net',\r\n className = '',\r\n}: RCNRMountainLogoProps) {\r\n return (\r\n <a\r\n href={href}\r\n className={`flex items-center justify-center w-10 h-10 rounded-lg bg-brand/10 hover:bg-brand/20 transition-colors ${className}`}\r\n title=\"Back to Dashboard\"\r\n >\r\n <svg\r\n width=\"28\"\r\n height=\"24\"\r\n viewBox=\"0 0 120 100\"\r\n fill=\"none\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n >\r\n <path\r\n d=\"M10,90l26.48-39.76h.31s4.54,5.02,4.54,5.02l.32-.16,23.06-37.14,17.34,28.63,3.65-4.61,30.22,48.03h-8.43c-7.17-12.26-15.51-24.35-23.06-36.26-1.43.52-2.03,3.6-3.49,3.97l-16.31-25.93-21.87,34.36-5.09-5.01-19.48,28.87h-8.19Z\"\r\n fill=\"#99D9D9\"\r\n />\r\n <path\r\n d=\"M89.84,90h-5.73c-6.34-8.18-12.93-16.89-19.64-24.65-6.66,8.19-13.04,16.6-19.89,24.65h-5.81l25.69-39.76,25.37,39.76Z\"\r\n fill=\"#99D9D9\"\r\n />\r\n </svg>\r\n </a>\r\n )\r\n}\r\n\r\nexport default RCNRMountainLogo\r\n","import type { ReactNode } from 'react'\r\nimport type { RCNRHeaderProps } from './types'\r\nimport RCNRMountainLogo from './RCNRMountainLogo'\r\n\r\nfunction NavButton({\r\n onClick,\r\n icon,\r\n label,\r\n title,\r\n}: {\r\n onClick: () => void\r\n icon: ReactNode\r\n label: string\r\n title?: string\r\n}) {\r\n return (\r\n <button\r\n onClick={onClick}\r\n className=\"flex items-center gap-2 px-3 py-2 text-brand/70 hover:text-brand hover:bg-brand/5 rounded-lg transition-colors\"\r\n title={title ?? label}\r\n >\r\n {icon}\r\n <span className=\"hidden md:inline text-sm\">{label}</span>\r\n </button>\r\n )\r\n}\r\n\r\nconst defaultReportIssue = () =>\r\n window.open('mailto:support@rcnr.net?subject=Bug%20Report', '_blank')\r\n\r\nconst defaultRequestTool = () =>\r\n window.open('mailto:support@rcnr.net?subject=Feature%20Request', '_blank')\r\n\r\nfunction RCNRHeader({\r\n toolName,\r\n dashboardUrl = 'https://teacher.rcnr.net',\r\n extraNavItems,\r\n userAvatar,\r\n onHowItWorks,\r\n onReportIssue = defaultReportIssue,\r\n onRequestTool = defaultRequestTool,\r\n}: RCNRHeaderProps) {\r\n return (\r\n <header className=\"glass-card border-b border-brand/15 px-6 py-4\">\r\n <div className=\"flex items-center justify-between\">\r\n {/* Left: Logo + Tool Name */}\r\n <div className=\"flex items-center gap-4\">\r\n <RCNRMountainLogo href={dashboardUrl} />\r\n <span className=\"text-xl font-serif text-brand\">{toolName}</span>\r\n </div>\r\n\r\n {/* Right: Nav Actions */}\r\n <div className=\"flex items-center gap-2\">\r\n {/* Tool-specific nav items first */}\r\n {extraNavItems?.map((item) => (\r\n <NavButton\r\n key={item.label}\r\n onClick={item.onClick}\r\n icon={item.icon}\r\n label={item.label}\r\n />\r\n ))}\r\n\r\n {/* Standard nav items in fixed order */}\r\n {onHowItWorks && (\r\n <NavButton\r\n onClick={onHowItWorks}\r\n label=\"How It Works\"\r\n icon={\r\n <svg\r\n className=\"w-[18px] h-[18px]\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n strokeWidth={2}\r\n >\r\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\r\n <path\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n d=\"M9.09 9a3 3 0 015.83 1c0 2-3 3-3 3\"\r\n />\r\n <line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\" />\r\n </svg>\r\n }\r\n />\r\n )}\r\n\r\n <NavButton\r\n onClick={onReportIssue}\r\n label=\"Report Issue\"\r\n icon={\r\n <svg\r\n className=\"w-[18px] h-[18px]\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n strokeWidth={2}\r\n >\r\n <path\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\r\n />\r\n </svg>\r\n }\r\n />\r\n\r\n <NavButton\r\n onClick={onRequestTool}\r\n label=\"Request Tool\"\r\n icon={\r\n <svg\r\n className=\"w-[18px] h-[18px]\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n strokeWidth={2}\r\n >\r\n <path\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n d=\"M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z\"\r\n />\r\n </svg>\r\n }\r\n />\r\n\r\n {userAvatar}\r\n </div>\r\n </div>\r\n </header>\r\n )\r\n}\r\n\r\nexport default RCNRHeader\r\n","import type { RCNRSubNavProps } from './types'\r\n\r\nfunction RCNRSubNav({ tabs }: RCNRSubNavProps) {\r\n return (\r\n <nav className=\"flex items-center gap-1 px-6 py-2 border-b border-brand/10 bg-surface/50\">\r\n {tabs.map((tab) => {\r\n const isWarning = tab.variant === 'warning'\r\n const activeClass = isWarning\r\n ? 'bg-warning/10 text-warning border border-warning/20'\r\n : 'bg-brand/15 text-brand'\r\n const inactiveClass = isWarning\r\n ? 'text-warning/70 hover:text-warning hover:bg-warning/5 border border-warning/10'\r\n : 'text-brand/50 hover:text-brand hover:bg-brand/5'\r\n\r\n const className = `flex items-center gap-2 px-4 py-2 rounded-lg text-sm transition-colors ${\r\n tab.active ? activeClass : inactiveClass\r\n }`\r\n\r\n if (tab.href) {\r\n return (\r\n <a key={tab.label} href={tab.href} className={className}>\r\n {tab.icon}\r\n <span>{tab.label}</span>\r\n </a>\r\n )\r\n }\r\n\r\n return (\r\n <button\r\n key={tab.label}\r\n onClick={tab.onClick}\r\n className={className}\r\n >\r\n {tab.icon}\r\n <span>{tab.label}</span>\r\n </button>\r\n )\r\n })}\r\n </nav>\r\n )\r\n}\r\n\r\nexport default RCNRSubNav\r\n","import type { RCNRFooterProps } from './types'\r\n\r\nfunction RCNRFooter({\r\n toolName,\r\n linkUrl = 'https://rcnr.net',\r\n}: RCNRFooterProps) {\r\n return (\r\n <footer className=\"mt-auto py-4 text-center text-sm text-brand-dark/50\">\r\n <a\r\n href={linkUrl}\r\n className=\"hover:text-brand transition-colors\"\r\n >\r\n {toolName} — Part of the RCNR Teacher Toolbox\r\n </a>\r\n </footer>\r\n )\r\n}\r\n\r\nexport default RCNRFooter\r\n","import { useState, useEffect, useCallback } from 'react'\n\ninterface ReportIssueModalProps {\n isOpen: boolean\n onClose: () => void\n toolName: string\n apiBaseUrl?: string\n userEmail?: string\n}\n\nexport function ReportIssueModal({\n isOpen,\n onClose,\n toolName,\n apiBaseUrl = 'https://api.rcnr.net',\n userEmail,\n}: ReportIssueModalProps) {\n const [description, setDescription] = useState('')\n const [submitting, setSubmitting] = useState(false)\n const [submitted, setSubmitted] = useState(false)\n const [error, setError] = useState('')\n\n const handleClose = useCallback(() => {\n setDescription('')\n setError('')\n setSubmitted(false)\n onClose()\n }, [onClose])\n\n useEffect(() => {\n if (!isOpen) return\n const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') handleClose() }\n document.addEventListener('keydown', onKey)\n return () => document.removeEventListener('keydown', onKey)\n }, [isOpen, handleClose])\n\n if (!isOpen) return null\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault()\n if (!description.trim()) return\n setSubmitting(true)\n setError('')\n try {\n const res = await fetch(`${apiBaseUrl}/api/feedback/report`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ tool_name: toolName, description, user_email: userEmail }),\n })\n if (res.ok) {\n setSubmitted(true)\n setTimeout(handleClose, 2000)\n } else {\n setError('Something went wrong. Please try again.')\n }\n } catch {\n setError('Could not send report. Check your connection.')\n } finally {\n setSubmitting(false)\n }\n }\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center p-4\"\n style={{ background: 'rgba(0,12,23,0.85)', backdropFilter: 'blur(6px)' }}\n onClick={handleClose}\n >\n <div\n className=\"relative glass-card rounded-2xl w-full max-w-md p-8\"\n style={{ animation: 'fadeIn 0.15s ease' }}\n onClick={(e) => e.stopPropagation()}\n >\n <button\n onClick={handleClose}\n className=\"absolute top-4 right-4 p-2 text-brand/50 hover:text-brand hover:bg-white/5 rounded-lg transition-colors\"\n aria-label=\"Close\"\n >\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M18 6L6 18M6 6l12 12\" />\n </svg>\n </button>\n\n {submitted ? (\n <div className=\"text-center py-6\">\n <div className=\"w-12 h-12 rounded-full bg-emerald-500/15 flex items-center justify-center mx-auto mb-4\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" className=\"text-emerald-400\">\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n </div>\n <p className=\"text-white font-semibold\">Report sent. We'll fix it fast.</p>\n </div>\n ) : (\n <>\n <h2 className=\"text-xl font-bold text-white font-serif mb-1\">Report an Issue</h2>\n <p className=\"text-brand/50 text-sm mb-6\">Found a bug or something broken? Let us know.</p>\n\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n <div>\n <label className=\"block text-xs font-semibold text-brand/60 uppercase tracking-wider mb-2\">Tool</label>\n <input\n type=\"text\"\n value={toolName}\n disabled\n className=\"w-full px-4 py-2.5 rounded-xl bg-white/5 border border-brand/10 text-brand/40 text-sm\"\n />\n </div>\n\n <div>\n <label className=\"block text-xs font-semibold text-brand/60 uppercase tracking-wider mb-2\">\n What went wrong?\n </label>\n <textarea\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"Describe the issue...\"\n rows={4}\n required\n className=\"w-full px-4 py-3 rounded-xl bg-white/5 border border-brand/15 text-white placeholder-brand/30 text-sm resize-none focus:outline-none focus:border-brand/40 transition-colors\"\n />\n </div>\n\n {error && <p className=\"text-red-400 text-sm\">{error}</p>}\n\n <button\n type=\"submit\"\n disabled={submitting || !description.trim()}\n className=\"btn-ice w-full py-3 text-sm font-semibold rounded-xl disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {submitting ? 'Sending...' : 'Submit Report'}\n </button>\n </form>\n </>\n )}\n </div>\n </div>\n )\n}\n","import { useState, useEffect, useCallback } from 'react'\n\ninterface RequestToolModalProps {\n isOpen: boolean\n onClose: () => void\n toolName: string\n apiBaseUrl?: string\n userEmail?: string\n}\n\nexport function RequestToolModal({\n isOpen,\n onClose,\n toolName,\n apiBaseUrl = 'https://api.rcnr.net',\n userEmail,\n}: RequestToolModalProps) {\n const [description, setDescription] = useState('')\n const [submitting, setSubmitting] = useState(false)\n const [submitted, setSubmitted] = useState(false)\n const [error, setError] = useState('')\n\n const handleClose = useCallback(() => {\n setDescription('')\n setError('')\n setSubmitted(false)\n onClose()\n }, [onClose])\n\n useEffect(() => {\n if (!isOpen) return\n const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') handleClose() }\n document.addEventListener('keydown', onKey)\n return () => document.removeEventListener('keydown', onKey)\n }, [isOpen, handleClose])\n\n if (!isOpen) return null\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault()\n if (!description.trim()) return\n setSubmitting(true)\n setError('')\n try {\n const res = await fetch(`${apiBaseUrl}/api/feedback/request`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ tool_name: toolName, description, user_email: userEmail }),\n })\n if (res.ok) {\n setSubmitted(true)\n setTimeout(handleClose, 2000)\n } else {\n setError('Something went wrong. Please try again.')\n }\n } catch {\n setError('Could not send request. Check your connection.')\n } finally {\n setSubmitting(false)\n }\n }\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center p-4\"\n style={{ background: 'rgba(0,12,23,0.85)', backdropFilter: 'blur(6px)' }}\n onClick={handleClose}\n >\n <div\n className=\"relative glass-card rounded-2xl w-full max-w-md p-8\"\n style={{ animation: 'fadeIn 0.15s ease' }}\n onClick={(e) => e.stopPropagation()}\n >\n <button\n onClick={handleClose}\n className=\"absolute top-4 right-4 p-2 text-brand/50 hover:text-brand hover:bg-white/5 rounded-lg transition-colors\"\n aria-label=\"Close\"\n >\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M18 6L6 18M6 6l12 12\" />\n </svg>\n </button>\n\n {submitted ? (\n <div className=\"text-center py-6\">\n <div className=\"w-12 h-12 rounded-full bg-brand/10 flex items-center justify-center mx-auto mb-4\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" className=\"text-brand\">\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n </div>\n <p className=\"text-white font-semibold\">Request received. Thanks!</p>\n </div>\n ) : (\n <>\n <h2 className=\"text-xl font-bold text-white font-serif mb-1\">Request a Tool</h2>\n <p className=\"text-brand/50 text-sm mb-6\">Have an idea for something we should build?</p>\n\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n <div>\n <label className=\"block text-xs font-semibold text-brand/60 uppercase tracking-wider mb-2\">\n Describe what you need\n </label>\n <textarea\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"What problem would this tool solve? What would it do?\"\n rows={5}\n required\n className=\"w-full px-4 py-3 rounded-xl bg-white/5 border border-brand/15 text-white placeholder-brand/30 text-sm resize-none focus:outline-none focus:border-brand/40 transition-colors\"\n />\n </div>\n\n {error && <p className=\"text-red-400 text-sm\">{error}</p>}\n\n <button\n type=\"submit\"\n disabled={submitting || !description.trim()}\n className=\"btn-ice w-full py-3 text-sm font-semibold rounded-xl disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {submitting ? 'Sending...' : 'Submit Request'}\n </button>\n </form>\n </>\n )}\n </div>\n </div>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACeM;AAVN,SAAS,iBAAiB;AAAA,EACxB,OAAO;AAAA,EACP,YAAY;AACd,GAA0B;AACxB,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,WAAW,yGAAyG,SAAS;AAAA,MAC7H,OAAM;AAAA,MAEN;AAAA,QAAC;AAAA;AAAA,UACC,OAAM;AAAA,UACN,QAAO;AAAA,UACP,SAAQ;AAAA,UACR,MAAK;AAAA,UACL,OAAM;AAAA,UAEN;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,GAAE;AAAA,gBACF,MAAK;AAAA;AAAA,YACP;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,GAAE;AAAA,gBACF,MAAK;AAAA;AAAA,YACP;AAAA;AAAA;AAAA,MACF;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,2BAAQ;;;ACnBX,IAAAA,sBAAA;AAZJ,SAAS,UAAU;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,WAAU;AAAA,MACV,OAAO,SAAS;AAAA,MAEf;AAAA;AAAA,QACD,6CAAC,UAAK,WAAU,4BAA4B,iBAAM;AAAA;AAAA;AAAA,EACpD;AAEJ;AAEA,IAAM,qBAAqB,MACzB,OAAO,KAAK,gDAAgD,QAAQ;AAEtE,IAAM,qBAAqB,MACzB,OAAO,KAAK,qDAAqD,QAAQ;AAE3E,SAAS,WAAW;AAAA,EAClB;AAAA,EACA,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB,gBAAgB;AAClB,GAAoB;AAClB,SACE,6CAAC,YAAO,WAAU,iDAChB,wDAAC,SAAI,WAAU,qCAEb;AAAA,kDAAC,SAAI,WAAU,2BACb;AAAA,mDAAC,4BAAiB,MAAM,cAAc;AAAA,MACtC,6CAAC,UAAK,WAAU,iCAAiC,oBAAS;AAAA,OAC5D;AAAA,IAGA,8CAAC,SAAI,WAAU,2BAEZ;AAAA,qBAAe,IAAI,CAAC,SACnB;AAAA,QAAC;AAAA;AAAA,UAEC,SAAS,KAAK;AAAA,UACd,MAAM,KAAK;AAAA,UACX,OAAO,KAAK;AAAA;AAAA,QAHP,KAAK;AAAA,MAIZ,CACD;AAAA,MAGA,gBACC;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,OAAM;AAAA,UACN,MACE;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,QAAO;AAAA,cACP,aAAa;AAAA,cAEb;AAAA,6DAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,MAAK;AAAA,gBAC/B;AAAA,kBAAC;AAAA;AAAA,oBACC,eAAc;AAAA,oBACd,gBAAe;AAAA,oBACf,GAAE;AAAA;AAAA,gBACJ;AAAA,gBACA,6CAAC,UAAK,IAAG,MAAK,IAAG,MAAK,IAAG,SAAQ,IAAG,MAAK;AAAA;AAAA;AAAA,UAC3C;AAAA;AAAA,MAEJ;AAAA,MAGF;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,OAAM;AAAA,UACN,MACE;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,QAAO;AAAA,cACP,aAAa;AAAA,cAEb;AAAA,gBAAC;AAAA;AAAA,kBACC,eAAc;AAAA,kBACd,gBAAe;AAAA,kBACf,GAAE;AAAA;AAAA,cACJ;AAAA;AAAA,UACF;AAAA;AAAA,MAEJ;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,OAAM;AAAA,UACN,MACE;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,QAAO;AAAA,cACP,aAAa;AAAA,cAEb;AAAA,gBAAC;AAAA;AAAA,kBACC,eAAc;AAAA,kBACd,gBAAe;AAAA,kBACf,GAAE;AAAA;AAAA,cACJ;AAAA;AAAA,UACF;AAAA;AAAA,MAEJ;AAAA,MAEC;AAAA,OACH;AAAA,KACF,GACF;AAEJ;AAEA,IAAO,qBAAQ;;;ACnHH,IAAAC,sBAAA;AAlBZ,SAAS,WAAW,EAAE,KAAK,GAAoB;AAC7C,SACE,6CAAC,SAAI,WAAU,4EACZ,eAAK,IAAI,CAAC,QAAQ;AACjB,UAAM,YAAY,IAAI,YAAY;AAClC,UAAM,cAAc,YAChB,wDACA;AACJ,UAAM,gBAAgB,YAClB,mFACA;AAEJ,UAAM,YAAY,0EAChB,IAAI,SAAS,cAAc,aAC7B;AAEA,QAAI,IAAI,MAAM;AACZ,aACE,8CAAC,OAAkB,MAAM,IAAI,MAAM,WAChC;AAAA,YAAI;AAAA,QACL,6CAAC,UAAM,cAAI,OAAM;AAAA,WAFX,IAAI,KAGZ;AAAA,IAEJ;AAEA,WACE;AAAA,MAAC;AAAA;AAAA,QAEC,SAAS,IAAI;AAAA,QACb;AAAA,QAEC;AAAA,cAAI;AAAA,UACL,6CAAC,UAAM,cAAI,OAAM;AAAA;AAAA;AAAA,MALZ,IAAI;AAAA,IAMX;AAAA,EAEJ,CAAC,GACH;AAEJ;AAEA,IAAO,qBAAQ;;;ACnCX,IAAAC,sBAAA;AALJ,SAAS,WAAW;AAAA,EAClB;AAAA,EACA,UAAU;AACZ,GAAoB;AAClB,SACE,6CAAC,YAAO,WAAU,uDAChB;AAAA,IAAC;AAAA;AAAA,MACC,MAAM;AAAA,MACN,WAAU;AAAA,MAET;AAAA;AAAA,QAAS;AAAA;AAAA;AAAA,EACZ,GACF;AAEJ;AAEA,IAAO,qBAAQ;;;AClBf,mBAAiD;AA+ErC,IAAAC,sBAAA;AArEL,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb;AACF,GAA0B;AACxB,QAAM,CAAC,aAAa,cAAc,QAAI,uBAAS,EAAE;AACjD,QAAM,CAAC,YAAY,aAAa,QAAI,uBAAS,KAAK;AAClD,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAS,KAAK;AAChD,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAS,EAAE;AAErC,QAAM,kBAAc,0BAAY,MAAM;AACpC,mBAAe,EAAE;AACjB,aAAS,EAAE;AACX,iBAAa,KAAK;AAClB,YAAQ;AAAA,EACV,GAAG,CAAC,OAAO,CAAC;AAEZ,8BAAU,MAAM;AACd,QAAI,CAAC,OAAQ;AACb,UAAM,QAAQ,CAAC,MAAqB;AAAE,UAAI,EAAE,QAAQ,SAAU,aAAY;AAAA,IAAE;AAC5E,aAAS,iBAAiB,WAAW,KAAK;AAC1C,WAAO,MAAM,SAAS,oBAAoB,WAAW,KAAK;AAAA,EAC5D,GAAG,CAAC,QAAQ,WAAW,CAAC;AAExB,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,eAAe,OAAO,MAAuB;AACjD,MAAE,eAAe;AACjB,QAAI,CAAC,YAAY,KAAK,EAAG;AACzB,kBAAc,IAAI;AAClB,aAAS,EAAE;AACX,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,UAAU,wBAAwB;AAAA,QAC3D,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,WAAW,UAAU,aAAa,YAAY,UAAU,CAAC;AAAA,MAClF,CAAC;AACD,UAAI,IAAI,IAAI;AACV,qBAAa,IAAI;AACjB,mBAAW,aAAa,GAAI;AAAA,MAC9B,OAAO;AACL,iBAAS,yCAAyC;AAAA,MACpD;AAAA,IACF,QAAQ;AACN,eAAS,+CAA+C;AAAA,IAC1D,UAAE;AACA,oBAAc,KAAK;AAAA,IACrB;AAAA,EACF;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,OAAO,EAAE,YAAY,sBAAsB,gBAAgB,YAAY;AAAA,MACvE,SAAS;AAAA,MAET;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,OAAO,EAAE,WAAW,oBAAoB;AAAA,UACxC,SAAS,CAAC,MAAM,EAAE,gBAAgB;AAAA,UAElC;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,SAAS;AAAA,gBACT,WAAU;AAAA,gBACV,cAAW;AAAA,gBAEX,uDAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAC5F,uDAAC,UAAK,GAAE,wBAAuB,GACjC;AAAA;AAAA,YACF;AAAA,YAEC,YACC,8CAAC,SAAI,WAAU,oBACb;AAAA,2DAAC,SAAI,WAAU,0FACb,uDAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,OAAM,WAAU,oBAC5G,uDAAC,cAAS,QAAO,kBAAiB,GACpC,GACF;AAAA,cACA,6CAAC,OAAE,WAAU,4BAA2B,6CAA+B;AAAA,eACzE,IAEA,8EACE;AAAA,2DAAC,QAAG,WAAU,gDAA+C,6BAAe;AAAA,cAC5E,6CAAC,OAAE,WAAU,8BAA6B,2DAA6C;AAAA,cAEvF,8CAAC,UAAK,UAAU,cAAc,WAAU,aACtC;AAAA,8DAAC,SACC;AAAA,+DAAC,WAAM,WAAU,2EAA0E,kBAAI;AAAA,kBAC/F;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,OAAO;AAAA,sBACP,UAAQ;AAAA,sBACR,WAAU;AAAA;AAAA,kBACZ;AAAA,mBACF;AAAA,gBAEA,8CAAC,SACC;AAAA,+DAAC,WAAM,WAAU,2EAA0E,8BAE3F;AAAA,kBACA;AAAA,oBAAC;AAAA;AAAA,sBACC,OAAO;AAAA,sBACP,UAAU,CAAC,MAAM,eAAe,EAAE,OAAO,KAAK;AAAA,sBAC9C,aAAY;AAAA,sBACZ,MAAM;AAAA,sBACN,UAAQ;AAAA,sBACR,WAAU;AAAA;AAAA,kBACZ;AAAA,mBACF;AAAA,gBAEC,SAAS,6CAAC,OAAE,WAAU,wBAAwB,iBAAM;AAAA,gBAErD;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,UAAU,cAAc,CAAC,YAAY,KAAK;AAAA,oBAC1C,WAAU;AAAA,oBAET,uBAAa,eAAe;AAAA;AAAA,gBAC/B;AAAA,iBACF;AAAA,eACF;AAAA;AAAA;AAAA,MAEJ;AAAA;AAAA,EACF;AAEJ;;;ACzIA,IAAAC,gBAAiD;AA+ErC,IAAAC,sBAAA;AArEL,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb;AACF,GAA0B;AACxB,QAAM,CAAC,aAAa,cAAc,QAAI,wBAAS,EAAE;AACjD,QAAM,CAAC,YAAY,aAAa,QAAI,wBAAS,KAAK;AAClD,QAAM,CAAC,WAAW,YAAY,QAAI,wBAAS,KAAK;AAChD,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAS,EAAE;AAErC,QAAM,kBAAc,2BAAY,MAAM;AACpC,mBAAe,EAAE;AACjB,aAAS,EAAE;AACX,iBAAa,KAAK;AAClB,YAAQ;AAAA,EACV,GAAG,CAAC,OAAO,CAAC;AAEZ,+BAAU,MAAM;AACd,QAAI,CAAC,OAAQ;AACb,UAAM,QAAQ,CAAC,MAAqB;AAAE,UAAI,EAAE,QAAQ,SAAU,aAAY;AAAA,IAAE;AAC5E,aAAS,iBAAiB,WAAW,KAAK;AAC1C,WAAO,MAAM,SAAS,oBAAoB,WAAW,KAAK;AAAA,EAC5D,GAAG,CAAC,QAAQ,WAAW,CAAC;AAExB,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,eAAe,OAAO,MAAuB;AACjD,MAAE,eAAe;AACjB,QAAI,CAAC,YAAY,KAAK,EAAG;AACzB,kBAAc,IAAI;AAClB,aAAS,EAAE;AACX,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,UAAU,yBAAyB;AAAA,QAC5D,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,WAAW,UAAU,aAAa,YAAY,UAAU,CAAC;AAAA,MAClF,CAAC;AACD,UAAI,IAAI,IAAI;AACV,qBAAa,IAAI;AACjB,mBAAW,aAAa,GAAI;AAAA,MAC9B,OAAO;AACL,iBAAS,yCAAyC;AAAA,MACpD;AAAA,IACF,QAAQ;AACN,eAAS,gDAAgD;AAAA,IAC3D,UAAE;AACA,oBAAc,KAAK;AAAA,IACrB;AAAA,EACF;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,OAAO,EAAE,YAAY,sBAAsB,gBAAgB,YAAY;AAAA,MACvE,SAAS;AAAA,MAET;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,OAAO,EAAE,WAAW,oBAAoB;AAAA,UACxC,SAAS,CAAC,MAAM,EAAE,gBAAgB;AAAA,UAElC;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,SAAS;AAAA,gBACT,WAAU;AAAA,gBACV,cAAW;AAAA,gBAEX,uDAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAC5F,uDAAC,UAAK,GAAE,wBAAuB,GACjC;AAAA;AAAA,YACF;AAAA,YAEC,YACC,8CAAC,SAAI,WAAU,oBACb;AAAA,2DAAC,SAAI,WAAU,oFACb,uDAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,OAAM,WAAU,cAC5G,uDAAC,cAAS,QAAO,kBAAiB,GACpC,GACF;AAAA,cACA,6CAAC,OAAE,WAAU,4BAA2B,uCAAyB;AAAA,eACnE,IAEA,8EACE;AAAA,2DAAC,QAAG,WAAU,gDAA+C,4BAAc;AAAA,cAC3E,6CAAC,OAAE,WAAU,8BAA6B,yDAA2C;AAAA,cAErF,8CAAC,UAAK,UAAU,cAAc,WAAU,aACtC;AAAA,8DAAC,SACC;AAAA,+DAAC,WAAM,WAAU,2EAA0E,oCAE3F;AAAA,kBACA;AAAA,oBAAC;AAAA;AAAA,sBACC,OAAO;AAAA,sBACP,UAAU,CAAC,MAAM,eAAe,EAAE,OAAO,KAAK;AAAA,sBAC9C,aAAY;AAAA,sBACZ,MAAM;AAAA,sBACN,UAAQ;AAAA,sBACR,WAAU;AAAA;AAAA,kBACZ;AAAA,mBACF;AAAA,gBAEC,SAAS,6CAAC,OAAE,WAAU,wBAAwB,iBAAM;AAAA,gBAErD;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,UAAU,cAAc,CAAC,YAAY,KAAK;AAAA,oBAC1C,WAAU;AAAA,oBAET,uBAAa,eAAe;AAAA;AAAA,gBAC/B;AAAA,iBACF;AAAA,eACF;AAAA;AAAA;AAAA,MAEJ;AAAA;AAAA,EACF;AAEJ;","names":["import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime"]}
|
package/dist/index.mjs
CHANGED
|
@@ -222,10 +222,252 @@ function RCNRFooter({
|
|
|
222
222
|
) });
|
|
223
223
|
}
|
|
224
224
|
var RCNRFooter_default = RCNRFooter;
|
|
225
|
+
|
|
226
|
+
// src/ReportIssueModal.tsx
|
|
227
|
+
import { useState, useEffect, useCallback } from "react";
|
|
228
|
+
import { Fragment, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
229
|
+
function ReportIssueModal({
|
|
230
|
+
isOpen,
|
|
231
|
+
onClose,
|
|
232
|
+
toolName,
|
|
233
|
+
apiBaseUrl = "https://api.rcnr.net",
|
|
234
|
+
userEmail
|
|
235
|
+
}) {
|
|
236
|
+
const [description, setDescription] = useState("");
|
|
237
|
+
const [submitting, setSubmitting] = useState(false);
|
|
238
|
+
const [submitted, setSubmitted] = useState(false);
|
|
239
|
+
const [error, setError] = useState("");
|
|
240
|
+
const handleClose = useCallback(() => {
|
|
241
|
+
setDescription("");
|
|
242
|
+
setError("");
|
|
243
|
+
setSubmitted(false);
|
|
244
|
+
onClose();
|
|
245
|
+
}, [onClose]);
|
|
246
|
+
useEffect(() => {
|
|
247
|
+
if (!isOpen) return;
|
|
248
|
+
const onKey = (e) => {
|
|
249
|
+
if (e.key === "Escape") handleClose();
|
|
250
|
+
};
|
|
251
|
+
document.addEventListener("keydown", onKey);
|
|
252
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
253
|
+
}, [isOpen, handleClose]);
|
|
254
|
+
if (!isOpen) return null;
|
|
255
|
+
const handleSubmit = async (e) => {
|
|
256
|
+
e.preventDefault();
|
|
257
|
+
if (!description.trim()) return;
|
|
258
|
+
setSubmitting(true);
|
|
259
|
+
setError("");
|
|
260
|
+
try {
|
|
261
|
+
const res = await fetch(`${apiBaseUrl}/api/feedback/report`, {
|
|
262
|
+
method: "POST",
|
|
263
|
+
headers: { "Content-Type": "application/json" },
|
|
264
|
+
body: JSON.stringify({ tool_name: toolName, description, user_email: userEmail })
|
|
265
|
+
});
|
|
266
|
+
if (res.ok) {
|
|
267
|
+
setSubmitted(true);
|
|
268
|
+
setTimeout(handleClose, 2e3);
|
|
269
|
+
} else {
|
|
270
|
+
setError("Something went wrong. Please try again.");
|
|
271
|
+
}
|
|
272
|
+
} catch {
|
|
273
|
+
setError("Could not send report. Check your connection.");
|
|
274
|
+
} finally {
|
|
275
|
+
setSubmitting(false);
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
return /* @__PURE__ */ jsx5(
|
|
279
|
+
"div",
|
|
280
|
+
{
|
|
281
|
+
className: "fixed inset-0 z-50 flex items-center justify-center p-4",
|
|
282
|
+
style: { background: "rgba(0,12,23,0.85)", backdropFilter: "blur(6px)" },
|
|
283
|
+
onClick: handleClose,
|
|
284
|
+
children: /* @__PURE__ */ jsxs5(
|
|
285
|
+
"div",
|
|
286
|
+
{
|
|
287
|
+
className: "relative glass-card rounded-2xl w-full max-w-md p-8",
|
|
288
|
+
style: { animation: "fadeIn 0.15s ease" },
|
|
289
|
+
onClick: (e) => e.stopPropagation(),
|
|
290
|
+
children: [
|
|
291
|
+
/* @__PURE__ */ jsx5(
|
|
292
|
+
"button",
|
|
293
|
+
{
|
|
294
|
+
onClick: handleClose,
|
|
295
|
+
className: "absolute top-4 right-4 p-2 text-brand/50 hover:text-brand hover:bg-white/5 rounded-lg transition-colors",
|
|
296
|
+
"aria-label": "Close",
|
|
297
|
+
children: /* @__PURE__ */ jsx5("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx5("path", { d: "M18 6L6 18M6 6l12 12" }) })
|
|
298
|
+
}
|
|
299
|
+
),
|
|
300
|
+
submitted ? /* @__PURE__ */ jsxs5("div", { className: "text-center py-6", children: [
|
|
301
|
+
/* @__PURE__ */ jsx5("div", { className: "w-12 h-12 rounded-full bg-emerald-500/15 flex items-center justify-center mx-auto mb-4", children: /* @__PURE__ */ jsx5("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", className: "text-emerald-400", children: /* @__PURE__ */ jsx5("polyline", { points: "20 6 9 17 4 12" }) }) }),
|
|
302
|
+
/* @__PURE__ */ jsx5("p", { className: "text-white font-semibold", children: "Report sent. We'll fix it fast." })
|
|
303
|
+
] }) : /* @__PURE__ */ jsxs5(Fragment, { children: [
|
|
304
|
+
/* @__PURE__ */ jsx5("h2", { className: "text-xl font-bold text-white font-serif mb-1", children: "Report an Issue" }),
|
|
305
|
+
/* @__PURE__ */ jsx5("p", { className: "text-brand/50 text-sm mb-6", children: "Found a bug or something broken? Let us know." }),
|
|
306
|
+
/* @__PURE__ */ jsxs5("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
307
|
+
/* @__PURE__ */ jsxs5("div", { children: [
|
|
308
|
+
/* @__PURE__ */ jsx5("label", { className: "block text-xs font-semibold text-brand/60 uppercase tracking-wider mb-2", children: "Tool" }),
|
|
309
|
+
/* @__PURE__ */ jsx5(
|
|
310
|
+
"input",
|
|
311
|
+
{
|
|
312
|
+
type: "text",
|
|
313
|
+
value: toolName,
|
|
314
|
+
disabled: true,
|
|
315
|
+
className: "w-full px-4 py-2.5 rounded-xl bg-white/5 border border-brand/10 text-brand/40 text-sm"
|
|
316
|
+
}
|
|
317
|
+
)
|
|
318
|
+
] }),
|
|
319
|
+
/* @__PURE__ */ jsxs5("div", { children: [
|
|
320
|
+
/* @__PURE__ */ jsx5("label", { className: "block text-xs font-semibold text-brand/60 uppercase tracking-wider mb-2", children: "What went wrong?" }),
|
|
321
|
+
/* @__PURE__ */ jsx5(
|
|
322
|
+
"textarea",
|
|
323
|
+
{
|
|
324
|
+
value: description,
|
|
325
|
+
onChange: (e) => setDescription(e.target.value),
|
|
326
|
+
placeholder: "Describe the issue...",
|
|
327
|
+
rows: 4,
|
|
328
|
+
required: true,
|
|
329
|
+
className: "w-full px-4 py-3 rounded-xl bg-white/5 border border-brand/15 text-white placeholder-brand/30 text-sm resize-none focus:outline-none focus:border-brand/40 transition-colors"
|
|
330
|
+
}
|
|
331
|
+
)
|
|
332
|
+
] }),
|
|
333
|
+
error && /* @__PURE__ */ jsx5("p", { className: "text-red-400 text-sm", children: error }),
|
|
334
|
+
/* @__PURE__ */ jsx5(
|
|
335
|
+
"button",
|
|
336
|
+
{
|
|
337
|
+
type: "submit",
|
|
338
|
+
disabled: submitting || !description.trim(),
|
|
339
|
+
className: "btn-ice w-full py-3 text-sm font-semibold rounded-xl disabled:opacity-40 disabled:cursor-not-allowed",
|
|
340
|
+
children: submitting ? "Sending..." : "Submit Report"
|
|
341
|
+
}
|
|
342
|
+
)
|
|
343
|
+
] })
|
|
344
|
+
] })
|
|
345
|
+
]
|
|
346
|
+
}
|
|
347
|
+
)
|
|
348
|
+
}
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// src/RequestToolModal.tsx
|
|
353
|
+
import { useState as useState2, useEffect as useEffect2, useCallback as useCallback2 } from "react";
|
|
354
|
+
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
355
|
+
function RequestToolModal({
|
|
356
|
+
isOpen,
|
|
357
|
+
onClose,
|
|
358
|
+
toolName,
|
|
359
|
+
apiBaseUrl = "https://api.rcnr.net",
|
|
360
|
+
userEmail
|
|
361
|
+
}) {
|
|
362
|
+
const [description, setDescription] = useState2("");
|
|
363
|
+
const [submitting, setSubmitting] = useState2(false);
|
|
364
|
+
const [submitted, setSubmitted] = useState2(false);
|
|
365
|
+
const [error, setError] = useState2("");
|
|
366
|
+
const handleClose = useCallback2(() => {
|
|
367
|
+
setDescription("");
|
|
368
|
+
setError("");
|
|
369
|
+
setSubmitted(false);
|
|
370
|
+
onClose();
|
|
371
|
+
}, [onClose]);
|
|
372
|
+
useEffect2(() => {
|
|
373
|
+
if (!isOpen) return;
|
|
374
|
+
const onKey = (e) => {
|
|
375
|
+
if (e.key === "Escape") handleClose();
|
|
376
|
+
};
|
|
377
|
+
document.addEventListener("keydown", onKey);
|
|
378
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
379
|
+
}, [isOpen, handleClose]);
|
|
380
|
+
if (!isOpen) return null;
|
|
381
|
+
const handleSubmit = async (e) => {
|
|
382
|
+
e.preventDefault();
|
|
383
|
+
if (!description.trim()) return;
|
|
384
|
+
setSubmitting(true);
|
|
385
|
+
setError("");
|
|
386
|
+
try {
|
|
387
|
+
const res = await fetch(`${apiBaseUrl}/api/feedback/request`, {
|
|
388
|
+
method: "POST",
|
|
389
|
+
headers: { "Content-Type": "application/json" },
|
|
390
|
+
body: JSON.stringify({ tool_name: toolName, description, user_email: userEmail })
|
|
391
|
+
});
|
|
392
|
+
if (res.ok) {
|
|
393
|
+
setSubmitted(true);
|
|
394
|
+
setTimeout(handleClose, 2e3);
|
|
395
|
+
} else {
|
|
396
|
+
setError("Something went wrong. Please try again.");
|
|
397
|
+
}
|
|
398
|
+
} catch {
|
|
399
|
+
setError("Could not send request. Check your connection.");
|
|
400
|
+
} finally {
|
|
401
|
+
setSubmitting(false);
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
return /* @__PURE__ */ jsx6(
|
|
405
|
+
"div",
|
|
406
|
+
{
|
|
407
|
+
className: "fixed inset-0 z-50 flex items-center justify-center p-4",
|
|
408
|
+
style: { background: "rgba(0,12,23,0.85)", backdropFilter: "blur(6px)" },
|
|
409
|
+
onClick: handleClose,
|
|
410
|
+
children: /* @__PURE__ */ jsxs6(
|
|
411
|
+
"div",
|
|
412
|
+
{
|
|
413
|
+
className: "relative glass-card rounded-2xl w-full max-w-md p-8",
|
|
414
|
+
style: { animation: "fadeIn 0.15s ease" },
|
|
415
|
+
onClick: (e) => e.stopPropagation(),
|
|
416
|
+
children: [
|
|
417
|
+
/* @__PURE__ */ jsx6(
|
|
418
|
+
"button",
|
|
419
|
+
{
|
|
420
|
+
onClick: handleClose,
|
|
421
|
+
className: "absolute top-4 right-4 p-2 text-brand/50 hover:text-brand hover:bg-white/5 rounded-lg transition-colors",
|
|
422
|
+
"aria-label": "Close",
|
|
423
|
+
children: /* @__PURE__ */ jsx6("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx6("path", { d: "M18 6L6 18M6 6l12 12" }) })
|
|
424
|
+
}
|
|
425
|
+
),
|
|
426
|
+
submitted ? /* @__PURE__ */ jsxs6("div", { className: "text-center py-6", children: [
|
|
427
|
+
/* @__PURE__ */ jsx6("div", { className: "w-12 h-12 rounded-full bg-brand/10 flex items-center justify-center mx-auto mb-4", children: /* @__PURE__ */ jsx6("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", className: "text-brand", children: /* @__PURE__ */ jsx6("polyline", { points: "20 6 9 17 4 12" }) }) }),
|
|
428
|
+
/* @__PURE__ */ jsx6("p", { className: "text-white font-semibold", children: "Request received. Thanks!" })
|
|
429
|
+
] }) : /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
430
|
+
/* @__PURE__ */ jsx6("h2", { className: "text-xl font-bold text-white font-serif mb-1", children: "Request a Tool" }),
|
|
431
|
+
/* @__PURE__ */ jsx6("p", { className: "text-brand/50 text-sm mb-6", children: "Have an idea for something we should build?" }),
|
|
432
|
+
/* @__PURE__ */ jsxs6("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
433
|
+
/* @__PURE__ */ jsxs6("div", { children: [
|
|
434
|
+
/* @__PURE__ */ jsx6("label", { className: "block text-xs font-semibold text-brand/60 uppercase tracking-wider mb-2", children: "Describe what you need" }),
|
|
435
|
+
/* @__PURE__ */ jsx6(
|
|
436
|
+
"textarea",
|
|
437
|
+
{
|
|
438
|
+
value: description,
|
|
439
|
+
onChange: (e) => setDescription(e.target.value),
|
|
440
|
+
placeholder: "What problem would this tool solve? What would it do?",
|
|
441
|
+
rows: 5,
|
|
442
|
+
required: true,
|
|
443
|
+
className: "w-full px-4 py-3 rounded-xl bg-white/5 border border-brand/15 text-white placeholder-brand/30 text-sm resize-none focus:outline-none focus:border-brand/40 transition-colors"
|
|
444
|
+
}
|
|
445
|
+
)
|
|
446
|
+
] }),
|
|
447
|
+
error && /* @__PURE__ */ jsx6("p", { className: "text-red-400 text-sm", children: error }),
|
|
448
|
+
/* @__PURE__ */ jsx6(
|
|
449
|
+
"button",
|
|
450
|
+
{
|
|
451
|
+
type: "submit",
|
|
452
|
+
disabled: submitting || !description.trim(),
|
|
453
|
+
className: "btn-ice w-full py-3 text-sm font-semibold rounded-xl disabled:opacity-40 disabled:cursor-not-allowed",
|
|
454
|
+
children: submitting ? "Sending..." : "Submit Request"
|
|
455
|
+
}
|
|
456
|
+
)
|
|
457
|
+
] })
|
|
458
|
+
] })
|
|
459
|
+
]
|
|
460
|
+
}
|
|
461
|
+
)
|
|
462
|
+
}
|
|
463
|
+
);
|
|
464
|
+
}
|
|
225
465
|
export {
|
|
226
466
|
RCNRFooter_default as RCNRFooter,
|
|
227
467
|
RCNRHeader_default as RCNRHeader,
|
|
228
468
|
RCNRMountainLogo_default as RCNRMountainLogo,
|
|
229
|
-
RCNRSubNav_default as RCNRSubNav
|
|
469
|
+
RCNRSubNav_default as RCNRSubNav,
|
|
470
|
+
ReportIssueModal,
|
|
471
|
+
RequestToolModal
|
|
230
472
|
};
|
|
231
473
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/RCNRMountainLogo.tsx","../src/RCNRHeader.tsx","../src/RCNRSubNav.tsx","../src/RCNRFooter.tsx"],"sourcesContent":["interface RCNRMountainLogoProps {\r\n href?: string\r\n className?: string\r\n}\r\n\r\nfunction RCNRMountainLogo({\r\n href = 'https://teacher.rcnr.net',\r\n className = '',\r\n}: RCNRMountainLogoProps) {\r\n return (\r\n <a\r\n href={href}\r\n className={`flex items-center justify-center w-10 h-10 rounded-lg bg-brand/10 hover:bg-brand/20 transition-colors ${className}`}\r\n title=\"Back to Dashboard\"\r\n >\r\n <svg\r\n width=\"28\"\r\n height=\"24\"\r\n viewBox=\"0 0 120 100\"\r\n fill=\"none\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n >\r\n <path\r\n d=\"M10,90l26.48-39.76h.31s4.54,5.02,4.54,5.02l.32-.16,23.06-37.14,17.34,28.63,3.65-4.61,30.22,48.03h-8.43c-7.17-12.26-15.51-24.35-23.06-36.26-1.43.52-2.03,3.6-3.49,3.97l-16.31-25.93-21.87,34.36-5.09-5.01-19.48,28.87h-8.19Z\"\r\n fill=\"#99D9D9\"\r\n />\r\n <path\r\n d=\"M89.84,90h-5.73c-6.34-8.18-12.93-16.89-19.64-24.65-6.66,8.19-13.04,16.6-19.89,24.65h-5.81l25.69-39.76,25.37,39.76Z\"\r\n fill=\"#99D9D9\"\r\n />\r\n </svg>\r\n </a>\r\n )\r\n}\r\n\r\nexport default RCNRMountainLogo\r\n","import type { ReactNode } from 'react'\r\nimport type { RCNRHeaderProps } from './types'\r\nimport RCNRMountainLogo from './RCNRMountainLogo'\r\n\r\nfunction NavButton({\r\n onClick,\r\n icon,\r\n label,\r\n title,\r\n}: {\r\n onClick: () => void\r\n icon: ReactNode\r\n label: string\r\n title?: string\r\n}) {\r\n return (\r\n <button\r\n onClick={onClick}\r\n className=\"flex items-center gap-2 px-3 py-2 text-brand/70 hover:text-brand hover:bg-brand/5 rounded-lg transition-colors\"\r\n title={title ?? label}\r\n >\r\n {icon}\r\n <span className=\"hidden md:inline text-sm\">{label}</span>\r\n </button>\r\n )\r\n}\r\n\r\nconst defaultReportIssue = () =>\r\n window.open('mailto:support@rcnr.net?subject=Bug%20Report', '_blank')\r\n\r\nconst defaultRequestTool = () =>\r\n window.open('mailto:support@rcnr.net?subject=Feature%20Request', '_blank')\r\n\r\nfunction RCNRHeader({\r\n toolName,\r\n dashboardUrl = 'https://teacher.rcnr.net',\r\n extraNavItems,\r\n userAvatar,\r\n onHowItWorks,\r\n onReportIssue = defaultReportIssue,\r\n onRequestTool = defaultRequestTool,\r\n}: RCNRHeaderProps) {\r\n return (\r\n <header className=\"glass-card border-b border-brand/15 px-6 py-4\">\r\n <div className=\"flex items-center justify-between\">\r\n {/* Left: Logo + Tool Name */}\r\n <div className=\"flex items-center gap-4\">\r\n <RCNRMountainLogo href={dashboardUrl} />\r\n <span className=\"text-xl font-serif text-brand\">{toolName}</span>\r\n </div>\r\n\r\n {/* Right: Nav Actions */}\r\n <div className=\"flex items-center gap-2\">\r\n {/* Tool-specific nav items first */}\r\n {extraNavItems?.map((item) => (\r\n <NavButton\r\n key={item.label}\r\n onClick={item.onClick}\r\n icon={item.icon}\r\n label={item.label}\r\n />\r\n ))}\r\n\r\n {/* Standard nav items in fixed order */}\r\n {onHowItWorks && (\r\n <NavButton\r\n onClick={onHowItWorks}\r\n label=\"How It Works\"\r\n icon={\r\n <svg\r\n className=\"w-[18px] h-[18px]\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n strokeWidth={2}\r\n >\r\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\r\n <path\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n d=\"M9.09 9a3 3 0 015.83 1c0 2-3 3-3 3\"\r\n />\r\n <line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\" />\r\n </svg>\r\n }\r\n />\r\n )}\r\n\r\n <NavButton\r\n onClick={onReportIssue}\r\n label=\"Report Issue\"\r\n icon={\r\n <svg\r\n className=\"w-[18px] h-[18px]\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n strokeWidth={2}\r\n >\r\n <path\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\r\n />\r\n </svg>\r\n }\r\n />\r\n\r\n <NavButton\r\n onClick={onRequestTool}\r\n label=\"Request Tool\"\r\n icon={\r\n <svg\r\n className=\"w-[18px] h-[18px]\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n strokeWidth={2}\r\n >\r\n <path\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n d=\"M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z\"\r\n />\r\n </svg>\r\n }\r\n />\r\n\r\n {userAvatar}\r\n </div>\r\n </div>\r\n </header>\r\n )\r\n}\r\n\r\nexport default RCNRHeader\r\n","import type { RCNRSubNavProps } from './types'\r\n\r\nfunction RCNRSubNav({ tabs }: RCNRSubNavProps) {\r\n return (\r\n <nav className=\"flex items-center gap-1 px-6 py-2 border-b border-brand/10 bg-surface/50\">\r\n {tabs.map((tab) => {\r\n const isWarning = tab.variant === 'warning'\r\n const activeClass = isWarning\r\n ? 'bg-warning/10 text-warning border border-warning/20'\r\n : 'bg-brand/15 text-brand'\r\n const inactiveClass = isWarning\r\n ? 'text-warning/70 hover:text-warning hover:bg-warning/5 border border-warning/10'\r\n : 'text-brand/50 hover:text-brand hover:bg-brand/5'\r\n\r\n const className = `flex items-center gap-2 px-4 py-2 rounded-lg text-sm transition-colors ${\r\n tab.active ? activeClass : inactiveClass\r\n }`\r\n\r\n if (tab.href) {\r\n return (\r\n <a key={tab.label} href={tab.href} className={className}>\r\n {tab.icon}\r\n <span>{tab.label}</span>\r\n </a>\r\n )\r\n }\r\n\r\n return (\r\n <button\r\n key={tab.label}\r\n onClick={tab.onClick}\r\n className={className}\r\n >\r\n {tab.icon}\r\n <span>{tab.label}</span>\r\n </button>\r\n )\r\n })}\r\n </nav>\r\n )\r\n}\r\n\r\nexport default RCNRSubNav\r\n","import type { RCNRFooterProps } from './types'\r\n\r\nfunction RCNRFooter({\r\n toolName,\r\n linkUrl = 'https://rcnr.net',\r\n}: RCNRFooterProps) {\r\n return (\r\n <footer className=\"mt-auto py-4 text-center text-sm text-brand-dark/50\">\r\n <a\r\n href={linkUrl}\r\n className=\"hover:text-brand transition-colors\"\r\n >\r\n {toolName} — Part of the RCNR Teacher Toolbox\r\n </a>\r\n </footer>\r\n )\r\n}\r\n\r\nexport default RCNRFooter\r\n"],"mappings":"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","names":["jsx","jsxs","jsx","jsxs","jsx","jsxs"]}
|
|
1
|
+
{"version":3,"sources":["../src/RCNRMountainLogo.tsx","../src/RCNRHeader.tsx","../src/RCNRSubNav.tsx","../src/RCNRFooter.tsx","../src/ReportIssueModal.tsx","../src/RequestToolModal.tsx"],"sourcesContent":["interface RCNRMountainLogoProps {\r\n href?: string\r\n className?: string\r\n}\r\n\r\nfunction RCNRMountainLogo({\r\n href = 'https://teacher.rcnr.net',\r\n className = '',\r\n}: RCNRMountainLogoProps) {\r\n return (\r\n <a\r\n href={href}\r\n className={`flex items-center justify-center w-10 h-10 rounded-lg bg-brand/10 hover:bg-brand/20 transition-colors ${className}`}\r\n title=\"Back to Dashboard\"\r\n >\r\n <svg\r\n width=\"28\"\r\n height=\"24\"\r\n viewBox=\"0 0 120 100\"\r\n fill=\"none\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n >\r\n <path\r\n d=\"M10,90l26.48-39.76h.31s4.54,5.02,4.54,5.02l.32-.16,23.06-37.14,17.34,28.63,3.65-4.61,30.22,48.03h-8.43c-7.17-12.26-15.51-24.35-23.06-36.26-1.43.52-2.03,3.6-3.49,3.97l-16.31-25.93-21.87,34.36-5.09-5.01-19.48,28.87h-8.19Z\"\r\n fill=\"#99D9D9\"\r\n />\r\n <path\r\n d=\"M89.84,90h-5.73c-6.34-8.18-12.93-16.89-19.64-24.65-6.66,8.19-13.04,16.6-19.89,24.65h-5.81l25.69-39.76,25.37,39.76Z\"\r\n fill=\"#99D9D9\"\r\n />\r\n </svg>\r\n </a>\r\n )\r\n}\r\n\r\nexport default RCNRMountainLogo\r\n","import type { ReactNode } from 'react'\r\nimport type { RCNRHeaderProps } from './types'\r\nimport RCNRMountainLogo from './RCNRMountainLogo'\r\n\r\nfunction NavButton({\r\n onClick,\r\n icon,\r\n label,\r\n title,\r\n}: {\r\n onClick: () => void\r\n icon: ReactNode\r\n label: string\r\n title?: string\r\n}) {\r\n return (\r\n <button\r\n onClick={onClick}\r\n className=\"flex items-center gap-2 px-3 py-2 text-brand/70 hover:text-brand hover:bg-brand/5 rounded-lg transition-colors\"\r\n title={title ?? label}\r\n >\r\n {icon}\r\n <span className=\"hidden md:inline text-sm\">{label}</span>\r\n </button>\r\n )\r\n}\r\n\r\nconst defaultReportIssue = () =>\r\n window.open('mailto:support@rcnr.net?subject=Bug%20Report', '_blank')\r\n\r\nconst defaultRequestTool = () =>\r\n window.open('mailto:support@rcnr.net?subject=Feature%20Request', '_blank')\r\n\r\nfunction RCNRHeader({\r\n toolName,\r\n dashboardUrl = 'https://teacher.rcnr.net',\r\n extraNavItems,\r\n userAvatar,\r\n onHowItWorks,\r\n onReportIssue = defaultReportIssue,\r\n onRequestTool = defaultRequestTool,\r\n}: RCNRHeaderProps) {\r\n return (\r\n <header className=\"glass-card border-b border-brand/15 px-6 py-4\">\r\n <div className=\"flex items-center justify-between\">\r\n {/* Left: Logo + Tool Name */}\r\n <div className=\"flex items-center gap-4\">\r\n <RCNRMountainLogo href={dashboardUrl} />\r\n <span className=\"text-xl font-serif text-brand\">{toolName}</span>\r\n </div>\r\n\r\n {/* Right: Nav Actions */}\r\n <div className=\"flex items-center gap-2\">\r\n {/* Tool-specific nav items first */}\r\n {extraNavItems?.map((item) => (\r\n <NavButton\r\n key={item.label}\r\n onClick={item.onClick}\r\n icon={item.icon}\r\n label={item.label}\r\n />\r\n ))}\r\n\r\n {/* Standard nav items in fixed order */}\r\n {onHowItWorks && (\r\n <NavButton\r\n onClick={onHowItWorks}\r\n label=\"How It Works\"\r\n icon={\r\n <svg\r\n className=\"w-[18px] h-[18px]\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n strokeWidth={2}\r\n >\r\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\r\n <path\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n d=\"M9.09 9a3 3 0 015.83 1c0 2-3 3-3 3\"\r\n />\r\n <line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\" />\r\n </svg>\r\n }\r\n />\r\n )}\r\n\r\n <NavButton\r\n onClick={onReportIssue}\r\n label=\"Report Issue\"\r\n icon={\r\n <svg\r\n className=\"w-[18px] h-[18px]\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n strokeWidth={2}\r\n >\r\n <path\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\r\n />\r\n </svg>\r\n }\r\n />\r\n\r\n <NavButton\r\n onClick={onRequestTool}\r\n label=\"Request Tool\"\r\n icon={\r\n <svg\r\n className=\"w-[18px] h-[18px]\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n strokeWidth={2}\r\n >\r\n <path\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n d=\"M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z\"\r\n />\r\n </svg>\r\n }\r\n />\r\n\r\n {userAvatar}\r\n </div>\r\n </div>\r\n </header>\r\n )\r\n}\r\n\r\nexport default RCNRHeader\r\n","import type { RCNRSubNavProps } from './types'\r\n\r\nfunction RCNRSubNav({ tabs }: RCNRSubNavProps) {\r\n return (\r\n <nav className=\"flex items-center gap-1 px-6 py-2 border-b border-brand/10 bg-surface/50\">\r\n {tabs.map((tab) => {\r\n const isWarning = tab.variant === 'warning'\r\n const activeClass = isWarning\r\n ? 'bg-warning/10 text-warning border border-warning/20'\r\n : 'bg-brand/15 text-brand'\r\n const inactiveClass = isWarning\r\n ? 'text-warning/70 hover:text-warning hover:bg-warning/5 border border-warning/10'\r\n : 'text-brand/50 hover:text-brand hover:bg-brand/5'\r\n\r\n const className = `flex items-center gap-2 px-4 py-2 rounded-lg text-sm transition-colors ${\r\n tab.active ? activeClass : inactiveClass\r\n }`\r\n\r\n if (tab.href) {\r\n return (\r\n <a key={tab.label} href={tab.href} className={className}>\r\n {tab.icon}\r\n <span>{tab.label}</span>\r\n </a>\r\n )\r\n }\r\n\r\n return (\r\n <button\r\n key={tab.label}\r\n onClick={tab.onClick}\r\n className={className}\r\n >\r\n {tab.icon}\r\n <span>{tab.label}</span>\r\n </button>\r\n )\r\n })}\r\n </nav>\r\n )\r\n}\r\n\r\nexport default RCNRSubNav\r\n","import type { RCNRFooterProps } from './types'\r\n\r\nfunction RCNRFooter({\r\n toolName,\r\n linkUrl = 'https://rcnr.net',\r\n}: RCNRFooterProps) {\r\n return (\r\n <footer className=\"mt-auto py-4 text-center text-sm text-brand-dark/50\">\r\n <a\r\n href={linkUrl}\r\n className=\"hover:text-brand transition-colors\"\r\n >\r\n {toolName} — Part of the RCNR Teacher Toolbox\r\n </a>\r\n </footer>\r\n )\r\n}\r\n\r\nexport default RCNRFooter\r\n","import { useState, useEffect, useCallback } from 'react'\n\ninterface ReportIssueModalProps {\n isOpen: boolean\n onClose: () => void\n toolName: string\n apiBaseUrl?: string\n userEmail?: string\n}\n\nexport function ReportIssueModal({\n isOpen,\n onClose,\n toolName,\n apiBaseUrl = 'https://api.rcnr.net',\n userEmail,\n}: ReportIssueModalProps) {\n const [description, setDescription] = useState('')\n const [submitting, setSubmitting] = useState(false)\n const [submitted, setSubmitted] = useState(false)\n const [error, setError] = useState('')\n\n const handleClose = useCallback(() => {\n setDescription('')\n setError('')\n setSubmitted(false)\n onClose()\n }, [onClose])\n\n useEffect(() => {\n if (!isOpen) return\n const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') handleClose() }\n document.addEventListener('keydown', onKey)\n return () => document.removeEventListener('keydown', onKey)\n }, [isOpen, handleClose])\n\n if (!isOpen) return null\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault()\n if (!description.trim()) return\n setSubmitting(true)\n setError('')\n try {\n const res = await fetch(`${apiBaseUrl}/api/feedback/report`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ tool_name: toolName, description, user_email: userEmail }),\n })\n if (res.ok) {\n setSubmitted(true)\n setTimeout(handleClose, 2000)\n } else {\n setError('Something went wrong. Please try again.')\n }\n } catch {\n setError('Could not send report. Check your connection.')\n } finally {\n setSubmitting(false)\n }\n }\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center p-4\"\n style={{ background: 'rgba(0,12,23,0.85)', backdropFilter: 'blur(6px)' }}\n onClick={handleClose}\n >\n <div\n className=\"relative glass-card rounded-2xl w-full max-w-md p-8\"\n style={{ animation: 'fadeIn 0.15s ease' }}\n onClick={(e) => e.stopPropagation()}\n >\n <button\n onClick={handleClose}\n className=\"absolute top-4 right-4 p-2 text-brand/50 hover:text-brand hover:bg-white/5 rounded-lg transition-colors\"\n aria-label=\"Close\"\n >\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M18 6L6 18M6 6l12 12\" />\n </svg>\n </button>\n\n {submitted ? (\n <div className=\"text-center py-6\">\n <div className=\"w-12 h-12 rounded-full bg-emerald-500/15 flex items-center justify-center mx-auto mb-4\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" className=\"text-emerald-400\">\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n </div>\n <p className=\"text-white font-semibold\">Report sent. We'll fix it fast.</p>\n </div>\n ) : (\n <>\n <h2 className=\"text-xl font-bold text-white font-serif mb-1\">Report an Issue</h2>\n <p className=\"text-brand/50 text-sm mb-6\">Found a bug or something broken? Let us know.</p>\n\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n <div>\n <label className=\"block text-xs font-semibold text-brand/60 uppercase tracking-wider mb-2\">Tool</label>\n <input\n type=\"text\"\n value={toolName}\n disabled\n className=\"w-full px-4 py-2.5 rounded-xl bg-white/5 border border-brand/10 text-brand/40 text-sm\"\n />\n </div>\n\n <div>\n <label className=\"block text-xs font-semibold text-brand/60 uppercase tracking-wider mb-2\">\n What went wrong?\n </label>\n <textarea\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"Describe the issue...\"\n rows={4}\n required\n className=\"w-full px-4 py-3 rounded-xl bg-white/5 border border-brand/15 text-white placeholder-brand/30 text-sm resize-none focus:outline-none focus:border-brand/40 transition-colors\"\n />\n </div>\n\n {error && <p className=\"text-red-400 text-sm\">{error}</p>}\n\n <button\n type=\"submit\"\n disabled={submitting || !description.trim()}\n className=\"btn-ice w-full py-3 text-sm font-semibold rounded-xl disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {submitting ? 'Sending...' : 'Submit Report'}\n </button>\n </form>\n </>\n )}\n </div>\n </div>\n )\n}\n","import { useState, useEffect, useCallback } from 'react'\n\ninterface RequestToolModalProps {\n isOpen: boolean\n onClose: () => void\n toolName: string\n apiBaseUrl?: string\n userEmail?: string\n}\n\nexport function RequestToolModal({\n isOpen,\n onClose,\n toolName,\n apiBaseUrl = 'https://api.rcnr.net',\n userEmail,\n}: RequestToolModalProps) {\n const [description, setDescription] = useState('')\n const [submitting, setSubmitting] = useState(false)\n const [submitted, setSubmitted] = useState(false)\n const [error, setError] = useState('')\n\n const handleClose = useCallback(() => {\n setDescription('')\n setError('')\n setSubmitted(false)\n onClose()\n }, [onClose])\n\n useEffect(() => {\n if (!isOpen) return\n const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') handleClose() }\n document.addEventListener('keydown', onKey)\n return () => document.removeEventListener('keydown', onKey)\n }, [isOpen, handleClose])\n\n if (!isOpen) return null\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault()\n if (!description.trim()) return\n setSubmitting(true)\n setError('')\n try {\n const res = await fetch(`${apiBaseUrl}/api/feedback/request`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ tool_name: toolName, description, user_email: userEmail }),\n })\n if (res.ok) {\n setSubmitted(true)\n setTimeout(handleClose, 2000)\n } else {\n setError('Something went wrong. Please try again.')\n }\n } catch {\n setError('Could not send request. Check your connection.')\n } finally {\n setSubmitting(false)\n }\n }\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center p-4\"\n style={{ background: 'rgba(0,12,23,0.85)', backdropFilter: 'blur(6px)' }}\n onClick={handleClose}\n >\n <div\n className=\"relative glass-card rounded-2xl w-full max-w-md p-8\"\n style={{ animation: 'fadeIn 0.15s ease' }}\n onClick={(e) => e.stopPropagation()}\n >\n <button\n onClick={handleClose}\n className=\"absolute top-4 right-4 p-2 text-brand/50 hover:text-brand hover:bg-white/5 rounded-lg transition-colors\"\n aria-label=\"Close\"\n >\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M18 6L6 18M6 6l12 12\" />\n </svg>\n </button>\n\n {submitted ? (\n <div className=\"text-center py-6\">\n <div className=\"w-12 h-12 rounded-full bg-brand/10 flex items-center justify-center mx-auto mb-4\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" className=\"text-brand\">\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n </div>\n <p className=\"text-white font-semibold\">Request received. Thanks!</p>\n </div>\n ) : (\n <>\n <h2 className=\"text-xl font-bold text-white font-serif mb-1\">Request a Tool</h2>\n <p className=\"text-brand/50 text-sm mb-6\">Have an idea for something we should build?</p>\n\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n <div>\n <label className=\"block text-xs font-semibold text-brand/60 uppercase tracking-wider mb-2\">\n Describe what you need\n </label>\n <textarea\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"What problem would this tool solve? What would it do?\"\n rows={5}\n required\n className=\"w-full px-4 py-3 rounded-xl bg-white/5 border border-brand/15 text-white placeholder-brand/30 text-sm resize-none focus:outline-none focus:border-brand/40 transition-colors\"\n />\n </div>\n\n {error && <p className=\"text-red-400 text-sm\">{error}</p>}\n\n <button\n type=\"submit\"\n disabled={submitting || !description.trim()}\n className=\"btn-ice w-full py-3 text-sm font-semibold rounded-xl disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {submitting ? 'Sending...' : 'Submit Request'}\n </button>\n </form>\n </>\n )}\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":["jsx","jsxs","jsx","jsxs","jsx","jsxs","jsx","jsxs","useState","useEffect","useCallback","Fragment","jsx","jsxs"]}
|
package/package.json
CHANGED