feature/IO-3499-React-19 - Checkpoint
This commit is contained in:
@@ -3,11 +3,10 @@ import * as Sentry from "@sentry/react";
|
|||||||
import { SplitFactoryProvider, useSplitClient } from "@splitsoftware/splitio-react";
|
import { SplitFactoryProvider, useSplitClient } from "@splitsoftware/splitio-react";
|
||||||
import { ConfigProvider } from "antd";
|
import { ConfigProvider } from "antd";
|
||||||
import enLocale from "antd/es/locale/en_US";
|
import enLocale from "antd/es/locale/en_US";
|
||||||
import { useEffect, useMemo } from "react";
|
import { useEffect } from "react";
|
||||||
import { CookiesProvider } from "react-cookie";
|
import { CookiesProvider } from "react-cookie";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
|
||||||
import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component";
|
import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component";
|
||||||
import { setDarkMode } from "../redux/application/application.actions";
|
import { setDarkMode } from "../redux/application/application.actions";
|
||||||
import { selectDarkMode } from "../redux/application/application.selectors";
|
import { selectDarkMode } from "../redux/application/application.selectors";
|
||||||
@@ -28,93 +27,99 @@ const config = {
|
|||||||
function SplitClientProvider({ children }) {
|
function SplitClientProvider({ children }) {
|
||||||
const imexshopid = useSelector((state) => state.user.imexshopid);
|
const imexshopid = useSelector((state) => state.user.imexshopid);
|
||||||
const splitClient = useSplitClient({ key: imexshopid || "anon" });
|
const splitClient = useSplitClient({ key: imexshopid || "anon" });
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (splitClient && imexshopid) {
|
if (import.meta.env.DEV && splitClient && imexshopid) {
|
||||||
console.log(`Split client initialized with key: ${imexshopid}, isReady: ${splitClient.isReady}`);
|
console.log(`Split client initialized with key: ${imexshopid}, isReady: ${splitClient.isReady}`);
|
||||||
}
|
}
|
||||||
}, [splitClient, imexshopid]);
|
}, [splitClient, imexshopid]);
|
||||||
|
|
||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
function AppContainer() {
|
||||||
currentUser: selectCurrentUser
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
|
||||||
setDarkMode: (isDarkMode) => dispatch(setDarkMode(isDarkMode)),
|
|
||||||
signOutStart: () => dispatch(signOutStart())
|
|
||||||
});
|
|
||||||
|
|
||||||
function AppContainer({ currentUser, setDarkMode, signOutStart }) {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const currentUser = useSelector(selectCurrentUser);
|
||||||
const isDarkMode = useSelector(selectDarkMode);
|
const isDarkMode = useSelector(selectDarkMode);
|
||||||
const theme = useMemo(() => getTheme(isDarkMode), [isDarkMode]);
|
|
||||||
|
const theme = () => getTheme(isDarkMode);
|
||||||
|
|
||||||
|
const antdInput = () => ({ autoComplete: "new-password" });
|
||||||
|
|
||||||
|
const antdForm = () => ({
|
||||||
|
validateMessages: {
|
||||||
|
required: t("general.validation.required", { label: "${label}" })
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Global seamless logout listener with redirect to /signin
|
// Global seamless logout listener with redirect to /signin
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleSeamlessLogout = (event) => {
|
const handleSeamlessLogout = (event) => {
|
||||||
if (event.data?.type !== "seamlessLogoutRequest") return;
|
if (event.data?.type !== "seamlessLogoutRequest") return;
|
||||||
|
|
||||||
const requestOrigin = event.origin;
|
// Only accept messages from the parent window
|
||||||
|
if (event.source !== window.parent) return;
|
||||||
|
|
||||||
|
const targetOrigin = event.origin || "*";
|
||||||
|
|
||||||
if (currentUser?.authorized !== true) {
|
if (currentUser?.authorized !== true) {
|
||||||
window.parent.postMessage(
|
window.parent?.postMessage({ type: "seamlessLogoutResponse", status: "already_logged_out" }, targetOrigin);
|
||||||
{ type: "seamlessLogoutResponse", status: "already_logged_out" },
|
|
||||||
requestOrigin || "*"
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
signOutStart();
|
dispatch(signOutStart());
|
||||||
window.parent.postMessage({ type: "seamlessLogoutResponse", status: "logged_out" }, requestOrigin || "*");
|
window.parent?.postMessage({ type: "seamlessLogoutResponse", status: "logged_out" }, targetOrigin);
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener("message", handleSeamlessLogout);
|
window.addEventListener("message", handleSeamlessLogout);
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener("message", handleSeamlessLogout);
|
window.removeEventListener("message", handleSeamlessLogout);
|
||||||
};
|
};
|
||||||
}, [signOutStart, currentUser]);
|
}, [dispatch, currentUser?.authorized]);
|
||||||
|
|
||||||
// Update data-theme attribute
|
// Update data-theme attribute (no cleanup to avoid transient style churn)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.documentElement.setAttribute("data-theme", isDarkMode ? "dark" : "light");
|
document.documentElement.dataset.theme = isDarkMode ? "dark" : "light";
|
||||||
return () => document.documentElement.removeAttribute("data-theme");
|
|
||||||
}, [isDarkMode]);
|
}, [isDarkMode]);
|
||||||
|
|
||||||
// Sync darkMode with localStorage
|
// Sync darkMode with localStorage
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentUser?.uid) {
|
const uid = currentUser?.uid;
|
||||||
const savedMode = localStorage.getItem(`dark-mode-${currentUser.uid}`);
|
|
||||||
if (savedMode !== null) {
|
if (!uid) {
|
||||||
setDarkMode(JSON.parse(savedMode));
|
dispatch(setDarkMode(false));
|
||||||
} else {
|
return;
|
||||||
setDarkMode(false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setDarkMode(false);
|
|
||||||
}
|
}
|
||||||
}, [currentUser?.uid, setDarkMode]);
|
|
||||||
|
const key = `dark-mode-${uid}`;
|
||||||
|
const raw = localStorage.getItem(key);
|
||||||
|
|
||||||
|
if (raw == null) {
|
||||||
|
dispatch(setDarkMode(false));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
dispatch(setDarkMode(Boolean(JSON.parse(raw))));
|
||||||
|
} catch {
|
||||||
|
dispatch(setDarkMode(false));
|
||||||
|
}
|
||||||
|
}, [currentUser?.uid, dispatch]);
|
||||||
|
|
||||||
// Persist darkMode
|
// Persist darkMode
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentUser?.uid) {
|
const uid = currentUser?.uid;
|
||||||
localStorage.setItem(`dark-mode-${currentUser.uid}`, JSON.stringify(isDarkMode));
|
if (!uid) return;
|
||||||
}
|
|
||||||
|
localStorage.setItem(`dark-mode-${uid}`, JSON.stringify(isDarkMode));
|
||||||
}, [isDarkMode, currentUser?.uid]);
|
}, [isDarkMode, currentUser?.uid]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CookiesProvider>
|
<CookiesProvider>
|
||||||
<ApolloProvider client={client}>
|
<ApolloProvider client={client}>
|
||||||
<ConfigProvider
|
<ConfigProvider input={antdInput} locale={enLocale} theme={theme} form={antdForm}>
|
||||||
input={{ autoComplete: "new-password" }}
|
|
||||||
locale={enLocale}
|
|
||||||
theme={theme}
|
|
||||||
form={{
|
|
||||||
validateMessages: {
|
|
||||||
required: t("general.validation.required", { label: "${label}" })
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<GlobalLoadingBar />
|
<GlobalLoadingBar />
|
||||||
<SplitFactoryProvider config={config}>
|
<SplitFactoryProvider config={config}>
|
||||||
<SplitClientProvider>
|
<SplitClientProvider>
|
||||||
@@ -127,4 +132,4 @@ function AppContainer({ currentUser, setDarkMode, signOutStart }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Sentry.withProfiler(connect(mapStateToProps, mapDispatchToProps)(AppContainer));
|
export default Sentry.withProfiler(AppContainer);
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ const NotificationCenterComponent = ({
|
|||||||
onNotificationClick,
|
onNotificationClick,
|
||||||
unreadCount,
|
unreadCount,
|
||||||
isEmployee,
|
isEmployee,
|
||||||
|
isDarkMode,
|
||||||
ref
|
ref
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -112,14 +113,16 @@ const NotificationCenterComponent = ({
|
|||||||
<Alert title={t("notifications.labels.employee-notification")} type="warning" />
|
<Alert title={t("notifications.labels.employee-notification")} type="warning" />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Virtuoso
|
<div className={isDarkMode ? "notification-center--dark" : "notification-center--light"} style={{ height: "400px", width: "100%" }}>
|
||||||
ref={virtuosoRef}
|
<Virtuoso
|
||||||
style={{ height: "400px", width: "100%" }}
|
ref={virtuosoRef}
|
||||||
data={notifications}
|
style={{ height: "100%", width: "100%" }}
|
||||||
totalCount={notifications.length}
|
data={notifications}
|
||||||
endReached={loadMore}
|
totalCount={notifications.length}
|
||||||
itemContent={renderNotification}
|
endReached={loadMore}
|
||||||
/>
|
itemContent={renderNotification}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import NotificationCenterComponent from "./notification-center.component";
|
|||||||
import { GET_NOTIFICATIONS } from "../../graphql/notifications.queries";
|
import { GET_NOTIFICATIONS } from "../../graphql/notifications.queries";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors.js";
|
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors.js";
|
||||||
|
import { selectDarkMode } from "../../redux/application/application.selectors.js";
|
||||||
import day from "../../utils/day.js";
|
import day from "../../utils/day.js";
|
||||||
import { INITIAL_NOTIFICATIONS, useSocket } from "../../contexts/SocketIO/useSocket.js";
|
import { INITIAL_NOTIFICATIONS, useSocket } from "../../contexts/SocketIO/useSocket.js";
|
||||||
import { useIsEmployee } from "../../utils/useIsEmployee.js";
|
import { useIsEmployee } from "../../utils/useIsEmployee.js";
|
||||||
@@ -22,7 +23,7 @@ const NOTIFICATION_POLL_INTERVAL_SECONDS = 60;
|
|||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const NotificationCenterContainer = ({ visible, onClose, bodyshop, unreadCount, currentUser }) => {
|
const NotificationCenterContainer = ({ visible, onClose, bodyshop, unreadCount, currentUser, isDarkMode }) => {
|
||||||
const [showUnreadOnly, setShowUnreadOnly] = useState(false);
|
const [showUnreadOnly, setShowUnreadOnly] = useState(false);
|
||||||
const [notifications, setNotifications] = useState([]);
|
const [notifications, setNotifications] = useState([]);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
@@ -213,13 +214,15 @@ const NotificationCenterContainer = ({ visible, onClose, bodyshop, unreadCount,
|
|||||||
loadMore={loadMore}
|
loadMore={loadMore}
|
||||||
onNotificationClick={handleNotificationClick}
|
onNotificationClick={handleNotificationClick}
|
||||||
unreadCount={unreadCount}
|
unreadCount={unreadCount}
|
||||||
|
isDarkMode={isDarkMode}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
currentUser: selectCurrentUser
|
currentUser: selectCurrentUser,
|
||||||
|
isDarkMode: selectDarkMode
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, null)(NotificationCenterContainer);
|
export default connect(mapStateToProps, null)(NotificationCenterContainer);
|
||||||
|
|||||||
@@ -173,3 +173,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.notification-center--dark {
|
||||||
|
color-scheme: dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-center--light {
|
||||||
|
color-scheme: light;
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ const TaskCenterComponent = ({
|
|||||||
hasMore,
|
hasMore,
|
||||||
createNewTask,
|
createNewTask,
|
||||||
incompleteTaskCount,
|
incompleteTaskCount,
|
||||||
|
isDarkMode,
|
||||||
ref
|
ref
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -140,22 +141,24 @@ const TaskCenterComponent = ({
|
|||||||
{tasks.length === 0 && !loading ? (
|
{tasks.length === 0 && !loading ? (
|
||||||
<div className="no-tasks-message">{t("tasks.labels.no_tasks")}</div>
|
<div className="no-tasks-message">{t("tasks.labels.no_tasks")}</div>
|
||||||
) : (
|
) : (
|
||||||
<Virtuoso
|
<div className={isDarkMode ? "task-center--dark" : "task-center--light"} style={{ height: "550px", width: "100%" }}>
|
||||||
ref={virtuosoRef}
|
<Virtuoso
|
||||||
style={{ height: "550px", width: "100%" }}
|
ref={virtuosoRef}
|
||||||
groupCounts={groupCounts}
|
style={{ height: "100%", width: "100%" }}
|
||||||
groupContent={groupContent}
|
groupCounts={groupCounts}
|
||||||
itemContent={itemContent}
|
groupContent={groupContent}
|
||||||
endReached={hasMore && !loading ? onLoadMore : undefined}
|
itemContent={itemContent}
|
||||||
components={{
|
endReached={hasMore && !loading ? onLoadMore : undefined}
|
||||||
Footer: () =>
|
components={{
|
||||||
loading ? (
|
Footer: () =>
|
||||||
<div className="loading-footer">
|
loading ? (
|
||||||
<Spin />
|
<div className="loading-footer">
|
||||||
</div>
|
<Spin />
|
||||||
) : null
|
</div>
|
||||||
}}
|
) : null
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useQuery } from "@apollo/client/react";
|
|||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||||
|
import { selectDarkMode } from "../../redux/application/application.selectors.js";
|
||||||
import { INITIAL_TASKS, TASKS_CENTER_POLL_INTERVAL, useSocket } from "../../contexts/SocketIO/useSocket";
|
import { INITIAL_TASKS, TASKS_CENTER_POLL_INTERVAL, useSocket } from "../../contexts/SocketIO/useSocket";
|
||||||
import { useIsEmployee } from "../../utils/useIsEmployee";
|
import { useIsEmployee } from "../../utils/useIsEmployee";
|
||||||
import TaskCenterComponent from "./task-center.component";
|
import TaskCenterComponent from "./task-center.component";
|
||||||
@@ -11,7 +12,8 @@ import { QUERY_TASKS_NO_DUE_DATE_PAGINATED, QUERY_TASKS_WITH_DUE_DATES } from ".
|
|||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
currentUser: selectCurrentUser
|
currentUser: selectCurrentUser,
|
||||||
|
isDarkMode: selectDarkMode
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
@@ -24,7 +26,8 @@ const TaskCenterContainer = ({
|
|||||||
bodyshop,
|
bodyshop,
|
||||||
currentUser,
|
currentUser,
|
||||||
setTaskUpsertContext,
|
setTaskUpsertContext,
|
||||||
incompleteTaskCount
|
incompleteTaskCount,
|
||||||
|
isDarkMode
|
||||||
}) => {
|
}) => {
|
||||||
const [tasks, setTasks] = useState([]);
|
const [tasks, setTasks] = useState([]);
|
||||||
const { isConnected } = useSocket();
|
const { isConnected } = useSocket();
|
||||||
@@ -128,6 +131,7 @@ const TaskCenterContainer = ({
|
|||||||
hasMore={hasMore}
|
hasMore={hasMore}
|
||||||
createNewTask={createNewTask}
|
createNewTask={createNewTask}
|
||||||
incompleteTaskCount={incompleteTaskCount}
|
incompleteTaskCount={incompleteTaskCount}
|
||||||
|
isDarkMode={isDarkMode}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -141,3 +141,11 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.task-center--dark {
|
||||||
|
color-scheme: dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-center--light {
|
||||||
|
color-scheme: light;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import "./utils/sentry"; //Must be first.
|
import "./utils/sentry"; // Must be first.
|
||||||
import * as Sentry from "@sentry/react";
|
import * as Sentry from "@sentry/react";
|
||||||
import { ConfigProvider } from "antd";
|
|
||||||
import Dinero from "dinero.js";
|
import Dinero from "dinero.js";
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
import { Provider } from "react-redux";
|
import { Provider } from "react-redux";
|
||||||
@@ -14,7 +13,7 @@ import { persistor, store } from "./redux/store";
|
|||||||
import reportWebVitals from "./reportWebVitals";
|
import reportWebVitals from "./reportWebVitals";
|
||||||
import "./translations/i18n";
|
import "./translations/i18n";
|
||||||
import "./utils/CleanAxios";
|
import "./utils/CleanAxios";
|
||||||
//import * as amplitude from "@amplitude/analytics-browser";
|
// import * as amplitude from "@amplitude/analytics-browser";
|
||||||
import { PostHogProvider } from "posthog-js/react";
|
import { PostHogProvider } from "posthog-js/react";
|
||||||
import posthog from "posthog-js";
|
import posthog from "posthog-js";
|
||||||
|
|
||||||
@@ -52,39 +51,32 @@ posthog.init(import.meta.env.VITE_PUBLIC_POSTHOG_KEY, {
|
|||||||
|
|
||||||
const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouterV6(createBrowserRouter);
|
const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouterV6(createBrowserRouter);
|
||||||
|
|
||||||
const router = sentryCreateBrowserRouter(
|
const router = sentryCreateBrowserRouter(createRoutesFromElements(<Route path="*" element={<AppContainer />} />), {
|
||||||
createRoutesFromElements(<Route path="*" element={<AppContainer />} />),
|
future: {
|
||||||
{
|
v7_startTransition: true,
|
||||||
future: {
|
v7_relativeSplatPath: true
|
||||||
v7_startTransition: true,
|
|
||||||
v7_relativeSplatPath: true,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
|
|
||||||
if (import.meta.env.DEV) {
|
if (import.meta.env.DEV) {
|
||||||
let styles =
|
const styles =
|
||||||
"font-weight: bold; font-size: 50px;color: red; 6px 6px 0 rgb(226,91,14) , 9px 9px 0 rgb(245,221,8) , 12px 12px 0 rgb(5,148,68) ";
|
"font-weight: bold; font-size: 50px;color: red; 6px 6px 0 rgb(226,91,14) , 9px 9px 0 rgb(245,221,8) , 12px 12px 0 rgb(5,148,68) ";
|
||||||
|
|
||||||
console.log("%c %s", styles, `VER: ${import.meta.env.VITE_APP_INSTANCE}`);
|
console.log("%c %s", styles, `VER: ${import.meta.env.VITE_APP_INSTANCE}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<PersistGate loading={<LoadingSpinner message="Restoring your settings..." />} persistor={persistor}>
|
<Provider store={store}>
|
||||||
<Provider store={store}>
|
<PersistGate loading={<LoadingSpinner message="Restoring your settings..." />} persistor={persistor}>
|
||||||
<PostHogProvider client={posthog}>
|
<PostHogProvider client={posthog}>
|
||||||
<RouterProvider router={router} />
|
<RouterProvider router={router} />
|
||||||
</PostHogProvider>
|
</PostHogProvider>
|
||||||
</Provider>
|
</PersistGate>
|
||||||
</PersistGate>
|
</Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used for ANTD Component Tokens
|
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
|
||||||
// https://ant.design/docs/react/migrate-less-variables
|
|
||||||
ReactDOM.createRoot(document.getElementById("root")).render(
|
|
||||||
<ConfigProvider>
|
|
||||||
<App />
|
|
||||||
</ConfigProvider>
|
|
||||||
);
|
|
||||||
|
|
||||||
reportWebVitals();
|
reportWebVitals();
|
||||||
|
|||||||
Reference in New Issue
Block a user