release/2026-02-27 - Disable Responsive Design
This commit is contained in:
184
client/src/App/App.container.backup-2026-03-04.jsx
Normal file
184
client/src/App/App.container.backup-2026-03-04.jsx
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
import { ApolloProvider } from "@apollo/client/react";
|
||||||
|
import * as Sentry from "@sentry/react";
|
||||||
|
import { SplitFactoryProvider, useSplitClient } from "@splitsoftware/splitio-react";
|
||||||
|
import { ConfigProvider, Grid } from "antd";
|
||||||
|
import enLocale from "antd/es/locale/en_US";
|
||||||
|
import { useEffect, useMemo } from "react";
|
||||||
|
import { CookiesProvider } from "react-cookie";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component";
|
||||||
|
import { setDarkMode } from "../redux/application/application.actions";
|
||||||
|
import { selectDarkMode } from "../redux/application/application.selectors";
|
||||||
|
import { selectCurrentUser } from "../redux/user/user.selectors.js";
|
||||||
|
import { signOutStart } from "../redux/user/user.actions";
|
||||||
|
import client from "../utils/GraphQLClient";
|
||||||
|
import App from "./App";
|
||||||
|
import getTheme from "./themeProvider";
|
||||||
|
|
||||||
|
// Base Split configuration
|
||||||
|
const config = {
|
||||||
|
core: {
|
||||||
|
authorizationKey: import.meta.env.VITE_APP_SPLIT_API,
|
||||||
|
key: "anon"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function SplitClientProvider({ children }) {
|
||||||
|
const imexshopid = useSelector((state) => state.user.imexshopid);
|
||||||
|
const splitClient = useSplitClient({ key: imexshopid || "anon" });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (import.meta.env.DEV && splitClient && imexshopid) {
|
||||||
|
console.log(`Split client initialized with key: ${imexshopid}, isReady: ${splitClient.isReady}`);
|
||||||
|
}
|
||||||
|
}, [splitClient, imexshopid]);
|
||||||
|
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
function AppContainer() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const currentUser = useSelector(selectCurrentUser);
|
||||||
|
const isDarkMode = useSelector(selectDarkMode);
|
||||||
|
const screens = Grid.useBreakpoint();
|
||||||
|
const isPhone = !screens.md;
|
||||||
|
const isUltraWide = Boolean(screens.xxxl);
|
||||||
|
|
||||||
|
const theme = useMemo(() => {
|
||||||
|
const baseTheme = getTheme(isDarkMode);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...baseTheme,
|
||||||
|
token: {
|
||||||
|
...(baseTheme.token || {}),
|
||||||
|
screenXXXL: 2160
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
...(baseTheme.components || {}),
|
||||||
|
Table: {
|
||||||
|
...(baseTheme.components?.Table || {}),
|
||||||
|
cellFontSizeSM: isPhone ? 12 : 13,
|
||||||
|
cellFontSizeMD: isPhone ? 13 : isUltraWide ? 15 : 14,
|
||||||
|
cellFontSize: isUltraWide ? 15 : 14,
|
||||||
|
cellPaddingInlineSM: isPhone ? 8 : 10,
|
||||||
|
cellPaddingInlineMD: isPhone ? 10 : 14,
|
||||||
|
cellPaddingInline: isUltraWide ? 20 : 16,
|
||||||
|
cellPaddingBlockSM: isPhone ? 8 : 10,
|
||||||
|
cellPaddingBlockMD: isPhone ? 10 : 12,
|
||||||
|
cellPaddingBlock: isUltraWide ? 14 : 12,
|
||||||
|
selectionColumnWidth: isPhone ? 44 : 52
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [isDarkMode, isPhone, isUltraWide]);
|
||||||
|
|
||||||
|
const antdInput = useMemo(() => ({ autoComplete: "new-password" }), []);
|
||||||
|
const antdTable = useMemo(() => ({ scroll: { x: "max-content" } }), []);
|
||||||
|
const antdPagination = useMemo(
|
||||||
|
() => ({
|
||||||
|
showSizeChanger: !isPhone,
|
||||||
|
totalBoundaryShowSizeChanger: 100
|
||||||
|
}),
|
||||||
|
[isPhone]
|
||||||
|
);
|
||||||
|
|
||||||
|
const antdForm = useMemo(
|
||||||
|
() => ({
|
||||||
|
validateMessages: {
|
||||||
|
required: t("general.validation.required", { label: "${label}" })
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
[t]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Global seamless logout listener with redirect to /signin
|
||||||
|
useEffect(() => {
|
||||||
|
const handleSeamlessLogout = (event) => {
|
||||||
|
if (event.data?.type !== "seamlessLogoutRequest") return;
|
||||||
|
|
||||||
|
// Only accept messages from the parent window
|
||||||
|
if (event.source !== window.parent) return;
|
||||||
|
|
||||||
|
const targetOrigin = event.origin || "*";
|
||||||
|
|
||||||
|
if (currentUser?.authorized !== true) {
|
||||||
|
window.parent?.postMessage({ type: "seamlessLogoutResponse", status: "already_logged_out" }, targetOrigin);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(signOutStart());
|
||||||
|
window.parent?.postMessage({ type: "seamlessLogoutResponse", status: "logged_out" }, targetOrigin);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("message", handleSeamlessLogout);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("message", handleSeamlessLogout);
|
||||||
|
};
|
||||||
|
}, [dispatch, currentUser?.authorized]);
|
||||||
|
|
||||||
|
// Update data-theme attribute (no cleanup to avoid transient style churn)
|
||||||
|
useEffect(() => {
|
||||||
|
document.documentElement.dataset.theme = isDarkMode ? "dark" : "light";
|
||||||
|
}, [isDarkMode]);
|
||||||
|
|
||||||
|
// Sync darkMode with localStorage
|
||||||
|
useEffect(() => {
|
||||||
|
const uid = currentUser?.uid;
|
||||||
|
|
||||||
|
if (!uid) {
|
||||||
|
dispatch(setDarkMode(false));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
useEffect(() => {
|
||||||
|
const uid = currentUser?.uid;
|
||||||
|
if (!uid) return;
|
||||||
|
|
||||||
|
localStorage.setItem(`dark-mode-${uid}`, JSON.stringify(isDarkMode));
|
||||||
|
}, [isDarkMode, currentUser?.uid]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CookiesProvider>
|
||||||
|
<ApolloProvider client={client}>
|
||||||
|
<ConfigProvider
|
||||||
|
input={antdInput}
|
||||||
|
locale={enLocale}
|
||||||
|
theme={theme}
|
||||||
|
form={antdForm}
|
||||||
|
table={antdTable}
|
||||||
|
pagination={antdPagination}
|
||||||
|
componentSize={isPhone ? "small" : isUltraWide ? "large" : "middle"}
|
||||||
|
popupOverflow="viewport"
|
||||||
|
>
|
||||||
|
<GlobalLoadingBar />
|
||||||
|
<SplitFactoryProvider config={config}>
|
||||||
|
<SplitClientProvider>
|
||||||
|
<App />
|
||||||
|
</SplitClientProvider>
|
||||||
|
</SplitFactoryProvider>
|
||||||
|
</ConfigProvider>
|
||||||
|
</ApolloProvider>
|
||||||
|
</CookiesProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Sentry.withProfiler(AppContainer);
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ApolloProvider } from "@apollo/client/react";
|
import { ApolloProvider } from "@apollo/client/react";
|
||||||
import * as Sentry from "@sentry/react";
|
import * as Sentry from "@sentry/react";
|
||||||
import { SplitFactoryProvider, useSplitClient } from "@splitsoftware/splitio-react";
|
import { SplitFactoryProvider, useSplitClient } from "@splitsoftware/splitio-react";
|
||||||
import { ConfigProvider, Grid } 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, useMemo } from "react";
|
||||||
import { CookiesProvider } from "react-cookie";
|
import { CookiesProvider } from "react-cookie";
|
||||||
@@ -43,47 +43,10 @@ function AppContainer() {
|
|||||||
|
|
||||||
const currentUser = useSelector(selectCurrentUser);
|
const currentUser = useSelector(selectCurrentUser);
|
||||||
const isDarkMode = useSelector(selectDarkMode);
|
const isDarkMode = useSelector(selectDarkMode);
|
||||||
const screens = Grid.useBreakpoint();
|
|
||||||
const isPhone = !screens.md;
|
|
||||||
const isUltraWide = Boolean(screens.xxxl);
|
|
||||||
|
|
||||||
const theme = useMemo(() => {
|
const theme = useMemo(() => getTheme(isDarkMode), [isDarkMode]);
|
||||||
const baseTheme = getTheme(isDarkMode);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...baseTheme,
|
|
||||||
token: {
|
|
||||||
...(baseTheme.token || {}),
|
|
||||||
screenXXXL: 2160
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
...(baseTheme.components || {}),
|
|
||||||
Table: {
|
|
||||||
...(baseTheme.components?.Table || {}),
|
|
||||||
cellFontSizeSM: isPhone ? 12 : 13,
|
|
||||||
cellFontSizeMD: isPhone ? 13 : isUltraWide ? 15 : 14,
|
|
||||||
cellFontSize: isUltraWide ? 15 : 14,
|
|
||||||
cellPaddingInlineSM: isPhone ? 8 : 10,
|
|
||||||
cellPaddingInlineMD: isPhone ? 10 : 14,
|
|
||||||
cellPaddingInline: isUltraWide ? 20 : 16,
|
|
||||||
cellPaddingBlockSM: isPhone ? 8 : 10,
|
|
||||||
cellPaddingBlockMD: isPhone ? 10 : 12,
|
|
||||||
cellPaddingBlock: isUltraWide ? 14 : 12,
|
|
||||||
selectionColumnWidth: isPhone ? 44 : 52
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, [isDarkMode, isPhone, isUltraWide]);
|
|
||||||
|
|
||||||
const antdInput = useMemo(() => ({ autoComplete: "new-password" }), []);
|
const antdInput = useMemo(() => ({ autoComplete: "new-password" }), []);
|
||||||
const antdTable = useMemo(() => ({ scroll: { x: "max-content" } }), []);
|
|
||||||
const antdPagination = useMemo(
|
|
||||||
() => ({
|
|
||||||
showSizeChanger: !isPhone,
|
|
||||||
totalBoundaryShowSizeChanger: 100
|
|
||||||
}),
|
|
||||||
[isPhone]
|
|
||||||
);
|
|
||||||
|
|
||||||
const antdForm = useMemo(
|
const antdForm = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
@@ -159,16 +122,7 @@ function AppContainer() {
|
|||||||
return (
|
return (
|
||||||
<CookiesProvider>
|
<CookiesProvider>
|
||||||
<ApolloProvider client={client}>
|
<ApolloProvider client={client}>
|
||||||
<ConfigProvider
|
<ConfigProvider input={antdInput} locale={enLocale} theme={theme} form={antdForm}>
|
||||||
input={antdInput}
|
|
||||||
locale={enLocale}
|
|
||||||
theme={theme}
|
|
||||||
form={antdForm}
|
|
||||||
table={antdTable}
|
|
||||||
pagination={antdPagination}
|
|
||||||
componentSize={isPhone ? "small" : isUltraWide ? "large" : "middle"}
|
|
||||||
popupOverflow="viewport"
|
|
||||||
>
|
|
||||||
<GlobalLoadingBar />
|
<GlobalLoadingBar />
|
||||||
<SplitFactoryProvider config={config}>
|
<SplitFactoryProvider config={config}>
|
||||||
<SplitClientProvider>
|
<SplitClientProvider>
|
||||||
|
|||||||
184
client/src/App/App.container.pre-rollback-2026-03-04.jsx
Normal file
184
client/src/App/App.container.pre-rollback-2026-03-04.jsx
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
import { ApolloProvider } from "@apollo/client/react";
|
||||||
|
import * as Sentry from "@sentry/react";
|
||||||
|
import { SplitFactoryProvider, useSplitClient } from "@splitsoftware/splitio-react";
|
||||||
|
import { ConfigProvider, Grid } from "antd";
|
||||||
|
import enLocale from "antd/es/locale/en_US";
|
||||||
|
import { useEffect, useMemo } from "react";
|
||||||
|
import { CookiesProvider } from "react-cookie";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component";
|
||||||
|
import { setDarkMode } from "../redux/application/application.actions";
|
||||||
|
import { selectDarkMode } from "../redux/application/application.selectors";
|
||||||
|
import { selectCurrentUser } from "../redux/user/user.selectors.js";
|
||||||
|
import { signOutStart } from "../redux/user/user.actions";
|
||||||
|
import client from "../utils/GraphQLClient";
|
||||||
|
import App from "./App";
|
||||||
|
import getTheme from "./themeProvider";
|
||||||
|
|
||||||
|
// Base Split configuration
|
||||||
|
const config = {
|
||||||
|
core: {
|
||||||
|
authorizationKey: import.meta.env.VITE_APP_SPLIT_API,
|
||||||
|
key: "anon"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function SplitClientProvider({ children }) {
|
||||||
|
const imexshopid = useSelector((state) => state.user.imexshopid);
|
||||||
|
const splitClient = useSplitClient({ key: imexshopid || "anon" });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (import.meta.env.DEV && splitClient && imexshopid) {
|
||||||
|
console.log(`Split client initialized with key: ${imexshopid}, isReady: ${splitClient.isReady}`);
|
||||||
|
}
|
||||||
|
}, [splitClient, imexshopid]);
|
||||||
|
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
function AppContainer() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const currentUser = useSelector(selectCurrentUser);
|
||||||
|
const isDarkMode = useSelector(selectDarkMode);
|
||||||
|
const screens = Grid.useBreakpoint();
|
||||||
|
const isPhone = !screens.md;
|
||||||
|
const isUltraWide = Boolean(screens.xxxl);
|
||||||
|
|
||||||
|
const theme = useMemo(() => {
|
||||||
|
const baseTheme = getTheme(isDarkMode);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...baseTheme,
|
||||||
|
token: {
|
||||||
|
...(baseTheme.token || {}),
|
||||||
|
screenXXXL: 2160
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
...(baseTheme.components || {}),
|
||||||
|
Table: {
|
||||||
|
...(baseTheme.components?.Table || {}),
|
||||||
|
cellFontSizeSM: isPhone ? 12 : 13,
|
||||||
|
cellFontSizeMD: isPhone ? 13 : isUltraWide ? 15 : 14,
|
||||||
|
cellFontSize: isUltraWide ? 15 : 14,
|
||||||
|
cellPaddingInlineSM: isPhone ? 8 : 10,
|
||||||
|
cellPaddingInlineMD: isPhone ? 10 : 14,
|
||||||
|
cellPaddingInline: isUltraWide ? 20 : 16,
|
||||||
|
cellPaddingBlockSM: isPhone ? 8 : 10,
|
||||||
|
cellPaddingBlockMD: isPhone ? 10 : 12,
|
||||||
|
cellPaddingBlock: isUltraWide ? 14 : 12,
|
||||||
|
selectionColumnWidth: isPhone ? 44 : 52
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [isDarkMode, isPhone, isUltraWide]);
|
||||||
|
|
||||||
|
const antdInput = useMemo(() => ({ autoComplete: "new-password" }), []);
|
||||||
|
const antdTable = useMemo(() => ({ scroll: { x: "max-content" } }), []);
|
||||||
|
const antdPagination = useMemo(
|
||||||
|
() => ({
|
||||||
|
showSizeChanger: !isPhone,
|
||||||
|
totalBoundaryShowSizeChanger: 100
|
||||||
|
}),
|
||||||
|
[isPhone]
|
||||||
|
);
|
||||||
|
|
||||||
|
const antdForm = useMemo(
|
||||||
|
() => ({
|
||||||
|
validateMessages: {
|
||||||
|
required: t("general.validation.required", { label: "${label}" })
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
[t]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Global seamless logout listener with redirect to /signin
|
||||||
|
useEffect(() => {
|
||||||
|
const handleSeamlessLogout = (event) => {
|
||||||
|
if (event.data?.type !== "seamlessLogoutRequest") return;
|
||||||
|
|
||||||
|
// Only accept messages from the parent window
|
||||||
|
if (event.source !== window.parent) return;
|
||||||
|
|
||||||
|
const targetOrigin = event.origin || "*";
|
||||||
|
|
||||||
|
if (currentUser?.authorized !== true) {
|
||||||
|
window.parent?.postMessage({ type: "seamlessLogoutResponse", status: "already_logged_out" }, targetOrigin);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(signOutStart());
|
||||||
|
window.parent?.postMessage({ type: "seamlessLogoutResponse", status: "logged_out" }, targetOrigin);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("message", handleSeamlessLogout);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("message", handleSeamlessLogout);
|
||||||
|
};
|
||||||
|
}, [dispatch, currentUser?.authorized]);
|
||||||
|
|
||||||
|
// Update data-theme attribute (no cleanup to avoid transient style churn)
|
||||||
|
useEffect(() => {
|
||||||
|
document.documentElement.dataset.theme = isDarkMode ? "dark" : "light";
|
||||||
|
}, [isDarkMode]);
|
||||||
|
|
||||||
|
// Sync darkMode with localStorage
|
||||||
|
useEffect(() => {
|
||||||
|
const uid = currentUser?.uid;
|
||||||
|
|
||||||
|
if (!uid) {
|
||||||
|
dispatch(setDarkMode(false));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
useEffect(() => {
|
||||||
|
const uid = currentUser?.uid;
|
||||||
|
if (!uid) return;
|
||||||
|
|
||||||
|
localStorage.setItem(`dark-mode-${uid}`, JSON.stringify(isDarkMode));
|
||||||
|
}, [isDarkMode, currentUser?.uid]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CookiesProvider>
|
||||||
|
<ApolloProvider client={client}>
|
||||||
|
<ConfigProvider
|
||||||
|
input={antdInput}
|
||||||
|
locale={enLocale}
|
||||||
|
theme={theme}
|
||||||
|
form={antdForm}
|
||||||
|
table={antdTable}
|
||||||
|
pagination={antdPagination}
|
||||||
|
componentSize={isPhone ? "small" : isUltraWide ? "large" : "middle"}
|
||||||
|
popupOverflow="viewport"
|
||||||
|
>
|
||||||
|
<GlobalLoadingBar />
|
||||||
|
<SplitFactoryProvider config={config}>
|
||||||
|
<SplitClientProvider>
|
||||||
|
<App />
|
||||||
|
</SplitClientProvider>
|
||||||
|
</SplitFactoryProvider>
|
||||||
|
</ConfigProvider>
|
||||||
|
</ApolloProvider>
|
||||||
|
</CookiesProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Sentry.withProfiler(AppContainer);
|
||||||
@@ -471,34 +471,34 @@
|
|||||||
// padding: 0;
|
// padding: 0;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
/* globally allow shrink inside table cells */
|
///* globally allow shrink inside table cells */
|
||||||
.prod-list-table .ant-table-cell,
|
//.prod-list-table .ant-table-cell,
|
||||||
.prod-list-table .ant-table-cell > * {
|
//.prod-list-table .ant-table-cell > * {
|
||||||
min-width: 0;
|
// min-width: 0;
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
/* common AntD offenders */
|
///* common AntD offenders */
|
||||||
.prod-list-table > .ant-table-cell .ant-space,
|
//.prod-list-table > .ant-table-cell .ant-space,
|
||||||
.ant-table-cell .ant-space-item {
|
//.ant-table-cell .ant-space-item {
|
||||||
min-width: 0;
|
// min-width: 0;
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
/* Keep your custom header content on the left, push AntD sorter to the far right */
|
///* Keep your custom header content on the left, push AntD sorter to the far right */
|
||||||
.prod-list-table .ant-table-column-sorters {
|
//.prod-list-table .ant-table-column-sorters {
|
||||||
display: flex !important;
|
// display: flex !important;
|
||||||
align-items: center;
|
// align-items: center;
|
||||||
width: 100%;
|
// width: 100%;
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
.prod-list-table .ant-table-column-title {
|
//.prod-list-table .ant-table-column-title {
|
||||||
flex: 1 1 auto;
|
// flex: 1 1 auto;
|
||||||
min-width: 0; /* allows ellipsis to work */
|
// min-width: 0; /* allows ellipsis to work */
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
.prod-list-table .ant-table-column-sorter {
|
//.prod-list-table .ant-table-column-sorter {
|
||||||
margin-left: auto;
|
// margin-left: auto;
|
||||||
flex: 0 0 auto;
|
// flex: 0 0 auto;
|
||||||
}
|
//}
|
||||||
|
|
||||||
|
|
||||||
.global-search-autocomplete-fix {
|
.global-search-autocomplete-fix {
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import { Grid, Table } from "antd";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import "./responsive-table.styles.scss";
|
||||||
|
|
||||||
|
function ResponsiveTable({ className, columns, mobileColumnKeys, scroll, tableLayout, ...rest }) {
|
||||||
|
const screens = Grid.useBreakpoint();
|
||||||
|
const isPhone = !screens.md;
|
||||||
|
const isCompactViewport = !screens.lg;
|
||||||
|
const prefersHorizontalScroll = isPhone || isCompactViewport;
|
||||||
|
const isResponsiveFilteringEnabled = ["1", "true", "yes", "on"].includes(
|
||||||
|
String(import.meta.env.VITE_APP_ENABLE_RESPONSIVE_TABLE_FILTERING || "")
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
);
|
||||||
|
|
||||||
|
const resolvedColumns = useMemo(() => {
|
||||||
|
if (
|
||||||
|
!isResponsiveFilteringEnabled ||
|
||||||
|
!Array.isArray(columns) ||
|
||||||
|
!isPhone ||
|
||||||
|
!Array.isArray(mobileColumnKeys) ||
|
||||||
|
mobileColumnKeys.length === 0
|
||||||
|
) {
|
||||||
|
return columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
const visibleColumnKeys = new Set(mobileColumnKeys);
|
||||||
|
const filteredColumns = columns.filter((column) => {
|
||||||
|
const key = column?.key ?? column?.dataIndex;
|
||||||
|
|
||||||
|
// Keep columns with no stable key to avoid accidental loss.
|
||||||
|
if (key == null) return true;
|
||||||
|
|
||||||
|
if (Array.isArray(key)) {
|
||||||
|
return key.some((part) => visibleColumnKeys.has(part));
|
||||||
|
}
|
||||||
|
|
||||||
|
return visibleColumnKeys.has(key);
|
||||||
|
});
|
||||||
|
|
||||||
|
return filteredColumns.length > 0 ? filteredColumns : columns;
|
||||||
|
}, [columns, isPhone, isResponsiveFilteringEnabled, mobileColumnKeys]);
|
||||||
|
|
||||||
|
const resolvedScroll = useMemo(() => {
|
||||||
|
if (prefersHorizontalScroll) {
|
||||||
|
if (scroll == null) {
|
||||||
|
return { x: "max-content" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof scroll !== "object" || Array.isArray(scroll)) {
|
||||||
|
return scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { x, ...baseScroll } = scroll;
|
||||||
|
|
||||||
|
return { ...baseScroll, x: x ?? "max-content" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scroll == null) {
|
||||||
|
// Explicitly override ConfigProvider table.scroll desktop defaults.
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof scroll !== "object" || Array.isArray(scroll)) {
|
||||||
|
return scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { x, ...desktopScroll } = scroll;
|
||||||
|
|
||||||
|
// On desktop we prefer fitting columns with ellipsis over forced horizontal scroll.
|
||||||
|
if (x == null) {
|
||||||
|
return desktopScroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
return desktopScroll;
|
||||||
|
}, [prefersHorizontalScroll, scroll]);
|
||||||
|
|
||||||
|
const resolvedTableLayout = tableLayout ?? (prefersHorizontalScroll ? "auto" : "fixed");
|
||||||
|
const responsiveClassName = prefersHorizontalScroll ? undefined : "responsive-table-fit";
|
||||||
|
const resolvedClassName = [responsiveClassName, className].filter(Boolean).join(" ");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table
|
||||||
|
className={resolvedClassName}
|
||||||
|
columns={resolvedColumns}
|
||||||
|
scroll={resolvedScroll}
|
||||||
|
tableLayout={resolvedTableLayout}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResponsiveTable.Summary = Table.Summary;
|
||||||
|
ResponsiveTable.Column = Table.Column;
|
||||||
|
ResponsiveTable.ColumnGroup = Table.ColumnGroup;
|
||||||
|
ResponsiveTable.SELECTION_COLUMN = Table.SELECTION_COLUMN;
|
||||||
|
ResponsiveTable.EXPAND_COLUMN = Table.EXPAND_COLUMN;
|
||||||
|
|
||||||
|
export default ResponsiveTable;
|
||||||
@@ -1,93 +1,7 @@
|
|||||||
import { Grid, Table } from "antd";
|
import { Table } from "antd";
|
||||||
import { useMemo } from "react";
|
|
||||||
import "./responsive-table.styles.scss";
|
|
||||||
|
|
||||||
function ResponsiveTable({ className, columns, mobileColumnKeys, scroll, tableLayout, ...rest }) {
|
function ResponsiveTable(props) {
|
||||||
const screens = Grid.useBreakpoint();
|
return <Table {...props} />;
|
||||||
const isPhone = !screens.md;
|
|
||||||
const isCompactViewport = !screens.lg;
|
|
||||||
const prefersHorizontalScroll = isPhone || isCompactViewport;
|
|
||||||
const isResponsiveFilteringEnabled = ["1", "true", "yes", "on"].includes(
|
|
||||||
String(import.meta.env.VITE_APP_ENABLE_RESPONSIVE_TABLE_FILTERING || "")
|
|
||||||
.trim()
|
|
||||||
.toLowerCase()
|
|
||||||
);
|
|
||||||
|
|
||||||
const resolvedColumns = useMemo(() => {
|
|
||||||
if (
|
|
||||||
!isResponsiveFilteringEnabled ||
|
|
||||||
!Array.isArray(columns) ||
|
|
||||||
!isPhone ||
|
|
||||||
!Array.isArray(mobileColumnKeys) ||
|
|
||||||
mobileColumnKeys.length === 0
|
|
||||||
) {
|
|
||||||
return columns;
|
|
||||||
}
|
|
||||||
|
|
||||||
const visibleColumnKeys = new Set(mobileColumnKeys);
|
|
||||||
const filteredColumns = columns.filter((column) => {
|
|
||||||
const key = column?.key ?? column?.dataIndex;
|
|
||||||
|
|
||||||
// Keep columns with no stable key to avoid accidental loss.
|
|
||||||
if (key == null) return true;
|
|
||||||
|
|
||||||
if (Array.isArray(key)) {
|
|
||||||
return key.some((part) => visibleColumnKeys.has(part));
|
|
||||||
}
|
|
||||||
|
|
||||||
return visibleColumnKeys.has(key);
|
|
||||||
});
|
|
||||||
|
|
||||||
return filteredColumns.length > 0 ? filteredColumns : columns;
|
|
||||||
}, [columns, isPhone, isResponsiveFilteringEnabled, mobileColumnKeys]);
|
|
||||||
|
|
||||||
const resolvedScroll = useMemo(() => {
|
|
||||||
if (prefersHorizontalScroll) {
|
|
||||||
if (scroll == null) {
|
|
||||||
return { x: "max-content" };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof scroll !== "object" || Array.isArray(scroll)) {
|
|
||||||
return scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { x, ...baseScroll } = scroll;
|
|
||||||
|
|
||||||
return { ...baseScroll, x: x ?? "max-content" };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scroll == null) {
|
|
||||||
// Explicitly override ConfigProvider table.scroll desktop defaults.
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof scroll !== "object" || Array.isArray(scroll)) {
|
|
||||||
return scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { x, ...desktopScroll } = scroll;
|
|
||||||
|
|
||||||
// On desktop we prefer fitting columns with ellipsis over forced horizontal scroll.
|
|
||||||
if (x == null) {
|
|
||||||
return desktopScroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
return desktopScroll;
|
|
||||||
}, [prefersHorizontalScroll, scroll]);
|
|
||||||
|
|
||||||
const resolvedTableLayout = tableLayout ?? (prefersHorizontalScroll ? "auto" : "fixed");
|
|
||||||
const responsiveClassName = prefersHorizontalScroll ? undefined : "responsive-table-fit";
|
|
||||||
const resolvedClassName = [responsiveClassName, className].filter(Boolean).join(" ");
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Table
|
|
||||||
className={resolvedClassName}
|
|
||||||
columns={resolvedColumns}
|
|
||||||
scroll={resolvedScroll}
|
|
||||||
tableLayout={resolvedTableLayout}
|
|
||||||
{...rest}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ResponsiveTable.Summary = Table.Summary;
|
ResponsiveTable.Summary = Table.Summary;
|
||||||
|
|||||||
Reference in New Issue
Block a user