This commit is contained in:
zadit
2025-01-21 07:41:46 +07:00
parent 308eafa6d4
commit e0ef8d5023
7 changed files with 371 additions and 63 deletions

View File

@@ -413,12 +413,10 @@ function App() {
setModalContent(content) setModalContent(content)
}; };
// Function to close the modal
const closeModal = (closeTheseContent = []) => { const closeModal = (closeTheseContent = []) => {
if ( if (
Array.isArray(closeTheseContent) && Array.isArray(closeTheseContent) &&
(closeTheseContent.length === 0 || (closeTheseContent.length === 0 || closeTheseContent.includes(modalContent))
closeTheseContent.includes(modalContent))
) { ) {
setIsModalOpen(false); setIsModalOpen(false);
setModalContent(null); setModalContent(null);
@@ -426,15 +424,17 @@ function App() {
const queryParams = new URLSearchParams(location.search); const queryParams = new URLSearchParams(location.search);
// Remove the 'modal' parameter // Clear all query parameters
queryParams.delete("modal"); queryParams.keys() && [...queryParams.keys()].forEach(key => {
queryParams.delete("transactionId"); queryParams.delete(key);
});
// Update the URL without the 'modal' parameter // Update the URL without any query parameters
navigate({ search: queryParams.toString() }, { replace: true }); navigate({ search: queryParams.toString() }, { replace: true });
} }
}; };
// useEffect(() => { // useEffect(() => {
// const askNotificationPermission = async () => { // const askNotificationPermission = async () => {
// let permission = Notification.permission; // let permission = Notification.permission;

56
src/components/Coupon.css Normal file
View File

@@ -0,0 +1,56 @@
/* Coupon container */
.coupon {
display: flex;
border: 2px solid #ccc;
height: 50%;
background-color: #f8f8f8;
border-radius: 8px;
font-family: Arial, sans-serif;
align-items: center;
}
/* Left side (with the rotated code and dotted line) */
.coupon-left {
width: 80px;
position: relative;
display: flex;
justify-content: center;
align-items: center;
border-right: 2px dotted #ccc;
height: 100%;
}
.coupon-code {
writing-mode: vertical-rl;
font-size: 18px;
font-weight: bold;
color: #333;
margin: 0;
}
.dotted-line {
position: absolute;
left: 0;
bottom: 10px;
width: 60px;
border-bottom: 2px dotted #ccc;
}
/* Right side (coupon details) */
.coupon-right {
padding: 10px;
flex-grow: 1;
}
.coupon-value {
font-size: clamp(18px, 3vw, 24px); /* Minimum 18px, 6vw (responsive), Maximum 24px */
font-weight: bold;
color: #2c3e50;
text-align: left;
}
.coupon-period,
.coupon-expiration {
font-size: 14px;
color: #7f8c8d;
}

28
src/components/Coupon.js Normal file
View File

@@ -0,0 +1,28 @@
import React from 'react';
import './Coupon.css'; // Import a CSS file for styling
const Coupon = ({ code, value, period, type, expiration }) => {
// Format the value based on type
const formattedValue = type === 'fixed' ? `Rp ${value}` : value != 0 ? `${value}%` : 'kupon berlangganan';
return (
<div className='coupon'>
<div className='coupon-left'>
<div className='coupon-code'>{code == null ? '404' : code}</div>
<div className='dotted-line'></div>
</div>
<div className='coupon-right'>
<h2 className='coupon-value'>{code == null ? 'Kupon tidak ditemukan' : formattedValue}</h2>
{type && <span className='coupon-type'>{type}</span>} {/* Display type if provided */}
<p className='coupon-period'>
{code == null ? '-' : value == 0 ? `Masa berlangganan ${period} minggu` : `Masa kupon ${period} minggu`} {/* Fixed string concatenation */}
</p>
<p className='coupon-expiration'>
{expiration == null ? (code == null ? '-' : 'Tanpa kadaluarsa') : `Berlaku sampai: ${expiration}`}
</p>
</div>
</div>
);
};
export default Coupon;

View File

@@ -30,6 +30,7 @@ import { getImageUrl } from "../helpers/itemHelper.js";
import CreateCoupon from "../pages/CreateCoupon"; import CreateCoupon from "../pages/CreateCoupon";
import CheckCoupon from "../pages/CheckCoupon"; import CheckCoupon from "../pages/CheckCoupon";
import CreateUserWithCoupon from "../pages/CreateUserWithCoupon";
const Modal = ({ user, shop, isOpen, onClose, modalContent, setModal, handleMoveToTransaction,welcomePageConfig }) => { const Modal = ({ user, shop, isOpen, onClose, modalContent, setModal, handleMoveToTransaction,welcomePageConfig }) => {
@@ -60,7 +61,7 @@ const Modal = ({ user, shop, isOpen, onClose, modalContent, setModal, handleMove
<div className={styles.modalContent} onClick={handleContentClick}> <div className={styles.modalContent} onClick={handleContentClick}>
{modalContent === "edit_account" && <AccountUpdatePage user={user} />} {modalContent === "edit_account" && <AccountUpdatePage user={user} />}
{modalContent === "join" && <Join />} {modalContent === "join" && <Join setModal={setModal} />}
{modalContent === "reset-password" && <ResetPassword />} {modalContent === "reset-password" && <ResetPassword />}
{modalContent === "req_notification" && <NotificationRequest setModal={setModal} />} {modalContent === "req_notification" && <NotificationRequest setModal={setModal} />}
{modalContent === "blocked_notification" && <NotificationBlocked />} {modalContent === "blocked_notification" && <NotificationBlocked />}
@@ -103,6 +104,7 @@ const Modal = ({ user, shop, isOpen, onClose, modalContent, setModal, handleMove
{modalContent === "create_coupon" && <CreateCoupon />} {modalContent === "create_coupon" && <CreateCoupon />}
{modalContent === "check_coupon" && <CheckCoupon />} {modalContent === "check_coupon" && <CheckCoupon />}
{modalContent === "create_user" && <CreateUserWithCoupon />}
</div> </div>
</div> </div>
); );

View File

@@ -0,0 +1,123 @@
import React, { useState } from 'react';
import styles from './Join.module.css'; // Import the module.css file
import API_BASE_URL from '../config.js';
function getAuthToken() {
return localStorage.getItem('auth');
}
const LinktreePage = ({ setModal }) => {
const [isUsingCoupon, setIsUsingCoupon] = useState(false);
const [couponCode, setCouponCode] = useState('');
const [couponStatus, setCouponStatus] = useState('');
const [couponDetails, setCouponDetails] = useState(null);
const [username, setUsername] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [retypePassword, setRetypePassword] = useState('');
const handleCheckCoupon = async (e) => {
e.preventDefault();
try {
const response = await fetch(`${API_BASE_URL}/coupon/check/${couponCode}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${getAuthToken()}`,
},
});
if (response.ok) {
const data = await response.json();
setCouponStatus('Coupon is valid');
setCouponDetails(data.coupon);
} else {
setCouponStatus('Coupon not found or expired');
setCouponDetails(null);
}
} catch (error) {
setCouponStatus('Error checking coupon.');
setCouponDetails(null);
}
};
const handleCreateUserWithCoupon = async (e) => {
e.preventDefault();
if (password !== retypePassword) {
setCouponStatus('Passwords do not match');
return;
}
try {
const response = await fetch(`${API_BASE_URL}/user/create-with-coupon`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${getAuthToken()}`,
},
body: JSON.stringify({
username,
email,
password,
couponCode,
}),
});
if (response.ok) {
const data = await response.json();
setCouponStatus('User created successfully with coupon');
setCouponDetails(null);
console.log(data);
} else {
const errorData = await response.json();
setCouponStatus(errorData.message || 'Error creating user');
}
} catch (error) {
setCouponStatus('Error creating user.');
}
};
return (
<div className={styles.linktreePage}>
<div className={styles.dashboardContainer}>
<div className={styles.mainHeading}>Gunakan Kupon</div>
<form className={styles.linktreeForm} onSubmit={handleCreateUserWithCoupon}>
<input
type="text"
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
className={styles.usernameInput}
/>
<input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className={styles.usernameInput}
/>
<input
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className={styles.usernameInput}
/>
<input
type="password"
placeholder="Re-type Password"
value={retypePassword}
onChange={(e) => setRetypePassword(e.target.value)}
className={styles.usernameInput}
/>
<button type="submit" className={styles.claimButton}>
<span>Buat Akun</span>
</button>
</form>
</div>
</div>
);
};
export default LinktreePage;

View File

@@ -946,7 +946,7 @@ const sortedMaterials = allMaterials.sort((a, b) => new Date(a.date) - new Date(
onError={(e) => e.target.src = '/fallback-image.png'} onError={(e) => e.target.src = '/fallback-image.png'}
/> />
</div> </div>
<a style={{left: 0, right: 0, bottom: 0, textAlign: 'center', color: '#254F1A', fontSize:'13px', position: 'fixed'}}>©2025 KEDIRITECHNOPARK</a> <a style={{left: 0, right: 0, bottom: 0, textAlign: 'center', color: '#254F1A', fontSize:'13px', position: 'fixed'}}>©2025 KEDIRITECHNOPARK.COM</a>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,63 +1,162 @@
import React from 'react'; import React, { useState } from 'react';
import styles from './Join.module.css'; // Import the module.css file import styles from './Join.module.css'; // Import the module.css file
import API_BASE_URL from '../config.js';
import Coupon from '../components/Coupon';
function getAuthToken() {
return localStorage.getItem('auth');
}
const LinktreePage = ({ data, setModal }) => {
const [isUsingCoupon, setIsUsingCoupon] = useState(false);
const [couponCode, setCouponCode] = useState('');
const [couponStatus, setCouponStatus] = useState(0);
const [couponDetails, setCouponDetails] = useState(null);
const handleCheckCoupon = async (e) => {
e.preventDefault();
try {
const response = await fetch(`${API_BASE_URL}/coupon/check/${couponCode}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${getAuthToken()}`,
},
});
if (response.ok) {
const data = await response.json();
setCouponStatus(200);
setCouponDetails(data.coupon);
} else {
setCouponStatus(404);
setCouponDetails(null);
}
} catch (error) {
setCouponStatus(404);
setCouponDetails(null);
}
};
const LinktreePage = ({ data }) => {
return ( return (
<div className={styles.linktreePage}> <div className={styles.linktreePage}>
{!isUsingCoupon ? (
<div className={styles.dashboardContainer}>
{/* Main Heading */}
<div className={styles.mainHeading}>Nikmati Kemudahan Mengelola Kafe</div>
<div className={styles.dashboardContainer}> {/* Sub Heading */}
{/* Main Heading */} <div className={styles.subHeading}>
<div className={styles.mainHeading}> Daftarkan kedaimu sekarang dan mulai gunakan semua fitur unggulan kami.
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}
>
Gunakan kupon
</a>
</div> </div>
<div className={styles.footerImage}>
<img {/* Form Section */}
src="./laporan.png" <form className={styles.linktreeForm}>
alt="Linktree visual" <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
onClick={() => setIsUsingCoupon(true)}
className={styles.footerLink}
>
Gunakan kupon
</a>
</div>
<div className={styles.footerImage}>
<img src="./laporan.png" alt="Linktree visual" />
</div>
</div> </div>
</div> </div>
</div> ) : (
<div className={styles.dashboardContainer}>
{/* Main Heading */}
<div className={styles.mainHeading}>Daftar Menggunakan Kupon</div>
{/* Sub Heading */}
<div className={styles.subHeading}>
Kupon tidak hanya dapat digunakan untuk pembuatan akun penyewa, tetapi juga dapat digunakan untuk memperpanjang masa berlangganan.
</div>
{/* Coupon Check Section */}
{couponStatus == 0 ?
<form className={styles.linktreeForm} onSubmit={handleCheckCoupon}>
<label htmlFor="coupon" className={styles.usernameLabel}>
--------------------------------------------
</label>
<input
id="coupon"
placeholder="kode kupon"
maxLength="30"
className={styles.usernameInput}
value={couponCode}
onChange={(e) => setCouponCode(e.target.value)}
/>
<button type="submit" className={styles.claimButton}>
<span>Cek</span>
</button>
</form>
:
<>
<Coupon code={couponDetails?.code || null} value={couponDetails?.discountValue} period={couponDetails?.discountPeriods} expiration={couponDetails?.expirationDate} />
<form className={styles.linktreeForm}>
<label htmlFor="username" className={styles.usernameLabel}>
--------------------------------------------
</label>
<button type="submit" className={styles.claimButton} style={{ width: '266px' }} onClick={() => setModal('create_user', { codeStatus: 200, couponCode })}>
<span>Buat akun dengan kupon ini</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
onClick={() => { setIsUsingCoupon(couponStatus == 0 ? false : true); setCouponCode(null); setCouponDetails(null); setCouponStatus(0) }}
className={styles.footerLink}
>
Kembali
</a>
</div>
<div className={styles.footerImage}>
<img src="./laporan.png" alt="Linktree visual" />
</div>
</div>
</div>
)}
</div> </div>
); );
}; };