Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
476d26cf51 | ||
|
|
866f5263c5 | ||
|
|
b02b2fa52d | ||
|
|
b0b177c6d8 | ||
|
|
9ee76a0742 |
BIN
assets/pasted-20260115-104151-31d654f5.png
Normal file
BIN
assets/pasted-20260115-104151-31d654f5.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
7272
frontend/package-lock.json
generated
Normal file
7272
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,7 @@ export const gradientBgBase = 'bg-gradient-to-tr'
|
||||
export const colorBgBase = "bg-midnightBlueTheme-mainBG"
|
||||
export const gradientBgPurplePink = `${gradientBgBase} from-purple-400 via-pink-500 to-red-500`
|
||||
export const gradientBgViolet = `${gradientBgBase} ${colorBgBase}`
|
||||
export const gradientBgDark = `${gradientBgBase} from-dark-700 via-dark-900 to-dark-800`;
|
||||
export const gradientBgDark = `${gradientBgBase} from-gray-100 via-gray-200 to-gray-100`;
|
||||
export const gradientBgPinkRed = `${gradientBgBase} from-pink-400 via-red-500 to-yellow-500`
|
||||
|
||||
export const colorsBgLight = {
|
||||
@ -74,7 +74,7 @@ export const getButtonColor = (
|
||||
lightDark: 'bg-gray-100 text-black dark:bg-slate-800 dark:text-white',
|
||||
contrast: 'bg-gray-800 text-white dark:bg-white dark:text-black',
|
||||
success: 'bg-emerald-600 dark:bg-pavitra-blue text-white',
|
||||
danger: 'bg-midnightBlueTheme-outsideCardColor text-red-500 dark:text-white dark:bg-red-500 ',
|
||||
danger: 'bg-red-500 text-white',
|
||||
warning: 'bg-yellow-600 dark:bg-yellow-500 text-white',
|
||||
info: " bg-midnightBlueTheme-buttonColor dark:bg-pavitra-blue text-white ",
|
||||
},
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
@ -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} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -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%' }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -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}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -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} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -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%' }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -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}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -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%' }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -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} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -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%' }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -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}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -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} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -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%' }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -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}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -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[];
|
||||
}
|
||||
@ -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(' / ');
|
||||
};
|
||||
@ -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}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -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 />
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -1,94 +1,106 @@
|
||||
import * as icon from '@mdi/js';
|
||||
import Head from 'next/head'
|
||||
import React from 'react'
|
||||
import axios from 'axios';
|
||||
import React, { useState, useEffect } from 'react'
|
||||
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 axios from 'axios';
|
||||
import CardBox from '../components/CardBox';
|
||||
import BaseIcon from '../components/BaseIcon';
|
||||
import { mdiAccountMultiple, mdiCartOutline, mdiCalendarCheck } from '@mdi/js';
|
||||
import ChartLineSample from '../components/ChartLineSample/index';
|
||||
|
||||
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 [stats, setStats] = useState({
|
||||
customers: 0,
|
||||
menuItems: 0,
|
||||
reservations: 0,
|
||||
});
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
const { isFetchingQuery } = useAppSelector((state) => state.openAi);
|
||||
const [chartData, setChartData] = useState(null);
|
||||
const [recentReservations, setRecentReservations] = useState([]);
|
||||
const [lowStockItems, setLowStockItems] = useState([]);
|
||||
|
||||
const { rolesWidgets, loading } = useAppSelector((state) => state.roles);
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const statsPromises = [
|
||||
axios.get('/customers', { params: { limit: 1 } }),
|
||||
axios.get('/menu_items', { params: { limit: 1 } }),
|
||||
axios.get('/reservations', { params: { limit: 1 } }),
|
||||
];
|
||||
|
||||
const chartPromise = axios.get('/payments');
|
||||
const reservationsPromise = axios.get('/reservations?limit=5&orderBy=createdAt_DESC');
|
||||
const inventoryPromise = axios.get('/inventory_items');
|
||||
|
||||
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 [customersResponse, menuItemsResponse, reservationsResponse] = await Promise.all(statsPromises);
|
||||
|
||||
const requests = entities.map((entity, index) => {
|
||||
setStats({
|
||||
customers: customersResponse.data.count,
|
||||
menuItems: menuItemsResponse.data.count,
|
||||
reservations: reservationsResponse.data.count,
|
||||
});
|
||||
|
||||
if(hasPermission(currentUser, `READ_${entity.toUpperCase()}`)) {
|
||||
return axios.get(`/${entity.toLowerCase()}/count`);
|
||||
} else {
|
||||
fns[index](null);
|
||||
return Promise.resolve({data: {count: null}});
|
||||
const chartResponse = await chartPromise;
|
||||
const payments = chartResponse.data.rows;
|
||||
|
||||
if (payments) {
|
||||
const groupedData = payments.reduce((acc, payment) => {
|
||||
const date = new Date(payment.paid_at).toLocaleDateString();
|
||||
if (!acc[date]) {
|
||||
acc[date] = 0;
|
||||
}
|
||||
acc[date] += parseFloat(payment.amount);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const labels = Object.keys(groupedData);
|
||||
const data = Object.values(groupedData);
|
||||
|
||||
setChartData({
|
||||
labels,
|
||||
datasets: [
|
||||
{
|
||||
label: 'Revenue',
|
||||
data,
|
||||
fill: true,
|
||||
borderColor: '#4A5568',
|
||||
backgroundColor: '#4A556820',
|
||||
tension: 0.2,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
const recentReservationsResponse = await reservationsPromise;
|
||||
setRecentReservations(recentReservationsResponse.data.rows);
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
const inventoryResponse = await inventoryPromise;
|
||||
const inventoryItems = inventoryResponse.data.rows;
|
||||
if (inventoryItems) {
|
||||
const lowStock = inventoryItems.filter(
|
||||
(item) => item.quantity <= (item.reorder_level || 10)
|
||||
);
|
||||
setLowStockItems(lowStock);
|
||||
}
|
||||
|
||||
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]);
|
||||
} catch (error) {
|
||||
console.error('Failed to load dashboard data', error);
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!currentUser || !widgetsRole?.role?.value) return;
|
||||
getWidgets(widgetsRole?.role?.value || '').then();
|
||||
}, [widgetsRole?.role?.value]);
|
||||
loadData();
|
||||
}, []);
|
||||
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -105,443 +117,53 @@ const Dashboard = () => {
|
||||
{''}
|
||||
</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 className="grid grid-cols-1 gap-6 lg:grid-cols-3 mb-6">
|
||||
<CardBox
|
||||
title="Clients"
|
||||
number={stats.customers}
|
||||
icon={<BaseIcon path={mdiAccountMultiple} w="24" h="24" />}
|
||||
/>
|
||||
<CardBox
|
||||
title="Menu Items"
|
||||
number={stats.menuItems}
|
||||
icon={<BaseIcon path={mdiCartOutline} w="24" h="24" />}
|
||||
/>
|
||||
<CardBox
|
||||
title="Reservations"
|
||||
number={stats.reservations}
|
||||
icon={<BaseIcon path={mdiCalendarCheck} w="24" h="24" />}
|
||||
/>
|
||||
</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>}
|
||||
|
||||
{chartData && (
|
||||
<CardBox className="mb-6">
|
||||
<ChartLineSample data={chartData} />
|
||||
</CardBox>
|
||||
)}
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
|
||||
<CardBox>
|
||||
<h2 className="text-xl font-semibold mb-4">Recent Reservations</h2>
|
||||
<ul>
|
||||
{recentReservations.map((reservation) => (
|
||||
<li key={reservation.id} className="flex justify-between items-center py-2 border-b">
|
||||
<span>{reservation.code}</span>
|
||||
<span>{new Date(reservation.start_at).toLocaleString()}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</CardBox>
|
||||
<CardBox>
|
||||
<h2 className="text-xl font-semibold mb-4">Low Stock Items</h2>
|
||||
<ul>
|
||||
{lowStockItems.map((item) => (
|
||||
<li key={item.id} className="flex justify-between items-center py-2 border-b">
|
||||
<span>{item.name}</span>
|
||||
<span className="text-red-500">{item.quantity}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</CardBox>
|
||||
</div>
|
||||
</SectionMain>
|
||||
</>
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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: '',
|
||||
|
||||
@ -32,30 +32,30 @@ interface StyleState {
|
||||
|
||||
|
||||
const initialState: StyleState = {
|
||||
asideStyle: styles.midnightBlueTheme.aside,
|
||||
asideStyle: styles.white.aside,
|
||||
asideScrollbarsStyle: styles.white.asideScrollbars,
|
||||
asideBrandStyle: styles.white.asideBrand,
|
||||
asideMenuItemStyle: styles.midnightBlueTheme.asideMenuItem,
|
||||
asideMenuItemActiveStyle: styles.midnightBlueTheme.asideMenuItemActive,
|
||||
activeLinkColor: styles.midnightBlueTheme.activeLinkColor,
|
||||
asideMenuItemStyle: styles.white.asideMenuItem,
|
||||
asideMenuItemActiveStyle: styles.white.asideMenuItemActive,
|
||||
activeLinkColor: styles.white.activeLinkColor,
|
||||
asideMenuDropdownStyle: styles.white.asideMenuDropdown,
|
||||
navBarItemLabelStyle: styles.midnightBlueTheme.navBarItemLabel,
|
||||
navBarItemLabelHoverStyle: styles.midnightBlueTheme.navBarItemLabelHover,
|
||||
navBarItemLabelActiveColorStyle: styles.midnightBlueTheme.navBarItemLabelActiveColor,
|
||||
overlayStyle: styles.midnightBlueTheme.overlay,
|
||||
navBarItemLabelStyle: styles.white.navBarItemLabel,
|
||||
navBarItemLabelHoverStyle: styles.white.navBarItemLabelHover,
|
||||
navBarItemLabelActiveColorStyle: styles.white.navBarItemLabelActiveColor,
|
||||
overlayStyle: styles.white.overlay,
|
||||
darkMode: false,
|
||||
bgLayoutColor: styles.midnightBlueTheme.bgLayoutColor,
|
||||
iconsColor: styles.midnightBlueTheme.iconsColor,
|
||||
cardsColor: styles.midnightBlueTheme.cardsColor,
|
||||
focusRingColor: styles.midnightBlueTheme.focusRingColor,
|
||||
corners: styles.midnightBlueTheme.corners,
|
||||
cardsStyle: styles.midnightBlueTheme.cardsStyle,
|
||||
linkColor: styles.midnightBlueTheme.linkColor,
|
||||
websiteHeder: styles.midnightBlueTheme.websiteHeder,
|
||||
borders: styles.midnightBlueTheme.borders,
|
||||
shadow: styles.midnightBlueTheme.shadow,
|
||||
websiteSectionStyle: styles.midnightBlueTheme.websiteSectionStyle,
|
||||
textSecondary: styles.midnightBlueTheme.textSecondary,
|
||||
bgLayoutColor: styles.white.bgLayoutColor,
|
||||
iconsColor: styles.white.iconsColor,
|
||||
cardsColor: styles.white.cardsColor,
|
||||
focusRingColor: styles.white.focusRingColor,
|
||||
corners: styles.white.corners,
|
||||
cardsStyle: styles.white.cardsStyle,
|
||||
linkColor: styles.white.linkColor,
|
||||
websiteHeder: styles.white.websiteHeder,
|
||||
borders: styles.white.borders,
|
||||
shadow: styles.white.shadow,
|
||||
websiteSectionStyle: styles.white.websiteSectionStyle,
|
||||
textSecondary: styles.white.textSecondary,
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -71,7 +71,7 @@ export const midnightBlueTheme: StyleObject = {
|
||||
focusRingColor:
|
||||
'focus:ring focus:ring-midnightBlueTheme-800 focus:border-midnightBlueTheme-800 focus:outline-none border border-gray-600 dark:focus:ring-blue-600 dark:focus:border-blue-600',
|
||||
corners: 'rounded-lg',
|
||||
cardsStyle: 'bg-midnightBlueTheme-outsideCardColor border border-midnightBlueTheme-outsideCardColor shadow-xl',
|
||||
cardsStyle: 'bg-midnightBlueTheme-outsideCardColor border border-gray-200 dark:border-dark-700',
|
||||
linkColor: 'text-midnightBlueTheme-buttonColor',
|
||||
websiteHeder: 'border-b border-white border-opacity-10 shadow-md',
|
||||
borders: 'border-white border-opacity-10',
|
||||
|
||||
@ -68,29 +68,35 @@ module.exports = {
|
||||
|
||||
|
||||
midnightBlueTheme: {
|
||||
text: '#D4D7E2',
|
||||
iconsColor: '#5A7BEB',
|
||||
mainBG: '#1E2338',
|
||||
buttonColor: '#5A7BEB',
|
||||
cardColor: '#20263C',
|
||||
outsideCardColor: '#262E45',
|
||||
webSiteComponentBg: '#5A7BEB3D',
|
||||
testimonials: "#262E45",
|
||||
pricing:"#262E45",
|
||||
diversityContact: '#131618',
|
||||
diversityMain: '#5A7BEB',
|
||||
diversityHeader: '#5A7BEB',
|
||||
text: '#4A4A4A',
|
||||
iconsColor: '#85B6FF',
|
||||
mainBG: '#F7F7FC',
|
||||
buttonColor: '#85B6FF',
|
||||
cardColor: '#FFFFFF',
|
||||
outsideCardColor: '#F7F7FC',
|
||||
webSiteComponentBg: '#85B6FF3D',
|
||||
testimonials: "#FFFFFF",
|
||||
pricing:"#FFFFFF",
|
||||
diversityContact: '#F7F7FC',
|
||||
diversityMain: '#85B6FF',
|
||||
diversityHeader: '#85B6FF',
|
||||
900: '#14142A',
|
||||
800: '#262E45',
|
||||
800: '#E0E0E0',
|
||||
},
|
||||
primaryText: '#E9EAEF',
|
||||
primaryText: '#1E2338',
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['Ubuntu', 'sans-serif'],
|
||||
|
||||
},
|
||||
borderRadius: {
|
||||
'3xl': '2rem',
|
||||
borderRadius: {
|
||||
sm: '0.1rem',
|
||||
DEFAULT: '0.2rem',
|
||||
md: '0.3rem',
|
||||
lg: '0.4rem',
|
||||
xl: '0.6rem',
|
||||
'2xl': '0.8rem',
|
||||
'3xl': '0.8rem',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@ -67,6 +67,15 @@
|
||||
dependencies:
|
||||
regenerator-runtime "^0.14.0"
|
||||
|
||||
"@babel/runtime@^7.26.0":
|
||||
version "7.28.4"
|
||||
|
||||
"@babel/runtime@^7.27.6":
|
||||
version "7.28.4"
|
||||
|
||||
"@babel/runtime@^7.28.4":
|
||||
version "7.28.4"
|
||||
|
||||
"@babel/template@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz"
|
||||
@ -263,6 +272,25 @@
|
||||
resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz"
|
||||
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
|
||||
|
||||
"@img/colour@^1.0.0":
|
||||
version "1.0.0"
|
||||
|
||||
"@img/sharp-libvips-linux-x64@1.2.4":
|
||||
version "1.2.4"
|
||||
|
||||
"@img/sharp-libvips-linuxmusl-x64@1.2.4":
|
||||
version "1.2.4"
|
||||
|
||||
"@img/sharp-linux-x64@0.34.5":
|
||||
version "0.34.5"
|
||||
optionalDependencies:
|
||||
"@img/sharp-libvips-linux-x64" "1.2.4"
|
||||
|
||||
"@img/sharp-linuxmusl-x64@0.34.5":
|
||||
version "0.34.5"
|
||||
optionalDependencies:
|
||||
"@img/sharp-libvips-linuxmusl-x64" "1.2.4"
|
||||
|
||||
"@isaacs/cliui@^8.0.2":
|
||||
version "8.0.2"
|
||||
resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz"
|
||||
@ -317,59 +345,51 @@
|
||||
resolved "https://registry.npmjs.org/@mdi/js/-/js-7.4.47.tgz"
|
||||
integrity sha512-KPnNOtm5i2pMabqZxpUz7iQf+mfrYZyKCZ8QNz85czgEt7cuHcGorWfdzUMWYA0SD+a6Hn4FmJ+YhzzzjkTZrQ==
|
||||
|
||||
"@mui/core-downloads-tracker@^5.16.13":
|
||||
version "5.16.13"
|
||||
resolved "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.13.tgz"
|
||||
integrity sha512-xe5RwI0Q2O709Bd2Y7l1W1NIwNmln0y+xaGk5VgX3vDJbkQEqzdfTFZ73e0CkEZgJwyiWgk5HY0l8R4nysOxjw==
|
||||
"@mui/core-downloads-tracker@^6.5.0":
|
||||
version "6.5.0"
|
||||
|
||||
"@mui/material@^5.15.7":
|
||||
version "5.16.13"
|
||||
resolved "https://registry.npmjs.org/@mui/material/-/material-5.16.13.tgz"
|
||||
integrity sha512-FhLDkDPYDzvrWCHFsdXzRArhS4AdYufU8d69rmLL+bwhodPcbm2C7cS8Gq5VR32PsW6aKZb58gvAgvEVaiiJbA==
|
||||
"@mui/material@^6.3.0":
|
||||
version "6.5.0"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.23.9"
|
||||
"@mui/core-downloads-tracker" "^5.16.13"
|
||||
"@mui/system" "^5.16.13"
|
||||
"@mui/types" "^7.2.15"
|
||||
"@mui/utils" "^5.16.13"
|
||||
"@babel/runtime" "^7.26.0"
|
||||
"@mui/core-downloads-tracker" "^6.5.0"
|
||||
"@mui/system" "^6.5.0"
|
||||
"@mui/types" "~7.2.24"
|
||||
"@mui/utils" "^6.4.9"
|
||||
"@popperjs/core" "^2.11.8"
|
||||
"@types/react-transition-group" "^4.4.10"
|
||||
clsx "^2.1.0"
|
||||
"@types/react-transition-group" "^4.4.12"
|
||||
clsx "^2.1.1"
|
||||
csstype "^3.1.3"
|
||||
prop-types "^15.8.1"
|
||||
react-is "^19.0.0"
|
||||
react-transition-group "^4.4.5"
|
||||
|
||||
"@mui/private-theming@^5.16.13":
|
||||
version "5.16.13"
|
||||
resolved "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.13.tgz"
|
||||
integrity sha512-+s0FklvDvO7j0yBZn19DIIT3rLfub2fWvXGtMX49rG/xHfDFcP7fbWbZKHZMMP/2/IoTRDrZCbY1iP0xZlmuJA==
|
||||
"@mui/private-theming@^6.4.9":
|
||||
version "6.4.9"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.23.9"
|
||||
"@mui/utils" "^5.16.13"
|
||||
"@babel/runtime" "^7.26.0"
|
||||
"@mui/utils" "^6.4.9"
|
||||
prop-types "^15.8.1"
|
||||
|
||||
"@mui/styled-engine@^5.16.13":
|
||||
version "5.16.13"
|
||||
resolved "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.13.tgz"
|
||||
integrity sha512-2XNHEG8/o1ucSLhTA9J+HIIXjzlnEc0OV7kneeUQ5JukErPYT2zc6KYBDLjlKWrzQyvnQzbiffjjspgHUColZg==
|
||||
"@mui/styled-engine@^6.5.0":
|
||||
version "6.5.0"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.23.9"
|
||||
"@babel/runtime" "^7.26.0"
|
||||
"@emotion/cache" "^11.13.5"
|
||||
"@emotion/serialize" "^1.3.3"
|
||||
"@emotion/sheet" "^1.4.0"
|
||||
csstype "^3.1.3"
|
||||
prop-types "^15.8.1"
|
||||
|
||||
"@mui/system@^5.16.13":
|
||||
version "5.16.13"
|
||||
resolved "https://registry.npmjs.org/@mui/system/-/system-5.16.13.tgz"
|
||||
integrity sha512-JnO3VH3yNoAmgyr44/2jiS1tcNwshwAqAaG5fTEEjHQbkuZT/mvPYj2GC1cON0zEQ5V03xrCNl/D+gU9AXibpw==
|
||||
"@mui/system@^6.5.0":
|
||||
version "6.5.0"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.23.9"
|
||||
"@mui/private-theming" "^5.16.13"
|
||||
"@mui/styled-engine" "^5.16.13"
|
||||
"@mui/types" "^7.2.15"
|
||||
"@mui/utils" "^5.16.13"
|
||||
clsx "^2.1.0"
|
||||
"@babel/runtime" "^7.26.0"
|
||||
"@mui/private-theming" "^6.4.9"
|
||||
"@mui/styled-engine" "^6.5.0"
|
||||
"@mui/types" "~7.2.24"
|
||||
"@mui/utils" "^6.4.9"
|
||||
clsx "^2.1.1"
|
||||
csstype "^3.1.3"
|
||||
prop-types "^15.8.1"
|
||||
|
||||
@ -378,7 +398,10 @@
|
||||
resolved "https://registry.npmjs.org/@mui/types/-/types-7.2.20.tgz"
|
||||
integrity sha512-straFHD7L8v05l/N5vcWk+y7eL9JF0C2mtph/y4BPm3gn2Eh61dDwDB65pa8DLss3WJfDXYC7Kx5yjP0EmXpgw==
|
||||
|
||||
"@mui/utils@^5.14.16", "@mui/utils@^5.16.13":
|
||||
"@mui/types@~7.2.24":
|
||||
version "7.2.24"
|
||||
|
||||
"@mui/utils@^5.14.16":
|
||||
version "5.16.13"
|
||||
resolved "https://registry.npmjs.org/@mui/utils/-/utils-5.16.13.tgz"
|
||||
integrity sha512-35kLiShnDPByk57Mz4PP66fQUodCFiOD92HfpW6dK9lc7kjhZsKHRKeYPgWuwEHeXwYsCSFtBCW4RZh/8WT+TQ==
|
||||
@ -390,6 +413,16 @@
|
||||
prop-types "^15.8.1"
|
||||
react-is "^19.0.0"
|
||||
|
||||
"@mui/utils@^6.4.9":
|
||||
version "6.4.9"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.26.0"
|
||||
"@mui/types" "~7.2.24"
|
||||
"@types/prop-types" "^15.7.14"
|
||||
clsx "^2.1.1"
|
||||
prop-types "^15.8.1"
|
||||
react-is "^19.0.0"
|
||||
|
||||
"@mui/x-data-grid@^6.19.2":
|
||||
version "6.20.4"
|
||||
resolved "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-6.20.4.tgz"
|
||||
@ -401,10 +434,8 @@
|
||||
prop-types "^15.8.1"
|
||||
reselect "^4.1.8"
|
||||
|
||||
"@next/env@14.2.22":
|
||||
version "14.2.22"
|
||||
resolved "https://registry.npmjs.org/@next/env/-/env-14.2.22.tgz"
|
||||
integrity sha512-EQ6y1QeNQglNmNIXvwP/Bb+lf7n9WtgcWvtoFsHquVLCJUuxRs+6SfZ5EK0/EqkkLex4RrDySvKgKNN7PXip7Q==
|
||||
"@next/env@15.5.9":
|
||||
version "15.5.9"
|
||||
|
||||
"@next/eslint-plugin-next@13.0.4":
|
||||
version "13.0.4"
|
||||
@ -413,10 +444,11 @@
|
||||
dependencies:
|
||||
glob "7.1.7"
|
||||
|
||||
"@next/swc-darwin-arm64@14.2.22":
|
||||
version "14.2.22"
|
||||
resolved "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.22.tgz"
|
||||
integrity sha512-HUaLiehovgnqY4TMBZJ3pDaOsTE1spIXeR10pWgdQVPYqDGQmHJBj3h3V6yC0uuo/RoY2GC0YBFRkOX3dI9WVQ==
|
||||
"@next/swc-linux-x64-gnu@15.5.7":
|
||||
version "15.5.7"
|
||||
|
||||
"@next/swc-linux-x64-musl@15.5.7":
|
||||
version "15.5.7"
|
||||
|
||||
"@nodelib/fs.scandir@2.1.5":
|
||||
version "2.1.5"
|
||||
@ -461,6 +493,15 @@
|
||||
resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz"
|
||||
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
|
||||
|
||||
"@react-dnd/asap@^5.0.1":
|
||||
version "5.0.2"
|
||||
|
||||
"@react-dnd/invariant@^4.0.1":
|
||||
version "4.0.2"
|
||||
|
||||
"@react-dnd/shallowequal@^4.0.1":
|
||||
version "4.0.2"
|
||||
|
||||
"@reduxjs/toolkit@^2.1.0":
|
||||
version "2.5.0"
|
||||
resolved "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.5.0.tgz"
|
||||
@ -488,18 +529,10 @@
|
||||
resolved "https://registry.npmjs.org/@seznam/compose-react-refs/-/compose-react-refs-1.0.6.tgz"
|
||||
integrity sha512-izzOXQfeQLonzrIQb8u6LQ8dk+ymz3WXTIXjvOlTXHq6sbzROg3NWU+9TTAOpEoK9Bth24/6F/XrfHJ5yR5n6Q==
|
||||
|
||||
"@swc/counter@^0.1.3":
|
||||
version "0.1.3"
|
||||
resolved "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz"
|
||||
integrity sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==
|
||||
|
||||
"@swc/helpers@0.5.5":
|
||||
version "0.5.5"
|
||||
resolved "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz"
|
||||
integrity sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==
|
||||
"@swc/helpers@0.5.15":
|
||||
version "0.5.15"
|
||||
dependencies:
|
||||
"@swc/counter" "^0.1.3"
|
||||
tslib "^2.4.0"
|
||||
tslib "^2.8.0"
|
||||
|
||||
"@tailwindcss/forms@^0.5.7":
|
||||
version "0.5.9"
|
||||
@ -544,6 +577,11 @@
|
||||
"@types/react" "*"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
|
||||
"@types/hoist-non-react-statics@^3.3.6":
|
||||
version "3.3.7"
|
||||
dependencies:
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
|
||||
"@types/json-schema@^7.0.9":
|
||||
version "7.0.11"
|
||||
resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz"
|
||||
@ -574,6 +612,9 @@
|
||||
resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz"
|
||||
integrity sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==
|
||||
|
||||
"@types/prop-types@^15.7.14":
|
||||
version "15.7.15"
|
||||
|
||||
"@types/react-big-calendar@^1.8.8":
|
||||
version "1.16.0"
|
||||
resolved "https://registry.npmjs.org/@types/react-big-calendar/-/react-big-calendar-1.16.0.tgz"
|
||||
@ -593,7 +634,7 @@
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
redux "^4.0.0"
|
||||
|
||||
"@types/react-transition-group@^4.4.0", "@types/react-transition-group@^4.4.10":
|
||||
"@types/react-transition-group@^4.4.0", "@types/react-transition-group@^4.4.12":
|
||||
version "4.4.12"
|
||||
resolved "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz"
|
||||
integrity sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==
|
||||
@ -896,13 +937,11 @@ axe-core@^4.4.3:
|
||||
resolved "https://registry.npmjs.org/axe-core/-/axe-core-4.4.3.tgz"
|
||||
integrity sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w==
|
||||
|
||||
axios@^1.6.7:
|
||||
version "1.7.9"
|
||||
resolved "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz"
|
||||
integrity sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==
|
||||
axios@^1.8.4:
|
||||
version "1.13.2"
|
||||
dependencies:
|
||||
follow-redirects "^1.15.6"
|
||||
form-data "^4.0.0"
|
||||
form-data "^4.0.4"
|
||||
proxy-from-env "^1.1.0"
|
||||
|
||||
axobject-query@^2.2.0:
|
||||
@ -924,6 +963,9 @@ balanced-match@^1.0.0:
|
||||
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
|
||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||
|
||||
base64-arraybuffer@^1.0.2:
|
||||
version "1.0.2"
|
||||
|
||||
binary-extensions@^2.0.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz"
|
||||
@ -966,12 +1008,11 @@ buffer-equal-constant-time@1.0.1:
|
||||
resolved "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz"
|
||||
integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==
|
||||
|
||||
busboy@1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz"
|
||||
integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==
|
||||
call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
|
||||
version "1.0.2"
|
||||
dependencies:
|
||||
streamsearch "^1.1.0"
|
||||
es-errors "^1.3.0"
|
||||
function-bind "^1.1.2"
|
||||
|
||||
call-bind@^1.0.0, call-bind@^1.0.2:
|
||||
version "1.0.2"
|
||||
@ -1041,17 +1082,12 @@ client-only@0.0.1:
|
||||
resolved "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz"
|
||||
integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==
|
||||
|
||||
clsx@^1.1.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz"
|
||||
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
|
||||
|
||||
clsx@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz"
|
||||
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
|
||||
|
||||
clsx@^2.0.0, clsx@^2.1.0, clsx@^2.1.1:
|
||||
clsx@^2.0.0, clsx@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz"
|
||||
integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
|
||||
@ -1095,6 +1131,9 @@ core-js-pure@^3.20.2:
|
||||
resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.25.1.tgz"
|
||||
integrity sha512-7Fr74bliUDdeJCBMxkkIuQ4xfxn/SwrVg+HkJUAoNEXVqYLv55l6Af0dJ5Lq2YBUW9yKqSkLXaS5SYPK6MGa/A==
|
||||
|
||||
core-js@^3:
|
||||
version "3.47.0"
|
||||
|
||||
cosmiconfig@^7.0.0:
|
||||
version "7.1.0"
|
||||
resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz"
|
||||
@ -1113,6 +1152,11 @@ cross-env@^7.0.3:
|
||||
dependencies:
|
||||
cross-spawn "^7.0.1"
|
||||
|
||||
cross-fetch@4.0.0:
|
||||
version "4.0.0"
|
||||
dependencies:
|
||||
node-fetch "^2.6.12"
|
||||
|
||||
cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz"
|
||||
@ -1122,6 +1166,11 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
|
||||
shebang-command "^2.0.0"
|
||||
which "^2.0.1"
|
||||
|
||||
css-line-break@^2.1.0:
|
||||
version "2.1.0"
|
||||
dependencies:
|
||||
utrie "^1.0.2"
|
||||
|
||||
cssesc@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz"
|
||||
@ -1213,6 +1262,9 @@ dequal@^2.0.3:
|
||||
resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz"
|
||||
integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==
|
||||
|
||||
detect-libc@^2.1.2:
|
||||
version "2.1.2"
|
||||
|
||||
didyoumean@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz"
|
||||
@ -1230,6 +1282,13 @@ dlv@^1.1.3:
|
||||
resolved "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz"
|
||||
integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==
|
||||
|
||||
dnd-core@^16.0.1:
|
||||
version "16.0.1"
|
||||
dependencies:
|
||||
"@react-dnd/asap" "^5.0.1"
|
||||
"@react-dnd/invariant" "^4.0.1"
|
||||
redux "^4.2.0"
|
||||
|
||||
doctrine@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz"
|
||||
@ -1252,6 +1311,13 @@ dom-helpers@^5.0.1, dom-helpers@^5.2.0, dom-helpers@^5.2.1:
|
||||
"@babel/runtime" "^7.8.7"
|
||||
csstype "^3.0.2"
|
||||
|
||||
dunder-proto@^1.0.1:
|
||||
version "1.0.1"
|
||||
dependencies:
|
||||
call-bind-apply-helpers "^1.0.1"
|
||||
es-errors "^1.3.0"
|
||||
gopd "^1.2.0"
|
||||
|
||||
eastasianwidth@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz"
|
||||
@ -1324,6 +1390,25 @@ es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19
|
||||
string.prototype.trimstart "^1.0.5"
|
||||
unbox-primitive "^1.0.2"
|
||||
|
||||
es-define-property@^1.0.1:
|
||||
version "1.0.1"
|
||||
|
||||
es-errors@^1.3.0:
|
||||
version "1.3.0"
|
||||
|
||||
es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
|
||||
version "1.1.1"
|
||||
dependencies:
|
||||
es-errors "^1.3.0"
|
||||
|
||||
es-set-tostringtag@^2.1.0:
|
||||
version "2.1.0"
|
||||
dependencies:
|
||||
es-errors "^1.3.0"
|
||||
get-intrinsic "^1.2.6"
|
||||
has-tostringtag "^1.0.2"
|
||||
hasown "^2.0.2"
|
||||
|
||||
es-shim-unscopables@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz"
|
||||
@ -1673,13 +1758,13 @@ foreground-child@^3.1.0:
|
||||
cross-spawn "^7.0.0"
|
||||
signal-exit "^4.0.1"
|
||||
|
||||
form-data@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz"
|
||||
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
|
||||
form-data@^4.0.4:
|
||||
version "4.0.5"
|
||||
dependencies:
|
||||
asynckit "^0.4.0"
|
||||
combined-stream "^1.0.8"
|
||||
es-set-tostringtag "^2.1.0"
|
||||
hasown "^2.0.2"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
formik@^2.4.5:
|
||||
@ -1740,6 +1825,26 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@
|
||||
has "^1.0.3"
|
||||
has-symbols "^1.0.3"
|
||||
|
||||
get-intrinsic@^1.2.6:
|
||||
version "1.3.0"
|
||||
dependencies:
|
||||
call-bind-apply-helpers "^1.0.2"
|
||||
es-define-property "^1.0.1"
|
||||
es-errors "^1.3.0"
|
||||
es-object-atoms "^1.1.1"
|
||||
function-bind "^1.1.2"
|
||||
get-proto "^1.0.1"
|
||||
gopd "^1.2.0"
|
||||
has-symbols "^1.1.0"
|
||||
hasown "^2.0.2"
|
||||
math-intrinsics "^1.1.0"
|
||||
|
||||
get-proto@^1.0.1:
|
||||
version "1.0.1"
|
||||
dependencies:
|
||||
dunder-proto "^1.0.1"
|
||||
es-object-atoms "^1.0.0"
|
||||
|
||||
get-symbol-description@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz"
|
||||
@ -1753,26 +1858,26 @@ get-tsconfig@^4.2.0:
|
||||
resolved "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.2.0.tgz"
|
||||
integrity sha512-X8u8fREiYOE6S8hLbq99PeykTDoLVnxvF4DjWKJmz9xy2nNRdUcV8ZN9tniJFeKyTU3qnC9lL8n4Chd6LmVKHg==
|
||||
|
||||
glob-parent@^5.1.2:
|
||||
glob-parent@^5.1.2, glob-parent@~5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz"
|
||||
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
|
||||
dependencies:
|
||||
is-glob "^4.0.1"
|
||||
|
||||
glob-parent@^6.0.1, glob-parent@^6.0.2:
|
||||
glob-parent@^6.0.1:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz"
|
||||
integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
|
||||
dependencies:
|
||||
is-glob "^4.0.3"
|
||||
|
||||
glob-parent@~5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz"
|
||||
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
|
||||
glob-parent@^6.0.2:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz"
|
||||
integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
|
||||
dependencies:
|
||||
is-glob "^4.0.1"
|
||||
is-glob "^4.0.3"
|
||||
|
||||
glob@^10.3.10:
|
||||
version "10.4.5"
|
||||
@ -1848,7 +1953,10 @@ globrex@^0.1.2:
|
||||
resolved "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz"
|
||||
integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==
|
||||
|
||||
graceful-fs@^4.2.11, graceful-fs@^4.2.4:
|
||||
gopd@^1.2.0:
|
||||
version "1.2.0"
|
||||
|
||||
graceful-fs@^4.2.4:
|
||||
version "4.2.11"
|
||||
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz"
|
||||
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
|
||||
@ -1880,6 +1988,9 @@ has-symbols@^1.0.2, has-symbols@^1.0.3:
|
||||
resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz"
|
||||
integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
|
||||
|
||||
has-symbols@^1.1.0:
|
||||
version "1.1.0"
|
||||
|
||||
has-tostringtag@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz"
|
||||
@ -1887,6 +1998,11 @@ has-tostringtag@^1.0.0:
|
||||
dependencies:
|
||||
has-symbols "^1.0.2"
|
||||
|
||||
has-tostringtag@^1.0.2:
|
||||
version "1.0.2"
|
||||
dependencies:
|
||||
has-symbols "^1.0.3"
|
||||
|
||||
has@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz"
|
||||
@ -1908,6 +2024,35 @@ hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-
|
||||
dependencies:
|
||||
react-is "^16.7.0"
|
||||
|
||||
html-parse-stringify@^3.0.1:
|
||||
version "3.0.1"
|
||||
dependencies:
|
||||
void-elements "3.1.0"
|
||||
|
||||
html2canvas@^1.4.1:
|
||||
version "1.4.1"
|
||||
dependencies:
|
||||
css-line-break "^2.1.0"
|
||||
text-segmentation "^1.0.3"
|
||||
|
||||
i18next-browser-languagedetector@^8.1.0:
|
||||
version "8.2.0"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.23.2"
|
||||
|
||||
i18next-fs-backend@^2.6.0:
|
||||
version "2.6.1"
|
||||
|
||||
i18next-http-backend@^3.0.2:
|
||||
version "3.0.2"
|
||||
dependencies:
|
||||
cross-fetch "4.0.0"
|
||||
|
||||
i18next@^25.1.2:
|
||||
version "25.7.4"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.28.4"
|
||||
|
||||
ignore@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz"
|
||||
@ -2321,6 +2466,9 @@ luxon@^3.2.1:
|
||||
resolved "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz"
|
||||
integrity sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==
|
||||
|
||||
math-intrinsics@^1.1.0:
|
||||
version "1.1.0"
|
||||
|
||||
memoize-one@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz"
|
||||
@ -2387,7 +2535,7 @@ moment-timezone@^0.5.40:
|
||||
dependencies:
|
||||
moment "^2.29.4"
|
||||
|
||||
moment@^2.29.4:
|
||||
moment@^2.29.4, moment@^2.30.1:
|
||||
version "2.30.1"
|
||||
resolved "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz"
|
||||
integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==
|
||||
@ -2421,28 +2569,38 @@ natural-compare@^1.4.0:
|
||||
resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
|
||||
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
||||
|
||||
next@^14.1.0:
|
||||
version "14.2.22"
|
||||
resolved "https://registry.npmjs.org/next/-/next-14.2.22.tgz"
|
||||
integrity sha512-Ps2caobQ9hlEhscLPiPm3J3SYhfwfpMqzsoCMZGWxt9jBRK9hoBZj2A37i8joKhsyth2EuVKDVJCTF5/H4iEDw==
|
||||
next-i18next@^15.4.2:
|
||||
version "15.4.3"
|
||||
dependencies:
|
||||
"@next/env" "14.2.22"
|
||||
"@swc/helpers" "0.5.5"
|
||||
busboy "1.6.0"
|
||||
"@babel/runtime" "^7.23.2"
|
||||
"@types/hoist-non-react-statics" "^3.3.6"
|
||||
core-js "^3"
|
||||
hoist-non-react-statics "^3.3.2"
|
||||
i18next-fs-backend "^2.6.0"
|
||||
|
||||
next@^15.3.1:
|
||||
version "15.5.9"
|
||||
dependencies:
|
||||
"@next/env" "15.5.9"
|
||||
"@swc/helpers" "0.5.15"
|
||||
caniuse-lite "^1.0.30001579"
|
||||
graceful-fs "^4.2.11"
|
||||
postcss "8.4.31"
|
||||
styled-jsx "5.1.1"
|
||||
styled-jsx "5.1.6"
|
||||
optionalDependencies:
|
||||
"@next/swc-darwin-arm64" "14.2.22"
|
||||
"@next/swc-darwin-x64" "14.2.22"
|
||||
"@next/swc-linux-arm64-gnu" "14.2.22"
|
||||
"@next/swc-linux-arm64-musl" "14.2.22"
|
||||
"@next/swc-linux-x64-gnu" "14.2.22"
|
||||
"@next/swc-linux-x64-musl" "14.2.22"
|
||||
"@next/swc-win32-arm64-msvc" "14.2.22"
|
||||
"@next/swc-win32-ia32-msvc" "14.2.22"
|
||||
"@next/swc-win32-x64-msvc" "14.2.22"
|
||||
"@next/swc-darwin-arm64" "15.5.7"
|
||||
"@next/swc-darwin-x64" "15.5.7"
|
||||
"@next/swc-linux-arm64-gnu" "15.5.7"
|
||||
"@next/swc-linux-arm64-musl" "15.5.7"
|
||||
"@next/swc-linux-x64-gnu" "15.5.7"
|
||||
"@next/swc-linux-x64-musl" "15.5.7"
|
||||
"@next/swc-win32-arm64-msvc" "15.5.7"
|
||||
"@next/swc-win32-x64-msvc" "15.5.7"
|
||||
sharp "^0.34.3"
|
||||
|
||||
node-fetch@^2.6.12:
|
||||
version "2.7.0"
|
||||
dependencies:
|
||||
whatwg-url "^5.0.0"
|
||||
|
||||
node-releases@^2.0.6:
|
||||
version "2.0.6"
|
||||
@ -2686,15 +2844,7 @@ postcss-nested@^6.2.0:
|
||||
dependencies:
|
||||
postcss-selector-parser "^6.1.1"
|
||||
|
||||
postcss-selector-parser@^6.1.1:
|
||||
version "6.1.2"
|
||||
resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz"
|
||||
integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==
|
||||
dependencies:
|
||||
cssesc "^3.0.0"
|
||||
util-deprecate "^1.0.2"
|
||||
|
||||
postcss-selector-parser@^6.1.2:
|
||||
postcss-selector-parser@^6.1.1, postcss-selector-parser@^6.1.2:
|
||||
version "6.1.2"
|
||||
resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz"
|
||||
integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==
|
||||
@ -2822,6 +2972,20 @@ react-datepicker@^4.10.0:
|
||||
react-onclickoutside "^6.13.0"
|
||||
react-popper "^2.3.0"
|
||||
|
||||
react-dnd-html5-backend@^16.0.1:
|
||||
version "16.0.1"
|
||||
dependencies:
|
||||
dnd-core "^16.0.1"
|
||||
|
||||
react-dnd@^16.0.1:
|
||||
version "16.0.1"
|
||||
dependencies:
|
||||
"@react-dnd/invariant" "^4.0.1"
|
||||
"@react-dnd/shallowequal" "^4.0.1"
|
||||
dnd-core "^16.0.1"
|
||||
fast-deep-equal "^3.1.3"
|
||||
hoist-non-react-statics "^3.3.2"
|
||||
|
||||
react-dom@^19.0.0:
|
||||
version "19.0.0"
|
||||
resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz"
|
||||
@ -2839,7 +3003,18 @@ react-fast-compare@^3.0.1:
|
||||
resolved "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz"
|
||||
integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==
|
||||
|
||||
react-is@^16.13.1, react-is@^16.7.0:
|
||||
react-i18next@^15.5.1:
|
||||
version "15.7.4"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.27.6"
|
||||
html-parse-stringify "^3.0.1"
|
||||
|
||||
react-is@^16.13.1:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
|
||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||
|
||||
react-is@^16.7.0:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
|
||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||
@ -2932,12 +3107,10 @@ react-switch@^7.0.0:
|
||||
dependencies:
|
||||
prop-types "^15.7.2"
|
||||
|
||||
react-toastify@^9.1.2:
|
||||
version "9.1.3"
|
||||
resolved "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz"
|
||||
integrity sha512-fPfb8ghtn/XMxw3LkxQBk3IyagNpF/LIKjOBflbexr2AWxAH1MJgvnESwEwBn9liLFXgTKWgBSdZpw9m4OTHTg==
|
||||
react-toastify@^11.0.2:
|
||||
version "11.0.5"
|
||||
dependencies:
|
||||
clsx "^1.1.1"
|
||||
clsx "^2.1.1"
|
||||
|
||||
react-transition-group@^4.3.0, react-transition-group@^4.4.5:
|
||||
version "4.4.5"
|
||||
@ -2980,6 +3153,11 @@ redux@^4.0.0:
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
|
||||
redux@^4.2.0:
|
||||
version "4.2.1"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
|
||||
redux@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz"
|
||||
@ -3090,6 +3268,41 @@ semver@^7.3.7, semver@^7.5.4:
|
||||
resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz"
|
||||
integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
|
||||
|
||||
semver@^7.7.3:
|
||||
version "7.7.3"
|
||||
|
||||
sharp@^0.34.3:
|
||||
version "0.34.5"
|
||||
dependencies:
|
||||
"@img/colour" "^1.0.0"
|
||||
detect-libc "^2.1.2"
|
||||
semver "^7.7.3"
|
||||
optionalDependencies:
|
||||
"@img/sharp-darwin-arm64" "0.34.5"
|
||||
"@img/sharp-darwin-x64" "0.34.5"
|
||||
"@img/sharp-libvips-darwin-arm64" "1.2.4"
|
||||
"@img/sharp-libvips-darwin-x64" "1.2.4"
|
||||
"@img/sharp-libvips-linux-arm" "1.2.4"
|
||||
"@img/sharp-libvips-linux-arm64" "1.2.4"
|
||||
"@img/sharp-libvips-linux-ppc64" "1.2.4"
|
||||
"@img/sharp-libvips-linux-riscv64" "1.2.4"
|
||||
"@img/sharp-libvips-linux-s390x" "1.2.4"
|
||||
"@img/sharp-libvips-linux-x64" "1.2.4"
|
||||
"@img/sharp-libvips-linuxmusl-arm64" "1.2.4"
|
||||
"@img/sharp-libvips-linuxmusl-x64" "1.2.4"
|
||||
"@img/sharp-linux-arm" "0.34.5"
|
||||
"@img/sharp-linux-arm64" "0.34.5"
|
||||
"@img/sharp-linux-ppc64" "0.34.5"
|
||||
"@img/sharp-linux-riscv64" "0.34.5"
|
||||
"@img/sharp-linux-s390x" "0.34.5"
|
||||
"@img/sharp-linux-x64" "0.34.5"
|
||||
"@img/sharp-linuxmusl-arm64" "0.34.5"
|
||||
"@img/sharp-linuxmusl-x64" "0.34.5"
|
||||
"@img/sharp-wasm32" "0.34.5"
|
||||
"@img/sharp-win32-arm64" "0.34.5"
|
||||
"@img/sharp-win32-ia32" "0.34.5"
|
||||
"@img/sharp-win32-x64" "0.34.5"
|
||||
|
||||
shebang-command@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz"
|
||||
@ -3146,11 +3359,6 @@ split-on-first@^3.0.0:
|
||||
resolved "https://registry.npmjs.org/split-on-first/-/split-on-first-3.0.0.tgz"
|
||||
integrity sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==
|
||||
|
||||
streamsearch@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz"
|
||||
integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
|
||||
|
||||
"string-width-cjs@npm:string-width@^4.2.0":
|
||||
version "4.2.3"
|
||||
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
|
||||
@ -3241,10 +3449,8 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
|
||||
resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz"
|
||||
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
||||
|
||||
styled-jsx@5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz"
|
||||
integrity sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==
|
||||
styled-jsx@5.1.6:
|
||||
version "5.1.6"
|
||||
dependencies:
|
||||
client-only "0.0.1"
|
||||
|
||||
@ -3379,6 +3585,11 @@ tapable@^2.2.0:
|
||||
resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz"
|
||||
integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==
|
||||
|
||||
text-segmentation@^1.0.3:
|
||||
version "1.0.3"
|
||||
dependencies:
|
||||
utrie "^1.0.2"
|
||||
|
||||
text-table@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz"
|
||||
@ -3423,6 +3634,9 @@ to-regex-range@^5.0.1:
|
||||
dependencies:
|
||||
is-number "^7.0.0"
|
||||
|
||||
tr46@~0.0.3:
|
||||
version "0.0.3"
|
||||
|
||||
ts-interface-checker@^0.1.9:
|
||||
version "0.1.13"
|
||||
resolved "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz"
|
||||
@ -3448,6 +3662,9 @@ tslib@^2.0.0, tslib@^2.4.0:
|
||||
resolved "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz"
|
||||
integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
|
||||
|
||||
tslib@^2.8.0:
|
||||
version "2.8.1"
|
||||
|
||||
tsutils@^3.21.0:
|
||||
version "3.21.0"
|
||||
resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz"
|
||||
@ -3467,10 +3684,8 @@ type-fest@^0.20.2:
|
||||
resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz"
|
||||
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
|
||||
|
||||
typescript@^4.8.3:
|
||||
version "4.8.3"
|
||||
resolved "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz"
|
||||
integrity sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==
|
||||
typescript@^5.4.5:
|
||||
version "5.9.3"
|
||||
|
||||
unbox-primitive@^1.0.2:
|
||||
version "1.0.2"
|
||||
@ -3534,11 +3749,19 @@ util-deprecate@^1.0.2:
|
||||
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
|
||||
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
||||
|
||||
utrie@^1.0.2:
|
||||
version "1.0.2"
|
||||
dependencies:
|
||||
base64-arraybuffer "^1.0.2"
|
||||
|
||||
uuid@^9.0.0:
|
||||
version "9.0.1"
|
||||
resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz"
|
||||
integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
|
||||
|
||||
void-elements@3.1.0:
|
||||
version "3.1.0"
|
||||
|
||||
warning@^4.0.2, warning@^4.0.3:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz"
|
||||
@ -3546,6 +3769,15 @@ warning@^4.0.2, warning@^4.0.3:
|
||||
dependencies:
|
||||
loose-envify "^1.0.0"
|
||||
|
||||
webidl-conversions@^3.0.0:
|
||||
version "3.0.1"
|
||||
|
||||
whatwg-url@^5.0.0:
|
||||
version "5.0.0"
|
||||
dependencies:
|
||||
tr46 "~0.0.3"
|
||||
webidl-conversions "^3.0.0"
|
||||
|
||||
which-boxed-primitive@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user