import React, { useState, useEffect } from 'react'; import { useNavigate, useLocation } from "react-router-dom"; import styles from './LinktreePage.module.css'; import { loginUser, getAnalytics, createCafeOwner } from "../helpers/userHelpers"; import { getOwnedCafes, createCafe, updateCafe } from "../helpers/cafeHelpers"; import { getMyTransactions } from "../helpers/transactionHelpers"; import { unsubscribeUser } from "../helpers/subscribeHelpers.js"; import { getLocalStorage, removeLocalStorage } from "../helpers/localStorageHelpers"; import { ThreeDots } from "react-loader-spinner"; import Header from '../components/Header'; import CircularDiagram from "./CircularDiagram"; import API_BASE_URL from '../config'; import BarChart from '../components/BarChart'; const LinktreePage = ({ user, setModal }) => { const navigate = useNavigate(); const location = useLocation(); const [lastModal, setLastModal] = useState(false); const [inputtingPassword, setInputtingPassword] = useState(false); const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [loading, setLoading] = useState(false); const [error, setError] = useState(false); const [items, setItems] = useState([]); const [isCreating, setIsCreating] = useState(false); const [newItem, setNewItem] = useState({ name: "", type: "" }); const [isModalOpen, setIsModalOpen] = useState(false); const [selectedItemId, setSelectedItemId] = useState(0); const [selectedSubItemId, setSelectedSubItemId] = useState(0); useEffect(() => { const urlParams = new URLSearchParams(location.search); const modalParam = urlParams.get('modal'); if (lastModal && !modalParam) { if (selectedItemId == -1) setSelectedItemId(0); else if (selectedSubItemId == -1) setSelectedSubItemId(0); } if (modalParam) { setLastModal(modalParam); } }, [location]); useEffect(() => { const url = new URL(window.location.href); // Get the current URL const searchParams = new URLSearchParams(url.search); // Get the query parameters console.log(selectedItemId) if (selectedItemId != 0 && selectedItemId != -1) { // Add cafeId to the query parameter searchParams.set('cafeId', selectedItemId); } else { // Remove cafeId from the query parameter searchParams.delete('cafeId'); } // Update the URL with the modified query params window.history.replaceState(null, '', `${url.pathname}?${searchParams.toString()}`); }, [selectedItemId]); // Handle user transactions const handleMyTransactions = async () => { try { setError(false); setLoading(true); const response = await getMyTransactions(); if (response) { setItems(response); } else { setError(true); } } catch (error) { setError(true); } finally { setLoading(false); console.log(items); } }; // Handle login const handleLogin = async () => { try { setError(false); setLoading(true); const response = await loginUser(username, password); if (response.success) { localStorage.setItem('auth', response.token); window.location.href = response.cafeId ? `/${response.cafeId}` : '/'; } else { setError(true); } } catch (error) { setError(true); } finally { setLoading(false); } }; // Handle logout const handleLogout = () => { removeLocalStorage("auth"); unsubscribeUser(); navigate(0); }; // Fetch data when the user changes useEffect(() => { if (user) { setLoading(true); switch (user.roleId) { case 0: getAnalytics().then(setItems).catch(console.error).finally(() => setLoading(false)); break; case 1: getAnalytics().then(setItems).catch(console.error).finally(() => setLoading(false)); break; case 3: handleMyTransactions(); break; default: setLoading(false); break; } } console.log(items); }, [user]); // Handle create item (admin or cafe owner) const handleCreateItem = async () => { try { if (user.roleId < 1) { const newOwner = await createCafeOwner(newItem.email, newItem.username, newItem.password); setItems([...items, { userId: newOwner.userId, name: newOwner.username }]); } else { const newCafe = await createCafe(newItem.name); setItems([...items, { cafeId: newCafe.cafeId, name: newCafe.name }]); } setIsCreating(false); setNewItem({ name: "", type: "" }); } catch (error) { console.error("Error creating item:", error); } }; 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(); } }; const colors = [ // Strong contrasting colors for visibility on white background "#333333", // Dark Gray (great contrast for legibility) "#555555", // Medium Gray (slightly lighter, still legible) // Subtle accent colors (not too bright, but distinct) "#4B9F8D", // Muted Teal (offers a soft contrast) "#7F7F7F", // Slate Gray (elegant and balanced) // Softer neutral colors (for less emphasis) "#B0B0B0", // Light Gray (gentle tone for subtle slices) "#D3D3D3", // Silver Gray (light, but still visible on white background) // A touch of color for balance (but still muted) "#9C6E5C", // Muted Brown (earthy, grounded tone) "#A1A1A1", // Silver (neutral highlight) ]; console.log(items) const selectedItems = items.items?.find(item => (item.userId || item.cafeId) === selectedItemId); // If the selected tenant is found, extract the cafes const selectedSubItems = selectedItems?.subItems || []; // 1. Optionally combine all report items from cafes of the selected tenant let allSelectedSubItems = null; if (user.roleId == 1) allSelectedSubItems = selectedSubItems?.flatMap(cafe => cafe.report?.items || selectedItems.report?.items || []); // 2. Retrieve the specific cafe's report items if needed const filteredItems = selectedSubItems?.find(cafe => cafe.cafeId == selectedSubItemId) || { report: { items: [] } }; // 3. Decide whether to use combined items or individual cafe items let segments = []; if (user.roleId == 1 || user.roleId == 0 && selectedItemId == 0) segments = (selectedItemId != 0 && selectedItemId != -1 && selectedSubItemId == 0 ? allSelectedSubItems : selectedItemId != 0 && selectedItemId != -1 ? filteredItems.report?.items || [] : items?.items || []).map((item, index) => ({ percentage: item.percentage || (items?.totalIncome / item?.totalIncome || items?.totalIncome / item?.report?.totalIncome) * 100, value: item.username || item.name, color: (colors && colors[index]) || "#cccccc", // Safe check for colors array })) || []; // Ensure segments is an empty array if no items are availabled // Function to combine items of all cafes for the selected tenant console.log(selectedItems) console.log(selectedItemId) // Check if items and items.items are defined before proceeding const allMaterials = (items?.items || []).flatMap(item => item.report?.materialsPurchased || []); // Sort the merged array by date if it's not empty const sortedMaterials = allMaterials.sort((a, b) => new Date(a.date) - new Date(b.date)); return ( <> {user && user.roleId < 2 ? ( <>
setIsModalOpen(true)} isLogout={handleLogout} user={user} showProfile={true} setModal={setModal} HeaderMargin='0px' />
{user.roleId == 0 ? ( selectedItemId == 0 || selectedItemId == -1 ? (

{items?.totalIncome}

pemasukan

keuntungan

{items?.totalIncome * 0.02}

) : (

{selectedItems?.totalIncome}

pemasukann

{selectedItems?.totalIncome * 0.02}

tagihan

{selectedItems?.totalIncome * 0.02}

{filteredItems.name}

) ) : ( selectedItemId == 0 || selectedItemId == -1 ? (

{formatIncome(items?.totalIncome)}

pemasukan

{formatIncome(items?.totalOutcome)}

pengeluaran

{formatIncome(items?.totalIncome * 0.02)}

tanggungan

) : (

{formatIncome(selectedItems?.report?.totalIncome)}

pemasukan

{formatIncome(selectedItems?.report?.currentOutcome)}

pengeluaran

 

{selectedItems?.name}

) )}

terlaku

{segments && segments.map((item, index) => (
{item.percentage == 'Infinity' || isNaN(item.percentage) ? 0 : item.percentage}%   {item.value}
))} {segments.length < 1 && <> {Array.from({ length: 5 }).map((_, index) => (
-
))} }

penambahan stok

Semua {user.roleId < 1 ? 'penyewa' : 'kedai anda'}
{user.roleId < 1 &&
{ setSelectedItemId(selectedItemId === -1 ? 0 : -1); setModal('create_tenant'); }} > Tambah penyewa
} { items?.items?.length > 0 ? ( items.items.map((item) => { const isTenantSelected = selectedItemId === (item.userId || item.cafeId); const tenantBackgroundColor = isTenantSelected && !selectedSubItemId ? 'rgb(69, 69, 69)' : 'rgb(114, 114, 114)'; const hasSubItems = item?.subItems?.length > 0; return (
{ setSelectedItemId(isTenantSelected && !selectedSubItemId ? 0 : (item.userId || item.cafeId)); setSelectedSubItemId(0); // Reset subitem selection when changing tenant }} style={{ backgroundColor: tenantBackgroundColor }} className={isTenantSelected ? styles.rectangleNLine : styles.rectangle} >

{item.username || item.name}  

Rp{formatIncome(item.totalIncome || item.report?.totalIncome) || 0}

{/* Only show cafes if the tenant is selected */} {selectedItemId === (item.userId || item.cafeId) && hasSubItems && item.subItems.map((subItem) => { const isCafeSelected = selectedSubItemId == (subItem.cafeId || subItem.userId); const cafeBackgroundColor = isCafeSelected ? 'rgb(69, 69, 69)' : 'rgb(114, 114, 114)'; return (
{ setSelectedSubItemId(isCafeSelected ? 0 : (subItem.cafeId || subItem.userId)); // Toggle subitem selection setSelectedItemId(item.userId || item.cafeId); // Ensure tenant stays selected }} style={{ backgroundColor: cafeBackgroundColor }} > {subItem.name || subItem.username} {/*  pendapatan {formatIncome(subItem.report?.totalIncome || 0)} */}
); })} {selectedItemId == item.cafeId &&
{ setSelectedSubItemId(selectedSubItemId == -1 ? 0 : -1); setModal('create_clerk') }} style={{ backgroundColor: selectedItemId == item.cafeId && selectedSubItemId == -1 ? 'rgb(69, 69, 69)' : 'rgb(114, 114, 114)' }} > tambah kasir
}
); }) ) : (
No tenants available
) } {user.roleId > 0 &&
{ setSelectedItemId(selectedItemId == -1 ? 0 : -1); setModal('create_kedai') }} > Tambah kedai
}
) : (
{API_BASE_URL == 'https://test.api.kedaimaster.com' ?
KEDAIMASTER CREDITS
{['AI - MUHAMMAD AINUL FIKRI', 'BACKEND - ZADIT TAQWA W.', 'FRONTEND - M. PASHA A. P.' , 'FRONTEND - NAUFAL DANIYAL P.', 'FRONTEND - ZADIT TAQWA W.', 'UI/UX - KEVIN DWI WIJAYA', 'UI/UX - LUNA CHELISA A.', 'UI/UX - MAULINA AYU E.', 'UI/UX - NUR ARINDA P.', 'UI/UX - NAURA IZZATI B.',].map((item, index) => (
{item}
))}
diskon 50%
:
COBA KEDAIMASTER
{['pemesanan langsung dari meja', 'pengelolaan pesanan dan keuangan', 'tentukan suasana musik', 'pengelolaan stok dan manajemen', 'jangan pernah ragukan pelanggan'].map((item, index) => (
{item}
))}
diskon 50%
}
Solusi berbasis web untuk memudahkan pengelolaan kedai, dengan fitur yang mempermudah pemilik, kasir, dan tamu berinteraksi.
{getLocalStorage('auth') == null && (
setUsername(e.target.value)} />
setPassword(e.target.value)} />
)}
Pelajari lebih lanjut Tentang kedaimaster.com setModal('join')} > Daftarkan kedaimu
Linktree visual e.target.src = '/fallback-image.png'} />
)} ); }; export default LinktreePage;