148 lines
6.0 KiB
TypeScript
148 lines
6.0 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import { UsersIcon, UserCheckIcon } from "lucide-react";
|
|
import { apiConfig } from '@/config/api.config';
|
|
import { useCallApi } from '@/hooks';
|
|
|
|
const API_URL = apiConfig.api_dashboard;
|
|
|
|
interface Props {
|
|
startdate: string;
|
|
enddate: string;
|
|
}
|
|
|
|
const polarToCartesian = (cx: number, cy: number, radius: number, angleInDegrees: number) => {
|
|
const angleInRadians = (angleInDegrees * Math.PI) / 180.0;
|
|
return {
|
|
x: cx + radius * Math.cos(angleInRadians),
|
|
y: cy + radius * Math.sin(angleInRadians),
|
|
};
|
|
};
|
|
|
|
const describeArc = (
|
|
x: number,
|
|
y: number,
|
|
radius: number,
|
|
startAngle: number,
|
|
endAngle: number
|
|
) => {
|
|
const start = polarToCartesian(x, y, radius, endAngle);
|
|
const end = polarToCartesian(x, y, radius, startAngle);
|
|
const largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";
|
|
|
|
return [
|
|
"M", start.x, start.y,
|
|
"A", radius, radius, 0, largeArcFlag, 0, end.x, end.y,
|
|
].join(" ");
|
|
};
|
|
|
|
const MemberActivity = ({ startdate, enddate }: Props) => {
|
|
const { GetData } = useCallApi();
|
|
const [responseTransactionValue, setResponseTransactionValue] = useState<any>(null);
|
|
|
|
useEffect(() => {
|
|
const fetchData = async () => {
|
|
try {
|
|
const res = await GetData(`${API_URL}/active-user`, {
|
|
date_from: startdate,
|
|
date_to: enddate,
|
|
});
|
|
setResponseTransactionValue(res);
|
|
} catch (error) {
|
|
console.error('Error fetching data:', error);
|
|
}
|
|
};
|
|
|
|
fetchData();
|
|
}, [startdate, enddate, GetData]);
|
|
|
|
const percentage = parseFloat((responseTransactionValue?.data?.total_customer_active_percentage ?? 0).toFixed(2));
|
|
const totalCustomer = responseTransactionValue?.data?.total_customer ?? 0;
|
|
const activeCustomer = responseTransactionValue?.data?.total_customer_active ?? 0;
|
|
const reguler = responseTransactionValue?.data?.reguler ?? 0;
|
|
const premium = responseTransactionValue?.data?.premium ?? 0;
|
|
const agent = responseTransactionValue?.data?.agent ?? 0;
|
|
|
|
// Calculate pointer angle
|
|
const angle = (percentage / 100) * 180;
|
|
const pointerX = 50 + 25 * Math.cos((angle - 180) * Math.PI / 180);
|
|
const pointerY = 50 + 25 * Math.sin((angle - 180) * Math.PI / 180);
|
|
|
|
return (
|
|
<div className="p-6 bg-white rounded-lg shadow-md w-full max-w-md">
|
|
<div className="pb-3 mb-4">
|
|
<h2 className="text-lg font-semibold text-gray-700">Member Activity</h2>
|
|
</div>
|
|
|
|
<div className="flex gap-6">
|
|
{/* Sidebar Info */}
|
|
<ul className="space-y-4 w-1/2 text-gray-700 text-sm">
|
|
<li className="flex items-center gap-2">
|
|
<UsersIcon className="w-4 h-4" />
|
|
<span>Total Customer: {totalCustomer}</span>
|
|
</li>
|
|
<li className="flex items-center gap-2">
|
|
<UserCheckIcon className="w-4 h-4" />
|
|
<span>Active Customer: {activeCustomer}</span>
|
|
</li>
|
|
<li className="flex items-center gap-2">
|
|
<UserCheckIcon className="w-4 h-4" color='blue' />
|
|
<span>Reguler: {reguler}</span>
|
|
</li>
|
|
<li className="flex items-center gap-2">
|
|
<UserCheckIcon className="w-4 h-4" color='green' />
|
|
<span>Premium: {premium}</span>
|
|
</li>
|
|
<li className="flex items-center gap-2">
|
|
<UserCheckIcon className="w-4 h-4" color='orange' />
|
|
<span>Agent: {agent}</span>
|
|
</li>
|
|
</ul>
|
|
|
|
{/* Gauge Chart */}
|
|
<div className="w-1/2">
|
|
<div className="border border-gray-200 rounded-md p-4 text-center">
|
|
<h3 className="text-sm text-gray-600 font-medium mb-2">Active User</h3>
|
|
<div className="relative h-24 w-full">
|
|
<svg viewBox="0 0 100 50" className="w-full h-full">
|
|
{/* Background arc */}
|
|
<path
|
|
d="M 10 50 A 40 40 0 0 1 90 50"
|
|
fill="none"
|
|
stroke="#e5e7eb"
|
|
strokeWidth="10"
|
|
/>
|
|
{/* Active arc */}
|
|
{percentage > 0 && (
|
|
<path
|
|
d={describeArc(50, 50, 40, 180, 180 + (percentage * 180 / 100))}
|
|
fill="none"
|
|
stroke="#34d399"
|
|
strokeWidth="10"
|
|
strokeLinecap="round"
|
|
/>
|
|
)}
|
|
{/* Pointer */}
|
|
<line
|
|
x1="50"
|
|
y1="50"
|
|
x2={pointerX}
|
|
y2={pointerY}
|
|
stroke="#111827"
|
|
strokeWidth="4"
|
|
strokeLinecap="round"
|
|
/>
|
|
</svg>
|
|
<div className="flex justify-between text-xs text-gray-500 px-1 mt-1">
|
|
<span>{percentage}%</span>
|
|
<span>100%</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default MemberActivity;
|