diff --git a/src/electron/ipc/ipcHandler.ts b/src/electron/ipc/ipcHandler.ts
index bc14fc3..e496bff 100644
--- a/src/electron/ipc/ipcHandler.ts
+++ b/src/electron/ipc/ipcHandler.ts
@@ -342,26 +342,44 @@ ipcMain.handle("create-timesheet", async (event, data) => {
ipcMain.handle("create-leave-request", async (event, payload) => {
try {
- console.log(" Leave Request Received:", payload);
+ console.log("Leave Request Received:", payload);
const response = await api.post("/leave/request", payload);
- console.log(" Leave Saved:", response.data);
+ console.log("Leave Saved:", response);
- return { success: true, data: response.data };
+ return {
+ success: true,
+ data: response.data
+ };
- } catch (error) {
- console.error("❌ Leave Save Failed:", error);
+ } catch (error: any) {
+ console.error("❌ Leave Save Failed:");
+
+ let message = "Unknown error";
+
+ if (error.response) {
+ console.log("Status:", error.response.status);
+ console.log("Message:", error.response.statusText);
+ console.log("Server Response:", error.response.data);
+
+ // Extract the backend message properly
+ message = error.response.data?.message || error.response.statusText;
+ } else {
+ message = error.message;
+ }
return {
success: false,
- error: error || "Unknown error"
+ message
};
}
});
+
+
export const sendTokenExpired = () => {
console.log("⚠️ Sending token-expired event to renderer");
diff --git a/src/ui/pages/LeaveAndAdjustments/LeaveAndAdjustments.tsx b/src/ui/pages/LeaveAndAdjustments/LeaveAndAdjustments.tsx
index c3c9716..8c71d47 100644
--- a/src/ui/pages/LeaveAndAdjustments/LeaveAndAdjustments.tsx
+++ b/src/ui/pages/LeaveAndAdjustments/LeaveAndAdjustments.tsx
@@ -25,16 +25,19 @@ const LeaveAndAdjustments: React.FC = () => {
console.log("Leave /sick Payload:", payload);
- return
-
const response = await window.electron.createPTOAndSickLeaveRequest(payload)
- console.log("Leave Payload:" ,response)
+ console.log("Leave Payload:", response)
if (response.success) {
- return true;
+ return { success: true };
}
+
+ return {
+ success: false,
+ message: response.message
+ };
} catch (err) {
console.error(err);
- return false;
+ return { success: false, message: "Request failed" };
} finally {
setIsSubmitting(false);
}
@@ -49,9 +52,8 @@ const LeaveAndAdjustments: React.FC = () => {
setActiveTab("pto")}
>
PTO Leave
@@ -59,9 +61,8 @@ const LeaveAndAdjustments: React.FC = () => {
setActiveTab("sick")}
>
Sick Leave
@@ -69,9 +70,8 @@ const LeaveAndAdjustments: React.FC = () => {
setActiveTab("adjustment")}
>
Adjustment Submission
diff --git a/src/ui/pages/LeaveAndAdjustments/tabs/AdjustmentTab.tsx b/src/ui/pages/LeaveAndAdjustments/tabs/AdjustmentTab.tsx
index 4dcf8a3..f923f9a 100644
--- a/src/ui/pages/LeaveAndAdjustments/tabs/AdjustmentTab.tsx
+++ b/src/ui/pages/LeaveAndAdjustments/tabs/AdjustmentTab.tsx
@@ -222,28 +222,96 @@ const AdjustmentTab: React.FC = ({ projects }) => {
useEffect(() => {
const editor = quillRef.current?.getEditor();
-
if (!editor) return;
- editor.getModule("clipboard").addMatcher(Node.ELEMENT_NODE, (node: any, delta: any) => {
- return delta;
- });
+ // Override image handler in toolbar
+ editor.getModule("toolbar").handlers.image = () => {
+ const input = document.createElement("input");
+ input.setAttribute("type", "file");
+ input.setAttribute("accept", "image/*");
+ input.click();
- 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();
+ input.onchange = async () => {
+ const file = input.files?.[0];
+ if (file) {
const base64 = await convertToBase64(file);
-
const range = editor.getSelection();
- editor.insertEmbed(range.index, "image", base64);
+ if (range) {
+ editor.insertEmbed(range.index, "image", base64);
+ }
+ }
+ };
+ };
+
+ // COMPLETELY disable Quill's default paste behavior
+ const disableQuillPaste = () => {
+ // Remove all existing paste event listeners from Quill
+ const pasteListeners = (editor.root as any).__pasteHandlers || [];
+ pasteListeners.forEach((handler: any) => {
+ editor.root.removeEventListener('paste', handler);
+ });
+
+ // Override the clipboard module to do nothing
+ const clipboard = editor.getModule('clipboard');
+ const originalPaste = clipboard.paste;
+ clipboard.paste = (e: any) => {
+ // Completely prevent Quill's default paste behavior
+ e.preventDefault();
+ e.stopPropagation();
+ return false;
+ };
+ };
+
+ disableQuillPaste();
+
+ // Our custom paste handler - this is the ONLY handler that will run
+ const handlePaste = async (e: ClipboardEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ const items = e.clipboardData?.items;
+ if (!items || items.length === 0) return;
+
+ // Check for images first
+ for (let i = 0; i < items.length; i++) {
+ const item = items[i];
+ if (item.type.startsWith("image/")) {
+ const file = item.getAsFile();
+ if (file) {
+ try {
+ const base64 = await convertToBase64(file);
+ const range = editor.getSelection(true);
+ if (range !== null) {
+ // Insert the image
+ editor.insertEmbed(range.index, "image", base64);
+ // Move cursor after the image
+ editor.setSelection(range.index + 1, 0);
+ }
+ } catch (error) {
+ console.error("Error processing pasted image:", error);
+ }
+ }
+ return; // Exit after handling the first image
}
}
- });
+
+ // If no images found, handle text
+ const text = e.clipboardData?.getData('text/plain');
+ if (text) {
+ const range = editor.getSelection(true);
+ if (range !== null) {
+ editor.insertText(range.index, text);
+ editor.setSelection(range.index + text.length, 0);
+ }
+ }
+ };
+
+ // Add our custom paste handler with high priority
+ editor.root.addEventListener("paste", handlePaste, true);
+
+ return () => {
+ editor.root.removeEventListener("paste", handlePaste, true);
+ };
}, []);
const convertToBase64 = (file: File): Promise =>
@@ -571,14 +639,14 @@ const AdjustmentTab: React.FC = ({ projects }) => {
modules={quillModules}
onChange={handleDescription}
className="h-40"
- // placeholder="Please enter the task name or leave notes to help us better understand your request."
+ // placeholder="Please enter the task name or leave notes to help us better understand your request."
/>
- {formErrors.comment && (
-
- )}
+ {formErrors.comment && (
+
+ )}
{/* Submit Button */}
diff --git a/src/ui/pages/LeaveAndAdjustments/tabs/PtoLeaveTab.tsx b/src/ui/pages/LeaveAndAdjustments/tabs/PtoLeaveTab.tsx
index 74541c2..ba531d8 100644
--- a/src/ui/pages/LeaveAndAdjustments/tabs/PtoLeaveTab.tsx
+++ b/src/ui/pages/LeaveAndAdjustments/tabs/PtoLeaveTab.tsx
@@ -3,7 +3,7 @@ import React, { useEffect, useRef, useState } from "react";
import { quillModules } from "../../../utils/quillConfig";
import ReactQuill from "react-quill-new";
import Quill from "quill";
-import "quill/dist/quill.snow.css"; // correct CSS
+import "quill/dist/quill.snow.css";
interface LeaveFormData {
fromDate: string;
@@ -21,43 +21,114 @@ const PtoLeaveTab: React.FC<{
toDate: "",
comment: "",
});
+ const [serverError, setServerError] = useState("");
const quillRef = useRef
(null);
const [errors, setErrors] = useState>({});
useEffect(() => {
const editor = quillRef.current?.getEditor();
-
if (!editor) return;
- editor.getModule("clipboard").addMatcher(Node.ELEMENT_NODE, (node: any, delta: any) => {
- return delta;
- });
+ // Override image handler in toolbar
+ editor.getModule("toolbar").handlers.image = () => {
+ const input = document.createElement("input");
+ input.setAttribute("type", "file");
+ input.setAttribute("accept", "image/*");
+ input.click();
- 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();
+ input.onchange = async () => {
+ const file = input.files?.[0];
+ if (file) {
const base64 = await convertToBase64(file);
-
const range = editor.getSelection();
- editor.insertEmbed(range.index, "image", base64);
+ if (range) {
+ editor.insertEmbed(range.index, "image", base64);
+ }
+ }
+ };
+ };
+
+ // COMPLETELY disable Quill's default paste behavior
+ const disableQuillPaste = () => {
+ // Remove all existing paste event listeners from Quill
+ const pasteListeners = (editor.root as any).__pasteHandlers || [];
+ pasteListeners.forEach((handler: any) => {
+ editor.root.removeEventListener('paste', handler);
+ });
+
+ // Override the clipboard module to do nothing
+ const clipboard = editor.getModule('clipboard');
+ const originalPaste = clipboard.paste;
+ clipboard.paste = (e: any) => {
+ // Completely prevent Quill's default paste behavior
+ e.preventDefault();
+ e.stopPropagation();
+ return false;
+ };
+ };
+
+ disableQuillPaste();
+
+ // Our custom paste handler - this is the ONLY handler that will run
+ const handlePaste = async (e: ClipboardEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ const items = e.clipboardData?.items;
+ if (!items || items.length === 0) return;
+
+ // Check for images first
+ for (let i = 0; i < items.length; i++) {
+ const item = items[i];
+ if (item.type.startsWith("image/")) {
+ const file = item.getAsFile();
+ if (file) {
+ try {
+ const base64 = await convertToBase64(file);
+ const range = editor.getSelection(true);
+ if (range !== null) {
+ // Insert the image
+ editor.insertEmbed(range.index, "image", base64);
+ // Move cursor after the image
+ editor.setSelection(range.index + 1, 0);
+ }
+ } catch (error) {
+ console.error("Error processing pasted image:", error);
+ }
+ }
+ return; // Exit after handling the first image
}
}
- });
+
+ // If no images found, handle text
+ const text = e.clipboardData?.getData('text/plain');
+ if (text) {
+ const range = editor.getSelection(true);
+ if (range !== null) {
+ editor.insertText(range.index, text);
+ editor.setSelection(range.index + text.length, 0);
+ }
+ }
+ };
+
+ // Add our custom paste handler with high priority
+ editor.root.addEventListener("paste", handlePaste, true);
+
+ return () => {
+ editor.root.removeEventListener("paste", handlePaste, true);
+ };
}, []);
const convertToBase64 = (file: File): Promise =>
- new Promise((resolve) => {
+ new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result as string);
+ reader.onerror = reject;
reader.readAsDataURL(file);
});
+ // ... rest of your component code remains the same
const validate = () => {
const e: Partial = {};
@@ -67,26 +138,22 @@ const PtoLeaveTab: React.FC<{
const from = ptoLeave.fromDate ? new Date(ptoLeave.fromDate) : null;
const to = ptoLeave.toDate ? new Date(ptoLeave.toDate) : null;
- // From date required
if (!from) {
e.fromDate = "From date is required";
} else if (from < today) {
e.fromDate = "From date cannot be in the past";
}
- // To date required
if (!to) {
e.toDate = "To date is required";
} else if (to < today) {
e.toDate = "To date cannot be in the past";
}
- // To date must be same or after From date
if (from && to && to < from) {
e.toDate = "To date cannot be before From date";
}
- // Comment validation
if (!ptoLeave.comment.trim()) {
e.comment = "Comment required";
}
@@ -100,14 +167,13 @@ const PtoLeaveTab: React.FC<{
toDate: "",
comment: ""
};
-
+
setPtoLeave(emptyForm);
setErrors({});
-
- // Properly reset the Quill editor
+
if (quillRef.current) {
const editor = quillRef.current.getEditor();
- editor.setContents([]); // Clear editor contents completely
+ editor.setContents([]);
}
};
@@ -119,90 +185,100 @@ const PtoLeaveTab: React.FC<{
};
const handleSubmit = async () => {
+ setServerError("")
const e = validate();
if (Object.keys(e).length) return setErrors(e);
- const success = await submitLeave({
+ const result = await submitLeave({
dateFrom: ptoLeave.fromDate,
dateTo: ptoLeave.toDate,
leaveType: "PTO",
comment: ptoLeave.comment,
});
- if (success) {
- resetForm();
+ if (result.success) {
+ resetForm();
alert("PTO submitted!");
+ } else {
+ setServerError(result.message || "Something went wrong");
}
};
return (
-
-
+ <>
+
+
+
- {/* From Date */}
-
-
-
handleChange('fromDate', e.target.value)}
- className={`input input-bordered input-sm ${errors.fromDate ? "input-error" : ""
- }`}
- />
- {errors.fromDate &&
{errors.fromDate}
}
+
+ {/* From Date */}
+
+
+
handleChange('fromDate', e.target.value)}
+ className={`input input-bordered input-sm ${errors.fromDate ? "input-error" : ""
+ }`}
+ />
+ {errors.fromDate &&
{errors.fromDate}
}
+
+
+ {/* To Date */}
+
+
+
handleChange('toDate', e.target.value)}
+ className={`input input-bordered input-sm ${errors.toDate ? "input-error" : ""
+ }`}
+ />
+ {errors.toDate &&
{errors.toDate}
}
+
+
+ {/* Comment */}
+
+
+
handleChange('comment', val)}
+ className="h-60"
+ />
+ {errors.comment && {errors.comment}
}
+
- {/* To Date */}
-
-
-
handleChange('toDate', e.target.value)}
- className={`input input-bordered input-sm ${errors.toDate ? "input-error" : ""
- }`}
- />
- {errors.toDate &&
{errors.toDate}
}
-
+ {serverError && (
+
{serverError}
+ )}
- {/* Comment */}
-
-
- handleChange('comment', val)}
- className="h-60"
- />
-
-
- {errors.comment &&
{errors.comment}
}
+
-
-
-
+ >
);
};
diff --git a/src/ui/pages/LeaveAndAdjustments/tabs/SickLeaveTab.tsx b/src/ui/pages/LeaveAndAdjustments/tabs/SickLeaveTab.tsx
index 2977cd8..180e6ae 100644
--- a/src/ui/pages/LeaveAndAdjustments/tabs/SickLeaveTab.tsx
+++ b/src/ui/pages/LeaveAndAdjustments/tabs/SickLeaveTab.tsx
@@ -19,33 +19,102 @@ const SickLeaveTab: React.FC<{
toDate: '',
comment: ''
});
+ const [serverError, setServerError] = useState("");
const quillRef = useRef
(null);
const [sickErrors, setSickErrors] = useState>({});
useEffect(() => {
const editor = quillRef.current?.getEditor();
-
if (!editor) return;
- editor.getModule("clipboard").addMatcher(Node.ELEMENT_NODE, (node: any, delta: any) => {
- return delta;
- });
+ // Override image handler in toolbar
+ editor.getModule("toolbar").handlers.image = () => {
+ const input = document.createElement("input");
+ input.setAttribute("type", "file");
+ input.setAttribute("accept", "image/*");
+ input.click();
- 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();
+ input.onchange = async () => {
+ const file = input.files?.[0];
+ if (file) {
const base64 = await convertToBase64(file);
-
const range = editor.getSelection();
- editor.insertEmbed(range.index, "image", base64);
+ if (range) {
+ editor.insertEmbed(range.index, "image", base64);
+ }
+ }
+ };
+ };
+
+ // COMPLETELY disable Quill's default paste behavior
+ const disableQuillPaste = () => {
+ // Remove all existing paste event listeners from Quill
+ const pasteListeners = (editor.root as any).__pasteHandlers || [];
+ pasteListeners.forEach((handler: any) => {
+ editor.root.removeEventListener('paste', handler);
+ });
+
+ // Override the clipboard module to do nothing
+ const clipboard = editor.getModule('clipboard');
+ const originalPaste = clipboard.paste;
+ clipboard.paste = (e: any) => {
+ // Completely prevent Quill's default paste behavior
+ e.preventDefault();
+ e.stopPropagation();
+ return false;
+ };
+ };
+
+ disableQuillPaste();
+
+ // Our custom paste handler - this is the ONLY handler that will run
+ const handlePaste = async (e: ClipboardEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ const items = e.clipboardData?.items;
+ if (!items || items.length === 0) return;
+
+ // Check for images first
+ for (let i = 0; i < items.length; i++) {
+ const item = items[i];
+ if (item.type.startsWith("image/")) {
+ const file = item.getAsFile();
+ if (file) {
+ try {
+ const base64 = await convertToBase64(file);
+ const range = editor.getSelection(true);
+ if (range !== null) {
+ // Insert the image
+ editor.insertEmbed(range.index, "image", base64);
+ // Move cursor after the image
+ editor.setSelection(range.index + 1, 0);
+ }
+ } catch (error) {
+ console.error("Error processing pasted image:", error);
+ }
+ }
+ return; // Exit after handling the first image
}
}
- });
+
+ // If no images found, handle text
+ const text = e.clipboardData?.getData('text/plain');
+ if (text) {
+ const range = editor.getSelection(true);
+ if (range !== null) {
+ editor.insertText(range.index, text);
+ editor.setSelection(range.index + text.length, 0);
+ }
+ }
+ };
+
+ // Add our custom paste handler with high priority
+ editor.root.addEventListener("paste", handlePaste, true);
+
+ return () => {
+ editor.root.removeEventListener("paste", handlePaste, true);
+ };
}, []);
const convertToBase64 = (file: File): Promise =>
@@ -90,7 +159,7 @@ const SickLeaveTab: React.FC<{
toDate: "",
comment: ""
};
-
+
setSickLeave(emptyForm);
setSickErrors({});
@@ -102,6 +171,7 @@ const SickLeaveTab: React.FC<{
};
const handleSubmit = async () => {
+ setServerError("")
const errors = validateLeaveForm(sickLeave);
if (Object.keys(errors).length > 0) {
@@ -109,20 +179,23 @@ const SickLeaveTab: React.FC<{
return;
}
- const success = await submitLeave({
+ const result = await submitLeave({
dateFrom: sickLeave.fromDate,
dateTo: sickLeave.toDate,
leaveType: "SICK",
comment: sickLeave.comment,
});
- if (success) {
+ if (result.success) {
resetForm();
alert("SICK submitted!");
+ } else {
+ setServerError(result.message || "Something went wrong");
}
};
return (
+ <>
@@ -182,13 +255,17 @@ const SickLeaveTab: React.FC<{
className="h-60"
/>
- {sickErrors.comment && (
-
- )}
+ {sickErrors.comment && (
+
+ )}
+ {serverError && (
+
{serverError}
+ )}
+
{/* Submit Button */}
+ >
);
};