ui: fixed card and tag removal, alert buttons, kai input disable state
This commit is contained in:
parent
75230d72ec
commit
0d9c8d70c8
7 changed files with 96 additions and 82 deletions
|
|
@ -1,10 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Button, Form, Input, Space, Modal } from 'antd';
|
import { Button, Form, Input, Space } from 'antd';
|
||||||
import { Trash } from 'UI/Icons';
|
import { Trash } from 'UI/Icons';
|
||||||
import { useStore } from '@/mstore';
|
import { useStore } from '@/mstore';
|
||||||
import { useModal } from 'Components/ModalContext';
|
import { useModal } from 'Components/ModalContext';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { confirm } from 'UI';
|
||||||
interface Props {
|
interface Props {
|
||||||
tag: any;
|
tag: any;
|
||||||
projectId: number;
|
projectId: number;
|
||||||
|
|
@ -23,14 +23,16 @@ function TagForm(props: Props) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDelete = async () => {
|
const onDelete = async () => {
|
||||||
Modal.confirm({
|
if (
|
||||||
title: t('Tag'),
|
await confirm({
|
||||||
content: t('Are you sure you want to remove?'),
|
header: t('Remove Tag'),
|
||||||
onOk: async () => {
|
confirmButton: t('Remove'),
|
||||||
await tagWatchStore.deleteTag(tag.tagId, projectId);
|
confirmation: t('Are you sure you want to remove this tag?'),
|
||||||
closeModal();
|
})
|
||||||
},
|
) {
|
||||||
});
|
await tagWatchStore.deleteTag(tag.tagId, projectId);
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSave = async () => {
|
const onSave = async () => {
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ function BottomButtons({
|
||||||
<Button
|
<Button
|
||||||
loading={loading}
|
loading={loading}
|
||||||
type="primary"
|
type="primary"
|
||||||
|
htmlType="submit"
|
||||||
disabled={loading || !instance.validate()}
|
disabled={loading || !instance.validate()}
|
||||||
id="submit-button"
|
id="submit-button"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,7 @@ function Condition({
|
||||||
<label className="w-1/6 flex-shrink-0 font-normal">{t('is')}</label>
|
<label className="w-1/6 flex-shrink-0 font-normal">{t('is')}</label>
|
||||||
<div className="w-2/6 flex items-center">
|
<div className="w-2/6 flex items-center">
|
||||||
<Select
|
<Select
|
||||||
|
popupMatchSelectWidth={false}
|
||||||
placeholder={t('Select Condition')}
|
placeholder={t('Select Condition')}
|
||||||
options={localizedConditions}
|
options={localizedConditions}
|
||||||
name="operator"
|
name="operator"
|
||||||
|
|
|
||||||
|
|
@ -7,19 +7,18 @@ import {
|
||||||
Button,
|
Button,
|
||||||
Dropdown,
|
Dropdown,
|
||||||
Modal as AntdModal,
|
Modal as AntdModal,
|
||||||
Avatar, TableColumnType, Spin
|
Avatar,
|
||||||
|
TableColumnType,
|
||||||
|
Spin,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import {
|
import { EditOutlined, DeleteOutlined } from '@ant-design/icons';
|
||||||
EditOutlined,
|
|
||||||
DeleteOutlined,
|
|
||||||
} from '@ant-design/icons';
|
|
||||||
import { EllipsisVertical } from 'lucide-react';
|
import { EllipsisVertical } from 'lucide-react';
|
||||||
import { TablePaginationConfig, SorterResult } from 'antd/lib/table/interface';
|
import { TablePaginationConfig, SorterResult } from 'antd/lib/table/interface';
|
||||||
import { useStore } from 'App/mstore';
|
import { useStore } from 'App/mstore';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { useHistory } from 'react-router';
|
import { useHistory } from 'react-router';
|
||||||
import { withSiteId } from 'App/routes';
|
import { withSiteId } from 'App/routes';
|
||||||
import { Icon } from 'UI';
|
import { Icon, confirm } from 'UI';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import { TYPE_ICONS, TYPE_NAMES } from 'App/constants/card';
|
import { TYPE_ICONS, TYPE_NAMES } from 'App/constants/card';
|
||||||
import Widget from 'App/mstore/types/widget';
|
import Widget from 'App/mstore/types/widget';
|
||||||
|
|
@ -45,7 +44,7 @@ const ListView: React.FC<Props> = ({
|
||||||
toggleSelection,
|
toggleSelection,
|
||||||
disableSelection = false,
|
disableSelection = false,
|
||||||
inLibrary = false,
|
inLibrary = false,
|
||||||
loading = false
|
loading = false,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [editingMetricId, setEditingMetricId] = useState<number | null>(null);
|
const [editingMetricId, setEditingMetricId] = useState<number | null>(null);
|
||||||
|
|
@ -63,7 +62,7 @@ const ListView: React.FC<Props> = ({
|
||||||
<Text strong>
|
<Text strong>
|
||||||
{Math.min(
|
{Math.min(
|
||||||
(metricStore.pageSize || 10) * (metricStore.page || 1),
|
(metricStore.pageSize || 10) * (metricStore.page || 1),
|
||||||
list.length
|
list.length,
|
||||||
)}
|
)}
|
||||||
</Text>{' '}
|
</Text>{' '}
|
||||||
{t('of')} <Text strong>{list.length}</Text> {t('cards')}
|
{t('of')} <Text strong>{list.length}</Text> {t('cards')}
|
||||||
|
|
@ -124,15 +123,17 @@ const ListView: React.FC<Props> = ({
|
||||||
|
|
||||||
const onMenuClick = async (metric: Widget, { key }: { key: string }) => {
|
const onMenuClick = async (metric: Widget, { key }: { key: string }) => {
|
||||||
if (key === 'delete') {
|
if (key === 'delete') {
|
||||||
AntdModal.confirm({
|
if (
|
||||||
title: t('Confirm'),
|
await confirm({
|
||||||
content: t('Are you sure you want to permanently delete this card?'),
|
header: t('Delete Card'),
|
||||||
okText: t('Yes, delete'),
|
confirmButton: t('Delete'),
|
||||||
cancelText: t('No'),
|
confirmation: t(
|
||||||
onOk: async () => {
|
'Are you sure you want to permanently delete this card? This action cannot be undone.',
|
||||||
await metricStore.delete(metric);
|
),
|
||||||
}
|
})
|
||||||
});
|
) {
|
||||||
|
await metricStore.delete(metric);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (key === 'rename') {
|
if (key === 'rename') {
|
||||||
setEditingMetricId(metric.metricId);
|
setEditingMetricId(metric.metricId);
|
||||||
|
|
@ -155,7 +156,7 @@ const ListView: React.FC<Props> = ({
|
||||||
|
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
{ key: 'rename', icon: <EditOutlined />, label: t('Rename') },
|
{ key: 'rename', icon: <EditOutlined />, label: t('Rename') },
|
||||||
{ key: 'delete', icon: <DeleteOutlined />, label: t('Delete') }
|
{ key: 'delete', icon: <DeleteOutlined />, label: t('Delete') },
|
||||||
];
|
];
|
||||||
|
|
||||||
const renderTitle = (_text: string, metric: Widget) => (
|
const renderTitle = (_text: string, metric: Widget) => (
|
||||||
|
|
@ -201,9 +202,10 @@ const ListView: React.FC<Props> = ({
|
||||||
key: 'title',
|
key: 'title',
|
||||||
className: 'cap-first pl-4',
|
className: 'cap-first pl-4',
|
||||||
sorter: true,
|
sorter: true,
|
||||||
sortOrder: metricStore.sort.field === 'name' ? metricStore.sort.order : undefined,
|
sortOrder:
|
||||||
|
metricStore.sort.field === 'name' ? metricStore.sort.order : undefined,
|
||||||
width: inLibrary ? '31%' : '25%',
|
width: inLibrary ? '31%' : '25%',
|
||||||
render: renderTitle
|
render: renderTitle,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('Owner'),
|
title: t('Owner'),
|
||||||
|
|
@ -211,19 +213,25 @@ const ListView: React.FC<Props> = ({
|
||||||
key: 'owner',
|
key: 'owner',
|
||||||
className: 'capitalize',
|
className: 'capitalize',
|
||||||
sorter: true,
|
sorter: true,
|
||||||
sortOrder: metricStore.sort.field === 'owner_email' ? metricStore.sort.order : undefined,
|
sortOrder:
|
||||||
|
metricStore.sort.field === 'owner_email'
|
||||||
|
? metricStore.sort.order
|
||||||
|
: undefined,
|
||||||
width: inLibrary ? '31%' : '25%',
|
width: inLibrary ? '31%' : '25%',
|
||||||
render: renderOwner
|
render: renderOwner,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('Last Modified'),
|
title: t('Last Modified'),
|
||||||
dataIndex: 'edited_at',
|
dataIndex: 'edited_at',
|
||||||
key: 'lastModified',
|
key: 'lastModified',
|
||||||
sorter: true,
|
sorter: true,
|
||||||
sortOrder: metricStore.sort.field === 'edited_at' ? metricStore.sort.order : undefined,
|
sortOrder:
|
||||||
|
metricStore.sort.field === 'edited_at'
|
||||||
|
? metricStore.sort.order
|
||||||
|
: undefined,
|
||||||
width: inLibrary ? '31%' : '25%',
|
width: inLibrary ? '31%' : '25%',
|
||||||
render: renderLastModified
|
render: renderLastModified,
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!inLibrary) {
|
if (!inLibrary) {
|
||||||
|
|
@ -232,14 +240,14 @@ const ListView: React.FC<Props> = ({
|
||||||
key: 'options',
|
key: 'options',
|
||||||
className: 'text-right',
|
className: 'text-right',
|
||||||
width: '5%',
|
width: '5%',
|
||||||
render: renderOptions
|
render: renderOptions,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleTableChange = (
|
const handleTableChange = (
|
||||||
pag: TablePaginationConfig,
|
pag: TablePaginationConfig,
|
||||||
_filters: Record<string, (string | number | boolean)[] | null>,
|
_filters: Record<string, (string | number | boolean)[] | null>,
|
||||||
sorterParam: SorterResult<Widget> | SorterResult<Widget>[]
|
sorterParam: SorterResult<Widget> | SorterResult<Widget>[],
|
||||||
) => {
|
) => {
|
||||||
const sorter = Array.isArray(sorterParam) ? sorterParam[0] : sorterParam;
|
const sorter = Array.isArray(sorterParam) ? sorterParam[0] : sorterParam;
|
||||||
let order = sorter.order;
|
let order = sorter.order;
|
||||||
|
|
@ -268,19 +276,19 @@ const ListView: React.FC<Props> = ({
|
||||||
onRow={
|
onRow={
|
||||||
inLibrary
|
inLibrary
|
||||||
? (record) => ({
|
? (record) => ({
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
if (!disableSelection) toggleSelection?.(record?.metricId);
|
if (!disableSelection) toggleSelection?.(record?.metricId);
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
rowSelection={
|
rowSelection={
|
||||||
!disableSelection
|
!disableSelection
|
||||||
? {
|
? {
|
||||||
selectedRowKeys: selectedList,
|
selectedRowKeys: selectedList,
|
||||||
onChange: (keys) => toggleSelection && toggleSelection(keys),
|
onChange: (keys) => toggleSelection && toggleSelection(keys),
|
||||||
columnWidth: 16
|
columnWidth: 16,
|
||||||
}
|
}
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
pagination={{
|
pagination={{
|
||||||
|
|
@ -292,7 +300,7 @@ const ListView: React.FC<Props> = ({
|
||||||
showLessItems: true,
|
showLessItems: true,
|
||||||
showTotal: () => totalMessage,
|
showTotal: () => totalMessage,
|
||||||
size: 'small',
|
size: 'small',
|
||||||
simple: true
|
simple: true,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<AntdModal
|
<AntdModal
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,10 @@ import AddCardSection from '../AddCardSection/AddCardSection';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
function MetricsList({
|
function MetricsList({
|
||||||
siteId,
|
siteId,
|
||||||
onSelectionChange,
|
onSelectionChange,
|
||||||
inLibrary
|
inLibrary,
|
||||||
}: {
|
}: {
|
||||||
siteId: string;
|
siteId: string;
|
||||||
onSelectionChange?: (selected: any[]) => void;
|
onSelectionChange?: (selected: any[]) => void;
|
||||||
inLibrary?: boolean;
|
inLibrary?: boolean;
|
||||||
|
|
@ -26,16 +26,16 @@ function MetricsList({
|
||||||
const dashboard = dashboardStore.selectedDashboard;
|
const dashboard = dashboardStore.selectedDashboard;
|
||||||
const existingCardIds = useMemo(
|
const existingCardIds = useMemo(
|
||||||
() => dashboard?.widgets?.map((i) => parseInt(i.metricId)),
|
() => dashboard?.widgets?.map((i) => parseInt(i.metricId)),
|
||||||
[dashboard]
|
[dashboard],
|
||||||
);
|
);
|
||||||
const cards = useMemo(
|
const cards = useMemo(
|
||||||
() =>
|
() =>
|
||||||
onSelectionChange
|
onSelectionChange
|
||||||
? metricStore.filteredCards.filter(
|
? metricStore.filteredCards.filter(
|
||||||
(i) => !existingCardIds?.includes(parseInt(i.metricId))
|
(i) => !existingCardIds?.includes(parseInt(i.metricId)),
|
||||||
)
|
)
|
||||||
: metricStore.filteredCards,
|
: metricStore.filteredCards,
|
||||||
[metricStore.filteredCards, existingCardIds, onSelectionChange]
|
[metricStore.filteredCards, existingCardIds, onSelectionChange],
|
||||||
);
|
);
|
||||||
const loading = metricStore.isLoading;
|
const loading = metricStore.isLoading;
|
||||||
|
|
||||||
|
|
@ -66,7 +66,8 @@ function MetricsList({
|
||||||
metricStore.updateKey('sessionsPage', 1);
|
metricStore.updateKey('sessionsPage', 1);
|
||||||
}, [metricStore]);
|
}, [metricStore]);
|
||||||
|
|
||||||
const isFiltered = metricStore.filter.query !== '' || metricStore.filter.type !== '';
|
const isFiltered =
|
||||||
|
metricStore.filter.query !== '' || metricStore.filter.type !== '';
|
||||||
|
|
||||||
const searchImageDimensions = { width: 60, height: 'auto' };
|
const searchImageDimensions = { width: 60, height: 'auto' };
|
||||||
const defaultImageDimensions = { width: 600, height: 'auto' };
|
const defaultImageDimensions = { width: 600, height: 'auto' };
|
||||||
|
|
@ -93,7 +94,9 @@ function MetricsList({
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
<div>
|
<div>
|
||||||
{t('Create and customize cards to analyze trends and user behavior effectively.')}
|
{t(
|
||||||
|
'Create and customize cards to analyze trends and user behavior effectively.',
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<Popover
|
<Popover
|
||||||
arrow={false}
|
arrow={false}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { useHistory } from 'react-router';
|
import { useHistory } from 'react-router';
|
||||||
import { useStore } from 'App/mstore';
|
import { useStore } from 'App/mstore';
|
||||||
import { observer } from 'mobx-react-lite';
|
import { observer } from 'mobx-react-lite';
|
||||||
import { Button, Dropdown, MenuProps, Modal } from 'antd';
|
import { Button, Dropdown, MenuProps } from 'antd';
|
||||||
import {
|
import {
|
||||||
BellIcon,
|
BellIcon,
|
||||||
EllipsisVertical,
|
EllipsisVertical,
|
||||||
|
|
@ -14,6 +14,7 @@ import { useModal } from 'Components/ModalContext';
|
||||||
import AlertFormModal from 'Components/Alerts/AlertFormModal/AlertFormModal';
|
import AlertFormModal from 'Components/Alerts/AlertFormModal/AlertFormModal';
|
||||||
import { showAddToDashboardModal } from 'Components/Dashboard/components/AddToDashboardButton';
|
import { showAddToDashboardModal } from 'Components/Dashboard/components/AddToDashboardButton';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { confirm } from 'UI';
|
||||||
|
|
||||||
function CardViewMenu() {
|
function CardViewMenu() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
@ -51,29 +52,25 @@ function CardViewMenu() {
|
||||||
label: t('Delete'),
|
label: t('Delete'),
|
||||||
icon: <TrashIcon size={15} />,
|
icon: <TrashIcon size={15} />,
|
||||||
disabled: !widget.exists(),
|
disabled: !widget.exists(),
|
||||||
onClick: () => {
|
onClick: async () => {
|
||||||
Modal.confirm({
|
if (
|
||||||
title: t('Confirm Card Deletion'),
|
await confirm({
|
||||||
icon: null,
|
header: t('Remove Card'),
|
||||||
content:
|
confirmButton: t('Remove'),
|
||||||
t('Are you sure you want to remove this card? This action is permanent and cannot be undone.'),
|
confirmation: t(
|
||||||
footer: (_, { OkBtn, CancelBtn }) => (
|
'Are you sure you want to remove this card? This action is permanent and cannot be undone.',
|
||||||
<>
|
),
|
||||||
<CancelBtn />
|
})
|
||||||
<OkBtn />
|
) {
|
||||||
</>
|
metricStore
|
||||||
),
|
.delete(widget)
|
||||||
onOk: () => {
|
.then(() => {
|
||||||
metricStore
|
history.goBack();
|
||||||
.delete(widget)
|
})
|
||||||
.then(() => {
|
.catch(() => {
|
||||||
history.goBack();
|
toast.error(t('Failed to remove card'));
|
||||||
})
|
});
|
||||||
.catch(() => {
|
}
|
||||||
toast.error(t('Failed to remove card'));
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,9 @@ function ChatInput({
|
||||||
kaiStore.setQueryText(text);
|
kaiStore.setQueryText(text);
|
||||||
};
|
};
|
||||||
|
|
||||||
const submit = () => {
|
const submit = (e: any) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
if (limited) {
|
if (limited) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -80,7 +82,7 @@ function ChatInput({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<div className="relative bg-white">
|
||||||
<Input
|
<Input
|
||||||
onPressEnter={submit}
|
onPressEnter={submit}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue