ok
This commit is contained in:
@@ -60,6 +60,7 @@ function App() {
|
|||||||
const [shopItems, setShopItems] = useState([]);
|
const [shopItems, setShopItems] = useState([]);
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
const [modalContent, setModalContent] = useState(null);
|
const [modalContent, setModalContent] = useState(null);
|
||||||
|
const [onModalCloseFunction, setOnModalCloseFunction] = useState(null);
|
||||||
const transactionList = useRef(null);
|
const transactionList = useRef(null);
|
||||||
const [queue, setQueue] = useState([]);
|
const [queue, setQueue] = useState([]);
|
||||||
|
|
||||||
@@ -394,7 +395,7 @@ function App() {
|
|||||||
}, [navigate]);
|
}, [navigate]);
|
||||||
|
|
||||||
// Function to open the modal
|
// Function to open the modal
|
||||||
const setModal = (content, params = {}) => {
|
const setModal = (content, params = {}, onCloseFunction) => {
|
||||||
const queryParams = new URLSearchParams(location.search);
|
const queryParams = new URLSearchParams(location.search);
|
||||||
|
|
||||||
// Update the modal and any additional params
|
// Update the modal and any additional params
|
||||||
@@ -411,6 +412,8 @@ function App() {
|
|||||||
|
|
||||||
setIsModalOpen(true);
|
setIsModalOpen(true);
|
||||||
setModalContent(content)
|
setModalContent(content)
|
||||||
|
if (onCloseFunction) setOnModalCloseFunction(onCloseFunction)
|
||||||
|
else setOnModalCloseFunction(null)
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeModal = (closeTheseContent = []) => {
|
const closeModal = (closeTheseContent = []) => {
|
||||||
@@ -664,6 +667,7 @@ function App() {
|
|||||||
welcomePageConfig={shop.welcomePageConfig}
|
welcomePageConfig={shop.welcomePageConfig}
|
||||||
onClose={closeModal}
|
onClose={closeModal}
|
||||||
setModal={setModal}
|
setModal={setModal}
|
||||||
|
onModalCloseFunction={onModalCloseFunction}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import CreateCoupon from "../pages/CreateCoupon";
|
|||||||
import CheckCoupon from "../pages/CheckCoupon";
|
import CheckCoupon from "../pages/CheckCoupon";
|
||||||
import CreateUserWithCoupon from "../pages/CreateUserWithCoupon";
|
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, onModalCloseFunction }) => {
|
||||||
|
|
||||||
const [shopImg, setShopImg] = useState('');
|
const [shopImg, setShopImg] = useState('');
|
||||||
|
|
||||||
@@ -49,6 +49,7 @@ const Modal = ({ user, shop, isOpen, onClose, modalContent, setModal, handleMove
|
|||||||
// Function to handle clicks on the overlay
|
// Function to handle clicks on the overlay
|
||||||
const handleOverlayClick = (event) => {
|
const handleOverlayClick = (event) => {
|
||||||
// Close the modal only if the overlay is clicked
|
// Close the modal only if the overlay is clicked
|
||||||
|
onModalCloseFunction();
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,24 +7,31 @@ export function getAuthToken() {
|
|||||||
return localStorage.getItem('auth');
|
return localStorage.getItem('auth');
|
||||||
}
|
}
|
||||||
// Function to create a coupon
|
// Function to create a coupon
|
||||||
export async function createCoupon(discountType, discountValue, discountPeriods) {
|
export async function createCoupon(discountType, discountValue, discountPeriods, codeExpectation) {
|
||||||
try {
|
try {
|
||||||
|
const bodyData = {
|
||||||
|
discountType,
|
||||||
|
discountValue,
|
||||||
|
discountPeriods,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Conditionally add `codeExpectation` only if it's defined
|
||||||
|
if (codeExpectation !== null && codeExpectation !== undefined) {
|
||||||
|
bodyData.couponCodeExpect = codeExpectation;
|
||||||
|
}
|
||||||
|
|
||||||
const response = await fetch(`${API_BASE_URL}/coupon/create`, {
|
const response = await fetch(`${API_BASE_URL}/coupon/create`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
Authorization: `Bearer ${getAuthToken()}`,
|
Authorization: `Bearer ${getAuthToken()}`,
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify(bodyData),
|
||||||
discountType,
|
|
||||||
discountValue,
|
|
||||||
discountPeriods,
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
return { success: true, couponCode: data.coupon.code };
|
return { success: true, coupon: data.coupon };
|
||||||
} else {
|
} else {
|
||||||
return { success: false, message: 'Failed to create coupon.' };
|
return { success: false, message: 'Failed to create coupon.' };
|
||||||
}
|
}
|
||||||
@@ -33,6 +40,7 @@ export async function createCoupon(discountType, discountValue, discountPeriods)
|
|||||||
return { success: false, message: 'Error creating coupon.' };
|
return { success: false, message: 'Error creating coupon.' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to check the validity of the coupon code
|
// Function to check the validity of the coupon code
|
||||||
export async function checkCoupon(couponCode) {
|
export async function checkCoupon(couponCode) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
import styles from './Join.module.css';
|
||||||
|
import Coupon from '../components/Coupon';
|
||||||
|
|
||||||
|
import CryptoJS from 'crypto-js';
|
||||||
import {createCoupon} from "../helpers/couponHelpers.js"
|
import {createCoupon} from "../helpers/couponHelpers.js"
|
||||||
|
|
||||||
function getAuthToken() {
|
function getAuthToken() {
|
||||||
@@ -9,57 +12,134 @@ const CreateCouponPage = () => {
|
|||||||
const [discountType, setDiscountType] = useState("percentage");
|
const [discountType, setDiscountType] = useState("percentage");
|
||||||
const [discountValue, setDiscountValue] = useState("");
|
const [discountValue, setDiscountValue] = useState("");
|
||||||
const [discountPeriods, setDiscountPeriods] = useState("");
|
const [discountPeriods, setDiscountPeriods] = useState("");
|
||||||
const [message, setMessage] = useState("");
|
const [couponDetails, setCouponDetails] = useState(null);
|
||||||
|
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [error, setError] = useState(false);
|
||||||
|
const [wasInputtingPassword, setWasInputtingPassword] = useState(false);
|
||||||
|
const [inputtingPassword, setInputtingPassword] = useState(false);
|
||||||
|
const [username, setUsername] = useState('');
|
||||||
|
const [email, setEmail] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const [retypePassword, setRetypePassword] = useState('');
|
||||||
|
const [codeExpectation, setCodeExpectation] = useState('');
|
||||||
|
const [couponUrl, setCouponUrl] = useState('');
|
||||||
|
|
||||||
|
|
||||||
const handleSubmit = async (e) => {
|
const handleSubmit = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
// Call the helper function to create the coupon
|
// Call the helper function to create the coupon
|
||||||
const result = await createCoupon(discountType, discountValue, discountPeriods);
|
const result = await createCoupon(discountType, discountValue, discountPeriods, codeExpectation);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
setMessage(`Coupon created successfully: ${result.couponCode}`);
|
setCouponDetails(result.coupon);
|
||||||
|
const secretKey = 'xixixi666'; // Your AES-256 key (32 characters)
|
||||||
|
|
||||||
|
// Encrypt couponCode inline
|
||||||
|
const encryptedCouponCode = CryptoJS.AES.encrypt(result.coupon.code, secretKey).toString();
|
||||||
|
setCouponUrl(encryptedCouponCode)
|
||||||
|
console.log(encryptedCouponCode)
|
||||||
|
setLoading(false);
|
||||||
} else {
|
} else {
|
||||||
setMessage(result.message);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className={styles.linktreePage}>
|
||||||
<h1>Create Coupon</h1>
|
<div className={styles.dashboardContainer}>
|
||||||
<form onSubmit={handleSubmit}>
|
<div className={styles.mainHeading}>Buat Voucher {couponDetails != null && 'Berhasil'}</div>
|
||||||
<div>
|
<div className={styles.subHeadingTransparent}>
|
||||||
<label>Discount Type:</label>
|
Daftarkan kedaimu sekarang dan mulai gunakan semua fitur unggulan kami.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{couponDetails != null ?
|
||||||
|
<>
|
||||||
|
<Coupon
|
||||||
|
code={couponDetails?.code || null}
|
||||||
|
value={couponDetails?.discountValue}
|
||||||
|
period={couponDetails?.discountPeriods}
|
||||||
|
type={couponDetails?.type}
|
||||||
|
expiration={couponDetails?.expirationDate}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
navigator.clipboard.writeText('https://dev.coupon.kedaimaster.com/coupon?c=' + couponUrl)
|
||||||
|
.then(() => {
|
||||||
|
// Optional: Show a message that the text has been copied
|
||||||
|
alert("Coupon URL copied to clipboard!");
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error("Failed to copy text: ", err);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
className={styles.claimButton}
|
||||||
|
style={{marginBottom: '10px'}}
|
||||||
|
>
|
||||||
|
<span>Salin URL VOUCHER</span>
|
||||||
|
</button></>
|
||||||
|
|
||||||
|
:
|
||||||
|
<div className={styles.LoginForm}>
|
||||||
|
<div className={`${styles.FormUsername} ${inputtingPassword ? styles.animateForm : wasInputtingPassword ? styles.reverseForm : ''}`}>
|
||||||
|
<label htmlFor="username" className={styles.usernameLabel}>---- Daftar -------------------------------</label>
|
||||||
<select
|
<select
|
||||||
|
className={!error ? styles.usernameInput : styles.usernameInputError}
|
||||||
value={discountType}
|
value={discountType}
|
||||||
onChange={(e) => setDiscountType(e.target.value)}
|
onChange={(e) => setDiscountType(e.target.value)}
|
||||||
>
|
>
|
||||||
<option value="percentage">Percentage</option>
|
<option value="percentage">Percentage</option>
|
||||||
<option value="fixed">Fixed</option>
|
<option value="fixed">Fixed</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label>Discount Value:</label>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
|
placeholder="Nilai voucher"
|
||||||
value={discountValue}
|
value={discountValue}
|
||||||
onChange={(e) => setDiscountValue(e.target.value)}
|
onChange={(e) => setDiscountValue(e.target.value)}
|
||||||
required
|
className={!error ? styles.usernameInput : styles.usernameInputError}
|
||||||
/>
|
/>
|
||||||
|
<button onClick={() => { setInputtingPassword(true); setWasInputtingPassword(true); }} className={styles.claimButton}>
|
||||||
|
<span>➜</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<label>Discount Periods (in weeks):</label>
|
<div className={`${styles.FormPassword} ${inputtingPassword ? styles.animateForm : wasInputtingPassword ? styles.reverseForm : ''}`}>
|
||||||
|
<span>
|
||||||
|
<label onClick={() => setInputtingPassword(false)} htmlFor="password" className={styles.usernameLabel}> <--- <-- kembali </label>
|
||||||
|
<label htmlFor="password" className={styles.usernameLabel}> ----------------- </label>
|
||||||
|
</span>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
|
placeholder="Periode diskon (minggu)"
|
||||||
value={discountPeriods}
|
value={discountPeriods}
|
||||||
onChange={(e) => setDiscountPeriods(e.target.value)}
|
onChange={(e) => setDiscountPeriods(e.target.value)}
|
||||||
required
|
maxLength="30"
|
||||||
|
className={!error ? styles.usernameInput : styles.usernameInputError}
|
||||||
/>
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Ekspektasi kode"
|
||||||
|
value={codeExpectation}
|
||||||
|
onChange={(e) => setCodeExpectation(e.target.value)}
|
||||||
|
maxLength="30"
|
||||||
|
className={!error ? styles.usernameInput : styles.usernameInputError}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={handleSubmit}
|
||||||
|
className={`${styles.claimButton} ${loading ? styles.loading : ''}`}
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
<span>{loading ? 'Loading...' : 'Buat'}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<button type="submit">Create Coupon</button>
|
|
||||||
</form>
|
|
||||||
{message && <p>{message}</p>}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const LinktreePage = ({ setModal }) => {
|
|||||||
|
|
||||||
// Detect query params on component mount
|
// Detect query params on component mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const code = queryParams.get('couponCode');
|
const code = queryParams.get('c');
|
||||||
if (code) {
|
if (code) {
|
||||||
setCouponStatus('Coupon is valid');
|
setCouponStatus('Coupon is valid');
|
||||||
setCouponCode(code);
|
setCouponCode(code);
|
||||||
@@ -29,14 +29,6 @@ const LinktreePage = ({ setModal }) => {
|
|||||||
}
|
}
|
||||||
}, [queryParams]);
|
}, [queryParams]);
|
||||||
|
|
||||||
// Handle coupon validation
|
|
||||||
const handleCheckCoupon = async (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
const { status, coupon } = await checkCoupon(couponCode);
|
|
||||||
setCouponStatus(status);
|
|
||||||
setCouponDetails(coupon);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle user creation with coupon
|
// Handle user creation with coupon
|
||||||
const handleCreateUserWithCoupon = async (e) => {
|
const handleCreateUserWithCoupon = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ const LinktreePage = ({ data, setModal }) => {
|
|||||||
|
|
||||||
// Encrypt couponCode inline
|
// Encrypt couponCode inline
|
||||||
const encryptedCouponCode = CryptoJS.AES.encrypt(couponCode, secretKey).toString();
|
const encryptedCouponCode = CryptoJS.AES.encrypt(couponCode, secretKey).toString();
|
||||||
|
console.log(encryptedCouponCode)
|
||||||
// If it's only claiming a coupon, trigger claim logic
|
// If it's only claiming a coupon, trigger claim logic
|
||||||
setModal('create_user', { codeStatus: 200, c: encryptedCouponCode });
|
setModal('create_user', { codeStatus: 200, c: encryptedCouponCode });
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import PeriodCharts from '../components/PeriodCharts.js';
|
|||||||
|
|
||||||
import Coupon from "../components/Coupon.js";
|
import Coupon from "../components/Coupon.js";
|
||||||
|
|
||||||
|
import CreateCouponPage from "./CreateCoupon.js";
|
||||||
|
|
||||||
const RoundedRectangle = ({
|
const RoundedRectangle = ({
|
||||||
onClick,
|
onClick,
|
||||||
title,
|
title,
|
||||||
@@ -306,12 +308,12 @@ const App = ({ forCafe = true, cafeId = -1,
|
|||||||
let updatedFullTexts;
|
let updatedFullTexts;
|
||||||
|
|
||||||
if (otherCafes.length === 0) {
|
if (otherCafes.length === 0) {
|
||||||
updatedFullTexts = [["Buat bisnis", 0]];
|
updatedFullTexts = [[user.roleId == 0 ? "Buat Voucher":"Buat bisnis", 0]];
|
||||||
setSelectedCafeId(-1);
|
setSelectedCafeId(-1);
|
||||||
} else if (otherCafes.length === 1) {
|
} else if (otherCafes.length === 1) {
|
||||||
updatedFullTexts = [
|
updatedFullTexts = [
|
||||||
[otherCafes[0].name || otherCafes[0].username, otherCafes[0].cafeId || otherCafes[0].userId],
|
[otherCafes[0].name || otherCafes[0].username, otherCafes[0].cafeId || otherCafes[0].userId],
|
||||||
["Buat bisnis", -1]
|
[user.roleId == 0 ? "Buat Voucher":"Buat bisnis", -1]
|
||||||
];
|
];
|
||||||
|
|
||||||
setSelectedCafeId(otherCafes[0].cafeId); // Get the cafeId (second part of the pair)
|
setSelectedCafeId(otherCafes[0].cafeId); // Get the cafeId (second part of the pair)
|
||||||
@@ -319,7 +321,7 @@ const App = ({ forCafe = true, cafeId = -1,
|
|||||||
updatedFullTexts = [
|
updatedFullTexts = [
|
||||||
["semua", 0], // First entry is "semua"
|
["semua", 0], // First entry is "semua"
|
||||||
...otherCafes.map(item => [item.name || item.username, item.cafeId || item.userId]), // Map over cafes to get name and cafeId pairs
|
...otherCafes.map(item => [item.name || item.username, item.cafeId || item.userId]), // Map over cafes to get name and cafeId pairs
|
||||||
["tambah bisnis +", -1] // Add the "+" entry
|
[user.roleId == 0 ? "Buat Voucher":"Tambah Bisnis +", -1] // Add the "+" entry
|
||||||
];
|
];
|
||||||
|
|
||||||
setSelectedCafeId(0);
|
setSelectedCafeId(0);
|
||||||
@@ -385,6 +387,7 @@ const App = ({ forCafe = true, cafeId = -1,
|
|||||||
if (selectedItem) {
|
if (selectedItem) {
|
||||||
setSelectedCafeId(selectedItem[1]); // Get the cafeId (second part of the pair)
|
setSelectedCafeId(selectedItem[1]); // Get the cafeId (second part of the pair)
|
||||||
}
|
}
|
||||||
|
if(selectedItem[1] == -1 && user.roleId == 0) setModal('create_coupon',{},setSelectedCafeId(0));
|
||||||
|
|
||||||
setResetKey((prevKey) => prevKey + 1); // Increase the key to force re-render
|
setResetKey((prevKey) => prevKey + 1); // Increase the key to force re-render
|
||||||
};
|
};
|
||||||
@@ -654,61 +657,6 @@ const App = ({ forCafe = true, cafeId = -1,
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
{!forCafe && selectedCafeId == -1 &&
|
|
||||||
<div style={{
|
|
||||||
textAlign: "center",
|
|
||||||
}}>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexWrap: "wrap",
|
|
||||||
justifyContent: "center",
|
|
||||||
padding: "20px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{user?.roleId == 0 ?
|
|
||||||
<RoundedRectangle
|
|
||||||
title={"Masukkan nama bisnis"}
|
|
||||||
width="calc(100% - 10px)"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
value={itemName}
|
|
||||||
onChange={(e) => setItemName(e.target.value)}
|
|
||||||
style={{
|
|
||||||
width: '70%',
|
|
||||||
fontSize: '25px',
|
|
||||||
borderRadius: '7px',
|
|
||||||
border: '1px solid black'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</RoundedRectangle>
|
|
||||||
:
|
|
||||||
|
|
||||||
<RoundedRectangle
|
|
||||||
title={"Masukkan nama bisnis"}
|
|
||||||
width="calc(100% - 10px)"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
value={itemName}
|
|
||||||
onChange={(e) => setItemName(e.target.value)}
|
|
||||||
style={{
|
|
||||||
width: '70%',
|
|
||||||
fontSize: '25px',
|
|
||||||
borderRadius: '7px',
|
|
||||||
border: '1px solid black'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</RoundedRectangle>
|
|
||||||
|
|
||||||
}
|
|
||||||
<RoundedRectangle
|
|
||||||
title={user?.roleId === 0 ? "Buat Voucher" : "Buat Bisnis"}
|
|
||||||
width="calc(100% - 10px)"
|
|
||||||
onClick={handleClick}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
{user?.roleId == 1 &&
|
{user?.roleId == 1 &&
|
||||||
<>
|
<>
|
||||||
<div className={`${styles.couponContainer}`}>
|
<div className={`${styles.couponContainer}`}>
|
||||||
|
|||||||
Reference in New Issue
Block a user