390 lines
15 KiB
JavaScript
390 lines
15 KiB
JavaScript
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 { getLocalStorage, removeLocalStorage } from "../helpers/localStorageHelpers";
|
|
import { getAllCafeOwner, createCafeOwner } from "../helpers/userHelpers";
|
|
import { getOwnedCafes, createCafe, updateCafe } from "../helpers/cafeHelpers";
|
|
import { getMyTransactions } from "../helpers/transactionHelpers";
|
|
import { unsubscribeUser } from "../helpers/subscribeHelpers.js";
|
|
|
|
import Header from '../components/Header';
|
|
|
|
const LinktreePage = ({ user, setModal }) => {
|
|
const navigate = useNavigate();
|
|
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) {
|
|
setLoading(true);
|
|
getAllCafeOwner()
|
|
.then((data) => {
|
|
setItems(data);
|
|
setLoading(false);
|
|
})
|
|
.catch((error) => {
|
|
console.error("Error fetching cafe owners:", error);
|
|
setLoading(false);
|
|
});
|
|
}
|
|
if (user && user.roleId === 1) {
|
|
setLoading(true);
|
|
getOwnedCafes(user.userId)
|
|
.then((data) => {
|
|
setItems(data);
|
|
setLoading(false);
|
|
})
|
|
.catch((error) => {
|
|
console.error("Error fetching owned cafes:", error);
|
|
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 handleCreateItem = () => {
|
|
if (user.roleId < 1) {
|
|
// Create admin functionality
|
|
createCafeOwner(newItem.email, newItem.username, newItem.password)
|
|
.then((newitem) => {
|
|
setItems([...items, { userId: newitem.userId, name: newitem.username }]);
|
|
setIsCreating(false);
|
|
setNewItem({ name: "", type: "" });
|
|
})
|
|
.catch((error) => {
|
|
console.error("Error creating admin:", error);
|
|
});
|
|
} else {
|
|
// Create cafe functionality
|
|
createCafe(newItem.name)
|
|
.then((newitem) => {
|
|
setItems([...items, { cafeId: newitem.cafeId, name: newitem.name }]);
|
|
setIsCreating(false);
|
|
setNewItem({ name: "", type: "" });
|
|
})
|
|
.catch((error) => {
|
|
console.error("Error creating cafe:", error);
|
|
});
|
|
}
|
|
};
|
|
return (
|
|
<div className={styles.linktreePage}>
|
|
{/* SVG Icon */}
|
|
|
|
{user && user.roleId < 2 && (
|
|
<div className={styles.dashboard}>
|
|
{loading && <ThreeDots />}
|
|
{items.map((item, index) => (
|
|
<div
|
|
key={index}
|
|
onClick={() => navigate("/" + item.cafeId)}
|
|
className={styles.rectangle}
|
|
>
|
|
<h1>{item.name || item.username}</h1>
|
|
<div><h1>{item.report?.totalIncome}</h1></div>
|
|
</div>
|
|
))}
|
|
{user && user.roleId < 1 ? (
|
|
<div
|
|
className={styles.rectangle}
|
|
onClick={() => setIsCreating(true)}
|
|
>
|
|
Create Client
|
|
</div>
|
|
) : (
|
|
<div
|
|
className={styles.rectangle}
|
|
onClick={() => setIsCreating(true)}
|
|
>
|
|
+
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
{(user.length == 0 || user.roleId > 1) &&
|
|
<>
|
|
<div className={styles.dashboardLine}></div>
|
|
|
|
<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>
|
|
|
|
{/* 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}>
|
|
<--- <-- kembali
|
|
</label><label htmlFor="password" className={styles.usernameLabel}>
|
|
------
|
|
</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 LinktreePage;
|