From a0c95b05bc58531339d7c3f4d8593929ab868b15 Mon Sep 17 00:00:00 2001 From: JaiPandey425 Date: Fri, 28 Nov 2025 19:41:06 +0530 Subject: [PATCH 01/10] payslip and shedules --- src/electron/ipc/ipcHandler.ts | 82 ++++++-- src/electron/preload.cts | 3 + src/ui/App.tsx | 2 + src/ui/layout/DashboardLayout.tsx | 6 +- .../LeaveAndAdjustments/tabs/PtoLeaveTab.tsx | 2 +- src/ui/pages/PaySlips/PaysSlips.tsx | 131 +++++++++++++ src/ui/pages/Schedule/Schedule.tsx | 185 +++++++++++------- types.d.ts | 40 +++- 8 files changed, 363 insertions(+), 88 deletions(-) create mode 100644 src/ui/pages/PaySlips/PaysSlips.tsx diff --git a/src/electron/ipc/ipcHandler.ts b/src/electron/ipc/ipcHandler.ts index e496bff..cd9b1f9 100644 --- a/src/electron/ipc/ipcHandler.ts +++ b/src/electron/ipc/ipcHandler.ts @@ -183,19 +183,6 @@ ipcMain.handle("tracking-tabs-opening-and-closing", async (event, payload) => { }); -// ipcMain.handle("get-project-logs", async (event, projectID) => { -// try { -// console.log("get-project-logs",projectID) -// const response = await api.get(`/report/project/${projectID}`); -// console.log("response-logs 164" , response) - -// return response; -// } catch (error) { -// console.error("Error fetching project logs:", error); -// throw error; -// } -// }); - ipcMain.handle("get-project-logs", async (event, { projectID, fromDate, toDate }) => { try { @@ -378,6 +365,75 @@ ipcMain.handle("create-leave-request", async (event, payload) => { +ipcMain.handle("get-schedules", async () => { + try { + console.log(" Fetching schedules..."); + + const response = await api.get("/payrollSchedule"); // Backend API call + + console.log("✅ Schedule Data:", response.data); + + return { + success: true, + result: response.data + }; + + } catch (error: any) { + console.error("❌ Fetch Schedules Failed:"); + + let message = "Unknown error occurred"; + + if (error.response) { + console.log("Status:", error.response.status); + console.log("Message:", error.response.statusText); + console.log("Server Response:", error.response.data); + + message = error.response.data?.message || error.response.statusText; + } else { + message = error.message; + } + + return { + success: false, + message + }; + } +}); + + +ipcMain.handle("get-payslips", async (event, vaId: string) => { + console.log("get-payslips",vaId) + try { + const response = await api.get(`/paySlip/${vaId}`); + + + return { + success: true, + data: response.data?.data || [], + }; + } catch (error: any) { + let message = "Unknown error occurred"; + + if (error.response) { + console.log("Status:", error.response.status); + console.log("Message:", error.response.statusText); + console.log("Server Response:", error.response.data); + + message = error.response.data?.message || error.response.statusText; + } else { + message = error.message; + } + + return { + success: false, + message + }; + } +}); + + + + export const sendTokenExpired = () => { diff --git a/src/electron/preload.cts b/src/electron/preload.cts index 7a4c939..34c9b41 100644 --- a/src/electron/preload.cts +++ b/src/electron/preload.cts @@ -53,6 +53,9 @@ contextBridge.exposeInMainWorld('electron', { createPTOAndSickLeaveRequest: (data) => ipcRenderer.invoke("create-leave-request", data), +getSchedules: () => ipcRenderer.invoke("get-schedules"), + +getPayslips: (vaId: string) => ipcRenderer.invoke("get-payslips", vaId), } satisfies Window['electron']); diff --git a/src/ui/App.tsx b/src/ui/App.tsx index 72f3ab8..7b59fca 100644 --- a/src/ui/App.tsx +++ b/src/ui/App.tsx @@ -95,6 +95,7 @@ import Settings from "./pages/Settings/Settings"; import HelpSupport from "./pages/HelpSupport/HelpSupport"; import Vpn from "./pages/Vpn/Vpn"; import Schedule from "./pages/Schedule/Schedule"; +import PaysSlips from "./pages/PaySlips/PaysSlips"; const App: React.FC = () => { @@ -142,6 +143,7 @@ const App: React.FC = () => { } /> } /> } /> + } /> {/* Default Redirect */} diff --git a/src/ui/layout/DashboardLayout.tsx b/src/ui/layout/DashboardLayout.tsx index 4eef5d3..99f7bbf 100644 --- a/src/ui/layout/DashboardLayout.tsx +++ b/src/ui/layout/DashboardLayout.tsx @@ -27,6 +27,7 @@ import { ChevronUp, GlobeLock, ClipboardClock, + CircleDollarSign, } from "lucide-react"; import { useActiveAppTracker } from "../hooks/useActiveAppTracker"; import { useDispatch, useSelector } from "react-redux"; @@ -113,6 +114,7 @@ const DashboardLayout = () => { { label: "Timesheet", path: "/dashboard/timesheet", icon: }, { label: "Leave & Adjust", path: "/dashboard/leave-adjustments", icon: }, { label: "Schedule", path: "/dashboard/schedule", icon: }, + { label: "Pays Slips", path: "/dashboard/pays-slips", icon: }, { label: "Settings", path: "/dashboard/settings", icon: }, { label: "VPN", path: "/dashboard/vpn", icon: }, { label: "Help & Support", path: "/dashboard/help-support", icon: }, @@ -297,7 +299,7 @@ const DashboardLayout = () => { ); console.log("Alert Message 2") // Stop tracking - await handleStopTracking(); + // await handleStopTracking(); }, [selectedProject, handleStopTracking]); // Initialize useActiveAppTracker and store finalizeTracking in ref @@ -743,7 +745,7 @@ const DashboardLayout = () => { ))} -
+
+ {errors.comment &&

{errors.comment}

}
{serverError && ( diff --git a/src/ui/pages/PaySlips/PaysSlips.tsx b/src/ui/pages/PaySlips/PaysSlips.tsx new file mode 100644 index 0000000..b54e3c9 --- /dev/null +++ b/src/ui/pages/PaySlips/PaysSlips.tsx @@ -0,0 +1,131 @@ +import { useState, useEffect } from "react"; +import { useSelector } from "react-redux"; +import { RootState } from "../../store"; + +const PaysSlips = () => { + const { projectSummary } = + useSelector((state: any) => state.projects); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [rows, setRows] = useState([]); + + useEffect(() => { + if (!projectSummary?.vaId) return; + + fetchPayslips(projectSummary?.vaId); + +}, [projectSummary?.vaId]); + + const fetchPayslips = async (vaId: string) => { + setLoading(true); + setError(null); + + try { + console.log("getPayslips" , vaId) + const result = await window.electron.getPayslips(vaId); + + if (!result.success) { + setError(result.message || "Failed to load payslips"); + return; + } + + const response = result.data; + + if (!response || response.length === 0) { + setRows([]); + return; + } + + // Extract payrolls array from first index + const payrolls = response[0]?.payrolls || []; + + setRows(payrolls); + } catch (err: any) { + setError("An unexpected error occurred"); + } finally { + setLoading(false); + } + }; + + return ( + <> +
+
+

Payslips

+
+ +
+ + {loading && ( +
+ +
+ )} + + {error && ( +
{error}
+ )} + + {!loading && !error && rows.length === 0 && ( +
+ No payslip data available. +
+ )} + + {!loading && !error && rows.length > 0 && ( +
+ + + + + + + + + + + + + + {rows.map((row, index) => ( + + + + + + + + + + + + + + ))} + +
#StatusDateTotal HoursTotal PayAction
{index + 1} + + {row.status} + + {row.payrollSchedule}{row.totalHoursFormatted}${row.totalPayAmount.toFixed(2)} + +
+
+ )} +
+
+ + ); +}; + +export default PaysSlips; diff --git a/src/ui/pages/Schedule/Schedule.tsx b/src/ui/pages/Schedule/Schedule.tsx index 36fdcfb..ca8ae10 100644 --- a/src/ui/pages/Schedule/Schedule.tsx +++ b/src/ui/pages/Schedule/Schedule.tsx @@ -1,70 +1,115 @@ +import React, { useEffect, useState } from "react"; +import { DateTime } from "luxon"; + +interface ScheduleItem { + id: string; + isActive: boolean; + status: string | null; + ScheduleDate: string; + cutOffTime: string | null; + periodEnd: string | null; + periodStart: string | null; +} + const Schedule = () => { - const rows = [ - { - period: "Oct 25, 2025 to Nov 07, 2025", - cutoff: "Nov 11, 2025 05:29:59 AM", - payDate: "Nov 14, 2025" - }, - { - period: "Nov 08, 2025 to Nov 21, 2025", - cutoff: "Nov 25, 2025 05:29:59 AM", - payDate: "Nov 28, 2025" - }, - { - period: "Nov 22, 2025 to Dec 05, 2025", - cutoff: "Dec 09, 2025 05:29:59 AM", - payDate: "Dec 12, 2025" - }, - { - period: "Dec 06, 2025 to Dec 19, 2025", - cutoff: "Dec 23, 2025 05:29:59 AM", - payDate: "Dec 26, 2025" - }, - { - period: "Dec 20, 2025 to Jan 02, 2026", - cutoff: "Jan 06, 2026 05:29:59 AM", - payDate: "Jan 09, 2026" - }, - { - period: "Jan 03, 2026 to Jan 16, 2026", - cutoff: "Jan 20, 2026 05:29:59 AM", - payDate: "Jan 23, 2026" - }, - { - period: "Jan 17, 2026 to Jan 30, 2026", - cutoff: "Feb 03, 2026 05:29:59 AM", - payDate: "Feb 06, 2026" - }, - { - period: "Jan 31, 2026 to Feb 13, 2026", - cutoff: "Feb 17, 2026 05:29:59 AM", - payDate: "Feb 20, 2026" - }, - { - period: "Feb 14, 2026 to Feb 27, 2026", - cutoff: "Mar 03, 2026 05:29:59 AM", - payDate: "Mar 06, 2026" - }, - { - period: "Feb 28, 2026 to Mar 13, 2026", - cutoff: "Mar 17, 2026 05:29:59 AM", - payDate: "Mar 20, 2026" - } - ]; + const [rows, setRows] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + // Format helpers using Luxon + const formatDateTime = (input: string | null) => { + if (!input) return "-"; + return DateTime.fromISO(input) + .setZone("local") + .toLocaleString(DateTime.DATETIME_MED); + }; + + const formatOnlyDate = (input: string | null) => { + if (!input) return "-"; + return DateTime.fromISO(input) + .setZone("local") + .toLocaleString(DateTime.DATE_MED); + }; + + // Fetch schedules + useEffect(() => { + const loadSchedules = async () => { + try { + const res = await window.electron.getSchedules(); + console.log(res.result); + + if (!res.success) { + setError(res.message || "Failed to load schedules"); + return; + } + + const list = res.result || []; // Use data array + setRows(list); + } catch (err: any) { + setError(err.message || "Unexpected error"); + } finally { + setLoading(false); + } + }; + + loadSchedules(); + }, []); + + // Determine if current date is within the schedule period + const isActiveRow = (row: ScheduleItem) => { + if (!row.periodStart || !row.periodEnd) return false; + + const now = DateTime.local(); + const start = DateTime.fromISO(row.periodStart).startOf("day"); + const end = DateTime.fromISO(row.periodEnd).endOf("day"); + + return now >= start && now <= end; + }; + + // Get Active Schedule (only one) + const activeSchedule = rows.find((r) => isActiveRow(r)); return ( - <> -
- {/* Header */} -
-

Schedules

-
+
+ {/* Header */} +
+

Schedules

+ {activeSchedule && ( +
+

+ Pay Date: + {formatOnlyDate(activeSchedule.ScheduleDate)} + +

+
+ )} +
-
+
+ {/* Loader */} + {loading && ( +
+ +
+ )} - {/* ⭐ FIXED: Horizontal scroll wrapper */} + {/* Error */} + {!loading && error && ( +
❌ {error}
+ )} + + {/* No Data */} + {!loading && !error && rows.length === 0 && ( +
No schedules found.
+ )} + + + + + {/* Table */} + {!loading && !error && rows.length > 0 && (
- +
@@ -76,24 +121,24 @@ const Schedule = () => { {rows.map((row, index) => ( - + - - + + ))}
#
{index + 1}{row.period} - {row.cutoff} -
- (Asia/Calcutta) + {formatOnlyDate(row.periodStart)} → {formatOnlyDate(row.periodEnd)}
{row.payDate}{formatDateTime(row.cutOffTime)}{formatOnlyDate(row.ScheduleDate)}
- -
+ )}
- +
); }; diff --git a/types.d.ts b/types.d.ts index 1ec1280..bb7af91 100644 --- a/types.d.ts +++ b/types.d.ts @@ -92,6 +92,36 @@ type LeaveRequestResponse = { message?: string; }; +interface ScheduleItem { + id: string; + isActive: boolean; + status: string | null; + ScheduleDate: string; + cutOffTime: string | null; + periodEnd: string | null; + periodStart: string | null; +} + + +interface PayslipItem { + totalHoursFormatted: string; + payrollSchedule: string; + totalPayAmount: number; + status: string; + date: string | null; + createdDate: string; + name: string; + id: string; +} + +interface PayslipResponse { + payrolls: PayslipItem[]; + vaPortalId: string; + vaName: string; + id: string; +} + + interface Window { electron: { login: (credentials: { email: string; password: string }) => Promise; @@ -174,9 +204,15 @@ interface Window { payload: LeaveRequestPayload ) => Promise; - + getSchedules: () => Promise; + + getPayslips: (vaId: string) => Promise<{ + success: boolean; + data?: PayslipResponse[]; + message?: string; + }>; }; - + } From d23933c517678bbc31d282563df5a2782eae82eb Mon Sep 17 00:00:00 2001 From: BhagwanSingh Date: Sun, 30 Nov 2025 11:39:41 +0530 Subject: [PATCH 02/10] 30-11 --- package-lock.json | 1763 ------------------------ package.json | 2 - src/electron/api/axiosClient.ts | 2 +- src/electron/utils/utilityFunctions.ts | 2 +- src/ui/layout/DashboardLayout.tsx | 2 +- 5 files changed, 3 insertions(+), 1768 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2165f01..5cd7899 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,11 +18,9 @@ "luxon": "^3.7.2", "os-utils": "^0.0.14", "quill": "^2.0.3", - "quill": "^2.0.3", "react": "^18.3.1", "react-dom": "^18.3.1", "react-quill-new": "^3.6.0", - "react-quill-new": "^3.6.0", "react-redux": "^9.2.0", "react-router-dom": "^7.9.5", "recharts": "^2.12.7", @@ -770,358 +768,6 @@ "tslib": "^2.4.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@esbuild/win32-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", @@ -1155,443 +801,6 @@ "node": ">=18" } }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", - "cpu": [ - "ppc64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", - "cpu": [ - "riscv64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", - "cpu": [ - "s390x" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", - "cpu": [ - "ppc64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", - "cpu": [ - "riscv64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", - "cpu": [ - "s390x" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.7.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, "node_modules/@img/sharp-win32-x64": { "version": "0.34.5", "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", @@ -2006,7 +1215,6 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, "license": "MIT", "optional": true, "engines": { @@ -2062,266 +1270,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.2.tgz", - "integrity": "sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.2.tgz", - "integrity": "sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.2.tgz", - "integrity": "sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.2.tgz", - "integrity": "sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.2.tgz", - "integrity": "sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.2.tgz", - "integrity": "sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.2.tgz", - "integrity": "sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.2.tgz", - "integrity": "sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.2.tgz", - "integrity": "sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.2.tgz", - "integrity": "sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.2.tgz", - "integrity": "sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.2.tgz", - "integrity": "sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.2.tgz", - "integrity": "sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.2.tgz", - "integrity": "sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.2.tgz", - "integrity": "sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.2.tgz", - "integrity": "sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.2.tgz", - "integrity": "sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.2.tgz", - "integrity": "sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.2.tgz", - "integrity": "sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.2.tgz", - "integrity": "sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@rollup/rollup-win32-x64-gnu": { "version": "4.53.2", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.2.tgz", @@ -2430,195 +1378,6 @@ "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" } }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.17.tgz", - "integrity": "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.17.tgz", - "integrity": "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.17.tgz", - "integrity": "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.17.tgz", - "integrity": "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.17.tgz", - "integrity": "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.17.tgz", - "integrity": "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.17.tgz", - "integrity": "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.17.tgz", - "integrity": "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.17.tgz", - "integrity": "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.17.tgz", - "integrity": "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.6.0", - "@emnapi/runtime": "^1.6.0", - "@emnapi/wasi-threads": "^1.1.0", - "@napi-rs/wasm-runtime": "^1.0.7", - "@tybys/wasm-util": "^0.10.1", - "tslib": "^2.4.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.17.tgz", - "integrity": "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { "version": "4.1.17", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.17.tgz", @@ -2854,18 +1613,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/plist": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz", - "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@types/node": "*", - "xmlbuilder": ">=11.0.1" - } - }, "node_modules/@types/prop-types": { "version": "15.7.15", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", @@ -2910,14 +1657,6 @@ "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", "license": "MIT" }, - "node_modules/@types/verror": { - "version": "1.10.11", - "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz", - "integrity": "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/@types/yauzl": { "version": "2.10.3", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", @@ -3493,17 +2232,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.8" - } - }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -3514,17 +2242,6 @@ "node": ">=12" } }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", @@ -4199,24 +2916,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -4514,17 +3213,6 @@ "dev": true, "license": "MIT" }, - "node_modules/crc": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", - "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "buffer": "^5.1.0" - } - }, "node_modules/crc-32": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", @@ -5077,33 +3765,6 @@ "node": ">= 10.0.0" } }, - "node_modules/dmg-license": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz", - "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==", - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "@types/plist": "^3.0.1", - "@types/verror": "^1.10.3", - "ajv": "^6.10.0", - "crc": "^3.8.0", - "iconv-corefoundation": "^1.1.7", - "plist": "^3.0.4", - "smart-buffer": "^4.0.2", - "verror": "^1.10.0" - }, - "bin": { - "dmg-license": "bin/dmg-license.js" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/dom-helpers": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", @@ -6029,17 +4690,6 @@ "@types/yauzl": "^2.9.1" } }, - "node_modules/extsprintf": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", - "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT", - "optional": true - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -6052,12 +4702,6 @@ "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "license": "Apache-2.0" }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "license": "Apache-2.0" - }, "node_modules/fast-equals": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.3.3.tgz", @@ -6100,25 +4744,6 @@ "pend": "~1.2.0" } }, - "node_modules/ffi-napi": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/ffi-napi/-/ffi-napi-4.0.3.tgz", - "integrity": "sha512-PMdLCIvDY9mS32RxZ0XGb95sonPRal8aqRhLbeEtWKZTe2A87qRFG9HjOhvG8EX2UmQw5XNRMIOT+1MYlWmdeg==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "debug": "^4.1.1", - "get-uv-event-loop-napi-h": "^1.0.5", - "node-addon-api": "^3.0.0", - "node-gyp-build": "^4.2.1", - "ref-napi": "^2.0.1 || ^3.0.2", - "ref-struct-di": "^1.1.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -6277,20 +4902,6 @@ "devOptional": true, "license": "ISC" }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -6461,16 +5072,6 @@ "license": "MIT", "optional": true }, - "node_modules/get-uv-event-loop-napi-h": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/get-uv-event-loop-napi-h/-/get-uv-event-loop-napi-h-1.0.6.tgz", - "integrity": "sha512-t5c9VNR84nRoF+eLiz6wFrEp1SE2Acg0wS+Ysa2zF0eROes+LzOfuTaVHxGy8AbS8rq7FHEJzjnCZo1BupwdJg==", - "license": "MIT", - "optional": true, - "dependencies": { - "get-symbol-from-current-process-h": "^1.0.1" - } - }, "node_modules/get-windows": { "version": "9.2.3", "resolved": "https://registry.npmjs.org/get-windows/-/get-windows-9.2.3.tgz", @@ -7146,32 +5747,6 @@ "node": ">=0.8.0" } }, - "node_modules/iconv-corefoundation": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", - "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "cli-truncate": "^2.1.0", - "node-addon-api": "^1.6.3" - }, - "engines": { - "node": "^8.11.2 || >=10" - } - }, - "node_modules/iconv-corefoundation/node_modules/node-addon-api": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", - "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -8008,206 +6583,6 @@ "lightningcss-win32-x64-msvc": "1.30.2" } }, - "node_modules/lightningcss-android-arm64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", - "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", - "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", - "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", - "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", - "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", - "cpu": [ - "arm" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", - "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", - "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", - "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", - "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", - "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/lightningcss-win32-x64-msvc": { "version": "1.30.2", "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", @@ -8262,18 +6637,6 @@ "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", "license": "MIT" }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "license": "MIT" - }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", - "license": "MIT" - }, "node_modules/lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", @@ -9554,12 +7917,6 @@ "integrity": "sha512-HUrJFQ/StvgmXRcQ1ftY6VEZUq3jA2t9ncFN4F84J/vN0/FPpQF+8FKXb3l6fLces6q0uOHj6NJn+2xvZnxO6A==", "license": "BSD-3-Clause" }, - "node_modules/parchment": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/parchment/-/parchment-3.0.0.tgz", - "integrity": "sha512-HUrJFQ/StvgmXRcQ1ftY6VEZUq3jA2t9ncFN4F84J/vN0/FPpQF+8FKXb3l6fLces6q0uOHj6NJn+2xvZnxO6A==", - "license": "BSD-3-Clause" - }, "node_modules/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -10001,21 +8358,6 @@ "react-dom": "^16 || ^17 || ^18 || ^19" } }, - "node_modules/react-quill-new": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/react-quill-new/-/react-quill-new-3.6.0.tgz", - "integrity": "sha512-weU6YfB2+7Cujw5Hjgmi0aN/qJd3B6ADWrxgUJMp2MO3tEvKX5kfB0sg3P0UdOVfU0z8icsKFzlnEIpeW1mLhw==", - "license": "MIT", - "dependencies": { - "lodash-es": "^4.17.21", - "quill": "~2.0.2" - }, - "peerDependencies": { - "quill-delta": "^5.1.0", - "react": "^16 || ^17 || ^18 || ^19", - "react-dom": "^16 || ^17 || ^18 || ^19" - } - }, "node_modules/react-redux": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", @@ -10261,26 +8603,6 @@ "node": ">= 10.0" } }, - "node_modules/ref-struct-di": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ref-struct-di/-/ref-struct-di-1.1.1.tgz", - "integrity": "sha512-2Xyn/0Qgz89VT+++WP0sTosdm9oeowLP23wRJYhG4BFdMUrLj3jhwHZNEytYNYgtPKLNTP3KJX4HEgBvM1/Y2g==", - "license": "MIT", - "optional": true, - "dependencies": { - "debug": "^3.1.0" - } - }, - "node_modules/ref-struct-di/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "ms": "^2.1.1" - } - }, "node_modules/ref-wchar-napi": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/ref-wchar-napi/-/ref-wchar-napi-1.0.3.tgz", @@ -10951,61 +9273,6 @@ "node": ">=10" } }, - "node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/slice-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -11940,22 +10207,6 @@ "spdx-expression-parse": "^3.0.0" } }, - "node_modules/verror": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", - "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/victory-vendor": { "version": "36.9.2", "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", @@ -12060,20 +10311,6 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/vite/node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/vitest": { "version": "2.1.9", "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz", diff --git a/package.json b/package.json index 90580f5..3a8865e 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,6 @@ "axios": "^1.13.2", "chalk": "^5.6.2", "electron-store": "^11.0.2", - "quill": "^2.0.3", - "react-quill-new": "^3.6.0", "https": "^1.0.0", "lucide-react": "^0.552.0", "luxon": "^3.7.2", diff --git a/src/electron/api/axiosClient.ts b/src/electron/api/axiosClient.ts index a017ae4..0060ea6 100644 --- a/src/electron/api/axiosClient.ts +++ b/src/electron/api/axiosClient.ts @@ -12,7 +12,7 @@ const httpsAgent = new https.Agent({ }); const api: AxiosInstance = axios.create({ - baseURL: 'http://localhost:3000/api/v1/va', // 🔁 your backend + baseURL: 'https://daniela-nonprofessed-boastfully.ngrok-free.app/api/v1/va', // 🔁 your backend httpsAgent // timeout: 10000, }); diff --git a/src/electron/utils/utilityFunctions.ts b/src/electron/utils/utilityFunctions.ts index d9f508a..0a15c7d 100644 --- a/src/electron/utils/utilityFunctions.ts +++ b/src/electron/utils/utilityFunctions.ts @@ -68,7 +68,7 @@ export const handleLogin = async (credentials: { email: string, password: string try { console.log("➡️ Calling API..."); const response = await axios.post( - 'http://localhost:3000/api/v1/va/login', + 'https://daniela-nonprofessed-boastfully.ngrok-free.app/api/v1/va/login', { userDetails: { ...credentials } }, { headers: { "ngrok-skip-browser-warning": 69420 }, httpsAgent } ); diff --git a/src/ui/layout/DashboardLayout.tsx b/src/ui/layout/DashboardLayout.tsx index 0ef023d..afb1615 100644 --- a/src/ui/layout/DashboardLayout.tsx +++ b/src/ui/layout/DashboardLayout.tsx @@ -532,7 +532,7 @@ const DashboardLayout = () => { if (!selectedProject?.projectId) return console.log(selectedProject?.projectId, "SelectedProject ID") - socketRef.current = io("https://quality-friendly-goldfish.ngrok-free.app", { + socketRef.current = io("https://daniela-nonprofessed-boastfully.ngrok-free.app", { transports: ["websocket"], secure: true, rejectUnauthorized: false, From 4fb0720e7de406237369fdfb2394289e3e28ae77 Mon Sep 17 00:00:00 2001 From: JaiPandey425 Date: Mon, 1 Dec 2025 12:05:13 +0530 Subject: [PATCH 03/10] change the web-socket ngrok url --- src/ui/layout/DashboardLayout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/layout/DashboardLayout.tsx b/src/ui/layout/DashboardLayout.tsx index 99f7bbf..8ff0f2b 100644 --- a/src/ui/layout/DashboardLayout.tsx +++ b/src/ui/layout/DashboardLayout.tsx @@ -536,7 +536,7 @@ const DashboardLayout = () => { if (!selectedProject?.projectId) return console.log(selectedProject?.projectId, "SelectedProject ID") - socketRef.current = io("https://quality-friendly-goldfish.ngrok-free.app", { + socketRef.current = io("https://daniela-nonprofessed-boastfully.ngrok-free.app", { transports: ["websocket"], secure: true, rejectUnauthorized: false, From 17dcdec1c6c014ed2b5f2429db6a3a75b2cd9e87 Mon Sep 17 00:00:00 2001 From: JaiPandey425 Date: Mon, 1 Dec 2025 16:40:24 +0530 Subject: [PATCH 04/10] refresh dashboard projects --- src/ui/layout/DashboardLayout.tsx | 307 +++++++++++++------------ src/ui/pages/Dashboard.tsx | 112 +++++---- src/ui/pages/Timesheets/Timesheets.tsx | 4 +- 3 files changed, 220 insertions(+), 203 deletions(-) diff --git a/src/ui/layout/DashboardLayout.tsx b/src/ui/layout/DashboardLayout.tsx index 8ff0f2b..8ac3cf2 100644 --- a/src/ui/layout/DashboardLayout.tsx +++ b/src/ui/layout/DashboardLayout.tsx @@ -113,8 +113,8 @@ const DashboardLayout = () => { { label: "Dashboard", path: "/dashboard", icon: , exact: true }, { label: "Timesheet", path: "/dashboard/timesheet", icon: }, { label: "Leave & Adjust", path: "/dashboard/leave-adjustments", icon: }, - { label: "Schedule", path: "/dashboard/schedule", icon: }, - { label: "Pays Slips", path: "/dashboard/pays-slips", icon: }, + { label: "Schedule", path: "/dashboard/schedule", icon: }, + { label: "Pays Slips", path: "/dashboard/pays-slips", icon: }, { label: "Settings", path: "/dashboard/settings", icon: }, { label: "VPN", path: "/dashboard/vpn", icon: }, { label: "Help & Support", path: "/dashboard/help-support", icon: }, @@ -630,170 +630,171 @@ const DashboardLayout = () => { return ( <> - {/* Header */} - {isTracking && -
-
- {isStopping ? ( - // Skeleton for left side during stop -
-
-
-
- ) : selectedProject ? ( - isTracking ? ( - +
+ {/* Header */} + {isTracking && +
+
+ {isStopping ? ( + // Skeleton for left side during stop +
+
+
+
+ ) : selectedProject ? ( + isTracking ? ( + + ) : ( + + ) ) : ( - ) - ) : ( + )} + + {!isStopping && selectedProject ? ( + + {selectedProject.projectName} + + ) : !isStopping ? ( + + No Project Selected + + ) : null} +
+ +
+ {isStopping ? ( + // Skeleton for right side during stop +
+
+
+
+
+ ) : ( + <> +
+ {formatTime( + isTracking ? elapsedTime : parseTimeStringToSeconds(selectedProject?.totalWorkTime || "0h 0m 0s") + )} +
+ + {/* + + */} + + + + )} +
+
+ } + + {/* Layout Body */} +
+ {/* Sidebar */} + - {!isStopping && selectedProject ? ( - - {selectedProject.projectName} - - ) : !isStopping ? ( - - No Project Selected - - ) : null} -
+ {/* Main Content */} +
+ +
-
- {isStopping ? ( - // Skeleton for right side during stop -
-
-
-
-
- ) : ( - <> -
- {formatTime( - isTracking ? elapsedTime : parseTimeStringToSeconds(selectedProject?.totalWorkTime || "0h 0m 0s") - )} -
- - - - - - - - )} -
-
- } - - {/* Layout Body */} -
- {/* Sidebar */} - - - {/* Main Content */} -
- -
- - setShowLogoutModal(false)} - onConfirm={handleLogout} - /> + setShowLogoutModal(false)} + onConfirm={handleLogout} + /> +
- ); }; diff --git a/src/ui/pages/Dashboard.tsx b/src/ui/pages/Dashboard.tsx index 26d8220..fd6f7e9 100644 --- a/src/ui/pages/Dashboard.tsx +++ b/src/ui/pages/Dashboard.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useState } from "react"; -import { Clock, TrendingUp, Play, Square, ProjectorIcon, Pause } from "lucide-react"; +import { Clock, TrendingUp, Play, Square, ProjectorIcon, Pause, RefreshCw } from "lucide-react"; import { useOutletContext } from "react-router-dom"; import NoData from "./NoData"; import { useDispatch, useSelector } from "react-redux"; @@ -105,6 +105,7 @@ const Dashboard: React.FC = () => { const selectedProject = useSelector((state: any) => state.projects.selectedProject); const projectSummary = useSelector((state: any) => state.projects.projectSummary); const projectTrackingMap = useSelector((state: any) => state.projects.projectTrackingMap); + const [isRefreshing, setIsRefreshing] = useState(false); console.log("Line 111", projectsData, selectedProject, projectTrackingMap) @@ -127,60 +128,66 @@ const Dashboard: React.FC = () => { } }; - useEffect(() => { - const fetchProjects = async () => { - try { - console.log("📡 Fetching projects...Dashboard"); - const res = await window.electron.getProjectList(); - console.log(" Project list response in dashboard:", res); + const fetchProjects = async () => { + try { + console.log("📡 Fetching projects...Dashboard"); + const res = await window.electron.getProjectList(); + console.log(" Project list response in dashboard:", res); - if (res?.success && Array.isArray(res.data)) { - // Store data redux store - dispatch(setProjectsDataSlice(res.data)); + if (res?.success && Array.isArray(res.data)) { + // Store data redux store + dispatch(setProjectsDataSlice(res.data)); + dispatch( + setProjectSummarySlice({ + totalProjects: res.totalProjects || 0, + activeProjects: res.activeProjects || 0, + awayProjects: res.awayProjects || 0, + totalWorkTime: res.totalWorkTime || "0h 0m 0s", + totalWorkMs: res.totalWorkMs || 0, + vaName: res.vaName || "", + vaId: res.vaId || "", + }) + ); + + // Auto-select first project + if (res.data.length > 0 && !selectedProject) { + const firstProject = res.data[0]; + + // Select the project + dispatch(setSelectedProjectSlice(firstProject)); + + const initialElapsed = parseTimeStringToSeconds(firstProject.totalWorkTime); + + // Initialize tracking data for first project dispatch( - setProjectSummarySlice({ - totalProjects: res.totalProjects || 0, - activeProjects: res.activeProjects || 0, - awayProjects: res.awayProjects || 0, - totalWorkTime: res.totalWorkTime || "0h 0m 0s", - totalWorkMs: res.totalWorkMs || 0, - vaName: res.vaName || "", - vaId: res.vaId || "", + initializeProjectTracking({ + projectId: firstProject.projectId, + initialTime: initialElapsed, }) ); - - // Auto-select first project - if (res.data.length > 0 && !selectedProject) { - const firstProject = res.data[0]; - - // Select the project - dispatch(setSelectedProjectSlice(firstProject)); - - const initialElapsed = parseTimeStringToSeconds(firstProject.totalWorkTime); - - // Initialize tracking data for first project - dispatch( - initializeProjectTracking({ - projectId: firstProject.projectId, - initialTime: initialElapsed, - }) - ); - } - - fetchProfile() - - } else { - console.warn("⚠️ Unexpected response:", res); } - } catch (err) { - console.error("❌ Error fetching projects:", err); - } - }; + fetchProfile() + + } else { + console.warn("⚠️ Unexpected response:", res); + } + } catch (err) { + console.error("❌ Error fetching projects:", err); + } + }; + + useEffect(() => { fetchProjects(); }, []); + const handleRefresh = async () => { + setIsRefreshing(true); + await fetchProjects(); + setTimeout(() => setIsRefreshing(false), 500); // Small delay for visual feedback + }; + const selectedData = selectedProject ? projectTrackingMap[selectedProject.projectId] : null; @@ -234,7 +241,7 @@ const Dashboard: React.FC = () => { }; return ( -
+
{/* Header */}

Dashboard

@@ -243,7 +250,7 @@ const Dashboard: React.FC = () => {
- {isStopping ? ( + {(isStopping || isRefreshing) ? ( // Skeleton loader for tracking section
@@ -318,10 +325,19 @@ const Dashboard: React.FC = () => { {/* 🔹 Sticky Header */}

Projects

+
{/* 🔹 Table or Empty State or Skeleton */} - {isStopping ? ( + {(isStopping || isRefreshing) ? ( // Skeleton loader for projects table
{[...Array(3)].map((_, index) => ( diff --git a/src/ui/pages/Timesheets/Timesheets.tsx b/src/ui/pages/Timesheets/Timesheets.tsx index 51bc4b0..943104d 100644 --- a/src/ui/pages/Timesheets/Timesheets.tsx +++ b/src/ui/pages/Timesheets/Timesheets.tsx @@ -106,7 +106,7 @@ const Timesheets: React.FC = () => { return ( -
+
{/* Header */}
@@ -150,7 +150,7 @@ const Timesheets: React.FC = () => {
*/} {/* Timesheet Table */} -
+
From 0eea767e5e923b5ba4cc5d738ff2751df70c5729 Mon Sep 17 00:00:00 2001 From: JaiPandey425 Date: Tue, 2 Dec 2025 17:16:50 +0530 Subject: [PATCH 05/10] listing all the menus components and add the refresh button with the last time refresh list --- src/electron/api/axiosClient.ts | 2 +- src/electron/ipc/ipcHandler.ts | 62 +++-- src/electron/preload.cts | 1 + src/ui/App.tsx | 2 + src/ui/index.css | 15 + src/ui/layout/DashboardLayout.tsx | 2 +- src/ui/pages/Dashboard.tsx | 7 +- .../ApplyLeaveAndAdjustmentsTime.tsx | 114 ++++++++ .../LeaveAndAdjustments.tsx | 258 +++++++++++------ .../tabs/AdjustmentTab.tsx | 14 +- .../LeaveAndAdjustments/tabs/PtoLeaveTab.tsx | 3 + .../LeaveAndAdjustments/tabs/SickLeaveTab.tsx | 3 + src/ui/pages/Schedule/Schedule.tsx | 207 +++++++++----- src/ui/pages/Settings/Settings.tsx | 8 - src/ui/pages/Timesheets/Timesheets.tsx | 259 ++++++++++++------ types.d.ts | 5 + 16 files changed, 695 insertions(+), 267 deletions(-) create mode 100644 src/ui/pages/LeaveAndAdjustments/ApplyLeaveAndAdjustmentsTime.tsx diff --git a/src/electron/api/axiosClient.ts b/src/electron/api/axiosClient.ts index a017ae4..a4960b4 100644 --- a/src/electron/api/axiosClient.ts +++ b/src/electron/api/axiosClient.ts @@ -42,7 +42,7 @@ api.interceptors.response.use( (response: AxiosResponse) => response.data, async (error) => { // console.error('[Axios Error]', error.message); - if (error.response?.status === 401) { + if (error.response?.status === 401 || 403) { console.warn('Token expired — trigger refresh logic if needed'); sendTokenExpired(); } diff --git a/src/electron/ipc/ipcHandler.ts b/src/electron/ipc/ipcHandler.ts index cd9b1f9..759d285 100644 --- a/src/electron/ipc/ipcHandler.ts +++ b/src/electron/ipc/ipcHandler.ts @@ -29,7 +29,7 @@ export const registerIPCHandlers = (mainWindow: BrowserWindow) => { return { success: true }; }); - ipcMain.handle("login-request", async (_, credentials: { email: string; password: string }) => { + ipcMain.handle("login-request", async (_, credentials: { email: string; password: string }) => { return handleLogin(credentials); }); @@ -46,8 +46,8 @@ export const registerIPCHandlers = (mainWindow: BrowserWindow) => { return { success: false, message: "Logout failed" }; } }); - - + + @@ -96,7 +96,7 @@ ipcMain.handle("get-project-list", async () => { return { success: false, message: error?.message || String(error) || "Unknown error", - status:error?.status + status: error?.status }; } }); @@ -110,8 +110,8 @@ ipcMain.handle("start-project-tracking", async (event, payload) => { const response = await api.post("/log/project", payload); console.log(" Tracking start synced:", response); - return { success: true, data: response }; - } catch (error:any) { + return { success: true, data: response }; + } catch (error: any) { console.error("❌ Error syncing project start:", error); // If API sent a response (like 400) @@ -261,14 +261,14 @@ ipcMain.handle("change-password", async (event, data) => { // Build final payload const payload = { - userDetails: { - oldPassword: data.oldPassword, - password: data.password, - cPassword: data.cPassword, - }, - }; + userDetails: { + oldPassword: data.oldPassword, + password: data.password, + cPassword: data.cPassword, + }, + }; - console.log("Change Password payload" , payload) + console.log("Change Password payload", payload) try { const response = await api.patch("/profile", payload); console.log("Chnage-passowrd-response", response) @@ -303,20 +303,20 @@ ipcMain.handle("get-project-summary", async (_, { projectId, date }) => { ipcMain.handle("create-timesheet", async (event, data) => { - console.log("data" , data) + console.log("data", data) try { console.log("📩 Timesheet Payload:", data); const res = await api.post("/timesheet/adjustment/request", data); - console.log(res , " Line 323") + console.log(res, " Line 323") return { success: true, message: "Timesheet created successfully", data: res, }; - } catch (error:any) { + } catch (error: any) { console.error("❌ Timesheet Error:", error); return { @@ -402,7 +402,7 @@ ipcMain.handle("get-schedules", async () => { ipcMain.handle("get-payslips", async (event, vaId: string) => { - console.log("get-payslips",vaId) + console.log("get-payslips", vaId) try { const response = await api.get(`/paySlip/${vaId}`); @@ -433,6 +433,34 @@ ipcMain.handle("get-payslips", async (event, vaId: string) => { +ipcMain.handle("get-leave-list", async () => { + + try { + const res = await api.get("/leaves/all"); + + console.log("/leaves/all", res.data); + return { + success: true, + data: res.data, + }; + } catch (error: any) { + let message = "Unknown error occurred"; + + if (error.response) { + console.log("Status:", error.response.status); + console.log("Message:", error.response.statusText); + console.log("Server Response:", error.response.data); + + message = error.response.data?.message || error.response.statusText; + } else { + message = error.message; + } + return { + success: false, + message + }; + } +}); diff --git a/src/electron/preload.cts b/src/electron/preload.cts index 34c9b41..3ebc272 100644 --- a/src/electron/preload.cts +++ b/src/electron/preload.cts @@ -56,6 +56,7 @@ contextBridge.exposeInMainWorld('electron', { getSchedules: () => ipcRenderer.invoke("get-schedules"), getPayslips: (vaId: string) => ipcRenderer.invoke("get-payslips", vaId), + getLeaveList: () => ipcRenderer.invoke("get-leave-list") } satisfies Window['electron']); diff --git a/src/ui/App.tsx b/src/ui/App.tsx index 7b59fca..7de1c12 100644 --- a/src/ui/App.tsx +++ b/src/ui/App.tsx @@ -96,6 +96,7 @@ import HelpSupport from "./pages/HelpSupport/HelpSupport"; import Vpn from "./pages/Vpn/Vpn"; import Schedule from "./pages/Schedule/Schedule"; import PaysSlips from "./pages/PaySlips/PaysSlips"; +import ApplyLeaveAndAdjustmentsTime from "./pages/LeaveAndAdjustments/ApplyLeaveAndAdjustmentsTime"; const App: React.FC = () => { @@ -138,6 +139,7 @@ const App: React.FC = () => { } /> } /> } /> + } /> } /> } /> } /> diff --git a/src/ui/index.css b/src/ui/index.css index 4f0c033..72448ce 100644 --- a/src/ui/index.css +++ b/src/ui/index.css @@ -22,6 +22,21 @@ } } +@layer utilities { + .thin-scrollbar::-webkit-scrollbar { + width: 6px; + height: 6px; + } + .thin-scrollbar::-webkit-scrollbar-thumb { + background-color: hsl(var(--bc) / 0.3); /* base-content/30 */ + border-radius: 999px; + } + .thin-scrollbar::-webkit-scrollbar-track { + background: transparent; + } +} + + input[type=number] { -moz-appearance: textfield; } diff --git a/src/ui/layout/DashboardLayout.tsx b/src/ui/layout/DashboardLayout.tsx index 8ac3cf2..3f0f0f2 100644 --- a/src/ui/layout/DashboardLayout.tsx +++ b/src/ui/layout/DashboardLayout.tsx @@ -746,7 +746,7 @@ const DashboardLayout = () => { ))} -
+
+ + + + + + + + )); + }; + + const formatLastRefreshed = () => { + if (!lastRefreshed) return "Never"; + + return DateTime.fromJSDate(lastRefreshed) + .toLocal() + .toFormat("HH:mm:ss"); // Only time + }; return ( -
-
-

Leave & Adjust Timesheet

+
+ {/* Header */} +
+

Leave & Adjust Timesheet

+ +
- {/* Tabs — Disabled while submitting */} -
- setActiveTab("pto")} - > - PTO Leave - + {/* Table Container */} +
+
+ + + + + + + + + + - setActiveTab("sick")} - > - Sick Leave - + + {loading ? ( + renderSkeletonRows() + ) : refreshing && leaves.length === 0 ? ( + renderSkeletonRows() + ) : leaves.length > 0 ? ( + leaves.map((leave, index) => ( + + - setActiveTab("adjustment")} - > - Adjustment Submission - + + + + + + + + + + + )) + ) : ( + + + + )} + + {refreshing && leaves.length > 0 && ( + + + + )} + +
#Leave TypeDate From → ToStatusTotal DaysApplied On
{index + 1} + {leave.leaveType} + + {leave.dateFrom} +
+ {leave.dateTo} +
+ + {leave.status} + + {leave.totalDays}{new Date(leave.appliedOn).toLocaleString()}
+ No leaves found +
+
+ + Refreshing... +
+
+
+ + {/* Footer */} +
+
+ {leaves.length} leave{leaves.length !== 1 ? "s" : ""} found
- {/* Main Container */} -
-
+
+ {lastRefreshed && ( +
+ Last updated: {formatLastRefreshed()} +
+ )} - {activeTab === "pto" && ( - +
+
-
+
); }; diff --git a/src/ui/pages/LeaveAndAdjustments/tabs/AdjustmentTab.tsx b/src/ui/pages/LeaveAndAdjustments/tabs/AdjustmentTab.tsx index f923f9a..fb85198 100644 --- a/src/ui/pages/LeaveAndAdjustments/tabs/AdjustmentTab.tsx +++ b/src/ui/pages/LeaveAndAdjustments/tabs/AdjustmentTab.tsx @@ -4,6 +4,7 @@ import ReactQuill from 'react-quill-new'; import { quillModules } from '../../../utils/quillConfig'; import Quill from 'quill'; import 'quill/dist/quill.snow.css'; +import { useNavigate } from 'react-router-dom'; interface Project { projectId: string; @@ -48,6 +49,7 @@ const AdjustmentTab: React.FC = ({ projects }) => { const [comment, setComment] = useState(""); const [isSubmitting, setIsSubmitting] = useState(false); const quillRef = useRef(null); + const navigate = useNavigate() // Field validation functions const validateProjectId = (projectId: string): string | undefined => { @@ -323,6 +325,12 @@ const AdjustmentTab: React.FC = ({ projects }) => { useEffect(() => { if (selectedProjectId && selectedDate) { + setProjectSummary(null) + setStartTime(""); + setEndTime(""); + setComment(""); + setFormErrors({}); + setTotalAdjustment("0h 0m"); fetchLogs(); } else { setProjectSummary(null); @@ -454,6 +462,7 @@ const AdjustmentTab: React.FC = ({ projects }) => { console.log("Timesheet Created Successfully"); alert(res.message); resetForm(); + navigate('/dashboard/leave-adjustments') } else { setFormErrors(prev => ({ ...prev, @@ -655,11 +664,6 @@ const AdjustmentTab: React.FC = ({ projects }) => { disabled={isSubmitting} className={`btn btn-primary flex-1 hover:shadow-lg transition-all duration-300 ${isSubmitting ? 'loading' : ''}`} > - {!isSubmitting && ( - - - - )} {isSubmitting ? 'Submitting...' : 'Submit Request'} diff --git a/src/ui/pages/LeaveAndAdjustments/tabs/PtoLeaveTab.tsx b/src/ui/pages/LeaveAndAdjustments/tabs/PtoLeaveTab.tsx index e41ffbd..a9a8956 100644 --- a/src/ui/pages/LeaveAndAdjustments/tabs/PtoLeaveTab.tsx +++ b/src/ui/pages/LeaveAndAdjustments/tabs/PtoLeaveTab.tsx @@ -4,6 +4,7 @@ import { quillModules } from "../../../utils/quillConfig"; import ReactQuill from "react-quill-new"; import Quill from "quill"; import "quill/dist/quill.snow.css"; +import { useNavigate } from "react-router-dom"; interface LeaveFormData { fromDate: string; @@ -23,6 +24,7 @@ const PtoLeaveTab: React.FC<{ }); const [serverError, setServerError] = useState(""); const quillRef = useRef(null); + const navigate = useNavigate() const [errors, setErrors] = useState>({}); @@ -199,6 +201,7 @@ const PtoLeaveTab: React.FC<{ if (result.success) { resetForm(); alert("PTO submitted!"); + navigate('/dashboard/leave-adjustments') } else { setServerError(result.message || "Something went wrong"); } diff --git a/src/ui/pages/LeaveAndAdjustments/tabs/SickLeaveTab.tsx b/src/ui/pages/LeaveAndAdjustments/tabs/SickLeaveTab.tsx index 180e6ae..fd817ad 100644 --- a/src/ui/pages/LeaveAndAdjustments/tabs/SickLeaveTab.tsx +++ b/src/ui/pages/LeaveAndAdjustments/tabs/SickLeaveTab.tsx @@ -3,6 +3,7 @@ import { quillModules } from "../../../utils/quillConfig"; import ReactQuill from "react-quill-new"; import Quill from "quill"; import "quill/dist/quill.snow.css"; // correct CSS +import { useNavigate } from 'react-router-dom'; interface LeaveFormData { fromDate: string; @@ -22,6 +23,7 @@ const SickLeaveTab: React.FC<{ const [serverError, setServerError] = useState(""); const quillRef = useRef(null); const [sickErrors, setSickErrors] = useState>({}); + const navigate = useNavigate() useEffect(() => { const editor = quillRef.current?.getEditor(); @@ -189,6 +191,7 @@ const SickLeaveTab: React.FC<{ if (result.success) { resetForm(); alert("SICK submitted!"); + navigate('/dashboard/leave-adjustments') } else { setServerError(result.message || "Something went wrong"); } diff --git a/src/ui/pages/Schedule/Schedule.tsx b/src/ui/pages/Schedule/Schedule.tsx index ca8ae10..63d0461 100644 --- a/src/ui/pages/Schedule/Schedule.tsx +++ b/src/ui/pages/Schedule/Schedule.tsx @@ -1,4 +1,7 @@ import React, { useEffect, useState } from "react"; +import { useOutletContext } from "react-router-dom"; +import { TrackingState } from "../../layout/DashboardLayout"; +import { RefreshCw, Loader2 } from "lucide-react"; import { DateTime } from "luxon"; interface ScheduleItem { @@ -12,50 +15,58 @@ interface ScheduleItem { } const Schedule = () => { + const { isTracking } = useOutletContext(); + const [rows, setRows] = useState([]); const [loading, setLoading] = useState(true); + const [refreshing, setRefreshing] = useState(false); const [error, setError] = useState(null); + // const [lastUpdated, setLastUpdated] = useState(null); + const [lastRefreshed, setLastRefreshed] = useState(null); - // Format helpers using Luxon - const formatDateTime = (input: string | null) => { - if (!input) return "-"; - return DateTime.fromISO(input) - .setZone("local") - .toLocaleString(DateTime.DATETIME_MED); - }; - + /** Format: Only Date */ const formatOnlyDate = (input: string | null) => { if (!input) return "-"; - return DateTime.fromISO(input) - .setZone("local") - .toLocaleString(DateTime.DATE_MED); + return DateTime.fromISO(input).toLocaleString(DateTime.DATE_MED); }; - // Fetch schedules - useEffect(() => { - const loadSchedules = async () => { - try { - const res = await window.electron.getSchedules(); - console.log(res.result); + /** Format: Date + Time */ + const formatDateTime = (input: string | null) => { + if (!input) return "-"; + return DateTime.fromISO(input).toLocaleString(DateTime.DATETIME_MED); + }; - if (!res.success) { - setError(res.message || "Failed to load schedules"); - return; - } + /** Fetch Data */ + const fetchSchedules = async () => { + try { + const res = await window.electron.getSchedules(); - const list = res.result || []; // Use data array - setRows(list); - } catch (err: any) { - setError(err.message || "Unexpected error"); - } finally { - setLoading(false); + if (!res.success) { + setError(res.message || "Failed to load schedules"); + return; } - }; - loadSchedules(); + setRows(res.result || []); + setLastRefreshed(new Date()); + } catch (err: any) { + setError(err.message || "Unexpected error"); + } finally { + setLoading(false); + setRefreshing(false); + } + }; + + /** Refresh Button */ + const handleRefresh = async () => { + setRefreshing(true); + await fetchSchedules(); + }; + + useEffect(() => { + fetchSchedules(); }, []); - // Determine if current date is within the schedule period + /** Determine Active Period */ const isActiveRow = (row: ScheduleItem) => { if (!row.periodStart || !row.periodEnd) return false; @@ -66,50 +77,69 @@ const Schedule = () => { return now >= start && now <= end; }; - // Get Active Schedule (only one) const activeSchedule = rows.find((r) => isActiveRow(r)); + /** Skeleton Rows */ + const renderSkeletonRows = () => + Array.from({ length: 5 }).map((_, idx) => ( + +
+
+
+
+ + )); + + const formatLastRefreshed = () => { + if (!lastRefreshed) return "Never"; + + return DateTime.fromJSDate(lastRefreshed) + .toLocal() + .toFormat("HH:mm:ss"); // Only time + }; + return ( -
- {/* Header */} -
+
+ {/* HEADER */} +

Schedules

- {activeSchedule && ( -
-

- Pay Date: - {formatOnlyDate(activeSchedule.ScheduleDate)} - -

-
- )} + +
+ {activeSchedule && ( +
+ Pay Date: {formatOnlyDate(activeSchedule.ScheduleDate)} +
+ )} + +
-
- {/* Loader */} - {loading && ( -
- + {/* TABLE WRAPPER */} +
+ {/* Loading */} + {loading ? ( + + {renderSkeletonRows()} +
+ ) : error ? ( +
{error}
+ ) : rows.length === 0 ? ( +
+ No schedules found
- )} - - {/* Error */} - {!loading && error && ( -
❌ {error}
- )} - - {/* No Data */} - {!loading && !error && rows.length === 0 && ( -
No schedules found.
- )} - - - - - {/* Table */} - {!loading && !error && rows.length > 0 && ( -
- + ) : ( +
+
@@ -123,22 +153,59 @@ const Schedule = () => { {rows.map((row, index) => ( ))} + + {refreshing && ( + + + + )}
#
{index + 1} - {formatOnlyDate(row.periodStart)} → {formatOnlyDate(row.periodEnd)} + {formatOnlyDate(row.periodStart)} →{" "} + {formatOnlyDate(row.periodEnd)} {formatDateTime(row.cutOffTime)} {formatOnlyDate(row.ScheduleDate)}
+ + Refreshing... +
)}
-
+ + {/* FOOTER */} +
+
{rows.length} schedules found
+ + +
+ {lastRefreshed && + `Last updated: ${formatLastRefreshed()}`} + +
+
+
); }; diff --git a/src/ui/pages/Settings/Settings.tsx b/src/ui/pages/Settings/Settings.tsx index 7a0d6b1..180d786 100644 --- a/src/ui/pages/Settings/Settings.tsx +++ b/src/ui/pages/Settings/Settings.tsx @@ -82,14 +82,6 @@ const Settings: React.FC = () => {

-
-

- 💬 Skype -

-

- {profile?.skype || "Not Linked"} -

-
{/* Payment Info */}
diff --git a/src/ui/pages/Timesheets/Timesheets.tsx b/src/ui/pages/Timesheets/Timesheets.tsx index 943104d..33d9d00 100644 --- a/src/ui/pages/Timesheets/Timesheets.tsx +++ b/src/ui/pages/Timesheets/Timesheets.tsx @@ -1,8 +1,22 @@ import React, { useEffect, useState } from "react"; -import { CalendarDays, Clock, FileText, CheckCircle, XCircle, Eye, EyeOff } from "lucide-react"; +import { + CalendarDays, + Clock, + FileText, + CheckCircle, + XCircle, + Eye, + EyeOff, + RefreshCw, + Loader2 +} from "lucide-react"; import { useSelector } from "react-redux"; import { RootState } from "../../store"; import ProjectLogsModal from "./ProjectLogsModal"; +import { TrackingState } from "../../layout/DashboardLayout"; +import { useOutletContext } from "react-router-dom"; +import { DateTime } from "luxon"; + interface TimesheetEntry { id: number; @@ -28,45 +42,78 @@ interface Project { totalWorkTime: string; totalWorkMs: number; totalLogs: number; - status:string + status: string } const Timesheets: React.FC = () => { + const { isTracking } = useOutletContext(); const totalHours = timesheetData.reduce((acc, t) => acc + t.hours, 0); const approvedCount = timesheetData.filter((t) => t.status === "Approved").length; const { windowLogs } = useSelector((state: RootState) => state.window); - console.log("🧾 Timesheet in component:", windowLogs); + console.log(" Timesheet in component:", windowLogs); const [projectsData, setProjectsData] = useState([]); const [logs, setLogs] = useState([]); const [selectedProject, setSelectedProject] = useState(null); const [selectedProjectName, setSelectedProjectName] = useState(""); const [modalOpen, setModalOpen] = useState(false); const [loading, setLoading] = useState(false); + const [refreshing, setRefreshing] = useState(false); + const [lastRefreshed, setLastRefreshed] = useState(null); + const [initialLoading, setInitialLoading] = useState(true); + const [tick, setTick] = useState(0); + + const activeProjects = projectsData.filter(p => p.status === "ACTIVE"); + useEffect(() => { - const fetchProjects = async () => { - try { - console.log("📡 Fetching projects..."); - const res = await window.electron.getProjectList(); - console.log("✅ Project list response:", res); + const interval = setInterval(() => { + setTick((t) => t + 1); // force re-render every second + }, 1000); - if (res?.success && Array.isArray(res.data)) { - // Store project list - setProjectsData(res.data); - } else { - console.warn(" Unexpected response:", res); - } - } catch (err) { - console.error("Error fetching projects:", err); + return () => clearInterval(interval); + }, []); + + const formatLastRefreshed = () => { + if (!lastRefreshed) return "Never"; + + return DateTime.fromJSDate(lastRefreshed) + .toLocal() + .toFormat("HH:mm:ss"); // Only time + }; + + + + const fetchProjects = async () => { + try { + console.log("📡 Fetching projects..."); + const res = await window.electron.getProjectList(); + console.log("✅ Project list response:", res); + + if (res?.success && Array.isArray(res.data)) { + // Store project list + setProjectsData(res.data); + setLastRefreshed(new Date()); + } else { + console.warn(" Unexpected response:", res); } - }; + } catch (err) { + console.error("Error fetching projects:", err); + } finally { + setInitialLoading(false); + setRefreshing(false); + } + }; + useEffect(() => { fetchProjects(); }, []); - + const handleRefresh = async () => { + setRefreshing(true); + await fetchProjects(); + }; const handleViewLogs = async (projectID: string, projectName: string) => { try { @@ -105,54 +152,49 @@ const Timesheets: React.FC = () => { + + + // Skeleton loader rows + const renderSkeletonRows = () => { + return Array.from({ length: 5 }).map((_, index) => ( + + +
+ + +
+ + +
+ + +
+ + + )); + }; + return ( -
+
{/* Header */} -
-
-

Timesheets

-
+
+

Timesheets

- {/* Summary Cards */} - {/*
-
-
-
-

Total Logged Hours

-

{totalHours} hrs

-
- -
-
- -
-
-
-

Approved Days

-

{approvedCount}

-
- -
-
- -
-
-
-

Pending Days

-

- {timesheetData.length - approvedCount} -

-
- -
-
-
*/} - {/* Timesheet Table */} -
- - +
+
+ @@ -161,34 +203,83 @@ const Timesheets: React.FC = () => { - {projectsData.map((entry, index) => ( - - - - - + + + + + + )) + )} + + {refreshing && projectsData.length > 0 && ( + // Show a single skeleton row at the top while refreshing with existing data + + - ))} + )}
# Project
{index + 1}{entry.projectName}{formatWorkTime(entry.totalWorkTime)} - + {initialLoading ? ( + // Show skeleton while initially loading + renderSkeletonRows() + ) : refreshing && projectsData.length === 0 ? ( + // Show skeleton while refreshing with empty data + renderSkeletonRows() + ) : ( + // Show actual data + projectsData.map((entry, index) => ( +
{index + 1}{entry.projectName}{formatWorkTime(entry.totalWorkTime)} + +
+
+ + Refreshing... +
{/* Footer Summary */} -
- {/* - Showing {timesheetData.length} entries - */} - - Last updated: {new Date().toLocaleDateString()} - +
+
+ {activeProjects.length} project{activeProjects.length !== 1 ? 's' : ''} shown +
+ +
+ {lastRefreshed && ( +
+ Last updated: {formatLastRefreshed()} +
+ )} + + + +
{ logs={logs} loading={loading} /> -
+
); }; -export default Timesheets; +export default Timesheets; \ No newline at end of file diff --git a/types.d.ts b/types.d.ts index bb7af91..062dd79 100644 --- a/types.d.ts +++ b/types.d.ts @@ -211,6 +211,11 @@ interface Window { data?: PayslipResponse[]; message?: string; }>; + + getLeaveList: () => Promise<{ + success: boolean; + data?: any; + }>; }; } From 475776b2147315eb55cc2dea95fcfda50dee7ce0 Mon Sep 17 00:00:00 2001 From: JaiPandey425 Date: Tue, 2 Dec 2025 19:22:48 +0530 Subject: [PATCH 06/10] payment method modal added in settings sections --- src/ui/pages/Dashboard.tsx | 2 +- src/ui/pages/Schedule/Schedule.tsx | 4 +- .../SettingModal/ChangePaymentModal.tsx | 157 ++++++++++++++++++ src/ui/pages/Settings/Settings.tsx | 36 ++-- src/ui/store/profileSlice.tsx | 1 + types.d.ts | 1 + 6 files changed, 187 insertions(+), 14 deletions(-) create mode 100644 src/ui/pages/Settings/SettingModal/ChangePaymentModal.tsx diff --git a/src/ui/pages/Dashboard.tsx b/src/ui/pages/Dashboard.tsx index 03d742e..5506739 100644 --- a/src/ui/pages/Dashboard.tsx +++ b/src/ui/pages/Dashboard.tsx @@ -16,7 +16,7 @@ interface Project { totalWorkTime: string; totalWorkMs: number; totalLogs: number; - status: string + status: string; } interface ProjectTrackingData { diff --git a/src/ui/pages/Schedule/Schedule.tsx b/src/ui/pages/Schedule/Schedule.tsx index 63d0461..759ebd8 100644 --- a/src/ui/pages/Schedule/Schedule.tsx +++ b/src/ui/pages/Schedule/Schedule.tsx @@ -38,6 +38,8 @@ const Schedule = () => { /** Fetch Data */ const fetchSchedules = async () => { + setRefreshing(true); + setLoading(true); try { const res = await window.electron.getSchedules(); @@ -58,7 +60,7 @@ const Schedule = () => { /** Refresh Button */ const handleRefresh = async () => { - setRefreshing(true); + await fetchSchedules(); }; diff --git a/src/ui/pages/Settings/SettingModal/ChangePaymentModal.tsx b/src/ui/pages/Settings/SettingModal/ChangePaymentModal.tsx new file mode 100644 index 0000000..6c08447 --- /dev/null +++ b/src/ui/pages/Settings/SettingModal/ChangePaymentModal.tsx @@ -0,0 +1,157 @@ +import React, { useState, useEffect } from "react"; +import { CreditCard } from "lucide-react"; +import { useSelector, useDispatch } from "react-redux"; +import { RootState } from "../../../store"; + +interface ChangePaymentModalProps { + isOpen: boolean; + onClose: () => void; +} + +const ChangePaymentModal: React.FC = ({ + isOpen, + onClose, +}) => { + const dispatch = useDispatch(); + const { profile } = useSelector((state: RootState) => state.profile); + + const [method, setMethod] = useState(profile?.paymentMethod || ""); + const [wiseEmail, setWiseEmail] = useState(profile?.wiseEmail || ""); + const [veemAccount, setVeemAccount] = useState(profile?.veemAccount || ""); + + const [loading, setLoading] = useState(false); + const [error, setError] = useState(""); + + useEffect(() => { + if (isOpen) { + setMethod(profile?.paymentMethod || ""); + setWiseEmail(profile?.wiseEmail || ""); + setVeemAccount(profile?.veemAccount || ""); + setError(""); + } + }, [isOpen]); + + if (!isOpen) return null; + + const handleSubmit = async () => { + setError(""); + + // Validation + if (!method) { + return setError("Please select a payment method."); + } + + if (method === "wise" && !wiseEmail.trim()) { + return setError("Wise email is required."); + } + + if (method === "wise" && !/^\S+@\S+\.\S+$/.test(wiseEmail)) { + return setError("Enter a valid Wise email."); + } + + if (method === "veem" && !veemAccount.trim()) { + return setError("Veem account number is required."); + } + + setLoading(true); + + // try { + // // Replace with your electron API + // await window.electron.updatePayment({ + // method, + // wiseEmail: method === "wise" ? wiseEmail.trim() : "", + // veemAccount: method === "veem" ? veemAccount.trim() : "", + // }); + + // // Refresh or update Redux + // // dispatch(updateProfile({...})); + + // onClose(); + // } catch (err) { + // setError("Failed to update payment info. Try again."); + // } + + setLoading(false); + }; + + return ( + +
+ +

+ Change Payment Method +

+ + {/* Error */} + {error && ( +
{error}
+ )} + + {/* Dropdown */} +
+ + + +
+ + {/* Wise Email */} + {method === "wise" && ( +
+ + setWiseEmail(e.target.value)} + /> +
+ )} + + {/* Veem Account */} + {method === "veem" && ( +
+ + setVeemAccount(e.target.value)} + /> +
+ )} + +
+ + + +
+
+
+ ); +}; + +export default ChangePaymentModal; diff --git a/src/ui/pages/Settings/Settings.tsx b/src/ui/pages/Settings/Settings.tsx index 180d786..c8a8f4a 100644 --- a/src/ui/pages/Settings/Settings.tsx +++ b/src/ui/pages/Settings/Settings.tsx @@ -1,13 +1,15 @@ import React, { useState } from "react"; -import { User, Mail, Phone, Calendar, MapPin, CreditCard, Globe, Lock } from "lucide-react"; +import { User, Mail, Phone, Calendar, MapPin, CreditCard, Globe, Lock, SquarePen } from "lucide-react"; import { useSelector } from "react-redux"; import { RootState } from "../../store"; import ChangePasswordModal from "./ChangePasswordModal"; +import ChangePaymentModal from "./SettingModal/ChangePaymentModal"; const Settings: React.FC = () => { const { profile } = useSelector((state: RootState) => state.profile); const [modalOpen, setModalOpen] = useState(false); + const [showPaymentModal,setShowPaymentModal] = useState(false) const fullName = `${profile?.firstName || ""} ${profile?.lastName || ""}`.trim(); @@ -84,22 +86,31 @@ const Settings: React.FC = () => { {/* Payment Info */} -
-

- Payment -

-

- {profile?.paymentMethod || "N/A"} -

+
setShowPaymentModal(true)}> +
+
+

+ Payment +

+

+ {profile?.paymentMethod || "N/A"} +

+
+ + + +
- {profile?.paymentMethod === "payoneer" && ( + + {profile?.paymentMethod === "wise" && (

- 💳 Payoneer + 💳 Wise

- {maskLast5(profile?.payoneerAccount)} + {maskLast5(profile?.wiseEmail)}

)} @@ -129,8 +140,9 @@ const Settings: React.FC = () => {
- {/* Change Password Modal */} + {/* Modal */} setModalOpen(false)} /> + setShowPaymentModal(false)} />
); }; diff --git a/src/ui/store/profileSlice.tsx b/src/ui/store/profileSlice.tsx index eca87d4..35f250d 100644 --- a/src/ui/store/profileSlice.tsx +++ b/src/ui/store/profileSlice.tsx @@ -19,6 +19,7 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; paymentMethod:string; payoneerAccount:string; veemAccount:string; + wiseEmail:string _id: string; } diff --git a/types.d.ts b/types.d.ts index 062dd79..6eb395d 100644 --- a/types.d.ts +++ b/types.d.ts @@ -83,6 +83,7 @@ interface ProfileData { paymentMethod: string; payoneerAccount: string; veemAccount: string; + wiseEmail:string _id: string; } From 70193501d3fb0c1728ef89e59f6473a10cf79788 Mon Sep 17 00:00:00 2001 From: JaiPandey425 Date: Tue, 2 Dec 2025 19:42:19 +0530 Subject: [PATCH 07/10] Timesheets open the modal with loader show the logs with loading --- src/ui/pages/Timesheets/ProjectLogsModal.tsx | 1 + src/ui/pages/Timesheets/Timesheets.tsx | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ui/pages/Timesheets/ProjectLogsModal.tsx b/src/ui/pages/Timesheets/ProjectLogsModal.tsx index 9724311..7144932 100644 --- a/src/ui/pages/Timesheets/ProjectLogsModal.tsx +++ b/src/ui/pages/Timesheets/ProjectLogsModal.tsx @@ -95,3 +95,4 @@ const ProjectLogsModal: React.FC = ({ isOpen, onClose, projectName, logs, }; export default ProjectLogsModal; + diff --git a/src/ui/pages/Timesheets/Timesheets.tsx b/src/ui/pages/Timesheets/Timesheets.tsx index 33d9d00..98e893f 100644 --- a/src/ui/pages/Timesheets/Timesheets.tsx +++ b/src/ui/pages/Timesheets/Timesheets.tsx @@ -117,6 +117,7 @@ const Timesheets: React.FC = () => { const handleViewLogs = async (projectID: string, projectName: string) => { try { + setModalOpen(true); setLoading(true); setSelectedProject(projectID); setSelectedProjectName(projectName); @@ -132,11 +133,13 @@ const Timesheets: React.FC = () => { setLogs([]); } - setModalOpen(true); + } catch (error) { console.error("Error fetching logs:", error); + setModalOpen(false) } finally { setLoading(false); + } }; From ed0bcb710778803bd1e829bdec790449df0f9640 Mon Sep 17 00:00:00 2001 From: BhagwanSingh Date: Tue, 2 Dec 2025 20:02:55 +0530 Subject: [PATCH 08/10] Socket update --- src/electron/api/axiosClient.ts | 2 +- src/electron/utils/utilityFunctions.ts | 2 +- src/ui/layout/DashboardLayout.tsx | 56 +++++++++++++++++-- .../LeaveAndAdjustments/tabs/PtoLeaveTab.tsx | 2 +- .../LeaveAndAdjustments/tabs/SickLeaveTab.tsx | 2 +- 5 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/electron/api/axiosClient.ts b/src/electron/api/axiosClient.ts index 0060ea6..c4662a1 100644 --- a/src/electron/api/axiosClient.ts +++ b/src/electron/api/axiosClient.ts @@ -12,7 +12,7 @@ const httpsAgent = new https.Agent({ }); const api: AxiosInstance = axios.create({ - baseURL: 'https://daniela-nonprofessed-boastfully.ngrok-free.app/api/v1/va', // 🔁 your backend + baseURL: 'https://quality-friendly-goldfish.ngrok-free.app/api/v1/va', // 🔁 your backend httpsAgent // timeout: 10000, }); diff --git a/src/electron/utils/utilityFunctions.ts b/src/electron/utils/utilityFunctions.ts index 0a15c7d..c09a7ab 100644 --- a/src/electron/utils/utilityFunctions.ts +++ b/src/electron/utils/utilityFunctions.ts @@ -68,7 +68,7 @@ export const handleLogin = async (credentials: { email: string, password: string try { console.log("➡️ Calling API..."); const response = await axios.post( - 'https://daniela-nonprofessed-boastfully.ngrok-free.app/api/v1/va/login', + 'https://quality-friendly-goldfish.ngrok-free.app/api/v1/va/login', { userDetails: { ...credentials } }, { headers: { "ngrok-skip-browser-warning": 69420 }, httpsAgent } ); diff --git a/src/ui/layout/DashboardLayout.tsx b/src/ui/layout/DashboardLayout.tsx index 8ff0f2b..afd12cd 100644 --- a/src/ui/layout/DashboardLayout.tsx +++ b/src/ui/layout/DashboardLayout.tsx @@ -113,8 +113,8 @@ const DashboardLayout = () => { { label: "Dashboard", path: "/dashboard", icon: , exact: true }, { label: "Timesheet", path: "/dashboard/timesheet", icon: }, { label: "Leave & Adjust", path: "/dashboard/leave-adjustments", icon: }, - { label: "Schedule", path: "/dashboard/schedule", icon: }, - { label: "Pays Slips", path: "/dashboard/pays-slips", icon: }, + { label: "Schedule", path: "/dashboard/schedule", icon: }, + { label: "Pays Slips", path: "/dashboard/pays-slips", icon: }, { label: "Settings", path: "/dashboard/settings", icon: }, { label: "VPN", path: "/dashboard/vpn", icon: }, { label: "Help & Support", path: "/dashboard/help-support", icon: }, @@ -318,6 +318,7 @@ const DashboardLayout = () => { const socketRef = useRef(null); const intervalRef = useRef(null); + const lastAppsRef = useRef(null); const [isConnected, setIsConnected] = useState(false); useEffect(() => { @@ -525,6 +526,7 @@ const DashboardLayout = () => { } }; + const handleSocketConnect = async () => { const token = await window.electron.getToken("ACCESS_TOKEN"); @@ -536,7 +538,7 @@ const DashboardLayout = () => { if (!selectedProject?.projectId) return console.log(selectedProject?.projectId, "SelectedProject ID") - socketRef.current = io("https://daniela-nonprofessed-boastfully.ngrok-free.app", { + socketRef.current = io("https://quality-friendly-goldfish.ngrok-free.app", { transports: ["websocket"], secure: true, rejectUnauthorized: false, @@ -558,12 +560,56 @@ const DashboardLayout = () => { setIsConnected(true); // Start sending every 5 seconds + // intervalRef.current = setInterval(async () => { + // const apps = await window.electron.getRunningApps() + // socketRef.current?.emit("getVisibleApps", { token: await token, apps }); + // }, 5000); + }); + + // socketRef.current.on("startSendAppsDetails", async ({ apps }) => { + // socketRef.current?.emit("getVisibleApps", { token: await token, apps }); + // }); + + socketRef.current.on("startSendAppsDetails", async ({projId}) => { + if (projId !== selectedProject?.projectId) return; + console.log("▶️ startSendAppsDetails received"); + + // avoid multiple intervals + if (intervalRef.current) return; + intervalRef.current = setInterval(async () => { - const apps = await window.electron.getRunningApps() - socketRef.current?.emit("getVisibleApps", { token: await token, apps }); + const currentApps = await window.electron.getRunningApps(); + + // Compare with last sent data + const hasChanged = JSON.stringify(currentApps) !== JSON.stringify(lastAppsRef.current); + + if (hasChanged) { + lastAppsRef.current = currentApps; + + socketRef.current?.emit("getVisibleApps", { + token: await token, + apps: currentApps, + }); + } }, 5000); }); + socketRef.current.on("stopSendAppsDetails", async ({projId}) => { + if (projId !== selectedProject?.projectId) return; + console.log("⏹ stopSendAppsDetails received"); + + // clear the interval + if (intervalRef.current) { + clearInterval(intervalRef.current); + intervalRef.current = null; + } + + // Send final stop event to server + // socketRef.current?.emit("stopSendAppsDetails", { + // token: await token, + // }); + }); + socketRef.current.on("appToCapture", async ({ appTitle }) => { console.log("📸 Capture requested for:", appTitle); diff --git a/src/ui/pages/LeaveAndAdjustments/tabs/PtoLeaveTab.tsx b/src/ui/pages/LeaveAndAdjustments/tabs/PtoLeaveTab.tsx index e41ffbd..3b9a30a 100644 --- a/src/ui/pages/LeaveAndAdjustments/tabs/PtoLeaveTab.tsx +++ b/src/ui/pages/LeaveAndAdjustments/tabs/PtoLeaveTab.tsx @@ -198,7 +198,7 @@ const PtoLeaveTab: React.FC<{ if (result.success) { resetForm(); - alert("PTO submitted!"); + alert("PTO Request submitted!"); } else { setServerError(result.message || "Something went wrong"); } diff --git a/src/ui/pages/LeaveAndAdjustments/tabs/SickLeaveTab.tsx b/src/ui/pages/LeaveAndAdjustments/tabs/SickLeaveTab.tsx index 180e6ae..c64a501 100644 --- a/src/ui/pages/LeaveAndAdjustments/tabs/SickLeaveTab.tsx +++ b/src/ui/pages/LeaveAndAdjustments/tabs/SickLeaveTab.tsx @@ -188,7 +188,7 @@ const SickLeaveTab: React.FC<{ if (result.success) { resetForm(); - alert("SICK submitted!"); + alert("SICK leave applied!"); } else { setServerError(result.message || "Something went wrong"); } From df2b35a4e4832d02753dadcabf37a3fd8c6b4572 Mon Sep 17 00:00:00 2001 From: JaiPandey425 Date: Wed, 3 Dec 2025 16:17:10 +0530 Subject: [PATCH 09/10] leave and adjut. show both the table and tabs based table --- src/electron/ipc/ipcHandler.ts | 29 ++ src/electron/preload.cts | 9 +- .../LeaveAndAdjustments/AdjustmentsTable.tsx | 183 ++++++++++++ .../LeaveAndAdjustments.tsx | 278 ++++++++---------- .../pages/LeaveAndAdjustments/LeaveTable.tsx | 184 ++++++++++++ .../tabs/AdjustmentTab.tsx | 2 +- src/ui/pages/Timesheets/Timesheets.tsx | 4 +- types.d.ts | 7 +- 8 files changed, 533 insertions(+), 163 deletions(-) create mode 100644 src/ui/pages/LeaveAndAdjustments/AdjustmentsTable.tsx create mode 100644 src/ui/pages/LeaveAndAdjustments/LeaveTable.tsx diff --git a/src/electron/ipc/ipcHandler.ts b/src/electron/ipc/ipcHandler.ts index 759d285..8c50481 100644 --- a/src/electron/ipc/ipcHandler.ts +++ b/src/electron/ipc/ipcHandler.ts @@ -462,6 +462,35 @@ ipcMain.handle("get-leave-list", async () => { } }); +ipcMain.handle("get-adjustments-list", async () => { + + try { + const res = await api.get("/timesheet/adjustments/list"); + + console.log("/timesheet/adjustments/list", res.data); + return { + success: true, + data: res.data, + }; + } catch (error: any) { + let message = "Unknown error occurred"; + + if (error.response) { + console.log("Status:", error.response.status); + console.log("Message:", error.response.statusText); + console.log("Server Response:", error.response.data); + + message = error.response.data?.message || error.response.statusText; + } else { + message = error.message; + } + return { + success: false, + message + }; + } +}); + export const sendTokenExpired = () => { diff --git a/src/electron/preload.cts b/src/electron/preload.cts index 3ebc272..142079a 100644 --- a/src/electron/preload.cts +++ b/src/electron/preload.cts @@ -51,12 +51,13 @@ contextBridge.exposeInMainWorld('electron', { createTimesheet: (data: any) => ipcRenderer.invoke('create-timesheet', data), createPTOAndSickLeaveRequest: (data) => - ipcRenderer.invoke("create-leave-request", data), + ipcRenderer.invoke("create-leave-request", data), -getSchedules: () => ipcRenderer.invoke("get-schedules"), + getSchedules: () => ipcRenderer.invoke("get-schedules"), -getPayslips: (vaId: string) => ipcRenderer.invoke("get-payslips", vaId), - getLeaveList: () => ipcRenderer.invoke("get-leave-list") + getPayslips: (vaId: string) => ipcRenderer.invoke("get-payslips", vaId), + getLeaveList: () => ipcRenderer.invoke("get-leave-list"), + getAdjustmentsList:() => ipcRenderer.invoke("get-adjustments-list"), } satisfies Window['electron']); diff --git a/src/ui/pages/LeaveAndAdjustments/AdjustmentsTable.tsx b/src/ui/pages/LeaveAndAdjustments/AdjustmentsTable.tsx new file mode 100644 index 0000000..0e0e65d --- /dev/null +++ b/src/ui/pages/LeaveAndAdjustments/AdjustmentsTable.tsx @@ -0,0 +1,183 @@ +import React from "react"; +import { Loader2, RefreshCw } from "lucide-react"; +import { DateTime } from "luxon"; + +interface AdjustmentItem { + id: string; + date: string; + loginTime: string; + logoutTime: string; + comment: string; + status: string; + requestedAt: string; + adjustedHours: string; + project: { + id: string; + name: string; + }; +} + +interface AdjustmentTableProps { + isTracking: boolean; + adjustments: AdjustmentItem[]; + loading: boolean; + lastRefreshed: Date | null; + onRefresh: () => Promise; +} + +const AdjustmentsTable: React.FC = ({ + isTracking, + adjustments, + loading, + lastRefreshed, + onRefresh +}) => { + const [refreshing, setRefreshing] = React.useState(false); + + // 🌍 Convert UTC → Local System Time + const toLocal = (utcString: string, format: string) => { + return DateTime.fromISO(utcString, { zone: "utc" }) + .setZone(DateTime.local().zoneName) + .toFormat(format); + }; + + const formatTime = (t: string) => toLocal(t, "HH:mm:ss"); + const formatDate = (d: string) => toLocal(d, "yyyy-MM-dd"); + + const handleRefresh = async () => { + setRefreshing(true); + await onRefresh(); + setRefreshing(false); + }; + + const renderSkeleton = () => + Array.from({ length: 7 }).map((_, i) => ( + +
+
+
+
+
+
+
+ + )); + + return ( + <> +
+ + + + + + + + + + + + + + + {loading + ? renderSkeleton() + : adjustments.length === 0 + ? ( + + + + ) + : adjustments.map((item, idx) => ( + + + + + + + + + + + + + + + + ))} + + {/* {refreshing && adjustments.length > 0 && ( + + + + )} */} + +
#ProjectDateIn → OutAdjusted HoursStatusRequested At
+ No adjustments found +
{idx + 1} + + {item.project?.name || "N/A"} + + {formatDate(item.date)} + {formatTime(item.loginTime)} + → {" "} + {formatTime(item.logoutTime)} + + + {item.adjustedHours} + + + + {item.status} + + {toLocal(item.requestedAt, "yyyy-MM-dd HH:mm:ss")}
+
+ {renderSkeleton()} +
+
+
+ + {/* FOOTER */} +
+
+ {adjustments.length} adjustment{adjustments.length !== 1 ? "s" : ""} found +
+ +
+ {lastRefreshed && ( +
+ Last updated: {toLocal(lastRefreshed.toISOString(), "HH:mm:ss")} +
+ )} + + +
+
+ + ); +}; + +export default AdjustmentsTable; \ No newline at end of file diff --git a/src/ui/pages/LeaveAndAdjustments/LeaveAndAdjustments.tsx b/src/ui/pages/LeaveAndAdjustments/LeaveAndAdjustments.tsx index 1ab0085..c87ca3c 100644 --- a/src/ui/pages/LeaveAndAdjustments/LeaveAndAdjustments.tsx +++ b/src/ui/pages/LeaveAndAdjustments/LeaveAndAdjustments.tsx @@ -1,8 +1,9 @@ -import React, { useEffect, useState } from "react"; +import React, { useState, useEffect } from "react"; import { useNavigate, useOutletContext } from "react-router-dom"; import { TrackingState } from "../../layout/DashboardLayout"; -import { Loader2, RefreshCw } from "lucide-react"; -import { DateTime } from "luxon"; +import LeaveTable from "./LeaveTable"; +import AdjustmentsTable from "./AdjustmentsTable"; +import { useSelector } from "react-redux"; interface LeaveItem { leaveId: string; @@ -17,70 +18,104 @@ interface LeaveItem { rejectionReason: string | null; } +interface AdjustmentItem { + id: string; + date: string; + loginTime: string; + logoutTime: string; + comment: string; + status: string; + requestedAt: string; + adjustedHours: string; + project: { + id: string; + name: string; + }; +} + +interface ApiResponse { + success: boolean; + data?: T; +} + const LeaveAndAdjustments: React.FC = () => { const { isTracking } = useOutletContext(); const navigate = useNavigate(); + const { projectSummary } = useSelector((state: any) => state.projects); +console.log("projectSummary46" , projectSummary) + + // Tabs state + const [activeTab, setActiveTab] = useState<"leave" | "adjustments">("leave"); + + // Data states const [leaves, setLeaves] = useState([]); - const [loading, setLoading] = useState(true); - const [refreshing, setRefreshing] = useState(false); + const [adjustments, setAdjustments] = useState([]); + + // Loading states + const [loadingLeaves, setLoadingLeaves] = useState(true); + const [loadingAdjustments, setLoadingAdjustments] = useState(true); + + // Refresh state const [lastRefreshed, setLastRefreshed] = useState(null); - const fetchLeaves = async () => { - try { - const res = await window.electron.getLeaveList(); - - if (res.success) { - setLeaves(res?.data?.leaves || []); - } - setLastRefreshed(new Date()); - } catch (e) { - console.error("Error fetching leaves:", e); - } finally { - setLoading(false); - setRefreshing(false); - } - }; - - const handleRefresh = async () => { - setRefreshing(true); - await fetchLeaves(); - }; - - useEffect(() => { - fetchLeaves(); - }, []); - const handleApplyLeaveAndAdjust = () => { navigate("/dashboard/leave-adjustments/apply"); }; - const renderSkeletonRows = () => { - return Array.from({ length: 5 }).map((_, index) => ( - -
-
-
-
-
-
- - )); + // Fetch leaves data + const fetchLeaves = async () => { + setLoadingLeaves(true); + try { + const res: ApiResponse<{ leaves: LeaveItem[] }> = await window.electron.getLeaveList(); + if (res.success) { + setLeaves(res?.data?.leaves || []); + } + } catch (e) { + console.error("Error fetching leaves:", e); + } finally { + setLoadingLeaves(false); + } }; - const formatLastRefreshed = () => { - if (!lastRefreshed) return "Never"; - - return DateTime.fromJSDate(lastRefreshed) - .toLocal() - .toFormat("HH:mm:ss"); // Only time - }; + // Fetch adjustments data + const fetchAdjustments = async () => { + setLoadingAdjustments(true); + try { + const res: ApiResponse = await window.electron.getAdjustmentsList(); + if (res.success) { + setAdjustments(res.data || []); + } + } catch (err) { + console.error("Error fetching adjustments:", err); + } finally { + setLoadingAdjustments(false); + } + }; + + // Combined refresh function + const handleRefresh = async () => { + if (activeTab === "leave") { + await fetchLeaves(); + } else { + await fetchAdjustments(); + } + setLastRefreshed(new Date()); + }; + + // Initial data fetch + useEffect(() => { + fetchLeaves(); + fetchAdjustments(); + setLastRefreshed(new Date()); + }, []); + return (
{/* Header */}
@@ -90,115 +125,48 @@ const LeaveAndAdjustments: React.FC = () => {
- {/* Table Container */} -
- - - - - - - - - - - + {/* Tabs */} +
+ -
- {loading ? ( - renderSkeletonRows() - ) : refreshing && leaves.length === 0 ? ( - renderSkeletonRows() - ) : leaves.length > 0 ? ( - leaves.map((leave, index) => ( - - - - - - - - - - - - - - )) - ) : ( - - - - )} - - {refreshing && leaves.length > 0 && ( - - - - )} - -
#Leave TypeDate From → ToStatusTotal DaysApplied On
{index + 1} - {leave.leaveType} - - {leave.dateFrom} -
- {leave.dateTo} -
- - {leave.status} - - {leave.totalDays}{new Date(leave.appliedOn).toLocaleString()}
- No leaves found -
-
- - Refreshing... -
-
+
- {/* Footer */} -
-
- {leaves.length} leave{leaves.length !== 1 ? "s" : ""} found -
- -
- {lastRefreshed && ( -
- Last updated: {formatLastRefreshed()} -
- )} - - -
+ {/* TABLE AREA - Scrollable */} +
+ {activeTab === "leave" && ( + + )} + {activeTab === "adjustments" && ( + + )}
); }; -export default LeaveAndAdjustments; +export default LeaveAndAdjustments; \ No newline at end of file diff --git a/src/ui/pages/LeaveAndAdjustments/LeaveTable.tsx b/src/ui/pages/LeaveAndAdjustments/LeaveTable.tsx new file mode 100644 index 0000000..8ffd576 --- /dev/null +++ b/src/ui/pages/LeaveAndAdjustments/LeaveTable.tsx @@ -0,0 +1,184 @@ +import React from "react"; +import { Loader2, RefreshCw } from "lucide-react"; +import { DateTime } from "luxon"; + +interface LeaveItem { + leaveId: string; + dateFrom: string; + dateTo: string; + totalDays: number; + leaveType: string; + comment: string; + status: string; + appliedOn: string; + reviewedAt: string | null; + rejectionReason: string | null; +} + +interface LeaveTableProps { + isTracking: boolean; + leaves: LeaveItem[]; + loading: boolean; + lastRefreshed: Date | null; + onRefresh: () => Promise; +} + +const LeaveTable: React.FC = ({ + isTracking, + leaves, + loading, + lastRefreshed, + onRefresh +}) => { + const [refreshing, setRefreshing] = React.useState(false); + + // 🌍 Convert UTC → Local System Time + const toLocal = (utcString: string, format: string) => { + return DateTime.fromISO(utcString, { zone: "utc" }) + .setZone(DateTime.local().zoneName) + .toFormat(format); + }; + + const formatDate = (d: string) => toLocal(d, "yyyy-MM-dd"); + const formatDateTime = (d: string) => toLocal(d, "yyyy-MM-dd HH:mm:ss"); + + const handleRefresh = async () => { + setRefreshing(true); + await onRefresh(); + setRefreshing(false); + }; + + const renderSkeletonRows = () => + Array.from({ length: 5 }).map((_, index) => ( + +
+
+
+
+
+
+ + )); + + const formatLastRefreshed = () => + lastRefreshed + ? DateTime.fromJSDate(lastRefreshed) + .setZone(DateTime.local().zoneName) + .toFormat("HH:mm:ss") + : "Never"; + + return ( + <> + {/* Table Container */} +
+ + + + + + + + + + + + + + {loading ? ( + renderSkeletonRows() + ) : refreshing && leaves.length === 0 ? ( + renderSkeletonRows() + ) : leaves.length > 0 ? ( + leaves.map((leave, index) => ( + + + + + + + + + + + + + + )) + ) : ( + + + + )} + + {/* {!refreshing && leaves.length > 0 && ( + + + + )} */} + +
#Leave TypeDate From → ToStatusTotal DaysApplied On
{index + 1} + + {leave.leaveType} + + + {formatDate(leave.dateFrom)} + → {" "} + {formatDate(leave.dateTo)} + + + {leave.status} + + {leave.totalDays}{formatDateTime(leave.appliedOn)}
+ No leaves found +
+
+ {renderSkeletonRows()} +
+
+
+ + {/* Footer */} +
+
+ {leaves.length} leave{leaves.length !== 1 ? "s" : ""} found +
+ +
+ {lastRefreshed && ( +
+ Last updated: {formatLastRefreshed()} +
+ )} + + +
+
+ + ); +}; + +export default LeaveTable; \ No newline at end of file diff --git a/src/ui/pages/LeaveAndAdjustments/tabs/AdjustmentTab.tsx b/src/ui/pages/LeaveAndAdjustments/tabs/AdjustmentTab.tsx index fb85198..516127f 100644 --- a/src/ui/pages/LeaveAndAdjustments/tabs/AdjustmentTab.tsx +++ b/src/ui/pages/LeaveAndAdjustments/tabs/AdjustmentTab.tsx @@ -662,7 +662,7 @@ const AdjustmentTab: React.FC = ({ projects }) => { diff --git a/src/ui/pages/Timesheets/Timesheets.tsx b/src/ui/pages/Timesheets/Timesheets.tsx index 98e893f..a73d773 100644 --- a/src/ui/pages/Timesheets/Timesheets.tsx +++ b/src/ui/pages/Timesheets/Timesheets.tsx @@ -133,13 +133,13 @@ const Timesheets: React.FC = () => { setLogs([]); } - + } catch (error) { console.error("Error fetching logs:", error); setModalOpen(false) } finally { setLoading(false); - + } }; diff --git a/types.d.ts b/types.d.ts index 6eb395d..495aa44 100644 --- a/types.d.ts +++ b/types.d.ts @@ -83,7 +83,7 @@ interface ProfileData { paymentMethod: string; payoneerAccount: string; veemAccount: string; - wiseEmail:string + wiseEmail: string _id: string; } @@ -217,6 +217,11 @@ interface Window { success: boolean; data?: any; }>; + + getAdjustmentsList: () => Promise<{ + success: boolean; + data?: any; + }>; }; } From 9eeb14ffcd64a10371fd077e85d449912e31f903 Mon Sep 17 00:00:00 2001 From: JaiPandey425 Date: Thu, 4 Dec 2025 11:43:00 +0530 Subject: [PATCH 10/10] Change the Dashboard Layout when times is up show the alert window but time still run when user handleStop then only stop --- src/electron/ipc/ipcHandler.ts | 4 +- src/electron/preload.cts | 2 +- src/ui/layout/DashboardLayout.tsx | 41 +++++++++++++------ .../LeaveAndAdjustments.tsx | 2 +- src/ui/pages/Timesheets/Timesheets.tsx | 5 ++- types.d.ts | 2 +- 6 files changed, 36 insertions(+), 20 deletions(-) diff --git a/src/electron/ipc/ipcHandler.ts b/src/electron/ipc/ipcHandler.ts index 8c50481..8d1b67f 100644 --- a/src/electron/ipc/ipcHandler.ts +++ b/src/electron/ipc/ipcHandler.ts @@ -462,10 +462,10 @@ ipcMain.handle("get-leave-list", async () => { } }); -ipcMain.handle("get-adjustments-list", async () => { +ipcMain.handle("get-adjustments-list", async (event, vaId: string) => { try { - const res = await api.get("/timesheet/adjustments/list"); + const res = await api.get(`/timesheet/adjustments/list?vaId=${vaId}`); console.log("/timesheet/adjustments/list", res.data); return { diff --git a/src/electron/preload.cts b/src/electron/preload.cts index 142079a..668496d 100644 --- a/src/electron/preload.cts +++ b/src/electron/preload.cts @@ -57,7 +57,7 @@ contextBridge.exposeInMainWorld('electron', { getPayslips: (vaId: string) => ipcRenderer.invoke("get-payslips", vaId), getLeaveList: () => ipcRenderer.invoke("get-leave-list"), - getAdjustmentsList:() => ipcRenderer.invoke("get-adjustments-list"), + getAdjustmentsList:(vaId: string) => ipcRenderer.invoke("get-adjustments-list",vaId), } satisfies Window['electron']); diff --git a/src/ui/layout/DashboardLayout.tsx b/src/ui/layout/DashboardLayout.tsx index 3f0f0f2..259550e 100644 --- a/src/ui/layout/DashboardLayout.tsx +++ b/src/ui/layout/DashboardLayout.tsx @@ -138,6 +138,7 @@ const DashboardLayout = () => { // Create a ref to store the stop function so it can be called from the hook const stopTrackingRef = useRef<(() => Promise) | null>(null); + const limitAlertShownRef = useRef(false); // Define handleStopTracking FIRST const handleStopTracking = useCallback(async () => { @@ -148,6 +149,9 @@ const DashboardLayout = () => { return; } + // Reset alert flag when stopping + limitAlertShownRef.current = false; + let currentStopTime = new Date().toISOString(); const projectId = selectedProject.projectId; @@ -282,25 +286,34 @@ const DashboardLayout = () => { // NOW define handleLimitExceeded using the defined handleStopTracking const handleLimitExceeded = useCallback(async () => { - console.log("🚨 LIMIT EXCEEDED - Auto-stopping tracking"); + console.log("🚨 LIMIT EXCEEDED - Showing alert (not stopping)"); if (!selectedProject) { - console.error(" No selected project for limit exceeded"); + console.error("❌ No selected project for limit exceeded"); return; } - console.log("Alert Message 1") + // Only show alert once per tracking session + if (limitAlertShownRef.current) { + console.log("⚠️ Alert already shown, skipping"); + return; + } + + limitAlertShownRef.current = true; + + console.log("⏰ Showing limit exceeded alert"); + // Show alert to user alert( `⏰ Daily Limit Reached!\n\n` + `Project: ${selectedProject.projectName}\n` + `Dedicated Hours: ${selectedProject.dedicatedHours}\n\n` + - `Your tracking has been stopped automatically because you've reached the daily time limit for this project.` + `You've reached the daily time limit for this project.\n` + + `Timer will continue running. Please stop manually when ready.` ); - console.log("Alert Message 2") - // Stop tracking - // await handleStopTracking(); - }, [selectedProject, handleStopTracking]); + + console.log("✅ Alert shown, timer continues running"); + }, [selectedProject]); // Initialize useActiveAppTracker and store finalizeTracking in ref const { finalizeTracking } = useActiveAppTracker( @@ -384,6 +397,8 @@ const DashboardLayout = () => { console.log(`⏰ Starting tracking. Remaining time: ${limitCheck.remaining}`); + limitAlertShownRef.current = false; + setLoadingStart(true); const now = Date.now(); @@ -468,15 +483,15 @@ const DashboardLayout = () => { // Check if time limit reached const totalTimeWorked = alreadyWorkedSeconds + timeSinceStart; - if (totalTimeWorked >= dedicatedSeconds) { + if (totalTimeWorked >= dedicatedSeconds && !limitAlertShownRef.current) { console.warn("🚨 CLIENT-SIDE: Time limit reached!"); console.log(`Total worked: ${Math.floor(totalTimeWorked / 60)}m vs Limit: ${Math.floor(dedicatedSeconds / 60)}m`); // Clear timer first to prevent multiple triggers - if (timerRef.current) { - clearInterval(timerRef.current); - timerRef.current = null; - } + // if (timerRef.current) { + // clearInterval(timerRef.current); + // timerRef.current = null; + // } // Trigger auto-stop handleLimitExceeded(); diff --git a/src/ui/pages/LeaveAndAdjustments/LeaveAndAdjustments.tsx b/src/ui/pages/LeaveAndAdjustments/LeaveAndAdjustments.tsx index c87ca3c..e6e728f 100644 --- a/src/ui/pages/LeaveAndAdjustments/LeaveAndAdjustments.tsx +++ b/src/ui/pages/LeaveAndAdjustments/LeaveAndAdjustments.tsx @@ -82,7 +82,7 @@ console.log("projectSummary46" , projectSummary) const fetchAdjustments = async () => { setLoadingAdjustments(true); try { - const res: ApiResponse = await window.electron.getAdjustmentsList(); + const res: ApiResponse = await window.electron.getAdjustmentsList(projectSummary?.vaId); if (res.success) { setAdjustments(res.data || []); } diff --git a/src/ui/pages/Timesheets/Timesheets.tsx b/src/ui/pages/Timesheets/Timesheets.tsx index a73d773..b6cc269 100644 --- a/src/ui/pages/Timesheets/Timesheets.tsx +++ b/src/ui/pages/Timesheets/Timesheets.tsx @@ -86,6 +86,7 @@ const Timesheets: React.FC = () => { const fetchProjects = async () => { + setInitialLoading(true); try { console.log("📡 Fetching projects..."); const res = await window.electron.getProjectList(); @@ -241,7 +242,7 @@ const Timesheets: React.FC = () => { )) )} - {refreshing && projectsData.length > 0 && ( + {/* {refreshing && projectsData.length > 0 && ( // Show a single skeleton row at the top while refreshing with existing data @@ -251,7 +252,7 @@ const Timesheets: React.FC = () => { - )} + )} */} diff --git a/types.d.ts b/types.d.ts index 495aa44..51df381 100644 --- a/types.d.ts +++ b/types.d.ts @@ -218,7 +218,7 @@ interface Window { data?: any; }>; - getAdjustmentsList: () => Promise<{ + getAdjustmentsList: (vaId: string) => Promise<{ success: boolean; data?: any; }>;