Merge commit '1936a9cb78a25629d8e4f3b4a107d2ad42e87a60' into production

This commit is contained in:
Dobromir Popov
2024-06-25 17:54:56 +03:00
12 changed files with 272 additions and 102 deletions

View File

@ -0,0 +1,37 @@
import React from 'react';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
this.logger = null;
if (typeof window === 'undefined') {
this.logger = require('../src/logger');
}
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error, info) {
// Log the error to an error reporting service
console.error(error, info);
if (this.logger) {
this.logger.error(`${error}: ${info.componentStack}`);
}
}
render() {
if (this.state.hasError) {
// Render any custom fallback UI
return <h1>Нещо се обърка при изтриването. Моля, опитай отново и се свържете с нас ако проблема продължи. </h1>;
}
return this.props.children;
}
}
export default ErrorBoundary;

View File

@ -35,6 +35,7 @@ function PwaManager({ subs }) {
useEffect(() => {
if (isSupported()) {
setNotificationPermission(Notification.permission);
getSubscriptionCount();
}
// Handle Push Notification Subscription
@ -77,6 +78,10 @@ function PwaManager({ subs }) {
window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt);
window.removeEventListener('appinstalled', handleAppInstalled);
};
}, []);
@ -127,6 +132,7 @@ function PwaManager({ subs }) {
throw new Error("Failed to fetch VAPID public key from server.");
}
}
const sub = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: common.base64ToUint8Array(vapidPublicKey)
@ -197,6 +203,19 @@ function PwaManager({ subs }) {
}
};
const getSubscriptionCount = async () => {
try {
const response = await fetch('/api/notify?id=' + session.user.id, { method: 'GET' });
if (!response.ok) {
throw new Error('Failed to fetch subscription data.');
}
const result = await response.json();
setSubs(result.subs);
} catch (error) {
console.error('Error fetching subscription data:', error);
}
};
// Function to request push notification permission
const requestNotificationPermission = async (e) => {
e.preventDefault();
@ -243,48 +262,56 @@ function PwaManager({ subs }) {
headers: {
'Content-Type': 'application/json'
},
//sends test notification to the current subscription
// body: JSON.stringify({ subscription })
//sends test notification to all subscriptions of this user
body: JSON.stringify({ id: session.user.id, title: "Тестово уведомление", message: "Това е тестово уведомление" })
body: JSON.stringify(
{
id: session.user.id,
title: "Тестово уведомление",
message: "Това е тестово уведомление",
actions: [{ action: 'test', title: 'Тест', icon: '✅' },
{ action: 'close', title: 'Затвори', icon: '❌' }]
})
});
};
async function sendTestReminder(event: MouseEvent<HTMLButtonElement, MouseEvent>): Promise<void> {
event.preventDefault();
if (!subscription) {
console.error('Web push not subscribed');
return;
}
// async function sendTestReminder(event: MouseEvent<HTMLButtonElement, MouseEvent>): Promise<void> {
// event.preventDefault();
// if (!subscription) {
// console.error('Web push not subscribed');
// return;
// }
await fetch('/api/notify', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ broadcast: true, message: "Мили братя, искаме да ви напомним да ни изпратите вашите предпочитания за юни до 25-то число като използвате меню 'Възможности'. Ако имате проблем, моля пишете ни на 'specialnosvidetelstvanesofia@gmail.com'" })
});
}
// await fetch('/api/notify', {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json'
// },
// body: JSON.stringify({
// broadcast: true,
// message: "Мили братя, искаме да ви напомним да ни изпратите вашите предпочитания за юни до 25-то число като използвате меню 'Възможности'. Ако имате проблем, моля пишете ни на 'specialnosvidetelstvanesofia@gmail.com'"
// })
// });
// }
async function sendTestCoverMe(event: MouseEvent<HTMLButtonElement, MouseEvent>): Promise<void> {
event.preventDefault();
if (!subscription) {
console.error('Web push not subscribed');
return;
}
// async function sendTestCoverMe(event: MouseEvent<HTMLButtonElement, MouseEvent>): Promise<void> {
// event.preventDefault();
// if (!subscription) {
// console.error('Web push not subscribed');
// return;
// }
await fetch('/api/notify', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
broadcast: true, message: "Брат ТЕСТ търси заместник за 24-ти май от 10:00 ч. Можеш ли да го покриеш?",
//use fontawesome icons for actions
actions: [{ action: 'covermeaccepted', title: 'Да ', icon: '✅' }]
})
});
}
// await fetch('/api/notify', {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json'
// },
// body: JSON.stringify({
// id: session.user.id,
// message: "Брат ТЕСТ търси заместник за 24-ти май от 10:00 ч. Можеш ли да го покриеш?",
// //use fontawesome icons for actions
// actions: [{ action: 'covermeaccepted', title: 'Да ', icon: '✅' }]
// })
// });
// }
async function deleteAllSubscriptions(event: MouseEvent<HTMLButtonElement, MouseEvent>): Promise<void> {
event.preventDefault();
@ -358,22 +385,22 @@ function PwaManager({ subs }) {
</div>
{isAdmin &&
<div>
<button
{/* <button
onClick={sendTestReminder}
disabled={!isSubscribed}
className={`text-xs py-1 px-2 rounded-full focus:outline-none transition duration-150 ease-in-out ${!isSubscribed ? 'cursor-not-allowed bg-gray-300 text-gray-500' : 'bg-yellow-500 hover:bg-yellow-600 text-white'
}`}
>
Broadcast Reminder
</button>
<button
</button> */}
{/* <button
onClick={sendTestCoverMe}
disabled={!isSubscribed}
className={`text-xs py-1 px-2 rounded-full focus:outline-none transition duration-150 ease-in-out ${!isSubscribed ? 'cursor-not-allowed bg-gray-300 text-gray-500' : 'bg-yellow-500 hover:bg-yellow-600 text-white'
}`}
>
Broadcast CoverMe
</button>
</button> */}
</div>
}
{notificationPermission !== "granted" && (

View File

@ -10,6 +10,7 @@ import Body from 'next/document'
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { set } from "date-fns"
import ErrorBoundary from "./ErrorBoundary";
export default function Layout({ children }) {
const router = useRouter();
@ -61,7 +62,9 @@ export default function Layout({ children }) {
<Sidebar isSidebarOpen={isSidebarOpen} toggleSidebar={toggleSidebar} />
<main className={`flex-1 transition-all duration-300 ${marginLeftClass}`}>
<div className="">
{children}
<ErrorBoundary>
{children}
</ErrorBoundary>
</div>
<div id="modal-root"></div> {/* Modal container */}
</main>

View File

@ -203,6 +203,29 @@ const SurveyForm: React.FC<SurveyFormProps> = ({ existingItem }) => {
(err) => alert('Не успяхме да копираме имената: ', err)
);
};
const sendIndividualNotification = async (id, message) => {
const response = await fetch('/api/notify', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
id,
title: 'Нямаме отговор',
message: `${message}`,
})
});
if (response.ok) {
console.log(`Notification sent successfully to ${name}`);
} else {
console.error(`Failed to send notification to ${name}`);
}
};
const handleSendNotificationsToAllUnanswered = async (message) => {
getIdsForUnanswered().forEach((id, index) => sendIndividualNotification(id, message));
};
return (
<div className="w-full max-w-md mx-auto" >
@ -284,6 +307,26 @@ const SurveyForm: React.FC<SurveyFormProps> = ({ existingItem }) => {
{item.messages ? item.messages.filter((message) => !message.answer).length : 0}
</div>
</div>
<button
className="mt-2 px-4 py-2 bg-blue-500 text-white rounded"
onClick={() => { handleSendNotificationsToAllUnanswered(item?.content) }}>
Подсети ВСИЧКИ неотховорили с нотификация
</button>
<div className="mt-2">
{getIdsForUnanswered().map((id) => {
const pub = pubs.find((p) => p.id === id);
const name = pub ? `${pub.firstName} ${pub.lastName}` : '???';
return (
<button
key={id}
// className="block mt-1 px-1 py-0.5 bg-orange-500 text-white rounded"
className="mt-1 px-2 py-1 bg-green-500 text-white rounded text-xs"
onClick={() => sendIndividualNotification(id, item?.content)}>
{name}
</button>
);
})}
</div>
</div>
</div>
)}