ok
This commit is contained in:
@@ -40,6 +40,7 @@ function CafePage({
|
||||
removeConnectedGuestSides,
|
||||
setModal,
|
||||
loading,
|
||||
queue
|
||||
}) {
|
||||
const location = useLocation();
|
||||
const [searchParams] = useSearchParams();
|
||||
@@ -87,6 +88,7 @@ function CafePage({
|
||||
}
|
||||
checkWelcomePageConfig();
|
||||
}, [welcomePageConfig]);
|
||||
|
||||
useEffect(() => {
|
||||
if (user.cafeId != null && user.cafeId !== shopId) {
|
||||
// Preserve existing query parameters
|
||||
@@ -232,7 +234,9 @@ function CafePage({
|
||||
socket={socket}
|
||||
shopId={shopId}
|
||||
user={user}
|
||||
shopOwnerId={shopOwnerId}
|
||||
isSpotifyNeedLogin={isSpotifyNeedLogin}
|
||||
queue={queue}
|
||||
/>
|
||||
<ItemTypeLister
|
||||
user={user}
|
||||
|
||||
194
src/pages/Dashboard copy.js
Normal file
194
src/pages/Dashboard copy.js
Normal file
@@ -0,0 +1,194 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import styles from "./Dashboard.module.css"; // Import module CSS for styling
|
||||
import Header from "../components/Header";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import AccountUpdateModal from "../components/AccountUpdateModal";
|
||||
import { removeLocalStorage } from "../helpers/localStorageHelpers";
|
||||
import { getAllCafeOwner, createCafeOwner } from "../helpers/userHelpers";
|
||||
import { getOwnedCafes, createCafe, updateCafe } from "../helpers/cafeHelpers";
|
||||
|
||||
import { ThreeDots } from "react-loader-spinner";
|
||||
import { unsubscribeUser } from "../helpers/subscribeHelpers.js";
|
||||
|
||||
const Dashboard = ({ user, setModal }) => {
|
||||
const navigate = useNavigate();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [items, setItems] = useState([]);
|
||||
const [isCreating, setIsCreating] = useState(false);
|
||||
const [newItem, setNewItem] = useState({ name: "", type: "" });
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
}, [user]);
|
||||
|
||||
const handleModalClose = () => {
|
||||
setIsModalOpen(false);
|
||||
};
|
||||
|
||||
const handleLogout = () => {
|
||||
removeLocalStorage("auth");
|
||||
unsubscribeUser();
|
||||
navigate(0);
|
||||
};
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
};
|
||||
// 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}
|
||||
/>
|
||||
{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.username && (
|
||||
<AccountUpdateModal
|
||||
user={user}
|
||||
isOpen={isModalOpen}
|
||||
onClose={handleModalClose}
|
||||
/>
|
||||
)}
|
||||
|
||||
{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>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dashboard;
|
||||
@@ -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}>
|
||||
<--- <-- 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 Dashboard;
|
||||
export default LinktreePage;
|
||||
|
||||
65
src/pages/Join.js
Normal file
65
src/pages/Join.js
Normal file
@@ -0,0 +1,65 @@
|
||||
import React from 'react';
|
||||
import styles from './Join.module.css'; // Import the module.css file
|
||||
|
||||
const LinktreePage = ({ data }) => {
|
||||
return (
|
||||
<div className={styles.linktreePage}>
|
||||
|
||||
<div className={styles.dashboardContainer}>
|
||||
{/* Main Heading */}
|
||||
<div className={styles.mainHeading}>
|
||||
Nikmati Kemudahan Mengelola Kafe
|
||||
</div>
|
||||
|
||||
{/* Sub Heading */}
|
||||
<div className={styles.subHeading}>
|
||||
Daftarkan kedaimu sekarang dan mulai gunakan semua fitur unggulan kami.
|
||||
</div>
|
||||
|
||||
{/* Form Section */}
|
||||
<form className={styles.linktreeForm}>
|
||||
<label htmlFor="username" className={styles.usernameLabel}>--------------------------------------------</label>
|
||||
<input
|
||||
id="username"
|
||||
placeholder="nomor whatsapp atau email"
|
||||
maxLength="30"
|
||||
className={styles.usernameInput}
|
||||
/>
|
||||
<button type="submit" className={styles.claimButton}>
|
||||
<span>➜</span>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{/* 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>
|
||||
</div>
|
||||
<div className={styles.footerImage}>
|
||||
<img
|
||||
src="./laporan.png"
|
||||
alt="Linktree visual"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LinktreePage;
|
||||
161
src/pages/Join.module.css
Normal file
161
src/pages/Join.module.css
Normal file
@@ -0,0 +1,161 @@
|
||||
/* General container */
|
||||
.linktreePage {
|
||||
padding: 0 1rem;
|
||||
border-radius: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
background-color: rgb(232 204 88);
|
||||
}
|
||||
|
||||
|
||||
.dashboardContainer {
|
||||
z-index: 6;
|
||||
padding-top: 40px;
|
||||
}
|
||||
|
||||
/* Main Heading */
|
||||
.mainHeading {
|
||||
font-weight: 700;
|
||||
font-size: 32px;
|
||||
line-height: 2.25rem;
|
||||
margin-bottom: 1rem;
|
||||
letter-spacing: -1px;
|
||||
color: rgb(37, 79, 26);
|
||||
}
|
||||
|
||||
.swipeContainer {
|
||||
height: 75px;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
color: rgba(59, 130, 246, 0.5);
|
||||
}
|
||||
|
||||
.swipeContent {
|
||||
animation: swipeUp 12s infinite;
|
||||
}
|
||||
|
||||
.swipeItem {
|
||||
overflow: hidden;
|
||||
height: 75px;
|
||||
max-width: 300px;
|
||||
text-wrap: balance;
|
||||
line-height: 34px;
|
||||
}
|
||||
|
||||
/* Swipe Animation */
|
||||
@keyframes swipeUp {
|
||||
0%, 15% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
20%, 35% {
|
||||
transform: translateY(-20%);
|
||||
}
|
||||
40%, 55% {
|
||||
transform: translateY(-40%);
|
||||
}
|
||||
60%, 75% {
|
||||
transform: translateY(-60%);
|
||||
}
|
||||
80%, 95% {
|
||||
transform: translateY(-80%);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sub Heading */
|
||||
.subHeading {
|
||||
font-weight: 400;
|
||||
line-height: 1.5rem;
|
||||
font-size: 14px;
|
||||
font-family: 'poppins';
|
||||
color: black;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
/* Form */
|
||||
.linktreeForm {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.usernameLabel {
|
||||
font-size: 0.875rem;
|
||||
color: #444;
|
||||
margin-bottom: 5px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.usernameInput {
|
||||
width: 250px;
|
||||
height: 55px;
|
||||
padding-left: 10px;
|
||||
font-size: 1rem;
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #ccc;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.claimButton {
|
||||
width: 200px;
|
||||
height: 45px;
|
||||
background-color: #254F1A;
|
||||
color: #D2E823;
|
||||
text-align: center;
|
||||
font-size: 1rem;
|
||||
padding: 10px;
|
||||
border-radius: 30px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.claimButton span {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.footer {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.footerLinks {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.footerLink {
|
||||
font-size: 0.875rem;
|
||||
color: #254F1A;
|
||||
margin-bottom: 0.5rem;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.signupButton {
|
||||
background-color: transparent;
|
||||
border: 1px solid #254F1A;
|
||||
color: #254F1A;
|
||||
padding: 12px 30px;
|
||||
border-radius: 30px;
|
||||
text-align: center;
|
||||
font-size: 0.875rem;
|
||||
margin-top: 1.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.footerImage {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footerImage img {
|
||||
width: 150px;
|
||||
height: 226px;
|
||||
margin-top: -50px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
329
src/pages/LinktreePage.module.css
Normal file
329
src/pages/LinktreePage.module.css
Normal file
@@ -0,0 +1,329 @@
|
||||
/* General container */
|
||||
.linktreePage {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
background-color: rgb(210, 232, 35);
|
||||
}
|
||||
|
||||
.dashboardLine {
|
||||
position: fixed;
|
||||
left: 0px;
|
||||
height: 100vh;
|
||||
width: 30px;
|
||||
border-right: 1px solid black;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.dashboardContainer {
|
||||
background-color: rgb(210, 232, 35);
|
||||
z-index: 6;
|
||||
padding: 0 1rem;
|
||||
padding-top: 40px;
|
||||
}
|
||||
|
||||
/* Main Heading */
|
||||
.mainHeading {
|
||||
font-weight: 700;
|
||||
font-size: 32px;
|
||||
line-height: 2.25rem;
|
||||
margin-bottom: 1rem;
|
||||
letter-spacing: -1px;
|
||||
color: rgb(37, 79, 26);
|
||||
}
|
||||
|
||||
.swipeContainer {
|
||||
height: 75px;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
color: rgb(59 130 246);
|
||||
}
|
||||
|
||||
.swipeContent {
|
||||
animation: swipeUp 12s infinite;
|
||||
}
|
||||
|
||||
.swipeItem {
|
||||
overflow: hidden;
|
||||
height: 75px;
|
||||
max-width: 300px;
|
||||
text-wrap: balance;
|
||||
line-height: 34px;
|
||||
}
|
||||
|
||||
/* Swipe Animation */
|
||||
@keyframes swipeUp {
|
||||
0%, 15% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
20%, 35% {
|
||||
transform: translateY(-20%);
|
||||
}
|
||||
40%, 55% {
|
||||
transform: translateY(-40%);
|
||||
}
|
||||
60%, 75% {
|
||||
transform: translateY(-60%);
|
||||
}
|
||||
80%, 95% {
|
||||
transform: translateY(-80%);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sub Heading */
|
||||
.subHeading {
|
||||
font-weight: 400;
|
||||
line-height: 1.5rem;
|
||||
font-size: 14px;
|
||||
font-family: 'poppins';
|
||||
color: black;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.LoginForm {
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
height: 148px;
|
||||
}
|
||||
|
||||
/* Form */
|
||||
.FormUsername {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
position: absolute;
|
||||
left: 0vw;
|
||||
}
|
||||
|
||||
.FormUsername.animateForm {
|
||||
animation: FormUsernameProgress 0.5s forwards; /* Apply the animation when inputtingPassword is true */
|
||||
}
|
||||
|
||||
.FormUsername.reverseForm {
|
||||
animation: FormUsernameReverse 0.5s forwards; /* Reverse animation when inputtingPassword is false */
|
||||
}
|
||||
|
||||
@keyframes FormUsernameProgress {
|
||||
0% {
|
||||
left: 0vw;
|
||||
}
|
||||
100% {
|
||||
left: -100vw;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes FormUsernameReverse {
|
||||
0% {
|
||||
left: -100vw;
|
||||
}
|
||||
100% {
|
||||
left: 0vw;
|
||||
}
|
||||
}
|
||||
|
||||
.FormPassword {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
position: absolute;
|
||||
left: 100vw;
|
||||
}
|
||||
|
||||
.FormPassword.animateForm {
|
||||
animation: FormPasswordProgress 0.5s forwards; /* Apply the animation when inputtingPassword is true */
|
||||
}
|
||||
|
||||
.FormPassword.reverseForm {
|
||||
animation: FormPasswordReverse 0.5s forwards; /* Reverse animation when inputtingPassword is false */
|
||||
}
|
||||
|
||||
@keyframes FormPasswordProgress {
|
||||
0% {
|
||||
left: 100vw;
|
||||
}
|
||||
100% {
|
||||
left: 0vw;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes FormPasswordReverse {
|
||||
0% {
|
||||
left: 0vw;
|
||||
}
|
||||
100% {
|
||||
left: 100vw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.usernameLabel {
|
||||
font-size: 0.875rem;
|
||||
color: #444;
|
||||
margin-bottom: 5px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.usernameInput {
|
||||
width: 250px;
|
||||
height: 55px;
|
||||
padding-left: 10px;
|
||||
font-size: 1rem;
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #ccc;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.usernameInputError {
|
||||
width: 250px;
|
||||
height: 55px;
|
||||
padding-left: 10px;
|
||||
font-size: 1rem;
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 5px;
|
||||
border: 2px solid red; /* Red border when error is true */
|
||||
margin-top: 5px;
|
||||
margin-bottom: 15px;
|
||||
|
||||
/* Apply keyframe animation for border color transition */
|
||||
animation: borderTransition 2s ease-in-out forwards;
|
||||
}
|
||||
|
||||
/* Keyframe animation for border color transition */
|
||||
@keyframes borderTransition {
|
||||
0% {
|
||||
border-color: red; /* Initial red border */
|
||||
}
|
||||
100% {
|
||||
border-color: transparent; /* Transition to transparent */
|
||||
}
|
||||
}
|
||||
|
||||
.claimButton {
|
||||
width: 200px;
|
||||
height: 45px;
|
||||
background-color: #254F1A;
|
||||
color: #D2E823;
|
||||
text-align: center;
|
||||
font-size: 1rem;
|
||||
padding: 10px;
|
||||
border-radius: 30px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.claimButton span {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.footer {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.footerLinks {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.footerLink {
|
||||
font-size: 0.875rem;
|
||||
color: #254F1A;
|
||||
margin-bottom: 0.5rem;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.signupButton {
|
||||
background-color: transparent;
|
||||
border: 1px solid #254F1A;
|
||||
color: #254F1A;
|
||||
padding: 12px 30px;
|
||||
border-radius: 30px;
|
||||
text-align: center;
|
||||
font-size: 0.875rem;
|
||||
margin-top: 1.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.footerImage {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footerImage img {
|
||||
width: 150px;
|
||||
height: 226px;
|
||||
margin-top: -50px;
|
||||
}
|
||||
|
||||
.userInfo {
|
||||
width: 100vw;
|
||||
background-color: white;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
border-radius: 20px 20px 0 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
|
||||
top: 75vh;
|
||||
transition: top 0.5s ease, padding 0.5s ease;
|
||||
}
|
||||
|
||||
.userInfoExpanded {
|
||||
width: 100vw;
|
||||
background-color: white;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
border-radius: 20px 20px 0 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
|
||||
top: 15vh;
|
||||
transition: top 0.5s ease, padding 0.5s ease;
|
||||
}
|
||||
.userInfoExpandButton {
|
||||
width: 100%;
|
||||
background-color: #d1ecdf;
|
||||
height: 30px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
|
||||
.ItemContainer {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.Item {
|
||||
background-color: #fff2a3;
|
||||
border-radius: 20px;
|
||||
margin: 20px;
|
||||
padding: 10px;
|
||||
}
|
||||
.transactionContainer {
|
||||
margin-left: 20px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.transaction {
|
||||
padding: 10px;
|
||||
margin-bottom: 5px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
|
||||
.rectangle {
|
||||
height: 150px; /* Height of each rectangle */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 10px; /* Rounded corners */
|
||||
font-size: 24px;
|
||||
background-color: rgb(114, 114, 114);
|
||||
}
|
||||
137
src/pages/Login.js
Normal file
137
src/pages/Login.js
Normal file
@@ -0,0 +1,137 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import styles from './Login.module.css'; // Import the module.css file
|
||||
import { loginUser } from "../helpers/userHelpers";
|
||||
import { ThreeDots } from "react-loader-spinner";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
const LinktreePage = ({ user, setModal }) => {
|
||||
const [inputtingPassword, setInputtingPassword] = useState(false);
|
||||
const [username, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.linktreePage}>
|
||||
|
||||
<div className={styles.dashboardContainer}>
|
||||
<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>
|
||||
|
||||
<div className={styles.subHeading}>
|
||||
Solusi berbasis web untuk memudahkan pengelolaan kedai, dengan fitur yang mempermudah pemilik, kasir, dan tamu berinteraksi.
|
||||
</div>
|
||||
<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>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LinktreePage;
|
||||
320
src/pages/Login.module.css
Normal file
320
src/pages/Login.module.css
Normal file
@@ -0,0 +1,320 @@
|
||||
/* General container */
|
||||
.linktreePage {
|
||||
border-radius: 20px;
|
||||
padding: 0 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
background-color: rgb(210, 232, 35);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dashboardLine {
|
||||
position: fixed;
|
||||
left: 0px;
|
||||
height: 100vh;
|
||||
width: 30px;
|
||||
border-right: 1px solid black;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.dashboardContainer {
|
||||
background-color: rgb(210, 232, 35);
|
||||
z-index: 6;
|
||||
padding-top: 40px;
|
||||
}
|
||||
|
||||
/* Main Heading */
|
||||
.mainHeading {
|
||||
font-weight: 700;
|
||||
font-size: 32px;
|
||||
line-height: 2.25rem;
|
||||
margin-bottom: 1rem;
|
||||
letter-spacing: -1px;
|
||||
color: rgb(37, 79, 26);
|
||||
}
|
||||
|
||||
.swipeContainer {
|
||||
height: 75px;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
color: rgb(59 130 246);
|
||||
}
|
||||
|
||||
.swipeContent {
|
||||
animation: swipeUp 12s infinite;
|
||||
}
|
||||
|
||||
.swipeItem {
|
||||
overflow: hidden;
|
||||
height: 75px;
|
||||
max-width: 300px;
|
||||
text-wrap: balance;
|
||||
line-height: 34px;
|
||||
}
|
||||
|
||||
/* Swipe Animation */
|
||||
@keyframes swipeUp {
|
||||
0%, 15% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
20%, 35% {
|
||||
transform: translateY(-20%);
|
||||
}
|
||||
40%, 55% {
|
||||
transform: translateY(-40%);
|
||||
}
|
||||
60%, 75% {
|
||||
transform: translateY(-60%);
|
||||
}
|
||||
80%, 95% {
|
||||
transform: translateY(-80%);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sub Heading */
|
||||
.subHeading {
|
||||
font-weight: 400;
|
||||
line-height: 1.5rem;
|
||||
font-size: 14px;
|
||||
font-family: 'poppins';
|
||||
color: black;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.LoginForm {
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
height: 148px;
|
||||
}
|
||||
|
||||
/* Form */
|
||||
.FormUsername {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
position: absolute;
|
||||
left: 0vw;
|
||||
}
|
||||
|
||||
.FormUsername.animateForm {
|
||||
animation: FormUsernameProgress 0.5s forwards; /* Apply the animation when inputtingPassword is true */
|
||||
}
|
||||
|
||||
.FormUsername.reverseForm {
|
||||
animation: FormUsernameReverse 0.5s forwards; /* Reverse animation when inputtingPassword is false */
|
||||
}
|
||||
|
||||
@keyframes FormUsernameProgress {
|
||||
0% {
|
||||
left: 0vw;
|
||||
}
|
||||
100% {
|
||||
left: -100vw;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes FormUsernameReverse {
|
||||
0% {
|
||||
left: -100vw;
|
||||
}
|
||||
100% {
|
||||
left: 0vw;
|
||||
}
|
||||
}
|
||||
|
||||
.FormPassword {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
position: absolute;
|
||||
left: 100vw;
|
||||
}
|
||||
|
||||
.FormPassword.animateForm {
|
||||
animation: FormPasswordProgress 0.5s forwards; /* Apply the animation when inputtingPassword is true */
|
||||
}
|
||||
|
||||
.FormPassword.reverseForm {
|
||||
animation: FormPasswordReverse 0.5s forwards; /* Reverse animation when inputtingPassword is false */
|
||||
}
|
||||
|
||||
@keyframes FormPasswordProgress {
|
||||
0% {
|
||||
left: 100vw;
|
||||
}
|
||||
100% {
|
||||
left: 0vw;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes FormPasswordReverse {
|
||||
0% {
|
||||
left: 0vw;
|
||||
}
|
||||
100% {
|
||||
left: 100vw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.usernameLabel {
|
||||
font-size: 0.875rem;
|
||||
color: #444;
|
||||
margin-bottom: 5px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.usernameInput {
|
||||
width: 250px;
|
||||
height: 55px;
|
||||
padding-left: 10px;
|
||||
font-size: 1rem;
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #ccc;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.usernameInputError {
|
||||
width: 250px;
|
||||
height: 55px;
|
||||
padding-left: 10px;
|
||||
font-size: 1rem;
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 5px;
|
||||
border: 2px solid red; /* Red border when error is true */
|
||||
margin-top: 5px;
|
||||
margin-bottom: 15px;
|
||||
|
||||
/* Apply keyframe animation for border color transition */
|
||||
animation: borderTransition 2s ease-in-out forwards;
|
||||
}
|
||||
|
||||
/* Keyframe animation for border color transition */
|
||||
@keyframes borderTransition {
|
||||
0% {
|
||||
border-color: red; /* Initial red border */
|
||||
}
|
||||
100% {
|
||||
border-color: transparent; /* Transition to transparent */
|
||||
}
|
||||
}
|
||||
|
||||
.claimButton {
|
||||
width: 200px;
|
||||
height: 45px;
|
||||
background-color: #254F1A;
|
||||
color: #D2E823;
|
||||
text-align: center;
|
||||
font-size: 1rem;
|
||||
padding: 10px;
|
||||
border-radius: 30px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.claimButton span {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.footer {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.footerLinks {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.footerLink {
|
||||
font-size: 0.875rem;
|
||||
color: #254F1A;
|
||||
margin-bottom: 0.5rem;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.signupButton {
|
||||
background-color: transparent;
|
||||
border: 1px solid #254F1A;
|
||||
color: #254F1A;
|
||||
padding: 12px 30px;
|
||||
border-radius: 30px;
|
||||
text-align: center;
|
||||
font-size: 0.875rem;
|
||||
margin-top: 1.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.footerImage {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footerImage img {
|
||||
width: 150px;
|
||||
height: 226px;
|
||||
margin-top: -50px;
|
||||
}
|
||||
|
||||
.userInfo {
|
||||
width: 100vw;
|
||||
background-color: white;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
border-radius: 20px 20px 0 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
|
||||
top: 75vh;
|
||||
transition: top 0.5s ease, padding 0.5s ease;
|
||||
}
|
||||
|
||||
.userInfoExpanded {
|
||||
width: 100vw;
|
||||
background-color: white;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
border-radius: 20px 20px 0 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
|
||||
top: 15vh;
|
||||
transition: top 0.5s ease, padding 0.5s ease;
|
||||
}
|
||||
.userInfoExpandButton {
|
||||
width: 100%;
|
||||
background-color: #d1ecdf;
|
||||
height: 30px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
|
||||
.ItemContainer {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.Item {
|
||||
background-color: #fff2a3;
|
||||
border-radius: 20px;
|
||||
margin: 20px;
|
||||
padding: 10px;
|
||||
}
|
||||
.transactionContainer {
|
||||
margin-left: 20px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.transaction {
|
||||
padding: 10px;
|
||||
margin-bottom: 5px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
/* src/Login.css */
|
||||
.login-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
background-color: #ffffff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.login-form h2 {
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.input-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
color: #555555;
|
||||
}
|
||||
|
||||
.input-group input {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #cccccc;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.login-button {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
background-color: #007bff;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.login-button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
import { useNavigate, useLocation } from "react-router-dom";
|
||||
import "./LoginPage.css";
|
||||
import RouletteWheel from "../components/RouletteWheel";
|
||||
import { loginUser, signUpUser } from "../helpers/userHelpers";
|
||||
|
||||
const LoginPage = () => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
const next = searchParams.get("next");
|
||||
const table = searchParams.get("table");
|
||||
|
||||
const handleLogin = async (
|
||||
email,
|
||||
username,
|
||||
password,
|
||||
setLoading,
|
||||
setError
|
||||
) => {
|
||||
try {
|
||||
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 = "/";
|
||||
if (table && !next) {
|
||||
console.error('Parameter "table" requires "next" to be present.');
|
||||
navigate("/");
|
||||
return;
|
||||
}
|
||||
if (next) {
|
||||
destination = `/${next}`;
|
||||
if (table) destination += `?table=${table}`;
|
||||
}
|
||||
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 handleSignUp = async (
|
||||
email,
|
||||
username,
|
||||
password,
|
||||
setLoading,
|
||||
setError
|
||||
) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await signUpUser(email, username, password);
|
||||
|
||||
if (response.success) {
|
||||
localStorage.setItem("auth", response.token);
|
||||
|
||||
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
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="login-container">
|
||||
<RouletteWheel onSignIn={handleLogin} onSignUp={handleSignUp} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginPage;
|
||||
70
src/pages/ResetPassword.js
Normal file
70
src/pages/ResetPassword.js
Normal file
@@ -0,0 +1,70 @@
|
||||
import React from 'react';
|
||||
import styles from './Join.module.css'; // Import the module.css file
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
|
||||
const LinktreePage = ({ setModal }) => {
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
const username = searchParams.get("username") || "";
|
||||
return (
|
||||
<div className={styles.linktreePage}>
|
||||
|
||||
<div className={styles.dashboardContainer}>
|
||||
{/* Main Heading */}
|
||||
<div className={styles.mainHeading}>
|
||||
Lupa password
|
||||
</div>
|
||||
|
||||
{/* Sub Heading */}
|
||||
<div className={styles.subHeading}>
|
||||
Masukkan email atau nomor whatsapp, kami akan memberimu kode akses
|
||||
</div>
|
||||
|
||||
{/* Form Section */}
|
||||
<form className={styles.linktreeForm}>
|
||||
<label htmlFor="username" className={styles.usernameLabel}>--------- username atau email -------------</label>
|
||||
<input
|
||||
id="username"
|
||||
placeholder=""
|
||||
maxLength="30"
|
||||
className={styles.usernameInput}
|
||||
value={username}
|
||||
/>
|
||||
<button type="submit" className={styles.claimButton}>
|
||||
<span>➜</span>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{/* 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>
|
||||
</div>
|
||||
<div className={styles.footerImage}>
|
||||
<img
|
||||
src="./laporan.png"
|
||||
alt="Linktree visual"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LinktreePage;
|
||||
Reference in New Issue
Block a user