Initial commit for new panel
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
"use client";
|
||||
|
||||
import type { ApexOptions } from "apexcharts";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
type PropsType = {
|
||||
data: {
|
||||
x: string;
|
||||
y: number;
|
||||
}[];
|
||||
};
|
||||
|
||||
const Chart = dynamic(() => import("react-apexcharts"), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
export function CampaignVisitorsChart({ data }: PropsType) {
|
||||
const options: ApexOptions = {
|
||||
colors: ["#5750F1"],
|
||||
chart: {
|
||||
fontFamily: "Satoshi, sans-serif",
|
||||
type: "bar",
|
||||
height: 200,
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
plotOptions: {
|
||||
bar: {
|
||||
horizontal: false,
|
||||
columnWidth: "40%",
|
||||
borderRadius: 3,
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
stroke: {
|
||||
show: true,
|
||||
width: 4,
|
||||
colors: ["transparent"],
|
||||
},
|
||||
xaxis: {
|
||||
axisBorder: {
|
||||
show: false,
|
||||
},
|
||||
axisTicks: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
show: true,
|
||||
position: "top",
|
||||
horizontalAlign: "left",
|
||||
fontFamily: "Satoshi",
|
||||
},
|
||||
grid: {
|
||||
strokeDashArray: 7,
|
||||
yaxis: {
|
||||
lines: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
fill: {
|
||||
opacity: 1,
|
||||
},
|
||||
tooltip: {
|
||||
x: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="-ml-3.5 px-6 pb-1 pt-7.5">
|
||||
<Chart
|
||||
options={options}
|
||||
series={[
|
||||
{
|
||||
name: "Visitors",
|
||||
data,
|
||||
},
|
||||
]}
|
||||
type="bar"
|
||||
height={230}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { TrendingUpIcon } from "@/assets/icons";
|
||||
import { compactFormat } from "@/lib/format-number";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { getCampaignVisitorsData } from "@/services/charts.services";
|
||||
import { CampaignVisitorsChart } from "./chart";
|
||||
|
||||
export async function CampaignVisitors({ className }: { className?: string }) {
|
||||
const data = await getCampaignVisitorsData();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"rounded-[10px] bg-white shadow-1 dark:bg-gray-dark dark:shadow-card",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className="border-b border-stroke px-6 py-5.5 dark:border-dark-3">
|
||||
<div className="flex justify-between">
|
||||
<h2 className="mb-1.5 text-2xl font-bold text-dark dark:text-white">
|
||||
Campaign Visitors
|
||||
</h2>
|
||||
|
||||
<div className="mb-0.5 text-2xl font-bold text-dark dark:text-white">
|
||||
{compactFormat(data.total_visitors)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between">
|
||||
<div className="text-sm font-medium">Last Campaign Performance</div>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center gap-1.5",
|
||||
data.performance > 0 ? "text-green" : "text-red",
|
||||
)}
|
||||
>
|
||||
<TrendingUpIcon
|
||||
className={`${data.performance > 0 ? "-rotate-6" : "scale-y-[-1]"}`}
|
||||
/>
|
||||
|
||||
<span className="text-sm font-medium">{data.performance}%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CampaignVisitorsChart data={data.chart} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
"use client";
|
||||
|
||||
import { useIsMobile } from "@/hooks/use-mobile";
|
||||
import type { ApexOptions } from "apexcharts";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
type PropsType = {
|
||||
data: {
|
||||
received: { x: unknown; y: number }[];
|
||||
due: { x: unknown; y: number }[];
|
||||
};
|
||||
};
|
||||
|
||||
const Chart = dynamic(() => import("react-apexcharts"), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
export function PaymentsOverviewChart({ data }: PropsType) {
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const options: ApexOptions = {
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
colors: ["#5750F1", "#0ABEF9"],
|
||||
chart: {
|
||||
height: 310,
|
||||
type: "area",
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
fontFamily: "inherit",
|
||||
},
|
||||
fill: {
|
||||
gradient: {
|
||||
opacityFrom: 0.55,
|
||||
opacityTo: 0,
|
||||
},
|
||||
},
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 1024,
|
||||
options: {
|
||||
chart: {
|
||||
height: 300,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
breakpoint: 1366,
|
||||
options: {
|
||||
chart: {
|
||||
height: 320,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
stroke: {
|
||||
curve: "smooth",
|
||||
width: isMobile ? 2 : 3,
|
||||
},
|
||||
grid: {
|
||||
strokeDashArray: 5,
|
||||
yaxis: {
|
||||
lines: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
tooltip: {
|
||||
marker: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
xaxis: {
|
||||
axisBorder: {
|
||||
show: false,
|
||||
},
|
||||
axisTicks: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="-ml-4 -mr-5 h-[310px]">
|
||||
<Chart
|
||||
options={options}
|
||||
series={[
|
||||
{
|
||||
name: "Received",
|
||||
data: data.received,
|
||||
},
|
||||
{
|
||||
name: "Due",
|
||||
data: data.due,
|
||||
},
|
||||
]}
|
||||
type="area"
|
||||
height={310}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { PeriodPicker } from "@/components/period-picker";
|
||||
import { standardFormat } from "@/lib/format-number";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { getPaymentsOverviewData } from "@/services/charts.services";
|
||||
import { PaymentsOverviewChart } from "./chart";
|
||||
|
||||
type PropsType = {
|
||||
timeFrame?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export async function PaymentsOverview({
|
||||
timeFrame = "monthly",
|
||||
className,
|
||||
}: PropsType) {
|
||||
const data = await getPaymentsOverviewData(timeFrame);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"grid gap-2 rounded-[10px] bg-white px-7.5 pb-6 pt-7.5 shadow-1 dark:bg-gray-dark dark:shadow-card",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-wrap items-center justify-between gap-4">
|
||||
<h2 className="text-body-2xlg font-bold text-dark dark:text-white">
|
||||
Payments Overview
|
||||
</h2>
|
||||
|
||||
<PeriodPicker defaultValue={timeFrame} sectionKey="payments_overview" />
|
||||
</div>
|
||||
|
||||
<PaymentsOverviewChart data={data} />
|
||||
|
||||
<dl className="grid divide-stroke text-center dark:divide-dark-3 sm:grid-cols-2 sm:divide-x [&>div]:flex [&>div]:flex-col-reverse [&>div]:gap-1">
|
||||
<div className="dark:border-dark-3 max-sm:mb-3 max-sm:border-b max-sm:pb-3">
|
||||
<dt className="text-xl font-bold text-dark dark:text-white">
|
||||
${standardFormat(data.received.reduce((acc, { y }) => acc + y, 0))}
|
||||
</dt>
|
||||
<dd className="font-medium dark:text-dark-6">Received Amount</dd>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<dt className="text-xl font-bold text-dark dark:text-white">
|
||||
${standardFormat(data.due.reduce((acc, { y }) => acc + y, 0))}
|
||||
</dt>
|
||||
<dd className="font-medium dark:text-dark-6">Due Amount</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
"use client";
|
||||
|
||||
import { compactFormat } from "@/lib/format-number";
|
||||
import type { ApexOptions } from "apexcharts";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
type PropsType = {
|
||||
data: { name: string; amount: number }[];
|
||||
};
|
||||
|
||||
const Chart = dynamic(() => import("react-apexcharts"), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
export function DonutChart({ data }: PropsType) {
|
||||
const chartOptions: ApexOptions = {
|
||||
chart: {
|
||||
type: "donut",
|
||||
fontFamily: "inherit",
|
||||
},
|
||||
colors: ["#5750F1", "#5475E5", "#8099EC", "#ADBCF2"],
|
||||
labels: data.map((item) => item.name),
|
||||
legend: {
|
||||
show: true,
|
||||
position: "bottom",
|
||||
itemMargin: {
|
||||
horizontal: 10,
|
||||
vertical: 5,
|
||||
},
|
||||
formatter: (legendName, opts) => {
|
||||
const { seriesPercent } = opts.w.globals;
|
||||
return `${legendName}: ${seriesPercent[opts.seriesIndex]}%`;
|
||||
},
|
||||
},
|
||||
plotOptions: {
|
||||
pie: {
|
||||
donut: {
|
||||
size: "80%",
|
||||
background: "transparent",
|
||||
labels: {
|
||||
show: true,
|
||||
total: {
|
||||
show: true,
|
||||
showAlways: true,
|
||||
label: "Visitors",
|
||||
fontSize: "16px",
|
||||
fontWeight: "400",
|
||||
},
|
||||
value: {
|
||||
show: true,
|
||||
fontSize: "28px",
|
||||
fontWeight: "bold",
|
||||
formatter: (val) => compactFormat(+val),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 2600,
|
||||
options: {
|
||||
chart: {
|
||||
width: 415,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
breakpoint: 640,
|
||||
options: {
|
||||
chart: {
|
||||
width: "100%",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
breakpoint: 370,
|
||||
options: {
|
||||
chart: {
|
||||
width: 260,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
return (
|
||||
<Chart
|
||||
options={chartOptions}
|
||||
series={data.map((item) => item.amount)}
|
||||
type="donut"
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { PeriodPicker } from "@/components/period-picker";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { getDevicesUsedData } from "@/services/charts.services";
|
||||
import { DonutChart } from "./chart";
|
||||
|
||||
type PropsType = {
|
||||
timeFrame?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export async function UsedDevices({
|
||||
timeFrame = "monthly",
|
||||
className,
|
||||
}: PropsType) {
|
||||
const data = await getDevicesUsedData(timeFrame);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"grid grid-cols-1 grid-rows-[auto_1fr] gap-9 rounded-[10px] bg-white p-7.5 shadow-1 dark:bg-gray-dark dark:shadow-card",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-wrap items-center justify-between gap-4">
|
||||
<h2 className="text-body-2xlg font-bold text-dark dark:text-white">
|
||||
Used Devices
|
||||
</h2>
|
||||
|
||||
<PeriodPicker defaultValue={timeFrame} sectionKey="used_devices" />
|
||||
</div>
|
||||
|
||||
<div className="grid place-items-center">
|
||||
<DonutChart data={data} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
"use client";
|
||||
|
||||
import type { ApexOptions } from "apexcharts";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
type PropsType = {
|
||||
data: {
|
||||
sales: { x: string; y: number }[];
|
||||
revenue: { x: string; y: number }[];
|
||||
};
|
||||
};
|
||||
|
||||
const Chart = dynamic(() => import("react-apexcharts"), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
export function WeeksProfitChart({ data }: PropsType) {
|
||||
const options: ApexOptions = {
|
||||
colors: ["#5750F1", "#0ABEF9"],
|
||||
chart: {
|
||||
type: "bar",
|
||||
stacked: true,
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
zoom: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 1536,
|
||||
options: {
|
||||
plotOptions: {
|
||||
bar: {
|
||||
borderRadius: 3,
|
||||
columnWidth: "25%",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
plotOptions: {
|
||||
bar: {
|
||||
horizontal: false,
|
||||
borderRadius: 3,
|
||||
columnWidth: "25%",
|
||||
borderRadiusApplication: "end",
|
||||
borderRadiusWhenStacked: "last",
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
|
||||
grid: {
|
||||
strokeDashArray: 5,
|
||||
xaxis: {
|
||||
lines: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
lines: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
xaxis: {
|
||||
axisBorder: {
|
||||
show: false,
|
||||
},
|
||||
axisTicks: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
position: "top",
|
||||
horizontalAlign: "left",
|
||||
fontFamily: "inherit",
|
||||
fontWeight: 500,
|
||||
fontSize: "14px",
|
||||
markers: {
|
||||
size: 9,
|
||||
shape: "circle",
|
||||
},
|
||||
},
|
||||
fill: {
|
||||
opacity: 1,
|
||||
},
|
||||
};
|
||||
return (
|
||||
<div className="-ml-3.5 mt-3">
|
||||
<Chart
|
||||
options={options}
|
||||
series={[
|
||||
{
|
||||
name: "Sales",
|
||||
data: data.sales,
|
||||
},
|
||||
{
|
||||
name: "Revenue",
|
||||
data: data.revenue,
|
||||
},
|
||||
]}
|
||||
type="bar"
|
||||
height={370}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { PeriodPicker } from "@/components/period-picker";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { getWeeksProfitData } from "@/services/charts.services";
|
||||
import { WeeksProfitChart } from "./chart";
|
||||
|
||||
type PropsType = {
|
||||
timeFrame?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export async function WeeksProfit({ className, timeFrame }: PropsType) {
|
||||
const data = await getWeeksProfitData(timeFrame);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"rounded-[10px] bg-white px-7.5 pt-7.5 shadow-1 dark:bg-gray-dark dark:shadow-card",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-wrap items-center justify-between gap-4">
|
||||
<h2 className="text-body-2xlg font-bold text-dark dark:text-white">
|
||||
Profit {timeFrame || "this week"}
|
||||
</h2>
|
||||
|
||||
<PeriodPicker
|
||||
items={["this week", "last week"]}
|
||||
defaultValue={timeFrame || "this week"}
|
||||
sectionKey="weeks_profit"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<WeeksProfitChart data={data} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user