ok
This commit is contained in:
20
src/App.js
20
src/App.js
@@ -413,27 +413,27 @@ function App() {
|
||||
setModalContent(content)
|
||||
};
|
||||
|
||||
// Function to close the modal
|
||||
const closeModal = (closeTheseContent = []) => {
|
||||
if (
|
||||
Array.isArray(closeTheseContent) &&
|
||||
(closeTheseContent.length === 0 ||
|
||||
closeTheseContent.includes(modalContent))
|
||||
(closeTheseContent.length === 0 || closeTheseContent.includes(modalContent))
|
||||
) {
|
||||
setIsModalOpen(false);
|
||||
setModalContent(null);
|
||||
document.body.style.overflow = "auto";
|
||||
|
||||
|
||||
const queryParams = new URLSearchParams(location.search);
|
||||
|
||||
// Remove the 'modal' parameter
|
||||
queryParams.delete("modal");
|
||||
queryParams.delete("transactionId");
|
||||
|
||||
// Update the URL without the 'modal' parameter
|
||||
|
||||
// Clear all query parameters
|
||||
queryParams.keys() && [...queryParams.keys()].forEach(key => {
|
||||
queryParams.delete(key);
|
||||
});
|
||||
|
||||
// Update the URL without any query parameters
|
||||
navigate({ search: queryParams.toString() }, { replace: true });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// useEffect(() => {
|
||||
// const askNotificationPermission = async () => {
|
||||
|
||||
56
src/components/Coupon.css
Normal file
56
src/components/Coupon.css
Normal 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
28
src/components/Coupon.js
Normal 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;
|
||||
@@ -30,6 +30,7 @@ import { getImageUrl } from "../helpers/itemHelper.js";
|
||||
|
||||
import CreateCoupon from "../pages/CreateCoupon";
|
||||
import CheckCoupon from "../pages/CheckCoupon";
|
||||
import CreateUserWithCoupon from "../pages/CreateUserWithCoupon";
|
||||
|
||||
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}>
|
||||
|
||||
{modalContent === "edit_account" && <AccountUpdatePage user={user} />}
|
||||
{modalContent === "join" && <Join />}
|
||||
{modalContent === "join" && <Join setModal={setModal} />}
|
||||
{modalContent === "reset-password" && <ResetPassword />}
|
||||
{modalContent === "req_notification" && <NotificationRequest setModal={setModal} />}
|
||||
{modalContent === "blocked_notification" && <NotificationBlocked />}
|
||||
@@ -103,6 +104,7 @@ const Modal = ({ user, shop, isOpen, onClose, modalContent, setModal, handleMove
|
||||
|
||||
{modalContent === "create_coupon" && <CreateCoupon />}
|
||||
{modalContent === "check_coupon" && <CheckCoupon />}
|
||||
{modalContent === "create_user" && <CreateUserWithCoupon />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -946,7 +946,7 @@ const sortedMaterials = allMaterials.sort((a, b) => new Date(a.date) - new Date(
|
||||
onError={(e) => e.target.src = '/fallback-image.png'}
|
||||
/>
|
||||
</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>
|
||||
|
||||
@@ -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 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 (
|
||||
<div className={styles.linktreePage}>
|
||||
{!isUsingCoupon ? (
|
||||
<div className={styles.dashboardContainer}>
|
||||
{/* Main Heading */}
|
||||
<div className={styles.mainHeading}>Nikmati Kemudahan Mengelola Kafe</div>
|
||||
|
||||
<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}
|
||||
>
|
||||
Gunakan kupon
|
||||
</a>
|
||||
{/* Sub Heading */}
|
||||
<div className={styles.subHeading}>
|
||||
Daftarkan kedaimu sekarang dan mulai gunakan semua fitur unggulan kami.
|
||||
</div>
|
||||
<div className={styles.footerImage}>
|
||||
<img
|
||||
src="./laporan.png"
|
||||
alt="Linktree visual"
|
||||
|
||||
{/* 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
|
||||
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 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>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user