Initial commit for new panel

This commit is contained in:
AmirReza Jamali
2025-09-09 11:20:26 +03:30
commit 1d4ccb3575
343 changed files with 20031 additions and 0 deletions
@@ -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>
);
}