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}
onClose={closeModal}
setModal={setModal}
setIsModalOpen={setIsModalOpen}
onModalCloseFunction={onModalCloseFunction}
onModalYesFunction={onModalYesFunction}
shopIdentifier={shopIdentifier}
/>
</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;
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 = []
@@ -83,16 +83,19 @@ const DailyCharts = ({ incomeGraph, transactionGraph, materialGraph, colors, typ
sumOutcome(dayData?.hour21To24MaterialIds),
];
}
let totalValue = seriesData.reduce((acc, val) => acc + val, 0);
return {
date: new Date(dayData.date).toLocaleDateString(),
categories,
series: [
{
name: graphFilter === 'transactions' ? 'Transaksi' : (graphFilter === 'Pemasukan' ? 'Pemasukan' : 'Pengeluaran'),
name: graphFilter === 'transactions' ? 'Transaksi' : (graphFilter === 'income' ? 'Pemasukan' : 'Pengeluaran'),
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)
}
>
<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
style={{ color: index === indexx ? 'black' : 'transparent' }}>
{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}
</>
) : (
'Hari ini'
<>
{type != 'weekly' ? 'Hari ini' : day}
</>
)}
</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>
@@ -181,10 +193,11 @@ const DailyCharts = ({ incomeGraph, transactionGraph, materialGraph, colors, typ
} else {
return formatRupiah(val); // format Rupiah
}
},
style: {
colors: [(index == chartData.length - 1 || selectedIndex != -1) ? "#000" : "transparent"],
fontSize: '10px',
fontSize: '7px',
},
offsetY: -10,
background: {

View File

@@ -35,7 +35,7 @@ import CreateCoupon from "../pages/CreateCoupon";
import CheckCoupon from "../pages/CheckCoupon";
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 [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 === "edit_tables" && <TablesPage shop={shop} />}
{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" && (
<Transaction propsShopId={shop.cafeId} />
<Transaction propsShopId={shop.cafeId} setIsModalOpen={setIsModalOpen} cafeIdentityName={shopIdentifier} />
)}
{modalContent === "transaction_pending" && <Transaction_pending deviceType={deviceType} setModal={setModal}/>}
{modalContent === "transaction_item" && <Transaction_item />}

View File

@@ -7,182 +7,195 @@ const PeriodCharts = ({ type, graphFilter, aggregatedCurrentReports, aggregatedP
useEffect(() => {
setSelectedIndex(-1);
}, [aggregatedCurrentReports, aggregatedPreviousReports]);
}, [aggregatedCurrentReports, aggregatedPreviousReports, graphFilter]);
const monthly = ["1 - 7", "8 - 14", "15 - 21", "22 - 28", "29 - 31"];
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) {
currentIncomeData = aggregatedCurrentReports.map((report) => report.income);
currentOutcomeData = aggregatedCurrentReports.map((report) => report.outcome);
currentTransactionData = aggregatedCurrentReports.map((report) => report.transactions);
currentIncomeData = aggregatedCurrentReports.map((r) => r.income);
currentOutcomeData = aggregatedCurrentReports.map((r) => r.outcome);
currentTransactionData = aggregatedCurrentReports.map((r) => r.transactions);
if (type == 'monthly' && currentIncomeData.length === 4) {
if (type === "monthly" && currentIncomeData.length === 4) {
currentIncomeData.push(null);
}
if (type == 'monthly' && currentOutcomeData.length === 4) {
currentOutcomeData.push(null);
}
if (type == 'monthly' && currentTransactionData.length === 4) {
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);
}
if (type == 'monthly' && previousOutcomeData.length === 4) {
previousOutcomeData.push(null);
}
if (type == 'monthly' && previousTransactionData.length === 4) {
previousTransactionData.push(null);
}
}
let globalMax = null;
if (aggregatedCurrentReports || aggregatedPreviousReports) {
// Find the global maximum for the y-axis
globalMax = Math.max(
...(graphFilter === 'income'
? [...currentIncomeData, ...previousIncomeData]
: graphFilter === 'outcome'
? [...currentOutcomeData, ...previousOutcomeData]
: [...currentTransactionData, ...previousTransactionData])
);
}
// cari global max untuk y-axis
let globalMax = 0;
if (aggregatedCurrentReports || aggregatedPreviousReports) {
const dataset =
graphFilter === "income"
? [...(currentIncomeData || []), ...(previousIncomeData || [])]
: graphFilter === "outcome"
? [...(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 (
<div className={`${styles.chartItemContainer} ${selectedIndex !== -1 ? styles.expanded : ''}`}>
{aggregatedPreviousReports && (
<div className={`${styles.chartItemWrapper} ${selectedIndex !== -1 && selectedIndex !== 0
? styles.chartItemWrapperActive
: styles.chartItemWrapperInactive
}`}>
<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
className={`${styles.chartItemContainer} ${selectedIndex !== -1 ? styles.expanded : ""
}`}
>
{[aggregatedPreviousReports, aggregatedCurrentReports].map(
(dataset, i) =>
dataset && (
<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>{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', aggregatedCurrentReports?.length == 4 ? 'transparent' : 'black'],
<div className={styles.dateSelectorWrapper}>
{[0, 1].map((idx) => (
<div
key={idx}
className={`${styles.dateSelector} ${idx === i ? styles.dateSelectorActive : styles.dateSelectorInactive
}`}
style={{ position: "relative" }}
onClick={() =>
selectedIndex == idx ? setSelectedIndex(-1) : setSelectedIndex(idx)
}
}
},
yaxis: { max: globalMax, min: 0, labels: { maxWidth: 20, style: { colors: "transparent" } } },
grid: { show: false },
fill: { opacity: 0.5 },
colors: [colors[1]],
}}
series={[
// { name: "Pemasukan", data: currentIncomeData },
// { name: "Pengeluaran", data: currentOutcomeData },
{ name: "Total transaksi", data: graphFilter == 'income' ? currentIncomeData : graphFilter == 'outcome' ? currentOutcomeData : currentTransactionData },
]}
type="area"
height={200}
width="100%"
/>
</div>
</div>
>
{idx === i && (
<div
style={{
position: "absolute",
bottom: 0,
left: "10%",
right: "10%",
borderBottom: `2px solid ${colors[i]}`,
}}
></div>
)}
<div style={{ color: idx === i ? "black" : "transparent" }}>
{type === "monthly"
? idx === 0
? "bulan lalu"
: "bulan ini"
: 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>
);

View File

@@ -19,7 +19,7 @@ const HorizontalBarDiagram = ({ segments, width = 300 }) => {
style={{ display: "block", margin: "0 auto" }}
>
{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 barWidth = (width * percentage) / 100;
@@ -61,7 +61,7 @@ const HorizontalBarDiagram = ({ segments, width = 300 }) => {
fontWeight="bold"
style={{ pointerEvents: "none", textTransform: "capitalize" }}
>
{`${name}: ${value} (${percentage}%)`}
{unit ? (name + ' ( ' + value +' '+ unit+')') : (name + ' ' + value +' ('+ percentage + '%)')}
</text>
</g>
);

View File

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

View File

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

View File

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

View File

@@ -13,7 +13,7 @@ import { getTables } from "../helpers/tableHelper";
import TableCanvas from "../components/TableCanvas";
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();
if (sendParam) sendParam({ shopId, tableId });
@@ -140,7 +140,8 @@ const handlePrint = (transaction) => {
const queryString = qs.stringify({ data: JSON.stringify(printableData) });
// Navigate to /print with query string
navigate(`/print?${queryString}`);
setIsModalOpen(false);
navigate(`/${cafeIdentityName}/print?${queryString}`);
};
return (

View File

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