This commit is contained in:
zadit
2024-11-08 13:50:03 +07:00
parent 32e8ebd69b
commit bea0ff63d7
9 changed files with 270 additions and 26 deletions

50
package-lock.json generated
View File

@@ -26,6 +26,7 @@
"react-router-dom": "^6.24.0", "react-router-dom": "^6.24.0",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"react-switch": "^7.0.0", "react-switch": "^7.0.0",
"react-youtube": "^10.1.0",
"smooth-scroll-into-view-if-needed": "^2.0.2", "smooth-scroll-into-view-if-needed": "^2.0.2",
"socket.io-client": "^4.7.5", "socket.io-client": "^4.7.5",
"styled-components": "^6.1.11", "styled-components": "^6.1.11",
@@ -13339,6 +13340,11 @@
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
}, },
"node_modules/load-script": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/load-script/-/load-script-1.0.0.tgz",
"integrity": "sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA=="
},
"node_modules/loader-runner": { "node_modules/loader-runner": {
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
@@ -16220,6 +16226,22 @@
"react-dom": ">=16.6.0" "react-dom": ">=16.6.0"
} }
}, },
"node_modules/react-youtube": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/react-youtube/-/react-youtube-10.1.0.tgz",
"integrity": "sha512-ZfGtcVpk0SSZtWCSTYOQKhfx5/1cfyEW1JN/mugGNfAxT3rmVJeMbGpA9+e78yG21ls5nc/5uZJETE3cm3knBg==",
"dependencies": {
"fast-deep-equal": "3.1.3",
"prop-types": "15.8.1",
"youtube-player": "5.5.2"
},
"engines": {
"node": ">= 14.x"
},
"peerDependencies": {
"react": ">=0.14.1"
}
},
"node_modules/read-cache": { "node_modules/read-cache": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -17084,6 +17106,11 @@
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
}, },
"node_modules/sister": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/sister/-/sister-3.0.2.tgz",
"integrity": "sha512-p19rtTs+NksBRKW9qn0UhZ8/TUI9BPw9lmtHny+Y3TinWlOa9jWh9xB0AtPSdmOy49NJJJSSe0Ey4C7h0TrcYA=="
},
"node_modules/sisteransi": { "node_modules/sisteransi": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
@@ -19567,6 +19594,29 @@
"funding": { "funding": {
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
},
"node_modules/youtube-player": {
"version": "5.5.2",
"resolved": "https://registry.npmjs.org/youtube-player/-/youtube-player-5.5.2.tgz",
"integrity": "sha512-ZGtsemSpXnDky2AUYWgxjaopgB+shFHgXVpiJFeNB5nWEugpW1KWYDaHKuLqh2b67r24GtP6HoSW5swvf0fFIQ==",
"dependencies": {
"debug": "^2.6.6",
"load-script": "^1.0.0",
"sister": "^3.0.0"
}
},
"node_modules/youtube-player/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/youtube-player/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
} }
} }
} }

View File

@@ -21,6 +21,7 @@
"react-router-dom": "^6.24.0", "react-router-dom": "^6.24.0",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"react-switch": "^7.0.0", "react-switch": "^7.0.0",
"react-youtube": "^10.1.0",
"smooth-scroll-into-view-if-needed": "^2.0.2", "smooth-scroll-into-view-if-needed": "^2.0.2",
"socket.io-client": "^4.7.5", "socket.io-client": "^4.7.5",
"styled-components": "^6.1.11", "styled-components": "^6.1.11",

View File

@@ -208,6 +208,15 @@ function App() {
}); });
}); });
const checkNotifications = () => {
let permission = Notification.permission;
// Check current permission
if (permission !== "granted") {
setModal("req_notification");
}
};
socket.on("checkUserTokenRes", async (data) => { socket.on("checkUserTokenRes", async (data) => {
if (data.status !== 200) { if (data.status !== 200) {
removeLocalStorage("auth"); removeLocalStorage("auth");
@@ -226,7 +235,7 @@ function App() {
console.log("getting guest side"); console.log("getting guest side");
setDeviceType("clerk"); setDeviceType("clerk");
// checkNotifications(data.data.user.userId); checkNotifications();
} else { } else {
setDeviceType("guestDevice"); setDeviceType("guestDevice");
} }
@@ -315,6 +324,7 @@ function App() {
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;

View File

@@ -14,6 +14,7 @@ import Payment_claimed from "../pages/Payment_claimed";
import MaterialList from "../pages/MaterialList.js"; import MaterialList from "../pages/MaterialList.js";
import MaterialMutationsPage from "../pages/MaterialMutationsPage.js"; import MaterialMutationsPage from "../pages/MaterialMutationsPage.js";
import Reports from "../pages/Reports.js"; import Reports from "../pages/Reports.js";
import NotificationRequest from "../pages/NotificationRequest.js";
import NotificationBlocked from "../pages/NotificationBlocked.js"; import NotificationBlocked from "../pages/NotificationBlocked.js";
import WelcomePageEditor from "../pages/WelcomePageEditor.js"; import WelcomePageEditor from "../pages/WelcomePageEditor.js";
import GuidePage from "../pages/GuidePage"; import GuidePage from "../pages/GuidePage";
@@ -34,7 +35,7 @@ const Modal = ({ shop, isOpen, onClose, modalContent, setModal }) => {
return ( return (
<div onClick={handleOverlayClick} className={styles.modalOverlay}> <div onClick={handleOverlayClick} className={styles.modalOverlay}>
<div className={styles.modalContent} onClick={handleContentClick}> <div className={styles.modalContent} onClick={handleContentClick}>
{modalContent === "req_notification" && <NotificationBlocked />} {modalContent === "req_notification" && <NotificationRequest setModal={setModal} />}
{modalContent === "blocked_notification" && <NotificationBlocked />} {modalContent === "blocked_notification" && <NotificationBlocked />}
{modalContent === "create_clerk" && <CreateClerk shopId={shop.cafeId} />} {modalContent === "create_clerk" && <CreateClerk shopId={shop.cafeId} />}
{modalContent === "edit_tables" && <TablesPage shop={shop} />} {modalContent === "edit_tables" && <TablesPage shop={shop} />}

View File

@@ -2,7 +2,7 @@
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: -1px;
bottom: 0; bottom: 0;
background: rgba(0, 0, 0, 0.5); background: rgba(0, 0, 0, 0.5);
display: flex; display: flex;

View File

@@ -0,0 +1,108 @@
import React, { useState, useEffect, useRef } from 'react';
import YouTube from 'react-youtube';
export function TrackPlayer({ next }) {
// State to store the progress in milliseconds, video duration, and the next video ID
const [progress, setProgress] = useState(0);
const [duration, setDuration] = useState(0);
const [currentTrack, setCurrentTrack] = useState(null); // Initial video ID
const [nextTrack, setNextTrack] = useState(null); // Initial video ID
const [isNearEnd, setIsNearEnd] = useState(false); // Flag for 20 seconds left
const playerRef = useRef(null);
useEffect(() => {
if (next == null) return;
if (currentTrack == null) setCurrentTrack(next);
setNextTrack(next);
}, [next]);
const handlePlayerStateChange = (event) => {
if (event.data === window.YT.PlayerState.PLAYING) {
// Start tracking progress once the video starts playing
const interval = setInterval(() => {
if (playerRef.current) {
const currentTime = playerRef.current.getCurrentTime(); // Get current time in seconds
setProgress(currentTime * 1000); // Convert to milliseconds
// Check if the video is 20 seconds from ending
if (currentTime >= duration / 1000 - 20 && !isNearEnd) {
setIsNearEnd(true);
} else if (currentTime < duration / 1000 - 20 && isNearEnd) {
setIsNearEnd(false);
}
}
}, 100); // Update every 100 ms
// Clean up when the video is paused or finished
event.target.addEventListener('onStateChange', (e) => {
if (e.data === window.YT.PlayerState.PAUSED || e.data === window.YT.PlayerState.ENDED) {
clearInterval(interval);
// When the video ends, set the next track
if (e.data === window.YT.PlayerState.ENDED) {
// Logic to set the next video ID (for now, just updating to another static video)
setCurrentTrack(nextTrack); // Replace 'newVideoId' with the ID of the next video you want
}
}
});
}
};
const handlePlayerReady = (event) => {
playerRef.current = event.target;
const durationInSeconds = playerRef.current.getDuration(); // Get video duration in seconds
setDuration(durationInSeconds * 1000); // Set the duration in milliseconds
// Set the video quality to the lowest available (typically 360p or smaller)
playerRef.current.setPlaybackQuality('small');
};
useEffect(() => {
// Make sure to load the YouTube iframe API script when the component mounts
const script = document.createElement('script');
script.src = 'https://www.youtube.com/iframe_api';
document.body.appendChild(script);
return () => {
// Cleanup if needed (for example, removing the script)
document.body.removeChild(script);
};
}, []);
useEffect(() => {
// When the currentTrack changes, reset progress and duration
setProgress(0);
setDuration(0);
setIsNearEnd(false);
}, [currentTrack]);
return (
<div className="App" style={{visibility: 'hidden', position: 'fixed'}}>
{currentTrack != null && (
<div>
<YouTube
videoId={currentTrack.videoId} // Dynamically change video based on currentTrack
opts={{
height: '315',
width: '560',
playerVars: {
autoplay: 1,
controls: 1,
mute: 0,
loop: 0, // Do not loop; handle next video manually
quality: 'small', // Request small quality (360p or lower)
},
}}
onReady={handlePlayerReady} // Get duration and set quality on ready
onStateChange={handlePlayerStateChange}
/>
</div>
)}
<div>Progress: {progress} ms</div>
<div>Video Duration: {duration} ms</div>
<div>
{isNearEnd && <div>Video is near the end (20 seconds left)</div>}
</div>
</div>
);
}

View File

@@ -59,8 +59,8 @@ const Dashboard = ({ user, setModal }) => {
if (user.roleId < 1) { if (user.roleId < 1) {
// Create admin functionality // Create admin functionality
createCafeOwner(newItem.email, newItem.username, newItem.password) createCafeOwner(newItem.email, newItem.username, newItem.password)
.then(() => { .then((newitem) => {
setItems([...items, { name: newItem.username }]); setItems([...items, { userId: newitem.userId, name: newitem.username }]);
setIsCreating(false); setIsCreating(false);
setNewItem({ name: "", type: "" }); setNewItem({ name: "", type: "" });
}) })
@@ -70,8 +70,8 @@ const Dashboard = ({ user, setModal }) => {
} else { } else {
// Create cafe functionality // Create cafe functionality
createCafe(newItem.name) createCafe(newItem.name)
.then(() => { .then((newitem) => {
setItems([...items, { name: newItem.name }]); setItems([...items, { cafeId: newitem.cafeId, name: newitem.name }]);
setIsCreating(false); setIsCreating(false);
setNewItem({ name: "", type: "" }); setNewItem({ name: "", type: "" });
}) })
@@ -126,8 +126,7 @@ const Dashboard = ({ user, setModal }) => {
className={styles.rectangle} className={styles.rectangle}
> >
<h1>{item.name || item.username}</h1> <h1>{item.name || item.username}</h1>
<div><h1>{item.report?.totalIncome}</h1></div>
<div><h1>{item.report.totalIncome}</h1><h1>{item.report.totalIncome}</h1></div>
</div> </div>
))} ))}
{user && user.roleId < 1 ? ( {user && user.roleId < 1 ? (

View File

@@ -3,27 +3,42 @@ import React from "react";
const NotificationBlocked = () => { const NotificationBlocked = () => {
return ( return (
<div style={styles.container}> <div style={styles.container}>
<h2 style={styles.header}>Heads Up! Notifications Are Off</h2> <h2 style={styles.header}>Notifikasi Terblokir</h2>
<p style={styles.message}> <p style={styles.message}>
It looks like youve got notifications turned off. Turning them on will Sepertinya notifikasi untuk situs ini tidak aktif. Aktifkan notifikasi supaya kamu tetap dapat info pesanan, meski sedang buka aplikasi lain.
make sure you get important updates, like new orders or alerts, right on </p>
your device. <p style={styles.message}>
Berikut cara mengaktifkannya:
</p> </p>
<h3 style={styles.instructionsHeader}>Heres how to turn them on:</h3>
<ol style={styles.instructions}> <ol style={styles.instructions}>
<li>Open Chrome and go to our café's website.</li>
<li>Tap the menu (three dots) in the top-right corner.</li>
<li> <li>
Go to <strong>Settings</strong> &gt; <strong>Site settings</strong>{" "} Klik ikon{" "}
&gt; <strong>Notifications</strong>. <span style={styles.icon}>
<svg
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
fill="#000000"
style={{ verticalAlign: "middle", display: "inline-block" }}
>
<g id="SVGRepo_bgCarrier" strokeWidth="0"></g>
<g id="SVGRepo_tracerCarrier" strokeLinecap="round" strokeLinejoin="round"></g>
<g id="SVGRepo_iconCarrier">
<path
fill="#000000"
fillRule="evenodd"
d="M4,8 C5.240909,8 6.30584855,8.75341602 6.76233298,9.8277624 L6.82929,10 L14,10 C14.5523,10 15,10.4477 15,11 C15,11.51285 14.613973,11.9355092 14.1166239,11.9932725 L14,12 L6.82929,12 C6.41746,13.1652 5.30622,14 4,14 C2.34315,14 1,12.6569 1,11 C1,9.34315 2.34315,8 4,8 Z M4,10 C3.44772,10 3,10.4477 3,11 C3,11.5523 3.44772,12 4,12 C4.55228,12 5,11.5523 5,11 C5,10.4477 4.55228,10 4,10 Z M12,2 C13.6569,2 15,3.34315 15,5 C15,6.65685 13.6569,8 12,8 C10.75911,8 9.69415335,7.24658397 9.23766716,6.1722376 L9.17071,6 L2,6 C1.44772,6 1,5.55228 1,5 C1,4.48716857 1.38604429,4.06449347 1.88337975,4.0067278 L2,4 L9.17071,4 C9.58254,2.83481 10.6938,2 12,2 Z M12,4 C11.4477,4 11,4.44772 11,5 C11,5.55228 11.4477,6 12,6 C12.5523,6 13,5.55228 13,5 C13,4.44772 12.5523,4 12,4 Z"
></path>
</g>
</svg>
</span>{" "}
di pojok kiri atas
</li> </li>
<li> <li>
Find our café in the list and set it to <strong>Allow</strong>. Pilih <strong>Izin</strong> &gt; <strong>Notifikasi</strong> &gt; <strong>Izinkan Notifikasi</strong>.
</li> </li>
</ol> </ol>
<p style={styles.footer}> <p style={styles.footer}>
Once youve turned on notifications, youll start getting updates Setelah itu, kamu bisa terus dapet informasi pesanan meski gak lagi buka situs ini.
instantly. Need a hand? Just ask!
</p> </p>
</div> </div>
); );
@@ -44,11 +59,7 @@ const styles = {
color: "#e74c3c", color: "#e74c3c",
}, },
message: { message: {
marginBottom: "20px", marginBottom: "15px",
},
instructionsHeader: {
marginTop: "20px",
fontWeight: "bold",
}, },
instructions: { instructions: {
listStyleType: "decimal", listStyleType: "decimal",
@@ -59,6 +70,13 @@ const styles = {
marginTop: "20px", marginTop: "20px",
fontStyle: "italic", fontStyle: "italic",
}, },
icon: {
display: "inline-block",
verticalAlign: "middle",
width: "20px",
height: "20px",
marginBottom: '6px'
},
}; };
export default NotificationBlocked; export default NotificationBlocked;

View File

@@ -0,0 +1,57 @@
import React from "react";
import styles from "./Transactions.module.css";
import { requestNotificationPermission } from '../services/notificationService'; // Import the notification service
export default function Transaction_pending({ setModal }) {
// const containerStyle = {
// display: "flex",
// justifyContent: "center",
// alignItems: "center",
// width: "100%",
// height: "100%",
// backgroundColor: "",
// };
const handleNotificationClick = async () => {
const permission = await requestNotificationPermission();
if (permission === "granted") {
console.log("Notification permission granted.");
// Set up notifications or show a success modal
} else {
console.error("Notification permission denied.");
setModal('blocked_notification'); // Show modal for blocked notifications
}
};
return (
<div className={styles.Transactions}>
<div style={{ marginTop: "30px", textAlign: "center" }}>
<h2>Aktifkan Notifikasi</h2>
<img
className={styles.expression}
src="https://i.imgur.com/sgvMI02.png"
alt="Success"
/>
<p style={{ marginTop: "20px", color: "black" }}>
Sepertinya notifikasi untuk situs ini tidak aktif. Aktifkan notifikasi supaya kamu tetap dapat info pesanan, meski sedang buka aplikasi lain.
</p>
<button
onClick={handleNotificationClick}
style={{
marginTop: "10px",
padding: "10px 20px",
fontSize: "16px",
cursor: "pointer",
backgroundColor: "#4CAF50",
color: "#fff",
border: "none",
borderRadius: "5px",
}}
>
Aktifkan
</button>
</div>
</div>
);
}