From ed201936813d9a720b51e6cb40a05267b94e7314 Mon Sep 17 00:00:00 2001
From: Max Nanis
Date: Thu, 10 Sep 2026 17:46:41 -0700
Subject: Typescript prettier
Committing seperate so it's easier to see actual changes.
---
jb-ui/src/components/EventMarquee.tsx | 428 ++++++++++++++++++----------------
1 file changed, 224 insertions(+), 204 deletions(-)
(limited to 'jb-ui/src/components/EventMarquee.tsx')
diff --git a/jb-ui/src/components/EventMarquee.tsx b/jb-ui/src/components/EventMarquee.tsx
index 4ce4f15..6aab3f8 100644
--- a/jb-ui/src/components/EventMarquee.tsx
+++ b/jb-ui/src/components/EventMarquee.tsx
@@ -1,230 +1,250 @@
-import { EventEnvelope, EventType, TaskEnterPayload, TaskFinishPayload } from "@/api_fsb";
-import { formatSecondsVerbose, formatSource, formatStatus, truncate } from "@/lib/utils";
+import {
+ EventEnvelope,
+ EventType,
+ TaskEnterPayload,
+ TaskFinishPayload,
+} from "@/api_fsb";
+import {
+ formatSecondsVerbose,
+ formatSource,
+ formatStatus,
+ truncate,
+} from "@/lib/utils";
import { selectAllEvents, selectCurrentSpeed } from "@/models/grlEventsSlice";
-import getUnicodeFlagIcon from 'country-flag-icons/unicode';
+import getUnicodeFlagIcon from "country-flag-icons/unicode";
import { useEffect, useRef, useState } from "react";
import { useSelector } from "react-redux";
// ─── Config ───────────────────────────────────────────────────────────────────
-const SPEED_EASING = 0.025; // how quickly speed transitions (per frame)
+const SPEED_EASING = 0.025; // how quickly speed transitions (per frame)
interface EventItemProps {
- event: EventEnvelope;
+ event: EventEnvelope;
}
const EventTaskEnter = ({ event }: EventItemProps) => {
- const payload = event.payload as TaskEnterPayload;
-
- return (
- <>
- {getUnicodeFlagIcon(payload.country_iso)}
-
- {truncate(event.product_user_id!, 6, "*****")} Entered{" "}
- {formatSource(payload.source)}{" "}
- (#{truncate(payload.survey_id!, 5, "***")})
-
- >
- )
-}
+ const payload = event.payload as TaskEnterPayload;
+
+ return (
+ <>
+
+ {getUnicodeFlagIcon(payload.country_iso)}
+
+
+ {truncate(event.product_user_id!, 6, "*****")} Entered{" "}
+ {formatSource(payload.source)} (#
+ {truncate(payload.survey_id!, 5, "***")})
+
+ >
+ );
+};
const EventTaskFinish = ({ event }: EventItemProps) => {
- const payload = event.payload as TaskFinishPayload;
-
- return (
- <>
- {getUnicodeFlagIcon(event.payload.country_iso)}
-
- {truncate(event.product_user_id!, 6, "*****")}{" "}
- {formatStatus(payload.status)} {formatSource(payload.source)}{" "}
- (#{truncate(payload.survey_id!, 5, "***")}) in{" "}
- {formatSecondsVerbose(payload.duration_sec!)}
-
- >
- )
-}
+ const payload = event.payload as TaskFinishPayload;
+
+ return (
+ <>
+
+ {getUnicodeFlagIcon(event.payload.country_iso)}
+
+
+ {truncate(event.product_user_id!, 6, "*****")}{" "}
+ {formatStatus(payload.status)} {formatSource(payload.source)} (#
+ {truncate(payload.survey_id!, 5, "***")}) in{" "}
+ {formatSecondsVerbose(payload.duration_sec!)}
+
+ >
+ );
+};
const EventUserCreated = ({ event }: EventItemProps) => {
-
- return (
- <>
- {getUnicodeFlagIcon(event.payload.country_iso)}
- {truncate(event.product_user_id!, 6, "*****")} Created
- >
- )
-}
-
+ return (
+ <>
+
+ {getUnicodeFlagIcon(event.payload.country_iso)}
+
+ {truncate(event.product_user_id!, 6, "*****")} Created
+ >
+ );
+};
const EventComponent = ({ event }: EventItemProps) => {
-
- const renderContent = () => {
- switch (event.event_type) {
- case EventType.TaskEnter:
- return
- case EventType.TaskFinish:
- return
- case EventType.UserCreated:
- return
- default:
- return <>Unknown event
>
- }
+ const renderContent = () => {
+ switch (event.event_type) {
+ case EventType.TaskEnter:
+ return ;
+ case EventType.TaskFinish:
+ return ;
+ case EventType.UserCreated:
+ return ;
+ default:
+ return (
+ <>
+ Unknown event
+ >
+ );
}
+ };
- return (
-
- {renderContent()}
-
- {/* ◆ */}
-
- )
-}
+ return (
+
+ {renderContent()}
+ {/* ◆ */}
+
+ );
+};
// ─── Main Component ───────────────────────────────────────────────────────────
export default function NewsTicker() {
- // --- Redux ---
- const reduxItems = useSelector(selectAllEvents);
- const targetSpeedRedux = useSelector(selectCurrentSpeed); // px/sec
-
- // --- Scroll state (all refs — no re-renders from animation) ---
- const containerRef = useRef(null); // The ticker band
- const stripRef = useRef(null); // Scrolling strip
- const translateX = useRef(0);
- const currentSpeed = useRef(targetSpeedRedux);
- const targetSpeed = useRef(targetSpeedRedux);
-
- // Only state and refs persist across renders, the rest are re-initialized,
- // that is why this isn't just a standard variable.
- // Request Animation Frame reference (stores the animation frame ID)
- const rafRef = useRef(null);
- const lastTime = useRef(null);
-
- // --- Item tracking ---
- // Track which IDs are already in the scroller to only append
- // genuinely new ones.
- const seenUUIDs = useRef(new Set());
- const pendingQueue = useRef([]);
- const [scrollItems, setScrollItems] = useState([]);
-
- // --- Sync Redux → pendingQueue (only new items) ---
- useEffect(() => {
- const newItems = reduxItems.filter((item) => !seenUUIDs.current.has(item.event_uuid!));
- if (newItems.length === 0) return;
-
- newItems.forEach(item => seenUUIDs.current.add(item.event_uuid!));
- pendingQueue.current.push(...newItems);
- }, [reduxItems]);
-
- // --- Sync target speed from Redux ---
- useEffect(() => {
- targetSpeed.current = targetSpeedRedux;
- }, [targetSpeedRedux]);
-
- // --- RAF loop ---
- useEffect(() => {
- // This timestamp is a "high-resolution timestamp" provided by
- // the browser, which is more accurate for measuring frame intervals
- // than Date.now()
- const loop = (timestamp: number) => {
- console.log("RAF loop, timestamp:", timestamp, lastTime.current);
-
- if (!lastTime.current) lastTime.current = timestamp;
- const delta = (timestamp - lastTime.current) / 1000;
- lastTime.current = timestamp;
-
- // Slow easement towards targetSpeed
- currentSpeed.current +=
- (targetSpeed.current - currentSpeed.current) * SPEED_EASING;
-
- // flush pending queue into React state
- if (pendingQueue.current.length > 0) {
- const incoming = pendingQueue.current.splice(0);
- setScrollItems((prev) => [...prev, ...incoming]);
- }
-
- // Advance strip, slides it over left by X pixels
- translateX.current -= currentSpeed.current * delta;
- if (stripRef.current) {
- stripRef.current.style.transform = `translateX(${translateX.current}px)`;
- }
-
- // prune items that have fully exited the left edge
- if (stripRef.current && containerRef.current) {
- const children = Array.from(stripRef.current.children);
- const containerLeft = containerRef.current.getBoundingClientRect().left;
- let pruneCount = 0;
- let removedWidth = 0;
-
- for (const child of children) {
- const rect = child.getBoundingClientRect();
- if (rect.right < containerLeft - 20) {
- pruneCount++;
- removedWidth += rect.width;
- } else {
- break;
- }
- }
-
- if (pruneCount > 0) {
- translateX.current += removedWidth;
- setScrollItems((prev) => prev.slice(pruneCount));
- }
- }
-
- // Assign the next loop
- rafRef.current = requestAnimationFrame(loop);
- };
-
- rafRef.current = requestAnimationFrame(loop);
- return () => cancelAnimationFrame(rafRef.current!);
- }, []);
-
- return (
-
-
- {/* Ticker wrapper */}
-
-
- {/* The ticker band */}
-
-
- {/* Scrolling strip */}
-
- {scrollItems.map(item => (
-
- ))}
-
- {/* Trailing spacer so new content doesn't immediately snap in */}
-
-
-
-
-
-
+ // --- Redux ---
+ const reduxItems = useSelector(selectAllEvents);
+ const targetSpeedRedux = useSelector(selectCurrentSpeed); // px/sec
+
+ // --- Scroll state (all refs — no re-renders from animation) ---
+ const containerRef = useRef
(null); // The ticker band
+ const stripRef = useRef(null); // Scrolling strip
+ const translateX = useRef(0);
+ const currentSpeed = useRef(targetSpeedRedux);
+ const targetSpeed = useRef(targetSpeedRedux);
+
+ // Only state and refs persist across renders, the rest are re-initialized,
+ // that is why this isn't just a standard variable.
+ // Request Animation Frame reference (stores the animation frame ID)
+ const rafRef = useRef(null);
+ const lastTime = useRef(null);
+
+ // --- Item tracking ---
+ // Track which IDs are already in the scroller to only append
+ // genuinely new ones.
+ const seenUUIDs = useRef(new Set());
+ const pendingQueue = useRef([]);
+ const [scrollItems, setScrollItems] = useState([]);
+
+ // --- Sync Redux → pendingQueue (only new items) ---
+ useEffect(() => {
+ const newItems = reduxItems.filter(
+ (item) => !seenUUIDs.current.has(item.event_uuid!),
+ );
+ if (newItems.length === 0) return;
+
+ newItems.forEach((item) => seenUUIDs.current.add(item.event_uuid!));
+ pendingQueue.current.push(...newItems);
+ }, [reduxItems]);
+
+ // --- Sync target speed from Redux ---
+ useEffect(() => {
+ targetSpeed.current = targetSpeedRedux;
+ }, [targetSpeedRedux]);
+
+ // --- RAF loop ---
+ useEffect(() => {
+ // This timestamp is a "high-resolution timestamp" provided by
+ // the browser, which is more accurate for measuring frame intervals
+ // than Date.now()
+ const loop = (timestamp: number) => {
+ console.log("RAF loop, timestamp:", timestamp, lastTime.current);
+
+ if (!lastTime.current) lastTime.current = timestamp;
+ const delta = (timestamp - lastTime.current) / 1000;
+ lastTime.current = timestamp;
+
+ // Slow easement towards targetSpeed
+ currentSpeed.current +=
+ (targetSpeed.current - currentSpeed.current) * SPEED_EASING;
+
+ // flush pending queue into React state
+ if (pendingQueue.current.length > 0) {
+ const incoming = pendingQueue.current.splice(0);
+ setScrollItems((prev) => [...prev, ...incoming]);
+ }
+
+ // Advance strip, slides it over left by X pixels
+ translateX.current -= currentSpeed.current * delta;
+ if (stripRef.current) {
+ stripRef.current.style.transform = `translateX(${translateX.current}px)`;
+ }
+
+ // prune items that have fully exited the left edge
+ if (stripRef.current && containerRef.current) {
+ const children = Array.from(stripRef.current.children);
+ const containerLeft = containerRef.current.getBoundingClientRect().left;
+ let pruneCount = 0;
+ let removedWidth = 0;
+
+ for (const child of children) {
+ const rect = child.getBoundingClientRect();
+ if (rect.right < containerLeft - 20) {
+ pruneCount++;
+ removedWidth += rect.width;
+ } else {
+ break;
+ }
+ }
+ if (pruneCount > 0) {
+ translateX.current += removedWidth;
+ setScrollItems((prev) => prev.slice(pruneCount));
+ }
+ }
+
+ // Assign the next loop
+ rafRef.current = requestAnimationFrame(loop);
+ };
+
+ rafRef.current = requestAnimationFrame(loop);
+ return () => cancelAnimationFrame(rafRef.current!);
+ }, []);
+
+ return (
+
+ {/* Ticker wrapper */}
+
+ {/* The ticker band */}
+
+ {/* Scrolling strip */}
+
+ {scrollItems.map((item) => (
+
+ ))}
+
+ {/* Trailing spacer so new content doesn't immediately snap in */}
+
+
- );
+
+
+ );
}
--
cgit v1.2.3
From cb5e40f9b5fb0ae4bb9a769dd7a9c83c2af18a65 Mon Sep 17 00:00:00 2001
From: Max Nanis
Date: Fri, 11 Sep 2026 07:49:51 -0700
Subject: Transfer page. Jenkins CDN. Layout url check
---
jb-ui/Jenkinsfile | 6 +-
jb-ui/src/JBApp.tsx | 18 ++--
jb-ui/src/components/EventMarquee.tsx | 2 +-
jb-ui/src/pages/LinkAmtAccount.tsx | 84 -----------------
jb-ui/src/pages/Transfer.tsx | 171 ++++++++++++++++++++++++++++------
jb-ui/vite.config.ts | 102 ++++++++++----------
6 files changed, 207 insertions(+), 176 deletions(-)
delete mode 100644 jb-ui/src/pages/LinkAmtAccount.tsx
(limited to 'jb-ui/src/components/EventMarquee.tsx')
diff --git a/jb-ui/Jenkinsfile b/jb-ui/Jenkinsfile
index 27a0ab9..faf63de 100644
--- a/jb-ui/Jenkinsfile
+++ b/jb-ui/Jenkinsfile
@@ -82,7 +82,7 @@ pipeline {
expression { env.BRANCH_NAME == 'master' }
}
steps {
- sh "/usr/bin/rsync -avz ${WORKSPACE}/jb-ui/${env.BUILD_DIR}/ thl-cdn:/root/amt-jb-cdn/"
+ sh "/usr/bin/rsync -avz ${WORKSPACE}/jb-ui/${env.BUILD_DIR}/ cdn-jb:/var/www/amt-jb-cdn/"
}
}
@@ -101,7 +101,7 @@ pipeline {
sh "cp ${env.JS_FILENAME}.js ${versioned_js}.js"
sh "cp ${env.CSS_FILENAME}.js ${versioned_css}.js"
- sh "/usr/bin/rsync -avz ${WORKSPACE}/jb-ui/${env.BUILD_DIR}/ thl-cdn:/root/amt-jb-cdn/"
+ sh "/usr/bin/rsync -avz ${WORKSPACE}/jb-ui/${env.BUILD_DIR}/ cdn-jb:/var/www/amt-jb-cdn/"
}
}
}
@@ -126,7 +126,7 @@ pipeline {
sh "cp ${env.JS_FILENAME}.js ${versioned}.js"
sh "cp ${env.CSS_FILENAME}.js ${versioned}.js"
- sh "/usr/bin/rsync -avz ${WORKSPACE}/jb-ui/${env.BUILD_DIR}/ thl-cdn:/root/amt-jb-cdn/"
+ sh "/usr/bin/rsync -avz ${WORKSPACE}/jb-ui/${env.BUILD_DIR}/ cdn-jb:/var/www/amt-jb-cdn/"
}
}
}
diff --git a/jb-ui/src/JBApp.tsx b/jb-ui/src/JBApp.tsx
index 472b976..7c99315 100644
--- a/jb-ui/src/JBApp.tsx
+++ b/jb-ui/src/JBApp.tsx
@@ -27,11 +27,15 @@ import Home from "@/pages/Home";
import MagicLink from "@/pages/MagicLink";
import Preview from "@/pages/Preview";
import Result from "@/pages/Result";
+import clsx from "clsx";
+
import Work from "@/pages/Work";
import Transfer from "@/pages/Transfer";
import {
BrowserRouter,
Outlet,
+ useLocation,
+ useMatch,
Route,
Routes,
useSearchParams,
@@ -118,6 +122,9 @@ function QueryParamProcessor() {
}
const Layout = () => {
+ const location = useLocation();
+ const isSpecialSubview = useMatch("/work/*");
+
return (
@@ -134,13 +141,10 @@ const Layout = () => {
diff --git a/jb-ui/src/components/EventMarquee.tsx b/jb-ui/src/components/EventMarquee.tsx
index 6aab3f8..46508ff 100644
--- a/jb-ui/src/components/EventMarquee.tsx
+++ b/jb-ui/src/components/EventMarquee.tsx
@@ -148,7 +148,7 @@ export default function NewsTicker() {
// the browser, which is more accurate for measuring frame intervals
// than Date.now()
const loop = (timestamp: number) => {
- console.log("RAF loop, timestamp:", timestamp, lastTime.current);
+ // console.log("RAF loop, timestamp:", timestamp, lastTime.current);
if (!lastTime.current) lastTime.current = timestamp;
const delta = (timestamp - lastTime.current) / 1000;
diff --git a/jb-ui/src/pages/LinkAmtAccount.tsx b/jb-ui/src/pages/LinkAmtAccount.tsx
deleted file mode 100644
index a72626f..0000000
--- a/jb-ui/src/pages/LinkAmtAccount.tsx
+++ /dev/null
@@ -1,84 +0,0 @@
-import { type FormEvent, useState } from "react";
-
-import { Button } from "@/components/ui/button";
-
-type RequestState = "idle" | "working" | "sent" | "failed" | "missing-worker";
-
-const LinkAmtAccount = function () {
- const [email, setEmail] = useState("");
- const [state, setState] = useState
("idle");
- const [mockLink, setMockLink] = useState(null);
- const amtWorkerId = new URLSearchParams(window.location.search).get(
- "workerId",
- );
-
- const submit = async (event: FormEvent) => {
- event.preventDefault();
- if (!amtWorkerId) {
- setState("missing-worker");
- return;
- }
-
- setState("working");
- setMockLink(null);
- try {
- const response = await fetch("/auth/link-amt/request", {
- method: "POST",
- credentials: "include",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({
- email: email,
- amt_worker_id: amtWorkerId,
- }),
- });
- if (!response.ok) throw new Error("Account-link request failed");
- const body = (await response.json()) as { magic_link?: string };
- setMockLink(body.magic_link ?? null);
- setState("sent");
- } catch {
- setState("failed");
- }
- };
-
- return (
-
-
- Link your MTurk account
- Enter the email address you want to use for James Billings.
-
- {state === "sent" && (
- Check your email to finish linking your account.
- )}
- {state === "failed" && (
- Unable to send the link. Please try again.
- )}
- {state === "missing-worker" && (
- This page is missing your MTurk worker ID.
- )}
- {mockLink && (
-
- Development link:{" "}
-
- continue
-
-
- )}
-
-
- );
-};
-
-export default LinkAmtAccount;
diff --git a/jb-ui/src/pages/Transfer.tsx b/jb-ui/src/pages/Transfer.tsx
index 6e087d6..bef40d4 100644
--- a/jb-ui/src/pages/Transfer.tsx
+++ b/jb-ui/src/pages/Transfer.tsx
@@ -1,50 +1,167 @@
-import { clsx } from "clsx";
+import { type FormEvent, useState } from "react";
+import { useAppDispatch, useAppSelector } from "@/hooks";
+import { Button } from "@/components/ui/button";
+import max_lulu from "./max-and-lulu.jpg";
-import { useSelector } from "react-redux";
+// const handleInputChange = (
+// event: React.KeyboardEvent,
+// ) => {};
-const Transfer = function () {
- let error = [];
+// const handleKeyPress = (event: KeyboardEvent) => {};
+// const handleSubmit = (e) => {
+// e.preventDefault();
+// console.log("Form submitted successfully!");
+// };
+
+const TransferComplete = function () {
+ return (
+
+
+ Please check your email.
+
+
+ If you don't recieve anything, please check your spam folder, or try
+ again shortly.
+
+
+ We will be assisting account transfers throughout the rest of the month.
+ If you have any further questions or concerns, please email us directly
+ with your desired account email and the AMT ID to associate with your
+ worker history.
+
+
+
+ );
+};
+
+type RequestState = "idle" | "working" | "sent" | "failed";
- const handleInputChange = (
- event: React.KeyboardEvent,
- ) => {};
+const TransferSubmit = function ({ onSubmitSuccess }) {
+ const [email, setEmail] = useState("");
+ const [state, setState] = useState("idle");
+ const bpuid = useAppSelector((state) => state.app.bpuid);
- const handleKeyPress = (event: KeyboardEvent) => {};
+ const submit = async (event: FormEvent) => {
+ event.preventDefault();
+ setState("working");
- const handleSubmit = (e) => {
- e.preventDefault();
- console.log("Form submitted successfully!");
+ try {
+ const response = await fetch("/auth/link-amt/request", {
+ method: "POST",
+ credentials: "include",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ email: email,
+ amt_worker_id: bpuid,
+ }),
+ });
+
+ if (!response.ok) throw new Error("Account-link request failed");
+
+ const body = (await response.json()) as { magic_link?: string };
+ console.log(body.magic_link ?? null);
+ onSubmitSuccess();
+ } catch {
+ setState("failed");
+ }
};
return (
Transfer Required
-
- James Billings is finished 😭 Just Kidding!!!
-
+ James Billings is no longer available on Amazon Mechanical Turk. Our
+ dedicated platform will enable you to do the same work as always, paid
+ directly to you.
+
+ Enter your email below to transfer your account history.
+
+ This will allow you to keep your full account history and wallet
+ balance.
+
+
+
-
+ {state === "sent" && (
+
+ Check your email to finish linking your account.
+
+ )}
+
+ {state === "failed" &&
Unable to send the link. Please try again.
}
);
};
+const Transfer = function () {
+ const assignment_id = useAppSelector((state) => state.app.assignment_id);
+ const bpuid = useAppSelector((state) => state.app.bpuid);
+ const [submitted, setSubmitted] = useState(false);
+
+ if (!bpuid) {
+ return (
+
+ No Worker ID found. Please access this page through the Amazon
+ Mechanical Turk platform.
+
+ );
+ }
+
+ if (!assignment_id) {
+ return (
+
+ No Assignment ID found. Please access this page through the Amazon
+ Mechanical Turk platform.
+
+ );
+ }
+
+ return (
+ <>
+ {submitted ? (
+
+ ) : (
+ setSubmitted(true)} />
+ )}
+ >
+ );
+};
+
export default Transfer;
diff --git a/jb-ui/vite.config.ts b/jb-ui/vite.config.ts
index 6b0e530..2336e4a 100644
--- a/jb-ui/vite.config.ts
+++ b/jb-ui/vite.config.ts
@@ -1,62 +1,56 @@
-import react from "@vitejs/plugin-react"
-import {defineConfig} from "vite"
-import path from 'path';
-import tailwindcss from '@tailwindcss/vite'
-
+import react from "@vitejs/plugin-react";
+import { defineConfig } from "vite";
+import path from "path";
+import tailwindcss from "@tailwindcss/vite";
export default defineConfig({
- plugins: [
- react(),
- tailwindcss(),
-
- ],
+ plugins: [react(), tailwindcss()],
- resolve: {
- alias: {
- '@': path.resolve(__dirname, './src'),
- },
+ resolve: {
+ alias: {
+ "@": path.resolve(__dirname, "./src"),
},
-
- server: {
- // host: "0.0.0.0",
- // port: 8001,
-
- // This forwards requests to the FastAPI development server
- // that must also be running
- proxy: {
- '^/(status|statuses|report|survey)(/|$)': {
- target: 'http://localhost:8000',
- changeOrigin: true,
- }
- }
+ },
+
+ server: {
+ // host: "0.0.0.0",
+ // port: 8001,
+
+ // This forwards requests to the FastAPI development server
+ // that must also be running
+ proxy: {
+ "^/(auth|status|statuses|report|survey)(/|$)": {
+ target: "http://localhost:8000",
+ changeOrigin: true,
+ },
},
+ },
- define: {
- 'process.env.NODE_ENV': JSON.stringify('production')
- },
+ define: {
+ "process.env.NODE_ENV": JSON.stringify("production"),
+ },
- build: {
- cssCodeSplit: false,
- lib: {
- name: "jb-ui",
- entry: 'src/main.tsx',
+ build: {
+ cssCodeSplit: false,
+ lib: {
+ name: "jb-ui",
+ entry: "src/main.tsx",
+ },
+ rollupOptions: {
+ output: {
+ entryFileNames: "james-billings.js",
+ assetFileNames: (assetInfo) => {
+ if (assetInfo.names[0].endsWith(".css")) {
+ return "james-has-style.css";
+ }
+
+ return "assets/[name].[hash][extname]";
},
- rollupOptions: {
- output: {
- entryFileNames: 'james-billings.js',
- assetFileNames: (assetInfo) => {
-
- if (assetInfo.names[0].endsWith('.css')) {
- return 'james-has-style.css';
- }
-
- return 'assets/[name].[hash][extname]';
- },
- globals: {
- react: 'React',
- 'react-dom': 'ReactDOM'
- }
- }
- }
- }
-})
\ No newline at end of file
+ globals: {
+ react: "React",
+ "react-dom": "ReactDOM",
+ },
+ },
+ },
+ },
+});
--
cgit v1.2.3