337 lines
10 KiB
JavaScript
337 lines
10 KiB
JavaScript
import React, { useEffect, useState } from "react";
|
|
import {
|
|
getReports,
|
|
} from "../helpers/transactionHelpers.js";
|
|
import CircularDiagram from "./CircularDiagram";
|
|
import styles from "./Transactions.module.css";
|
|
import "./Switch.css";
|
|
|
|
import MultiSwitch from "react-multi-switch-toggle";
|
|
import DailyCharts from '../components/DailyCharts.js';
|
|
import PeriodCharts from '../components/PeriodCharts.js';
|
|
|
|
const RoundedRectangle = ({
|
|
onClick,
|
|
title,
|
|
value,
|
|
percentage,
|
|
invert,
|
|
fontSize = "15px",
|
|
loading = false,
|
|
children, // Assuming this is a React component or JSX
|
|
isChildren,
|
|
}) => {
|
|
const containerStyle = {
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
alignItems: "flex-start",
|
|
justifyContent: "center",
|
|
width: !children && !isChildren && "calc(100% / 2 - 10px)",
|
|
height: "auto",
|
|
borderRadius: "15px",
|
|
padding: "20px",
|
|
margin: "5px",
|
|
textAlign: "left",
|
|
fontFamily: "Arial, sans-serif",
|
|
boxSizing: "border-box",
|
|
backgroundColor: loading ? "rgb(127 127 127)" : 'white',
|
|
border: '1px solid #b3b1b1'
|
|
};
|
|
|
|
const titleStyle = {
|
|
fontWeight: "bold",
|
|
marginBottom: "10px",
|
|
width: "100%",
|
|
backgroundColor: loading
|
|
? "rgb(85 85 85)"
|
|
: !isChildren && !children && "inherit",
|
|
color: loading ? "transparent" : "inherit",
|
|
};
|
|
|
|
const valueAndPercentageContainerStyle = {
|
|
display: "flex",
|
|
flexDirection: "row",
|
|
justifyContent: "space-between",
|
|
alignItems: "center",
|
|
width: "100%",
|
|
};
|
|
|
|
const valueStyle = {
|
|
fontSize: loading ? "15px" : fontSize,
|
|
fontWeight: "bold",
|
|
flex: "1",
|
|
textAlign: "left",
|
|
color: loading ? "transparent" : "inherit",
|
|
backgroundColor: loading ? "rgb(85 85 85)" : "transparent",
|
|
overflow: "hidden",
|
|
textOverflow: "ellipsis",
|
|
whiteSpace: "nowrap",
|
|
};
|
|
|
|
const percentageStyle = {
|
|
fontSize: "16px",
|
|
display: "flex",
|
|
alignItems: "center",
|
|
textAlign: "right",
|
|
color: loading ? "black" : percentage >= 0 ? (invert ? "red" : "#2fd45e") : (invert ? "#2fd45e" : "red"),
|
|
};
|
|
|
|
const arrowStyle = {
|
|
marginLeft: "5px",
|
|
};
|
|
|
|
return (
|
|
<div style={containerStyle} onClick={onClick}>
|
|
<div style={titleStyle}>{title}</div>
|
|
{!children && (
|
|
<div style={valueAndPercentageContainerStyle}>
|
|
<div style={valueStyle}>{loading ? "Loading..." : value}</div>
|
|
<div style={percentageStyle}>
|
|
{loading ? "" : percentage}
|
|
{percentage !== undefined && !loading && "%"}
|
|
{percentage !== undefined && !loading && (
|
|
<span style={arrowStyle}>
|
|
{percentage > 0 ? "↗" : percentage === 0 ? "-" : "↘"}
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
{children && <div>{children}</div>} {/* Properly render children */}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const App = ({ cafeId,
|
|
handleClose }) => {
|
|
const [analytics, setAnalytics] = useState({});
|
|
const [loading, setLoading] = useState(true);
|
|
const [filter, setFilter] = useState("yesterday");
|
|
|
|
const fetchData = async (filter) => {
|
|
try {
|
|
setLoading(true);
|
|
// Fetch the analytics data with the selected filter
|
|
const analyticsData = await getReports(cafeId, filter);
|
|
console.log(analyticsData);
|
|
if (analyticsData) setAnalytics(analyticsData);
|
|
} catch (error) {
|
|
console.error("Error fetching data:", error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchData(filter); // Fetch data when filter changes
|
|
}, [filter]);
|
|
|
|
const filteredItems = analytics.itemSales || [];
|
|
|
|
const colors = [
|
|
"#E63946", // Bright Red
|
|
"#F4A261", // Soft Orange
|
|
"#FFD166", // Sunshine Yellow
|
|
"#06D6A0", // Mint Green
|
|
"#118AB2", // Rich Teal
|
|
"#9D4EDD", // Vivid Violet
|
|
"#FF66B3", // Bubblegum Pink
|
|
"#FF7F50", // Coral
|
|
"#FFA07A", // Light Salmon
|
|
"#F7CAD0", // Blush Pink
|
|
"#A8DADC", // Aqua Blue
|
|
"#457B9D", // Steel Blue
|
|
"#2A9D8F", // Jungle Green
|
|
"#FFB703", // Mustard Yellow
|
|
"#E9C46A", // Honey Gold
|
|
"#264653", // Charcoal Green
|
|
"#D4A5A5", // Rose Beige
|
|
"#A6D6F0", // Sky Blue
|
|
"#BC4749", // Cranberry Red
|
|
];
|
|
|
|
|
|
const segments = filteredItems.map((item, index) => ({
|
|
percentage: item.percentage,
|
|
color: colors[index] || "#cccccc",
|
|
}));
|
|
|
|
const formatIncome = (amount) => {
|
|
if (amount >= 1_000_000_000) {
|
|
// Format for billions
|
|
const billions = amount / 1_000_000_000;
|
|
return billions.toFixed(0) + "b"; // No decimal places for billions
|
|
} else if (amount >= 1_000_000) {
|
|
// Format for millions
|
|
const millions = amount / 1_000_000;
|
|
return millions.toFixed(2).replace(/\.00$/, "") + "m"; // Two decimal places, remove trailing '.00'
|
|
} else if (amount >= 1_000) {
|
|
// Format for thousands
|
|
const thousands = amount / 1_000;
|
|
return thousands.toFixed(1).replace(/\.0$/, "") + "k"; // One decimal place, remove trailing '.0'
|
|
} else {
|
|
// Less than a thousand
|
|
if (amount != null) return amount.toString();
|
|
}
|
|
};
|
|
|
|
function roundToInteger(num) {
|
|
return Math.round(num);
|
|
}
|
|
|
|
function onToggle(selectedItem) {
|
|
const filterMap = ["yesterday", "weekly", "monthly", "yearly"];
|
|
setFilter(filterMap[selectedItem]);
|
|
}
|
|
|
|
const filterTexts = ["1", "7", "30", "365"];
|
|
const comparisonText =
|
|
filterTexts[["yesterday", "weekly", "monthly", "yearly"].indexOf(filter)];
|
|
// const formatDate = (isoDateString) => {
|
|
// const date = new Date(isoDateString);
|
|
// return date.toLocaleDateString("en-US", {
|
|
// year: "numeric",
|
|
// month: "long",
|
|
// day: "numeric",
|
|
// hour: "numeric",
|
|
// });
|
|
// };
|
|
|
|
return (
|
|
<div style={{
|
|
position: 'fixed',
|
|
height: '100vh',
|
|
width: '100vw',
|
|
top: 0,
|
|
right: 0,
|
|
backgroundColor: 'white',
|
|
overflowY: 'auto'
|
|
}}
|
|
>
|
|
<h2 className={styles["Transactions-title"]}>Laporan</h2>
|
|
<div style={{
|
|
textAlign: "center",
|
|
marginTop: '30px'
|
|
}}>
|
|
<MultiSwitch
|
|
texts={["kemarin", "minggu ini", "bulan ini", "tahun ini"]}
|
|
selectedSwitch={["yesterday", "weekly", "monthly", "yearly"].indexOf(
|
|
filter
|
|
)}
|
|
bgColor={'#f4efe6'}
|
|
borderColor={'transparent'}
|
|
borderWidth={0.1}
|
|
onToggleCallback={onToggle}
|
|
fontColor={"#af9463"}
|
|
selectedFontColor={"black"}
|
|
selectedSwitchColor={"white"}
|
|
eachSwitchWidth={70}
|
|
height={"25px"}
|
|
fontSize={"12px"}
|
|
/>
|
|
<div
|
|
style={{
|
|
display: "flex",
|
|
flexWrap: "wrap",
|
|
justifyContent: "center",
|
|
padding: "20px",
|
|
}}
|
|
>
|
|
<RoundedRectangle
|
|
title="Pendapatan"
|
|
fontSize="12px"
|
|
value={!loading && "Rp" + formatIncome(analytics?.currentTotals?.income)}
|
|
percentage={roundToInteger(analytics?.growth?.incomeGrowth)}
|
|
invert={false}
|
|
loading={loading}
|
|
/>
|
|
|
|
<RoundedRectangle
|
|
title="Pengeluaran"
|
|
fontSize="12px"
|
|
value={!loading && "Rp" + formatIncome(analytics?.currentTotals?.outcome)}
|
|
percentage={roundToInteger(analytics?.growth?.outcomeGrowth)}
|
|
invert={true}
|
|
loading={loading}
|
|
/>
|
|
<RoundedRectangle
|
|
title="Transaksi"
|
|
value={analytics?.currentTotals?.transactions}
|
|
percentage={roundToInteger(analytics?.growth?.transactionGrowth)}
|
|
invert={false}
|
|
loading={loading}
|
|
/>
|
|
{analytics?.itemSales &&
|
|
analytics?.itemSales.length > 0 && (
|
|
<RoundedRectangle
|
|
title={"Item favorit"}
|
|
value={analytics?.itemSales[0]?.itemName}
|
|
loading={loading}
|
|
/>
|
|
)}
|
|
{(!analytics?.itemSales ||
|
|
analytics?.itemSales.length === 0) && (
|
|
<RoundedRectangle
|
|
title={"Item favorit"}
|
|
value={"-"}
|
|
loading={loading}
|
|
/>
|
|
)}
|
|
<div
|
|
style={{ display: "flex", alignItems: "center", margin: "10px" }}
|
|
>
|
|
<div style={{ marginRight: "5px", fontSize: "1.2em" }}>ⓘ</div>
|
|
<h6 style={{ margin: 0, textAlign: "left" }}>
|
|
Persentase pertumbuhan dihitung dengan membandingkan {" "}
|
|
{comparisonText} hari terakhir dengan {comparisonText} hari sebelumnya.
|
|
</h6>
|
|
</div>
|
|
</div>
|
|
<div
|
|
style={{
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "space-between",
|
|
padding: "20px",
|
|
}}
|
|
>
|
|
<div style={{ flex: 1 }}>
|
|
<CircularDiagram segments={segments} />
|
|
</div>
|
|
<div style={{ flex: 1, marginLeft: "20px" }}>
|
|
{filteredItems.map((item, index) => (
|
|
<div
|
|
key={index}
|
|
style={{
|
|
display: "flex",
|
|
alignItems: "center",
|
|
margin: "10px",
|
|
}}
|
|
>
|
|
<div
|
|
style={{
|
|
marginRight: "5px",
|
|
fontSize: "1.2em",
|
|
color: colors[index],
|
|
}}
|
|
>
|
|
★
|
|
</div>
|
|
<h5 style={{ margin: 0, textAlign: "left" }}>{item.itemName}</h5>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
{filter == 'yesterday' || filter == 'weekly' ?
|
|
<DailyCharts transactionGraph={analytics?.transactionGraph} type={filter} colors={colors}/>
|
|
:
|
|
<PeriodCharts type={filter} aggregatedCurrentReports={analytics?.aggregatedCurrentReports} aggregatedPreviousReports={analytics?.aggregatedPreviousReports} colors={colors}/>
|
|
}
|
|
</div>
|
|
<div class="ItemLister_PaymentOption__YZlDL"><div style={{ marginTop: '20px' }} onClick={handleClose} class="ItemLister_Pay2Button__+MIxX">Kembali</div></div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default App;
|