Clear Overview

This commit is contained in:
Flatlogic Bot 2026-01-15 09:47:49 +00:00
parent b88bdc0896
commit 9ee76a0742
21 changed files with 8 additions and 1982 deletions

View File

@ -1,99 +0,0 @@
import React from 'react';
import BaseButton from '../BaseButton';
import BaseIcon from '../BaseIcon';
import * as icons from '@mdi/js';
import { useAppDispatch, useAppSelector } from '../../stores/hooks';
import { fetchWidgets, removeWidget } from '../../stores/roles/rolesSlice';
import { WidgetChartType, WidgetType } from "./models/widget.model";
import { BarChart } from "./components/BarChart";
import { PieChart } from "./components/PieChart";
import { AreaChart } from "./components/AreaChart";
import { LineChart } from "./components/LineChart";
export const SmartWidget = ({ widget, userId, admin, roleId }) => {
const dispatch = useAppDispatch();
const corners = useAppSelector((state) => state.style.corners);
const cardsStyle = useAppSelector((state) => state.style.cardsStyle);
const deleteWidget = async () => {
await dispatch(
removeWidget({ id: userId, widgetId: widget.widget_id, roleId }),
);
await dispatch(fetchWidgets(roleId));
};
return (
<div
className={`${
corners !== 'rounded-full' ? corners : 'rounded-3xl'
} dark:bg-dark-900 dark:border-dark-700 p-6 ${cardsStyle} ${
widget.widget_type === 'chart'
? 'col-span-2'
: 'lg:col-span-1 col-span-2'
}`}
>
<div className='flex justify-between flex-col h-full'>
<div className='flex justify-between items-center'>
<div className='text-lg leading-tight text-gray-500 dark:text-dark-600 line-clamp-2'>
{widget.label}
</div>
{admin && (
<BaseButton
icon={icons.mdiClose}
color='whiteDark'
roundedFull
onClick={deleteWidget}
/>
)}
</div>
<div className='flex justify-center items-center h-full'>
<div
className={`${
widget.widget_type === WidgetType.chart ? 'w-5/6 justify-center' : ''
} items-center flex flex-grow justify-center`}
>
{widget.value ? (
widget.widget_type === WidgetType.chart ? (
widget.chart_type === WidgetChartType.bar ? (
<BarChart widget={widget}/>
) : widget.chart_type === WidgetChartType.line ? (
<LineChart widget={widget}/>
) : widget.chart_type === WidgetChartType.pie ? (
<PieChart widget={widget}/>
) : widget.chart_type === WidgetChartType.area ? (
<AreaChart widget={widget}/>
) : widget.chart_type === WidgetChartType.funnel ? (
<AreaChart widget={widget}/>
) : null
) : (
<div className='text-3xl leading-tight font-semibold truncate'>
{widget.value}
</div>
)
) : (
<div className='text-center text-red-400'>
Something went wrong, please try again or use a different query.
</div>
)}
</div>
{widget.type === WidgetType.scalar && widget.mdiIcon && (
<div className='flex justify-end w-1/4'>
<BaseIcon
className='text-blue-500 flex-shrink-0'
w='w-16'
h='h-16'
size={48}
fill={widget.color}
path={icons[widget.mdiIcon]}
/>
</div>
)}
</div>
</div>
</div>
);
};

View File

@ -1,18 +0,0 @@
import React from 'react';
import { WidgetLibName } from '../models/widget.model';
import { ApexAreaChart } from './AreaChart/ApexAreaChart';
import { ChartJSAreaChart } from './AreaChart/ChartJSAreaChart';
export const AreaChart = ({ widget }) => {
return (
<>
{!widget.lib_name && <ChartJSAreaChart widget={widget} />}
{widget.lib_name === WidgetLibName.chartjs && (
<ChartJSAreaChart widget={widget} />
)}
{widget.lib_name === WidgetLibName.apex && (
<ApexAreaChart widget={widget} />
)}
</>
);
};

View File

@ -1,135 +0,0 @@
import React from 'react';
import dynamic from 'next/dynamic';
import { humanize } from '../../../../helpers/humanize';
const Chart = dynamic(() => import('react-apexcharts'), { ssr: false });
type ValueType = { [key: string]: string | number }[];
export const ApexAreaChart = ({ widget }) => {
const dataForLineChart = (value: any[]) => {
if (!value?.length || value?.length > 10000)
return [{ name: '', data: [] }];
const valueKey = Object.keys(value[0])[1];
const data = value.map((el) => +el[valueKey]);
return [{ name: humanize(valueKey), data }];
};
const optionsForLineChart = (
value: ValueType,
chartColor: string[],
currency: boolean,
) => {
const chartColors = Array.isArray(chartColor)
? chartColor
: [chartColor || '#3751FF'];
const defaultOptions = {
xaxis: {},
chart: {
toolbar: {
show: true,
offsetX: 0,
offsetY: 0,
tools: {
download: true,
selection: true,
zoom: true,
zoomin: true,
zoomout: true,
pan: true,
},
export: {
csv: {
filename: undefined,
columnDelimiter: ',',
headerCategory: 'category',
headerValue: 'value',
},
svg: {
filename: undefined,
},
png: {
filename: undefined,
},
},
},
},
plotOptions: {
bar: {
distributed: false,
},
},
colors: [],
};
if (!value?.length || value?.length > 10000) return defaultOptions;
const key = Object.keys(value[0])[0];
const categories = value
.map((el) => el[key])
.map((item) =>
typeof item === 'string' && item?.length > 7
? item?.slice(0, 7)
: item || '',
);
if (categories.length <= 3) {
defaultOptions.plotOptions = {
bar: {
distributed: true,
},
};
}
const colors = [];
for (let i = 0; i < categories.length; i++) {
colors.push(chartColors[i % chartColors.length]);
}
return {
...defaultOptions,
yaxis: {
labels: {
formatter: function (value) {
if (currency) {
return '$' + value;
} else {
return value;
}
},
},
},
dataLabels: {
formatter: (val) => {
if (currency) {
return '$' + val;
} else {
return val;
}
},
},
legend: {
show: false,
},
xaxis: {
categories,
},
colors,
};
};
return (
<Chart
options={optionsForLineChart(
widget.value,
widget.color_array,
widget.currency,
)}
series={dataForLineChart(widget.value)}
type={widget.chart_type}
height={200}
style={{ width: '100%' }}
/>
);
};

View File

@ -1,96 +0,0 @@
import React from 'react';
import { Line } from 'react-chartjs-2';
import chroma from 'chroma-js';
import { humanize } from '../../../../helpers/humanize';
import { collectOtherData, findFirstNumericKey } from '../../widgetHelpers';
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Filler,
Legend,
ChartData,
} from 'chart.js';
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Filler,
Legend,
);
export const ChartJSAreaChart = ({ widget }) => {
const options = {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
display: true,
},
x: {
display: true,
},
},
plugins: {
legend: {
display: true,
},
},
};
const dataForBarChart = (
value: any[],
chartColors: string[],
): ChartData<'line', number[], string> => {
if (!value?.length) return { labels: [''], datasets: [{ data: [] }] };
const initColors = Array.isArray(chartColors)
? chartColors
: [chartColors || '#3751FF'];
const valueKey = findFirstNumericKey(value[0]);
const label = humanize(valueKey);
const data = value.map((el) => +el[valueKey]);
const labels = value.map((el) =>
Object.keys(el).length <= 2
? humanize(String(el[Object.keys(el)[0]]))
: collectOtherData(el, valueKey),
);
const backgroundColor =
labels.length > initColors.length
? chroma
.scale([
chroma(initColors[0]).brighten(),
chroma(initColors.slice(-1)[0]).darken(),
])
.colors(labels.length)
: initColors;
return {
labels,
datasets: [
{
label,
data,
backgroundColor,
},
],
};
};
return (
<Line
data={dataForBarChart(widget.value, widget.color_array)}
options={options}
height={350}
/>
);
};

View File

@ -1,18 +0,0 @@
import React from 'react';
import { ChartJSBarChart } from './BarChart/ChartJSBarChart';
import { ApexBarChart } from './BarChart/ApexBarChart';
import { WidgetLibName } from '../models/widget.model';
export const BarChart = ({ widget }) => {
return (
<>
{!widget.lib_name && <ChartJSBarChart widget={widget} />}
{widget.lib_name === WidgetLibName.chartjs && (
<ChartJSBarChart widget={widget} />
)}
{widget.lib_name === WidgetLibName.apex && (
<ApexBarChart widget={widget} />
)}
</>
);
};

View File

@ -1,131 +0,0 @@
import React from 'react';
import dynamic from 'next/dynamic';
import { humanize } from '../../../../helpers/humanize';
const Chart = dynamic(() => import('react-apexcharts'), { ssr: false });
type ValueType = { [key: string]: string | number }[];
export const ApexBarChart = ({ widget }) => {
const dataForBarChart = (value: any[]) => {
if (!value?.length || value?.length > 10000)
return [{ name: '', data: [] }];
const valueKey = Object.keys(value[0])[1];
const data = value.map((el) => +el[valueKey]);
return [{ name: humanize(valueKey), data }];
};
const optionsForBarChart = (
value: ValueType,
chartColor: string[],
currency: boolean,
) => {
const chartColors = Array.isArray(chartColor)
? chartColor
: [chartColor || '#3751FF'];
const defaultOptions = {
xaxis: {},
chart: {
toolbar: {
show: true,
offsetX: 0,
offsetY: 0,
tools: {
download: true,
selection: true,
zoom: true,
},
export: {
csv: {
filename: undefined,
columnDelimiter: ',',
headerCategory: 'category',
headerValue: 'value',
},
svg: {
filename: undefined,
},
png: {
filename: undefined,
},
},
},
},
plotOptions: {
bar: {
distributed: false,
},
},
colors: [],
};
if (!value?.length || value?.length > 10000) return defaultOptions;
const key = Object.keys(value[0])[0];
const categories = value
.map((el) => el[key])
.map((item) =>
typeof item === 'string' && item?.length > 20
? item?.slice(0, 15)
: item || '',
);
if (categories.length <= 3) {
defaultOptions.plotOptions = {
bar: {
distributed: true,
},
};
}
const colors = [];
for (let i = 0; i < categories.length; i++) {
colors.push(chartColors[i % chartColors.length]);
}
return {
...defaultOptions,
yaxis: {
labels: {
formatter: function (value) {
if (currency) {
return '$' + value;
} else {
return value;
}
},
},
},
dataLabels: {
formatter: (val) => {
if (currency) {
return '$' + val;
} else {
return val;
}
},
},
legend: {
show: true,
},
xaxis: {
categories,
},
colors,
};
};
return (
<Chart
options={optionsForBarChart(
widget.value,
widget.color_array,
widget.currency,
)}
series={dataForBarChart(widget.value)}
type={widget.chart_type}
height={300}
style={{ width: '100%' }}
/>
);
};

View File

@ -1,92 +0,0 @@
import React from 'react';
import { humanize } from '../../../../helpers/humanize';
import { Bar } from 'react-chartjs-2';
import { collectOtherData, findFirstNumericKey } from '../../widgetHelpers';
import {
BarElement,
CategoryScale,
Chart as ChartJS,
ChartData,
Legend,
LinearScale,
Title,
Tooltip,
} from 'chart.js';
import chroma from 'chroma-js';
ChartJS.register(
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend,
);
export const ChartJSBarChart = ({ widget }) => {
console.log(widget)
const options = () => {
return {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'top' as const,
},
title: {
display: true,
text: widget.label,
},
},
};
};
const dataForBarChart = (
value: any[],
chartColors: string[],
): ChartData<'bar', number[], string> => {
if (!value?.length) return { labels: [''], datasets: [{ data: [] }] };
const initColors = Array.isArray(chartColors)
? chartColors
: [chartColors || '#3751FF'];
const valueKey = findFirstNumericKey(value[0]);
const label = humanize(valueKey);
const data = value.map((el) => +el[valueKey]);
const labels = value.map((el) =>
Object.keys(el).length <= 2
? humanize(String(el[Object.keys(el)[0]]))
: collectOtherData(el, valueKey),
);
const backgroundColor =
labels.length > initColors.length
? chroma
.scale([
chroma(initColors[0]).brighten(),
chroma(initColors.slice(-1)[0]).darken(),
])
.colors(labels.length)
: initColors;
return {
labels,
datasets: [
{
label,
data,
backgroundColor,
},
],
};
};
return (
<Bar
data={dataForBarChart(widget.value, widget.color_array)}
options={options()}
height={350}
/>
);
};

View File

@ -1,134 +0,0 @@
import React from 'react';
import dynamic from 'next/dynamic';
import { humanize } from '../../../helpers/humanize';
const Chart = dynamic(() => import('react-apexcharts'), { ssr: false });
type ValueType = { [key: string]: string | number }[];
export const FunnelChart = ({ widget }) => {
const dataForBarChart = (value: any[]) => {
if (!value?.length || value?.length > 10000)
return [{ name: '', data: [] }];
const valueKey = Object.keys(value[0])[1];
const data = value.map((el) => +el[valueKey]);
return [{ name: humanize(valueKey), data }];
};
const optionsForBarChart = (
value: ValueType,
chartColor: string[],
currency: boolean,
) => {
const chartColors = Array.isArray(chartColor)
? chartColor
: [chartColor || '#3751FF'];
const defaultOptions = {
xaxis: {},
chart: {
toolbar: {
show: true,
offsetX: 0,
offsetY: 0,
tools: {
download: true,
selection: true,
zoom: true,
},
export: {
csv: {
filename: undefined,
columnDelimiter: ',',
headerCategory: 'category',
headerValue: 'value',
},
svg: {
filename: undefined,
},
png: {
filename: undefined,
},
},
},
},
plotOptions: {
bar: {
distributed: false,
horizontal: true,
isFunnel: true,
},
},
colors: [],
};
if (!value?.length || value?.length > 10000) return defaultOptions;
const key = Object.keys(value[0])[0];
const categories = value
.map((el) => el[key])
.map((item) =>
typeof item === 'string' && item?.length > 20
? item?.slice(0, 15)
: item || '',
);
if (categories.length <= 3) {
defaultOptions.plotOptions = {
bar: {
distributed: true,
horizontal: true,
isFunnel: true,
},
};
}
const colors = [];
for (let i = 0; i < categories.length; i++) {
colors.push(chartColors[i % chartColors.length]);
}
return {
...defaultOptions,
yaxis: {
labels: {
formatter: function (value) {
if (currency) {
return '$' + value;
} else {
return value;
}
},
},
},
dataLabels: {
formatter: (val) => {
if (currency) {
return '$' + val;
} else {
return val;
}
},
},
legend: {
show: true,
},
xaxis: {
categories,
},
colors,
};
};
return (
<Chart
options={optionsForBarChart(
widget.value,
widget.color_array,
widget.currency,
)}
series={dataForBarChart(widget.value)}
type={widget.chart_type}
height={300}
style={{ width: '100%' }}
/>
);
};

View File

@ -1,18 +0,0 @@
import React from 'react';
import { WidgetLibName } from '../models/widget.model';
import { ApexLineChart } from './LineChart/ApexLineChart';
import { ChartJSLineChart } from './LineChart/ChartJSLineChart';
export const LineChart = ({ widget }) => {
return (
<>
{!widget.lib_name && <ChartJSLineChart widget={widget} />}
{widget.lib_name === WidgetLibName.chartjs && (
<ChartJSLineChart widget={widget} />
)}
{widget.lib_name === WidgetLibName.apex && (
<ApexLineChart widget={widget} />
)}
</>
);
};

View File

@ -1,135 +0,0 @@
import React from 'react';
import dynamic from 'next/dynamic';
import { humanize } from '../../../../helpers/humanize';
const Chart = dynamic(() => import('react-apexcharts'), { ssr: false });
type ValueType = { [key: string]: string | number }[];
export const ApexLineChart = ({ widget }) => {
const dataForLineChart = (value: any[]) => {
if (!value?.length || value?.length > 10000)
return [{ name: '', data: [] }];
const valueKey = Object.keys(value[0])[1];
const data = value.map((el) => +el[valueKey]);
return [{ name: humanize(valueKey), data }];
};
const optionsForLineChart = (
value: ValueType,
chartColor: string[],
currency: boolean,
) => {
const chartColors = Array.isArray(chartColor)
? chartColor
: [chartColor || '#3751FF'];
const defaultOptions = {
xaxis: {},
chart: {
toolbar: {
show: true,
offsetX: 0,
offsetY: 0,
tools: {
download: true,
selection: true,
zoom: true,
zoomin: true,
zoomout: true,
pan: true,
},
export: {
csv: {
filename: undefined,
columnDelimiter: ',',
headerCategory: 'category',
headerValue: 'value',
},
svg: {
filename: undefined,
},
png: {
filename: undefined,
},
},
},
},
plotOptions: {
bar: {
distributed: false,
},
},
colors: [],
};
if (!value?.length || value?.length > 10000) return defaultOptions;
const key = Object.keys(value[0])[0];
const categories = value
.map((el) => el[key])
.map((item) =>
typeof item === 'string' && item?.length > 7
? item?.slice(0, 7)
: item || '',
);
if (categories.length <= 3) {
defaultOptions.plotOptions = {
bar: {
distributed: true,
},
};
}
const colors = [];
for (let i = 0; i < categories.length; i++) {
colors.push(chartColors[i % chartColors.length]);
}
return {
...defaultOptions,
yaxis: {
labels: {
formatter: function (value) {
if (currency) {
return '$' + value;
} else {
return value;
}
},
},
},
dataLabels: {
formatter: (val) => {
if (currency) {
return '$' + val;
} else {
return val;
}
},
},
legend: {
show: false,
},
xaxis: {
categories,
},
colors,
};
};
return (
<Chart
options={optionsForLineChart(
widget.value,
widget.color_array,
widget.currency,
)}
series={dataForLineChart(widget.value)}
type={widget.chart_type}
height={200}
style={{ width: '100%' }}
/>
);
};

View File

@ -1,97 +0,0 @@
import React from 'react';
import { humanize } from '../../../../helpers/humanize';
import { Line } from 'react-chartjs-2';
import chroma from 'chroma-js';
import { collectOtherData, findFirstNumericKey } from '../../widgetHelpers';
import { Widget } from '../../models/widget.model';
import {
Chart,
LineElement,
PointElement,
LineController,
LinearScale,
CategoryScale,
Tooltip,
ChartData,
} from 'chart.js';
Chart.register(
LineElement,
PointElement,
LineController,
LinearScale,
CategoryScale,
Tooltip,
);
interface Props {
widget: Widget;
}
export const ChartJSLineChart = (props: Props) => {
const options = {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
display: true,
},
x: {
display: true,
},
},
plugins: {
legend: {
display: true,
},
},
};
const dataForBarChart = (
value: any[],
chartColors: string[],
): ChartData<'line', number[], string> => {
if (!value?.length) return { labels: [''], datasets: [{ data: [] }] };
const initColors = Array.isArray(chartColors)
? chartColors
: [chartColors || '#3751FF'];
const valueKey = findFirstNumericKey(value[0]);
const label = humanize(valueKey);
const data = value.map((el) => +el[valueKey]);
const labels = value.map((el) =>
Object.keys(el).length <= 2
? humanize(String(el[Object.keys(el)[0]]))
: collectOtherData(el, valueKey),
);
const backgroundColor =
labels.length > initColors.length
? chroma
.scale([
chroma(initColors[0]).brighten(),
chroma(initColors.slice(-1)[0]).darken(),
])
.colors(labels.length)
: initColors;
return {
labels,
datasets: [
{
label,
data,
backgroundColor,
},
],
};
};
return (
<Line
data={dataForBarChart(props.widget.value, props.widget.color_array)}
options={options}
height={350}
/>
);
};

View File

@ -1,18 +0,0 @@
import React from 'react';
import { WidgetLibName } from '../models/widget.model';
import { ApexPieChart } from './PieChart/ApexPieChart';
import { ChartJSPieChart } from './PieChart/ChartJSPieChart';
export const PieChart = ({ widget }) => {
return (
<>
{!widget.lib_name && <ChartJSPieChart widget={widget} />}
{widget.lib_name === WidgetLibName.chartjs && (
<ChartJSPieChart widget={widget} />
)}
{widget.lib_name === WidgetLibName.apex && (
<ApexPieChart widget={widget} />
)}
</>
);
};

View File

@ -1,106 +0,0 @@
import React from 'react';
import dynamic from 'next/dynamic';
import chroma from 'chroma-js';
const Chart = dynamic(() => import('react-apexcharts'), { ssr: false });
type ValueType = { [key: string]: string | number }[];
export const ApexPieChart = ({ widget }) => {
const optionsForPieChart = (value: ValueType, chartColor: string) => {
const chartColors = Array.isArray(chartColor)
? chartColor
: [chartColor || '#3751FF'];
const defaultOptions = {
xaxis: {},
toolbar: {
show: true,
offsetX: 0,
offsetY: 0,
tools: {
download: true,
selection: true,
zoom: true,
zoomin: true,
zoomout: true,
pan: true,
customIcons: [],
},
export: {
csv: {
filename: undefined,
columnDelimiter: ',',
headerCategory: 'category',
headerValue: 'value',
},
svg: {
filename: undefined,
},
png: {
filename: undefined,
},
},
autoSelected: 'zoom',
},
colors: [],
};
if (!value?.length || value?.length > 10000) return defaultOptions;
if (
!isNaN(Number(value[0][Object.keys(value[0])[1]])) &&
isFinite(Number(value[0][Object.keys(value[0])[1]]))
) {
const labels = value
.map((el) => String(el[Object.keys(value[0])[0]]))
.reverse();
let colors: string[] | (string & any[]);
if (labels.length > chartColors.length) {
colors = chroma
.scale([
chroma(chartColors.at(0)).brighten(),
chroma(chartColors.at(-1)).darken(),
])
.colors(labels.length);
} else {
colors = chartColors;
}
return {
...defaultOptions,
colors,
labels,
};
}
const key = Object.keys(value[0])[1];
const categories = value.map((el) => String(el[key])).reverse();
return {
...defaultOptions,
labels: categories,
};
};
const dataForPieChart = (value: any[]) => {
if (!value?.length || value?.length > 10000)
return [{ name: '', data: [] }];
if (
!isNaN(parseFloat(value[0][Object.keys(value[0])[1]])) &&
isFinite(value[0][Object.keys(value[0])[1]])
) {
return value.map((el) => +el[Object.keys(value[0])[1]]).reverse();
}
const valueKey = Object.keys(value[0])[0];
return value.map((el) => +el[valueKey]).reverse();
};
return (
<Chart
options={optionsForPieChart(widget.value, widget.color_array)}
series={dataForPieChart(widget.value)}
type={widget.chart_type}
height={200}
style={{ width: '100%' }}
/>
);
};

View File

@ -1,81 +0,0 @@
import React from 'react';
import { humanize } from '../../../../helpers/humanize';
import { Pie } from 'react-chartjs-2';
import chroma from 'chroma-js';
import { collectOtherData, findFirstNumericKey } from '../../widgetHelpers';
import {
Chart as ChartJS,
ArcElement,
Tooltip,
Legend,
ChartData,
} from 'chart.js';
ChartJS.register(ArcElement, Tooltip, Legend);
export const ChartJSPieChart = ({ widget }) => {
const options = () => {
return {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right' as const,
},
title: {
display: true,
text: widget.label,
},
},
};
};
const dataForBarChart = (
value: any[],
chartColors: string[],
): ChartData<'pie', number[], string> => {
if (!value?.length) return { labels: [''], datasets: [{ data: [] }] };
const initColors = Array.isArray(chartColors)
? chartColors
: [chartColors || '#3751FF'];
const valueKey = findFirstNumericKey(value[0]);
const label = humanize(valueKey);
const data = value.map((el) => +el[valueKey]);
const labels = value.map((el) =>
Object.keys(el).length <= 2
? humanize(String(el[Object.keys(el)[0]]))
: collectOtherData(el, valueKey),
);
const backgroundColor =
labels.length > initColors.length
? chroma
.scale([
chroma(initColors[0]).brighten(),
chroma(initColors.slice(-1)[0]).darken(),
])
.colors(labels.length)
: initColors;
return {
labels,
datasets: [
{
label,
data,
backgroundColor,
},
],
};
};
return (
<Pie
data={dataForBarChart(widget.value, widget.color_array)}
options={options()}
height={350}
/>
);
};

View File

@ -1,35 +0,0 @@
export enum WidgetLibName {
apex = 'apex',
chartjs = 'chartjs',
}
export enum WidgetChartType {
scalar = 'scalar',
bar = 'bar',
line = 'line',
pie = 'pie',
area = 'area',
funnel = 'funnel',
}
export enum WidgetType {
chart = 'chart',
scalar = 'scalar',
}
export interface Widget {
type: WidgetType;
chartType: WidgetChartType;
query: string;
mdiIcon: string;
iconColor: string;
label: string;
id: string;
lib?: WidgetLibName;
value: any[];
chartColors: string[];
options?: any;
prompt: string;
color: string;
color_array: string[];
}

View File

@ -1,36 +0,0 @@
import { humanize } from '../../helpers/humanize';
interface DataObject {
[key: string]: any;
}
export const findFirstNumericKey = (obj: Record<string, any>): string | undefined => {
for (const [key, value] of Object.entries(obj)) {
if (typeof value === 'string') {
const trimmedValue = value.trim();
// Only allow numbers, and optionally a single decimal point
const isNumeric = /^-?\d+(\.\d+)?$/.test(trimmedValue);
if (isNumeric) {
// Check if the number is the same as the trimmed value
// This is to avoid cases like '1.0' being treated as a number
const numberValue = parseFloat(trimmedValue);
if (numberValue.toString() === trimmedValue) {
return key;
}
}
}
}
return undefined;
};
export const collectOtherData = (
obj: DataObject,
excludeKey: string,
): string => {
return Object.entries(obj)
.filter(([key, _]) => key !== excludeKey)
.map(([_, value]) => humanize(value))
.join(' / ');
};

View File

@ -1,53 +0,0 @@
import React, { useEffect, useId, useState } from 'react';
import { AsyncPaginate } from 'react-select-async-paginate';
import axios from 'axios';
export const RoleSelect = ({ options, field, form, itemRef, disabled, currentUser }) => {
const [value, setValue] = useState(null);
const PAGE_SIZE = 100;
React.useEffect(() => {
if (currentUser.app_role.id) {
setValue({ value: currentUser.app_role.id, label: currentUser.app_role.name });
}
}, [currentUser]);
useEffect(() => {
if (options?.value && options?.label) {
setValue({ value: options.value, label: options.label });
}
}, [options?.id, field?.value?.id]);
const mapResponseToValuesAndLabels = (data) => ({
value: data.id,
label: data.label,
});
const handleChange = (option) => {
form.setFieldValue(field.name, option);
setValue(option);
};
async function callApi(inputValue: string, loadedOptions: any[]) {
const path = `/${itemRef}/autocomplete?limit=${PAGE_SIZE}&offset=${loadedOptions.length}${inputValue ? `&query=${inputValue}` : ''}`;
const { data } = await axios(path);
return {
options: data.map(mapResponseToValuesAndLabels),
hasMore: data.length === PAGE_SIZE,
}
}
return (
<AsyncPaginate
classNames={{
control: () => 'px-1 py-2',
}}
classNamePrefix={'react-select'}
instanceId={useId()}
value={value}
debounceTimeout={1000}
loadOptions={callApi}
onChange={handleChange}
defaultOptions
isDisabled={disabled}
/>
);
};

View File

@ -1,140 +0,0 @@
import CardBox from '../CardBox';
import { mdiCog } from '@mdi/js';
import { Field, Form, Formik } from 'formik';
import { ToastContainer, toast } from 'react-toastify';
import FormField from '../FormField';
import React from 'react';
import {
aiPrompt,
setErrorNotification,
resetNotify,
} from '../../stores/openAiSlice';
import { useAppDispatch, useAppSelector } from '../../stores/hooks';
import { fetchWidgets } from '../../stores/roles/rolesSlice';
import BaseButton from '../BaseButton';
import CardBoxModal from '../CardBoxModal';
import { RoleSelect } from './RoleSelect';
export const WidgetCreator = ({
currentUser,
isFetchingQuery,
setWidgetsRole,
widgetsRole,
}) => {
const dispatch = useAppDispatch();
const [isModalOpen, setIsModalOpen] = React.useState(false);
const { notify: openAiNotify } = useAppSelector((state) => state.openAi);
const notify = (type, msg) => toast(msg, { type, position: 'bottom-center' });
React.useEffect(() => {
if (openAiNotify.showNotification) {
notify(openAiNotify.typeNotification, openAiNotify.textNotification);
dispatch(resetNotify());
}
}, [openAiNotify.showNotification]);
const openModal = (): void => {
setIsModalOpen(true);
};
const handleCloseModal = (value = {}) => {
setWidgetsRole(value);
setIsModalOpen(false);
};
const getWidgets = async () => {
await dispatch(fetchWidgets(widgetsRole?.role?.value || ''));
};
const smartSearch = async (values: { description: string }, resetForm: any) => {
const description = values.description;
const projectId = '';
const payload = {
roleId: widgetsRole?.role?.value,
description,
projectId,
userId: currentUser?.id,
};
const { payload: responcePayload, error }: any = await dispatch(aiPrompt(payload));
await getWidgets().then();
resetForm({ values: { description: '' } });
if (responcePayload.data?.error || error) {
const errorMessage =
responcePayload.data?.error?.message || error?.message;
await dispatch(
setErrorNotification(errorMessage || 'Error with widget creation'),
);
}
};
return (
<>
<CardBox id="widgetCreator" className='mb-6 relative'>
<BaseButton
className='absolute top-0 right-0 m-4'
icon={mdiCog}
color='whiteDark'
roundedFull
onClick={openModal}
/>
<Formik
initialValues={{
description: '',
}}
onSubmit={(values, { resetForm }) => smartSearch(values, resetForm)}
>
<Form>
<FormField
label='Create Chart or Widget'
help={
isFetchingQuery ?
'Loading...' :
'Describe your new widget or chart in natural language. For example: "Number of admin users" OR "red chart with number of closed contracts grouped by month"'
}
>
<Field type='input' name='description' disabled={isFetchingQuery} />
</FormField>
</Form>
</Formik>
</CardBox>
<Formik
initialValues={{
role: '',
}}
onSubmit={(values) => handleCloseModal(values)}
>
{({ submitForm }) => (
<CardBoxModal
title='Widget Creator Settings'
buttonColor='info'
buttonLabel='Done'
isActive={isModalOpen}
onConfirm={submitForm}
onCancel={() => setIsModalOpen(false)}
>
<p>What role are we showing and creating widgets for?</p>
<Form>
<FormField>
<Field
name='role'
id='role'
component={RoleSelect}
options={widgetsRole?.role || []}
itemRef={'roles'}
currentUser={currentUser}
></Field>
</FormField>
</Form>
</CardBoxModal>
)}
</Formik>
<ToastContainer />
</>
);
};

View File

@ -1,94 +1,30 @@
import * as icon from '@mdi/js';
import Head from 'next/head'
import React from 'react'
import axios from 'axios';
import type { ReactElement } from 'react'
import LayoutAuthenticated from '../layouts/Authenticated'
import SectionMain from '../components/SectionMain'
import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton'
import BaseIcon from "../components/BaseIcon";
import { getPageTitle } from '../config'
import Link from "next/link";
import { useAppSelector, useAppDispatch } from '../stores/hooks';
import { hasPermission } from "../helpers/userPermissions";
import { fetchWidgets } from '../stores/roles/rolesSlice';
import { WidgetCreator } from '../components/WidgetCreator/WidgetCreator';
import { SmartWidget } from '../components/SmartWidget/SmartWidget';
import { useAppDispatch, useAppSelector } from '../stores/hooks';
const Dashboard = () => {
const dispatch = useAppDispatch();
const iconsColor = useAppSelector((state) => state.style.iconsColor);
const corners = useAppSelector((state) => state.style.corners);
const cardsStyle = useAppSelector((state) => state.style.cardsStyle);
const loadingMessage = 'Loading...';
const [users, setUsers] = React.useState(loadingMessage);
const [roles, setRoles] = React.useState(loadingMessage);
const [permissions, setPermissions] = React.useState(loadingMessage);
const [staff_roles, setStaff_roles] = React.useState(loadingMessage);
const [customers, setCustomers] = React.useState(loadingMessage);
const [tables, setTables] = React.useState(loadingMessage);
const [reservations, setReservations] = React.useState(loadingMessage);
const [tabs, setTabs] = React.useState(loadingMessage);
const [line_items, setLine_items] = React.useState(loadingMessage);
const [menu_items, setMenu_items] = React.useState(loadingMessage);
const [inventory_items, setInventory_items] = React.useState(loadingMessage);
const [suppliers, setSuppliers] = React.useState(loadingMessage);
const [payments, setPayments] = React.useState(loadingMessage);
const [promotions, setPromotions] = React.useState(loadingMessage);
const [widgetsRole, setWidgetsRole] = React.useState({
role: { value: '', label: '' },
});
const { currentUser } = useAppSelector((state) => state.auth);
const { isFetchingQuery } = useAppSelector((state) => state.openAi);
const { rolesWidgets, loading } = useAppSelector((state) => state.roles);
async function loadData() {
const entities = ['users','roles','permissions','staff_roles','customers','tables','reservations','tabs','line_items','menu_items','inventory_items','suppliers','payments','promotions',];
const fns = [setUsers,setRoles,setPermissions,setStaff_roles,setCustomers,setTables,setReservations,setTabs,setLine_items,setMenu_items,setInventory_items,setSuppliers,setPayments,setPromotions,];
const requests = entities.map((entity, index) => {
if(hasPermission(currentUser, `READ_${entity.toUpperCase()}`)) {
return axios.get(`/${entity.toLowerCase()}/count`);
} else {
fns[index](null);
return Promise.resolve({data: {count: null}});
}
});
Promise.allSettled(requests).then((results) => {
results.forEach((result, i) => {
if (result.status === 'fulfilled') {
fns[i](result.value.data.count);
} else {
fns[i](result.reason.message);
}
});
});
}
async function getWidgets(roleId) {
await dispatch(fetchWidgets(roleId));
}
React.useEffect(() => {
if (!currentUser) return;
loadData().then();
setWidgetsRole({ role: { value: currentUser?.app_role?.id, label: currentUser?.app_role?.name } });
}, [currentUser]);
React.useEffect(() => {
if (!currentUser || !widgetsRole?.role?.value) return;
getWidgets(widgetsRole?.role?.value || '').then();
}, [widgetsRole?.role?.value]);
return (
<>
@ -104,445 +40,6 @@ const Dashboard = () => {
main>
{''}
</SectionTitleLineWithButton>
{hasPermission(currentUser, 'CREATE_ROLES') && <WidgetCreator
currentUser={currentUser}
isFetchingQuery={isFetchingQuery}
setWidgetsRole={setWidgetsRole}
widgetsRole={widgetsRole}
/>}
{!!rolesWidgets.length &&
hasPermission(currentUser, 'CREATE_ROLES') && (
<p className=' text-gray-500 dark:text-gray-400 mb-4'>
{`${widgetsRole?.role?.label || 'Users'}'s widgets`}
</p>
)}
<div className='grid grid-cols-1 gap-6 lg:grid-cols-4 mb-6 grid-flow-dense'>
{(isFetchingQuery || loading) && (
<div className={` ${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 text-lg leading-tight text-gray-500 flex items-center ${cardsStyle} dark:border-dark-700 p-6`}>
<BaseIcon
className={`${iconsColor} animate-spin mr-5`}
w='w-16'
h='h-16'
size={48}
path={icon.mdiLoading}
/>{' '}
Loading widgets...
</div>
)}
{ rolesWidgets &&
rolesWidgets.map((widget) => (
<SmartWidget
key={widget.id}
userId={currentUser?.id}
widget={widget}
roleId={widgetsRole?.role?.value || ''}
admin={hasPermission(currentUser, 'CREATE_ROLES')}
/>
))}
</div>
{!!rolesWidgets.length && <hr className='my-6 text-midnightBlueTheme-mainBG ' />}
<div id="dashboard" className='grid grid-cols-1 gap-6 lg:grid-cols-3 mb-6'>
{hasPermission(currentUser, 'READ_USERS') && <Link href={'/users/users-list'}>
<div
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
>
<div className="flex justify-between align-center">
<div>
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
Users
</div>
<div className="text-3xl leading-tight font-semibold">
{users}
</div>
</div>
<div>
<BaseIcon
className={`${iconsColor}`}
w="w-16"
h="h-16"
size={48}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
path={icon.mdiAccountGroup || icon.mdiTable}
/>
</div>
</div>
</div>
</Link>}
{hasPermission(currentUser, 'READ_ROLES') && <Link href={'/roles/roles-list'}>
<div
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
>
<div className="flex justify-between align-center">
<div>
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
Roles
</div>
<div className="text-3xl leading-tight font-semibold">
{roles}
</div>
</div>
<div>
<BaseIcon
className={`${iconsColor}`}
w="w-16"
h="h-16"
size={48}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
path={icon.mdiShieldAccountVariantOutline || icon.mdiTable}
/>
</div>
</div>
</div>
</Link>}
{hasPermission(currentUser, 'READ_PERMISSIONS') && <Link href={'/permissions/permissions-list'}>
<div
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
>
<div className="flex justify-between align-center">
<div>
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
Permissions
</div>
<div className="text-3xl leading-tight font-semibold">
{permissions}
</div>
</div>
<div>
<BaseIcon
className={`${iconsColor}`}
w="w-16"
h="h-16"
size={48}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
path={icon.mdiShieldAccountOutline || icon.mdiTable}
/>
</div>
</div>
</div>
</Link>}
{hasPermission(currentUser, 'READ_STAFF_ROLES') && <Link href={'/staff_roles/staff_roles-list'}>
<div
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
>
<div className="flex justify-between align-center">
<div>
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
Staff roles
</div>
<div className="text-3xl leading-tight font-semibold">
{staff_roles}
</div>
</div>
<div>
<BaseIcon
className={`${iconsColor}`}
w="w-16"
h="h-16"
size={48}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
path={'mdiShieldAccount' in icon ? icon['mdiShieldAccount' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
/>
</div>
</div>
</div>
</Link>}
{hasPermission(currentUser, 'READ_CUSTOMERS') && <Link href={'/customers/customers-list'}>
<div
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
>
<div className="flex justify-between align-center">
<div>
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
Customers
</div>
<div className="text-3xl leading-tight font-semibold">
{customers}
</div>
</div>
<div>
<BaseIcon
className={`${iconsColor}`}
w="w-16"
h="h-16"
size={48}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
path={'mdiAccount' in icon ? icon['mdiAccount' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
/>
</div>
</div>
</div>
</Link>}
{hasPermission(currentUser, 'READ_TABLES') && <Link href={'/tables/tables-list'}>
<div
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
>
<div className="flex justify-between align-center">
<div>
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
Tables
</div>
<div className="text-3xl leading-tight font-semibold">
{tables}
</div>
</div>
<div>
<BaseIcon
className={`${iconsColor}`}
w="w-16"
h="h-16"
size={48}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
path={'mdiSeat' in icon ? icon['mdiSeat' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
/>
</div>
</div>
</div>
</Link>}
{hasPermission(currentUser, 'READ_RESERVATIONS') && <Link href={'/reservations/reservations-list'}>
<div
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
>
<div className="flex justify-between align-center">
<div>
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
Reservations
</div>
<div className="text-3xl leading-tight font-semibold">
{reservations}
</div>
</div>
<div>
<BaseIcon
className={`${iconsColor}`}
w="w-16"
h="h-16"
size={48}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
path={'mdiCalendar' in icon ? icon['mdiCalendar' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
/>
</div>
</div>
</div>
</Link>}
{hasPermission(currentUser, 'READ_TABS') && <Link href={'/tabs/tabs-list'}>
<div
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
>
<div className="flex justify-between align-center">
<div>
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
Tabs
</div>
<div className="text-3xl leading-tight font-semibold">
{tabs}
</div>
</div>
<div>
<BaseIcon
className={`${iconsColor}`}
w="w-16"
h="h-16"
size={48}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
path={'mdiWallet' in icon ? icon['mdiWallet' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
/>
</div>
</div>
</div>
</Link>}
{hasPermission(currentUser, 'READ_LINE_ITEMS') && <Link href={'/line_items/line_items-list'}>
<div
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
>
<div className="flex justify-between align-center">
<div>
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
Line items
</div>
<div className="text-3xl leading-tight font-semibold">
{line_items}
</div>
</div>
<div>
<BaseIcon
className={`${iconsColor}`}
w="w-16"
h="h-16"
size={48}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
path={'mdiFormatListBulleted' in icon ? icon['mdiFormatListBulleted' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
/>
</div>
</div>
</div>
</Link>}
{hasPermission(currentUser, 'READ_MENU_ITEMS') && <Link href={'/menu_items/menu_items-list'}>
<div
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
>
<div className="flex justify-between align-center">
<div>
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
Menu items
</div>
<div className="text-3xl leading-tight font-semibold">
{menu_items}
</div>
</div>
<div>
<BaseIcon
className={`${iconsColor}`}
w="w-16"
h="h-16"
size={48}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
path={'mdiFood' in icon ? icon['mdiFood' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
/>
</div>
</div>
</div>
</Link>}
{hasPermission(currentUser, 'READ_INVENTORY_ITEMS') && <Link href={'/inventory_items/inventory_items-list'}>
<div
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
>
<div className="flex justify-between align-center">
<div>
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
Inventory items
</div>
<div className="text-3xl leading-tight font-semibold">
{inventory_items}
</div>
</div>
<div>
<BaseIcon
className={`${iconsColor}`}
w="w-16"
h="h-16"
size={48}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
path={'mdiWarehouse' in icon ? icon['mdiWarehouse' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
/>
</div>
</div>
</div>
</Link>}
{hasPermission(currentUser, 'READ_SUPPLIERS') && <Link href={'/suppliers/suppliers-list'}>
<div
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
>
<div className="flex justify-between align-center">
<div>
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
Suppliers
</div>
<div className="text-3xl leading-tight font-semibold">
{suppliers}
</div>
</div>
<div>
<BaseIcon
className={`${iconsColor}`}
w="w-16"
h="h-16"
size={48}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
path={'mdiTruck' in icon ? icon['mdiTruck' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
/>
</div>
</div>
</div>
</Link>}
{hasPermission(currentUser, 'READ_PAYMENTS') && <Link href={'/payments/payments-list'}>
<div
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
>
<div className="flex justify-between align-center">
<div>
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
Payments
</div>
<div className="text-3xl leading-tight font-semibold">
{payments}
</div>
</div>
<div>
<BaseIcon
className={`${iconsColor}`}
w="w-16"
h="h-16"
size={48}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
path={'mdiCreditCard' in icon ? icon['mdiCreditCard' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
/>
</div>
</div>
</div>
</Link>}
{hasPermission(currentUser, 'READ_PROMOTIONS') && <Link href={'/promotions/promotions-list'}>
<div
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
>
<div className="flex justify-between align-center">
<div>
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
Promotions
</div>
<div className="text-3xl leading-tight font-semibold">
{promotions}
</div>
</div>
<div>
<BaseIcon
className={`${iconsColor}`}
w="w-16"
h="h-16"
size={48}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
path={'mdiTag' in icon ? icon['mdiTag' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
/>
</div>
</div>
</div>
</Link>}
</div>
</SectionMain>
</>
)

View File

@ -4,7 +4,7 @@ import axios from 'axios';
interface MainState {
isFetchingQuery: boolean;
errorMessage: string;
smartWidgets: any[];
gptResponse: string | null;
aiResponse: any | null;
isAskingQuestion: boolean;
@ -18,7 +18,7 @@ interface MainState {
const initialState: MainState = {
isFetchingQuery: false,
errorMessage: '',
smartWidgets: [],
gptResponse: null,
aiResponse: null,
isAskingQuestion: false,
@ -36,20 +36,6 @@ const fulfilledNotify = (state, msg, type?: string) => {
state.notify.showNotification = true;
};
export const aiPrompt = createAsyncThunk(
'openai/aiPrompt',
async (data: any, { rejectWithValue }) => {
try {
return await axios.post('/openai/create_widget', data);
} catch (error) {
if (!error.response) {
throw error;
}
return rejectWithValue(error.response.data);
}
},
);
export const askGpt = createAsyncThunk(
'openai/askGpt',
async (prompt: string, { rejectWithValue }) => {
@ -94,21 +80,6 @@ export const openAiSlice = createSlice({
},
},
extraReducers: (builder) => {
builder.addCase(aiPrompt.pending, (state) => {
state.isFetchingQuery = true;
});
builder.addCase(aiPrompt.fulfilled, (state, action: Record<any, any>) => {
state.isFetchingQuery = false;
state.errorMessage = '';
state.smartWidgets.unshift(action.payload.data);
});
builder.addCase(aiPrompt.rejected, (state) => {
state.errorMessage = 'Something was wrong. Try again';
state.isFetchingQuery = false;
state.smartWidgets = null;
});
builder.addCase(askGpt.pending, (state) => {
state.isAskingQuestion = true;
state.gptResponse = null;

View File

@ -7,7 +7,7 @@ interface MainState {
loading: boolean
count: number
refetch: boolean;
rolesWidgets: any[];
notify: {
showNotification: boolean
textNotification: string
@ -20,7 +20,7 @@ const initialState: MainState = {
loading: false,
count: 0,
refetch: false,
rolesWidgets: [],
notify: {
showNotification: false,
textNotification: '',