Compare commits
16 Commits
52e45f8328
...
f5f94f016b
| Author | SHA1 | Date |
|---|---|---|
|
|
f5f94f016b | |
|
|
93704a70f6 | |
|
|
ed0bcb7107 | |
|
|
3ef615a160 | |
|
|
b55f9469ad | |
|
|
d23933c517 | |
|
|
92e8c15023 | |
|
|
35eb073f46 | |
|
|
94eedff8af | |
|
|
ac3ddf4b2b | |
|
|
a55e6ccba4 | |
|
|
bd4a3656e7 | |
|
|
345822885f | |
|
|
0ae8f6185a | |
|
|
882e8349c9 | |
|
|
d641e9432d |
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
|
@ -4,26 +4,59 @@
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
font-family: Arial, sans-serif;
|
font-family: Arial, sans-serif;
|
||||||
}
|
}
|
||||||
.loader {
|
|
||||||
width: 50px;
|
.loader-container {
|
||||||
height: 50px;
|
position: relative;
|
||||||
border: 5px solid #e0e0e0;
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Circular spinner */
|
||||||
|
.loader-ring {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: 6px solid #e0e0e0;
|
||||||
border-top-color: #6a5acd;
|
border-top-color: #6a5acd;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
animation: spin 1s linear infinite;
|
animation: spin 1s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Logo in center */
|
||||||
|
.loader-logo {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader-logo img {
|
||||||
|
width: 45px;
|
||||||
|
height: 45px;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
to { transform: rotate(360deg); }
|
to { transform: rotate(360deg); }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="loader"></div>
|
<div class="loader-container">
|
||||||
|
<div class="loader-ring"></div>
|
||||||
|
|
||||||
|
<!-- Place your logo here -->
|
||||||
|
<div class="loader-logo">
|
||||||
|
<img src="your-logo.png" alt="App Logo">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ const httpsAgent = new https.Agent({
|
||||||
});
|
});
|
||||||
|
|
||||||
const api: AxiosInstance = axios.create({
|
const api: AxiosInstance = axios.create({
|
||||||
baseURL: 'http://localhost:3000/api/v1/va', // 🔁 your backend
|
// https://dreadingly-unfighting-pandora.ngrok-free.dev/
|
||||||
|
baseURL: 'https://dreadingly-unfighting-pandora.ngrok-free.dev/api/v1/va', // 🔁 your backend
|
||||||
httpsAgent
|
httpsAgent
|
||||||
// timeout: 10000,
|
// timeout: 10000,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -78,10 +78,6 @@ app.on("ready", async () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Create the main window
|
// Create the main window
|
||||||
const win = new BrowserWindow({
|
const win = new BrowserWindow({
|
||||||
width: 500,
|
width: 500,
|
||||||
|
|
@ -91,8 +87,8 @@ app.on("ready", async () => {
|
||||||
minimizable: false,
|
minimizable: false,
|
||||||
maximizable: false,
|
maximizable: false,
|
||||||
closable: true,
|
closable: true,
|
||||||
alwaysOnTop: true,
|
// alwaysOnTop: true,
|
||||||
skipTaskbar: true,
|
// skipTaskbar: true,
|
||||||
autoHideMenuBar: true,
|
autoHideMenuBar: true,
|
||||||
frame: true,
|
frame: true,
|
||||||
show: false,
|
show: false,
|
||||||
|
|
@ -105,9 +101,24 @@ app.on("ready", async () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
win.setAlwaysOnTop(true, 'screen-saver');
|
|
||||||
win.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });
|
|
||||||
win.setFullScreenable(false);
|
// win.setAlwaysOnTop(true, 'screen-saver');
|
||||||
|
// win.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });
|
||||||
|
// win.setFullScreenable(false);
|
||||||
|
|
||||||
|
// Cross-platform safe always-on-top
|
||||||
|
const isMac = process.platform === "darwin";
|
||||||
|
|
||||||
|
if (isMac) {
|
||||||
|
win.setAlwaysOnTop(true, "floating"); // best for macOS
|
||||||
|
win.setVisibleOnAllWorkspaces(true); // NO fullscreen override
|
||||||
|
} else {
|
||||||
|
win.setAlwaysOnTop(true, "normal"); // safest for Windows + Zoom
|
||||||
|
win.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });
|
||||||
|
win.setFullScreenable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
mainWindow = win;
|
mainWindow = win;
|
||||||
|
|
||||||
|
|
@ -149,7 +160,7 @@ app.on("ready", async () => {
|
||||||
// ------------------------------------
|
// ------------------------------------
|
||||||
// HANDLE CLOSE EVENTS
|
// HANDLE CLOSE EVENTS
|
||||||
// ------------------------------------
|
// ------------------------------------
|
||||||
handleCloseEvents(win);
|
// handleCloseEvents(win);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -173,28 +184,28 @@ app.on("ready", async () => {
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
// CUSTOM CLOSE BEHAVIOR
|
// CUSTOM CLOSE BEHAVIOR
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
function handleCloseEvents(window: BrowserWindow) {
|
// function handleCloseEvents(window: BrowserWindow) {
|
||||||
let willClose = false;
|
// let willClose = false;
|
||||||
|
|
||||||
window.on("close", (e) => {
|
// window.on("close", (e) => {
|
||||||
if (willClose) return;
|
// if (willClose) return;
|
||||||
|
|
||||||
e.preventDefault();
|
// e.preventDefault();
|
||||||
window.hide();
|
// window.hide();
|
||||||
|
|
||||||
if (app.dock) {
|
// if (app.dock) {
|
||||||
app.dock.hide();
|
// app.dock.hide();
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
app.on("before-quit", () => {
|
// app.on("before-quit", () => {
|
||||||
willClose = true;
|
// willClose = true;
|
||||||
});
|
// });
|
||||||
|
|
||||||
window.on("show", () => {
|
// window.on("show", () => {
|
||||||
willClose = false;
|
// willClose = false;
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
// GET MAIN WINDOW EXPORT
|
// GET MAIN WINDOW EXPORT
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ export const handleLogin = async (credentials: { email: string, password: string
|
||||||
try {
|
try {
|
||||||
console.log("➡️ Calling API...");
|
console.log("➡️ Calling API...");
|
||||||
const response = await axios.post(
|
const response = await axios.post(
|
||||||
'http://localhost:3000/api/v1/va/login',
|
'https://dreadingly-unfighting-pandora.ngrok-free.dev/api/v1/va/login',
|
||||||
{ userDetails: { ...credentials } },
|
{ userDetails: { ...credentials } },
|
||||||
{ headers: { "ngrok-skip-browser-warning": 69420 }, httpsAgent }
|
{ headers: { "ngrok-skip-browser-warning": 69420 }, httpsAgent }
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -331,6 +331,7 @@ const DashboardLayout = () => {
|
||||||
|
|
||||||
const socketRef = useRef<Socket | null>(null);
|
const socketRef = useRef<Socket | null>(null);
|
||||||
const intervalRef = useRef<number | null>(null);
|
const intervalRef = useRef<number | null>(null);
|
||||||
|
const lastAppsRef = useRef<Object | null>(null);
|
||||||
const [isConnected, setIsConnected] = useState(false);
|
const [isConnected, setIsConnected] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -540,6 +541,7 @@ const DashboardLayout = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleSocketConnect = async () => {
|
const handleSocketConnect = async () => {
|
||||||
|
|
||||||
const token = await window.electron.getToken("ACCESS_TOKEN");
|
const token = await window.electron.getToken("ACCESS_TOKEN");
|
||||||
|
|
@ -573,12 +575,56 @@ const DashboardLayout = () => {
|
||||||
setIsConnected(true);
|
setIsConnected(true);
|
||||||
|
|
||||||
// Start sending every 5 seconds
|
// 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 () => {
|
intervalRef.current = setInterval(async () => {
|
||||||
const apps = await window.electron.getRunningApps()
|
const currentApps = await window.electron.getRunningApps();
|
||||||
socketRef.current?.emit("getVisibleApps", { token: await token, apps });
|
|
||||||
|
// 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);
|
}, 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 }) => {
|
socketRef.current.on("appToCapture", async ({ appTitle }) => {
|
||||||
console.log("📸 Capture requested for:", appTitle);
|
console.log("📸 Capture requested for:", appTitle);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -200,7 +200,7 @@ const PtoLeaveTab: React.FC<{
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
resetForm();
|
resetForm();
|
||||||
alert("PTO submitted!");
|
alert("PTO Request submitted!");
|
||||||
navigate('/dashboard/leave-adjustments')
|
navigate('/dashboard/leave-adjustments')
|
||||||
} else {
|
} else {
|
||||||
setServerError(result.message || "Something went wrong");
|
setServerError(result.message || "Something went wrong");
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,33 @@ const SickLeaveTab: React.FC<{
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const editor = quillRef.current?.getEditor();
|
||||||
|
|
||||||
|
if (!editor) return;
|
||||||
|
|
||||||
|
editor.getModule("clipboard").addMatcher(Node.ELEMENT_NODE, (node: any, delta: any) => {
|
||||||
|
return delta;
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.root.addEventListener("paste", async (e: any) => {
|
||||||
|
const items = e.clipboardData?.items;
|
||||||
|
|
||||||
|
if (!items) return;
|
||||||
|
|
||||||
|
for (const item of items) {
|
||||||
|
if (item.type.indexOf("image") === 0) {
|
||||||
|
const file = item.getAsFile();
|
||||||
|
const base64 = await convertToBase64(file);
|
||||||
|
|
||||||
|
const range = editor.getSelection();
|
||||||
|
editor.insertEmbed(range.index, "image", base64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
const validateLeaveForm = (data: LeaveFormData): Partial<LeaveFormData> => {
|
const validateLeaveForm = (data: LeaveFormData): Partial<LeaveFormData> => {
|
||||||
const errors: Partial<LeaveFormData> = {};
|
const errors: Partial<LeaveFormData> = {};
|
||||||
|
|
||||||
|
|
@ -161,7 +188,6 @@ const SickLeaveTab: React.FC<{
|
||||||
toDate: "",
|
toDate: "",
|
||||||
comment: ""
|
comment: ""
|
||||||
};
|
};
|
||||||
|
|
||||||
setSickLeave(emptyForm);
|
setSickLeave(emptyForm);
|
||||||
setSickErrors({});
|
setSickErrors({});
|
||||||
|
|
||||||
|
|
@ -190,7 +216,7 @@ const SickLeaveTab: React.FC<{
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
resetForm();
|
resetForm();
|
||||||
alert("SICK submitted!");
|
alert("SICK leave applied!");
|
||||||
navigate('/dashboard/leave-adjustments')
|
navigate('/dashboard/leave-adjustments')
|
||||||
} else {
|
} else {
|
||||||
setServerError(result.message || "Something went wrong");
|
setServerError(result.message || "Something went wrong");
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,13 @@
|
||||||
|
declare module "quill";
|
||||||
|
declare module "quill-image-resize-module";
|
||||||
|
declare module "quill-image-drop-module";
|
||||||
|
|
||||||
|
declare module "quill-image-resize-module-react" {
|
||||||
|
const ImageResize: any;
|
||||||
|
export default ImageResize;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
type Statistics = {
|
type Statistics = {
|
||||||
cpuUsage: number;
|
cpuUsage: number;
|
||||||
ramUsage: number;
|
ramUsage: number;
|
||||||
|
|
@ -144,14 +154,16 @@ interface Window {
|
||||||
vaId: string;
|
vaId: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
type: string;
|
type: string;
|
||||||
|
time:string
|
||||||
}) => Promise<any>;
|
}) => Promise<any>;
|
||||||
stopProjectTracking: (data: {
|
stopProjectTracking: (data: {
|
||||||
vaId: string;
|
vaId: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
type: string;
|
type: string;
|
||||||
|
time:string
|
||||||
}) => Promise<any>;
|
}) => Promise<any>;
|
||||||
trackingTabsOpeningClosing: (data: {
|
trackingTabsOpeningClosing: (data: {
|
||||||
id?: string; // optional, only present if updating
|
id?: string;
|
||||||
logId: string;
|
logId: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue