diff --git a/client/src/components/toasts/ToastLayout.tsx b/client/src/components/toasts/ToastLayout.tsx
new file mode 100644
index 0000000..8f3567d
--- /dev/null
+++ b/client/src/components/toasts/ToastLayout.tsx
@@ -0,0 +1,61 @@
+import clsx from "clsx";
+import React from "react";
+import Button from "../ui/Button";
+import useToastsStore from "../../store/toastsStore";
+
+export interface ToastLayoutProps {
+ id: string;
+ type: "notification" | "warning";
+ title: string;
+ message: string;
+ onDeny: () => void;
+ onAllow: () => void;
+ image?: string;
+ icon?: React.ReactNode;
+}
+
+export default function ToastLayout({
+ id,
+ type,
+ title,
+ message,
+ onDeny,
+ onAllow,
+}: ToastLayoutProps) {
+ const { removeToast } = useToastsStore();
+
+ function handleDeny() {
+ onDeny();
+ removeToast(id);
+ }
+
+ function handleAllow() {
+ onAllow();
+ removeToast(id);
+ }
+
+ return (
+
+
+
+ {title}
+
+
{message}
+
+
+
+
+
+
+
+ );
+}
diff --git a/client/src/components/toasts/ToastsContainer.tsx b/client/src/components/toasts/ToastsContainer.tsx
new file mode 100644
index 0000000..fe7f8b6
--- /dev/null
+++ b/client/src/components/toasts/ToastsContainer.tsx
@@ -0,0 +1,31 @@
+import useToastsStore from "../../store/toastsStore";
+import ToastLayout from "./ToastLayout";
+import { AnimatePresence, motion } from "motion/react";
+
+export default function ToastsContainer() {
+ const { toasts } = useToastsStore();
+
+ return (
+
+
+ {toasts.map((toast) => (
+
+
+
+ ))}
+
+
+ );
+}
diff --git a/client/src/main.tsx b/client/src/main.tsx
index 16220df..5f0f989 100644
--- a/client/src/main.tsx
+++ b/client/src/main.tsx
@@ -11,6 +11,7 @@ import ProtectedRoute from "./components/ProtectedRoute";
import PublicRoute from "./components/PublicRoute";
import ModalContainer from "./components/ModalContainer";
import PopupContainer from "./components/PopupContainer";
+import ToastsContainer from "./components/toasts/ToastsContainer";
// import NewSessionPage from "./pages/NewSessionPage";
import TestPage from "./pages/TestPage";
import NewSessionPage from "./pages/NewSessionPage";
@@ -55,5 +56,6 @@ createRoot(document.getElementById("root")!).render(
+
);
diff --git a/client/src/pages/HomePage.tsx b/client/src/pages/HomePage.tsx
index ae9a568..73412f8 100644
--- a/client/src/pages/HomePage.tsx
+++ b/client/src/pages/HomePage.tsx
@@ -9,6 +9,8 @@ import useModalStore from "../store/modalStore";
import CogFilledIcon from "../components/icons/CogFilledIcon";
import SessionUsersPanel from "../components/SessionUsersPanel";
import SharePopup from "../components/popups/SharePopup";
+import { useEffect } from "react";
+import useToastsStore from "../store/toastsStore";
function HomePage() {
const { data: user } = useMe();
@@ -23,6 +25,31 @@ function HomePage() {
const { setPopup } = usePopupStore();
const { setModal } = useModalStore();
+ // -------------------------------- Toasts test --------------------------------
+ const { addToast } = useToastsStore();
+
+ useEffect(() => {
+ addToast({
+ id: crypto.randomUUID(),
+ type: "warning",
+ title: "Тестовое предупреждение",
+ message: "Это тестовое предупреждение",
+ onDeny: () => {},
+ onAllow: () => {},
+ });
+ const timer = setTimeout(() => {
+ addToast({
+ id: crypto.randomUUID(),
+ type: "notification",
+ title: "Тестовое уведомление",
+ message: "Это тестовое уведомление",
+ onDeny: () => {},
+ onAllow: () => {},
+ });
+ }, 1000);
+ return () => clearTimeout(timer);
+ }, []);
+
return (
diff --git a/client/src/store/toastsStore.ts b/client/src/store/toastsStore.ts
new file mode 100644
index 0000000..956ce27
--- /dev/null
+++ b/client/src/store/toastsStore.ts
@@ -0,0 +1,24 @@
+import { create } from "zustand";
+import type { ToastLayoutProps } from "../components/toasts/ToastLayout";
+
+interface ToastState {
+ toasts: ToastLayoutProps[];
+ addToast: (toast: ToastLayoutProps) => void;
+ removeToast: (id: ToastLayoutProps["id"]) => void;
+ clearToasts: () => void;
+}
+
+const useToastsStore = create((set) => ({
+ toasts: [],
+ addToast: (toast) =>
+ set((state) => ({
+ toasts: [...state.toasts, toast],
+ })),
+ removeToast: (id) =>
+ set((state) => ({
+ toasts: state.toasts.filter((toast) => toast.id !== id),
+ })),
+ clearToasts: () => set({ toasts: [] }),
+}));
+
+export default useToastsStore;
\ No newline at end of file