diff --git a/Components/admin/Owners/DashboardPage.tsx b/Components/admin/Owners/DashboardPage.tsx index fa8103f1..367eb8de 100644 --- a/Components/admin/Owners/DashboardPage.tsx +++ b/Components/admin/Owners/DashboardPage.tsx @@ -82,8 +82,9 @@ interface Booking { interface Review { id: string; booking_id: string; - rating: number; - comment: string; + rating?: number; + overall_rating?: number; + comment?: string; created_at: string; booking?: { room_name: string; @@ -135,7 +136,8 @@ const DashboardPage = ({ const [refreshing, setRefreshing] = useState(false); // Fetch real data from APIs - const { data: bookingsData, isLoading: bookingsLoading, refetch: refetchBookings } = useGetRoomBookingsQuery(); + const havenIdFromProps = havens && havens.length > 0 ? havens[0].uuid_id : undefined; + const { data: bookingsData, isLoading: bookingsLoading, refetch: refetchBookings } = useGetRoomBookingsQuery(havenIdFromProps, { skip: !havenIdFromProps }); const { data: paymentsData, isLoading: paymentsLoading, refetch: refetchPayments } = useGetBookingPaymentsQuery(); const { data: reviewsData, isLoading: reviewsLoading, refetch: refetchReviews } = useGetReviewsQuery(); @@ -154,7 +156,7 @@ const DashboardPage = ({ // Calculate owner-specific metrics const bookings: Booking[] = bookingsData?.data || []; - const payments = paymentsData?.data || []; + const payments = paymentsData || []; const reviews: Review[] = reviewsData?.data || []; // Calculate revenue from approved payments diff --git a/Components/admin/Owners/GuestAssistancePage.tsx b/Components/admin/Owners/GuestAssistancePage.tsx index 99b2210a..76606fc4 100644 --- a/Components/admin/Owners/GuestAssistancePage.tsx +++ b/Components/admin/Owners/GuestAssistancePage.tsx @@ -1,7 +1,7 @@ "use client"; -import { useState, useMemo } from "react"; +import { useState, useMemo, useEffect} from "react"; import { Calendar, Search, @@ -31,7 +31,7 @@ import { import toast from "react-hot-toast"; interface Booking { - id: number; + id: string; bookingRef: string; guestName: string; email: string; @@ -41,10 +41,10 @@ interface Booking { checkOut: string; guests: number; amount: number; - rateType: string; - status: "pending" | "approved" | "declined"; + rateType?: string; + status: "pending" | "approved" | "declined" | "rejected" | "confirmed" | "checked-in" | "completed" | "cancelled"; createdAt: string; - message?: string; + message?: string | null; } const GuestAssistancePage = () => { @@ -57,93 +57,120 @@ const GuestAssistancePage = () => { const [selectedBooking, setSelectedBooking] = useState(null); const [isViewModalOpen, setIsViewModalOpen] = useState(false); - // Sample booking data (unchanged) - const [bookings, setBookings] = useState([ - { - id: 1, - bookingRef: "BK-2025-001", - guestName: "Juan Dela Cruz", - email: "juan@email.com", - phone: "+63 912 345 6789", - havenName: "Haven 1", - checkIn: "2025-01-15", - checkOut: "2025-01-16", - guests: 4, - amount: 1999, - rateType: "21H Weekend", - status: "pending", - createdAt: "2025-01-10 10:30 AM", - message: "Hi, planning a family gathering. Need early check-in if possible.", - }, - { - id: 2, - bookingRef: "BK-2025-002", - guestName: "Maria Santos", - email: "maria@email.com", - phone: "+63 917 234 5678", - havenName: "Haven 3", - checkIn: "2025-01-20", - checkOut: "2025-01-21", - guests: 2, - amount: 2500, - rateType: "21H Weekday", - status: "pending", - createdAt: "2025-01-10 11:45 AM", - }, - { - id: 3, - bookingRef: "BK-2025-003", - guestName: "Carlos Reyes", - email: "carlos@email.com", - phone: "+63 920 345 6789", - havenName: "Haven 2", - checkIn: "2025-01-18", - checkOut: "2025-01-18", - guests: 6, - amount: 1800, - rateType: "10H", - status: "approved", - createdAt: "2025-01-09 3:20 PM", - }, - { - id: 4, - bookingRef: "BK-2025-004", - guestName: "Anna Martinez", - email: "anna@email.com", - phone: "+63 918 456 7890", - havenName: "Haven 4", - checkIn: "2025-01-22", - checkOut: "2025-01-22", - guests: 3, - amount: 999, - rateType: "6H", - status: "declined", - createdAt: "2025-01-09 1:10 PM", - message: "Sorry, need to cancel due to schedule conflict.", - }, - { - id: 5, - bookingRef: "BK-2025-005", - guestName: "Pedro Garcia", - email: "pedro@email.com", - phone: "+63 915 567 8901", - havenName: "Haven 5", - checkIn: "2025-01-25", - checkOut: "2025-01-26", - guests: 5, - amount: 2100, - rateType: "21H Weekend", - status: "pending", - createdAt: "2025-01-10 2:15 PM", - }, - ]); - - const handleApprove = (id: number) => { - setBookings(bookings.map((b) => (b.id === id ? { ...b, status: "approved" } : b))); + const [bookings, setBookings] = useState([]); +const [loading, setLoading] = useState(true); + +useEffect(() => { + fetchBookings(); +}, []); + +const fetchBookings = async () => { + try { + // Request only the booking table columns from the backend (Neon) + const res = await fetch("/api/bookings?raw=true"); + const json = await res.json(); + const rows = Array.isArray(json?.data) ? json.data : []; + + const mapped: Booking[] = rows.map((r: any) => { + const guestsCount = Number(r.adults || 0) + Number(r.children || 0) + Number(r.infants || 0); + const createdAt = r.created_at ? new Date(r.created_at).toLocaleString() : ""; + + return { + id: String(r.id), + bookingRef: String(r.booking_id || r.id), + guestName: "", // guest info not stored directly on booking table + email: "", + phone: "", + havenName: r.room_name || "", + checkIn: r.check_in_date || "", + checkOut: r.check_out_date || "", + guests: guestsCount, + amount: 0, + rateType: undefined, + status: (r.status as Booking["status"]) || "pending", + createdAt, + message: r.rejection_reason || null, + }; + }); + + setBookings(mapped); + // Fetch main guest info for each booking and merge into list + try { + await Promise.all( + mapped.map(async (b) => { + try { + const res = await fetch(`/api/bookings/${b.id}`); + if (!res.ok) return; + const json = await res.json(); + const data = json?.data; + if (!data) return; + const mainGuest = data.main_guest || (Array.isArray(data.guests) && data.guests[0]) || null; + const guestName = mainGuest ? `${mainGuest.firstName || mainGuest.first_name || ""} ${mainGuest.lastName || mainGuest.last_name || ""}`.trim() : ""; + const email = mainGuest?.email || data.guest_email || ""; + const phone = mainGuest?.phone || data.guest_phone || ""; + const amountVal = Number( + data.total_amount ?? + data.totalAmount ?? + data.booking_payment?.total_amount ?? + (data.booking_payment && data.booking_payment.total_amount) ?? + 0, + ); + + setBookings((prev) => prev.map((p) => (p.id === b.id ? { ...p, guestName: guestName || p.guestName, email: email || p.email, phone: phone || p.phone, amount: amountVal || p.amount } : p))); + } catch (e) { + // ignore per-booking failure + } + }), + ); + } catch (e) { + // ignore + } + } catch (error) { + console.error("Failed to fetch bookings:", error); + } finally { + setLoading(false); + } +}; + + + const handleApprove = (id: string) => { + // optimistic UI update handled in async function below + void (async () => { + try { + // send update to backend + const body = { id: id, status: "approved" }; + const res = await fetch(`/api/bookings`, { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(body), + }); + if (!res.ok) throw new Error("Failed to update booking"); + setBookings((prev) => prev.map((b) => (b.id === id ? { ...b, status: "approved" } : b))); + toast.success("Booking approved"); + } catch (err) { + console.error(err); + toast.error("Failed to approve booking"); + } + })(); }; - const handleDecline = (id: number) => { - setBookings(bookings.map((b) => (b.id === id ? { ...b, status: "declined" } : b))); + const handleDecline = (id: string) => { + void (async () => { + try { + const body = { id: id, status: "rejected", rejection_reason: "Declined by owner" }; + const res = await fetch(`/api/bookings`, { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(body), + }); + if (!res.ok) throw new Error("Failed to update booking"); + setBookings((prev) => prev.map((b) => (b.id === id ? { ...b, status: "rejected", message: "Declined by owner" } : b))); + toast.error("Booking declined"); + } catch (err) { + console.error(err); + toast.error("Failed to decline booking"); + } + })(); }; const getStatusColor = (status: string) => { @@ -160,27 +187,36 @@ const GuestAssistancePage = () => { }; // Stats - const stats = useMemo(() => { - return { - total: bookings.length, - pending: bookings.filter(b => b.status === "pending").length, - approved: bookings.filter(b => b.status === "approved").length, - declined: bookings.filter(b => b.status === "declined").length, - }; - }, [bookings]); - - // Filter +const stats = useMemo(() => { + if (!Array.isArray(bookings)) { + return { total: 0, pending: 0, approved: 0, declined: 0 }; + } + + return { + total: bookings.length, + pending: bookings.filter(b => b.status === "pending").length, + approved: bookings.filter(b => b.status === "approved").length, + declined: bookings.filter(b => b.status === "declined" || b.status === "rejected").length, + }; +}, [bookings]); + + const filteredBookings = useMemo(() => { - return bookings.filter((booking) => { - const matchesSearch = - booking.guestName.toLowerCase().includes(searchTerm.toLowerCase()) || - booking.bookingRef.toLowerCase().includes(searchTerm.toLowerCase()) || - booking.havenName.toLowerCase().includes(searchTerm.toLowerCase()); - - const matchesFilter = filterStatus === "all" || booking.status === filterStatus; - return matchesSearch && matchesFilter; - }); - }, [bookings, searchTerm, filterStatus]); + if (!Array.isArray(bookings)) return []; + + return bookings.filter((booking) => { + const matchesSearch = + (booking.guestName || "").toLowerCase().includes(searchTerm.toLowerCase()) || + booking.bookingRef.toLowerCase().includes(searchTerm.toLowerCase()) || + (booking.havenName || "").toLowerCase().includes(searchTerm.toLowerCase()); + + const normalizedFilter = filterStatus === "declined" ? "rejected" : filterStatus; + const matchesStatus = normalizedFilter === "all" ? true : booking.status === normalizedFilter; + + return matchesSearch && matchesStatus; + }); +}, [bookings, searchTerm]); + // Sort const sortedBookings = useMemo(() => { @@ -238,9 +274,48 @@ const GuestAssistancePage = () => { } }; - const handleViewBooking = (booking: Booking) => { - setSelectedBooking(booking); - setIsViewModalOpen(true); + const handleViewBooking = async (booking: Booking) => { + // Fetch full booking details (includes guests/payments) for the modal + try { + const res = await fetch(`/api/bookings/${booking.id}`); + if (!res.ok) { + setSelectedBooking(booking); + setIsViewModalOpen(true); + return; + } + const json = await res.json(); + const data = json?.data || {}; + + const mainGuest = (data.main_guest || (data.guests && data.guests[0]) || {}); + const guestName = [mainGuest.firstName || data.guest_first_name, mainGuest.lastName || data.guest_last_name].filter(Boolean).join(" ") || "Guest"; + const email = mainGuest.email || data.guest_email || ""; + const phone = mainGuest.phone || data.guest_phone || ""; + const amount = Number(data.total_amount ?? (data.booking_payment && data.booking_payment.total_amount) ?? 0); + + const detailed: Booking = { + id: String(data.id || booking.id), + bookingRef: String(data.booking_id || booking.bookingRef), + guestName, + email, + phone, + havenName: data.room_name || booking.havenName || "", + checkIn: data.check_in_date || booking.checkIn, + checkOut: data.check_out_date || booking.checkOut, + guests: Number(data.adults || 0) + Number(data.children || 0) + Number(data.infants || 0), + amount, + rateType: data.room_rate ? String(data.room_rate) : booking.rateType, + status: (data.status as Booking["status"]) || booking.status, + createdAt: data.created_at ? new Date(data.created_at).toLocaleString() : booking.createdAt, + message: data.rejection_reason || booking.message || null, + }; + + setSelectedBooking(detailed); + setIsViewModalOpen(true); + } catch (error) { + console.error("Failed to fetch booking details:", error); + setSelectedBooking(booking); + setIsViewModalOpen(true); + } }; const handleCloseModal = () => { diff --git a/app/api/reviews/all/route.ts b/app/api/reviews/all/route.ts new file mode 100644 index 00000000..7c502e39 --- /dev/null +++ b/app/api/reviews/all/route.ts @@ -0,0 +1,7 @@ +import { NextRequest, NextResponse } from "next/server"; +import { GET as reviewsGET } from "../route"; + +// Forward /api/reviews/all to the main reviews GET handler +export async function GET(req: NextRequest) { + return reviewsGET(req as NextRequest); +} diff --git a/backend/controller/bookingController.ts b/backend/controller/bookingController.ts index e5a41321..ec8f4020 100644 --- a/backend/controller/bookingController.ts +++ b/backend/controller/bookingController.ts @@ -749,7 +749,48 @@ export const getAllBookings = async ( try { const { searchParams } = new URL(req.url); const status = searchParams.get("status"); + const raw = searchParams.get("raw"); + // If raw=true, return only the booking table columns (no joins) + if (raw === "true") { + let rawQuery = ` + SELECT + id, + booking_id, + user_id, + room_name, + check_in_date, + check_out_date, + check_in_time, + check_out_time, + adults, + children, + infants, + status, + rejection_reason, + has_security_deposit, + created_at, + updated_at + FROM booking + `; + + const values: any[] = []; + if (status) { + rawQuery += " WHERE status = $1"; + values.push(status); + } + + rawQuery += " ORDER BY created_at DESC"; + + const result = await pool.query(rawQuery, values); + return NextResponse.json({ + success: true, + data: result.rows, + count: result.rows.length, + }); + } + + // Default behavior: enriched booking data with joins let query = ` SELECT b.*, diff --git a/backend/migrations/2026-02-11-add-source-to-booking.sql b/backend/migrations/2026-02-11-add-source-to-booking.sql new file mode 100644 index 00000000..ab574294 --- /dev/null +++ b/backend/migrations/2026-02-11-add-source-to-booking.sql @@ -0,0 +1,15 @@ +-- Migration: Add `source` column to booking table +-- Adds optional `source` column with default 'online_checkout' +-- Run this on your PostgreSQL database (test/staging first) + +BEGIN; + +ALTER TABLE booking + ADD COLUMN IF NOT EXISTS source VARCHAR(50) DEFAULT 'online_checkout'; + +COMMIT; + +-- Optional: verify +SELECT column_name, column_default +FROM information_schema.columns +WHERE table_name = 'booking' AND column_name = 'source'; diff --git a/package-lock.json b/package-lock.json index 1fa46fb5..627b6828 100644 --- a/package-lock.json +++ b/package-lock.json @@ -866,6 +866,7 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -1800,6 +1801,7 @@ "resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.20.tgz", "integrity": "sha512-1cukXLlePFiJ8YKXn/4tMKsy0etxYLCkXk8nUCFi11nRONF2Ba2CD5b21/ovtOO2tL6afTJfwmc1ed3HG7eB1g==", "license": "MIT", + "peer": true, "dependencies": { "preact": "~10.12.1" } @@ -3835,6 +3837,7 @@ "resolved": "https://registry.npmjs.org/@nextui-org/system/-/system-2.4.6.tgz", "integrity": "sha512-6ujAriBZMfQ16n6M6Ad9g32KJUa1CzqIVaHN/tymadr/3m8hrr7xDw6z50pVjpCRq2PaaA1hT8Hx7EFU3f2z3Q==", "license": "MIT", + "peer": true, "dependencies": { "@internationalized/date": "3.6.0", "@nextui-org/react-utils": "2.1.3", @@ -3929,6 +3932,7 @@ "resolved": "https://registry.npmjs.org/@nextui-org/theme/-/theme-2.4.5.tgz", "integrity": "sha512-c7Y17n+hBGiFedxMKfg7Qyv93iY5MteamLXV4Po4c1VF1qZJI6I+IKULFh3FxPWzAoz96r6NdYT7OLFjrAJdWg==", "license": "MIT", + "peer": true, "dependencies": { "@nextui-org/shared-utils": "2.1.2", "clsx": "^1.2.1", @@ -7040,6 +7044,7 @@ "integrity": "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -7112,6 +7117,7 @@ "integrity": "sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.53.1", "@typescript-eslint/types": "8.53.1", @@ -7611,6 +7617,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -8255,6 +8262,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -8779,7 +8787,8 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/d3-array": { "version": "3.2.4", @@ -9545,6 +9554,7 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -9730,6 +9740,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -10303,6 +10314,7 @@ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.29.0.tgz", "integrity": "sha512-1gEFGXHYV2BD42ZPTFmSU9buehppU+bCuOnHU0AD18DKh9j4DuTx47MvqY5ax+NNWRtK32qIcJf1UxKo1WwjWg==", "license": "MIT", + "peer": true, "dependencies": { "motion-dom": "^12.29.0", "motion-utils": "^12.27.2", @@ -11488,6 +11500,7 @@ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "license": "MIT", + "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -11582,6 +11595,7 @@ "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-4.1.0.tgz", "integrity": "sha512-xd1d/XRkwqnsq6FP3zH1Q+Ejqn2ULIJeDZ+FTKpaabVpZREjsJKRJwuokTNgdqOU+fl55KgbvgZ1pRTSWCP2kQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.28.4", "fast-png": "^6.2.0", @@ -11776,7 +11790,8 @@ "version": "1.9.4", "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", - "license": "BSD-2-Clause" + "license": "BSD-2-Clause", + "peer": true }, "node_modules/levn": { "version": "0.4.1", @@ -12333,6 +12348,7 @@ "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.12.tgz", "integrity": "sha512-H+rnK5bX2Pi/6ms3sN4/jRQvYSMltV6vqup/0SFOrxYYY/qoNvhXPlYq3e+Pm9RFJRwrMGbMIwi81M4dxpomhA==", "license": "MIT-0", + "peer": true, "engines": { "node": ">=6.0.0" } @@ -12714,6 +12730,7 @@ "resolved": "https://registry.npmjs.org/pg/-/pg-8.17.2.tgz", "integrity": "sha512-vjbKdiBJRqzcYw1fNU5KuHyYvdJ1qpcQg1CeBrHFqV1pWgHeVR6j/+kX0E1AAXfyuLUGY1ICrN2ELKA/z2HWzw==", "license": "MIT", + "peer": true, "dependencies": { "pg-connection-string": "^2.10.1", "pg-pool": "^3.11.0", @@ -12872,6 +12889,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -13059,6 +13077,7 @@ "resolved": "https://registry.npmjs.org/preact/-/preact-10.28.2.tgz", "integrity": "sha512-lbteaWGzGHdlIuiJ0l2Jq454m6kcpI1zNje6d8MlGAFlYvP2GO4ibnat7P74Esfz4sPTdM6UxtTwh/d3pwM9JA==", "license": "MIT", + "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" @@ -13193,6 +13212,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -13202,6 +13222,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -13251,6 +13272,7 @@ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", "license": "MIT", + "peer": true, "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" @@ -13404,7 +13426,8 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/redux-persist": { "version": "6.0.0", @@ -14587,6 +14610,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -14681,6 +14705,7 @@ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" @@ -14792,6 +14817,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -15421,6 +15447,7 @@ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" }