This commit is contained in:
Vassshhh
2025-08-31 18:59:15 +07:00
parent 026813d1e0
commit 222169be74
10 changed files with 202 additions and 170 deletions

View File

@@ -819,8 +819,10 @@ function App() {
welcomePageConfig={shop.welcomePageConfig} welcomePageConfig={shop.welcomePageConfig}
onClose={closeModal} onClose={closeModal}
setModal={setModal} setModal={setModal}
setIsModalOpen={setIsModalOpen}
onModalCloseFunction={onModalCloseFunction} onModalCloseFunction={onModalCloseFunction}
onModalYesFunction={onModalYesFunction} onModalYesFunction={onModalYesFunction}
shopIdentifier={shopIdentifier}
/> />
</div> </div>
); );

View File

@@ -43,7 +43,7 @@ const DailyCharts = ({ incomeGraph, transactionGraph, materialGraph, colors, typ
Array.isArray(transactions) ? transactions.reduce((acc, t) => acc + t.totalPrice, 0) : transactions?.income || 0; Array.isArray(transactions) ? transactions.reduce((acc, t) => acc + t.totalPrice, 0) : transactions?.income || 0;
const sumOutcome = (transactions) => const sumOutcome = (transactions) =>
Array.isArray(transactions) ? transactions.reduce((acc, t) => acc + (t.materialOutcome || t.price), 0) : transactions?.outcome || 0; Array.isArray(transactions) ? transactions.reduce((acc, t) => acc + (t.materialOutcome || t.price * t.stockDifference), 0) : transactions?.outcome || 0;
let seriesData = [] let seriesData = []
@@ -83,16 +83,19 @@ const DailyCharts = ({ incomeGraph, transactionGraph, materialGraph, colors, typ
sumOutcome(dayData?.hour21To24MaterialIds), sumOutcome(dayData?.hour21To24MaterialIds),
]; ];
} }
let totalValue = seriesData.reduce((acc, val) => acc + val, 0);
return { return {
date: new Date(dayData.date).toLocaleDateString(), date: new Date(dayData.date).toLocaleDateString(),
categories, categories,
series: [ series: [
{ {
name: graphFilter === 'transactions' ? 'Transaksi' : (graphFilter === 'Pemasukan' ? 'Pemasukan' : 'Pengeluaran'), name: graphFilter === 'transactions' ? 'Transaksi' : (graphFilter === 'income' ? 'Pemasukan' : 'Pengeluaran'),
data: seriesData, data: seriesData,
}, },
], ],
totalValue, // ⬅️ Tambahkan ini
}; };
}); });
}; };
@@ -144,7 +147,8 @@ const DailyCharts = ({ incomeGraph, transactionGraph, materialGraph, colors, typ
type == 'yesterday' && selectedIndex == -1 || type != 'yesterday' && selectedIndex !== index ? setSelectedIndex(index) : setSelectedIndex(-1) type == 'yesterday' && selectedIndex == -1 || type != 'yesterday' && selectedIndex !== index ? setSelectedIndex(index) : setSelectedIndex(-1)
} }
> >
<div style={{ position: 'absolute', bottom: 0, left: '10%', right: '10%', borderBottom: index == indexx ? `2px solid ${colors[index % colors.length]}` : 'none' }}></div>
<div style={{ position: 'absolute', bottom: '28px', left: '10%', right: '10%', borderBottom: index == indexx ? `2px solid ${colors[index % colors.length]}` : 'none' }}></div>
<div <div
style={{ color: index === indexx ? 'black' : 'transparent' }}> style={{ color: index === indexx ? 'black' : 'transparent' }}>
{indexx !== chartData.length - 1 ? ( {indexx !== chartData.length - 1 ? (
@@ -153,10 +157,18 @@ const DailyCharts = ({ incomeGraph, transactionGraph, materialGraph, colors, typ
{(indexx === 0 || (formatDate(chartData[indexx - 1].date).month !== month && type != 'weekly')) && month} {(indexx === 0 || (formatDate(chartData[indexx - 1].date).month !== month && type != 'weekly')) && month}
</> </>
) : ( ) : (
'Hari ini' <>
{type != 'weekly' ? 'Hari ini' : day}
</>
)} )}
</div> </div>
{index == indexx && <p style={{ margin: '7px 0 0 0', fontSize: '12px', color: 'black' }}>
{graphFilter === 'transactions'
? chartData[indexx].totalValue
: formatRupiah(chartData[indexx].totalValue)}
</p>}
</div> </div>
); );
})} })}
</div> </div>
@@ -181,10 +193,11 @@ const DailyCharts = ({ incomeGraph, transactionGraph, materialGraph, colors, typ
} else { } else {
return formatRupiah(val); // format Rupiah return formatRupiah(val); // format Rupiah
} }
}, },
style: { style: {
colors: [(index == chartData.length - 1 || selectedIndex != -1) ? "#000" : "transparent"], colors: [(index == chartData.length - 1 || selectedIndex != -1) ? "#000" : "transparent"],
fontSize: '10px', fontSize: '7px',
}, },
offsetY: -10, offsetY: -10,
background: { background: {

View File

@@ -35,7 +35,7 @@ import CreateCoupon from "../pages/CreateCoupon";
import CheckCoupon from "../pages/CheckCoupon"; import CheckCoupon from "../pages/CheckCoupon";
import CreateUserWithCoupon from "../pages/CreateUserWithCoupon"; import CreateUserWithCoupon from "../pages/CreateUserWithCoupon";
const Modal = ({ user, shop, isOpen, onClose, modalContent, deviceType, setModal, handleMoveToTransaction, depth,welcomePageConfig, onModalCloseFunction, onModalYesFunction }) => { const Modal = ({ user, shop, shopIdentifier, isOpen, onClose, modalContent, deviceType, setModal, setIsModalOpen, handleMoveToTransaction, depth,welcomePageConfig, onModalCloseFunction, onModalYesFunction }) => {
const [shopImg, setShopImg] = useState(''); const [shopImg, setShopImg] = useState('');
const [updateKey, setUpdateKey] = useState(0); const [updateKey, setUpdateKey] = useState(0);
@@ -88,10 +88,10 @@ const Modal = ({ user, shop, isOpen, onClose, modalContent, deviceType, setModal
{modalContent === "create_tenant" && <CreateTenant shopId={shop.cafeId} />} {modalContent === "create_tenant" && <CreateTenant shopId={shop.cafeId} />}
{modalContent === "edit_tables" && <TablesPage shop={shop} />} {modalContent === "edit_tables" && <TablesPage shop={shop} />}
{modalContent === "new_transaction" && ( {modalContent === "new_transaction" && (
<Transaction propsShopId={shop.cafeId} handleMoveToTransaction={handleMoveToTransaction} depth={depth} shopImg={shopImg} setModal={setModal}/> <Transaction propsShopId={shop.cafeId} setIsModalOpen={setIsModalOpen} cafeIdentityName={shopIdentifier} handleMoveToTransaction={handleMoveToTransaction} depth={depth} shopImg={shopImg} setModal={setModal}/>
)} )}
{modalContent === "transaction_canceled" && ( {modalContent === "transaction_canceled" && (
<Transaction propsShopId={shop.cafeId} /> <Transaction propsShopId={shop.cafeId} setIsModalOpen={setIsModalOpen} cafeIdentityName={shopIdentifier} />
)} )}
{modalContent === "transaction_pending" && <Transaction_pending deviceType={deviceType} setModal={setModal}/>} {modalContent === "transaction_pending" && <Transaction_pending deviceType={deviceType} setModal={setModal}/>}
{modalContent === "transaction_item" && <Transaction_item />} {modalContent === "transaction_item" && <Transaction_item />}

View File

@@ -7,182 +7,195 @@ const PeriodCharts = ({ type, graphFilter, aggregatedCurrentReports, aggregatedP
useEffect(() => { useEffect(() => {
setSelectedIndex(-1); setSelectedIndex(-1);
}, [aggregatedCurrentReports, aggregatedPreviousReports]); }, [aggregatedCurrentReports, aggregatedPreviousReports, graphFilter]);
const monthly = ["1 - 7", "8 - 14", "15 - 21", "22 - 28", "29 - 31"]; const monthly = ["1 - 7", "8 - 14", "15 - 21", "22 - 28", "29 - 31"];
const yearly = ["Kuartal 1", "Kuartal 2", "Kuartal 3", "Kuartal 4"]; const yearly = ["Kuartal 1", "Kuartal 2", "Kuartal 3", "Kuartal 4"];
const cat = type == 'monthly' ? monthly : yearly; const cat = type === "monthly" ? monthly : yearly;
// Helper Rupiah formatter
const formatRupiah = (number) => {
if (number === null || number === undefined) return "Rp 0";
return new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(number);
};
let currentIncomeData,
currentOutcomeData,
currentTransactionData,
previousIncomeData,
previousOutcomeData,
previousTransactionData = null;
// Map the data for the current reports
let currentIncomeData, currentOutcomeData, currentTransactionData, previousIncomeData, previousOutcomeData, previousTransactionData = null;
if (aggregatedCurrentReports) { if (aggregatedCurrentReports) {
currentIncomeData = aggregatedCurrentReports.map((report) => report.income); currentIncomeData = aggregatedCurrentReports.map((r) => r.income);
currentOutcomeData = aggregatedCurrentReports.map((report) => report.outcome); currentOutcomeData = aggregatedCurrentReports.map((r) => r.outcome);
currentTransactionData = aggregatedCurrentReports.map((report) => report.transactions); currentTransactionData = aggregatedCurrentReports.map((r) => r.transactions);
if (type == 'monthly' && currentIncomeData.length === 4) { if (type === "monthly" && currentIncomeData.length === 4) {
currentIncomeData.push(null); currentIncomeData.push(null);
}
if (type == 'monthly' && currentOutcomeData.length === 4) {
currentOutcomeData.push(null); currentOutcomeData.push(null);
}
if (type == 'monthly' && currentTransactionData.length === 4) {
currentTransactionData.push(null); currentTransactionData.push(null);
} }
} }
if (aggregatedPreviousReports) {
// Map the data for the previous reports
previousIncomeData = aggregatedPreviousReports.map((report) => report.income);
previousOutcomeData = aggregatedPreviousReports.map((report) => report.outcome);
previousTransactionData = aggregatedPreviousReports.map((report) => report.transactions);
if (type == 'monthly' && previousIncomeData.length === 4) { if (aggregatedPreviousReports) {
previousIncomeData = aggregatedPreviousReports.map((r) => r.income);
previousOutcomeData = aggregatedPreviousReports.map((r) => r.outcome);
previousTransactionData = aggregatedPreviousReports.map((r) => r.transactions);
if (type === "monthly" && previousIncomeData.length === 4) {
previousIncomeData.push(null); previousIncomeData.push(null);
}
if (type == 'monthly' && previousOutcomeData.length === 4) {
previousOutcomeData.push(null); previousOutcomeData.push(null);
}
if (type == 'monthly' && previousTransactionData.length === 4) {
previousTransactionData.push(null); previousTransactionData.push(null);
} }
} }
let globalMax = null;
if (aggregatedCurrentReports || aggregatedPreviousReports) { // cari global max untuk y-axis
// Find the global maximum for the y-axis let globalMax = 0;
globalMax = Math.max( if (aggregatedCurrentReports || aggregatedPreviousReports) {
...(graphFilter === 'income' const dataset =
? [...currentIncomeData, ...previousIncomeData] graphFilter === "income"
: graphFilter === 'outcome' ? [...(currentIncomeData || []), ...(previousIncomeData || [])]
? [...currentOutcomeData, ...previousOutcomeData] : graphFilter === "outcome"
: [...currentTransactionData, ...previousTransactionData]) ? [...(currentOutcomeData || []), ...(previousOutcomeData || [])]
); : [...(currentTransactionData || []), ...(previousTransactionData || [])];
} globalMax = Math.max(...dataset);
}
const getSeries = (isCurrent) => {
if (graphFilter === "income")
return isCurrent ? currentIncomeData : previousIncomeData;
if (graphFilter === "outcome")
return isCurrent ? currentOutcomeData : previousOutcomeData;
return isCurrent ? currentTransactionData : previousTransactionData;
};
return ( return (
<div className={`${styles.chartItemContainer} ${selectedIndex !== -1 ? styles.expanded : ''}`}> <div
{aggregatedPreviousReports && ( className={`${styles.chartItemContainer} ${selectedIndex !== -1 ? styles.expanded : ""
<div className={`${styles.chartItemWrapper} ${selectedIndex !== -1 && selectedIndex !== 0 }`}
? styles.chartItemWrapperActive >
: styles.chartItemWrapperInactive {[aggregatedPreviousReports, aggregatedCurrentReports].map(
}`}> (dataset, i) =>
dataset && (
<div className={styles.dateSelectorWrapper}>
<div className={styles.dateSelector}
onClick={() =>
selectedIndex === -1 ? setSelectedIndex(0) : setSelectedIndex(-1)
}
style={{ color: 'black', position: 'relative' }}
>
<div style={{ position: 'absolute', bottom: 0, left: '10%', right: '10%', borderBottom: `2px solid ${colors[0]}` }}></div>
<div>{type == 'monthly' ? 'bulan lalu' : 'tahun lalu'}</div>
</div>
<div <div
className={`${styles.dateSelector} ${styles.dateSelectorInactive key={i}
className={`${styles.chartItemWrapper} ${selectedIndex !== -1 && selectedIndex !== i
? styles.chartItemWrapperActive
: styles.chartItemWrapperInactive
}`} }`}
onClick={() =>
selectedIndex === 0 ? setSelectedIndex(-1) : setSelectedIndex(1)
}>
<div>{type == 'monthly' ? 'bulan ini' : 'tahun ini'}</div>
</div>
</div>
<div className={styles.chartWrapper}>
<Chart
options={{
tooltip: { enabled: false },
chart: { type: "area", zoom: { enabled: false }, toolbar: { show: false } },
xaxis: {
categories: cat,
axisBorder: {
show: false, // Removes the x-axis line
},
axisTicks: {
show: false, // Removes the ticks on the x-axis
},
labels: {
style: {
colors: ['black', 'black', 'black', 'black', aggregatedPreviousReports?.length == 4 ? 'transparent' : 'black'],
}
}
},
yaxis: { max: globalMax, min: 0, labels: {
maxWidth: 20, style: { colors: "transparent" } } },
grid: { show: false },
fill: { opacity: 0.5 },
colors: [colors[0]],
}}
series={[
// { name: "Pemasukan", data: previousIncomeData },
// { name: "Pengaluaran", data: previousOutcomeData },
{ name: "Total transaksi", data: graphFilter == 'income' ? previousIncomeData : graphFilter == 'outcome' ? previousOutcomeData : previousTransactionData },
]}
type="area"
height={200}
width="100%"
/>
</div>
</div>
)}
{aggregatedCurrentReports && (
<div className={`${styles.chartItemWrapper} ${selectedIndex !== -1 && selectedIndex !== 1
? styles.chartItemWrapperActive
: styles.chartItemWrapperInactive
}`}>
<div className={styles.dateSelectorWrapper}>
<div
className={`${styles.dateSelector} ${styles.dateSelectorInactive
}`}
onClick={() =>
selectedIndex === 1 ? setSelectedIndex(-1) : setSelectedIndex(0)
}>
<div>{type == 'monthly' ? 'bulan lalu' : 'tahun lalu'}</div>
</div>
<div className={styles.dateSelector}
onClick={() =>
selectedIndex === -1 ? setSelectedIndex(1) : setSelectedIndex(-1)
}
style={{ color: 'black', position: 'relative' }}
> >
<div style={{ position: 'absolute', bottom: 0, left: '10%', right: '10%', borderBottom: `2px solid ${colors[1]}` }}></div> <div className={styles.dateSelectorWrapper}>
<div>{type == 'monthly' ? 'bulan ini' : 'tahun ini'}</div> {[0, 1].map((idx) => (
</div> <div
</div> key={idx}
<div className={styles.chartWrapper}> className={`${styles.dateSelector} ${idx === i ? styles.dateSelectorActive : styles.dateSelectorInactive
<Chart }`}
options={{ style={{ position: "relative" }}
tooltip: { enabled: false }, onClick={() =>
chart: { type: "area", zoom: { enabled: false }, toolbar: { show: false } }, selectedIndex == idx ? setSelectedIndex(-1) : setSelectedIndex(idx)
xaxis: {
categories: cat,
axisBorder: {
show: false, // Removes the x-axis line
},
axisTicks: {
show: false, // Removes the ticks on the x-axis
},
labels: {
style: {
colors: ['black', 'black', 'black', 'black', aggregatedCurrentReports?.length == 4 ? 'transparent' : 'black'],
} }
} >
}, {idx === i && (
yaxis: { max: globalMax, min: 0, labels: { maxWidth: 20, style: { colors: "transparent" } } }, <div
grid: { show: false }, style={{
fill: { opacity: 0.5 }, position: "absolute",
colors: [colors[1]], bottom: 0,
}} left: "10%",
series={[ right: "10%",
// { name: "Pemasukan", data: currentIncomeData }, borderBottom: `2px solid ${colors[i]}`,
// { name: "Pengeluaran", data: currentOutcomeData }, }}
{ name: "Total transaksi", data: graphFilter == 'income' ? currentIncomeData : graphFilter == 'outcome' ? currentOutcomeData : currentTransactionData }, ></div>
]} )}
type="area" <div style={{ color: idx === i ? "black" : "transparent" }}>
height={200} {type === "monthly"
width="100%" ? idx === 0
/> ? "bulan lalu"
</div> : "bulan ini"
</div> : idx === 0
? "tahun lalu"
: "tahun ini"}
</div>
</div>
))}
</div>
<div className={styles.chartWrapper}>
<Chart
options={{
tooltip: {
enabled: true,
y: {
formatter: (val) =>
graphFilter === "transactions" ? val : formatRupiah(val),
},
},
dataLabels: {
enabled: true,
formatter: (val) =>
graphFilter === "transactions" ? val : formatRupiah(val),
style: {
colors: ["#000"], // <- Selalu tampil hitam
fontSize: "10px",
},
offsetY: -10,
background: {
enabled: true, // <- Selalu tampil background label
},
},
chart: {
type: "area",
zoom: { enabled: false },
toolbar: { show: false },
},
xaxis: {
categories: cat,
labels: {
style: {
colors: cat.map(() =>
i === selectedIndex || selectedIndex === -1 ? "#000" : "transparent"
),
},
},
},
yaxis: {
max: globalMax,
min: 0,
labels: {
maxWidth: 20,
style: { colors: "transparent" },
formatter: (val) => formatRupiah(val),
},
},
grid: { show: false },
fill: { opacity: 0.5 },
colors: [colors[i]],
}}
series={[
{
name:
graphFilter === "transactions"
? "Transaksi"
: graphFilter === "income"
? "Pemasukan"
: "Pengeluaran",
data: getSeries(i === 1),
},
]}
type="area"
height={200}
width="100%"
/>
</div>
</div>
)
)} )}
</div> </div>
); );

View File

@@ -19,7 +19,7 @@ const HorizontalBarDiagram = ({ segments, width = 300 }) => {
style={{ display: "block", margin: "0 auto" }} style={{ display: "block", margin: "0 auto" }}
> >
{visibleSegments.map((segment, index) => { {visibleSegments.map((segment, index) => {
const { name, value, color } = segment; const { name, value, color, unit} = segment;
const percentage = total > 0 ? ((value / total) * 100).toFixed(1) : 0; const percentage = total > 0 ? ((value / total) * 100).toFixed(1) : 0;
const barWidth = (width * percentage) / 100; const barWidth = (width * percentage) / 100;
@@ -61,7 +61,7 @@ const HorizontalBarDiagram = ({ segments, width = 300 }) => {
fontWeight="bold" fontWeight="bold"
style={{ pointerEvents: "none", textTransform: "capitalize" }} style={{ pointerEvents: "none", textTransform: "capitalize" }}
> >
{`${name}: ${value} (${percentage}%)`} {unit ? (name + ' ( ' + value +' '+ unit+')') : (name + ' ' + value +' ('+ percentage + '%)')}
</text> </text>
</g> </g>
); );

View File

@@ -234,7 +234,7 @@ const LinktreePage = ({ user, setModal }) => {
return ( return (
<> <>
{user && user.roleId < 2 ? ( {getLocalStorage("auth") ? (
<div> <div>
<div className={styles.header}> <div className={styles.header}>

View File

@@ -180,7 +180,7 @@ const SetPaymentQr = ({ cafeId }) => {
setLatestMutation(latestMutation); setLatestMutation(latestMutation);
setCurrentQuantity(latestMutation.newStock); setCurrentQuantity(latestMutation.newStock);
setCurrentPrice(formatCurrency(latestMutation.priceAtp)); setCurrentPrice(formatCurrency(latestMutation.priceAtp));
setNewPrice(formatCurrency(latestMutation.priceAtp)); setNewPrice(formatCurrency(latestMutation.priceAtp) || 0);
} else { } else {
setCurrentQuantity(0); // Default value if no mutations exist setCurrentQuantity(0); // Default value if no mutations exist
setLatestMutation({ newStock: 0 }); setLatestMutation({ newStock: 0 });
@@ -195,12 +195,12 @@ const SetPaymentQr = ({ cafeId }) => {
const handleUpdateStock = async () => { const handleUpdateStock = async () => {
setLoading(true); setLoading(true);
console.log('aaa')
try { try {
const newprice = convertToInteger(newPrice)
const newStock = currentQuantity + quantityChange; const newStock = currentQuantity + quantityChange;
const formData = new FormData(); const formData = new FormData();
formData.append("newStock", newStock); formData.append("newStock", newStock);
formData.append("priceAtp", newprice); formData.append("priceAtp", newPrice);
formData.append("reason", "Stock update"); formData.append("reason", "Stock update");
await createMaterialMutation(materials[selectedMaterialIndex].materialId, formData); await createMaterialMutation(materials[selectedMaterialIndex].materialId, formData);

View File

@@ -240,6 +240,7 @@ let materialSegments =
return { return {
name: item.materialName, name: item.materialName,
value: item.spend, value: item.spend,
unit: item.unit,
color, color,
}; };
}); });
@@ -250,6 +251,8 @@ let materialSegments =
return { return {
name: item.materialName, name: item.materialName,
value: item.spend, value: item.spend,
unit: item.unit,
color, color,
}; };
}); });

View File

@@ -13,7 +13,7 @@ import { getTables } from "../helpers/tableHelper";
import TableCanvas from "../components/TableCanvas"; import TableCanvas from "../components/TableCanvas";
import { useSearchParams } from "react-router-dom"; import { useSearchParams } from "react-router-dom";
export default function Transactions({ propsShopId, sendParam, deviceType, handleMoveToTransaction, depth, shopImg, setModal }) { export default function Transactions({ propsShopId,setIsModalOpen,cafeIdentityName, sendParam, deviceType, handleMoveToTransaction, depth, shopImg, setModal }) {
const { shopId, tableId } = useParams(); const { shopId, tableId } = useParams();
if (sendParam) sendParam({ shopId, tableId }); if (sendParam) sendParam({ shopId, tableId });
@@ -140,7 +140,8 @@ const handlePrint = (transaction) => {
const queryString = qs.stringify({ data: JSON.stringify(printableData) }); const queryString = qs.stringify({ data: JSON.stringify(printableData) });
// Navigate to /print with query string // Navigate to /print with query string
navigate(`/print?${queryString}`); setIsModalOpen(false);
navigate(`/${cafeIdentityName}/print?${queryString}`);
}; };
return ( return (

View File

@@ -165,7 +165,7 @@ export default function Transactions({ shop, shopId, propsShopId, sendParam, dev
const queryString = qs.stringify({ data: JSON.stringify(printableData) }); const queryString = qs.stringify({ data: JSON.stringify(printableData) });
// Navigate to /print with query string // Navigate to /print with query string
navigate(`/print?${queryString}`); navigate(`/${shopIdentifier}/print?${queryString}`);
}; };
if (loading) if (loading)
return ( return (