This commit is contained in:
zadit
2024-11-16 23:44:48 +07:00
parent bea0ff63d7
commit b8c1d30d14
26 changed files with 1841 additions and 799 deletions

View File

@@ -1,22 +1,93 @@
import React, { useState, useEffect } from "react";
import styles from "./Dashboard.module.css"; // Import module CSS for styling
import Header from "../components/Header";
import React, { useState, useEffect } from 'react';
import styles from './LinktreePage.module.css'; // Import the module.css file
import { loginUser } from "../helpers/userHelpers";
import { ThreeDots } from "react-loader-spinner";
import { useNavigate } from "react-router-dom";
import AccountUpdateModal from "../components/AccountUpdateModal";
import { removeLocalStorage } from "../helpers/localStorageHelpers";
import { getLocalStorage, removeLocalStorage } from "../helpers/localStorageHelpers";
import { getAllCafeOwner, createCafeOwner } from "../helpers/userHelpers";
import { getOwnedCafes, createCafe, updateCafe } from "../helpers/cafeHelpers";
import { ThreeDots } from "react-loader-spinner";
import { getMyTransactions } from "../helpers/transactionHelpers";
import { unsubscribeUser } from "../helpers/subscribeHelpers.js";
const Dashboard = ({ user, setModal }) => {
import Header from '../components/Header';
const LinktreePage = ({ user, setModal }) => {
const navigate = useNavigate();
const [loading, setLoading] = useState(true);
const [isModalOpen, setIsModalOpen] = 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 [expanded, setIsExpand] = useState(false);
const [expandedCafeId, setExpandedCafeId] = useState(null); // Track which cafe is expanded
const handleToggleExpand = (cafeId) => {
setExpandedCafeId(expandedCafeId === cafeId ? null : cafeId); // Toggle expand for a specific cafe
};
const handleMyTransactions = async () => {
try {
setError(false);
setLoading(true);
const response = await getMyTransactions();
if (response) {
console.log(response)
return response;
} else {
setError(true); // Trigger error state in the button
console.error('Login failed');
}
} catch (error) {
setError(true);
console.error('Error occurred while logging in:', error.message);
} finally {
setLoading(false); // Ensure loading state is cleared
}
};
const handleLogin = async () => {
try {
setError(false);
setLoading(true);
const response = await loginUser(username, password);
if (response.success) {
localStorage.setItem('auth', response.token);
if (response.cafeId !== null) {
window.location.href = response.cafeId;
} else {
let destination = '/';
window.location.href = destination;
}
} else {
setError(true); // Trigger error state in the button
console.error('Login failed');
}
} catch (error) {
setError(true);
console.error('Error occurred while logging in:', error.message);
} finally {
setLoading(false); // Ensure loading state is cleared
}
};
const handleModalClose = () => {
setIsModalOpen(false);
};
const handleLogout = () => {
removeLocalStorage("auth");
unsubscribeUser();
navigate(0);
};
useEffect(() => {
if (user && user.roleId === 0) {
@@ -43,18 +114,19 @@ const Dashboard = ({ user, setModal }) => {
setLoading(false);
});
}
if (user && user.roleId == 3) {
handleMyTransactions()
.then((data) => {
setItems(data);
setLoading(false);
})
.catch((error) => {
console.error("Error fetching owned cafes:", error);
setLoading(false);
});
}
}, [user]);
const handleModalClose = () => {
setIsModalOpen(false);
};
const handleLogout = () => {
removeLocalStorage("auth");
unsubscribeUser();
navigate(0);
};
const handleCreateItem = () => {
if (user.roleId < 1) {
// Create admin functionality
@@ -80,42 +152,10 @@ const Dashboard = ({ user, setModal }) => {
});
}
};
// function calculateCafeMetrics(cafes) {
// let totalIncomeThisMonth = 0;
// let totalIncomeLastMonth = 0;
// cafes.forEach(cafe => {
// const currentIncome = cafe.totalIncome;
// const growth = cafe.growthIncome / 100;
// // Hitung keuntungan bulan lalu
// const lastMonthIncome = currentIncome / (1 + growth);
// // Tambahkan ke total
// totalIncomeThisMonth += currentIncome;
// totalIncomeLastMonth += lastMonthIncome;
// });
// // Hitung growth total
// const totalGrowth = ((totalIncomeThisMonth - totalIncomeLastMonth) / totalIncomeLastMonth) * 100;
// return {
// totalIncomeThisMonth,
// totalIncomeLastMonth: totalIncomeLastMonth.toFixed(2),
// totalGrowth: totalGrowth.toFixed(2)
// };
// }
return (
<>
<Header
HeaderText={"kedaimaster"}
isEdit={() => setIsModalOpen(true)}
isLogout={handleLogout}
user={user}
showProfile={true}
setModal={setModal}
/>
<div className={styles.linktreePage}>
{/* SVG Icon */}
{user && user.roleId < 2 && (
<div className={styles.dashboard}>
{loading && <ThreeDots />}
@@ -146,49 +186,204 @@ const Dashboard = ({ user, setModal }) => {
)}
</div>
)}
{(user.length == 0 || user.roleId > 1) &&
<>
<div className={styles.dashboardLine}></div>
{user.username && (
<AccountUpdateModal
user={user}
isOpen={isModalOpen}
onClose={handleModalClose}
/>
)}
<div className={styles.dashboardContainer}>
{/* Main Heading */}
<div className={styles.mainHeading}>
COBA KEDAIMASTER
<div className={styles.swipeContainer}>
<div className={styles.swipeContent}>
<div className={styles.swipeItem}>pemesanan langsung dari meja</div>
<div className={styles.swipeItem}>pengelolaan pesanan dan keuangan</div>
<div className={styles.swipeItem}>tentukan suasana musik</div>
<div className={styles.swipeItem}>pengelolaan stok dan manajemen</div>
<div className={styles.swipeItem}>jangan pernah ragukan pelanggan</div>
</div>
</div>
diskon 0%
</div>
{isCreating && (
<div className={styles.createModal}>
<h2>Create New {user.roleId < 1 ? "Admin" : "Cafe"}</h2>
{user.roleId < 1 ?<>
<input
type="email"
value={newItem.email}
onChange={(e) => setNewItem({ ...newItem, email: e.target.value })}
placeholder="email"
/>
<input
type="text"
value={newItem.username}
onChange={(e) => setNewItem({ ...newItem, username: e.target.value })}
placeholder="username"
/>
<input
type="password"
value={newItem.password}
onChange={(e) => setNewItem({ ...newItem, password: e.target.value })}
placeholder="Password"
/></> :
<input
type="text"
value={newItem.name}
onChange={(e) => setNewItem({ ...newItem, name: e.target.value })}
placeholder="Name"
/>}
<button onClick={handleCreateItem}>Create</button>
<button onClick={() => setIsCreating(false)}>Cancel</button>
</div>
)}
</>
{/* Sub Heading */}
<div className={styles.subHeading}>
Solusi berbasis web untuk memudahkan pengelolaan kedai, dengan fitur yang mempermudah pemilik, kasir, dan tamu berinteraksi.
</div>
{getLocalStorage('auth') == null &&
<div className={styles.LoginForm}>
<div className={`${styles.FormUsername} ${inputtingPassword ? styles.animateForm : styles.reverseForm}`}>
<label htmlFor="username" className={styles.usernameLabel}>
---- masuk -------------------------------
</label>
<input
id="username"
placeholder="username"
maxLength="30"
className={!error ? styles.usernameInput : styles.usernameInputError}
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<button onClick={() => setInputtingPassword(true)} className={styles.claimButton}>
<span></span>
</button>
</div>
<div className={`${styles.FormPassword} ${inputtingPassword ? styles.animateForm : styles.reverseForm}`}>
<span>
<label onClick={() => setInputtingPassword(false)} htmlFor="password" className={styles.usernameLabel}>
&lt;--- &lt;-- kembali
</label><label htmlFor="password" className={styles.usernameLabel}>
&nbsp; ------ &nbsp;
</label><label onClick={() => setModal('reset-password', { username: username })} htmlFor="password" className={styles.usernameLabel}>
lupa password? -
</label></span>
<input
id="password"
placeholder="password"
type="password"
maxLength="30"
className={!error ? styles.usernameInput : styles.usernameInputError}
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button
onClick={handleLogin}
className={`${styles.claimButton} ${loading ? styles.loading : ''}`}
disabled={loading}
>
<span>{loading ? 'Loading...' : 'Masuk'}</span>
</button>
</div>
</div>
}
{/* Footer Links */}
<div className={styles.footer}>
<div className={styles.footerLinks}>
<a
href="https://linktr.ee/discover/trending"
target="_blank"
rel="noreferrer"
className={styles.footerLink}
>
Pelajari lebih lanjut
</a>
<a
href="https://linktr.ee"
target="_blank"
rel="noreferrer"
className={styles.footerLink}
>
Tentang kedaimaster.com
</a>
<a
target="_blank"
rel="noreferrer"
className={styles.signupButton}
style={{ textDecoration: 'none' }}
onClick={() => setModal('join')}
>
Daftarkan kedaimu
</a>
</div>
<div className={styles.footerImage}>
<img
style={{ height: '226px', width: '150px', objectFit: 'cover' }}
src="/kedai.png"
alt="Linktree visual"
onError={(e) => e.target.src = '/fallback-image.png'}
/>
</div>
</div>
{user.length != 0 &&
<div className={` ${expanded ? styles.userInfoExpanded : styles.userInfo}`}>
<div onClick={() => setIsExpand(!expanded)} className={styles.userInfoExpandButton}>
{!expanded ?
<svg
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
fill="#000000"
style={{ width: '100%', height: '100%' }} // Ensure SVG fits the div
>
<g id="SVGRepo_bgCarrier" strokeWidth="0"></g>
<g id="SVGRepo_tracerCarrier" strokeLinecap="round" strokeLinejoin="round"></g>
<g id="SVGRepo_iconCarrier">
<path d="m 1 11 c 0 -0.265625 0.105469 -0.519531 0.292969 -0.707031 l 6 -6 c 0.390625 -0.390625 1.023437 -0.390625 1.414062 0 l 6 6 c 0.1875 0.1875 0.292969 0.441406 0.292969 0.707031 s -0.105469 0.519531 -0.292969 0.707031 c -0.390625 0.390625 -1.023437 0.390625 -1.414062 0 l -5.292969 -5.292969 l -5.292969 5.292969 c -0.390625 0.390625 -1.023437 0.390625 -1.414062 0 c -0.1875 -0.1875 -0.292969 -0.441406 -0.292969 -0.707031 z m 0 0" fill={"#2e3436"}></path>
</g>
</svg>
:
<svg
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
fill="#000000"
style={{ width: '100%', height: '100%' }} // Ensure SVG fits the div
>
<g id="SVGRepo_bgCarrier" strokeWidth="0"></g>
<g id="SVGRepo_tracerCarrier" strokeLinecap="round" strokeLinejoin="round"></g>
<g id="SVGRepo_iconCarrier">
<path d="m 1 5 c 0 -0.265625 0.105469 -0.519531 0.292969 -0.707031 c 0.390625 -0.390625 1.023437 -0.390625 1.414062 0 l 5.292969 5.292969 l 5.292969 -5.292969 c 0.390625 -0.390625 1.023437 -0.390625 1.414062 0 c 0.1875 0.1875 0.292969 0.441406 0.292969 0.707031 s -0.105469 0.519531 -0.292969 0.707031 l -6 6 c -0.390625 0.390625 -1.023437 0.390625 -1.414062 0 l -6 -6 c -0.1875 -0.1875 -0.292969 -0.441406 -0.292969 -0.707031 z m 0 0" fill={"#2e3436"}></path>
</g>
</svg>
}
</div>
<Header
HeaderText={"Kedai yang pernah kamu kunjungi"}
HeaderSize={'4vw'}
showProfile={true}
setModal={setModal}
isLogout={handleLogout}
user={user}
/>
<div className={styles.ItemContainer} style={{height: expanded?'85%':'43.5%'}}>
{items.map((item, index) => (
<div className={styles.Item}onClick={() => handleToggleExpand(item.cafeId)} key={index}>
{/* Render cafes */}
<div
// Toggle expansion on cafe click
className={styles.rectangle}
>
<h1>{item.name || item.username}</h1>
</div>
{/* Render transactions if the cafe is expanded */}
{expandedCafeId === item.cafeId && item.transactions && (
<div className={styles.Item}>
{item.transactions.map((transaction, transactionIndex) => (
<div
key={transactionIndex}
className={styles.transaction}
style={{ backgroundColor: 'orange' }}
>
{transaction.detailedTransactions && transaction.detailedTransactions.map((detailedTransaction, detailedTransactionIndex) => (
<div key={detailedTransactionIndex}>
<p>Quantity: {detailedTransaction.qty}</p>
{detailedTransaction.Item && (
<div>
<h4>Item Name: {detailedTransaction.Item.name}</h4>
<p>Price: {detailedTransaction.Item.price}</p>
</div>
)}
</div>
))}
</div>
))}
</div>
)}
</div>
))}
</div>
</div>
}
</div>
</>
}
</div>
);
};
export default Dashboard;
export default LinktreePage;