fix: android dropdown menu button

This commit is contained in:
2026-06-27 20:27:44 +07:00
parent a7e569d9b4
commit 3fa80b69bf
6 changed files with 113 additions and 181 deletions
+45 -58
View File
@@ -1,107 +1,94 @@
# To AI Agent: Fix Mobile Dropdown Layout Displacement and Eliminate `.filter is not a function` Runtime Crashes # To AI Agent: Fix Mobile Dropdown Displacement and Eliminate `.filter` Array Runtime Crashes
## 1. Bug Analysis & Context ## 1. Context & Identified Bugs
We are resolving three critical layout and runtime bugs observed during Android Simulator testing (`image.png`): During Android Simulator testing, the frontend app encounters three critical layout and runtime execution failures:
1. **Dropdown Menu Displacement:** The profile/avatar menu container gets forced down to the extreme bottom of the viewport instead of floating as an absolute element directly beneath the user's top-bar avatar anchor.
1. **Dropdown Menu Displacement:** The menu drawer/dropdown container is losing its absolute coordinate anchor pool. Instead of floating gracefully on top of the map layer as a neat dropdown card, it wraps below the viewport grid, conflicting with the lower navigation action buttons ("Khám phá", "Chụp ảnh") and community photo streams. 2. **`Uncaught TypeError: g.filter is not a function`:** Triggering the Friend List components crashes the interface into a blank white screen.
2. **`g.filter is not a function` (Friends List Crash):** Triggering the Friend List components crashes the UI thread into a White Screen of Death (WSoD). 3. **`Error fetching connections: TypeError: (intermediate value).filter is not a function`:** Opening the Live Chat view crashes identical array loops.
3. **`Error fetching connections: TypeError: ...filter is not a function` (LiveChat Crash):** Opening the Live Chat view displays the exact same array mutation failure. - *Root Cause for 2 & 3:* The API response payload from the backend server is **not returning a clean primitive Array**. It returns an object wrapper (e.g., `{ success: true, data: [...] }`) or `undefined`/`null` due to connection timing gaps. Invoking `.filter()` on a non-array instantly kills the React rendering thread.
- *Root Cause for 2 & 3:* The asynchronous API network response payload or socket stream data returned from the server backend is **not a clean Array structure**. It is likely either wrapped inside an object wrapper (e.g., `{ success: true, friends: [] }`) or returns `null`/`undefined` due to network delays. Invoking `.filter()` directly on a non-array object instantly freezes the React rendering lifecycle.
--- ---
## 2. Refactoring Strategy ## 2. Refactoring Blueprint
### 2.1. Fix Dropdown Layout Context for Mobile/Android Viewports ### Step 1: Fix Dropdown Positioning Context (`MapProfileDropdown.tsx`)
On mobile viewports, traditional hover/click absolute dropdown boxes overflow or clip out. We must force the dropdown menu container inside `MapProfileDropdown.tsx` to act as a structured **Sticky Floating Overlay** or a dedicated **Mobile Bottom-Sheet Box** with a precise `z-index`. On desktop, absolute drop panels function normally. However, on mobile viewports or custom wrappers, they lose anchoring. We must force the container component to lock its coordinate space relative to the top bar element using modern CSS constraints:
### 2.2. Implement Defensive Array Architecture (Fix Crashes 2 & 3)
We must implement a protective array fallback guard across all data mapping blocks (`friends.filter`, `connections.filter`) using **`Array.isArray()`** validation, coupled with unified error boundaries.
---
## 3. Code Refactoring Blueprint
### Step 1: Overhaul Dropdown Styling for Mobile Boundaries (`MapProfileDropdown.tsx`)
Ensure the menu card isolates itself perfectly above the underlying view map layout and handles sizing dimensions cleanly:
```jsx ```jsx
{/* ✅ RESPONSIVE FIX: Mobile-optimized absolute floating panel structure */} {/* ✅ RESPONSIVE REFACTOR: Force absolute rendering locked beneath the profile button context */}
<div <div
className="absolute right-0 top-14 w-[280px] bg-slate-900/95 backdrop-blur-md border border-slate-800 rounded-2xl p-2 shadow-2xl flex flex-col space-y-0.5 text-xs text-slate-200 z-[999999]" className="absolute right-0 top-14 w-[280px] bg-slate-900/95 backdrop-blur-md border border-slate-800 rounded-2xl p-2 shadow-2xl flex flex-col space-y-0.5 text-xs text-slate-200 z-[999999]"
style={{ style={{
/* Tight mobile screen safety rails layout positioning guards */ maxHeight: '75vh',
maxHeight: '80vh',
overflowY: 'auto', overflowY: 'auto',
boxShadow: '0 20px 25px -5px rgba(0, 0, 0, 0.5), 0 10px 10px -5px rgba(0, 0, 0, 0.5)' boxShadow: '0 20px 25px -5px rgba(0, 0, 0, 0.6)'
}} }}
> >
{/* Menu list rows (Tạo tour, Hành trình, Thư viện ảnh...) populate cleanly here */} {/* Menu option items: Tạo tour, Hành trình của tôi, Thư viện ảnh... */}
<button className="flex items-center gap-3 w-full px-4 py-3 hover:bg-slate-800/80 rounded-xl text-left">...</button>
</div> </div>
### Step 2: Fix Friend List Array Mutation Crash (FriendsManagerModal.tsx) ### Step 2: Enforce Defensive Array Check on Friend List Loop (FriendsManagerModal.tsx)
Locate where the backend data hook is consumed. Inject an explicit defensive array verification check: Locate where the system handles friend collections. Implement an explicit array layout typecheck validation using Array.isArray() before doing any mutation logic:
// ❌ OLD CRASH-PRONE CODE: // ❌ OLD ERROR-PRONE PATTERN:
// const activeFriends = data.filter(f => f.status === 'active'); // const activeFriends = data.filter(f => f.status === 'active');
// ✅ NEW DEFENSIVE RAY WRAPPER: // ✅ NEW IMPERATIVE PROTECTION:
const [friendsList, setFriendsList] = useState<any[]>([]); const [friendsList, setFriendsList] = useState<any[]>([]);
useEffect(() => { useEffect(() => {
api.get('/friends/connections') api.get('/friends/connections')
.then((res) => { .then((res) => {
// Deconstruct and verify input type carefully before committing to state const payload = res.data;
const rawPayload = res.data;
if (rawPayload && Array.isArray(rawPayload)) { // Defensively parse and normalize the input shape
setFriendsList(rawPayload); if (payload && Array.isArray(payload)) {
} else if (rawPayload && Array.isArray(rawPayload.data)) { setFriendsList(payload);
setFriendsList(rawPayload.data); // Fallback unpacker matching nested API responses } else if (payload && Array.isArray(payload.data)) {
setFriendsList(payload.data); // Support nested response architectures safely
} else { } else {
console.error("⚠️ Expected array structure but received:", rawPayload); console.error("⚠️ Backend returned non-array structure:", payload);
setFriendsList([]); // Secure fallback to empty array initialization to protect .filter loops setFriendsList([]); // Default fallback to shield downstream loops
} }
}) })
.catch((err) => { .catch((err) => {
console.error("Failed to compile connections list stream safely:", err); console.error("Error reading friends list data stream:", err);
setFriendsList([]); // Fallback safety initialization setFriendsList([]);
}); });
}, []); }, []);
// Secure conditional processing guard rail // Safely filter verified arrays exclusively
const activeFriends = Array.isArray(friendsList) const activeFriends = Array.isArray(friendsList)
? friendsList.filter((f: any) => f && f.status === 'active') ? friendsList.filter((f: any) => f && f.status === 'active')
: []; : [];
### Step 3: Fix Live Chat Connections Filtering Failure (LiveChatModal.tsx) ### Step 3: Secure Live Chat Target Connections Handler (LiveChatModal.tsx)
Locate the data fetcher inside your real-time socket listener or chat state parser module and re-engineer it with identical fallback shields: Apply the exact same defensive architecture inside your real-time chat sync queries or global context connection monitors:
const fetchUserConnections = async () => { const fetchUserConnections = async () => {
try { try {
const response = await api.get('/chat/connections'); const response = await api.get('/chat/connections');
const resultData = response.data; const responseBody = response.data;
// Direct defensive validation shield check // Check array schema context explicitly
const verifiedConnectionsArray = Array.isArray(resultData) const verifiedConnections = Array.isArray(responseBody)
? resultData ? responseBody
: (resultData && Array.isArray(resultData.connections) ? resultData.connections : []); : (responseBody && Array.isArray(responseBody.connections) ? responseBody.connections : []);
/* ✅ CRITICAL FIX: Running .filter on verified guaranteed array structure only */ /* ✅ CRITICAL SEPARATION: Running filter only on verified array collections */
const onlineConnections = verifiedConnectionsArray.filter((conn: any) => conn && conn.isOnline === true); const onlineConnections = verifiedConnections.filter((conn: any) => conn && conn.isOnline === true);
setConnections(verifiedConnectionsArray); setConnections(verifiedConnections);
} catch (error) { } catch (error) {
console.error("[LiveChatModal] Error fetching connections cleanly:", error); console.error("[LiveChatModal] Runtime error fetching message feeds intercepted gracefully:", error);
setConnections([]); // Force secure array context initialization on intercepting crash exceptions setConnections([]); // Initialize to fallback empty context
} }
}; };
## 4. Verification & Quality Acceptance Criteria for AI Agent ## 3. Automated Verification Checklist for AI Agent
[ ] Dropdown Layout Verification: Open the avatar menu on the Android Simulator. The profile dropdown modal panel must lock floating positions right below the top header profile cluster, without displacing the community images ribbon or bleeding into the screen bottom. [ ] Dropdown Anchor Alignment: Confirm the avatar profile drawer panel renders right under the top-bar avatar, leaving the bottom interaction tools untouched.
[ ] WSoD Prevention Validation: Simulate an empty or broken server endpoint response (500 or blank text). Confirm that the component prints the error safely to the console tracker log while the screen continues to render placeholder empty-states beautifully without turning into a white void. [ ] White Screen Eradication: Mock an empty server exception response (500 Internal Error). Verify the client component stays functional, logs the incident, and updates the empty UI state without crashing.
[ ] Array Guard Robustness: Run a global textual scan over modified view sheets. Confirm all custom list operations on fetched database schemas verify array validity using Array.isArray() wrappers prior to calling processing filters. [ ] Global Code Polish Check: Scan the repository to ensure no raw .filter() methods are executed directly against fetched network streams without preceding type guards.
+2 -48
View File
@@ -1,4 +1,3 @@
<<<<<<< HEAD
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
@@ -22,7 +21,7 @@
<meta name="twitter:title" content="Travel Planner - Lên kế hoạch chuyến đi của bạn" /> <meta name="twitter:title" content="Travel Planner - Lên kế hoạch chuyến đi của bạn" />
<meta name="twitter:description" content="Khám phá những hành trình tuyệt vời và chia sẻ khoảnh khắc đẹp với cộng đồng du lịch." /> <meta name="twitter:description" content="Khám phá những hành trình tuyệt vời và chia sẻ khoảnh khắc đẹp với cộng đồng du lịch." />
<meta name="twitter:image" content="https://yotrip.labz.io.vn/background.avif" /> <meta name="twitter:image" content="https://yotrip.labz.io.vn/background.avif" />
<script type="module" crossorigin src="/assets/index-eQcX5WJf.js"></script> <script type="module" crossorigin src="/assets/index-H2Y9oIlO.js"></script>
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-Cyuzqnbw.js"> <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-Cyuzqnbw.js">
<link rel="modulepreload" crossorigin href="/assets/vendor-others-VIU5qAPG.js"> <link rel="modulepreload" crossorigin href="/assets/vendor-others-VIU5qAPG.js">
<link rel="modulepreload" crossorigin href="/assets/vendor-editor-DOXkNaRS.js"> <link rel="modulepreload" crossorigin href="/assets/vendor-editor-DOXkNaRS.js">
@@ -31,54 +30,9 @@
<link rel="modulepreload" crossorigin href="/assets/vendor-pdf-IjT8x9OX.js"> <link rel="modulepreload" crossorigin href="/assets/vendor-pdf-IjT8x9OX.js">
<link rel="stylesheet" crossorigin href="/assets/vendor-maps-CcXJxNtP.css"> <link rel="stylesheet" crossorigin href="/assets/vendor-maps-CcXJxNtP.css">
<link rel="stylesheet" crossorigin href="/assets/vendor-react-CuSj0z1j.css"> <link rel="stylesheet" crossorigin href="/assets/vendor-react-CuSj0z1j.css">
<link rel="stylesheet" crossorigin href="/assets/index-BsXBn1-b.css"> <link rel="stylesheet" crossorigin href="/assets/index-BO3HsKs5.css">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
</body> </body>
</body>
=======
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<script src="https://accounts.google.com/gsi/client" async defer></script>
<title>Travel Planner</title>
<!-- Open Graph Meta Tags for Social Media Sharing -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://yotrip.labz.io.vn" />
<meta property="og:title" content="Travel Planner - Lên kế hoạch chuyến đi của bạn" />
<meta property="og:description"
content="Khám phá những hành trình tuyệt vời và chia sẻ khoảnh khắc đẹp với cộng đồng du lịch." />
<meta property="og:image" content="https://yotrip.labz.io.vn/background.avif" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<!-- Twitter Card Meta Tags -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Travel Planner - Lên kế hoạch chuyến đi của bạn" />
<meta name="twitter:description"
content="Khám phá những hành trình tuyệt vời và chia sẻ khoảnh khắc đẹp với cộng đồng du lịch." />
<meta name="twitter:image" content="https://yotrip.labz.io.vn/background.avif" />
<script type="module" crossorigin src="/assets/index-BximWk33.js"></script>
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-Cyuzqnbw.js">
<link rel="modulepreload" crossorigin href="/assets/vendor-others-VIU5qAPG.js">
<link rel="modulepreload" crossorigin href="/assets/vendor-editor-DOXkNaRS.js">
<link rel="modulepreload" crossorigin href="/assets/vendor-maps-DY-S_hoR.js">
<link rel="modulepreload" crossorigin href="/assets/vendor-react-BqXL8Z-i.js">
<link rel="modulepreload" crossorigin href="/assets/vendor-pdf-IjT8x9OX.js">
<link rel="stylesheet" crossorigin href="/assets/vendor-maps-CcXJxNtP.css">
<link rel="stylesheet" crossorigin href="/assets/vendor-react-CuSj0z1j.css">
<link rel="stylesheet" crossorigin href="/assets/index-BsXBn1-b.css">
</head>
<body>
<div id="root"></div>
</body>
</html> </html>
@@ -81,14 +81,13 @@ export const MapProfileDropdown: React.FC<MapProfileDropdownProps> = ({
/> />
<div <div
className="fixed bottom-0 left-0 right-0 sm:absolute sm:bottom-auto sm:top-14 sm:right-0 sm:left-auto z-[999999] w-full sm:w-64 bg-slate-900 border-t sm:border border-slate-800 rounded-t-3xl sm:rounded-2xl p-3 sm:p-2 shadow-2xl animate-in slide-in-from-bottom sm:slide-in-from-top-2 duration-300 text-xs text-slate-200" className="absolute right-0 top-14 z-[999999] w-64 bg-slate-900 border border-slate-800 rounded-2xl p-2 shadow-2xl flex flex-col space-y-0.5 text-xs text-slate-200"
style={{ style={{
maxHeight: '80vh', maxHeight: '75vh',
overflowY: 'auto', overflowY: 'auto',
boxShadow: '0 20px 25px -5px rgba(0, 0, 0, 0.5), 0 10px 10px -5px rgba(0, 0, 0, 0.4)' boxShadow: '0 20px 25px -5px rgba(0, 0, 0, 0.6)'
}} }}
> >
<div className="w-12 h-1 bg-slate-700 rounded-full mx-auto mb-3 sm:hidden" />
{isAuthenticated ? ( {isAuthenticated ? (
<div className="flex flex-col space-y-1"> <div className="flex flex-col space-y-1">
@@ -39,10 +39,16 @@ export const FriendsManagerModal: React.FC<FriendsManagerModalProps> = ({
const res = await fetch('/api/v1/connections', { headers: getHeaders() }); const res = await fetch('/api/v1/connections', { headers: getHeaders() });
if (res.ok) { if (res.ok) {
const data = await res.json(); const data = await res.json();
setConnections(data || []); const list = Array.isArray(data)
? data
: (data && Array.isArray(data.connections) ? data.connections : []);
setConnections(list);
} else {
setConnections([]);
} }
} catch (e) { } catch (e) {
console.error('[FriendsManagerModal] Error fetching connections:', e); console.error('[FriendsManagerModal] Error fetching connections:', e);
setConnections([]);
} finally { } finally {
setIsLoading(false); setIsLoading(false);
} }
@@ -170,15 +176,19 @@ export const FriendsManagerModal: React.FC<FriendsManagerModalProps> = ({
if (!isOpen || !user) return null; if (!isOpen || !user) return null;
// Filter lists // Filter lists
const activeFriends = connections.filter((c: any) => c.status === 'ACCEPTED'); const activeFriends = Array.isArray(connections)
? connections.filter((c: any) => c && c.status === 'ACCEPTED')
: [];
// Received pending requests // Received pending requests
const pendingRequests = connections.filter((c: any) => c.status === 'PENDING' && c.targetUser?.id === user?.id); const pendingRequests = Array.isArray(connections)
? connections.filter((c: any) => c && c.status === 'PENDING' && c.targetUser?.id === user?.id)
: [];
const getStatusText = (targetUserId: string) => { const getStatusText = (targetUserId: string) => {
const existing = connections.find( const existing = Array.isArray(connections)
c => c.targetUser?.id === targetUserId || c.requester?.id === targetUserId ? connections.find(c => c && (c.targetUser?.id === targetUserId || c.requester?.id === targetUserId))
); : null;
if (!existing) return null; if (!existing) return null;
if (existing.status === 'ACCEPTED') return 'FRIEND'; if (existing.status === 'ACCEPTED') return 'FRIEND';
if (existing.status === 'PENDING') { if (existing.status === 'PENDING') {
@@ -46,11 +46,17 @@ export const LiveChatModal: React.FC<LiveChatModalProps> = ({
if (res.ok) { if (res.ok) {
const data = await res.json(); const data = await res.json();
// Accepted connections only // Accepted connections only
const activeConns = data.filter((c: any) => c.status === 'ACCEPTED'); const list = Array.isArray(data)
? data
: (data && Array.isArray(data.connections) ? data.connections : []);
const activeConns = list.filter((c: any) => c && c.status === 'ACCEPTED');
setConnections(activeConns); setConnections(activeConns);
} else {
setConnections([]);
} }
} catch (e) { } catch (e) {
console.error('[LiveChatModal] Error fetching connections:', e); console.error('[LiveChatModal] Error fetching connections:', e);
setConnections([]);
} finally { } finally {
setIsLoadingContacts(false); setIsLoadingContacts(false);
} }
@@ -236,10 +242,12 @@ export const LiveChatModal: React.FC<LiveChatModalProps> = ({
if (!isOpen || !user) return null; if (!isOpen || !user) return null;
// Filter connections by search query // Filter connections by search query
const filteredConnections = connections.filter((conn: any) => { const filteredConnections = Array.isArray(connections)
? connections.filter((conn: any) => {
const friend = conn.targetUser?.id === user?.id ? conn.requester : conn.targetUser; const friend = conn.targetUser?.id === user?.id ? conn.requester : conn.targetUser;
return friend?.name?.toLowerCase().includes(searchQuery.toLowerCase()); return friend?.name?.toLowerCase().includes(searchQuery.toLowerCase());
}); })
: [];
return ( return (
<div className="fixed inset-0 z-[1000000] bg-black/70 backdrop-blur-sm flex items-end sm:items-center justify-center p-0 sm:p-4 pointer-events-auto"> <div className="fixed inset-0 z-[1000000] bg-black/70 backdrop-blur-sm flex items-end sm:items-center justify-center p-0 sm:p-4 pointer-events-auto">
+8 -34
View File
@@ -112,7 +112,7 @@
"version": "5.22.0", "version": "5.22.0",
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.22.0.tgz", "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.22.0.tgz",
"integrity": "sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==", "integrity": "sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==",
"devOptional": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
@@ -126,14 +126,14 @@
"version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", "version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2",
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2.tgz", "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2.tgz",
"integrity": "sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==", "integrity": "sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==",
"devOptional": true, "dev": true,
"license": "Apache-2.0" "license": "Apache-2.0"
}, },
"backend/node_modules/@prisma/fetch-engine": { "backend/node_modules/@prisma/fetch-engine": {
"version": "5.22.0", "version": "5.22.0",
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.22.0.tgz", "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.22.0.tgz",
"integrity": "sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==", "integrity": "sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==",
"devOptional": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@prisma/debug": "5.22.0", "@prisma/debug": "5.22.0",
@@ -145,7 +145,7 @@
"version": "5.22.0", "version": "5.22.0",
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.22.0.tgz", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.22.0.tgz",
"integrity": "sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==", "integrity": "sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==",
"devOptional": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@prisma/debug": "5.22.0" "@prisma/debug": "5.22.0"
@@ -174,7 +174,7 @@
"version": "5.22.0", "version": "5.22.0",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-5.22.0.tgz", "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.22.0.tgz",
"integrity": "sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==", "integrity": "sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==",
"devOptional": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
@@ -1120,7 +1120,6 @@
"os": [ "os": [
"aix" "aix"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1138,7 +1137,6 @@
"os": [ "os": [
"android" "android"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1156,7 +1154,6 @@
"os": [ "os": [
"android" "android"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1174,7 +1171,6 @@
"os": [ "os": [
"android" "android"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1192,7 +1188,6 @@
"os": [ "os": [
"darwin" "darwin"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1210,7 +1205,6 @@
"os": [ "os": [
"darwin" "darwin"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1228,7 +1222,6 @@
"os": [ "os": [
"freebsd" "freebsd"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1246,7 +1239,6 @@
"os": [ "os": [
"freebsd" "freebsd"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1264,7 +1256,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1282,7 +1273,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1300,7 +1290,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1318,7 +1307,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1336,7 +1324,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1354,7 +1341,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1372,7 +1358,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1390,7 +1375,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1408,7 +1392,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1426,7 +1409,6 @@
"os": [ "os": [
"netbsd" "netbsd"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1444,7 +1426,6 @@
"os": [ "os": [
"netbsd" "netbsd"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1462,7 +1443,6 @@
"os": [ "os": [
"openbsd" "openbsd"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1480,7 +1460,6 @@
"os": [ "os": [
"openbsd" "openbsd"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1498,7 +1477,6 @@
"os": [ "os": [
"openharmony" "openharmony"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1516,7 +1494,6 @@
"os": [ "os": [
"sunos" "sunos"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1534,7 +1511,6 @@
"os": [ "os": [
"win32" "win32"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1552,7 +1528,6 @@
"os": [ "os": [
"win32" "win32"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -1570,7 +1545,6 @@
"os": [ "os": [
"win32" "win32"
], ],
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -4081,7 +4055,7 @@
"version": "15.7.15", "version": "15.7.15",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
"integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
"devOptional": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/quill": { "node_modules/@types/quill": {
@@ -4104,7 +4078,7 @@
"version": "18.3.31", "version": "18.3.31",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz",
"integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==", "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==",
"devOptional": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/prop-types": "*", "@types/prop-types": "*",
@@ -5955,7 +5929,7 @@
"version": "3.2.3", "version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"devOptional": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/dargs": { "node_modules/dargs": {