diff --git a/src/components/Documents/DocumentSchema.js b/src/components/Documents/DocumentSchema.js
new file mode 100644
index 00000000..5842e488
--- /dev/null
+++ b/src/components/Documents/DocumentSchema.js
@@ -0,0 +1,63 @@
+import { z } from "zod";
+
+export const AttachmentSchema = z.object({
+ fileName: z.string().min(1, {message:"File name is required"}),
+ base64Data: z.string().min(1, {message:"File data is required"}),
+ contentType: z.string().min(1, {message:"MIME type is required"}),
+ fileSize: z
+ .number()
+ .int()
+ .nonnegative("fileSize must be ≥ 0")
+ .max(25 * 1024 * 1024, "fileSize must be ≤ 25MB"),
+ description: z.string().optional().default(""),
+ isActive: z.boolean(),
+});
+
+export const TagSchema = z.object({
+ name: z.string().min(1, {message:"Tag name is required"}),
+ isActive: z.boolean(),
+});
+
+export const DocumentPayloadSchema = (isMandatory, regularExp) => {
+ let documentIdSchema = z.string();
+
+ if (isMandatory) {
+ documentIdSchema = documentIdSchema.min(1, {message:"DocumentId is required"});
+ }
+
+ if (regularExp) {
+ documentIdSchema = documentIdSchema.regex(
+ new RegExp(regularExp),
+ "Invalid DocumentId format"
+ );
+ }
+
+ return z.object({
+ name: z.string().min(1, "Name is required"),
+ documentId: documentIdSchema,
+ description: z.string().min(1,{message:"Description is required"}),
+ entityId: z.string().min(1,{message:"Please Select Document Entity"}),
+ documentTypeId: z.string().min(1,{message:"Please Select Document Type"}),
+ documentCategoryId:z.string().min(1,{message:"Please Select Document Category"}),
+ attachment: AttachmentSchema,
+ tags: z.array(TagSchema).default([]),
+ });
+};
+
+export const defaultDocumentValues = {
+ name: "",
+ documentId: "",
+ description: "",
+ entityId: "",
+ documentTypeId: "",
+ documentCategoryId:"",
+ attachment: {
+ fileName: "",
+ base64Data: "",
+ contentType: "",
+ fileSize: 0,
+ description: "",
+ isActive: true,
+ },
+ tags: [],
+};
diff --git a/src/components/Documents/Documents.jsx b/src/components/Documents/Documents.jsx
new file mode 100644
index 00000000..343310d8
--- /dev/null
+++ b/src/components/Documents/Documents.jsx
@@ -0,0 +1,52 @@
+import React, { useState } from 'react'
+import GlobalModel from '../common/GlobalModel'
+import NewDocument from './NewDocument'
+
+
+const Documents = () => {
+ const [isUpload,setUpload] =useState(false);
+
+ return (
+
+
+
+
+ {/* Search */}
+
+
+
+
+ {/* Actions */}
+
+
+ Refresh
+ < i className={`bx bx-refresh ms-1 `}>
+
+
+
+
+
+
+ {isUpload && (
+
setUpload(false)}>
+
+
+ )}
+
+
+ )
+}
+
+export default Documents
\ No newline at end of file
diff --git a/src/components/Documents/NewDocument.jsx b/src/components/Documents/NewDocument.jsx
new file mode 100644
index 00000000..175f46f4
--- /dev/null
+++ b/src/components/Documents/NewDocument.jsx
@@ -0,0 +1,260 @@
+import { zodResolver } from "@hookform/resolvers/zod";
+import React, { useEffect, useState } from "react";
+import { useForm } from "react-hook-form";
+import { defaultDocumentValues, DocumentPayloadSchema } from "./DocumentSchema";
+import Label from "../common/Label";
+import { DOCUMENTS_ENTITIES } from "../../utils/constants";
+import {
+ useDocumentCategories,
+ useDocumentTypes,
+} from "../../hooks/masterHook/useMaster";
+
+// util fn: convert file → base64
+const toBase64 = (file) =>
+ new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.readAsDataURL(file);
+ reader.onload = () => resolve(reader.result);
+ reader.onerror = (err) => reject(err);
+ });
+
+const NewDocument = () => {
+ const [selectedType, setType] = useState("");
+
+ const DocumentUpload = DocumentPayloadSchema(
+ selectedType?.isMandatory ?? null,
+ selectedType?.regexExpression ?? null
+ );
+
+ const {
+ register,
+ handleSubmit,
+ watch,
+ setValue,
+ formState: { errors },
+ } = useForm({
+ resolver: zodResolver(DocumentUpload),
+ defaultValues: defaultDocumentValues,
+ });
+
+ const onSubmit = (data) => {
+ console.log("Form submitted:", data);
+ };
+
+ const documentTypeId = watch("documentTypeId");
+ const categoryId = watch("documentCategoryId");
+ const file = watch("attachment");
+
+ // API Data
+ const { DocumentCategories, isLoading } = useDocumentCategories(
+ DOCUMENTS_ENTITIES.EmployeeEntity
+ );
+
+ const { DocumentTypes, isLoading: isTypeLoading } =
+ useDocumentTypes(categoryId);
+
+ // File Upload
+ const onFileChange = async (e) => {
+ const uploaded = e.target.files[0];
+ if (!uploaded) return;
+
+ const base64Data = await toBase64(uploaded);
+
+ const parsedFile = {
+ fileName: uploaded.name,
+ base64Data,
+ contentType: uploaded.type,
+ fileSize: uploaded.size,
+ description: "",
+ isActive: true,
+ };
+
+ setValue("attachment", parsedFile, {
+ shouldDirty: true,
+ shouldValidate: true,
+ });
+ };
+
+ const removeFile = () => {
+ setValue("attachment", null, {
+ shouldDirty: true,
+ shouldValidate: true,
+ });
+ };
+
+ useEffect(() => {
+ if (documentTypeId) {
+ setType(DocumentTypes?.find((type) => type.id === documentTypeId));
+ }
+ }, [documentTypeId, DocumentTypes]);
+
+ return (
+
+
Upload New Document
+
+
+
+ );
+};
+
+export default NewDocument;
diff --git a/src/components/Employee/EmpDocuments.jsx b/src/components/Employee/EmpDocuments.jsx
index 46aa33d0..39d6e900 100644
--- a/src/components/Employee/EmpDocuments.jsx
+++ b/src/components/Employee/EmpDocuments.jsx
@@ -1,10 +1,12 @@
import React, { useState, useEffect } from "react";
import { ComingSoonPage } from "../../pages/Misc/ComingSoonPage";
+import DocumentPage from "../../pages/Documents/DocumentPage";
+import Documents from "../Documents/Documents";
const EmpDocuments = ({ profile, loggedInUser }) => {
return (
<>
-
+
>
);
};
diff --git a/src/components/common/DateRangePicker.jsx b/src/components/common/DateRangePicker.jsx
index 9b7505fe..90ea9b24 100644
--- a/src/components/common/DateRangePicker.jsx
+++ b/src/components/common/DateRangePicker.jsx
@@ -57,8 +57,8 @@ const DateRangePicker = ({
/>
);
diff --git a/src/hooks/masterHook/useMaster.js b/src/hooks/masterHook/useMaster.js
index 5675aba7..9a31dcf8 100644
--- a/src/hooks/masterHook/useMaster.js
+++ b/src/hooks/masterHook/useMaster.js
@@ -171,6 +171,56 @@ const {
return { ExpenseStatus, loading, error };
}
+export const useDocumentTypes =(category)=>{
+const {
+ data: DocumentTypes = [],
+ error,
+ isError,
+ isLoading
+ } = useQuery({
+ queryKey: ["Document Type"],
+ queryFn: async () => {
+ const res = await MasterRespository.getDocumentTypes(category)
+ return res.data;
+ },
+ enabled:!!category,
+ onError: (error) => {
+ showToast(
+ error?.response?.data?.message ||
+ error.message ||
+ "Failed to fetch Expense Status",
+ "error"
+ );
+ },
+ });
+
+ return { DocumentTypes, isError, isLoading, error };
+}
+export const useDocumentCategories =(EntityType)=>{
+const {
+ data: DocumentCategories = [],
+ error,
+ isError,
+ isLoading
+ } = useQuery({
+ queryKey: ["Document Category"],
+ queryFn: async () => {
+ const res = await MasterRespository.getDocumentCategories(EntityType)
+ return res.data;
+ },
+ enabled:!!EntityType,
+ onError: (error) => {
+ showToast(
+ error?.response?.data?.message ||
+ error.message ||
+ "Failed to fetch Expense Status",
+ "error"
+ );
+ },
+ });
+
+ return { DocumentCategories, isError, isLoading, error };
+}
// ===Application Masters Query=================================================
const fetchMasterData = async (masterType) => {
diff --git a/src/pages/Documents/DocumentPage.jsx b/src/pages/Documents/DocumentPage.jsx
index f519bb5e..ba71070d 100644
--- a/src/pages/Documents/DocumentPage.jsx
+++ b/src/pages/Documents/DocumentPage.jsx
@@ -3,13 +3,8 @@ import Breadcrumb from '../../components/common/Breadcrumb'
const DocumentPage = () => {
return (
-
-
+
+
{/* Search */}
@@ -18,7 +13,7 @@ const DocumentPage = () => {
type="search"
className="form-control form-control-sm"
- placeholder="Search Tenant"
+ placeholder="Search Document"
/>
@@ -31,7 +26,7 @@ const DocumentPage = () => {
-
)
}
diff --git a/src/repositories/DocumentRepository.jsx b/src/repositories/DocumentRepository.jsx
index 052b1ad6..d01d7984 100644
--- a/src/repositories/DocumentRepository.jsx
+++ b/src/repositories/DocumentRepository.jsx
@@ -1,2 +1,5 @@
import { api } from "../utils/axiosClient";
+export const DocumentRepository = {
+ uploadDocument:(data)=> api.post(`/api/Document/upload`,data)
+}
diff --git a/src/repositories/MastersRepository.jsx b/src/repositories/MastersRepository.jsx
index d084bf86..f8a885aa 100644
--- a/src/repositories/MastersRepository.jsx
+++ b/src/repositories/MastersRepository.jsx
@@ -83,6 +83,10 @@ export const MasterRespository = {
api.put(`/api/Master/expenses-status/edit/${id}`, data),
- getDocumentCategories:()=>api.get("/api/Master/document-category/list"),
- getDocumentTypes:()=>api.get("/api/Master/document-type/list")
+ getDocumentCategories: (entityType) =>
+ api.get(`/api/Master/document-category/list${entityType ? `?entityTypeId=${entityType}` : ""}`),
+
+getDocumentTypes: (category) =>
+ api.get(`/api/Master/document-type/list${category ? `?documentCategoryId=${category}` : ""}`),
+
};
diff --git a/src/utils/constants.jsx b/src/utils/constants.jsx
index 555e3a0b..b5daeb44 100644
--- a/src/utils/constants.jsx
+++ b/src/utils/constants.jsx
@@ -79,6 +79,12 @@ export const TENANT_STATUS = [
{id:"35d7840a-164a-448b-95e6-efb2ec84a751",name:"Supspended"}
]
+export const DOCUMENTS_ENTITIES = {
+ ProjectEntity : "c8fe7115-aa27-43bc-99f4-7b05fabe436e",
+ EmployeeEntity:"dbb9555a-7a0c-40f2-a9ed-f0463f1ceed7",
+}
+
+
export const CONSTANT_TEXT = {
}