parent
512230f224
commit
31d82f9d1d
14 changed files with 367 additions and 124 deletions
|
|
@ -1,60 +1,93 @@
|
|||
import { Card, Col, Modal, Row, Typography } from 'antd';
|
||||
import { GalleryVertical, Plus } from 'lucide-react';
|
||||
import React from 'react';
|
||||
import {Card, Col, Modal, Row, Typography} from "antd";
|
||||
import {GalleryVertical, Plus} from "lucide-react";
|
||||
import NewDashboardModal from "Components/Dashboard/components/DashboardList/NewDashModal";
|
||||
import {useStore} from "App/mstore";
|
||||
|
||||
import { useStore } from 'App/mstore';
|
||||
import NewDashboardModal from 'Components/Dashboard/components/DashboardList/NewDashModal';
|
||||
|
||||
import AiQuery from './DashboardView/AiQuery';
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
onClose?: () => void;
|
||||
open: boolean;
|
||||
onClose?: () => void;
|
||||
}
|
||||
|
||||
function AddCardSelectionModal(props: Props) {
|
||||
const {metricStore} = useStore();
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [isLibrary, setIsLibrary] = React.useState(false);
|
||||
const { metricStore } = useStore();
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [isLibrary, setIsLibrary] = React.useState(false);
|
||||
|
||||
const onCloseModal = () => {
|
||||
setOpen(false);
|
||||
props.onClose && props.onClose();
|
||||
}
|
||||
const onCloseModal = () => {
|
||||
setOpen(false);
|
||||
props.onClose && props.onClose();
|
||||
};
|
||||
|
||||
const onClick = (isLibrary: boolean) => {
|
||||
if (!isLibrary) {
|
||||
metricStore.init();
|
||||
}
|
||||
setIsLibrary(isLibrary);
|
||||
setOpen(true);
|
||||
const onClick = (isLibrary: boolean) => {
|
||||
if (!isLibrary) {
|
||||
metricStore.init();
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
title="Add a card to dashboard"
|
||||
open={props.open}
|
||||
footer={null}
|
||||
onCancel={props.onClose}
|
||||
className='addCard'
|
||||
setIsLibrary(isLibrary);
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
const originStr = window.env.ORIGIN || window.location.origin;
|
||||
const testingKey = localStorage.getItem('__mauricio_testing_access') === 'true';
|
||||
|
||||
const isSaas = testingKey && /app\.openreplay\.com/.test(originStr);
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
title="Add a card to dashboard"
|
||||
open={props.open}
|
||||
footer={null}
|
||||
onCancel={props.onClose}
|
||||
className="addCard"
|
||||
width={isSaas ? 900 : undefined}
|
||||
>
|
||||
{isSaas ? (
|
||||
<>
|
||||
<Row gutter={16} justify="center" className="py-2">
|
||||
<AiQuery />
|
||||
</Row>
|
||||
<div
|
||||
className={
|
||||
'flex items-center justify-center w-full text-disabled-text'
|
||||
}
|
||||
>
|
||||
<Row gutter={16} justify="center" className='py-5'>
|
||||
<Col span={12}>
|
||||
<div className="flex flex-col items-center justify-center hover:bg-indigo-50 border rounded-lg shadow-sm cursor-pointer gap-3" style={{height: '80px'}} onClick={() => onClick(true)}>
|
||||
<GalleryVertical style={{fontSize: '24px', color: '#394EFF'}}/>
|
||||
<Typography.Text strong>Add from library</Typography.Text>
|
||||
{/*<p>Select from 12 available</p>*/}
|
||||
</div>
|
||||
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<div className="flex flex-col items-center justify-center hover:bg-indigo-50 border rounded-lg shadow-sm cursor-pointer gap-3" style={{height: '80px'}} onClick={() => onClick(false)}>
|
||||
<Plus style={{fontSize: '24px', color: '#394EFF'}}/>
|
||||
<Typography.Text strong>Create New</Typography.Text>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Modal>
|
||||
<NewDashboardModal open={open} onClose={onCloseModal} isAddingFromLibrary={isLibrary}/>
|
||||
</>
|
||||
);
|
||||
or
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
<Row gutter={16} justify="center" className="py-5">
|
||||
<Col span={12}>
|
||||
<div
|
||||
className="flex flex-col items-center justify-center hover:bg-indigo-50 border rounded-lg shadow-sm cursor-pointer gap-3"
|
||||
style={{ height: '80px' }}
|
||||
onClick={() => onClick(true)}
|
||||
>
|
||||
<GalleryVertical style={{ fontSize: '24px', color: '#394EFF' }} />
|
||||
<Typography.Text strong>Add from library</Typography.Text>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<div
|
||||
className="flex flex-col items-center justify-center hover:bg-indigo-50 border rounded-lg shadow-sm cursor-pointer gap-3"
|
||||
style={{ height: '80px' }}
|
||||
onClick={() => onClick(false)}
|
||||
>
|
||||
<Plus style={{ fontSize: '24px', color: '#394EFF' }} />
|
||||
<Typography.Text strong>Create New</Typography.Text>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Modal>
|
||||
<NewDashboardModal
|
||||
open={open}
|
||||
onClose={onCloseModal}
|
||||
isAddingFromLibrary={isLibrary}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default AddCardSelectionModal;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ const getTitleByType = (type: string) => {
|
|||
|
||||
interface Props {
|
||||
// cardType: string,
|
||||
onBack: () => void
|
||||
onBack?: () => void
|
||||
onAdded?: () => void
|
||||
}
|
||||
|
||||
function CreateCard(props: Props) {
|
||||
|
|
@ -67,7 +68,8 @@ function CreateCard(props: Props) {
|
|||
|
||||
if (dashboardId) {
|
||||
await addCardToDashboard(dashboardId, cardId);
|
||||
dashboardStore.fetch(dashboardId);
|
||||
void dashboardStore.fetch(dashboardId);
|
||||
props.onAdded?.();
|
||||
} else if (isItDashboard) {
|
||||
const dashboardId = await createNewDashboard();
|
||||
await addCardToDashboard(dashboardId, cardId);
|
||||
|
|
@ -81,9 +83,9 @@ function CreateCard(props: Props) {
|
|||
<div className="flex gap-4 flex-col">
|
||||
<div className="flex items-center justify-between">
|
||||
<Space>
|
||||
<Button type="text" onClick={props.onBack}>
|
||||
<ArrowLeft size={16}/>
|
||||
</Button>
|
||||
{props.onBack ? <Button type="text" onClick={props.onBack}>
|
||||
<ArrowLeft size={16} />
|
||||
</Button> : null}
|
||||
<div className="text-xl leading-4 font-medium">
|
||||
{metric.name}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { Modal } from 'antd';
|
||||
import SelectCard from './SelectCard';
|
||||
import CreateCard from 'Components/Dashboard/components/DashboardList/NewDashModal/CreateCard';
|
||||
import colors from 'tailwindcss/colors';
|
||||
import React, { useEffect } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import colors from 'tailwindcss/colors';
|
||||
|
||||
import CreateCard from 'Components/Dashboard/components/DashboardList/NewDashModal/CreateCard';
|
||||
|
||||
import SelectCard from './SelectCard';
|
||||
|
||||
interface NewDashboardModalProps {
|
||||
onClose: () => void;
|
||||
|
|
@ -14,14 +16,15 @@ interface NewDashboardModalProps {
|
|||
}
|
||||
|
||||
const NewDashboardModal: React.FC<NewDashboardModalProps> = ({
|
||||
onClose,
|
||||
open,
|
||||
isAddingFromLibrary = false,
|
||||
isEnterprise = false,
|
||||
isMobile = false
|
||||
}) => {
|
||||
onClose,
|
||||
open,
|
||||
isAddingFromLibrary = false,
|
||||
isEnterprise = false,
|
||||
isMobile = false,
|
||||
}) => {
|
||||
const [step, setStep] = React.useState<number>(0);
|
||||
const [selectedCategory, setSelectedCategory] = React.useState<string>('product-analytics');
|
||||
const [selectedCategory, setSelectedCategory] =
|
||||
React.useState<string>('product-analytics');
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
|
|
@ -40,35 +43,42 @@ const NewDashboardModal: React.FC<NewDashboardModalProps> = ({
|
|||
closeIcon={false}
|
||||
styles={{
|
||||
content: {
|
||||
backgroundColor: colors.gray[100]
|
||||
}
|
||||
backgroundColor: colors.gray[100],
|
||||
},
|
||||
}}
|
||||
centered={true}
|
||||
>
|
||||
<div className="flex flex-col gap-4" style={{
|
||||
height: 'calc(100vh - 100px)',
|
||||
overflowY: 'auto',
|
||||
overflowX: 'hidden'
|
||||
}}>
|
||||
{step === 0 && <SelectCard onClose={onClose}
|
||||
selected={selectedCategory}
|
||||
setSelectedCategory={setSelectedCategory}
|
||||
onCard={() => setStep(step + 1)}
|
||||
isLibrary={isAddingFromLibrary}
|
||||
isMobile={isMobile}
|
||||
isEnterprise={isEnterprise} />}
|
||||
<div
|
||||
className="flex flex-col gap-4"
|
||||
style={{
|
||||
height: 'calc(100vh - 100px)',
|
||||
overflowY: 'auto',
|
||||
overflowX: 'hidden',
|
||||
}}
|
||||
>
|
||||
{step === 0 && (
|
||||
<SelectCard
|
||||
onClose={onClose}
|
||||
selected={selectedCategory}
|
||||
setSelectedCategory={setSelectedCategory}
|
||||
onCard={() => setStep(step + 1)}
|
||||
isLibrary={isAddingFromLibrary}
|
||||
isMobile={isMobile}
|
||||
isEnterprise={isEnterprise}
|
||||
/>
|
||||
)}
|
||||
{step === 1 && <CreateCard onBack={() => setStep(0)} />}
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
;
|
||||
);
|
||||
};
|
||||
|
||||
const mapStateToProps = (state: any) => ({
|
||||
isMobile: state.getIn(['site', 'instance', 'platform']) === 'ios',
|
||||
isEnterprise: state.getIn(['user', 'account', 'edition']) === 'ee' ||
|
||||
state.getIn(['user', 'account', 'edition']) === 'msaas'
|
||||
isEnterprise:
|
||||
state.getIn(['user', 'account', 'edition']) === 'ee' ||
|
||||
state.getIn(['user', 'account', 'edition']) === 'msaas',
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(NewDashboardModal);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,129 @@
|
|||
import { SendOutlined } from '@ant-design/icons';
|
||||
import { Modal } from 'antd';
|
||||
import Lottie from 'lottie-react';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import React from 'react';
|
||||
import colors from 'tailwindcss/colors';
|
||||
|
||||
import { gradientBox } from 'App/components/shared/SessionSearchField/AiSessionSearchField';
|
||||
import aiSpinner from 'App/lottie/aiSpinner.json';
|
||||
import { useStore } from 'App/mstore';
|
||||
import { Icon, Input } from 'UI';
|
||||
|
||||
import CreateCard from '../DashboardList/NewDashModal/CreateCard';
|
||||
|
||||
function AiQuery() {
|
||||
const grad = {
|
||||
background: 'linear-gradient(90deg, #F3F4FF 0%, #F2FEFF 100%)',
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<QueryModal />
|
||||
<div className={'rounded p-4 mb-4'} style={grad}>
|
||||
<InputBox />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const InputBox = observer(({ inModal }: { inModal?: boolean }) => {
|
||||
const { aiFiltersStore, metricStore } = useStore();
|
||||
const metric = metricStore.instance;
|
||||
const fetchResults = () => {
|
||||
aiFiltersStore
|
||||
.getCardFilters(aiFiltersStore.query, undefined)
|
||||
.then((f) => metric.createSeries(f.filters));
|
||||
if (!inModal) {
|
||||
aiFiltersStore.setModalOpen(true);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div className={'flex items-center mb-2 gap-2'}>
|
||||
<Icon name={'sparkles'} size={16} />
|
||||
<div className={'font-semibold'}>What would you like to visualize?</div>
|
||||
</div>
|
||||
<div style={gradientBox}>
|
||||
<Input
|
||||
wrapperClassName={'w-full pr-2'}
|
||||
value={aiFiltersStore.query}
|
||||
style={{
|
||||
minWidth: inModal ? '600px' : '840px',
|
||||
height: 34,
|
||||
borderRadius: 32,
|
||||
}}
|
||||
onChange={({ target }: any) => aiFiltersStore.setQuery(target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && aiFiltersStore.query.trim().length > 2) {
|
||||
fetchResults();
|
||||
}
|
||||
}}
|
||||
placeholder={'E.g., Track all the errors in checkout flow.'}
|
||||
className="ml-2 px-2 pe-9 text-lg placeholder-lg !border-0 rounded-e-full nofocus"
|
||||
leadingButton={
|
||||
aiFiltersStore.query !== '' ? (
|
||||
<div
|
||||
className={'h-full flex items-center cursor-pointer'}
|
||||
onClick={fetchResults}
|
||||
>
|
||||
<div className={'px-2 py-1 hover:bg-active-blue rounded mr-2'}>
|
||||
<SendOutlined />
|
||||
</div>
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
const QueryModal = observer(() => {
|
||||
const { aiFiltersStore } = useStore();
|
||||
|
||||
const onClose = () => {
|
||||
aiFiltersStore.setModalOpen(false);
|
||||
};
|
||||
return (
|
||||
<Modal
|
||||
open={aiFiltersStore.modalOpen}
|
||||
onCancel={onClose}
|
||||
width={900}
|
||||
destroyOnClose={true}
|
||||
footer={null}
|
||||
closeIcon={false}
|
||||
styles={{
|
||||
content: {
|
||||
backgroundColor: colors.gray[100],
|
||||
},
|
||||
}}
|
||||
centered={true}
|
||||
>
|
||||
<div className={'flex flex-col gap-2'}>
|
||||
<InputBox inModal />
|
||||
{aiFiltersStore.isLoading ? (
|
||||
<Loader />
|
||||
) : (
|
||||
<CreateCard onAdded={onClose} />
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
});
|
||||
|
||||
function Loader() {
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
'flex items-center justify-center flex-col font-semibold text-xl min-h-80'
|
||||
}
|
||||
>
|
||||
<div style={{ width: 150, height: 150 }}>
|
||||
<Lottie animationData={aiSpinner} loop={true} />
|
||||
</div>
|
||||
<div>AI is brewing your card, wait a few seconds...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default observer(AiQuery);
|
||||
|
|
@ -13,6 +13,7 @@ import withPageTitle from 'HOCs/withPageTitle';
|
|||
import withReport from 'App/components/hocs/withReport';
|
||||
import DashboardHeader from '../DashboardHeader';
|
||||
import {useHistory} from "react-router";
|
||||
import AiQuery from "./AiQuery";
|
||||
|
||||
interface IProps {
|
||||
siteId: string;
|
||||
|
|
@ -91,12 +92,16 @@ function DashboardView(props: Props) {
|
|||
|
||||
if (!dashboard) return null;
|
||||
|
||||
const originStr = window.env.ORIGIN || window.location.origin;
|
||||
const testingKey = localStorage.getItem('__mauricio_testing_access') === 'true';
|
||||
|
||||
const isSaas = testingKey && /app\.openreplay\.com/.test(originStr);
|
||||
return (
|
||||
<Loader loading={loading}>
|
||||
<div style={{maxWidth: '1360px', margin: 'auto'}}>
|
||||
{/* @ts-ignore */}
|
||||
<DashboardHeader renderReport={props.renderReport} siteId={siteId} dashboardId={dashboardId}/>
|
||||
|
||||
{isSaas ? <AiQuery /> : null}
|
||||
<DashboardWidgetGrid
|
||||
siteId={siteId}
|
||||
dashboardId={dashboardId}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import {
|
|||
TIMESERIES, TABLE, HEATMAP, FUNNEL, ERRORS, RESOURCE_MONITORING,
|
||||
PERFORMANCE, WEB_VITALS, INSIGHTS, USER_PATH, RETENTION
|
||||
} from 'App/constants/card';
|
||||
import {useParams} from 'react-router-dom';
|
||||
import {useHistory} from "react-router";
|
||||
|
||||
const tableOptions = metricOf.filter((i) => i.type === 'table');
|
||||
|
|
|
|||
|
|
@ -313,7 +313,7 @@ export const AskAiSwitchToggle = ({
|
|||
);
|
||||
};
|
||||
|
||||
const gradientBox = {
|
||||
export const gradientBox = {
|
||||
border: 'double 1.5px transparent',
|
||||
borderRadius: '100px',
|
||||
background:
|
||||
|
|
|
|||
|
|
@ -1,45 +1,72 @@
|
|||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import React from 'react';
|
||||
|
||||
import { Icon } from 'UI';
|
||||
|
||||
interface Props {
|
||||
wrapperClassName?: string;
|
||||
className?: string;
|
||||
icon?: string;
|
||||
leadingButton?: React.ReactNode;
|
||||
type?: string;
|
||||
rows?: number;
|
||||
height?: number;
|
||||
width?: number;
|
||||
[x: string]: any;
|
||||
wrapperClassName?: string;
|
||||
className?: string;
|
||||
icon?: string;
|
||||
leadingButton?: React.ReactNode;
|
||||
type?: string;
|
||||
rows?: number;
|
||||
height?: number;
|
||||
width?: number;
|
||||
[x: string]: any;
|
||||
}
|
||||
const Input = React.forwardRef((props: Props, ref: any) => {
|
||||
const { height = 36, width = 0, className = '', leadingButton = '', wrapperClassName = '', icon = '', type = 'text', rows = 4, ...rest } = props;
|
||||
return (
|
||||
<div className={cn({ relative: icon || leadingButton }, wrapperClassName)}>
|
||||
{icon && <Icon name={icon} className="absolute top-0 bottom-0 my-auto ml-4" size="14" />}
|
||||
{type === 'textarea' ? (
|
||||
<textarea
|
||||
ref={ref}
|
||||
rows={rows}
|
||||
style={{ resize: 'none' }}
|
||||
maxLength={500}
|
||||
className={cn('p-2 border border-gray-light bg-white w-full rounded-lg', className, { 'pl-10': icon })}
|
||||
{...rest}
|
||||
/>
|
||||
) : (
|
||||
<input
|
||||
ref={ref}
|
||||
type={type}
|
||||
style={{ height: `${height}px`, width: width? `${width}px` : '' }}
|
||||
className={cn('p-2 border border-gray-light bg-white w-full rounded-lg', className, { 'pl-10': icon })}
|
||||
{...rest}
|
||||
/>
|
||||
)}
|
||||
const {
|
||||
height = 36,
|
||||
width = 0,
|
||||
className = '',
|
||||
leadingButton = '',
|
||||
wrapperClassName = '',
|
||||
icon = '',
|
||||
type = 'text',
|
||||
rows = 4,
|
||||
...rest
|
||||
} = props;
|
||||
return (
|
||||
<div className={cn({ relative: icon || leadingButton }, wrapperClassName)}>
|
||||
{icon && (
|
||||
<Icon
|
||||
name={icon}
|
||||
className="absolute top-0 bottom-0 my-auto ml-4"
|
||||
size="14"
|
||||
/>
|
||||
)}
|
||||
{type === 'textarea' ? (
|
||||
<textarea
|
||||
ref={ref}
|
||||
rows={rows}
|
||||
style={{ resize: 'none' }}
|
||||
maxLength={500}
|
||||
className={cn(
|
||||
'p-2 border border-gray-light bg-white w-full rounded-lg',
|
||||
className,
|
||||
{ 'pl-10': icon }
|
||||
)}
|
||||
{...rest}
|
||||
/>
|
||||
) : (
|
||||
<input
|
||||
ref={ref}
|
||||
type={type}
|
||||
style={{ height: `${height}px`, width: width ? `${width}px` : '' }}
|
||||
className={cn(
|
||||
'p-2 border border-gray-light bg-white w-full rounded-lg',
|
||||
className,
|
||||
{ 'pl-10': icon }
|
||||
)}
|
||||
{...rest}
|
||||
/>
|
||||
)}
|
||||
|
||||
{leadingButton && <div className="absolute top-0 bottom-0 right-0">{leadingButton}</div>}
|
||||
</div>
|
||||
);
|
||||
{leadingButton && (
|
||||
<div className="absolute top-0 bottom-0 right-0">{leadingButton}</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default Input;
|
||||
|
|
|
|||
1
frontend/app/lottie/aiSpinner.json
Normal file
1
frontend/app/lottie/aiSpinner.json
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -10,11 +10,21 @@ export default class AiFiltersStore {
|
|||
cardFilters: Record<string, any> = { filters: [] };
|
||||
filtersSetKey = 0;
|
||||
isLoading: boolean = false;
|
||||
query: string = '';
|
||||
modalOpen: boolean = false;
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
|
||||
setQuery = (query: string): void => {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
setModalOpen = (isOpen: boolean): void => {
|
||||
this.modalOpen = isOpen;
|
||||
}
|
||||
|
||||
setFilters = (filters: Record<string, any>): void => {
|
||||
this.filters = filters;
|
||||
this.filtersSetKey += 1;
|
||||
|
|
@ -35,8 +45,12 @@ export default class AiFiltersStore {
|
|||
console.log(r)
|
||||
}
|
||||
|
||||
getCardFilters = async (query: string, chartType: string): Promise<any> => {
|
||||
this.isLoading = true;
|
||||
setLoading = (loading: boolean): void => {
|
||||
this.isLoading = loading;
|
||||
}
|
||||
|
||||
getCardFilters = async (query: string, chartType?: string): Promise<any> => {
|
||||
this.setLoading(true)
|
||||
try {
|
||||
const r = await aiService.getCardFilters(query, chartType);
|
||||
const filterObj = Filter({
|
||||
|
|
@ -57,6 +71,7 @@ export default class AiFiltersStore {
|
|||
? { ...filtersMap[matchingFilter], ...f }
|
||||
: { ...f, value: f.value ?? [] };
|
||||
return {
|
||||
type: filter.key,
|
||||
...filter,
|
||||
value: filter.value
|
||||
? filter.value.map((i: string) => parseInt(i, 10) * 60 * 1000)
|
||||
|
|
@ -76,7 +91,7 @@ export default class AiFiltersStore {
|
|||
} catch (e) {
|
||||
console.trace(e);
|
||||
} finally {
|
||||
this.isLoading = false;
|
||||
this.setLoading(false)
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ export default class FilterItem {
|
|||
}
|
||||
|
||||
fromData(data: any) {
|
||||
Object.assign(this, data)
|
||||
this.type = data.type
|
||||
this.key = data.key
|
||||
this.label = data.label
|
||||
|
|
@ -78,7 +79,7 @@ export default class FilterItem {
|
|||
this.isActive = Boolean(data.isActive)
|
||||
this.completed = data.completed
|
||||
this.dropped = data.dropped
|
||||
|
||||
this.options = data.options
|
||||
return this
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ export default class AiService extends BaseService {
|
|||
return data;
|
||||
}
|
||||
|
||||
async getCardFilters(query: string, chartType: string): Promise<Record<string, any>> {
|
||||
async getCardFilters(query: string, chartType?: string): Promise<Record<string, any>> {
|
||||
const r = await this.client.post('/intelligent/search-plus', {
|
||||
question: query,
|
||||
chartType
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
"jsbi": "^4.1.0",
|
||||
"jshint": "^2.11.1",
|
||||
"jspdf": "^2.5.1",
|
||||
"lottie-react": "^2.4.0",
|
||||
"lucide-react": "^0.396.0",
|
||||
"luxon": "^1.24.1",
|
||||
"microdiff": "^1.4.0",
|
||||
|
|
|
|||
|
|
@ -16655,6 +16655,25 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lottie-react@npm:^2.4.0":
|
||||
version: 2.4.0
|
||||
resolution: "lottie-react@npm:2.4.0"
|
||||
dependencies:
|
||||
lottie-web: ^5.10.2
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
checksum: 5c0ef3f1832b21232fe6826cc021cd90bb0e3c9d63f1047031ce77a0992092f8712b6f3a6aeeaa0f410d918ca557df160b1c776399f69b498c560273767befe0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lottie-web@npm:^5.10.2":
|
||||
version: 5.12.2
|
||||
resolution: "lottie-web@npm:5.12.2"
|
||||
checksum: 0aeaf631b10a76afd025df70c2a1486543530708e07a316946c08e55891dac483ffbaf2bf3648ae0b9c54c733118a0a086fd150aa76f7848606214c67ad72c30
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"loud-rejection@npm:^1.0.0":
|
||||
version: 1.6.0
|
||||
resolution: "loud-rejection@npm:1.6.0"
|
||||
|
|
@ -18704,6 +18723,7 @@ __metadata:
|
|||
jsbi: ^4.1.0
|
||||
jshint: ^2.11.1
|
||||
jspdf: ^2.5.1
|
||||
lottie-react: ^2.4.0
|
||||
lucide-react: ^0.396.0
|
||||
luxon: ^1.24.1
|
||||
microdiff: ^1.4.0
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue