From 1d110bc868a9411e0ab88d3075bd12925bda6ebe Mon Sep 17 00:00:00 2001 From: namnd Date: Mon, 22 Sep 2025 11:31:32 +0700 Subject: [PATCH 1/4] redesign login admin page, refactor admin login fn --- src/components/Form/FormField.jsx | 60 +++++++ src/configs/apiEndpoints.js | 11 ++ .../{axiosConfig.jsx => axiosConfig.js} | 11 +- ...axiosUserConfig.jsx => axiosUserConfig.js} | 0 src/context/AdminContext.jsx | 9 +- .../app-layouts/admin-layouts/index.jsx | 4 +- .../app-layouts/author-layouts/index.jsx | 4 +- .../auth-layouts/admin-layouts/index.jsx | 71 +++++++- src/pages/auth-pages/admin-auth/Login.jsx | 151 ++++++++++++------ ...inAuthServices.js => AdminAuthServices.js} | 22 ++- src/styles/admin-auth.css | 122 ++++++++++++++ src/utils/adminAuthUtils.js | 47 ++++++ src/utils/logoutAdmin.js | 4 +- src/validation/auth/adminAuthValidation.js | 54 +++++++ src/validation/auth/index.js | 1 + 15 files changed, 503 insertions(+), 68 deletions(-) create mode 100644 src/components/Form/FormField.jsx create mode 100644 src/configs/apiEndpoints.js rename src/configs/{axiosConfig.jsx => axiosConfig.js} (83%) rename src/configs/{axiosUserConfig.jsx => axiosUserConfig.js} (100%) rename src/services/authServices/{adminAuthServices.js => AdminAuthServices.js} (57%) create mode 100644 src/styles/admin-auth.css create mode 100644 src/utils/adminAuthUtils.js create mode 100644 src/validation/auth/adminAuthValidation.js create mode 100644 src/validation/auth/index.js diff --git a/src/components/Form/FormField.jsx b/src/components/Form/FormField.jsx new file mode 100644 index 0000000..4fa6967 --- /dev/null +++ b/src/components/Form/FormField.jsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { Form, Input } from 'antd'; +import PropTypes from 'prop-types'; +import { FORM_FIELD_CONFIGS } from '@/utils/formValidation'; + +const FormField = ({ + name, + label, + placeholder, + rules, + prefix, + type = 'text', + className = 'login-form-field', + showLabel = true, + autoComplete, + maxLength, + ...props +}) => { + const InputComponent = type === 'password' ? Input.Password : Input; + + return ( + + {label} + + )} + name={name} + hasFeedback + className={className} + rules={rules} + > + + + ); +}; + +FormField.propTypes = { + name: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + placeholder: PropTypes.string, + rules: PropTypes.array, + prefix: PropTypes.node, + type: PropTypes.oneOf(['text', 'password', 'email']), + className: PropTypes.string, + showLabel: PropTypes.bool, + autoComplete: PropTypes.string, + maxLength: PropTypes.number, +}; + +export default FormField; \ No newline at end of file diff --git a/src/configs/apiEndpoints.js b/src/configs/apiEndpoints.js new file mode 100644 index 0000000..18fe2d8 --- /dev/null +++ b/src/configs/apiEndpoints.js @@ -0,0 +1,11 @@ +const AUTH_ENDPOINTS = { + ADMIN: { + LOGIN: "/auth/admin/login", + REFRESH_TOKEN: "/auth/admin/refresh-token", + LOGOUT: "/auth/admin/logout", + CHECK_ROLE: "/auth/admin/check-role", + CURRENT: "/auth/admin/current", + }, +}; + +export { AUTH_ENDPOINTS }; \ No newline at end of file diff --git a/src/configs/axiosConfig.jsx b/src/configs/axiosConfig.js similarity index 83% rename from src/configs/axiosConfig.jsx rename to src/configs/axiosConfig.js index 1ea699d..c53ed47 100644 --- a/src/configs/axiosConfig.jsx +++ b/src/configs/axiosConfig.js @@ -1,5 +1,6 @@ import axios from "axios"; import { notification } from "antd"; +import AdminAuthServices from "../services/authServices/AdminAuthServices"; // import axiosRetry from "axios-retry"; const axiosInstance = axios.create({ @@ -42,7 +43,7 @@ axiosInstance.interceptors.response.use( function (response) { return response; }, - function (error) { + async function (error) { // if (axiosRetry.isNetworkError(error)) { // notification.error({ // message: "Lỗi", @@ -64,7 +65,13 @@ axiosInstance.interceptors.response.use( description: error?.response?.data?.message, }); } else { - return Promise.reject(error); + const response = await AdminAuthServices.refreshToken() + const token = response?.accessToken; + if (token) { + const authData = { token }; + const authString = JSON.stringify(authData); + localStorage.setItem("admin", authString); + } } } else if (error?.response?.status !== 422) { notification.error({ diff --git a/src/configs/axiosUserConfig.jsx b/src/configs/axiosUserConfig.js similarity index 100% rename from src/configs/axiosUserConfig.jsx rename to src/configs/axiosUserConfig.js diff --git a/src/context/AdminContext.jsx b/src/context/AdminContext.jsx index 92e5567..4f7af1f 100644 --- a/src/context/AdminContext.jsx +++ b/src/context/AdminContext.jsx @@ -4,7 +4,14 @@ import PropTypes from "prop-types"; const AdminContext = createContext(); const AdminContextProvider = ({ children }) => { - const [admin, setAdmin] = useState(JSON.parse(localStorage.getItem("admin"))); + const [admin, setAdmin] = useState(() => { + try { + const stored = localStorage.getItem("admin"); + return stored ? JSON.parse(stored) : null; + } catch (e) { + return null; + } + }); return ( {children} diff --git a/src/layouts/app-layouts/admin-layouts/index.jsx b/src/layouts/app-layouts/admin-layouts/index.jsx index e31e1e5..1a288ab 100644 --- a/src/layouts/app-layouts/admin-layouts/index.jsx +++ b/src/layouts/app-layouts/admin-layouts/index.jsx @@ -5,7 +5,7 @@ import HeaderNav from "./HeaderNav"; import SideBar from "./SideBar"; import BreadCrumbCustom from "@/components/BreadCrumbCustom"; import { AdminContext } from "@/context/AdminContext"; -import adminAuthServices from "@/services/authServices/adminAuthServices"; +import AdminAuthServices from "@/services/authServices/AdminAuthServices"; import logoutAdmin from "@/utils/logoutAdmin"; const { Content } = Layout; @@ -22,7 +22,7 @@ const AdminLayout = () => { } const getUserInfo = async () => { try { - const response = await adminAuthServices.getUserInfo(token); + const response = await AdminAuthServices.getUserInfo(token); if (response) { if (response?.data?.role?.role_name !== "ADMIN") { diff --git a/src/layouts/app-layouts/author-layouts/index.jsx b/src/layouts/app-layouts/author-layouts/index.jsx index 98a447f..9124c86 100644 --- a/src/layouts/app-layouts/author-layouts/index.jsx +++ b/src/layouts/app-layouts/author-layouts/index.jsx @@ -5,7 +5,7 @@ import HeaderNav from "./HeaderNav"; import SideBar from "./SideBar"; import BreadCrumbCustom from "@/components/BreadCrumbCustom"; import { AdminContext } from "@/context/AdminContext"; -import adminAuthServices from "@/services/authServices/adminAuthServices"; +import AdminAuthServices from "@/services/authServices/AdminAuthServices"; import logoutAdmin from "@/utils/logoutAdmin"; const { Content } = Layout; @@ -22,7 +22,7 @@ const AuthorLayout = () => { navigate("/auth/admin/login"); } const getUserInfo = async () => { - const response = await adminAuthServices.getUserInfo(token); + const response = await AdminAuthServices.getUserInfo(token); if (response) { if (response?.data?.role?.role_name !== "AUTHOR") { notification.error({ diff --git a/src/layouts/auth-layouts/admin-layouts/index.jsx b/src/layouts/auth-layouts/admin-layouts/index.jsx index ad06927..7eda11f 100644 --- a/src/layouts/auth-layouts/admin-layouts/index.jsx +++ b/src/layouts/auth-layouts/admin-layouts/index.jsx @@ -1,14 +1,71 @@ -import { Card } from "antd"; +import { Card, Typography } from "antd"; import { Outlet } from "react-router-dom"; import PropTypes from "prop-types"; +import { SafetyCertificateFilled, ReadFilled } from "@ant-design/icons"; +import "@/styles/admin-auth.css"; +import { useState } from "react"; -const AdminAuthLayout = ({ title = "Form đăng nhập quản trị" }) => { +const { Title, Text } = Typography; + +const AdminAuthLayout = ({ title = "Đăng Nhập Quản Trị" }) => { + const [currentYear] = useState(new Date().getFullYear()); return ( -
-
- - - +
+ {/* Background decorative elements */} +
+
+
+
+
+ + {/* Grid pattern overlay */} +
+ +
+
+ {/* Logo and header section */} +
+
+
+ + +
+
+ + News Admin + + + Hệ thống quản trị tin tức + +
+ + {/* Login card */} + +
+ + {title} + + + Vui lòng đăng nhập để tiếp tục + +
+ +
+ + {/* Footer */} +
+ + © {currentYear} News Admin System. All rights reserved. + +
+
); diff --git a/src/pages/auth-pages/admin-auth/Login.jsx b/src/pages/auth-pages/admin-auth/Login.jsx index d6f0007..be86652 100644 --- a/src/pages/auth-pages/admin-auth/Login.jsx +++ b/src/pages/auth-pages/admin-auth/Login.jsx @@ -1,75 +1,134 @@ import { useContext, useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; -import { Form, message } from "antd"; -import adminAuthServices from "@/services/authServices/adminAuthServices"; +import { Form, message, Button, Input, Divider } from "antd"; +import { UserOutlined, LockOutlined, LoginOutlined } from "@ant-design/icons"; +import AdminAuthServices from "@/services/authServices/AdminAuthServices"; import { AdminContext } from "@/context/AdminContext"; -import PasswordInput from "@/components/Input/PasswordInput"; -import ButtonSubmitForm from "@/components/Btn/ButtonSubmitForm"; -import UserNameInput from "@/components/Input/UserNameInput"; +import { ADMIN_FORM_FIELDS } from "@/validation/auth"; + +const ROLE_ROUTES = { + ADMIN: "/admin/dashboard", + AUTHOR: "/author/dashboard", +}; + +const SUPPORTED_ROLES = ["ADMIN", "AUTHOR"]; const Login = () => { const [loading, setLoading] = useState(false); const [form] = Form.useForm(); const { setAdmin } = useContext(AdminContext); const navigate = useNavigate(); + useEffect(() => { + clearAuthData(); + }, []); + + const clearAuthData = () => { localStorage.removeItem("admin"); setAdmin(null); - }, []); + }; + + const storeAuthData = (token) => { + const authData = { token }; + const authString = JSON.stringify(authData); + localStorage.setItem("admin", authString); + setAdmin(authData); + }; + const handleSubmit = async (values) => { setLoading(true); - const response = await adminAuthServices.login(values); + const response = await AdminAuthServices.login(values); if (response) { - const role = await adminAuthServices.checkRole(response?.accessToken); - if (role?.role_name === "ADMIN") { - const admin = { - token: response?.accessToken, - // data: response?.data, - }; - - const adminString = JSON.stringify(admin); - localStorage.setItem("admin", adminString); - setAdmin(admin); - message.success(response?.message); - navigate("/admin/dashboard"); - } else if (role?.role_name === "AUTHOR") { - const author = { - token: response?.accessToken, - // data: response?.data, - }; - const adminString = JSON.stringify(author); - localStorage.setItem("admin", adminString); - setAdmin(author); + const role = await AdminAuthServices.checkRole(response?.accessToken); + const roleName = role?.role_name; + if (SUPPORTED_ROLES.includes(roleName)) { + storeAuthData(response?.accessToken); message.success(response?.message); - navigate("/author/dashboard"); + navigate(ROLE_ROUTES[roleName]); } else { - localStorage.removeItem("admin"); - setAdmin(null); + clearAuthData(); navigate("/auth/admin/login"); } } setLoading(false); }; + const validateMessages = { required: "${label} là bắt buộc", }; - const passwordProps = { - label: "Password", - name: "password", - placeholder: "Nhập password", - }; - return ( -
- - - - + return ( +
+
+ Tên đăng nhập} + name="username" + hasFeedback + className="login-form-field" + rules={ADMIN_FORM_FIELDS.username.rules} + > + } + placeholder="Nhập tên đăng nhập" + size="large" + className="rounded-lg border-gray-300 hover:border-blue-400 focus:border-blue-500 transition-all duration-200" + autoComplete="username" + maxLength="50" + allowClear + /> + + Mật khẩu} + name="password" + hasFeedback + className="login-form-field" + rules={ADMIN_FORM_FIELDS.password.rules} + > + } + placeholder="Nhập mật khẩu" + size="large" + className="rounded-lg border-gray-300 hover:border-blue-400 focus:border-blue-500 transition-all duration-200" + autoComplete="current-password" + maxLength="20" + allowClear + /> + + + + + + Hoặc + +
+

+ Cần hỗ trợ? + + Liên hệ admin + +

+
+
+
); }; diff --git a/src/services/authServices/adminAuthServices.js b/src/services/authServices/AdminAuthServices.js similarity index 57% rename from src/services/authServices/adminAuthServices.js rename to src/services/authServices/AdminAuthServices.js index 57b34ec..b6206f6 100644 --- a/src/services/authServices/adminAuthServices.js +++ b/src/services/authServices/AdminAuthServices.js @@ -1,9 +1,10 @@ import axiosInstance from "@/configs/axiosConfig"; +import { AUTH_ENDPOINTS } from "@/configs/apiEndpoints"; -class adminAuthServices { +class AdminAuthServices { static async login({ username, password }) { try { - const response = await axiosInstance.post("/auth/admin/login", { + const response = await axiosInstance.post(AUTH_ENDPOINTS.ADMIN.LOGIN, { username, password, }); @@ -13,9 +14,18 @@ class adminAuthServices { console.log(error); } } + static async refreshToken() { + try { + const response = await axiosInstance.post(AUTH_ENDPOINTS.ADMIN.REFRESH_TOKEN); + const data = response.data; + return data; + } catch (error) { + console.log(error); + } + } static async logout() { try { - const response = await axiosInstance.get("/auth/admin/logout"); + const response = await axiosInstance.get(AUTH_ENDPOINTS.ADMIN.LOGOUT); const data = response.data; return data; } catch (error) { @@ -24,7 +34,7 @@ class adminAuthServices { } static async checkRole(accessToken) { try { - const response = await axiosInstance.get("/auth/admin/check-role", { + const response = await axiosInstance.get(AUTH_ENDPOINTS.ADMIN.CHECK_ROLE, { headers: { Authorization: `Bearer ${accessToken}`, }, @@ -37,7 +47,7 @@ class adminAuthServices { } static async getUserInfo(accessToken) { try { - const response = await axiosInstance.get("/auth/admin/current", { + const response = await axiosInstance.get(AUTH_ENDPOINTS.ADMIN.CURRENT, { headers: { Authorization: `Bearer ${accessToken}`, }, @@ -50,4 +60,4 @@ class adminAuthServices { } } -export default adminAuthServices; +export default AdminAuthServices; diff --git a/src/styles/admin-auth.css b/src/styles/admin-auth.css new file mode 100644 index 0000000..86cd158 --- /dev/null +++ b/src/styles/admin-auth.css @@ -0,0 +1,122 @@ +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes slideInLeft { + from { + opacity: 0; + transform: translateX(-30px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +@keyframes pulse { + 0%, 100% { + opacity: 0.2; + } + 50% { + opacity: 0.4; + } +} + +@keyframes float { + 0%, 100% { + transform: translateY(0px); + } + 50% { + transform: translateY(-10px); + } +} + +.login-form-container { + animation: fadeInUp 0.8s ease-out; +} + +.login-form-field { + animation: slideInLeft 0.6s ease-out; +} + +.login-form-field:nth-child(1) { + animation-delay: 0.1s; +} + +.login-form-field:nth-child(2) { + animation-delay: 0.2s; +} + +.login-form-field:nth-child(3) { + animation-delay: 0.3s; +} + +.admin-logo { + animation: float 3s ease-in-out infinite; +} + +.ant-input:focus, +.ant-input-password-input:focus { + box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1) !important; + border-color: #3b82f6 !important; +} + +.login-btn:hover { + transform: translateY(-2px); + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15); +} + +.bg-decoration { + animation: pulse 4s ease-in-out infinite; +} + +.bg-decoration:nth-child(2) { + animation-delay: 1s; +} + +.bg-decoration:nth-child(3) { + animation-delay: 2s; +} + +.glass-card { + backdrop-filter: blur(16px); + background: rgba(255, 255, 255, 0.1); + border: 1px solid rgba(255, 255, 255, 0.2); +} + +.ant-btn-loading .ant-btn-loading-icon { + animation: spin 1s linear infinite; +} + +@keyframes spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +@media (max-width: 768px) { + .login-form-container { + padding: 1rem; + } +} + +.ant-message { + animation: slideInLeft 0.5s ease-out; +} + +.ant-form-item-has-error .ant-input, +.ant-form-item-has-error .ant-input-password-input { + animation: shake 0.5s ease-in-out; +} + +@keyframes shake { + 0%, 100% { transform: translateX(0); } + 25% { transform: translateX(-5px); } + 75% { transform: translateX(5px); } +} \ No newline at end of file diff --git a/src/utils/adminAuthUtils.js b/src/utils/adminAuthUtils.js new file mode 100644 index 0000000..9d7f1d4 --- /dev/null +++ b/src/utils/adminAuthUtils.js @@ -0,0 +1,47 @@ +// Utility for creating loading skeleton +export const LoadingSkeleton = () => { + return ( +
+
+
+
+
+
+
+
+
+ ); +}; + +// Utility for enhanced form validation messages +export const getValidationMessage = (field, value) => { + const messages = { + username: { + required: "🔒 Tên đăng nhập là bắt buộc", + minLength: "📏 Tên đăng nhập phải có ít nhất 3 ký tự", + maxLength: "📏 Tên đăng nhập không được quá 50 ký tự", + pattern: "⚠️ Chỉ được sử dụng chữ cái và số" + }, + password: { + required: "🔐 Mật khẩu là bắt buộc", + minLength: "📏 Mật khẩu phải có ít nhất 8 ký tự", + maxLength: "📏 Mật khẩu không được quá 20 ký tự", + pattern: "🔧 Mật khẩu cần có: chữ hoa, chữ thường, số và ký tự đặc biệt" + } + }; + + return messages[field] || {}; +}; + +// Enhanced success messages +export const getSuccessMessage = (type) => { + const messages = { + login: "🎉 Đăng nhập thành công! Chào mừng bạn trở lại.", + logout: "👋 Đăng xuất thành công. Hẹn gặp lại!", + update: "✅ Cập nhật thành công!", + delete: "🗑️ Xóa thành công!", + create: "🆕 Tạo mới thành công!" + }; + + return messages[type] || "✅ Thành công!"; +}; \ No newline at end of file diff --git a/src/utils/logoutAdmin.js b/src/utils/logoutAdmin.js index a8d576d..627f72e 100644 --- a/src/utils/logoutAdmin.js +++ b/src/utils/logoutAdmin.js @@ -1,11 +1,11 @@ -import adminAuthServices from "@/services/authServices/adminAuthServices"; +import AdminAuthServices from "@/services/authServices/AdminAuthServices"; const logoutAdmin = async (setAdmin, navigate) => { try { localStorage.removeItem("admin"); setAdmin(null); navigate("/auth/admin/login"); - // const response = await adminAuthServices.logout(); + // const response = await AdminAuthServices.logout(); // if (response) { // localStorage.removeItem("admin"); // setAdmin(null); diff --git a/src/validation/auth/adminAuthValidation.js b/src/validation/auth/adminAuthValidation.js new file mode 100644 index 0000000..c46e5c5 --- /dev/null +++ b/src/validation/auth/adminAuthValidation.js @@ -0,0 +1,54 @@ +export const ADMIN_VALIDATION_PATTERNS = { + username: /^[a-zA-Z0-9]+$/, + strongPassword: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*(),.?":{}|<>]).*$/, +}; + +export const ADMIN_AUTH_VALIDATION_RULES = { + username: [ + { + required: true, + message: "Vui lòng nhập tên đăng nhập!", + }, + { + min: 3, + message: "Tên đăng nhập phải có ít nhất 3 ký tự!", + }, + { + max: 50, + message: "Tên đăng nhập không được quá 50 ký tự!", + }, + { + pattern: ADMIN_VALIDATION_PATTERNS.username, + message: "Chỉ được sử dụng chữ cái và số, không có ký tự đặc biệt!", + }, + ], + + password: [ + { + required: true, + message: "Vui lòng nhập mật khẩu!", + }, + { + min: 8, + message: "Mật khẩu phải có ít nhất 8 ký tự!", + }, + { + max: 20, + message: "Mật khẩu không được quá 20 ký tự!", + }, + { + pattern: ADMIN_VALIDATION_PATTERNS.strongPassword, + message: "Mật khẩu cần có: chữ hoa, chữ thường, số và ký tự đặc biệt", + }, + ], +}; + +export const ADMIN_FORM_FIELDS = { + username: { + rules: ADMIN_AUTH_VALIDATION_RULES.username, + }, + + password: { + rules: ADMIN_AUTH_VALIDATION_RULES.password, + }, +}; diff --git a/src/validation/auth/index.js b/src/validation/auth/index.js new file mode 100644 index 0000000..f334e73 --- /dev/null +++ b/src/validation/auth/index.js @@ -0,0 +1 @@ +export * from "./adminAuthValidation"; From 529e76dba868839fc42c072797f2ffc391099270 Mon Sep 17 00:00:00 2001 From: namnd Date: Thu, 23 Oct 2025 16:51:37 +0700 Subject: [PATCH 2/4] dashboard ui --- package-lock.json | 38 +- package.json | 3 + src/components/Dashboard/ChartCard.jsx | 43 ++ src/components/Dashboard/StatCard.jsx | 100 ++++ src/components/index.jsx | 4 + src/configs/axiosConfig.js | 103 +++-- src/constants/routeConstants.js | 15 + .../app-layouts/admin-layouts/SideBar.jsx | 90 +++- .../app-layouts/admin-layouts/index.jsx | 9 +- src/pages/app-pages/admin-pages/Dashboard.jsx | 370 +++++++++++++++ .../app-pages/author-pages/Dashboard.jsx | 434 ++++++++++++++++++ src/routers/adminRoutes/index.jsx | 3 +- src/routers/authorRoutes/index.jsx | 3 +- .../adminServices/dashboardServices.js | 240 ++++++++++ src/styles/admin-auth.css | 11 - 15 files changed, 1395 insertions(+), 71 deletions(-) create mode 100644 src/components/Dashboard/ChartCard.jsx create mode 100644 src/components/Dashboard/StatCard.jsx create mode 100644 src/constants/routeConstants.js create mode 100644 src/pages/app-pages/admin-pages/Dashboard.jsx create mode 100644 src/pages/app-pages/author-pages/Dashboard.jsx create mode 100644 src/services/adminServices/dashboardServices.js diff --git a/package-lock.json b/package-lock.json index aee8e64..9e08009 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,8 +15,11 @@ "antd": "^5.10.0", "axios": "^1.5.1", "axios-retry": "^4.0.0", + "chart.js": "^4.5.1", "date-fns": "^2.30.0", + "dayjs": "^1.11.18", "react": "^18.2.0", + "react-chartjs-2": "^5.3.0", "react-dom": "^18.2.0", "react-redux": "^8.1.3", "react-router-dom": "^6.16.0", @@ -1348,6 +1351,12 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@kurkle/color": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", + "license": "MIT" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2117,6 +2126,18 @@ "node": ">=4" } }, + "node_modules/chart.js": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz", + "integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==", + "license": "MIT", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -2295,9 +2316,10 @@ } }, "node_modules/dayjs": { - "version": "1.11.10", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", - "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + "version": "1.11.18", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.18.tgz", + "integrity": "sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==", + "license": "MIT" }, "node_modules/debug": { "version": "4.3.4", @@ -5112,6 +5134,16 @@ "node": ">=0.10.0" } }, + "node_modules/react-chartjs-2": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.3.0.tgz", + "integrity": "sha512-UfZZFnDsERI3c3CZGxzvNJd02SHjaSJ8kgW1djn65H1KK8rehwTjyrRKOG3VTMG8wtHZ5rgAO5oTHtHi9GCCmw==", + "license": "MIT", + "peerDependencies": { + "chart.js": "^4.1.1", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", diff --git a/package.json b/package.json index 78fe6db..7b38c56 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,11 @@ "antd": "^5.10.0", "axios": "^1.5.1", "axios-retry": "^4.0.0", + "chart.js": "^4.5.1", "date-fns": "^2.30.0", + "dayjs": "^1.11.18", "react": "^18.2.0", + "react-chartjs-2": "^5.3.0", "react-dom": "^18.2.0", "react-redux": "^8.1.3", "react-router-dom": "^6.16.0", diff --git a/src/components/Dashboard/ChartCard.jsx b/src/components/Dashboard/ChartCard.jsx new file mode 100644 index 0000000..a82e4c1 --- /dev/null +++ b/src/components/Dashboard/ChartCard.jsx @@ -0,0 +1,43 @@ +import { Card } from 'antd'; +import PropTypes from 'prop-types'; + +/** + * ChartCard - Card wrapper cho charts với styling đẹp + */ +const ChartCard = ({ + title, + children, + extra = null, + height = 400, + loading = false, + className = '' +}) => { + return ( + +
+ {children} +
+
+ ); +}; + +ChartCard.propTypes = { + title: PropTypes.string, + children: PropTypes.node.isRequired, + extra: PropTypes.node, + height: PropTypes.number, + loading: PropTypes.bool, + className: PropTypes.string, +}; + +export default ChartCard; \ No newline at end of file diff --git a/src/components/Dashboard/StatCard.jsx b/src/components/Dashboard/StatCard.jsx new file mode 100644 index 0000000..7177f1d --- /dev/null +++ b/src/components/Dashboard/StatCard.jsx @@ -0,0 +1,100 @@ +import { Card, Statistic } from 'antd'; +import PropTypes from 'prop-types'; + +/** + * StatCard - Card thống kê đẹp với gradient và animation + */ +const StatCard = ({ + title, + value, + icon, + color = '#1890ff', + suffix = '', + prefix = '', + description = '', + trend = null, + gradient = false, + className = '' +}) => { + const gradientColors = { + blue: 'from-blue-400 to-blue-600', + green: 'from-green-400 to-green-600', + yellow: 'from-yellow-400 to-yellow-600', + purple: 'from-purple-400 to-purple-600', + pink: 'from-pink-400 to-pink-600', + red: 'from-red-400 to-red-600', + }; + + const getGradientClass = (color) => { + const colorMap = { + '#1890ff': 'blue', + '#52c41a': 'green', + '#faad14': 'yellow', + '#722ed1': 'purple', + '#eb2f96': 'pink', + '#f5222d': 'red', + }; + return gradientColors[colorMap[color]] || gradientColors.blue; + }; + + return ( + +
+
+ + {title} + + } + value={value} + suffix={suffix} + prefix={prefix} + valueStyle={{ + color: gradient ? 'white' : color, + fontSize: '24px', + fontWeight: 'bold' + }} + /> + {description && ( +
+ {description} +
+ )} + {trend && ( +
+ {trend} +
+ )} +
+ {icon && ( +
+ {icon} +
+ )} +
+
+ ); +}; + +StatCard.propTypes = { + title: PropTypes.string.isRequired, + value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, + icon: PropTypes.node, + color: PropTypes.string, + suffix: PropTypes.string, + prefix: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + description: PropTypes.string, + trend: PropTypes.string, + gradient: PropTypes.bool, + className: PropTypes.string, +}; + +export default StatCard; \ No newline at end of file diff --git a/src/components/index.jsx b/src/components/index.jsx index fd1b5a4..f9cf0d2 100644 --- a/src/components/index.jsx +++ b/src/components/index.jsx @@ -1,3 +1,7 @@ export { default as PaginationCustom } from "./PaginationCustom"; export { default as ColumnSort } from "./ColumnSort"; export { default as HeaderTableBasic } from "./HeaderTableBasic"; + +// Dashboard components +export { default as StatCard } from "./Dashboard/StatCard"; +export { default as ChartCard } from "./Dashboard/ChartCard"; diff --git a/src/configs/axiosConfig.js b/src/configs/axiosConfig.js index c53ed47..568f53b 100644 --- a/src/configs/axiosConfig.js +++ b/src/configs/axiosConfig.js @@ -1,7 +1,6 @@ import axios from "axios"; import { notification } from "antd"; import AdminAuthServices from "../services/authServices/AdminAuthServices"; -// import axiosRetry from "axios-retry"; const axiosInstance = axios.create({ baseURL: `${import.meta.env.VITE_BASE_URL_API}/api/v1`, @@ -9,19 +8,21 @@ const axiosInstance = axios.create({ withCredentials: true, }); -// cấu hình retry -// axiosRetry(axiosInstance, { -// retries: 3, -// retryDelay: (retryCount) => { -// // Thời gian chờ giữa các lần retry -// return retryCount * 1000; -// }, -// retryCondition: (error) => { -// return ( -// axiosRetry.isNetworkError(error) || (error.response && error.response.status >= 500) -// ); -// }, -// }); +// Thêm biến để quản lý refresh token +let isRefreshing = false; +let failedQueue = []; + +const processQueue = (error, token = null) => { + failedQueue.forEach(prom => { + if (error) { + prom.reject(error); + } else { + prom.resolve(token); + } + }); + + failedQueue = []; +}; // Interceptor để thêm token vào header axiosInstance.interceptors.request.use( @@ -34,7 +35,7 @@ axiosInstance.interceptors.request.use( return config; }, function (error) { - Promise.reject(error); + return Promise.reject(error); } ); @@ -44,13 +45,8 @@ axiosInstance.interceptors.response.use( return response; }, async function (error) { - // if (axiosRetry.isNetworkError(error)) { - // notification.error({ - // message: "Lỗi", - // description: - // "Thao tác không thành công do lỗi server/internet , vui lòng thử lại sau", - // }); - // } else + const originalRequest = error.config; + if (error?.code === "ERR_NETWORK") { notification.error({ message: "Lỗi", @@ -59,18 +55,65 @@ axiosInstance.interceptors.response.use( }); } else { if (error?.response?.status === 401) { - if (error?.response?.data?.authError) { + if (error?.response?.data?.cause === 'JsonWebTokenError') { + localStorage.removeItem("admin"); + window.location.reload(); + } + else if (error?.response?.data?.authError) { notification.error({ message: "Lỗi", description: error?.response?.data?.message, }); } else { - const response = await AdminAuthServices.refreshToken() - const token = response?.accessToken; - if (token) { - const authData = { token }; - const authString = JSON.stringify(authData); - localStorage.setItem("admin", authString); + // Xử lý refresh token với queue + if (originalRequest._retry) { + return Promise.reject(error); + } + + if (isRefreshing) { + // Nếu đang refresh, thêm request vào queue + // để xử lý các request còn lại cần token mới + // các request khác sẽ đợi cho đến khi token được refresh xong + return new Promise(function(resolve, reject) { + failedQueue.push({ resolve, reject }); + }).then(token => { + originalRequest.headers.Authorization = `Bearer ${token}`; + return axiosInstance(originalRequest); + }).catch(err => { + return Promise.reject(err); + }); + } + + originalRequest._retry = true; + isRefreshing = true; + + try { + const response = await AdminAuthServices.refreshToken(); + const token = response?.accessToken; + + if (token) { + const authData = { token }; + const authString = JSON.stringify(authData); + localStorage.setItem("admin", authString); + + // Cập nhật header cho request hiện tại + originalRequest.headers.Authorization = `Bearer ${token}`; + + // Xử lý tất cả requests đang chờ + processQueue(null, token); + + // Retry request ban đầu + return axiosInstance(originalRequest); + } else { + throw new Error('No token received'); + } + } catch (refreshError) { + processQueue(refreshError, null); + localStorage.removeItem("admin"); + window.location.reload(); + return Promise.reject(refreshError); + } finally { + isRefreshing = false; } } } else if (error?.response?.status !== 422) { @@ -84,4 +127,4 @@ axiosInstance.interceptors.response.use( } ); -export default axiosInstance; +export default axiosInstance; \ No newline at end of file diff --git a/src/constants/routeConstants.js b/src/constants/routeConstants.js new file mode 100644 index 0000000..ab72ee9 --- /dev/null +++ b/src/constants/routeConstants.js @@ -0,0 +1,15 @@ +const ADMIN_ROUTES = { + DASHBOARD: "/admin/dashboard", + CATEGORY_LIST: "/admin/category", + CATEGORY_CREATE: "/admin/category/create", + GROUP_CATEGORY_LIST: "/admin/group-category", + GROUP_CATEGORY_CREATE: "/admin/group-category/create", + TAG_LIST: "/admin/tag", + TAG_CREATE: "/admin/tag/create", + POST_LIST: "/admin/post", + POST_CREATE: "/admin/post/create", + MANAGER_AUTHOR_LIST: "/admin/manager-author", + MANAGER_AUTHOR_CREATE: "/admin/manager-author/create", +} + +export { ADMIN_ROUTES }; \ No newline at end of file diff --git a/src/layouts/app-layouts/admin-layouts/SideBar.jsx b/src/layouts/app-layouts/admin-layouts/SideBar.jsx index a98ccc9..eca60bb 100644 --- a/src/layouts/app-layouts/admin-layouts/SideBar.jsx +++ b/src/layouts/app-layouts/admin-layouts/SideBar.jsx @@ -8,8 +8,9 @@ import { } from "@ant-design/icons"; import { Menu, Layout } from "antd"; import PropTypes from "prop-types"; -import { useState } from "react"; -import { useNavigate } from "react-router-dom"; +import { useEffect, useState } from "react"; +import { useNavigate, useLocation } from "react-router-dom"; +import { ADMIN_ROUTES } from "@/constants/routeConstants"; const { Sider } = Layout; @@ -52,62 +53,103 @@ const items = [ const navigations = [ { key: "1", - path: "/admin/dashboard", + path: ADMIN_ROUTES.DASHBOARD, }, { key: "2-1", - path: "/admin/group-category", + path: ADMIN_ROUTES.GROUP_CATEGORY_LIST, }, { key: "2-2", - path: "/admin/group-category/create", + path: ADMIN_ROUTES.GROUP_CATEGORY_CREATE, }, { key: "3-1", - path: "/admin/category", + path: ADMIN_ROUTES.CATEGORY_LIST, }, { key: "3-2", - path: "/admin/category/create", + path: ADMIN_ROUTES.CATEGORY_CREATE, }, { key: "4-1", - path: "/admin/tag", + path: ADMIN_ROUTES.TAG_LIST, }, { key: "4-2", - path: "/admin/tag/create", + path: ADMIN_ROUTES.TAG_CREATE, }, { key: "5-1", - path: "/admin/post", + path: ADMIN_ROUTES.POST_LIST, }, { key: "5-2", - path: "/admin/post/create", + path: ADMIN_ROUTES.POST_CREATE, }, { key: "6-1", - path: "/admin/manager-author", + path: ADMIN_ROUTES.MANAGER_AUTHOR_LIST, }, { key: "6-2", - path: "/admin/manager-author/create", + path: ADMIN_ROUTES.MANAGER_AUTHOR_CREATE, }, ]; -const handleSelectKeyUrl = () => { - const url = window.location.pathname; - const navigation = navigations.find((nav) => nav.path === url); - if (navigation) return navigation.key; - return ""; -}; + const SideBar = ({ collapsed }) => { - const [selectedKey] = useState(handleSelectKeyUrl); const navigate = useNavigate(); + const location = useLocation(); + const [selectedKey, setSelectedKey] = useState(""); + const [openKeys, setOpenKeys] = useState([]); + + // Hàm tìm key dựa trên URL + const getKeyFromPath = (pathname) => { + // Xử lý trường hợp đặc biệt: /admin -> dashboard + if (pathname === "/admin" || pathname === "/admin/") { + return "1"; + } + + const navigation = navigations.find((nav) => nav.path === pathname); + return navigation ? navigation.key : ""; + }; + + // Hàm tìm open keys (parent menu keys) dựa trên selected key + const getOpenKeysFromSelectedKey = (key) => { + if (!key) return []; + + // Tìm parent key từ selected key + // Ví dụ: "2-1" -> parent là "2" + const parentKey = key.split('-')[0]; + + // Chỉ return parent key nếu selected key có dấu gạch ngang (là submenu) + if (key.includes('-')) { + return [parentKey]; + } + return []; + }; + + // Cập nhật selectedKey và openKeys khi location thay đổi + useEffect(() => { + const currentKey = getKeyFromPath(location.pathname); + setSelectedKey(currentKey); + + // Cập nhật openKeys để expand menu chứa item được chọn + const newOpenKeys = getOpenKeysFromSelectedKey(currentKey); + setOpenKeys(newOpenKeys); + }, [location.pathname]); + + // Xử lý khi user click vào menu item const handleSelect = (item) => { const navigation = navigations.find((nav) => nav.key === item.key); + if (navigation) { + navigate(navigation.path); + } + }; - if (navigation) navigate(navigation.path); + // Xử lý khi user mở/đóng submenu + const handleOpenChange = (keys) => { + setOpenKeys(keys); }; return ( @@ -122,9 +164,11 @@ const SideBar = ({ collapsed }) => { ); @@ -134,4 +178,4 @@ SideBar.propTypes = { collapsed: PropTypes.bool, }; -export default SideBar; +export default SideBar; \ No newline at end of file diff --git a/src/layouts/app-layouts/admin-layouts/index.jsx b/src/layouts/app-layouts/admin-layouts/index.jsx index 1a288ab..7771050 100644 --- a/src/layouts/app-layouts/admin-layouts/index.jsx +++ b/src/layouts/app-layouts/admin-layouts/index.jsx @@ -1,4 +1,4 @@ -import { Outlet, useNavigate } from "react-router-dom"; +import { Outlet, useLocation, useNavigate } from "react-router-dom"; import { useContext, useEffect, useState } from "react"; import { Layout, notification, theme } from "antd"; import HeaderNav from "./HeaderNav"; @@ -7,12 +7,14 @@ import BreadCrumbCustom from "@/components/BreadCrumbCustom"; import { AdminContext } from "@/context/AdminContext"; import AdminAuthServices from "@/services/authServices/AdminAuthServices"; import logoutAdmin from "@/utils/logoutAdmin"; +import { ADMIN_ROUTES } from "@/constants/routeConstants"; const { Content } = Layout; const AdminLayout = () => { const [collapsed, setCollapsed] = useState(false); const navigate = useNavigate(); + const location = useLocation(); const { admin, setAdmin } = useContext(AdminContext); useEffect(() => { @@ -20,6 +22,9 @@ const AdminLayout = () => { if (!token) { navigate("/auth/admin/login"); } + if (location.pathname === "/admin" || location.pathname === "/admin/") { + navigate(ADMIN_ROUTES.DASHBOARD); + } const getUserInfo = async () => { try { const response = await AdminAuthServices.getUserInfo(token); @@ -43,7 +48,7 @@ const AdminLayout = () => { } }; getUserInfo(); - }, [admin?.token]); + }, [admin?.token, location.pathname, navigate, setAdmin, admin]); const { token: { colorBgContainer }, } = theme.useToken(); diff --git a/src/pages/app-pages/admin-pages/Dashboard.jsx b/src/pages/app-pages/admin-pages/Dashboard.jsx new file mode 100644 index 0000000..0ea6187 --- /dev/null +++ b/src/pages/app-pages/admin-pages/Dashboard.jsx @@ -0,0 +1,370 @@ +import { useEffect, useState } from 'react'; +import { + Row, + Col, + Card, + Table, + Tag, + Avatar, + List, + Button, + Space, + Tooltip, + Spin +} from 'antd'; +import { + FileTextOutlined, + FolderOutlined, + TagOutlined, + UserOutlined, + EyeOutlined, + TrophyOutlined, + EditOutlined, + EllipsisOutlined +} from '@ant-design/icons'; +import { Line, Doughnut } from 'react-chartjs-2'; +import { + Chart as ChartJS, + CategoryScale, + LinearScale, + PointElement, + LineElement, + ArcElement, + Title, + Tooltip as ChartTooltip, + Legend, +} from 'chart.js'; + +import DashboardServices from '@/services/adminServices/dashboardServices'; +import customRenderAvatar from '@/utils/customRenderAvatar'; +import { useNavigate } from 'react-router-dom'; +import dayjs from 'dayjs'; +import StatCard from '@/components/Dashboard/StatCard'; +import ChartCard from '@/components/Dashboard/ChartCard'; + +// Register ChartJS components +ChartJS.register( + CategoryScale, + LinearScale, + PointElement, + LineElement, + ArcElement, + Title, + ChartTooltip, + Legend +); + +const AdminDashboard = () => { + const navigate = useNavigate(); + const [loading, setLoading] = useState(true); + const [overviewStats, setOverviewStats] = useState({}); + const [postsAnalytics, setPostsAnalytics] = useState({}); + const [recentPosts, setRecentPosts] = useState([]); + const [categoriesStats, setCategoriesStats] = useState([]); + const [authorsStats, setAuthorsStats] = useState([]); + + useEffect(() => { + fetchDashboardData(); + }, []); + + const fetchDashboardData = async () => { + setLoading(true); + try { + const [overview, analytics, recent, categories, authors] = await Promise.all([ + DashboardServices.getOverviewStats(), + DashboardServices.getPostsAnalytics(), + DashboardServices.getRecentPosts(), + DashboardServices.getCategoriesStats(), + DashboardServices.getAuthorsStats() + ]); + + setOverviewStats(overview); + setPostsAnalytics(analytics); + setRecentPosts(recent.data); + setCategoriesStats(categories); + setAuthorsStats(authors); + } catch (error) { + console.error('Error fetching dashboard data:', error); + } finally { + setLoading(false); + } + }; + + // Prepare chart options + const chartOptions = { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + position: 'top', + }, + title: { + display: true, + text: 'Thống kê 7 ngày gần đây', + }, + }, + scales: { + y: { + beginAtZero: true, + }, + }, + }; + + const doughnutData = { + labels: categoriesStats.map(cat => cat.name), + datasets: [ + { + data: categoriesStats.map(cat => cat.posts), + backgroundColor: categoriesStats.map(cat => cat.color), + borderWidth: 2, + }, + ], + }; + + const doughnutOptions = { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + position: 'bottom', + }, + title: { + display: true, + text: 'Phân bố bài viết theo danh mục', + }, + }, + }; + + // Recent posts table columns + const recentPostsColumns = [ + { + title: 'Tiêu đề', + dataIndex: 'title', + key: 'title', + render: (text, record) => ( +
+
{text}
+
+ + {record?.published_at ? 'Đã xuất bản' : 'Nháp'} + + {record.category?.name || '---'} +
+
+ ), + }, + { + title: 'Tác giả', + dataIndex: 'created_by_admin', + key: 'created_by_admin', + width: 120, + render: (_, record) => { + return record?.created_by_admin?.display_name || '---'; + } + }, + { + title: 'Lượt xem', + dataIndex: 'view', + key: 'view', + width: 80, + render: (view) => ( + + + {view ? view.toLocaleString() : 0} + + ), + }, + { + title: 'Ngày tạo', + dataIndex: 'created_at', + key: 'created_at', + width: 100, + render: (date) => dayjs(date).format('DD/MM/YYYY'), + }, + { + title: 'Thao tác', + key: 'action', + width: 100, + render: (_, record) => ( + + + + } + > + + + + + navigate('/admin/manager-author')}> + Xem tất cả + + } + > + ( + + + {customRenderAvatar(author.name)} + + } + title={ +
+ {author.name} + + #{index + 1} + +
+ } + description={ +
+
{author.role}
+
+ {author.posts} bài viết + {author.views.toLocaleString()} lượt xem +
+
+ } + /> +
+ )} + /> +
+ + + + ); +}; + +export default AdminDashboard; \ No newline at end of file diff --git a/src/pages/app-pages/author-pages/Dashboard.jsx b/src/pages/app-pages/author-pages/Dashboard.jsx new file mode 100644 index 0000000..9066a50 --- /dev/null +++ b/src/pages/app-pages/author-pages/Dashboard.jsx @@ -0,0 +1,434 @@ +import { useEffect, useState } from 'react'; +import { + Row, + Col, + Card, + Statistic, + Table, + Tag, + Button, + Space, + Tooltip, + Spin, + Progress +} from 'antd'; +import { + FileTextOutlined, + EyeOutlined, + CalendarOutlined, + RiseOutlined, + EditOutlined, + EllipsisOutlined, + PlusOutlined, + CheckCircleOutlined, + ClockCircleOutlined +} from '@ant-design/icons'; +import { Line } from 'react-chartjs-2'; +import { + Chart as ChartJS, + CategoryScale, + LinearScale, + PointElement, + LineElement, + Title, + Tooltip as ChartTooltip, + Legend, +} from 'chart.js'; + +import { useNavigate } from 'react-router-dom'; +import dayjs from 'dayjs'; + +// Register ChartJS components +ChartJS.register( + CategoryScale, + LinearScale, + PointElement, + LineElement, + Title, + ChartTooltip, + Legend +); + +const AuthorDashboard = () => { + const navigate = useNavigate(); + const [loading, setLoading] = useState(true); + const [authorStats, setAuthorStats] = useState({}); + const [recentPosts, setRecentPosts] = useState([]); + const [performanceData, setPerformanceData] = useState({}); + + useEffect(() => { + fetchAuthorData(); + }, []); + + const fetchAuthorData = async () => { + setLoading(true); + try { + // Mock data cho author dashboard + await new Promise(resolve => setTimeout(resolve, 1000)); // Simulate API call + + setAuthorStats({ + totalPosts: 23, + publishedPosts: 18, + draftPosts: 5, + totalViews: 15420, + todayViews: 85, + avgViews: Math.round(15420 / 18), + engagement: 78 + }); + + setRecentPosts([ + { + id: 1, + title: "Hướng dẫn sử dụng React Hooks hiệu quả", + status: "published", + views: 1250, + created_at: "2025-10-20T10:30:00Z", + category: "Lập trình" + }, + { + id: 2, + title: "10 mẹo tối ưu hóa hiệu suất website", + status: "draft", + views: 0, + created_at: "2025-10-19T15:45:00Z", + category: "Web Development" + }, + { + id: 3, + title: "Xu hướng công nghệ 2025", + status: "published", + views: 890, + created_at: "2025-10-18T09:20:00Z", + category: "Công nghệ" + }, + { + id: 4, + title: "Machine Learning cơ bản cho người mới", + status: "published", + views: 2100, + created_at: "2025-10-17T14:10:00Z", + category: "AI/ML" + } + ]); + + setPerformanceData({ + labels: ['T2', 'T3', 'T4', 'T5', 'T6', 'T7', 'CN'], + datasets: [ + { + label: 'Lượt xem', + data: [120, 150, 180, 220, 190, 250, 300], + borderColor: '#1890ff', + backgroundColor: 'rgba(24, 144, 255, 0.1)', + tension: 0.4, + } + ] + }); + + } catch (error) { + console.error('Error fetching author data:', error); + } finally { + setLoading(false); + } + }; + + const chartOptions = { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + position: 'top', + }, + title: { + display: true, + text: 'Lượt xem trong 7 ngày gần đây', + }, + }, + scales: { + y: { + beginAtZero: true, + }, + }, + }; + + // Recent posts table columns + const recentPostsColumns = [ + { + title: 'Tiêu đề', + dataIndex: 'title', + key: 'title', + render: (text, record) => ( +
+
{text}
+
+ + {record.status === 'published' ? 'Đã xuất bản' : 'Nháp'} + + {record.category} +
+
+ ), + }, + { + title: 'Lượt xem', + dataIndex: 'views', + key: 'views', + width: 100, + render: (views) => ( + + + {views.toLocaleString()} + + ), + }, + { + title: 'Ngày tạo', + dataIndex: 'created_at', + key: 'created_at', + width: 120, + render: (date) => dayjs(date).format('DD/MM/YYYY'), + }, + { + title: 'Thao tác', + key: 'action', + width: 100, + render: (_, record) => ( + + + +
+

+ Bắt đầu viết bài mới +

+

+ Chia sẻ kiến thức và kinh nghiệm của bạn với cộng đồng +

+
+ + + + + + + + {/* Statistics Cards */} + + + + } + valueStyle={{ color: '#1890ff' }} + /> +
+ {authorStats.publishedPosts} đã xuất bản +
+
+ + + + } + valueStyle={{ color: '#faad14' }} + /> +
+ Chưa xuất bản +
+
+ + + + } + valueStyle={{ color: '#52c41a' }} + /> +
+ Hôm nay: {authorStats.todayViews} lượt +
+
+ + + + } + valueStyle={{ color: '#722ed1' }} + /> +
+ `${authorStats.engagement}% tương tác`} + /> +
+
+ + + + {/* Performance Chart */} + + + +
+ +
+
+ + + +
+
+
+ {authorStats.totalPosts ? Math.round((authorStats.publishedPosts / authorStats.totalPosts) * 100) : 0}% +
+
Tỷ lệ xuất bản
+
+ +
+
+ {authorStats.avgViews} +
+
Trung bình lượt xem/bài
+
+ +
+
+ {authorStats.engagement}% +
+
Mức độ tương tác
+
+
+
+ + + + {/* Recent Posts */} + navigate('/author/post')}> + Xem tất cả bài viết + + } + > +
+ + + {/* Tips and Guidelines */} + + + +
+
+
+
+
Tiêu đề hấp dẫn
+
Sử dụng từ khóa phù hợp và gây tò mò
+
+
+
+
+
+
Nội dung chất lượng
+
Cung cấp thông tin hữu ích và đáng tin cậy
+
+
+
+
+
+
Hình ảnh minh họa
+
Thêm hình ảnh để bài viết sinh động hơn
+
+
+
+
+ + + +
+
+
+ Bài viết mới + 3/5 +
+ +
+
+
+ Lượt xem + 15.4K/20K +
+ +
+
+
+ Tương tác + 78/80% +
+ +
+
+
+ + + + ); +}; + +export default AuthorDashboard; \ No newline at end of file diff --git a/src/routers/adminRoutes/index.jsx b/src/routers/adminRoutes/index.jsx index 099404e..c045adb 100644 --- a/src/routers/adminRoutes/index.jsx +++ b/src/routers/adminRoutes/index.jsx @@ -1,4 +1,5 @@ import AdminLayout from "../../layouts/app-layouts/admin-layouts"; +import AdminDashboard from "@/pages/app-pages/admin-pages/Dashboard"; import categoryRoutes from "./managerRoutes/categoryRoutes"; import groupCategoryRoutes from "./managerRoutes/groupCategoryRoutes"; @@ -14,7 +15,7 @@ const adminRoutes = [ children: [ { path: "dashboard", - element:

admin

, + element: , exact: true, }, groupCategoryRoutes, diff --git a/src/routers/authorRoutes/index.jsx b/src/routers/authorRoutes/index.jsx index 089f072..db407df 100644 --- a/src/routers/authorRoutes/index.jsx +++ b/src/routers/authorRoutes/index.jsx @@ -1,4 +1,5 @@ import AuthorLayout from "../../layouts/app-layouts/author-layouts"; +import AuthorDashboard from "@/pages/app-pages/author-pages/Dashboard"; import postRoutes from "./managerRoutes/postRoutes"; @@ -10,7 +11,7 @@ const authorRoutes = [ children: [ { path: "dashboard", - element:

author

, + element: , exact: true, }, postRoutes, diff --git a/src/services/adminServices/dashboardServices.js b/src/services/adminServices/dashboardServices.js new file mode 100644 index 0000000..6e60dea --- /dev/null +++ b/src/services/adminServices/dashboardServices.js @@ -0,0 +1,240 @@ +import axiosInstance from "@/configs/axiosConfig"; + +/** + * Service để lấy thống kê dashboard admin + */ +class DashboardServices { + /** + * Lấy tổng quan thống kê + */ + static async getOverviewStats() { + try { + const response = await axiosInstance.get("/dashboard/overview"); + return response.data; + } catch (error) { + console.log("Dashboard overview error:", error); + } + } + + static generateDateRange(startDate, endDate) { + const dates = []; + const current = new Date(startDate); + + while (current <= endDate) { + dates.push(new Date(current)); + current.setDate(current.getDate() + 1); + } + + return dates; + } + static formatDateLabel(date) { + const day = date.getDate().toString().padStart(2, '0'); + const month = (date.getMonth() + 1).toString().padStart(2, '0'); + return `${day}-${month}`; + } + + /** + * Lấy thống kê bài viết theo thời gian + */ + static async getPostsAnalytics(period = 7) { + try { + const response = await axiosInstance.get(`/dashboard/posts-analytics?period=${period}`); + const data = response.data; + const currentDate = new Date(); + const startDate = new Date(); + startDate.setDate(currentDate.getDate() - (parseInt(period) - 1)); + const dateRange = this.generateDateRange(startDate, currentDate); + const labels = dateRange.map(date => this.formatDateLabel(date)); + const postsMap = {}; + if (data?.posts && Array.isArray(data.posts)) { + data.posts.forEach(item => { + // item = {19/10: 5} + const dateKey = Object.keys(item)[0]; // "19/10" + const value = item[dateKey]; // 5 + postsMap[dateKey] = value; + }); + } + + const viewsMap = {}; + if (data?.views && Array.isArray(data.views)) { + data.views.forEach(item => { + const dateKey = Object.keys(item)[0]; + const value = item[dateKey]; + viewsMap[dateKey] = value; + }); + } + + const postsData = labels.map(label => { + return postsMap[label] || 0; + }); + + const viewsData = labels.map(label => { + return viewsMap[label] || 0; + }); + + return { + dateRange: dateRange.map(date => date.toISOString().split('T')[0]), + labels: labels, + datasets: [ + { + label: 'Bài viết mới', + data: postsData, + borderColor: '#1890ff', + backgroundColor: 'rgba(24, 144, 255, 0.1)', + tension: 0.4, + }, + { + label: 'Lượt xem', + data: viewsData, + borderColor: '#52c41a', + backgroundColor: 'rgba(82, 196, 26, 0.1)', + tension: 0.4, + } + ] + }; + } catch (error) { + console.log("Posts analytics error:", error); + // Mock data để test + return { + labels: ['T2', 'T3', 'T4', 'T5', 'T6', 'T7', 'CN'], + datasets: [ + { + label: 'Bài viết mới', + data: [12, 8, 15, 10, 6, 9, 14], + borderColor: '#1890ff', + backgroundColor: 'rgba(24, 144, 255, 0.1)', + }, + { + label: 'Lượt xem', + data: [850, 720, 950, 800, 600, 750, 890], + borderColor: '#52c41a', + backgroundColor: 'rgba(82, 196, 26, 0.1)', + } + ] + }; + } + } + + /** + * Lấy danh sách bài viết gần đây + */ + static async getRecentPosts(limit = 5) { + try { + const response = await axiosInstance.get(`/dashboard/recent-posts?limit=${limit}`); + return response.data; + } catch (error) { + console.log("Recent posts error:", error); + // Mock data để test + return { + data: [ + { + id: 1, + title: "Hướng dẫn sử dụng React Hooks hiệu quả", + status: "published", + views: 1250, + author: "Nguyễn Văn A", + created_at: "2025-10-20T10:30:00Z", + category: "Lập trình" + }, + { + id: 2, + title: "10 mẹo tối ưu hóa hiệu suất website", + status: "draft", + views: 0, + author: "Trần Thị B", + created_at: "2025-10-19T15:45:00Z", + category: "Web Development" + }, + { + id: 3, + title: "Xu hướng công nghệ 2025", + status: "published", + views: 890, + author: "Lê Văn C", + created_at: "2025-10-18T09:20:00Z", + category: "Công nghệ" + }, + { + id: 4, + title: "Machine Learning cơ bản cho người mới", + status: "published", + views: 2100, + author: "Phạm Thị D", + created_at: "2025-10-17T14:10:00Z", + category: "AI/ML" + }, + { + id: 5, + title: "Bảo mật website với HTTPS", + status: "published", + views: 760, + author: "Hoàng Văn E", + created_at: "2025-10-16T11:25:00Z", + category: "Bảo mật" + } + ] + }; + } + } + + /** + * Lấy thống kê danh mục + */ + static async getCategoriesStats() { + try { + const response = await axiosInstance.get("/dashboard/categories-stats"); + return response.data; + } catch (error) { + console.log("Categories stats error:", error); + } + } + + /** + * Lấy thống kê tác giả + */ + static async getAuthorsStats() { + try { + const response = await axiosInstance.get("/dashboard/authors-stats"); + return response.data; + } catch (error) { + console.log("Authors stats error:", error); + // Mock data để test + return [ + { + id: 1, + name: "Nguyễn Văn A", + posts: 23, + views: 15420, + avatar: null, + role: "Senior Author" + }, + { + id: 2, + name: "Trần Thị B", + posts: 18, + views: 12350, + avatar: null, + role: "Author" + }, + { + id: 3, + name: "Lê Văn C", + posts: 15, + views: 9870, + avatar: null, + role: "Author" + }, + { + id: 4, + name: "Phạm Thị D", + posts: 12, + views: 8920, + avatar: null, + role: "Junior Author" + } + ]; + } + } +} + +export default DashboardServices; \ No newline at end of file diff --git a/src/styles/admin-auth.css b/src/styles/admin-auth.css index 86cd158..bbfdccd 100644 --- a/src/styles/admin-auth.css +++ b/src/styles/admin-auth.css @@ -108,15 +108,4 @@ .ant-message { animation: slideInLeft 0.5s ease-out; -} - -.ant-form-item-has-error .ant-input, -.ant-form-item-has-error .ant-input-password-input { - animation: shake 0.5s ease-in-out; -} - -@keyframes shake { - 0%, 100% { transform: translateX(0); } - 25% { transform: translateX(-5px); } - 75% { transform: translateX(5px); } } \ No newline at end of file From d55ec765c2a6c51db5e94fe1869e4396f8f2c427 Mon Sep 17 00:00:00 2001 From: namnd Date: Fri, 24 Oct 2025 16:06:26 +0700 Subject: [PATCH 3/4] refactor ui --- src/components/Column/GenerateColumn.jsx | 2 + src/components/HeaderTableBasic.jsx | 2 +- .../app-layouts/admin-layouts/index.jsx | 2 +- src/pages/app-pages/admin-pages/Dashboard.jsx | 18 +-- .../app-pages/admin-pages/category/index.jsx | 5 +- .../admin-pages/group-category/Show.jsx | 6 +- .../admin-pages/group-category/index.jsx | 15 ++- .../admin-pages/manager-author/index.jsx | 2 +- .../app-pages/admin-pages/post/index.jsx | 2 +- src/pages/app-pages/admin-pages/tag/index.jsx | 2 +- .../adminServices/dashboardServices.js | 103 ------------------ 11 files changed, 31 insertions(+), 128 deletions(-) diff --git a/src/components/Column/GenerateColumn.jsx b/src/components/Column/GenerateColumn.jsx index 609dd56..c35703b 100644 --- a/src/components/Column/GenerateColumn.jsx +++ b/src/components/Column/GenerateColumn.jsx @@ -15,6 +15,7 @@ const generateBasicColumn = ( // title: () => , dataIndex: "id", key: "id", + width: 200, sorter: true, sortOrder: filter.sort.sortBy === "id" ? filter.sort.sortType : false, onHeaderCell: (column) => ({ @@ -102,6 +103,7 @@ const generateBasicColumn = ( dataIndex: "action", key: "action", fixed: "right", + width: 200, render: (_, record) => ( diff --git a/src/components/HeaderTableBasic.jsx b/src/components/HeaderTableBasic.jsx index ec709d4..a0799cb 100644 --- a/src/components/HeaderTableBasic.jsx +++ b/src/components/HeaderTableBasic.jsx @@ -13,7 +13,7 @@ const HeaderTableBasic = ({ filter, handleFilterData }) => { }); }; return ( - +
{ } }; getUserInfo(); - }, [admin?.token, location.pathname, navigate, setAdmin, admin]); + }, [admin?.token]); const { token: { colorBgContainer }, } = theme.useToken(); diff --git a/src/pages/app-pages/admin-pages/Dashboard.jsx b/src/pages/app-pages/admin-pages/Dashboard.jsx index 0ea6187..65d5cd9 100644 --- a/src/pages/app-pages/admin-pages/Dashboard.jsx +++ b/src/pages/app-pages/admin-pages/Dashboard.jsx @@ -227,17 +227,17 @@ const AdminDashboard = () => {
} color="#1890ff" - description={`${overviewStats.publishedPosts} đã xuất bản`} + description={`${overviewStats?.publishedPosts} đã xuất bản`} gradient /> } color="#52c41a" gradient @@ -246,7 +246,7 @@ const AdminDashboard = () => { } color="#faad14" gradient @@ -255,7 +255,7 @@ const AdminDashboard = () => { } color="#722ed1" gradient @@ -268,20 +268,20 @@ const AdminDashboard = () => { } color="#eb2f96" - description={`Hôm nay: ${overviewStats.todayViews?.toLocaleString()} lượt`} + description={`Hôm nay: ${overviewStats?.todayViews?.toLocaleString()} lượt`} /> } color="#fa541c" - description={`${overviewStats.publishedPosts}/${overviewStats.totalPosts} bài viết`} + description={`${overviewStats?.publishedPosts}/${overviewStats?.totalPosts} bài viết`} /> diff --git a/src/pages/app-pages/admin-pages/category/index.jsx b/src/pages/app-pages/admin-pages/category/index.jsx index bc2af8d..19c1e61 100644 --- a/src/pages/app-pages/admin-pages/category/index.jsx +++ b/src/pages/app-pages/admin-pages/category/index.jsx @@ -2,11 +2,10 @@ import { useEffect, useState } from "react"; import { Card, Table, message } from "antd"; import categoryServices from "@/services/adminServices/categoryServices"; -import { generateBasicColumn } from "@/components/Column/GenerateColumn"; +import { generateBasicColumn, generateColumn } from "@/components/Column/GenerateColumn"; import { PaginationCustom } from "@/components"; import HeaderTableBasic from "@/components/HeaderTableBasic"; -import { generateColumn } from "@/components/Column/GenerateColumn"; const List = () => { const [list, setList] = useState([]); @@ -21,7 +20,7 @@ const List = () => { }, sort: { sortBy: "id", - sortType: "ASC", + sortType: "DESC", }, flimit: 10, }); diff --git a/src/pages/app-pages/admin-pages/group-category/Show.jsx b/src/pages/app-pages/admin-pages/group-category/Show.jsx index 6df2950..5f4ab72 100644 --- a/src/pages/app-pages/admin-pages/group-category/Show.jsx +++ b/src/pages/app-pages/admin-pages/group-category/Show.jsx @@ -1,7 +1,7 @@ import { Card, Spin, Tag } from "antd"; import groupCategoryServices from "@/services/adminServices/groupCategoryServices"; -import { useParams } from "react-router-dom"; +import { Link, useParams } from "react-router-dom"; import { useEffect, useState } from "react"; const Show = () => { @@ -37,7 +37,9 @@ const Show = () => { {categoryList?.length > 0 && (
{categoryList?.map((item) => ( - {item?.name} + + {item?.name} + ))}
)} diff --git a/src/pages/app-pages/admin-pages/group-category/index.jsx b/src/pages/app-pages/admin-pages/group-category/index.jsx index 90d0cb8..b8f213c 100644 --- a/src/pages/app-pages/admin-pages/group-category/index.jsx +++ b/src/pages/app-pages/admin-pages/group-category/index.jsx @@ -2,11 +2,10 @@ import { useEffect, useState } from "react"; import { Card, Table, message } from "antd"; import groupCategoryServices from "@/services/adminServices/groupCategoryServices"; -import { generateBasicColumn } from "@/components/Column/GenerateColumn"; +import { generateBasicColumn, generateColumn } from "@/components/Column/GenerateColumn"; import { PaginationCustom } from "@/components"; import HeaderTableBasic from "@/components/HeaderTableBasic"; -import { generateColumn } from "@/components/Column/GenerateColumn"; const List = () => { const [list, setList] = useState([]); @@ -21,7 +20,7 @@ const List = () => { }, sort: { sortBy: "id", - sortType: "ASC", + sortType: "DESC", }, flimit: 10, }); @@ -77,11 +76,10 @@ const List = () => { } }; - const { id, createdByAdmin, updatedByAdmin, createdAt, updatedAt, action } = - generateBasicColumn(filter, handleSort, handleDelete, confirmLoading, handleConfirm); + const { id, action } = generateBasicColumn(filter, handleSort, handleDelete, confirmLoading, handleConfirm); const restColumnInfo = [ { - title: "Tên nhóm", + title: "Tên nhóm danh mục", dataIndex: "name", key: "name", filter, @@ -89,6 +87,11 @@ const List = () => { customRender: false, sorter: true, }, + { + title: "Tổng danh mục", + dataIndex: "categories_count", + key: "categories_count", + }, ]; const restColumns = restColumnInfo.map((column) => { diff --git a/src/pages/app-pages/admin-pages/manager-author/index.jsx b/src/pages/app-pages/admin-pages/manager-author/index.jsx index 573cf8b..cadeea0 100644 --- a/src/pages/app-pages/admin-pages/manager-author/index.jsx +++ b/src/pages/app-pages/admin-pages/manager-author/index.jsx @@ -21,7 +21,7 @@ const List = () => { }, sort: { sortBy: "id", - sortType: "ASC", + sortType: "DESC", }, flimit: 10, }); diff --git a/src/pages/app-pages/admin-pages/post/index.jsx b/src/pages/app-pages/admin-pages/post/index.jsx index 4504794..c4b7b24 100644 --- a/src/pages/app-pages/admin-pages/post/index.jsx +++ b/src/pages/app-pages/admin-pages/post/index.jsx @@ -21,7 +21,7 @@ const List = () => { }, sort: { sortBy: "id", - sortType: "ASC", + sortType: "DESC", }, flimit: 10, }); diff --git a/src/pages/app-pages/admin-pages/tag/index.jsx b/src/pages/app-pages/admin-pages/tag/index.jsx index c3099ff..1b7c2e2 100644 --- a/src/pages/app-pages/admin-pages/tag/index.jsx +++ b/src/pages/app-pages/admin-pages/tag/index.jsx @@ -21,7 +21,7 @@ const List = () => { }, sort: { sortBy: "id", - sortType: "ASC", + sortType: "DESC", }, flimit: 10, }); diff --git a/src/services/adminServices/dashboardServices.js b/src/services/adminServices/dashboardServices.js index 6e60dea..38d5910 100644 --- a/src/services/adminServices/dashboardServices.js +++ b/src/services/adminServices/dashboardServices.js @@ -94,24 +94,6 @@ class DashboardServices { }; } catch (error) { console.log("Posts analytics error:", error); - // Mock data để test - return { - labels: ['T2', 'T3', 'T4', 'T5', 'T6', 'T7', 'CN'], - datasets: [ - { - label: 'Bài viết mới', - data: [12, 8, 15, 10, 6, 9, 14], - borderColor: '#1890ff', - backgroundColor: 'rgba(24, 144, 255, 0.1)', - }, - { - label: 'Lượt xem', - data: [850, 720, 950, 800, 600, 750, 890], - borderColor: '#52c41a', - backgroundColor: 'rgba(82, 196, 26, 0.1)', - } - ] - }; } } @@ -124,56 +106,6 @@ class DashboardServices { return response.data; } catch (error) { console.log("Recent posts error:", error); - // Mock data để test - return { - data: [ - { - id: 1, - title: "Hướng dẫn sử dụng React Hooks hiệu quả", - status: "published", - views: 1250, - author: "Nguyễn Văn A", - created_at: "2025-10-20T10:30:00Z", - category: "Lập trình" - }, - { - id: 2, - title: "10 mẹo tối ưu hóa hiệu suất website", - status: "draft", - views: 0, - author: "Trần Thị B", - created_at: "2025-10-19T15:45:00Z", - category: "Web Development" - }, - { - id: 3, - title: "Xu hướng công nghệ 2025", - status: "published", - views: 890, - author: "Lê Văn C", - created_at: "2025-10-18T09:20:00Z", - category: "Công nghệ" - }, - { - id: 4, - title: "Machine Learning cơ bản cho người mới", - status: "published", - views: 2100, - author: "Phạm Thị D", - created_at: "2025-10-17T14:10:00Z", - category: "AI/ML" - }, - { - id: 5, - title: "Bảo mật website với HTTPS", - status: "published", - views: 760, - author: "Hoàng Văn E", - created_at: "2025-10-16T11:25:00Z", - category: "Bảo mật" - } - ] - }; } } @@ -198,41 +130,6 @@ class DashboardServices { return response.data; } catch (error) { console.log("Authors stats error:", error); - // Mock data để test - return [ - { - id: 1, - name: "Nguyễn Văn A", - posts: 23, - views: 15420, - avatar: null, - role: "Senior Author" - }, - { - id: 2, - name: "Trần Thị B", - posts: 18, - views: 12350, - avatar: null, - role: "Author" - }, - { - id: 3, - name: "Lê Văn C", - posts: 15, - views: 9870, - avatar: null, - role: "Author" - }, - { - id: 4, - name: "Phạm Thị D", - posts: 12, - views: 8920, - avatar: null, - role: "Junior Author" - } - ]; } } } From 8fdfe701b8f9640dcd338a58a116b63fab385967 Mon Sep 17 00:00:00 2001 From: namnd Date: Wed, 26 Nov 2025 14:58:20 +0700 Subject: [PATCH 4/4] update editor for post management screen --- package-lock.json | 3263 ++++++++++++++++- package.json | 4 + src/components/BlockNoteEditor/index.jsx | 220 ++ src/components/Dashboard/StatCard.jsx | 3 +- src/components/Form/FormField.jsx | 1 - src/components/PostPreview/index.jsx | 216 ++ src/components/PostPreview/styles.js | 73 + src/configs/axiosConfig.js | 39 +- src/context/AdminContext.jsx | 7 +- .../app-layouts/admin-layouts/SideBar.jsx | 12 +- .../app-layouts/admin-layouts/index.jsx | 2 +- .../app-pages/admin-pages/post/Create.jsx | 138 +- src/pages/app-pages/admin-pages/post/Edit.jsx | 159 +- src/utils/logoutAdmin.js | 18 +- 14 files changed, 3873 insertions(+), 282 deletions(-) create mode 100644 src/components/BlockNoteEditor/index.jsx create mode 100644 src/components/PostPreview/index.jsx create mode 100644 src/components/PostPreview/styles.js diff --git a/package-lock.json b/package-lock.json index 9e08009..3a90c4f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,9 @@ "version": "0.0.0", "dependencies": { "@ant-design/icons": "^5.2.6", + "@blocknote/core": "^0.42.3", + "@blocknote/mantine": "^0.42.3", + "@blocknote/react": "^0.42.3", "@ckeditor/ckeditor5-build-classic": "^40.0.0", "@ckeditor/ckeditor5-react": "^6.1.0", "@reduxjs/toolkit": "^1.9.7", @@ -26,6 +29,7 @@ "react-slick": "^0.29.0", "slick-carousel": "^1.8.1", "socket.io-client": "^4.7.2", + "styled-components": "^6.1.19", "zustand": "^4.4.6" }, "devDependencies": { @@ -479,6 +483,100 @@ "node": ">=6.9.0" } }, + "node_modules/@blocknote/core": { + "version": "0.42.3", + "resolved": "https://registry.npmjs.org/@blocknote/core/-/core-0.42.3.tgz", + "integrity": "sha512-wtZki6Gok5Ac9Ek6QTQztcDymstEQgVCisJwiUZTWXh8CD4UKfnIxM7C9+6eEnZMmQ8GNTvRf1HXFl+E4N78VA==", + "license": "MPL-2.0", + "dependencies": { + "@emoji-mart/data": "^1.2.1", + "@shikijs/types": "3.13.0", + "@tiptap/core": "^3.11.0", + "@tiptap/extension-bold": "^3.7.2", + "@tiptap/extension-code": "^3.7.2", + "@tiptap/extension-gapcursor": "^3.7.2", + "@tiptap/extension-history": "^3.7.2", + "@tiptap/extension-horizontal-rule": "^3.7.2", + "@tiptap/extension-italic": "^3.7.2", + "@tiptap/extension-link": "^3.7.2", + "@tiptap/extension-paragraph": "^3.7.2", + "@tiptap/extension-strike": "^3.7.2", + "@tiptap/extension-text": "^3.7.2", + "@tiptap/extension-underline": "^3.7.2", + "@tiptap/pm": "^3.11.0", + "emoji-mart": "^5.6.0", + "fast-deep-equal": "^3.1.3", + "hast-util-from-dom": "^5.0.1", + "prosemirror-dropcursor": "^1.8.2", + "prosemirror-highlight": "^0.13.0", + "prosemirror-model": "^1.25.4", + "prosemirror-state": "^1.4.4", + "prosemirror-tables": "^1.8.1", + "prosemirror-transform": "^1.10.5", + "prosemirror-view": "^1.41.3", + "rehype-format": "^5.0.1", + "rehype-parse": "^9.0.1", + "rehype-remark": "^10.0.1", + "rehype-stringify": "^10.0.1", + "remark-gfm": "^4.0.1", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.1.2", + "remark-stringify": "^11.0.0", + "unified": "^11.0.5", + "unist-util-visit": "^5.0.0", + "uuid": "^8.3.2", + "y-prosemirror": "^1.3.7", + "y-protocols": "^1.0.6", + "yjs": "^13.6.27" + }, + "peerDependencies": { + "@hocuspocus/provider": "^2.15.2" + }, + "peerDependenciesMeta": { + "@hocuspocus/provider": { + "optional": true + } + } + }, + "node_modules/@blocknote/mantine": { + "version": "0.42.3", + "resolved": "https://registry.npmjs.org/@blocknote/mantine/-/mantine-0.42.3.tgz", + "integrity": "sha512-xzLweZG1KfFoOp/aSHTXE10IrfEHnhDlP0C2Qt2eNO2IHHa7l8XZJpIGhCoVMsn0yylm91OSynNfTO7JkZZi8w==", + "license": "MPL-2.0", + "dependencies": { + "@blocknote/core": "0.42.3", + "@blocknote/react": "0.42.3", + "react-icons": "^5.5.0" + }, + "peerDependencies": { + "@mantine/core": "^8.3.4", + "@mantine/hooks": "^8.3.4", + "@mantine/utils": "^6.0.22", + "react": "^18.0 || ^19.0 || >= 19.0.0-rc", + "react-dom": "^18.0 || ^19.0 || >= 19.0.0-rc" + } + }, + "node_modules/@blocknote/react": { + "version": "0.42.3", + "resolved": "https://registry.npmjs.org/@blocknote/react/-/react-0.42.3.tgz", + "integrity": "sha512-YnrQ1uyezDbaxYcFstWOJ2r8BMxqwwEc7QAhrEjCMEyBAiOxSCPnrM4/GE2mOgCS0Xa9wIp2LDoPQP2Syv+2EA==", + "license": "MPL-2.0", + "dependencies": { + "@blocknote/core": "0.42.3", + "@emoji-mart/data": "^1.2.1", + "@floating-ui/react": "^0.27.16", + "@tiptap/core": "^3.11.0", + "@tiptap/pm": "^3.11.0", + "@tiptap/react": "^3.11.0", + "emoji-mart": "^5.6.0", + "lodash.merge": "^4.6.2", + "react-icons": "^5.5.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || >= 19.0.0-rc", + "react-dom": "^18.0 || ^19.0 || >= 19.0.0-rc" + } + }, "node_modules/@ckeditor/ckeditor5-adapter-ckfinder": { "version": "40.0.0", "resolved": "https://registry.npmjs.org/@ckeditor/ckeditor5-adapter-ckfinder/-/ckeditor5-adapter-ckfinder-40.0.0.tgz", @@ -837,11 +935,32 @@ "node": ">=10" } }, + "node_modules/@emoji-mart/data": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emoji-mart/data/-/data-1.2.1.tgz", + "integrity": "sha512-no2pQMWiBy6gpBEiqGeU77/bFejDqUTRY7KX+0+iur13op3bqUsXdnwoZs6Xb1zbv0gAj5VvS1PWoUUckSr5Dw==", + "license": "MIT" + }, "node_modules/@emotion/hash": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", + "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==", + "license": "MIT" + }, "node_modules/@emotion/unitless": { "version": "0.7.5", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", @@ -1270,6 +1389,59 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", + "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.27.16", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.16.tgz", + "integrity": "sha512-9O8N4SeG2z++TSM8QA/KTeKFBVCNEz/AGS7gWPJf6KFRzmRWixFRnCnkPHRDwSVZW6QPDO6uT0P2SpWNKCc9/g==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.1.6", + "@floating-ui/utils": "^0.2.10", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz", + "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.4" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.11", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", @@ -1357,6 +1529,59 @@ "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", "license": "MIT" }, + "node_modules/@mantine/core": { + "version": "8.3.9", + "resolved": "https://registry.npmjs.org/@mantine/core/-/core-8.3.9.tgz", + "integrity": "sha512-ivj0Crn5N521cI2eWZBsBGckg0ZYRqfOJz5vbbvYmfj65bp0EdsyqZuOxXzIcn2aUScQhskfvzyhV5XIUv81PQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@floating-ui/react": "^0.27.16", + "clsx": "^2.1.1", + "react-number-format": "^5.4.4", + "react-remove-scroll": "^2.7.1", + "react-textarea-autosize": "8.5.9", + "type-fest": "^4.41.0" + }, + "peerDependencies": { + "@mantine/hooks": "8.3.9", + "react": "^18.x || ^19.x", + "react-dom": "^18.x || ^19.x" + } + }, + "node_modules/@mantine/core/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "license": "(MIT OR CC0-1.0)", + "peer": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mantine/hooks": { + "version": "8.3.9", + "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-8.3.9.tgz", + "integrity": "sha512-Dfz7W0+K1cq4Gb1WFQCZn8tsMXkLH6MV409wZR/ToqsxdNDUMJ/xxbfnwEXWEZjXNJd1wDETHgc+cZG2lTe3Xw==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "react": "^18.x || ^19.x" + } + }, + "node_modules/@mantine/utils": { + "version": "6.0.22", + "resolved": "https://registry.npmjs.org/@mantine/utils/-/utils-6.0.22.tgz", + "integrity": "sha512-RSKlNZvxhMCkOFZ6slbYvZYbWjHUM+PxDQnupIOxIdsTZQQjx/BFfrfJ7kQFOP+g7MtpOds8weAetEs5obwMOQ==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1528,6 +1753,12 @@ } } }, + "node_modules/@remirror/core-constants": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-3.0.0.tgz", + "integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==", + "license": "MIT" + }, "node_modules/@remix-run/router": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.9.0.tgz", @@ -1536,11 +1767,309 @@ "node": ">=14.0.0" } }, + "node_modules/@shikijs/types": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.13.0.tgz", + "integrity": "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw==", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "license": "MIT" + }, "node_modules/@socket.io/component-emitter": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" }, + "node_modules/@tiptap/core": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.11.0.tgz", + "integrity": "sha512-kmS7ZVpHm1EMnW1Wmft9H5ZLM7E0G0NGBx+aGEHGDcNxZBXD2ZUa76CuWjIhOGpwsPbELp684ZdpF2JWoNi4Dg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/pm": "^3.11.0" + } + }, + "node_modules/@tiptap/extension-bold": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-3.11.0.tgz", + "integrity": "sha512-V/c3XYO09Le9GlBGq1MK4c97Fffi0GADQTbZ+LFoi65nUrAwutn5wYnXBcEyWQI6RmFWVDJTieamqtc4j9teyw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.11.0" + } + }, + "node_modules/@tiptap/extension-bubble-menu": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.11.0.tgz", + "integrity": "sha512-P3j9lQ+EZ5Zg/isJzLpCPX7bp7WUBmz8GPs/HPlyMyN2su8LqXntITBZr8IP1JNBlB/wR83k/W0XqdC57mG7cA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.11.0", + "@tiptap/pm": "^3.11.0" + } + }, + "node_modules/@tiptap/extension-code": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-3.11.0.tgz", + "integrity": "sha512-5OpR5O4bveHe1KG9CJsto86NgkuerYq3OLY78vzh9uFCLdv7xgXA2aZYJfRMhbZ7hKsR7hHg1etBJUCk+TKsMg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.11.0" + } + }, + "node_modules/@tiptap/extension-floating-menu": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-3.11.0.tgz", + "integrity": "sha512-nEHdWZHEJYX1II1oJQ4aeZ8O/Kss4BRbYFXQFGIvPelCfCYEATpUJh3aq3767ARSq40bOWyu+Dcd4SCW0We6Sw==", + "license": "MIT", + "optional": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@floating-ui/dom": "^1.0.0", + "@tiptap/core": "^3.11.0", + "@tiptap/pm": "^3.11.0" + } + }, + "node_modules/@tiptap/extension-gapcursor": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-3.11.0.tgz", + "integrity": "sha512-lXGEZiYX7k/pEFr8BgDE91vqjLTwuf+qhHLTgIpfhbt562nShLPIDj9Vzu3xrR4fwUAMiUNiLyaeInb8j3I4kg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extensions": "^3.11.0" + } + }, + "node_modules/@tiptap/extension-history": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-3.11.0.tgz", + "integrity": "sha512-Q/kuNDCoeH2dZ2P+OqEKnRW047SkrngNq+vSrwQlAKO8osO/eAS7aLzn1NELzE5jLvzOKqUda43bSTKsBeTh+w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extensions": "^3.11.0" + } + }, + "node_modules/@tiptap/extension-horizontal-rule": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.11.0.tgz", + "integrity": "sha512-FugFHZG+oiMBV6k42hn9NOA4wRNc2b9UeEIMR+XwEMpWJInV4VwSwDvu8JClgkDo8z7FEnker9e51DZ00CLWqg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.11.0", + "@tiptap/pm": "^3.11.0" + } + }, + "node_modules/@tiptap/extension-italic": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.11.0.tgz", + "integrity": "sha512-WP6wL2b//8bLVdeUCWOpYA7nUStvrAMMD0nRn0F9CEW+l7vH6El2PZFhHmJ9uqXo5MnyugBpARiwgxfoAlef5w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.11.0" + } + }, + "node_modules/@tiptap/extension-link": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-3.11.0.tgz", + "integrity": "sha512-RoUkGqowVMKLE76KktNOGhzNMyKtwrSDRqeYCe1ODPuOMZvDGexOE8cIuA4A1ODkgN6ji9qE/9Sf8uhpZdH39Q==", + "license": "MIT", + "dependencies": { + "linkifyjs": "^4.3.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.11.0", + "@tiptap/pm": "^3.11.0" + } + }, + "node_modules/@tiptap/extension-paragraph": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-3.11.0.tgz", + "integrity": "sha512-hxgjZOXOqstRTWv+QjWJjK23rD5qzIV9ePlhX3imLeq/MgX0aU9VBDaG5SGKbSjaBNQnpLw6+sABJi3CDP6Z5A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.11.0" + } + }, + "node_modules/@tiptap/extension-strike": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-3.11.0.tgz", + "integrity": "sha512-XVP/WMYLrqLBfUsGPu2H9MrOUZLhGUaxtZ3hSRffDi/lsw53x/coZ9eO0FxOB9R7z2ksHWmticIs+0YnKt9LNQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.11.0" + } + }, + "node_modules/@tiptap/extension-text": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.11.0.tgz", + "integrity": "sha512-ELAYm2BuChzZOqDG9B0k3W6zqM4pwNvXkam28KgHGiT2y7Ni68Rb+NXp16uVR+5zR6hkqnQ/BmJSKzAW59MXpA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.11.0" + } + }, + "node_modules/@tiptap/extension-underline": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-3.11.0.tgz", + "integrity": "sha512-D3PsS/84RlQKFjd5eerMIUioC0mNh4yy1RRV/WbXx6ugu+6T+0hT42gNk9Ap8pDsVQZCk0SHfDyBEUFC2KOwKw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.11.0" + } + }, + "node_modules/@tiptap/extensions": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.11.0.tgz", + "integrity": "sha512-g43beA73ZMLezez1st9LEwYrRHZ0FLzlsSlOZKk7sdmtHLmuqWHf4oyb0XAHol1HZIdGv104rYaGNgmQXr1ecQ==", + "license": "MIT", + "peer": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.11.0", + "@tiptap/pm": "^3.11.0" + } + }, + "node_modules/@tiptap/pm": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.11.0.tgz", + "integrity": "sha512-plCQDLCZIOc92cizB8NNhBRN0szvYR3cx9i5IXo6v9Xsgcun8KHNcJkesc2AyeqdIs0BtOJZaqQ9adHThz8UDw==", + "license": "MIT", + "dependencies": { + "prosemirror-changeset": "^2.3.0", + "prosemirror-collab": "^1.3.1", + "prosemirror-commands": "^1.6.2", + "prosemirror-dropcursor": "^1.8.1", + "prosemirror-gapcursor": "^1.3.2", + "prosemirror-history": "^1.4.1", + "prosemirror-inputrules": "^1.4.0", + "prosemirror-keymap": "^1.2.2", + "prosemirror-markdown": "^1.13.1", + "prosemirror-menu": "^1.2.4", + "prosemirror-model": "^1.24.1", + "prosemirror-schema-basic": "^1.2.3", + "prosemirror-schema-list": "^1.5.0", + "prosemirror-state": "^1.4.3", + "prosemirror-tables": "^1.6.4", + "prosemirror-trailing-node": "^3.0.0", + "prosemirror-transform": "^1.10.2", + "prosemirror-view": "^1.38.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, + "node_modules/@tiptap/react": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-3.11.0.tgz", + "integrity": "sha512-SDGei/2DjwmhzsxIQNr6dkB6NxLgXZjQ6hF36NfDm4937r5NLrWrNk5tCsoDQiKZ0DHEzuJ6yZM5C7I7LZLB6w==", + "license": "MIT", + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "fast-deep-equal": "^3.1.3", + "use-sync-external-store": "^1.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "optionalDependencies": { + "@tiptap/extension-bubble-menu": "^3.11.0", + "@tiptap/extension-floating-menu": "^3.11.0" + }, + "peerDependencies": { + "@tiptap/core": "^3.11.0", + "@tiptap/pm": "^3.11.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "@types/react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@tiptap/react/node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, + "node_modules/@tiptap/react/node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/@types/babel__core": { "version": "7.20.2", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz", @@ -1582,6 +2111,24 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/hoist-non-react-statics": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.4.tgz", @@ -1591,6 +2138,43 @@ "hoist-non-react-statics": "^3.3.0" } }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, "node_modules/@types/prop-types": { "version": "15.7.8", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.8.tgz", @@ -1610,7 +2194,6 @@ "version": "18.2.13", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.13.tgz", "integrity": "sha512-eJIUv7rPP+EC45uNYp/ThhSpE16k22VJUknt5OLoH9tbXoi8bMhwLf5xRuWMywamNbWzhrSmU7IBJfPup1+3fw==", - "devOptional": true, "dependencies": { "@types/react": "*" } @@ -1620,11 +2203,29 @@ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.4.tgz", "integrity": "sha512-2L9ifAGl7wmXwP4v3pN4p2FLhD0O1qsJpvKmNin5VA8+UvNVb447UDaAEV6UdrkA+m/Xs58U1RFps44x6TFsVQ==" }, + "node_modules/@types/stylis": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", + "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==", + "license": "MIT" + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, "node_modules/@types/use-sync-external-store": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, "node_modules/@vitejs/plugin-react": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.1.0.tgz", @@ -1793,8 +2394,7 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/array-buffer-byte-length": { "version": "1.0.0", @@ -1992,6 +2592,16 @@ "axios": "0.x || 1.x" } }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -2092,7 +2702,16 @@ "node": ">= 6" } }, - "node_modules/caniuse-lite": { + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caniuse-lite": { "version": "1.0.30001547", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001547.tgz", "integrity": "sha512-W7CrtIModMAxobGhz8iXmDfuJiiKg1WADMO/9x7/CLNin5cpSbuBjooyoIUVB5eyCc36QuTVlkVa1iB2S5+/eA==", @@ -2112,6 +2731,16 @@ } ] }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -2126,6 +2755,36 @@ "node": ">=4" } }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chart.js": { "version": "4.5.1", "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz", @@ -2202,6 +2861,16 @@ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -2235,6 +2904,16 @@ "node": ">= 0.8" } }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -2269,6 +2948,12 @@ "toggle-selection": "^1.0.6" } }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2283,6 +2968,26 @@ "node": ">= 8" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "license": "MIT", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -2296,9 +3001,10 @@ } }, "node_modules/csstype": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" }, "node_modules/date-fns": { "version": "2.30.0", @@ -2337,6 +3043,19 @@ } } }, + "node_modules/decode-named-character-reference": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", + "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -2382,6 +3101,35 @@ "node": ">=0.4.0" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT", + "peer": true + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -2417,6 +3165,12 @@ "integrity": "sha512-/Ng/W/kFv7wdEHYzxdK7Cv0BHEGSkSB3M0Ssl8Ndr1eMiYeas/+Mv4cNaDqamqWx6nd2uQZfPz6g25z25M/sdw==", "dev": true }, + "node_modules/emoji-mart": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/emoji-mart/-/emoji-mart-5.6.0.tgz", + "integrity": "sha512-eJp3QRe79pjwa+duv+n7+5YsNhRcMl812EcFVwrnRvYKoNPoQb5qxU8DG6Bgwji0akHdp6D4Ln6tYLG58MFSow==", + "license": "MIT" + }, "node_modules/engine.io-client": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.2.tgz", @@ -2442,6 +3196,18 @@ "resolved": "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz", "integrity": "sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw==" }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/es-abstract": { "version": "1.22.2", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.2.tgz", @@ -2913,11 +3679,16 @@ "node": ">=0.10.0" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { "version": "3.3.1", @@ -3159,6 +3930,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/get-symbol-description": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", @@ -3327,6 +4108,273 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hast-util-embedded": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-3.0.0.tgz", + "integrity": "sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-is-element": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-format": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hast-util-format/-/hast-util-format-1.1.0.tgz", + "integrity": "sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-minify-whitespace": "^1.0.0", + "hast-util-phrasing": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "html-whitespace-sensitive-tag-names": "^3.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-dom": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-5.0.1.tgz", + "integrity": "sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==", + "license": "ISC", + "dependencies": { + "@types/hast": "^3.0.0", + "hastscript": "^9.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-has-property": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz", + "integrity": "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-body-ok-link": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-is-body-ok-link/-/hast-util-is-body-ok-link-3.0.1.tgz", + "integrity": "sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-minify-whitespace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hast-util-minify-whitespace/-/hast-util-minify-whitespace-1.0.1.tgz", + "integrity": "sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-phrasing": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-phrasing/-/hast-util-phrasing-3.0.1.tgz", + "integrity": "sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-has-property": "^3.0.0", + "hast-util-is-body-ok-link": "^3.0.0", + "hast-util-is-element": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-mdast": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/hast-util-to-mdast/-/hast-util-to-mdast-10.1.2.tgz", + "integrity": "sha512-FiCRI7NmOvM4y+f5w32jPRzcxDIz+PUqDwEqn1A+1q2cdp3B8Gx7aVrXORdOKjMNDQsD1ogOr896+0jJHW1EFQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-phrasing": "^3.0.0", + "hast-util-to-html": "^9.0.0", + "hast-util-to-text": "^4.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "mdast-util-to-string": "^4.0.0", + "rehype-minify-whitespace": "^6.0.0", + "trim-trailing-lines": "^2.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-text": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -3335,6 +4383,26 @@ "react-is": "^16.7.0" } }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/html-whitespace-sensitive-tag-names": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-whitespace-sensitive-tag-names/-/html-whitespace-sensitive-tag-names-3.0.1.tgz", + "integrity": "sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -3618,6 +4686,18 @@ "node": ">=8" } }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -3757,6 +4837,16 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/isomorphic.js": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz", + "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==", + "license": "MIT", + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, "node_modules/iterator.prototype": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", @@ -3889,14 +4979,35 @@ "node": ">= 0.8.0" } }, - "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "dev": true, - "engines": { - "node": ">=10" - } + "node_modules/lib0": { + "version": "0.2.114", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.114.tgz", + "integrity": "sha512-gcxmNFzA4hv8UYi8j43uPlQ7CGcyMJ2KQb5kZASw6SnAKAf10hK12i2fjrS3Cl/ugZa5Ui6WwIu1/6MIXiHttQ==", + "license": "MIT", + "dependencies": { + "isomorphic.js": "^0.2.4" + }, + "bin": { + "0ecdsa-generate-keypair": "bin/0ecdsa-generate-keypair.js", + "0gentesthtml": "bin/gentesthtml.js", + "0serve": "bin/0serve.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } }, "node_modules/lines-and-columns": { "version": "1.2.4", @@ -3904,6 +5015,21 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/linkifyjs": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.2.tgz", + "integrity": "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==", + "license": "MIT" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -3932,8 +5058,17 @@ "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, "node_modules/loose-envify": { "version": "1.4.0", @@ -3952,17 +5087,835 @@ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "dependencies": { - "yallist": "^3.0.2" + "yallist": "^3.0.2" + } + }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" }, "node_modules/micromatch": { "version": "4.0.5", @@ -4025,16 +5978,16 @@ } }, "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", - "dev": true, + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -4212,6 +6165,12 @@ "node": ">= 0.8.0" } }, + "node_modules/orderedmap": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz", + "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==", + "license": "MIT" + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -4254,6 +6213,30 @@ "node": ">=6" } }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -4288,10 +6271,10 @@ "dev": true }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -4324,10 +6307,9 @@ } }, "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "dev": true, + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", "funding": [ { "type": "opencollective", @@ -4342,10 +6324,11 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "nanoid": "^3.3.7", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -4368,126 +6351,395 @@ "postcss": "^8.0.0" } }, - "node_modules/postcss-import/node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, + "node_modules/postcss-import/node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", + "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", + "dev": true, + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^2.1.1" + }, + "engines": { + "node": ">= 14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.11" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", + "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/prosemirror-changeset": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.3.1.tgz", + "integrity": "sha512-j0kORIBm8ayJNl3zQvD1TTPHJX3g042et6y/KQhZhnPrruO8exkTgG8X+NRpj7kIyMMEx74Xb3DyMIBtO0IKkQ==", + "license": "MIT", + "dependencies": { + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-collab": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.1.tgz", + "integrity": "sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0" + } + }, + "node_modules/prosemirror-commands": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz", + "integrity": "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.10.2" + } + }, + "node_modules/prosemirror-dropcursor": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.2.tgz", + "integrity": "sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0", + "prosemirror-view": "^1.1.0" + } + }, + "node_modules/prosemirror-gapcursor": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.4.0.tgz", + "integrity": "sha512-z00qvurSdCEWUIulij/isHaqu4uLS8r/Fi61IbjdIPJEonQgggbJsLnstW7Lgdk4zQ68/yr6B6bf7sJXowIgdQ==", + "license": "MIT", + "dependencies": { + "prosemirror-keymap": "^1.0.0", + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-view": "^1.0.0" + } + }, + "node_modules/prosemirror-highlight": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/prosemirror-highlight/-/prosemirror-highlight-0.13.0.tgz", + "integrity": "sha512-GIC2VCTUnukNdsEGLQWWOVpYPl/7/KrVp4xs7XMB48/4rhUrHK8hp8TEog4Irmv+2kmjx24RLnaisGOCP6U8jw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ocavue" + }, + "peerDependencies": { + "@shikijs/types": "^1.29.2 || ^2.0.0 || ^3.0.0", + "@types/hast": "^3.0.0", + "highlight.js": "^11.9.0", + "lowlight": "^3.1.0", + "prosemirror-model": "^1.19.3", + "prosemirror-state": "^1.4.3", + "prosemirror-transform": "^1.8.0", + "prosemirror-view": "^1.32.4", + "refractor": "^5.0.0", + "sugar-high": "^0.6.1 || ^0.7.0 || ^0.8.0 || ^0.9.0" + }, + "peerDependenciesMeta": { + "@shikijs/types": { + "optional": true + }, + "@types/hast": { + "optional": true + }, + "highlight.js": { + "optional": true + }, + "lowlight": { + "optional": true + }, + "prosemirror-model": { + "optional": true + }, + "prosemirror-state": { + "optional": true + }, + "prosemirror-transform": { + "optional": true + }, + "prosemirror-view": { + "optional": true + }, + "refractor": { + "optional": true + }, + "sugar-high": { + "optional": true + } + } + }, + "node_modules/prosemirror-history": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.5.0.tgz", + "integrity": "sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.2.2", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.31.0", + "rope-sequence": "^1.3.0" + } + }, + "node_modules/prosemirror-inputrules": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.5.1.tgz", + "integrity": "sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-keymap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.3.tgz", + "integrity": "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "w3c-keyname": "^2.2.0" + } + }, + "node_modules/prosemirror-markdown": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.13.2.tgz", + "integrity": "sha512-FPD9rHPdA9fqzNmIIDhhnYQ6WgNoSWX9StUZ8LEKapaXU9i6XgykaHKhp6XMyXlOWetmaFgGDS/nu/w9/vUc5g==", + "license": "MIT", + "dependencies": { + "@types/markdown-it": "^14.0.0", + "markdown-it": "^14.0.0", + "prosemirror-model": "^1.25.0" + } + }, + "node_modules/prosemirror-menu": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.2.5.tgz", + "integrity": "sha512-qwXzynnpBIeg1D7BAtjOusR+81xCp53j7iWu/IargiRZqRjGIlQuu1f3jFi+ehrHhWMLoyOQTSRx/IWZJqOYtQ==", + "license": "MIT", + "dependencies": { + "crelt": "^1.0.0", + "prosemirror-commands": "^1.0.0", + "prosemirror-history": "^1.0.0", + "prosemirror-state": "^1.0.0" + } + }, + "node_modules/prosemirror-model": { + "version": "1.25.4", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz", + "integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==", + "license": "MIT", + "dependencies": { + "orderedmap": "^2.0.0" + } + }, + "node_modules/prosemirror-schema-basic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.4.tgz", + "integrity": "sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.25.0" + } + }, + "node_modules/prosemirror-schema-list": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz", + "integrity": "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==", + "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.7.3" } }, - "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "dev": true, + "node_modules/prosemirror-state": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz", + "integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==", + "license": "MIT", "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.4.21" + "prosemirror-model": "^1.0.0", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.27.0" } }, - "node_modules/postcss-load-config": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", - "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", - "dev": true, + "node_modules/prosemirror-tables": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.8.1.tgz", + "integrity": "sha512-DAgDoUYHCcc6tOGpLVPSU1k84kCUWTWnfWX3UDy2Delv4ryH0KqTD6RBI6k4yi9j9I8gl3j8MkPpRD/vWPZbug==", + "license": "MIT", "dependencies": { - "lilconfig": "^2.0.5", - "yaml": "^2.1.1" - }, - "engines": { - "node": ">= 14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } + "prosemirror-keymap": "^1.2.2", + "prosemirror-model": "^1.25.0", + "prosemirror-state": "^1.4.3", + "prosemirror-transform": "^1.10.3", + "prosemirror-view": "^1.39.1" } }, - "node_modules/postcss-nested": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", - "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", - "dev": true, + "node_modules/prosemirror-trailing-node": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-3.0.0.tgz", + "integrity": "sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==", + "license": "MIT", "dependencies": { - "postcss-selector-parser": "^6.0.11" - }, - "engines": { - "node": ">=12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "@remirror/core-constants": "3.0.0", + "escape-string-regexp": "^4.0.0" }, "peerDependencies": { - "postcss": "^8.2.14" + "prosemirror-model": "^1.22.1", + "prosemirror-state": "^1.4.2", + "prosemirror-view": "^1.33.8" } }, - "node_modules/postcss-selector-parser": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", - "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", - "dev": true, - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, + "node_modules/prosemirror-trailing-node/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" + "node_modules/prosemirror-transform": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.10.5.tgz", + "integrity": "sha512-RPDQCxIDhIBb1o36xxwsaeAvivO8VLJcgBtzmOwQ64bMtsVFh5SSuJ6dWSxO1UsHTiTXPCgQm3PDJt7p6IOLbw==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.21.0" } }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "node_modules/prosemirror-view": { + "version": "1.41.3", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.3.tgz", + "integrity": "sha512-SqMiYMUQNNBP9kfPhLO8WXEk/fon47vc52FQsUiJzTBuyjKgEcoAwMyF04eQ4WZ2ArMn7+ReypYL60aKngbACQ==", + "license": "MIT", "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" + "prosemirror-model": "^1.20.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0" } }, "node_modules/proxy-from-env": { @@ -4504,6 +6756,15 @@ "node": ">=6" } }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/qrcode.react": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-3.1.0.tgz", @@ -5156,11 +7417,31 @@ "react": "^18.2.0" } }, + "node_modules/react-icons": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", + "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-number-format": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-5.4.4.tgz", + "integrity": "sha512-wOmoNZoOpvMminhifQYiYSTCLUDOiUbBunrMrMjA+dV52sY+vck1S4UhR6PkgnoCquvvMSeJjErXZ4qSaWCliA==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/react-redux": { "version": "8.1.3", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", @@ -5213,6 +7494,55 @@ "node": ">=0.10.0" } }, + "node_modules/react-remove-scroll": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz", + "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==", + "license": "MIT", + "peer": true, + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-router": { "version": "6.16.0", "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.16.0.tgz", @@ -5259,6 +7589,47 @@ "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-textarea-autosize": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.9.tgz", + "integrity": "sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.20.13", + "use-composed-ref": "^1.3.0", + "use-latest": "^1.2.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -5338,6 +7709,147 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/rehype-format": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/rehype-format/-/rehype-format-5.0.1.tgz", + "integrity": "sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-format": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-minify-whitespace": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/rehype-minify-whitespace/-/rehype-minify-whitespace-6.0.2.tgz", + "integrity": "sha512-Zk0pyQ06A3Lyxhe9vGtOtzz3Z0+qZ5+7icZ/PL/2x1SHPbKao5oB/g/rlc6BCTajqBb33JcOe71Ye1oFsuYbnw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-minify-whitespace": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-parse": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz", + "integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-from-html": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-remark": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/rehype-remark/-/rehype-remark-10.0.1.tgz", + "integrity": "sha512-EmDndlb5NVwXGfUa4c9GPK+lXeItTilLhE6ADSaQuHr4JUlKw9MidzGzx4HpqZrNCt6vnHmEifXQiiA+CEnjYQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "hast-util-to-mdast": "^10.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-stringify": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", + "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-to-html": "^9.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/reselect": { "version": "4.1.8", "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", @@ -5415,6 +7927,12 @@ "fsevents": "~2.3.2" } }, + "node_modules/rope-sequence": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz", + "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==", + "license": "MIT" + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -5509,6 +8027,12 @@ "node": ">= 0.4" } }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5579,14 +8103,24 @@ } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "dev": true, + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/string-convert": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", @@ -5657,6 +8191,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -5681,10 +8229,45 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/styled-components": { + "version": "6.1.19", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.19.tgz", + "integrity": "sha512-1v/e3Dl1BknC37cXMhwGomhO8AkYmN41CqyX9xhUDxry1ns3BFQy2lLDRQXJRdVVWB9OHemv/53xaStimvWyuA==", + "license": "MIT", + "dependencies": { + "@emotion/is-prop-valid": "1.2.2", + "@emotion/unitless": "0.8.1", + "@types/stylis": "4.2.5", + "css-to-react-native": "3.2.0", + "csstype": "3.1.3", + "postcss": "8.4.49", + "shallowequal": "1.1.0", + "stylis": "4.3.2", + "tslib": "2.6.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" + } + }, + "node_modules/styled-components/node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==", + "license": "MIT" + }, "node_modules/stylis": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.0.tgz", - "integrity": "sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ==" + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", + "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==", + "license": "MIT" }, "node_modules/sucrase": { "version": "3.34.0", @@ -5752,6 +8335,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tabbable": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.3.0.tgz", + "integrity": "sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==", + "license": "MIT" + }, "node_modules/tailwindcss": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.3.tgz", @@ -5867,12 +8456,48 @@ "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trim-trailing-lines": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-2.1.0.tgz", + "integrity": "sha512-5UR5Biq4VlVOtzqkm2AZlgvSlDJtME46uV0br0gENbwN4l5+mMKT4b9gJKqWtuL2zAIqajGJGuvbCbcAJUZqBg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", "dev": true }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "license": "0BSD" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -5962,6 +8587,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -5977,6 +8608,107 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-find-after": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", @@ -6016,6 +8748,99 @@ "punycode": "^2.1.0" } }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "peer": true, + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-composed-ref": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.4.0.tgz", + "integrity": "sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz", + "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-latest": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.3.0.tgz", + "integrity": "sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "use-isomorphic-layout-effect": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", @@ -6030,11 +8855,62 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/vanilla-colorful": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/vanilla-colorful/-/vanilla-colorful-0.7.2.tgz", "integrity": "sha512-z2YZusTFC6KnLERx1cgoIRX2CjPRP0W75N+3CC6gbvdX5Ch47rZkEMGO2Xnf+IEmi3RiFLxS18gayMA27iU7Kg==" }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vite": { "version": "4.4.11", "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.11.tgz", @@ -6090,6 +8966,22 @@ } } }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT" + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6215,6 +9107,50 @@ "node": ">=0.4.0" } }, + "node_modules/y-prosemirror": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/y-prosemirror/-/y-prosemirror-1.3.7.tgz", + "integrity": "sha512-NpM99WSdD4Fx4if5xOMDpPtU3oAmTSjlzh5U4353ABbRHl1HtAFUx6HlebLZfyFxXN9jzKMDkVbcRjqOZVkYQg==", + "license": "MIT", + "dependencies": { + "lib0": "^0.2.109" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + }, + "peerDependencies": { + "prosemirror-model": "^1.7.1", + "prosemirror-state": "^1.2.3", + "prosemirror-view": "^1.9.10", + "y-protocols": "^1.0.1", + "yjs": "^13.5.38" + } + }, + "node_modules/y-protocols": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.6.tgz", + "integrity": "sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q==", + "license": "MIT", + "dependencies": { + "lib0": "^0.2.85" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + }, + "peerDependencies": { + "yjs": "^13.0.0" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -6230,6 +9166,23 @@ "node": ">= 14" } }, + "node_modules/yjs": { + "version": "13.6.27", + "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.27.tgz", + "integrity": "sha512-OIDwaflOaq4wC6YlPBy2L6ceKeKuF7DeTxx+jPzv1FHn9tCZ0ZwSRnUBxD05E3yed46fv/FWJbvR+Ud7x0L7zw==", + "license": "MIT", + "dependencies": { + "lib0": "^0.2.99" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -6268,6 +9221,16 @@ "optional": true } } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/package.json b/package.json index 7b38c56..f572581 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,9 @@ }, "dependencies": { "@ant-design/icons": "^5.2.6", + "@blocknote/core": "^0.42.3", + "@blocknote/mantine": "^0.42.3", + "@blocknote/react": "^0.42.3", "@ckeditor/ckeditor5-build-classic": "^40.0.0", "@ckeditor/ckeditor5-react": "^6.1.0", "@reduxjs/toolkit": "^1.9.7", @@ -28,6 +31,7 @@ "react-slick": "^0.29.0", "slick-carousel": "^1.8.1", "socket.io-client": "^4.7.2", + "styled-components": "^6.1.19", "zustand": "^4.4.6" }, "devDependencies": { diff --git a/src/components/BlockNoteEditor/index.jsx b/src/components/BlockNoteEditor/index.jsx new file mode 100644 index 0000000..f37940e --- /dev/null +++ b/src/components/BlockNoteEditor/index.jsx @@ -0,0 +1,220 @@ +import { useEffect, useMemo, useState, useCallback, useRef } from "react"; +import { BlockNoteView } from "@blocknote/mantine"; +import { useCreateBlockNote } from "@blocknote/react"; +import "@blocknote/core/fonts/inter.css"; +import "@blocknote/mantine/style.css"; +import { Button } from "antd"; +import { FullscreenOutlined, FullscreenExitOutlined } from "@ant-design/icons"; +import styled from "styled-components"; +import PropTypes from "prop-types"; + +const BlockNoteEditor = ({ initialContent, onChange, placeholder = "Nhập nội dung bài viết..." }) => { + const [isFullscreen, setIsFullscreen] = useState(false); + const onChangeRef = useRef(onChange); + + // Update ref when onChange changes + useEffect(() => { + onChangeRef.current = onChange; + }, [onChange]); + + // Parse initial content + const parsedInitialContent = useMemo(() => { + if (!initialContent) return undefined; + + try { + if (typeof initialContent === 'string' && initialContent.trim().startsWith('<')) { + return undefined; // BlockNote will use default blocks + } + + if (Array.isArray(initialContent)) { + return initialContent; + } + + return undefined; + } catch (error) { + console.error('Error parsing initial content:', error); + return undefined; + } + }, [initialContent]); + + // Create editor instance + const editor = useCreateBlockNote({ + initialContent: parsedInitialContent, + onChange: () => { + handleEditorChange(); + }, + }); + + // Load HTML content separately + useEffect(() => { + if (!editor || !initialContent) return; + + if (typeof initialContent === 'string' && initialContent.trim().startsWith('<')) { + const loadContent = async () => { + try { + const blocks = editor.tryParseHTMLToBlocks(initialContent); + editor.replaceBlocks(editor.document, blocks); + } catch (error) { + console.error('Error loading HTML content:', error); + } + }; + loadContent(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [editor]); // Only run once when editor is ready + + // Handle content changes + const handleEditorChange = useCallback(async () => { + if (!editor) return; + + try { + const blocks = editor.document; + const html = editor.blocksToHTMLLossy(blocks); + + // Use setTimeout to defer the state update and avoid blocking UI + setTimeout(() => { + if (onChangeRef.current) { + onChangeRef.current(html, blocks); + } + }, 0); + } catch (error) { + console.error('Error handling editor change:', error); + } + }, [editor]); + + useEffect(() => { + if (!editor) return; + + let timeoutId = null; + + // Debounce the change handler to avoid rapid successive calls + const debouncedHandler = () => { + if (timeoutId) { + clearTimeout(timeoutId); + } + + timeoutId = setTimeout(() => { + handleEditorChange(); + }, 100); + }; + + // Subscribe to editor changes + const unsubscribe = editor.onChange(debouncedHandler); + + return () => { + if (timeoutId) { + clearTimeout(timeoutId); + } + if (typeof unsubscribe === 'function') { + unsubscribe(); + } + }; + }, [editor, handleEditorChange]); + + // Handle fullscreen + useEffect(() => { + const handleEscape = (e) => { + if (e.key === 'Escape' && isFullscreen) { + setIsFullscreen(false); + } + }; + + if (isFullscreen) { + document.addEventListener('keydown', handleEscape); + document.body.style.overflow = 'hidden'; + } else { + document.body.style.overflow = ''; + } + + return () => { + document.removeEventListener('keydown', handleEscape); + document.body.style.overflow = ''; + }; + }, [isFullscreen]); + + if (!editor) { + return
Loading editor...
; + } + + return ( + + +

{isFullscreen ? 'Chế độ toàn màn hình - Nhấn ESC để thoát' : 'Nội dung bài viết'}

+ +
+ +
+ ); +}; + +BlockNoteEditor.propTypes = { + initialContent: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.array + ]), + onChange: PropTypes.func, + placeholder: PropTypes.string +}; + +const EditorWrapper = styled.div` + position: relative; + border: 1px solid #d9d9d9; + border-radius: 8px; + min-height: 400px; + + &.fullscreen { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 9999; + background: white; + border-radius: 0; + + .bn-container { + height: calc(100vh - 60px); + } + } + + .bn-container { + padding: 16px; + + .bn-editor { + min-height: 300px; + } + } +`; + +const EditorHeader = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px 16px; + border-bottom: 1px solid #d9d9d9; + background: #fafafa; + border-radius: 8px 8px 0 0; + + .fullscreen & { + border-radius: 0; + } + + h4 { + margin: 0; + font-size: 14px; + color: #595959; + } +`; + +export default BlockNoteEditor; diff --git a/src/components/Dashboard/StatCard.jsx b/src/components/Dashboard/StatCard.jsx index 7177f1d..32333a4 100644 --- a/src/components/Dashboard/StatCard.jsx +++ b/src/components/Dashboard/StatCard.jsx @@ -40,8 +40,7 @@ const StatCard = ({ return ( { + return ( + + + Preview + + }> + + {!data.title && !data.content ? ( +
+ +
Chưa có nội dung
+
Bắt đầu nhập tiêu đề và nội dung để xem preview
+
+ ) : ( + <> + {/* Title */} +

+ {data.title || 'Tiêu đề bài viết'} +

+ + {/* Meta info */} +
+
+ + + {admin?.data?.username || 'Tác giả'} + +
+ +
+ + + {new Date().toLocaleDateString('vi-VN')} + +
+ + {data.category && ( + + {getCategoryName(data.category)} + + )} + +
+
+ + {getStatusText(data.status)} + +
+
+ + {/* Thumbnail */} + {data.thumbnail && ( +
+ Preview +
+ )} + + {/* Content */} +
Nội dung bài viết sẽ hiển thị tại đây...

' + }} + /> + + {/* Tags */} + {data.tags && data.tags.length > 0 && ( +
+
+ + Tags: +
+
+ {getTagNames(data.tags).map((tag, index) => ( + + {tag} + + ))} +
+
+ )} + + )} + + + ); +}; + +PostPreview.propTypes = { + data: PropTypes.shape({ + title: PropTypes.string, + content: PropTypes.string, + category: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + tags: PropTypes.array, + status: PropTypes.number, + thumbnail: PropTypes.string + }).isRequired, + categoryDatas: PropTypes.array.isRequired, + tagsDatas: PropTypes.array.isRequired, + statusPostDatas: PropTypes.array.isRequired, + admin: PropTypes.shape({ + data: PropTypes.shape({ + username: PropTypes.string + }) + }).isRequired, + getCategoryName: PropTypes.func.isRequired, + getTagNames: PropTypes.func.isRequired, + getStatusText: PropTypes.func.isRequired +}; + +export default PostPreview; diff --git a/src/components/PostPreview/styles.js b/src/components/PostPreview/styles.js new file mode 100644 index 0000000..2367196 --- /dev/null +++ b/src/components/PostPreview/styles.js @@ -0,0 +1,73 @@ +import styled from 'styled-components'; + +export const PreviewSection = styled.div` + flex: ${props => props.$visible ? '0 0 48%' : '0'}; + max-width: ${props => props.$visible ? '48%' : '0'}; + opacity: ${props => props.$visible ? '1' : '0'}; + overflow: hidden; + position: sticky; + top: 20px; + height: fit-content; + max-height: calc(100vh - 40px); + transition: all 0.3s ease; + + @media (max-width: 768px) { + flex: ${props => props.$visible ? '1' : '0'}; + max-width: 100%; + position: relative; + top: 0; + max-height: none; + margin-top: ${props => props.$visible ? '16px' : '0'}; + } +`; + +export const FormSection = styled.div` + flex: ${props => props.$previewVisible ? '0 0 50%' : '1'}; + max-width: ${props => props.$previewVisible ? '50%' : '100%'}; + transition: all 0.3s ease; + + @media (max-width: 768px) { + flex: none; + max-width: 100%; + } +`; + +export const PreviewToggle = styled.button` + margin-bottom: 16px; + border-radius: 8px; + width: auto; + height: 40px; + padding: 0 16px; + box-shadow: 0 2px 8px rgba(124, 58, 237, 0.3); + display: flex; + align-items: center; + gap: 8px; + background: #7c3aed; + color: white; + border: none; + cursor: pointer; + font-size: 14px; + font-weight: 500; + transition: all 0.2s; + + &:hover { + background: #6d28d9; + box-shadow: 0 4px 12px rgba(124, 58, 237, 0.4); + } + + @media (max-width: 768px) { + width: 100%; + justify-content: center; + } +`; + +export const FormContainer = styled.div` + display: flex; + gap: 24px; + min-height: 100vh; + + @media (max-width: 768px) { + flex-direction: column; + gap: 16px; + } +`; diff --git a/src/configs/axiosConfig.js b/src/configs/axiosConfig.js index 568f53b..4133371 100644 --- a/src/configs/axiosConfig.js +++ b/src/configs/axiosConfig.js @@ -20,10 +20,18 @@ const processQueue = (error, token = null) => { prom.resolve(token); } }); - + failedQueue = []; }; +// Cleanup khi window unload để tránh memory leak +if (typeof window !== "undefined") { + window.addEventListener('beforeunload', () => { + isRefreshing = false; + failedQueue = []; + }); +} + // Interceptor để thêm token vào header axiosInstance.interceptors.request.use( function (config) { @@ -57,7 +65,11 @@ axiosInstance.interceptors.response.use( if (error?.response?.status === 401) { if (error?.response?.data?.cause === 'JsonWebTokenError') { localStorage.removeItem("admin"); - window.location.reload(); + notification.warning({ + message: 'Phiên đăng nhập hết hạn', + description: 'Vui lòng đăng nhập lại' + }); + globalThis.location.reload(); } else if (error?.response?.data?.authError) { notification.error({ @@ -65,22 +77,22 @@ axiosInstance.interceptors.response.use( description: error?.response?.data?.message, }); } else { - // Xử lý refresh token với queue + // Handle refresh tokens with queue if (originalRequest._retry) { return Promise.reject(error); } if (isRefreshing) { - // Nếu đang refresh, thêm request vào queue - // để xử lý các request còn lại cần token mới - // các request khác sẽ đợi cho đến khi token được refresh xong + // If refreshing, add request to queue + // to handle other requests that need the new token + // other requests will wait until the token is refreshed return new Promise(function(resolve, reject) { failedQueue.push({ resolve, reject }); }).then(token => { originalRequest.headers.Authorization = `Bearer ${token}`; return axiosInstance(originalRequest); }).catch(err => { - return Promise.reject(err); + throw err; }); } @@ -90,18 +102,18 @@ axiosInstance.interceptors.response.use( try { const response = await AdminAuthServices.refreshToken(); const token = response?.accessToken; - + if (token) { const authData = { token }; const authString = JSON.stringify(authData); localStorage.setItem("admin", authString); - + // Cập nhật header cho request hiện tại originalRequest.headers.Authorization = `Bearer ${token}`; - + // Xử lý tất cả requests đang chờ processQueue(null, token); - + // Retry request ban đầu return axiosInstance(originalRequest); } else { @@ -109,9 +121,10 @@ axiosInstance.interceptors.response.use( } } catch (refreshError) { processQueue(refreshError, null); + failedQueue = []; // Clear queue on error localStorage.removeItem("admin"); - window.location.reload(); - return Promise.reject(refreshError); + globalThis.location.reload(); + throw refreshError; } finally { isRefreshing = false; } diff --git a/src/context/AdminContext.jsx b/src/context/AdminContext.jsx index 4f7af1f..a3989a7 100644 --- a/src/context/AdminContext.jsx +++ b/src/context/AdminContext.jsx @@ -1,4 +1,4 @@ -import { createContext, useState } from "react"; +import { createContext, useMemo, useState } from "react"; import PropTypes from "prop-types"; const AdminContext = createContext(); @@ -9,12 +9,15 @@ const AdminContextProvider = ({ children }) => { const stored = localStorage.getItem("admin"); return stored ? JSON.parse(stored) : null; } catch (e) { + console.error("Failed to parse admin from localStorage:", e); return null; } }); + const contextValue = useMemo(() => ({ admin, setAdmin }), [admin]); + return ( - {children} + {children} ); }; diff --git a/src/layouts/app-layouts/admin-layouts/SideBar.jsx b/src/layouts/app-layouts/admin-layouts/SideBar.jsx index eca60bb..25a1e75 100644 --- a/src/layouts/app-layouts/admin-layouts/SideBar.jsx +++ b/src/layouts/app-layouts/admin-layouts/SideBar.jsx @@ -118,15 +118,11 @@ const SideBar = ({ collapsed }) => { const getOpenKeysFromSelectedKey = (key) => { if (!key) return []; - // Tìm parent key từ selected key - // Ví dụ: "2-1" -> parent là "2" - const parentKey = key.split('-')[0]; - - // Chỉ return parent key nếu selected key có dấu gạch ngang (là submenu) - if (key.includes('-')) { - return [parentKey]; + const keyParts = key.split('-'); + if (keyParts.length !== 2 || !keyParts[0] || !keyParts[1]) { + return []; } - return []; + return [keyParts[0]]; }; // Cập nhật selectedKey và openKeys khi location thay đổi diff --git a/src/layouts/app-layouts/admin-layouts/index.jsx b/src/layouts/app-layouts/admin-layouts/index.jsx index 38a4aba..1c43e53 100644 --- a/src/layouts/app-layouts/admin-layouts/index.jsx +++ b/src/layouts/app-layouts/admin-layouts/index.jsx @@ -48,7 +48,7 @@ const AdminLayout = () => { } }; getUserInfo(); - }, [admin?.token]); + }, [admin?.token, location.pathname]); const { token: { colorBgContainer }, } = theme.useToken(); diff --git a/src/pages/app-pages/admin-pages/post/Create.jsx b/src/pages/app-pages/admin-pages/post/Create.jsx index 1772b87..b87c876 100644 --- a/src/pages/app-pages/admin-pages/post/Create.jsx +++ b/src/pages/app-pages/admin-pages/post/Create.jsx @@ -1,49 +1,65 @@ import { useContext, useEffect, useState } from "react"; -import { Card, Form, Input, message, Select, Radio, DatePicker, Flex } from "antd"; -import { CKEditor } from "@ckeditor/ckeditor5-react"; -import ClassicEditor from "@ckeditor/ckeditor5-build-classic"; +import { Card, Form, Input, message, Select, Radio, DatePicker, Flex, Button } from "antd"; +import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons'; import categoryServices from "@/services/adminServices/categoryServices"; import postServices from "@/services/adminServices/postServices"; import tagServices from "@/services/adminServices/tagServices"; -import uploader from "@/utils/createUploader"; import { AdminContext } from "@/context/AdminContext"; import { ButtonAddForm } from "@/components/Btn/ButtonAddAndUpdateForm"; import UploadPhotoInput from "@/components/Input/UploadPhotoInput"; +import PostPreview from "@/components/PostPreview"; +import { FormContainer, FormSection, PreviewSection } from "@/components/PostPreview/styles"; +import BlockNoteEditor from "@/components/BlockNoteEditor"; const Create = () => { const [form] = Form.useForm(); const { admin } = useContext(AdminContext); const accessToken = admin?.token; const [loading, setLoading] = useState(false); + const [previewVisible, setPreviewVisible] = useState(true); + const [previewData, setPreviewData] = useState({ + title: '', + content: '', + category: null, + tags: [], + status: 0, + thumbnail: null + }); const [categoryDatas, setCategoryDatas] = useState([]); const [editorContent, setEditorContent] = useState(""); - const [editorError, setEditorError] = useState(""); - const maxContentLength = 10000; const [tagsDatas, setTagsDatas] = useState([]); const [statusPostDatas, setStatusPostDatas] = useState([]); const [statusPostSelected, setStatusPostSelected] = useState(0); const [fileList, setFileList] = useState([]); - const ckeditorConfig = { - extraPlugins: [uploader], + + const updatePreviewData = (field, value) => { + setPreviewData(prev => ({ + ...prev, + [field]: value + })); }; - const validateEditorContent = (value) => { - return value && value.trim().length > 0 ? undefined : "Nội dung là bắt buộc"; + + const getStatusText = (statusId) => { + const status = statusPostDatas.find(s => s.id === statusId); + return status?.name || 'Draft'; }; - const handleEditorChange = (event, editor) => { - const data = editor.getData(); - const requiredError = validateEditorContent(data); - if (requiredError) { - setEditorError(requiredError); - } else { - if (data.length <= maxContentLength) { - setEditorContent(data); - setEditorError(""); - } else { - setEditorError(`Nội dung không được vượt quá ${maxContentLength} ký tự.`); - } - } + const getCategoryName = (categoryId) => { + const category = categoryDatas.find(c => c.value === categoryId); + return category?.label || ''; + }; + + const getTagNames = (tagIds) => { + if (!tagIds) return []; + return tagIds.map(id => { + const tag = tagsDatas.find(t => t.value === id); + return tag?.label || id; + }); + }; + const handleEditorChange = (html) => { + setEditorContent(html); + updatePreviewData('content', html); }; const propUpload = { @@ -62,12 +78,16 @@ const Create = () => { newFileList = newFileList.slice(-1); if (info.file.status === "removed") { setFileList([]); + updatePreviewData('thumbnail', null); const file = info.file?.response?.data; if (file) { await postServices.deletePhoto(file?.filename); } } else { setFileList(newFileList); + if (info.file.status === 'done' && info.file.response?.data?.url) { + updatePreviewData('thumbnail', info.file.response.data.url); + } } }, }; @@ -83,7 +103,7 @@ const Create = () => { form.resetFields(); setEditorContent(""); setFileList([]); - // focus on first input + setPreviewData({ title: '', content: '', category: null, tags: [], status: 0, thumbnail: null }); form.getFieldInstance("title").focus(); setLoading(false); } catch (error) { @@ -100,6 +120,7 @@ const Create = () => { (option?.label ?? "").toLowerCase().includes(input.toLowerCase()); const handleChangeStatusPost = (e) => { setStatusPostSelected(e.target.value); + updatePreviewData('status', e.target.value); }; useEffect(() => { @@ -146,15 +167,29 @@ const Create = () => { }; fetch(); }, []); + return ( - - + + {/* Form Section */} + + + {/* Preview Toggle Button */} + + + { }, ]} > - + updatePreviewData('title', e.target.value)} + /> - @@ -265,6 +301,7 @@ const Create = () => { filterOption={filterOption} options={categoryDatas} allowClear + onChange={(value) => updatePreviewData('category', value)} /> { // optionFilterProp="children" filterOption={filterOption} options={tagsDatas} + onChange={(value) => updatePreviewData('tags', value)} /> - - + + + + + {/* Preview Section */} + + + + ); }; diff --git a/src/pages/app-pages/admin-pages/post/Edit.jsx b/src/pages/app-pages/admin-pages/post/Edit.jsx index 2a51d09..3032b94 100644 --- a/src/pages/app-pages/admin-pages/post/Edit.jsx +++ b/src/pages/app-pages/admin-pages/post/Edit.jsx @@ -1,17 +1,18 @@ import { useContext, useEffect, useState } from "react"; import { useNavigate, useParams } from "react-router-dom"; import dayjs from "dayjs"; -import { Card, Form, Input, message, Select, Radio, DatePicker, Flex } from "antd"; -import { CKEditor } from "@ckeditor/ckeditor5-react"; -import ClassicEditor from "@ckeditor/ckeditor5-build-classic"; +import { Card, Form, Input, message, Select, Radio, DatePicker, Flex, Button } from "antd"; +import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons'; import { AdminContext } from "@/context/AdminContext"; import postServices from "@/services/adminServices/postServices"; import tagServices from "@/services/adminServices/tagServices"; -import uploader from "@/utils/createUploader"; import categoryServices from "@/services/adminServices/categoryServices"; import UploadPhotoInput from "@/components/Input/UploadPhotoInput"; import { ButtonUpdateForm } from "@/components/Btn/ButtonAddAndUpdateForm"; +import PostPreview from "@/components/PostPreview"; +import { FormContainer, FormSection, PreviewSection } from "@/components/PostPreview/styles"; +import BlockNoteEditor from "@/components/BlockNoteEditor"; const Edit = () => { const navigate = useNavigate(); @@ -23,32 +24,48 @@ const Edit = () => { const [loading, setLoading] = useState(false); const [data, setData] = useState({}); const [editorContent, setEditorContent] = useState(""); - const [editorError, setEditorError] = useState(""); - const maxContentLength = 10000; const [tagsDatas, setTagsDatas] = useState([]); const [statusPostDatas, setStatusPostDatas] = useState([]); const [statusPostSelected, setStatusPostSelected] = useState(0); const [fileList, setFileList] = useState([]); - const ckeditorConfig = { - extraPlugins: [uploader], + const [previewVisible, setPreviewVisible] = useState(true); + const [previewData, setPreviewData] = useState({ + title: '', + content: '', + category: null, + tags: [], + status: 0, + thumbnail: null + }); + + const updatePreviewData = (field, value) => { + setPreviewData(prev => ({ + ...prev, + [field]: value + })); }; - const validateEditorContent = (value) => { - return value && value.trim().length > 0 ? undefined : "Nội dung là bắt buộc"; + + const getStatusText = (statusId) => { + const status = statusPostDatas.find(s => s.id === statusId); + return status?.name || 'Draft'; }; - const handleEditorChange = (event, editor) => { - const data = editor.getData(); - const requiredError = validateEditorContent(data); - if (requiredError) { - setEditorError(requiredError); - } else { - if (data.length <= maxContentLength) { - setEditorContent(data); - setEditorError(""); - } else { - setEditorError(`Nội dung không được vượt quá ${maxContentLength} ký tự.`); - } - } + const getCategoryName = (categoryId) => { + const category = categoryDatas.find(c => c.value === categoryId); + return category?.label || ''; + }; + + const getTagNames = (tagIds) => { + if (!tagIds) return []; + return tagIds.map(id => { + const tag = tagsDatas.find(t => t.value === id); + return tag?.label || id; + }); + }; + + const handleEditorChange = (html) => { + setEditorContent(html); + updatePreviewData('content', html); }; const propUpload = { @@ -60,19 +77,23 @@ const Edit = () => { }, fileList: fileList, async onChange(info) { - // Note cần làm: khi xóa thì phải xóa cả ở trên cloudinary + // Note: when deleting, also delete on cloudinary let newFileList = [...info.fileList]; - // Giới hạn chỉ cho một tệp tin + // Limit to only one file newFileList = newFileList.slice(-1); if (info.file.status === "removed") { setFileList([]); + updatePreviewData('thumbnail', null); const filename = info.file?.name; if (filename) { await postServices.deletePhoto(filename); } } else { setFileList(newFileList); + if (info.file.status === 'done' && info.file.response?.data?.url) { + updatePreviewData('thumbnail', info.file.response.data.url); + } } }, }; @@ -105,6 +126,7 @@ const Edit = () => { (option?.label ?? "").toLowerCase().includes(input.toLowerCase()); const handleChangeStatusPost = (e) => { setStatusPostSelected(e.target.value); + updatePreviewData('status', e.target.value); }; useEffect(() => { @@ -114,16 +136,16 @@ const Edit = () => { setData(response); setEditorContent(response?.content); setStatusPostSelected(response?.status); - // if (response?.photo) - // setFileList([ - // { - // uid: "-1", - // name: response?.filename, - // // status: "done", - // url: response?.photo, - // thumbUrl: response?.photo, - // }, - // ]); + + // Initialize preview data + setPreviewData({ + title: response?.title || '', + content: response?.content || '', + category: response?.category_id || null, + tags: response?.tags || [], + status: response?.status || 0, + thumbnail: response?.photo || null + }); } catch (error) { console.log(error); } @@ -175,14 +197,26 @@ const Edit = () => { fetch(); }, []); return ( - -
+ + + + {/* Preview Toggle Button */} + + + @@ -212,25 +246,26 @@ const Edit = () => { }, ]} > - + updatePreviewData('title', e.target.value)} + /> - @@ -306,6 +341,7 @@ const Edit = () => { filterOption={filterOption} options={categoryDatas} allowClear + onChange={(value) => updatePreviewData('category', value)} /> { ]} >