ok
This commit is contained in:
50
package-lock.json
generated
50
package-lock.json
generated
@@ -26,6 +26,7 @@
|
||||
"react-router-dom": "^6.24.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-switch": "^7.0.0",
|
||||
"react-youtube": "^10.1.0",
|
||||
"smooth-scroll-into-view-if-needed": "^2.0.2",
|
||||
"socket.io-client": "^4.7.5",
|
||||
"styled-components": "^6.1.11",
|
||||
@@ -13339,6 +13340,11 @@
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||
"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": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
|
||||
@@ -16220,6 +16226,22 @@
|
||||
"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": {
|
||||
"version": "1.0.0",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
|
||||
@@ -19567,6 +19594,29 @@
|
||||
"funding": {
|
||||
"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=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"react-router-dom": "^6.24.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-switch": "^7.0.0",
|
||||
"react-youtube": "^10.1.0",
|
||||
"smooth-scroll-into-view-if-needed": "^2.0.2",
|
||||
"socket.io-client": "^4.7.5",
|
||||
"styled-components": "^6.1.11",
|
||||
|
||||
12
src/App.js
12
src/App.js
@@ -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) => {
|
||||
if (data.status !== 200) {
|
||||
removeLocalStorage("auth");
|
||||
@@ -226,7 +235,7 @@ function App() {
|
||||
console.log("getting guest side");
|
||||
setDeviceType("clerk");
|
||||
|
||||
// checkNotifications(data.data.user.userId);
|
||||
checkNotifications();
|
||||
} else {
|
||||
setDeviceType("guestDevice");
|
||||
}
|
||||
@@ -315,6 +324,7 @@ function App() {
|
||||
navigate({ search: queryParams.toString() }, { replace: true });
|
||||
}
|
||||
};
|
||||
|
||||
// useEffect(() => {
|
||||
// const askNotificationPermission = async () => {
|
||||
// let permission = Notification.permission;
|
||||
|
||||
@@ -14,6 +14,7 @@ import Payment_claimed from "../pages/Payment_claimed";
|
||||
import MaterialList from "../pages/MaterialList.js";
|
||||
import MaterialMutationsPage from "../pages/MaterialMutationsPage.js";
|
||||
import Reports from "../pages/Reports.js";
|
||||
import NotificationRequest from "../pages/NotificationRequest.js";
|
||||
import NotificationBlocked from "../pages/NotificationBlocked.js";
|
||||
import WelcomePageEditor from "../pages/WelcomePageEditor.js";
|
||||
import GuidePage from "../pages/GuidePage";
|
||||
@@ -34,7 +35,7 @@ const Modal = ({ shop, isOpen, onClose, modalContent, setModal }) => {
|
||||
return (
|
||||
<div onClick={handleOverlayClick} className={styles.modalOverlay}>
|
||||
<div className={styles.modalContent} onClick={handleContentClick}>
|
||||
{modalContent === "req_notification" && <NotificationBlocked />}
|
||||
{modalContent === "req_notification" && <NotificationRequest setModal={setModal} />}
|
||||
{modalContent === "blocked_notification" && <NotificationBlocked />}
|
||||
{modalContent === "create_clerk" && <CreateClerk shopId={shop.cafeId} />}
|
||||
{modalContent === "edit_tables" && <TablesPage shop={shop} />}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
right: -1px;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
|
||||
108
src/components/TrackPlayer.js
Normal file
108
src/components/TrackPlayer.js
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -59,8 +59,8 @@ const Dashboard = ({ user, setModal }) => {
|
||||
if (user.roleId < 1) {
|
||||
// Create admin functionality
|
||||
createCafeOwner(newItem.email, newItem.username, newItem.password)
|
||||
.then(() => {
|
||||
setItems([...items, { name: newItem.username }]);
|
||||
.then((newitem) => {
|
||||
setItems([...items, { userId: newitem.userId, name: newitem.username }]);
|
||||
setIsCreating(false);
|
||||
setNewItem({ name: "", type: "" });
|
||||
})
|
||||
@@ -70,8 +70,8 @@ const Dashboard = ({ user, setModal }) => {
|
||||
} else {
|
||||
// Create cafe functionality
|
||||
createCafe(newItem.name)
|
||||
.then(() => {
|
||||
setItems([...items, { name: newItem.name }]);
|
||||
.then((newitem) => {
|
||||
setItems([...items, { cafeId: newitem.cafeId, name: newitem.name }]);
|
||||
setIsCreating(false);
|
||||
setNewItem({ name: "", type: "" });
|
||||
})
|
||||
@@ -126,8 +126,7 @@ const Dashboard = ({ user, setModal }) => {
|
||||
className={styles.rectangle}
|
||||
>
|
||||
<h1>{item.name || item.username}</h1>
|
||||
|
||||
<div><h1>{item.report.totalIncome}</h1><h1>{item.report.totalIncome}</h1></div>
|
||||
<div><h1>{item.report?.totalIncome}</h1></div>
|
||||
</div>
|
||||
))}
|
||||
{user && user.roleId < 1 ? (
|
||||
|
||||
@@ -3,27 +3,42 @@ import React from "react";
|
||||
const NotificationBlocked = () => {
|
||||
return (
|
||||
<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}>
|
||||
It looks like you’ve got notifications turned off. Turning them on will
|
||||
make sure you get important updates, like new orders or alerts, right on
|
||||
your device.
|
||||
Sepertinya notifikasi untuk situs ini tidak aktif. Aktifkan notifikasi supaya kamu tetap dapat info pesanan, meski sedang buka aplikasi lain.
|
||||
</p>
|
||||
<p style={styles.message}>
|
||||
Berikut cara mengaktifkannya:
|
||||
</p>
|
||||
<h3 style={styles.instructionsHeader}>Here’s how to turn them on:</h3>
|
||||
<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>
|
||||
Go to <strong>Settings</strong> > <strong>Site settings</strong>{" "}
|
||||
> <strong>Notifications</strong>.
|
||||
Klik ikon{" "}
|
||||
<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>
|
||||
Find our café in the list and set it to <strong>Allow</strong>.
|
||||
Pilih <strong>Izin</strong> > <strong>Notifikasi</strong> > <strong>Izinkan Notifikasi</strong>.
|
||||
</li>
|
||||
</ol>
|
||||
<p style={styles.footer}>
|
||||
Once you’ve turned on notifications, you’ll start getting updates
|
||||
instantly. Need a hand? Just ask!
|
||||
Setelah itu, kamu bisa terus dapet informasi pesanan meski gak lagi buka situs ini.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
@@ -44,11 +59,7 @@ const styles = {
|
||||
color: "#e74c3c",
|
||||
},
|
||||
message: {
|
||||
marginBottom: "20px",
|
||||
},
|
||||
instructionsHeader: {
|
||||
marginTop: "20px",
|
||||
fontWeight: "bold",
|
||||
marginBottom: "15px",
|
||||
},
|
||||
instructions: {
|
||||
listStyleType: "decimal",
|
||||
@@ -59,6 +70,13 @@ const styles = {
|
||||
marginTop: "20px",
|
||||
fontStyle: "italic",
|
||||
},
|
||||
icon: {
|
||||
display: "inline-block",
|
||||
verticalAlign: "middle",
|
||||
width: "20px",
|
||||
height: "20px",
|
||||
marginBottom: '6px'
|
||||
},
|
||||
};
|
||||
|
||||
export default NotificationBlocked;
|
||||
|
||||
57
src/pages/NotificationRequest.js
Normal file
57
src/pages/NotificationRequest.js
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user