@ucheeddev/refine-antd
Version:
Custom Ant Design components for Refine framework
1,797 lines (1,745 loc) • 135 kB
JavaScript
// src/components/account-dropdown/account-dropdown.tsx
import { useCan as useCan2 } from "@refinedev/core";
import { Select } from "antd";
// src/hooks/import-modal/import-modal.ts
import { useCallback, useState } from "react";
import { useModalForm } from "@refinedev/antd";
import { useAsyncImport } from "@ucheeddev/refine-core";
var useImportModal = ({ resource, args }) => {
const parseArgs = args?.parseArgs;
const [isOpen, setIsOpen] = useState(false);
const { isLoading, triggerImport, canAccess } = useAsyncImport({ resource });
const { modalProps, formProps } = useModalForm({
action: "create"
});
const openModal = useCallback(() => setIsOpen(true), []);
const onCancel = useCallback(() => setIsOpen(false), []);
const onFinish = useCallback(
(values) => {
const parsedArgs = parseArgs ? parseArgs(values) : values;
setIsOpen(false);
return triggerImport(parsedArgs);
},
[parseArgs, triggerImport]
);
return {
modalProps,
isLoading,
formProps,
onCancel,
isOpen,
onFinish,
openModal,
canAccess
};
};
// src/hooks/is-url-button/is-url-button.ts
import { useCallback as useCallback2, useMemo } from "react";
// src/utils/is-url/is-url.ts
var isUrl = (url) => {
try {
new URL(url || "");
return true;
} catch (e) {
return false;
}
};
// src/utils/lighten-hex-color/lighten-hex-color.ts
var lightenHexColor = (color, amount) => {
amount = amount || 20;
if (!color) {
return "";
}
if (color[0] === "#") {
color = color.slice(1);
}
let r = parseInt(color.substring(0, 2), 16);
let g = parseInt(color.substring(2, 4), 16);
let b = parseInt(color.substring(4, 6), 16);
r = Math.min(255, r + amount);
g = Math.min(255, g + amount);
b = Math.min(255, b + amount);
r = r < 0 ? 0 : r;
g = g < 0 ? 0 : g;
b = b < 0 ? 0 : b;
const newColor = "#" + ("0" + r.toString(16)).slice(-2) + ("0" + g.toString(16)).slice(-2) + ("0" + b.toString(16)).slice(-2);
return newColor;
};
// src/hooks/is-url-button/is-url-button.ts
var useIsUrlButton = ({ url }) => {
const __isUrl = useMemo(() => isUrl(url), [url]);
const onClick = useCallback2(() => {
if (!__isUrl) {
return;
}
window.open(url, "_blank");
}, [url, __isUrl]);
return {
isUrl: __isUrl,
onClick
};
};
// src/hooks/ownership/ownership.tsx
import { useCallback as useCallback3 } from "react";
import { useCan } from "@refinedev/core";
import { FilterOutlined, UserOutlined } from "@ant-design/icons";
import { Fragment, jsx } from "react/jsx-runtime";
var useOwnership = (resource) => {
const { data: canViewOthers } = useCan({
resource,
action: "view_others"
});
const { data: canEditOthers } = useCan({
resource,
action: "edit_others"
});
const { data: canEditOwnership } = useCan({
resource,
action: "edit_ownership"
});
const { data: canDeleteOthers } = useCan({
resource,
action: "delete_others"
});
const { data: canListUsers } = useCan({
resource: "users",
action: "list"
});
const appendOwnerTableColumns = useCallback3(
(columns, args) => {
const title = args?.title || "Owner";
const viewType = args?.viewType || "selectOptionWithAvatar";
const roleFilters = args?.role ? [
{
field: "role",
operator: "eq",
value: args.role
}
] : void 0;
if (!canViewOthers?.can) {
return;
}
columns.push({
title,
dataIndex: "owner",
filterIcon: args?.hideFilter ? false : /* @__PURE__ */ jsx(FilterOutlined, {}),
filterDropdown: args?.hideFilter ? void 0 : !!canListUsers?.can ? (props) => /* @__PURE__ */ jsx(FilterDropdown, { ...props, children: /* @__PURE__ */ jsx(OwnerDropdown, { width: 200, filters: roleFilters }) }) : void 0,
render: (owner) => {
return owner ? viewType === "selectOptionWithAvatar" ? /* @__PURE__ */ jsx(
SelectOptionWithAvatar,
{
name: owner.full_name,
avatarUrl: owner.photo,
shape: "square"
}
) : owner.full_name : "--";
}
});
},
[canListUsers?.can, canViewOthers?.can]
);
const renderOwnerEditField = useCallback3(
({
getFormProps,
owner,
hideIcon,
selectProps,
label,
viewType = "selectOptionWithAvatar",
role
}) => {
if (!canViewOthers?.can) {
return /* @__PURE__ */ jsx(Fragment, {});
}
const roleFilters = role ? [
{
field: "role",
operator: "eq",
value: role
}
] : void 0;
return /* @__PURE__ */ jsx(
SingleElementForm,
{
icon: hideIcon ? void 0 : /* @__PURE__ */ jsx(UserOutlined, { className: "colorPrimary" }),
...getFormProps({
name: "owner",
label: label || "Owner",
initialValue: owner?.id
}),
view: owner ? viewType === "selectOptionWithAvatar" ? /* @__PURE__ */ jsx(
SelectOptionWithAvatar,
{
name: owner.full_name,
avatarUrl: owner.photo,
shape: "square"
}
) : owner.full_name : "--",
isDisabled: !canEditOwnership?.can,
children: /* @__PURE__ */ jsx(
OwnerDropdown,
{
...selectProps || {},
filters: selectProps?.filters ?? roleFilters
}
)
}
);
},
[canEditOwnership?.can, canViewOthers?.can]
);
return {
canViewOthers: !!canViewOthers?.can,
canEditOthers: !!canEditOthers?.can,
canEditOwnership: !!canEditOwnership?.can,
canDeleteOthers: !!canDeleteOthers?.can,
canListUsers: !!canListUsers?.can,
appendOwnerTableColumns,
renderOwnerEditField
};
};
// src/hooks/theme-config/use-theme-config.ts
import { useMemo as useMemo2 } from "react";
import { useServerSettingsStore } from "@ucheeddev/refine-core";
import { theme } from "antd";
// src/constants/theme-config.ts
var DEFAULT_THEME_CONFIG = {
// token: {
// colorPrimaryText: 'rgba(0, 0, 0, 0.85)',
// colorTextSecondary: 'rgba(0, 0, 0, 0.65)',
// colorTextTertiary: 'rgba(0, 0, 0, 0.45)',
// colorPrimary: '#6eb2cc',
// colorBgContainer: '#FFFFFF',
// colorBgLayout: '#F0F2F5',
// colorBorderBg: '#E8E9EA',
// fontFamily:
// "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', 'Arial', 'Noto Sans', 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'",
// },
// components: {
// Typography: {
// colorText: 'rgba(0, 0, 0, 0.85)',
// colorTextDescription: 'rgba(0, 0, 0, 0.65)',
// colorTextDisabled: 'rgba(0, 0, 0, 0.45)',
// },
// Card: {
// colorBgContainer: '#FFFFFF',
// headerBg: '#FFFFFF',
// boxShadowTertiary:
// '0px 1px 2px 0px #00000008,0px 1px 6px -1px #000000050px,2px 4px 0px #00000005',
// },
// Table: {
// colorBgContainer: '#fff',
// },
// Input: {
// colorBgContainer: '#fff',
// },
// InputNumber: {
// colorBgContainer: '#fff',
// },
// Calendar: {
// colorBgContainer: '#FFFFFF',
// },
// Radio: {
// colorBgContainer: '#fff',
// },
// Select: {
// colorBgContainer: '#fff',
// },
// },
};
// src/hooks/theme-config/use-theme-config.ts
var useThemeConfig = (props) => {
const { overrides, mode } = props;
const { token: tokenOverrides, ...overridesRest } = overrides || {};
const settings = useServerSettingsStore((state) => state.settings);
const colorPrimary = settings?.primary_color || "";
const colorPrimaryDark = settings?.primary_color_dark || lightenHexColor(colorPrimary);
const token = useMemo2(() => {
const _token = {
...DEFAULT_THEME_CONFIG.token || {},
...tokenOverrides || {}
};
if (colorPrimary) {
_token.colorPrimary = mode === "light" ? colorPrimary : colorPrimaryDark;
}
return _token;
}, [tokenOverrides, colorPrimary, colorPrimaryDark, mode]);
const themeConfig = useMemo2(
() => ({
...DEFAULT_THEME_CONFIG,
token,
...overridesRest || {}
}),
[token, overridesRest]
);
const customTheme = useMemo2(() => {
const { darkAlgorithm, defaultAlgorithm } = theme;
const algorithm = mode === "light" ? defaultAlgorithm : darkAlgorithm;
const customTheme2 = {
...themeConfig,
cssVar: true,
algorithm,
components: {
Card: {
headerBg: mode === "dark" ? "#1F1F1F" : "#FAFAFA"
}
}
};
return customTheme2;
}, [mode, themeConfig]);
return customTheme;
};
// src/hooks/use-account-select/use-account-select.ts
import { useSelect } from "@refinedev/antd";
var useAccountSelect = (params) => {
const { filters } = params || {};
return useSelect({
resource: "accounts",
optionLabel: "name",
optionValue: "id",
filters,
onSearch: (value) => [
{
field: "search",
operator: "eq",
value
}
]
});
};
// src/hooks/use-phone-input/use-phone-input.ts
import { useCallback as useCallback4 } from "react";
var usePhoneinput = () => {
const validator = useCallback4(
(_, value) => {
if (value?.valid?.(true)) {
return Promise.resolve();
}
return Promise.reject("Invalid phone number");
},
[]
);
const fieldProps = {
rules: [
{
validator
}
],
enableSearch: true,
enableArrow: true
};
const parsePhoneNumber = useCallback4((value) => {
if (!value?.valid?.(true)) return "";
const fullPhoneNumber = `+${value?.countryCode || ""}${value?.areaCode || ""}${value?.phoneNumber || ""}`;
return fullPhoneNumber;
}, []);
return {
fieldProps,
parsePhoneNumber
};
};
// src/hooks/use-user-select/use-user-select.ts
import { useSelect as useSelect2 } from "@refinedev/antd";
var useUserSelect = (params) => {
const { filters } = params || {};
return useSelect2({
resource: "users",
optionLabel: "full_name",
optionValue: "id",
filters,
onSearch: (value) => [
{
field: "search",
operator: "eq",
value
}
]
});
};
// src/components/select-option-with-avatar/select-option-with-avatar.tsx
import { Space } from "antd";
// src/components/custom-avatar/custom-avatar.tsx
import { memo } from "react";
import { Avatar as AntdAvatar } from "antd";
import { jsx as jsx2 } from "react/jsx-runtime";
var getRandomColorFromString = (text) => {
const colors = [
"#ff9c6e",
"#ff7875",
"#ffc069",
"#ffd666",
"#fadb14",
"#95de64",
"#5cdbd3",
"#69c0ff",
"#85a5ff",
"#b37feb",
"#ff85c0"
];
let hash = 0;
for (let i = 0; i < text.length; i++) {
hash = text.charCodeAt(i) + ((hash << 5) - hash);
hash = hash & hash;
}
hash = (hash % colors.length + colors.length) % colors.length;
return colors[hash];
};
var getNameInitials = (name, count = 2) => {
const initials = name.split(" ").map((n) => n[0]).join("");
const filtered = initials.replace(/[^a-zA-Z]/g, "");
return filtered.slice(0, count).toUpperCase();
};
var CustomAvatarComponent = ({
name,
style,
...rest
}) => {
return /* @__PURE__ */ jsx2(
AntdAvatar,
{
alt: name,
size: "small",
style: {
backgroundColor: rest?.src ? "transparent" : getRandomColorFromString(name || ""),
display: "flex",
alignItems: "center",
border: "none",
...style
},
...rest,
children: getNameInitials(name || "")
}
);
};
var CustomAvatar = memo(
CustomAvatarComponent,
(prevProps, nextProps) => {
return prevProps.name === nextProps.name && prevProps.src === nextProps.src;
}
);
// src/components/text/text.tsx
import { ConfigProvider, Typography } from "antd";
// src/components/text/sizes.ts
var sizes = {
xs: {
fontSize: 12,
lineHeight: 20 / 12
},
sm: {
fontSize: 14,
lineHeight: 22 / 14
},
md: {
fontSize: 16,
lineHeight: 24 / 16
},
lg: {
fontSize: 20,
lineHeight: 28 / 20
},
xl: {
fontSize: 24,
lineHeight: 32 / 24
},
xxl: {
fontSize: 30,
lineHeight: 38 / 30
},
xxxl: {
fontSize: 38,
lineHeight: 46 / 38
},
huge: {
fontSize: 46,
lineHeight: 54 / 46
},
xhuge: {
fontSize: 56,
lineHeight: 64 / 56
},
xxhuge: {
fontSize: 68,
lineHeight: 76 / 68
}
};
// src/components/text/text.tsx
import { jsx as jsx3 } from "react/jsx-runtime";
var Text = ({ size = "sm", children, ...rest }) => {
return /* @__PURE__ */ jsx3(
ConfigProvider,
{
theme: {
token: {
...sizes[size]
}
},
children: /* @__PURE__ */ jsx3(Typography.Text, { ...rest, children })
}
);
};
// src/components/select-option-with-avatar/select-option-with-avatar.tsx
import { jsx as jsx4, jsxs } from "react/jsx-runtime";
var SelectOptionWithAvatar = ({
avatarUrl,
name,
shape
}) => {
return /* @__PURE__ */ jsxs(Space, { children: [
/* @__PURE__ */ jsx4(CustomAvatar, { shape, name, src: avatarUrl }),
/* @__PURE__ */ jsx4(Text, { children: name })
] });
};
// src/components/account-dropdown/account-dropdown.tsx
import { Fragment as Fragment2, jsx as jsx5 } from "react/jsx-runtime";
var __AccountSelect = ({
width,
...props
}) => {
const { selectProps: accountSelectProps, query: accountQuery } = useAccountSelect();
return /* @__PURE__ */ jsx5(
Select,
{
...props,
...accountSelectProps,
filterSort: void 0,
allowClear: true,
options: accountQuery?.data?.data?.map(({ id, name, logo }) => ({
value: id,
label: /* @__PURE__ */ jsx5(SelectOptionWithAvatar, { name, avatarUrl: logo })
})),
placeholder: "Please select account",
style: { width: width || "100%" }
}
);
};
var AccountDropdown = ({
width,
...props
}) => {
const { data: canListAccount } = useCan2({
resource: "accounts",
action: "list"
});
if (!canListAccount?.can) {
return /* @__PURE__ */ jsx5(Fragment2, {});
}
return /* @__PURE__ */ jsx5(__AccountSelect, { ...props, width });
};
// src/components/avatar-group/avatar-group.tsx
import { Space as Space2, Tooltip } from "antd";
import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
var AvatarGroup = ({
avatars,
size,
overlap,
maxCount = 3,
gap = "8px",
containerStyle,
avatarStyle
}) => {
const visibleAvatars = avatars.slice(0, maxCount);
const remainingAvatars = avatars.slice(maxCount);
const hasRemainingAvatars = remainingAvatars.length > 0;
const shouldOverlap = overlap && avatars.length > 3;
const getImageSize = (size2) => {
if (typeof size2 === "number") {
return shouldOverlap ? `${size2 + 4}px` : `${size2}px`;
}
switch (size2) {
case "large":
return shouldOverlap ? "44px" : "40px";
case "small":
return shouldOverlap ? "28px" : "24px";
default:
return shouldOverlap ? "36px" : "32px";
}
};
return /* @__PURE__ */ jsxs2(
"div",
{
style: {
display: "flex",
alignItems: "center",
justifyContent: "flex-start",
gap: shouldOverlap ? "0" : gap,
...containerStyle
},
children: [
visibleAvatars.map((avatar, index) => {
const transform = shouldOverlap ? `translateX(-${index * 8}px)` : void 0;
return /* @__PURE__ */ jsx6(Tooltip, { title: avatar.name, children: /* @__PURE__ */ jsx6(
CustomAvatar,
{
style: {
cursor: "pointer",
transform,
zIndex: index,
border: shouldOverlap ? "2px solid #fff" : "none",
width: getImageSize(size),
height: getImageSize(size),
...avatarStyle
},
name: avatar?.name,
src: avatar?.src,
size
}
) }, index);
}),
hasRemainingAvatars && /* @__PURE__ */ jsx6(
Tooltip,
{
destroyTooltipOnHide: true,
title: /* @__PURE__ */ jsx6(Space2, { direction: "vertical", children: remainingAvatars.map((avatar, index) => {
return /* @__PURE__ */ jsxs2(Space2, { children: [
/* @__PURE__ */ jsx6(
CustomAvatar,
{
name: avatar.name,
src: avatar.src,
size: "small"
}
),
/* @__PURE__ */ jsx6(
Text,
{
style: {
color: "#fff"
},
children: avatar.name
},
avatar.name
)
] }, index);
}) }),
children: /* @__PURE__ */ jsxs2(
Text,
{
className: "tertiary",
style: {
userSelect: "none",
cursor: "pointer",
fontSize: "10px",
lineHeight: "22px",
letterSpacing: "0.5px",
fontWeight: 600,
display: "flex",
alignItems: "center",
justifyContent: "center",
borderRadius: "50%",
width: getImageSize(size),
height: getImageSize(size),
transform: shouldOverlap ? `translateX(-${visibleAvatars.length * 8}px)` : void 0,
zIndex: shouldOverlap ? visibleAvatars.length : 1,
backgroundColor: "#D9D9D9",
border: overlap ? "2px solid #fff" : "none"
},
children: [
"+",
remainingAvatars.length
]
}
)
}
)
]
}
);
};
// src/components/back-button/back-button.tsx
import { LeftOutlined } from "@ant-design/icons";
import { Button, Space as Space3 } from "antd";
import { jsx as jsx7 } from "react/jsx-runtime";
var BackButton = ({ onClick, label }) => {
return /* @__PURE__ */ jsx7(Space3, { children: /* @__PURE__ */ jsx7(
Button,
{
variant: "link",
onClick,
icon: /* @__PURE__ */ jsx7(LeftOutlined, {}),
color: "primary",
style: { fontSize: 17, fontWeight: "bold" },
children: label
}
) });
};
// src/components/bootstrap/bootstrap.tsx
import { useNotificationProvider } from "@refinedev/antd";
import { Bootstrap as CoreBootstrap } from "@ucheeddev/refine-core";
import { Alert, App as AntdApp } from "antd";
// src/providers/config/config-provider.tsx
import {
createContext,
useCallback as useCallback5,
useContext,
useEffect,
useState as useState2
} from "react";
import {
ConfigProvider as AntdConfigProvider
} from "antd";
import { ThemeProvider } from "antd-style";
import { jsx as jsx8 } from "react/jsx-runtime";
var ConfigProviderContext = createContext(
{}
);
var ConfigProvider2 = ({ children, theme: themeFromProps, forceLightTheme, ...rest }) => {
const colorModeFromLocalStorage = localStorage.getItem("colorMode");
const isSystemPreferenceDark = window.matchMedia(
"(prefers-color-scheme: dark)"
).matches;
const systemPreference = isSystemPreferenceDark ? "dark" : "light";
const [mode, setMode] = useState2(
forceLightTheme ? "light" : colorModeFromLocalStorage || systemPreference
);
const themeConfig = useThemeConfig({
overrides: themeFromProps,
mode
});
const toggleMode = useCallback5(() => {
if (forceLightTheme) {
setMode("light");
return;
}
setMode((prev) => prev === "light" ? "dark" : "light");
}, [forceLightTheme]);
useEffect(() => {
window.localStorage.setItem("colorMode", mode);
}, [mode]);
return /* @__PURE__ */ jsx8(
ConfigProviderContext.Provider,
{
value: {
toggleMode,
mode,
isDarkMode: mode === "dark"
},
children: /* @__PURE__ */ jsx8(AntdConfigProvider, { theme: themeConfig, children: /* @__PURE__ */ jsx8(ThemeProvider, { theme: themeConfig, appearance: mode, ...rest, children }) })
}
);
};
var useConfigProvider = () => {
const context = useContext(ConfigProviderContext);
if (!context) {
throw new Error("useConfigProvider must be used within a ConfigProvider");
}
return context;
};
// src/components/full-screen-loading/full-screen-loading.tsx
import { Spin } from "antd";
import { jsx as jsx9 } from "react/jsx-runtime";
var FullScreenLoading = () => {
return /* @__PURE__ */ jsx9(
Spin,
{
size: "large",
style: {
height: "100vh",
width: "100%",
display: "flex",
alignItems: "center",
justifyContent: "center"
}
}
);
};
// src/components/bootstrap/bootstrap.tsx
import { jsx as jsx10 } from "react/jsx-runtime";
var Bootstrap = ({
theme: theme6,
forceLightTheme,
configProviderProps,
children,
notificationProvider: notificationProviderFromProps,
...rest
}) => {
return /* @__PURE__ */ jsx10(
ConfigProvider2,
{
theme: theme6,
forceLightTheme,
...configProviderProps || {},
children: /* @__PURE__ */ jsx10(AntdApp, { children: /* @__PURE__ */ jsx10(
CoreBootstrap,
{
...rest,
notificationProvider: notificationProviderFromProps || useNotificationProvider,
Loading: FullScreenLoading,
Error: ({ error }) => /* @__PURE__ */ jsx10(Alert, { type: "error", message: error, showIcon: true }),
children
}
) })
}
);
};
// src/components/circle-icon-btn-action/circle-icon-btn-action.tsx
import { Button as Button2, Flex } from "antd";
import { jsx as jsx11, jsxs as jsxs3 } from "react/jsx-runtime";
var CircleIconButtonAction = ({
icon,
label,
onClick
}) => {
return /* @__PURE__ */ jsxs3(Flex, { vertical: true, align: "center", children: [
/* @__PURE__ */ jsx11(
Button2,
{
shape: "circle",
type: "default",
size: "large",
icon,
onClick
}
),
/* @__PURE__ */ jsx11(Text, { type: "secondary", style: { marginTop: 4, fontSize: 12 }, children: label })
] });
};
// src/components/current-user/current-user.tsx
import { useGetIdentity, useLogout } from "@refinedev/core";
import { LogoutOutlined, MailOutlined, UserOutlined as UserOutlined2 } from "@ant-design/icons";
import { Button as Button3, Popover, Space as Space4 } from "antd";
import { jsx as jsx12, jsxs as jsxs4 } from "react/jsx-runtime";
var CurrentUser = ({
beforeLogout
}) => {
const { data: user } = useGetIdentity();
const { mutate: logout } = useLogout();
const content = /* @__PURE__ */ jsxs4(
"div",
{
style: {
display: "flex",
flexDirection: "column"
},
children: [
/* @__PURE__ */ jsxs4(
"div",
{
style: {
padding: "12px 20px"
},
children: [
/* @__PURE__ */ jsx12("div", { children: /* @__PURE__ */ jsxs4(Space4, { size: 8, children: [
/* @__PURE__ */ jsx12(UserOutlined2, { className: "md colorPrimary" }),
/* @__PURE__ */ jsx12(Text, { strong: true, children: user?.display_name || "" })
] }) }),
/* @__PURE__ */ jsx12("div", { style: { paddingTop: 7 }, children: /* @__PURE__ */ jsxs4(Space4, { size: 12, children: [
/* @__PURE__ */ jsx12(MailOutlined, { className: "md colorPrimary" }),
/* @__PURE__ */ jsx12(Text, { style: { fontSize: 14, color: "#000" }, children: user?.email || "" })
] }) })
]
}
),
/* @__PURE__ */ jsx12(
"div",
{
style: {
borderTop: "1px solid #d9d9d9",
padding: "4px",
display: "flex",
flexDirection: "column",
gap: "4px"
},
children: /* @__PURE__ */ jsx12(
"div",
{
style: {
padding: "8px",
display: "flex",
flexDirection: "column",
gap: "4px"
},
children: /* @__PURE__ */ jsxs4(
"div",
{
style: {
display: "flex",
flexDirection: "column",
gap: "4px"
},
children: [
beforeLogout,
/* @__PURE__ */ jsx12(
Button3,
{
style: { textAlign: "left" },
icon: /* @__PURE__ */ jsx12(LogoutOutlined, {}),
type: "text",
danger: true,
block: true,
onClick: () => logout(),
children: "Logout"
}
)
]
}
)
}
)
}
)
]
}
);
return /* @__PURE__ */ jsx12(
Popover,
{
placement: "bottomRight",
content,
trigger: "click",
overlayInnerStyle: { padding: 0 },
overlayStyle: { zIndex: 999 },
children: /* @__PURE__ */ jsx12(
CustomAvatar,
{
name: user?.display_name || "",
src: user?.avatar_url || "",
size: "default",
style: { cursor: "pointer" }
}
)
}
);
};
// src/components/custom-date-picker/custom-date-picker.tsx
import { useEffect as useEffect2, useState as useState3 } from "react";
import { DatePicker } from "antd";
import dayjs from "dayjs";
import { jsx as jsx13 } from "react/jsx-runtime";
var CustomDatePicker = ({
value,
onChange,
onOk,
format,
needConfirm,
showTime,
showNow,
allowClear,
style
}) => {
showNow = showNow === void 0 ? false : true;
allowClear = allowClear === void 0 ? false : true;
needConfirm = needConfirm === void 0 ? true : false;
showTime = showTime === void 0 ? false : true;
format = format === void 0 ? showTime ? "DD/MM/YYYY hh:mm a" : "DD/MM/YYYY" : format;
const [_value, _setValue] = useState3(void 0);
useEffect2(() => {
if (!value) {
_setValue(void 0);
return;
}
_setValue(dayjs(value));
}, [value]);
return /* @__PURE__ */ jsx13(
DatePicker,
{
value: _value,
onChange,
onOk,
allowClear,
needConfirm,
showNow,
showTime,
format,
style
}
);
};
// src/components/custom-tabs/custom-tabs.tsx
import { Tabs } from "antd";
import { jsx as jsx14 } from "react/jsx-runtime";
var CustomTabs = ({
items,
defaultActiveKey = "1",
onChange,
style
}) => {
return /* @__PURE__ */ jsx14(
Tabs,
{
defaultActiveKey,
items,
onChange,
style
}
);
};
// src/components/custom-time-picker/custom-time-picker.tsx
import { useEffect as useEffect3, useState as useState4 } from "react";
import { DatePicker as DatePicker2 } from "antd";
import dayjs2 from "dayjs";
import { jsx as jsx15 } from "react/jsx-runtime";
var CustomTimePicker = ({
value,
onChange,
onOk,
format,
needConfirm,
allowClear,
style
}) => {
needConfirm = needConfirm === void 0 ? true : false;
allowClear = allowClear === void 0 ? false : true;
format = format === void 0 ? "hh:mm a" : format;
const [_value, _setValue] = useState4(void 0);
useEffect3(() => {
if (!value) {
_setValue(void 0);
return;
}
_setValue(dayjs2(value));
}, [value]);
return /* @__PURE__ */ jsx15(
DatePicker2.TimePicker,
{
value: _value,
onChange,
onOk,
allowClear,
needConfirm,
format,
style
}
);
};
// src/components/email-link/email-link.tsx
import { Typography as Typography2 } from "antd";
import { jsx as jsx16 } from "react/jsx-runtime";
var { Link } = Typography2;
var EmailLink = ({ email, style }) => {
return /* @__PURE__ */ jsx16(Link, { href: `mailto:${email}`, target: "_blank", style, children: email });
};
// src/components/field-form-title/field-form-title.tsx
import { Space as Space5, Typography as Typography3 } from "antd";
import { jsx as jsx17, jsxs as jsxs5 } from "react/jsx-runtime";
var FieldFormTitle = ({
title,
Icon: Icon2,
iconProps,
titleStyle,
...props
}) => {
return /* @__PURE__ */ jsxs5(Space5, { size: 11, ...props, children: [
Icon2 && /* @__PURE__ */ jsx17(Icon2, { className: "md colorPrimary", ...iconProps }),
/* @__PURE__ */ jsx17(
Typography3.Text,
{
style: { fontSize: "15px", color: "darkslategray", ...titleStyle },
children: title
}
)
] });
};
// src/components/filter-dropdown/filter-dropdown.tsx
import React2, { useCallback as useCallback6, useMemo as useMemo3 } from "react";
import { useTranslate } from "@refinedev/core";
import { ClearOutlined, FilterOutlined as FilterOutlined2 } from "@ant-design/icons";
import { Button as Button4, Space as Space6 } from "antd";
import dayjs3 from "dayjs";
import { jsx as jsx18, jsxs as jsxs6 } from "react/jsx-runtime";
var FilterDropdown = (props) => {
const {
setSelectedKeys,
confirm,
clearFilters,
mapValue = (value) => value,
selectedKeys,
children
} = props;
const translate = useTranslate();
const clearFilter = useCallback6(() => {
if (clearFilters) {
clearFilters();
confirm?.();
}
}, [clearFilters, confirm]);
const onFilter = useCallback6(() => {
let keys;
if (typeof selectedKeys === "number") {
keys = `${selectedKeys}`;
} else if (dayjs3.isDayjs(selectedKeys)) {
keys = [selectedKeys.toISOString()];
} else {
keys = selectedKeys;
}
setSelectedKeys(keys);
confirm?.();
}, [selectedKeys, setSelectedKeys, confirm]);
const onChange = useCallback6(
(e) => {
if (typeof e === "object") {
if (Array.isArray(e)) {
const mappedValue3 = mapValue(e, "onChange");
setSelectedKeys(mappedValue3);
confirm?.();
return;
}
const changeEvent = !e || !e.target || dayjs3.isDayjs(e) ? { target: { value: e } } : e;
const { target } = changeEvent;
const mappedValue2 = mapValue(target.value, "onChange");
setSelectedKeys(mappedValue2);
return;
}
const mappedValue = mapValue(e, "onChange");
if (typeof mappedValue === "number") {
setSelectedKeys(`${mappedValue}`);
} else {
setSelectedKeys(mappedValue);
}
confirm?.();
},
[confirm, mapValue, setSelectedKeys]
);
const childrenWithProps = useMemo3(() => {
return React2.Children.map(children, (child) => {
if (React2.isValidElement(child)) {
return React2.cloneElement(child, {
onChange,
value: mapValue(selectedKeys, "value")
});
}
return child;
});
}, [children, mapValue, onChange, selectedKeys]);
return /* @__PURE__ */ jsxs6(
"div",
{
style: {
padding: 10,
display: "flex",
flexDirection: "column",
alignItems: "flex-end"
},
children: [
/* @__PURE__ */ jsx18("div", { style: { marginBottom: 12 }, children: childrenWithProps }),
/* @__PURE__ */ jsxs6(Space6, { children: [
/* @__PURE__ */ jsxs6(
Button4,
{
style: { backgroundColor: "#ffff" },
danger: true,
size: "small",
onClick: () => clearFilter(),
children: [
/* @__PURE__ */ jsx18(ClearOutlined, {}),
translate("buttons.clear", "Clear")
]
}
),
/* @__PURE__ */ jsxs6(Button4, { type: "primary", size: "small", onClick: () => onFilter(), children: [
/* @__PURE__ */ jsx18(FilterOutlined2, {}),
" ",
translate("buttons.filter", "Filter")
] })
] })
]
}
);
};
// src/components/form-loading-skeleton/form-loading-skeleton.tsx
import { Col, Row, Skeleton } from "antd";
import { Fragment as Fragment3, jsx as jsx19 } from "react/jsx-runtime";
var FormLoadingSkeleton = () => {
return /* @__PURE__ */ jsx19(Fragment3, { children: [...Array(4)].map((_, rowIndex) => /* @__PURE__ */ jsx19(Row, { gutter: 24, style: { marginBottom: 16 }, children: [...Array(2)].map((_2, colIndex) => /* @__PURE__ */ jsx19(
Col,
{
span: 12,
style: {
display: "flex",
flexDirection: "column",
padding: 4
},
children: /* @__PURE__ */ jsx19(Skeleton.Input, { active: true, style: { width: "100%" }, size: "small" })
},
colIndex
)) }, rowIndex)) });
};
// src/components/formatted-number-field/formatted-number-field.tsx
import { NumberField } from "@refinedev/antd";
import { jsx as jsx20 } from "react/jsx-runtime";
var FormattedNumberField = ({
value,
currency = "USD",
...rest
}) => {
return /* @__PURE__ */ jsx20(
NumberField,
{
...rest,
value,
options: { style: "currency", currency },
prefix: "2"
}
);
};
// src/components/full-width-layout/full-width-layout.tsx
import { ThemedLayoutV2 } from "@refinedev/antd";
// src/components/header-with-menu/header-with-menu.tsx
import { Link as Link2, useMenu } from "@refinedev/core";
import { Button as Button5, Flex as Flex2, Layout, Menu as Menu2 } from "antd";
import classNames from "classnames";
// src/components/icons/icon-invoicer.tsx
import { jsx as jsx21, jsxs as jsxs7 } from "react/jsx-runtime";
var IconInvoicerLogo = (props) => {
const { mode } = useConfigProvider();
const isDarkMode = mode === "dark";
return /* @__PURE__ */ jsxs7(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
width: 24,
height: 20,
viewBox: "0 0 24 20",
fill: "none",
...props,
children: [
/* @__PURE__ */ jsx21("title", { children: "Invoicer" }),
/* @__PURE__ */ jsx21(
"path",
{
fill: isDarkMode ? "#3E2069" : "#EFDBFF",
d: "m0 0 24 4v12L0 20V0Z"
}
),
/* @__PURE__ */ jsx21(
"path",
{
fill: isDarkMode ? "#CDA8F0" : "#531DAB",
d: "M4.5 14.015 7.54 20 24 0 4.5 14.015Z"
}
),
/* @__PURE__ */ jsx21(
"path",
{
fill: isDarkMode ? "#EBD7FA" : "#22075E",
d: "M7.54 20v-4l2.681 1.267L7.541 20Z"
}
),
/* @__PURE__ */ jsx21(
"path",
{
fill: isDarkMode ? "#AB7AE0" : "#9254DE",
d: "m0 11 4.5 3.015L24 0 7.54 16 16 20l8-20L0 11Z"
}
)
]
}
);
};
// src/components/icons/icon-moon.tsx
import { jsx as jsx22, jsxs as jsxs8 } from "react/jsx-runtime";
var IconMoon = () => /* @__PURE__ */ jsxs8(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
className: "icon icon-tabler icon-tabler-moon-stars",
width: 20,
height: 20,
viewBox: "0 0 24 24",
strokeWidth: 2,
stroke: "currentColor",
fill: "none",
strokeLinecap: "round",
strokeLinejoin: "round",
children: [
/* @__PURE__ */ jsx22("path", { stroke: "none", d: "M0 0h24v24H0z", fill: "none" }),
/* @__PURE__ */ jsx22("path", { d: "M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" }),
/* @__PURE__ */ jsx22("path", { d: "M17 4a2 2 0 0 0 2 2a2 2 0 0 0 -2 2a2 2 0 0 0 -2 -2a2 2 0 0 0 2 -2" }),
/* @__PURE__ */ jsx22("path", { d: "M19 11h2m-1 -1v2" })
]
}
);
// src/components/icons/icon-sun.tsx
import { jsx as jsx23, jsxs as jsxs9 } from "react/jsx-runtime";
var IconSun = () => /* @__PURE__ */ jsxs9(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
className: "icon icon-tabler icon-tabler-sun",
width: 20,
height: 20,
viewBox: "0 0 24 24",
strokeWidth: "2",
stroke: "currentColor",
fill: "none",
strokeLinecap: "round",
strokeLinejoin: "round",
children: [
/* @__PURE__ */ jsx23("path", { stroke: "none", d: "M0 0h24v24H0z", fill: "none" }),
/* @__PURE__ */ jsx23("circle", { cx: 12, cy: 12, r: 4 }),
/* @__PURE__ */ jsx23("path", { d: "M3 12h1m8 -9v1m8 8h1m-9 8v1m-6.4 -15.4l.7 .7m12.1 -.7l-.7 .7m0 11.4l.7 .7m-12.1 -.7l-.7 .7" })
]
}
);
// src/components/logo/logo.tsx
import { jsx as jsx24 } from "react/jsx-runtime";
var WIDTH = 120;
var HEIGHT = 120;
var Logo = ({ height, width, src, alt }) => {
height = height || HEIGHT;
width = width || WIDTH;
if (!src) {
return;
}
return /* @__PURE__ */ jsx24(
"img",
{
alt,
src,
width,
height,
style: { maxWidth: "100%", maxHeight: "100%" }
}
);
};
// src/components/sider/menu-items.tsx
import { CanAccess, pickNotDeprecated } from "@refinedev/core";
import { UnorderedListOutlined } from "@ant-design/icons";
import { Menu } from "antd";
import { Fragment as Fragment4, jsx as jsx25, jsxs as jsxs10 } from "react/jsx-runtime";
var renderMenuItems = ({
tree,
selectedKey,
Link: Link5,
siderCollapsed
}) => {
return /* @__PURE__ */ jsx25(Fragment4, { children: tree.map((item) => {
const {
icon,
label,
route,
key,
name,
children,
parentName,
meta,
options
} = item;
if (children.length > 0) {
return /* @__PURE__ */ jsx25(
CanAccess,
{
resource: name.toLowerCase(),
action: "list",
params: { resource: item },
children: /* @__PURE__ */ jsx25(
Menu.SubMenu,
{
icon: icon ?? /* @__PURE__ */ jsx25(UnorderedListOutlined, {}),
title: label,
children: renderMenuItems({
tree: children,
selectedKey,
Link: Link5,
siderCollapsed
})
}
)
},
key
);
}
const isSelected = key === selectedKey;
const isRoute = !(pickNotDeprecated(meta?.parent, options?.parent, parentName) !== void 0 && children.length === 0);
return /* @__PURE__ */ jsx25(
CanAccess,
{
resource: name.toLowerCase(),
action: "list",
params: { resource: item },
children: /* @__PURE__ */ jsxs10(
Menu.Item,
{
icon: icon ?? (isRoute && /* @__PURE__ */ jsx25(UnorderedListOutlined, {})),
children: [
/* @__PURE__ */ jsx25(Link5, { to: route ?? "", children: label }),
!siderCollapsed && isSelected && /* @__PURE__ */ jsx25("div", { className: "ant-menu-tree-arrow" })
]
},
key
)
},
key
);
}) });
};
// src/components/header-with-menu/styled.tsx
import { createStyles } from "antd-style";
var useStyles = createStyles(({ css, token, isDarkMode, responsive }) => {
return {
headerContainer: {
backgroundColor: token.colorBgElevated,
padding: "0 16px",
minHeight: "48px",
height: "max-content"
},
flexContainer: {
width: "100%",
maxWidth: "1440px",
margin: "0 auto",
height: "100%"
},
headerTitleRefine: {
fontFamily: "Bricolage Grotesque, sans-serif",
color: isDarkMode ? token["purple-10"] : token["purple-8"]
},
headerTitleInvoicer: {
fontFamily: "Bricolage Grotesque, sans-serif",
color: isDarkMode ? token["purple-10"] : token["purple-8"],
fontWeight: 700
},
tabs: css`
margin-left: auto;
margin-right: auto;
width: 600px;
${responsive.mobile} {
width: 320px;
}
`,
inputSuffix: {
width: "20px",
height: "20px",
display: "flex",
alignItems: "center",
justifyContent: "center",
backgroundColor: token.colorBgTextHover,
color: token.colorTextDisabled,
borderRadius: "4px",
fontSize: "12px"
},
inputPrefix: {
color: token.colorTextPlaceholder,
marginRight: "4px"
},
languageSwitchText: {
color: token.colorTextSecondary
},
languageSwitchIcon: {
color: token.colorTextTertiary,
width: "10px"
},
themeSwitch: {
display: "flex",
alignItems: "center",
justifyContent: "center",
height: "32px",
width: "32px",
borderRadius: "50%",
cursor: "pointer",
flexShrink: 0,
backgroundColor: isDarkMode ? token.colorBgLayout : token.colorBgTextHover
},
rightSlot: {
marginLeft: "auto",
"@media (max-width: 1000px)": {
padding: "16px"
}
}
};
});
// src/components/header-with-menu/header-with-menu.tsx
import { Fragment as Fragment5, jsx as jsx26, jsxs as jsxs11 } from "react/jsx-runtime";
var HeaderWithMenu = ({
Search,
logo,
logoWidth,
logoHeight,
hideThemeButton,
beforeLogout,
disabledOverflow,
overflowedIndicator,
style,
className
}) => {
const { menuItems, selectedKey, defaultOpenKeys } = useMenu();
const { styles: styles2 } = useStyles();
const { isDarkMode, toggleMode } = useConfigProvider();
return /* @__PURE__ */ jsx26(
Layout.Header,
{
className: classNames("print-hidden", styles2.headerContainer, className),
style,
children: /* @__PURE__ */ jsxs11(
Flex2,
{
align: "center",
justify: "space-between",
wrap: "wrap",
className: styles2.flexContainer,
children: [
/* @__PURE__ */ jsxs11(Flex2, { align: "center", wrap: "wrap", children: [
logo ? /* @__PURE__ */ jsx26(
Link2,
{
go: {
to: "/"
},
children: /* @__PURE__ */ jsx26(Logo, { src: logo, width: logoWidth, height: logoHeight })
}
) : /* @__PURE__ */ jsx26(Fragment5, {}),
/* @__PURE__ */ jsx26(
Menu2,
{
selectedKeys: selectedKey ? [selectedKey] : [],
defaultOpenKeys,
mode: "horizontal",
className: styles2.tabs,
overflowedIndicator,
disabledOverflow,
children: renderMenuItems({
tree: menuItems,
Link: Link2,
selectedKey,
siderCollapsed: false
})
}
)
] }),
/* @__PURE__ */ jsxs11(Flex2, { align: "center", gap: 32, className: styles2.rightSlot, children: [
Search ? /* @__PURE__ */ jsx26(Search, {}) : null,
!hideThemeButton && /* @__PURE__ */ jsx26(
Button5,
{
className: styles2.themeSwitch,
type: "text",
icon: !isDarkMode ? /* @__PURE__ */ jsx26(IconMoon, {}) : /* @__PURE__ */ jsx26(IconSun, {}),
onClick: () => {
toggleMode();
}
}
),
/* @__PURE__ */ jsx26(CurrentUser, { beforeLogout })
] })
]
}
)
}
);
};
// src/components/full-width-layout/full-width-layout.tsx
import { jsx as jsx27 } from "react/jsx-runtime";
var FullWidthLayout = ({
headerProps,
overflowedIndicator,
disabledOverflow,
children,
className,
contentClassName,
contentStyle,
style
}) => {
return /* @__PURE__ */ jsx27(
ThemedLayoutV2,
{
Header: (layoutHeaderProps) => /* @__PURE__ */ jsx27(
HeaderWithMenu,
{
...headerProps || {},
overflowedIndicator,
disabledOverflow,
...layoutHeaderProps
}
),
Sider: () => null,
children: /* @__PURE__ */ jsx27(
"div",
{
style: {
width: "100%",
...style
},
className,
children: /* @__PURE__ */ jsx27(
"div",
{
style: {
maxWidth: "100%",
margin: "0 auto",
...contentStyle
},
className: contentClassName,
children
}
)
}
)
}
);
};
// src/components/header/header.tsx
import { Layout as Layout2, Space as Space7, theme as theme2 } from "antd";
import { jsx as jsx28, jsxs as jsxs12 } from "react/jsx-runtime";
var { useToken } = theme2;
var HeaderWithSearch = ({
Search,
beforeLogout,
beforeCurrentUser
}) => {
const { token } = useToken();
const headerStyles = {
backgroundColor: token.colorBgElevated,
display: "flex",
justifyContent: Search ? "space-between" : "flex-end",
alignItems: "center",
padding: "0px 24px",
height: "64px",
position: "sticky",
top: 0,
zIndex: 999
};
return /* @__PURE__ */ jsxs12(Layout2.Header, { style: headerStyles, children: [
Search ? /* @__PURE__ */ jsx28(Search, {}) : null,
/* @__PURE__ */ jsxs12(Space7, { align: "center", size: "middle", children: [
beforeCurrentUser,
/* @__PURE__ */ jsx28(CurrentUser, { beforeLogout })
] })
] });
};
// src/components/image-upload/image-upload.tsx
import { Image, Upload } from "antd";
// src/components/upload-button/upload-button.tsx
import { jsx as jsx29, jsxs as jsxs13 } from "react/jsx-runtime";
var UploadButton = ({ icon, label }) => {
return /* @__PURE__ */ jsxs13("button", { style: { border: 0, background: "none" }, type: "button", children: [
icon,
/* @__PURE__ */ jsx29("div", { style: { marginTop: 8 }, children: label })
] });
};
// src/components/image-upload/image-upload.tsx
import { Fragment as Fragment6, jsx as jsx30, jsxs as jsxs14 } from "react/jsx-runtime";
var ImageUpload = ({
listType,
fileList,
onPreview,
onChange,
previewImage,
previewImageOpen,
onVisibleChange,
afterOpenChange,
onRemove,
multiple,
disabled,
...rest
}) => {
return /* @__PURE__ */ jsxs14(Fragment6, { children: [
/* @__PURE__ */ jsx30(
Upload,
{
listType: listType || "picture-card",
fileList,
onPreview,
onChange,
beforeUpload: () => false,
onRemove,
multiple,
disabled,
...rest,
children: fileList && fileList.length >= 1 && !multiple ? null : /* @__PURE__ */ jsx30(UploadButton, {})
}
),
previewImage && /* @__PURE__ */ jsx30(
Image,
{
wrapperStyle: { display: "none" },
preview: {
visible: previewImageOpen,
onVisibleChange,
afterOpenChange
},
src: previewImage
}
)
] });
};
// src/components/import-button/import-button.tsx
import { UploadOutlined } from "@ant-design/icons";
import { Button as Button6 } from "antd";
import { useTheme } from "antd-style";
import { jsx as jsx31 } from "react/jsx-runtime";
var ImportButton = ({
canAccess,
openModal,
title
}) => {
const token = useTheme();
if (!canAccess) {
return null;
}
return /* @__PURE__ */ jsx31(
Button6,
{
icon: /* @__PURE__ */ jsx31(UploadOutlined, {}),
style: {
backgroundColor: token.colorPrimary,
color: "#FFFF",
border: "none"
},
onClick: openModal,
children: title || "Upload"
}
);
};
// src/components/import-modal/import-modal.tsx
import {
CloseOutlined,
FileAddOutlined,
InboxOutlined
} from "@ant-design/icons";
import { useUpload } from "@ucheeddev/react-web";
import { Form, Modal } from "antd";
import Dragger from "antd/es/upload/Dragger";
import { jsx as jsx32, jsxs as jsxs15 } from "react/jsx-runtime";
var ImportModal = ({
modalProps,
formProps,
onFinish,
onCancel,
isOpen,
isLoading,
title,
extra,
width
}) => {
const attachmentsUploadProps = useUpload();
return /* @__PURE__ */ jsx32(
Modal,
{
...modalProps,
onCancel,
closeIcon: /* @__PURE__ */ jsx32(CloseOutlined, {}),
open: isOpen,
width: width || 460,
title: title || "Import Records",
okText: "Import",
okButtonProps: {
...modalProps.okButtonProps || {},
loading: isLoading
},
children: /* @__PURE__ */ jsxs15(
Form,
{
...formProps,
layout: "vertical",
onFinish: (values) => {
const formData = new FormData();
for (const key in values) {
formData.set(key, values[key]);
}
formData.set(
"import_file",
attachmentsUploadProps.file || ""
);
return onFinish?.(formData);
},
children: [
extra,
/* @__PURE__ */ jsxs15(Form.Item, { name: "import_file", children: [
/* @__PURE__ */ jsx32(
FieldFormTitle,
{
title: "Drag a File",
Icon: FileAddOutlined,
titleStyle: { fontSize: 16, fontWeight: 500 }
}
),
/* @__PURE__ */ jsxs15(Dragger, { ...attachmentsUploadProps, children: [
/* @__PURE__ */ jsx32("p", { className: "