dealers added with production setup

This commit is contained in:
laxmanhalaki 2025-12-15 19:38:43 +05:30
parent 4b759395be
commit c97e5df689
30 changed files with 364 additions and 1079 deletions

View File

@ -1,7 +0,0 @@
<<<<<<<< HEAD:build/assets/conclusionApi-uNxtglEr.js
import{a as t}from"./index-9cOIFSn9.js";import"./radix-vendor-C2EbRL2a.js";import"./charts-vendor-Cji9-Yri.js";import"./utils-vendor-DHm03ykU.js";import"./ui-vendor-BmvKDhMD.js";import"./socket-vendor-TjCxX7sJ.js";import"./redux-vendor-tbZCm13o.js";import"./router-vendor-CRr9x_Jp.js";async function m(n){return(await t.post(`/conclusions/${n}/generate`)).data.data}async function d(n,o){return(await t.post(`/conclusions/${n}/finalize`,{finalRemark:o})).data.data}async function f(n){return(await t.get(`/conclusions/${n}`)).data.data}export{d as finalizeConclusion,m as generateConclusion,f as getConclusion};
//# sourceMappingURL=conclusionApi-uNxtglEr.js.map
========
import{a as t}from"./index-CogACwP9.js";import"./radix-vendor-C2EbRL2a.js";import"./charts-vendor-Cji9-Yri.js";import"./utils-vendor-DHm03ykU.js";import"./ui-vendor-BpFwwBOf.js";import"./socket-vendor-TjCxX7sJ.js";import"./redux-vendor-tbZCm13o.js";import"./router-vendor-CRr9x_Jp.js";async function m(n){return(await t.post(`/conclusions/${n}/generate`)).data.data}async function d(n,o){return(await t.post(`/conclusions/${n}/finalize`,{finalRemark:o})).data.data}async function f(n){return(await t.get(`/conclusions/${n}`)).data.data}export{d as finalizeConclusion,m as generateConclusion,f as getConclusion};
//# sourceMappingURL=conclusionApi-CGl-93sb.js.map
>>>>>>>> ee361a0c4ba611c87efe7f97d044a7c711024d1b:build/assets/conclusionApi-CGl-93sb.js

View File

@ -1,5 +0,0 @@
<<<<<<<< HEAD:build/assets/conclusionApi-uNxtglEr.js.map
{"version":3,"file":"conclusionApi-uNxtglEr.js","sources":["../../src/services/conclusionApi.ts"],"sourcesContent":["import apiClient from './authApi';\r\n\r\nexport interface ConclusionRemark {\r\n conclusionId: string;\r\n requestId: string;\r\n aiGeneratedRemark: string | null;\r\n aiModelUsed: string | null;\r\n aiConfidenceScore: number | null;\r\n finalRemark: string | null;\r\n editedBy: string | null;\r\n isEdited: boolean;\r\n editCount: number;\r\n approvalSummary: any;\r\n documentSummary: any;\r\n keyDiscussionPoints: string[];\r\n generatedAt: string | null;\r\n finalizedAt: string | null;\r\n createdAt: string;\r\n updatedAt: string;\r\n}\r\n\r\n/**\r\n * Generate AI-powered conclusion remark\r\n */\r\nexport async function generateConclusion(requestId: string): Promise<{\r\n conclusionId: string;\r\n aiGeneratedRemark: string;\r\n keyDiscussionPoints: string[];\r\n confidence: number;\r\n generatedAt: string;\r\n}> {\r\n const response = await apiClient.post(`/conclusions/${requestId}/generate`);\r\n return response.data.data;\r\n}\r\n\r\n/**\r\n * Update conclusion remark (edit by initiator)\r\n */\r\nexport async function updateConclusion(requestId: string, finalRemark: string): Promise<ConclusionRemark> {\r\n const response = await apiClient.put(`/conclusions/${requestId}`, { finalRemark });\r\n return response.data.data;\r\n}\r\n\r\n/**\r\n * Finalize conclusion and close request\r\n */\r\nexport async function finalizeConclusion(requestId: string, finalRemark: string): Promise<{\r\n conclusionId: string;\r\n requestNumber: string;\r\n status: string;\r\n finalRemark: string;\r\n finalizedAt: string;\r\n}> {\r\n const response = await apiClient.post(`/conclusions/${requestId}/finalize`, { finalRemark });\r\n return response.data.data;\r\n}\r\n\r\n/**\r\n * Get conclusion for a request\r\n */\r\nexport async function getConclusion(requestId: string): Promise<ConclusionRemark> {\r\n const response = await apiClient.get(`/conclusions/${requestId}`);\r\n return response.data.data;\r\n}\r\n\r\n"],"names":["generateConclusion","requestId","apiClient","finalizeConclusion","finalRemark","getConclusion"],"mappings":"6RAwBA,eAAsBA,EAAmBC,EAMtC,CAED,OADiB,MAAMC,EAAU,KAAK,gBAAgBD,CAAS,WAAW,GAC1D,KAAK,IACvB,CAaA,eAAsBE,EAAmBF,EAAmBG,EAMzD,CAED,OADiB,MAAMF,EAAU,KAAK,gBAAgBD,CAAS,YAAa,CAAE,YAAAG,EAAa,GAC3E,KAAK,IACvB,CAKA,eAAsBC,EAAcJ,EAA8C,CAEhF,OADiB,MAAMC,EAAU,IAAI,gBAAgBD,CAAS,EAAE,GAChD,KAAK,IACvB"}
========
{"version":3,"file":"conclusionApi-CGl-93sb.js","sources":["../../src/services/conclusionApi.ts"],"sourcesContent":["import apiClient from './authApi';\r\n\r\nexport interface ConclusionRemark {\r\n conclusionId: string;\r\n requestId: string;\r\n aiGeneratedRemark: string | null;\r\n aiModelUsed: string | null;\r\n aiConfidenceScore: number | null;\r\n finalRemark: string | null;\r\n editedBy: string | null;\r\n isEdited: boolean;\r\n editCount: number;\r\n approvalSummary: any;\r\n documentSummary: any;\r\n keyDiscussionPoints: string[];\r\n generatedAt: string | null;\r\n finalizedAt: string | null;\r\n createdAt: string;\r\n updatedAt: string;\r\n}\r\n\r\n/**\r\n * Generate AI-powered conclusion remark\r\n */\r\nexport async function generateConclusion(requestId: string): Promise<{\r\n conclusionId: string;\r\n aiGeneratedRemark: string;\r\n keyDiscussionPoints: string[];\r\n confidence: number;\r\n generatedAt: string;\r\n}> {\r\n const response = await apiClient.post(`/conclusions/${requestId}/generate`);\r\n return response.data.data;\r\n}\r\n\r\n/**\r\n * Update conclusion remark (edit by initiator)\r\n */\r\nexport async function updateConclusion(requestId: string, finalRemark: string): Promise<ConclusionRemark> {\r\n const response = await apiClient.put(`/conclusions/${requestId}`, { finalRemark });\r\n return response.data.data;\r\n}\r\n\r\n/**\r\n * Finalize conclusion and close request\r\n */\r\nexport async function finalizeConclusion(requestId: string, finalRemark: string): Promise<{\r\n conclusionId: string;\r\n requestNumber: string;\r\n status: string;\r\n finalRemark: string;\r\n finalizedAt: string;\r\n}> {\r\n const response = await apiClient.post(`/conclusions/${requestId}/finalize`, { finalRemark });\r\n return response.data.data;\r\n}\r\n\r\n/**\r\n * Get conclusion for a request\r\n */\r\nexport async function getConclusion(requestId: string): Promise<ConclusionRemark> {\r\n const response = await apiClient.get(`/conclusions/${requestId}`);\r\n return response.data.data;\r\n}\r\n\r\n"],"names":["generateConclusion","requestId","apiClient","finalizeConclusion","finalRemark","getConclusion"],"mappings":"6RAwBA,eAAsBA,EAAmBC,EAMtC,CAED,OADiB,MAAMC,EAAU,KAAK,gBAAgBD,CAAS,WAAW,GAC1D,KAAK,IACvB,CAaA,eAAsBE,EAAmBF,EAAmBG,EAMzD,CAED,OADiB,MAAMF,EAAU,KAAK,gBAAgBD,CAAS,YAAa,CAAE,YAAAG,EAAa,GAC3E,KAAK,IACvB,CAKA,eAAsBC,EAAcJ,EAA8C,CAEhF,OADiB,MAAMC,EAAU,IAAI,gBAAgBD,CAAS,EAAE,GAChD,KAAK,IACvB"}
>>>>>>>> ee361a0c4ba611c87efe7f97d044a7c711024d1b:build/assets/conclusionApi-CGl-93sb.js.map

View File

@ -0,0 +1,2 @@
import{a as t}from"./index-DtEUJDeH.js";import"./radix-vendor-DA0cB_hD.js";import"./charts-vendor-Cji9-Yri.js";import"./utils-vendor-DHm03ykU.js";import"./ui-vendor-BPwaxA-i.js";import"./socket-vendor-TjCxX7sJ.js";import"./redux-vendor-tbZCm13o.js";import"./router-vendor-CRr9x_Jp.js";async function m(n){return(await t.post(`/conclusions/${n}/generate`)).data.data}async function d(n,o){return(await t.post(`/conclusions/${n}/finalize`,{finalRemark:o})).data.data}async function f(n){return(await t.get(`/conclusions/${n}`)).data.data}export{d as finalizeConclusion,m as generateConclusion,f as getConclusion};
//# sourceMappingURL=conclusionApi-Dx0VmMvk.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"conclusionApi-Dx0VmMvk.js","sources":["../../src/services/conclusionApi.ts"],"sourcesContent":["import apiClient from './authApi';\r\n\r\nexport interface ConclusionRemark {\r\n conclusionId: string;\r\n requestId: string;\r\n aiGeneratedRemark: string | null;\r\n aiModelUsed: string | null;\r\n aiConfidenceScore: number | null;\r\n finalRemark: string | null;\r\n editedBy: string | null;\r\n isEdited: boolean;\r\n editCount: number;\r\n approvalSummary: any;\r\n documentSummary: any;\r\n keyDiscussionPoints: string[];\r\n generatedAt: string | null;\r\n finalizedAt: string | null;\r\n createdAt: string;\r\n updatedAt: string;\r\n}\r\n\r\n/**\r\n * Generate AI-powered conclusion remark\r\n */\r\nexport async function generateConclusion(requestId: string): Promise<{\r\n conclusionId: string;\r\n aiGeneratedRemark: string;\r\n keyDiscussionPoints: string[];\r\n confidence: number;\r\n generatedAt: string;\r\n}> {\r\n const response = await apiClient.post(`/conclusions/${requestId}/generate`);\r\n return response.data.data;\r\n}\r\n\r\n/**\r\n * Update conclusion remark (edit by initiator)\r\n */\r\nexport async function updateConclusion(requestId: string, finalRemark: string): Promise<ConclusionRemark> {\r\n const response = await apiClient.put(`/conclusions/${requestId}`, { finalRemark });\r\n return response.data.data;\r\n}\r\n\r\n/**\r\n * Finalize conclusion and close request\r\n */\r\nexport async function finalizeConclusion(requestId: string, finalRemark: string): Promise<{\r\n conclusionId: string;\r\n requestNumber: string;\r\n status: string;\r\n finalRemark: string;\r\n finalizedAt: string;\r\n}> {\r\n const response = await apiClient.post(`/conclusions/${requestId}/finalize`, { finalRemark });\r\n return response.data.data;\r\n}\r\n\r\n/**\r\n * Get conclusion for a request\r\n */\r\nexport async function getConclusion(requestId: string): Promise<ConclusionRemark> {\r\n const response = await apiClient.get(`/conclusions/${requestId}`);\r\n return response.data.data;\r\n}\r\n\r\n"],"names":["generateConclusion","requestId","apiClient","finalizeConclusion","finalRemark","getConclusion"],"mappings":"6RAwBA,eAAsBA,EAAmBC,EAMtC,CAED,OADiB,MAAMC,EAAU,KAAK,gBAAgBD,CAAS,WAAW,GAC1D,KAAK,IACvB,CAaA,eAAsBE,EAAmBF,EAAmBG,EAMzD,CAED,OADiB,MAAMF,EAAU,KAAK,gBAAgBD,CAAS,YAAa,CAAE,YAAAG,EAAa,GAC3E,KAAK,IACvB,CAKA,eAAsBC,EAAcJ,EAA8C,CAEhF,OADiB,MAAMC,EAAU,IAAI,gBAAgBD,CAAS,EAAE,GAChD,KAAK,IACvB"}

View File

@ -1,7 +0,0 @@
<<<<<<<< HEAD:build/assets/conclusionApi-uNxtglEr.js
import{a as t}from"./index-9cOIFSn9.js";import"./radix-vendor-C2EbRL2a.js";import"./charts-vendor-Cji9-Yri.js";import"./utils-vendor-DHm03ykU.js";import"./ui-vendor-BmvKDhMD.js";import"./socket-vendor-TjCxX7sJ.js";import"./redux-vendor-tbZCm13o.js";import"./router-vendor-CRr9x_Jp.js";async function m(n){return(await t.post(`/conclusions/${n}/generate`)).data.data}async function d(n,o){return(await t.post(`/conclusions/${n}/finalize`,{finalRemark:o})).data.data}async function f(n){return(await t.get(`/conclusions/${n}`)).data.data}export{d as finalizeConclusion,m as generateConclusion,f as getConclusion};
//# sourceMappingURL=conclusionApi-uNxtglEr.js.map
========
import{a as t}from"./index-CogACwP9.js";import"./radix-vendor-C2EbRL2a.js";import"./charts-vendor-Cji9-Yri.js";import"./utils-vendor-DHm03ykU.js";import"./ui-vendor-BpFwwBOf.js";import"./socket-vendor-TjCxX7sJ.js";import"./redux-vendor-tbZCm13o.js";import"./router-vendor-CRr9x_Jp.js";async function m(n){return(await t.post(`/conclusions/${n}/generate`)).data.data}async function d(n,o){return(await t.post(`/conclusions/${n}/finalize`,{finalRemark:o})).data.data}async function f(n){return(await t.get(`/conclusions/${n}`)).data.data}export{d as finalizeConclusion,m as generateConclusion,f as getConclusion};
//# sourceMappingURL=conclusionApi-CGl-93sb.js.map
>>>>>>>> ee361a0c4ba611c87efe7f97d044a7c711024d1b:build/assets/conclusionApi-CGl-93sb.js

View File

@ -1,5 +0,0 @@
<<<<<<<< HEAD:build/assets/conclusionApi-uNxtglEr.js.map
{"version":3,"file":"conclusionApi-uNxtglEr.js","sources":["../../src/services/conclusionApi.ts"],"sourcesContent":["import apiClient from './authApi';\r\n\r\nexport interface ConclusionRemark {\r\n conclusionId: string;\r\n requestId: string;\r\n aiGeneratedRemark: string | null;\r\n aiModelUsed: string | null;\r\n aiConfidenceScore: number | null;\r\n finalRemark: string | null;\r\n editedBy: string | null;\r\n isEdited: boolean;\r\n editCount: number;\r\n approvalSummary: any;\r\n documentSummary: any;\r\n keyDiscussionPoints: string[];\r\n generatedAt: string | null;\r\n finalizedAt: string | null;\r\n createdAt: string;\r\n updatedAt: string;\r\n}\r\n\r\n/**\r\n * Generate AI-powered conclusion remark\r\n */\r\nexport async function generateConclusion(requestId: string): Promise<{\r\n conclusionId: string;\r\n aiGeneratedRemark: string;\r\n keyDiscussionPoints: string[];\r\n confidence: number;\r\n generatedAt: string;\r\n}> {\r\n const response = await apiClient.post(`/conclusions/${requestId}/generate`);\r\n return response.data.data;\r\n}\r\n\r\n/**\r\n * Update conclusion remark (edit by initiator)\r\n */\r\nexport async function updateConclusion(requestId: string, finalRemark: string): Promise<ConclusionRemark> {\r\n const response = await apiClient.put(`/conclusions/${requestId}`, { finalRemark });\r\n return response.data.data;\r\n}\r\n\r\n/**\r\n * Finalize conclusion and close request\r\n */\r\nexport async function finalizeConclusion(requestId: string, finalRemark: string): Promise<{\r\n conclusionId: string;\r\n requestNumber: string;\r\n status: string;\r\n finalRemark: string;\r\n finalizedAt: string;\r\n}> {\r\n const response = await apiClient.post(`/conclusions/${requestId}/finalize`, { finalRemark });\r\n return response.data.data;\r\n}\r\n\r\n/**\r\n * Get conclusion for a request\r\n */\r\nexport async function getConclusion(requestId: string): Promise<ConclusionRemark> {\r\n const response = await apiClient.get(`/conclusions/${requestId}`);\r\n return response.data.data;\r\n}\r\n\r\n"],"names":["generateConclusion","requestId","apiClient","finalizeConclusion","finalRemark","getConclusion"],"mappings":"6RAwBA,eAAsBA,EAAmBC,EAMtC,CAED,OADiB,MAAMC,EAAU,KAAK,gBAAgBD,CAAS,WAAW,GAC1D,KAAK,IACvB,CAaA,eAAsBE,EAAmBF,EAAmBG,EAMzD,CAED,OADiB,MAAMF,EAAU,KAAK,gBAAgBD,CAAS,YAAa,CAAE,YAAAG,EAAa,GAC3E,KAAK,IACvB,CAKA,eAAsBC,EAAcJ,EAA8C,CAEhF,OADiB,MAAMC,EAAU,IAAI,gBAAgBD,CAAS,EAAE,GAChD,KAAK,IACvB"}
========
{"version":3,"file":"conclusionApi-CGl-93sb.js","sources":["../../src/services/conclusionApi.ts"],"sourcesContent":["import apiClient from './authApi';\r\n\r\nexport interface ConclusionRemark {\r\n conclusionId: string;\r\n requestId: string;\r\n aiGeneratedRemark: string | null;\r\n aiModelUsed: string | null;\r\n aiConfidenceScore: number | null;\r\n finalRemark: string | null;\r\n editedBy: string | null;\r\n isEdited: boolean;\r\n editCount: number;\r\n approvalSummary: any;\r\n documentSummary: any;\r\n keyDiscussionPoints: string[];\r\n generatedAt: string | null;\r\n finalizedAt: string | null;\r\n createdAt: string;\r\n updatedAt: string;\r\n}\r\n\r\n/**\r\n * Generate AI-powered conclusion remark\r\n */\r\nexport async function generateConclusion(requestId: string): Promise<{\r\n conclusionId: string;\r\n aiGeneratedRemark: string;\r\n keyDiscussionPoints: string[];\r\n confidence: number;\r\n generatedAt: string;\r\n}> {\r\n const response = await apiClient.post(`/conclusions/${requestId}/generate`);\r\n return response.data.data;\r\n}\r\n\r\n/**\r\n * Update conclusion remark (edit by initiator)\r\n */\r\nexport async function updateConclusion(requestId: string, finalRemark: string): Promise<ConclusionRemark> {\r\n const response = await apiClient.put(`/conclusions/${requestId}`, { finalRemark });\r\n return response.data.data;\r\n}\r\n\r\n/**\r\n * Finalize conclusion and close request\r\n */\r\nexport async function finalizeConclusion(requestId: string, finalRemark: string): Promise<{\r\n conclusionId: string;\r\n requestNumber: string;\r\n status: string;\r\n finalRemark: string;\r\n finalizedAt: string;\r\n}> {\r\n const response = await apiClient.post(`/conclusions/${requestId}/finalize`, { finalRemark });\r\n return response.data.data;\r\n}\r\n\r\n/**\r\n * Get conclusion for a request\r\n */\r\nexport async function getConclusion(requestId: string): Promise<ConclusionRemark> {\r\n const response = await apiClient.get(`/conclusions/${requestId}`);\r\n return response.data.data;\r\n}\r\n\r\n"],"names":["generateConclusion","requestId","apiClient","finalizeConclusion","finalRemark","getConclusion"],"mappings":"6RAwBA,eAAsBA,EAAmBC,EAMtC,CAED,OADiB,MAAMC,EAAU,KAAK,gBAAgBD,CAAS,WAAW,GAC1D,KAAK,IACvB,CAaA,eAAsBE,EAAmBF,EAAmBG,EAMzD,CAED,OADiB,MAAMF,EAAU,KAAK,gBAAgBD,CAAS,YAAa,CAAE,YAAAG,EAAa,GAC3E,KAAK,IACvB,CAKA,eAAsBC,EAAcJ,EAA8C,CAEhF,OADiB,MAAMC,EAAU,IAAI,gBAAgBD,CAAS,EAAE,GAChD,KAAK,IACvB"}
>>>>>>>> ee361a0c4ba611c87efe7f97d044a7c711024d1b:build/assets/conclusionApi-CGl-93sb.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
import{g as s}from"./index-DtEUJDeH.js";import"./radix-vendor-DA0cB_hD.js";import"./charts-vendor-Cji9-Yri.js";import"./utils-vendor-DHm03ykU.js";import"./ui-vendor-BPwaxA-i.js";import"./socket-vendor-TjCxX7sJ.js";import"./redux-vendor-tbZCm13o.js";import"./router-vendor-CRr9x_Jp.js";function R(o){const{requestId:e,status:t,request:a,navigate:r}=o;if((t==null?void 0:t.toLowerCase())==="draft"||t==="DRAFT"){r(`/edit-request/${e}`);return}const i=s(e);r(i)}export{R as navigateToRequest};
//# sourceMappingURL=requestNavigation-DjrXcYns.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"requestNavigation-DjrXcYns.js","sources":["../../src/utils/requestNavigation.ts"],"sourcesContent":["/**\r\n * Global Request Navigation Utility\r\n * \r\n * Centralized navigation logic for request-related routes.\r\n * This utility decides where to navigate when clicking on request cards\r\n * from anywhere in the application.\r\n * \r\n * Features:\r\n * - Single point of navigation logic\r\n * - Handles draft vs active requests\r\n * - Supports different flow types (CUSTOM, DEALER_CLAIM)\r\n * - Type-safe navigation\r\n */\r\n\r\nimport { NavigateFunction } from 'react-router-dom';\r\nimport { getRequestDetailRoute, RequestFlowType } from './requestTypeUtils';\r\n\r\nexport interface RequestNavigationOptions {\r\n requestId: string;\r\n requestTitle?: string;\r\n status?: string;\r\n request?: any; // Full request object if available\r\n navigate: NavigateFunction;\r\n}\r\n\r\n/**\r\n * Navigate to the appropriate request detail page based on request type\r\n * \r\n * This is the single point of navigation for all request cards.\r\n * It handles:\r\n * - Draft requests (navigate to edit)\r\n * - Different flow types (CUSTOM, DEALER_CLAIM)\r\n * - Status-based routing\r\n */\r\nexport function navigateToRequest(options: RequestNavigationOptions): void {\r\n const { requestId, status, request, navigate } = options;\r\n\r\n // Check if request is a draft - if so, route to edit form instead of detail view\r\n const isDraft = status?.toLowerCase() === 'draft' || status === 'DRAFT';\r\n if (isDraft) {\r\n navigate(`/edit-request/${requestId}`);\r\n return;\r\n }\r\n\r\n // Determine the appropriate route based on request type\r\n const route = getRequestDetailRoute(requestId, request);\r\n navigate(route);\r\n}\r\n\r\n/**\r\n * Navigate to create a new request based on flow type\r\n */\r\nexport function navigateToCreateRequest(\r\n navigate: NavigateFunction,\r\n flowType: RequestFlowType = 'CUSTOM'\r\n): void {\r\n const route = flowType === 'DEALER_CLAIM' \r\n ? '/claim-management' \r\n : '/new-request';\r\n navigate(route);\r\n}\r\n\r\n/**\r\n * Create a navigation handler function for request cards\r\n * This can be used directly in onClick handlers\r\n */\r\nexport function createRequestNavigationHandler(\r\n navigate: NavigateFunction\r\n) {\r\n return (requestId: string, requestTitle?: string, status?: string, request?: any) => {\r\n navigateToRequest({\r\n requestId,\r\n requestTitle,\r\n status,\r\n request,\r\n navigate,\r\n });\r\n };\r\n}\r\n"],"names":["navigateToRequest","options","requestId","status","request","navigate","route","getRequestDetailRoute"],"mappings":"6RAkCO,SAASA,EAAkBC,EAAyC,CACzE,KAAM,CAAE,UAAAC,EAAW,OAAAC,EAAQ,QAAAC,EAAS,SAAAC,GAAaJ,EAIjD,IADgBE,GAAA,YAAAA,EAAQ,iBAAkB,SAAWA,IAAW,QACnD,CACXE,EAAS,iBAAiBH,CAAS,EAAE,EACrC,MACF,CAGA,MAAMI,EAAQC,EAAsBL,CAAkB,EACtDG,EAASC,CAAK,CAChB"}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -52,15 +52,15 @@
transition: transform 0.2s ease;
}
</style>
<script type="module" crossorigin src="/assets/index-9cOIFSn9.js"></script>
<script type="module" crossorigin src="/assets/index-DtEUJDeH.js"></script>
<link rel="modulepreload" crossorigin href="/assets/charts-vendor-Cji9-Yri.js">
<link rel="modulepreload" crossorigin href="/assets/radix-vendor-C2EbRL2a.js">
<link rel="modulepreload" crossorigin href="/assets/radix-vendor-DA0cB_hD.js">
<link rel="modulepreload" crossorigin href="/assets/utils-vendor-DHm03ykU.js">
<link rel="modulepreload" crossorigin href="/assets/ui-vendor-BmvKDhMD.js">
<link rel="modulepreload" crossorigin href="/assets/ui-vendor-BPwaxA-i.js">
<link rel="modulepreload" crossorigin href="/assets/socket-vendor-TjCxX7sJ.js">
<link rel="modulepreload" crossorigin href="/assets/redux-vendor-tbZCm13o.js">
<link rel="modulepreload" crossorigin href="/assets/router-vendor-CRr9x_Jp.js">
<link rel="stylesheet" crossorigin href="/assets/index-BmOYs32D.css">
<link rel="stylesheet" crossorigin href="/assets/index-P-Le9vHs.css">
</head>
<body>
<div id="root"></div>

View File

@ -647,7 +647,7 @@ export class DealerClaimController {
const blockAmount = blockedAmount ? parseFloat(blockedAmount) : 0;
// Only store in database when blocking amount > 0
// Store in database when blocking amount > 0 OR when ioNumber and ioRemark are provided (for Step 3 approval)
if (blockAmount > 0) {
if (availableBalance === undefined) {
return ResponseHandler.error(res, 'Available balance is required when blocking amount', 400);
@ -666,6 +666,22 @@ export class DealerClaimController {
);
return ResponseHandler.success(res, { message: 'IO blocked successfully in SAP' }, 'IO blocked');
} else if (ioNumber && ioRemark !== undefined) {
// Save IO details (ioNumber, ioRemark) even without blocking amount
// This is useful when Step 3 is approved but amount hasn't been blocked yet
await this.dealerClaimService.updateIODetails(
requestId,
{
ioNumber,
ioRemark: ioRemark || '',
availableBalance: availableBalance ? parseFloat(availableBalance) : 0,
blockedAmount: 0,
remainingBalance: remainingBalance ? parseFloat(remainingBalance) : 0,
},
userId
);
return ResponseHandler.success(res, { message: 'IO details saved successfully' }, 'IO details saved');
} else {
// Just validate IO number without storing
// This is for validation only (fetch amount scenario)

View File

@ -466,14 +466,8 @@ export class ApprovalService {
|| (nextLevel as any).approverName === 'System Auto-Process'
|| (nextLevel as any).levelName === 'Activity Creation'
|| (nextLevel as any).levelName === 'E-Invoice Generation';
if (!isAutoStep && (nextLevel as any).approverId) {
await notificationService.sendToUsers([ (nextLevel as any).approverId ], {
title: `Action required: ${(wf as any).requestNumber}`,
body: `${(wf as any).title}`,
requestNumber: (wf as any).requestNumber,
url: `/request/${(wf as any).requestNumber}`
});
}
// Log approval activity
activityService.log({
requestId: level.requestId,
type: 'approval',
@ -484,6 +478,45 @@ export class ApprovalService {
ipAddress: requestMetadata?.ipAddress || undefined,
userAgent: requestMetadata?.userAgent || undefined
});
// Log assignment activity for next level (when it becomes active)
// Skip notifications and assignment logging for system/auto-steps
if (!isAutoStep && (nextLevel as any).approverId && (nextLevel as any).approverId !== 'system') {
// Additional check: ensure approverEmail is not a system email
const approverEmail = (nextLevel as any).approverEmail || '';
const isSystemEmail = approverEmail.toLowerCase() === 'system@royalenfield.com'
|| approverEmail.toLowerCase().includes('system');
if (!isSystemEmail) {
// Send notification to next approver (only for real users, not system processes)
await notificationService.sendToUsers([ (nextLevel as any).approverId ], {
title: `Action required: ${(wf as any).requestNumber}`,
body: `${(wf as any).title}`,
requestNumber: (wf as any).requestNumber,
requestId: (wf as any).requestId,
url: `/request/${(wf as any).requestNumber}`,
type: 'assignment',
priority: 'HIGH',
actionRequired: true
});
// Log assignment activity for the next approver
activityService.log({
requestId: level.requestId,
type: 'assignment',
user: { userId: level.approverId, name: level.approverName },
timestamp: new Date().toISOString(),
action: 'Assigned to approver',
details: `Request assigned to ${(nextLevel as any).approverName || (nextLevel as any).approverEmail || 'approver'} for ${(nextLevel as any).levelName || `level ${nextLevelNumber}`}`,
ipAddress: requestMetadata?.ipAddress || undefined,
userAgent: requestMetadata?.userAgent || undefined
});
} else {
logger.info(`[Approval] Skipping notification for system process: ${approverEmail} at level ${nextLevelNumber}`);
}
} else {
logger.info(`[Approval] Skipping notification for auto-step at level ${nextLevelNumber}`);
}
}
} else {
// No next level found but not final approver - this shouldn't happen

View File

@ -19,32 +19,39 @@ export interface DealerInfo {
}
/**
* Get all dealers (users with dealer designation or employeeId starting with 'RE-')
* Get all dealers (users with designation = 'dealer')
* Note: If employeeId is empty, generates a dummy dealerCode like "RE-MH-001" in the response
* The database employeeId remains unchanged - this is only for the API response
*/
export async function getAllDealers(): Promise<DealerInfo[]> {
try {
const dealers = await User.findAll({
where: {
[Op.or]: [
{ designation: { [Op.iLike]: '%dealer%' } as any },
{ employeeId: { [Op.like]: 'RE-%' } as any },
{ department: { [Op.iLike]: '%dealer%' } as any },
],
designation: { [Op.iLike]: 'dealer' } as any,
isActive: true,
},
order: [['displayName', 'ASC']],
});
return dealers.map((dealer) => ({
return dealers.map((dealer, index) => {
// Generate dummy dealerCode in response if employeeId is empty
// Format: RE-MH-XXX where XXX is a zero-padded index (001, 002, etc.)
// This is only for the API response - database employeeId is not modified
const dealerCode = dealer.employeeId && dealer.employeeId.trim() !== ''
? dealer.employeeId
: `RE-MH-${String(index + 1).padStart(3, '0')}`;
return {
userId: dealer.userId,
email: dealer.email,
dealerCode: dealer.employeeId || '', // Dealer code stored in employeeId
dealerCode: dealerCode,
dealerName: dealer.displayName || dealer.email,
displayName: dealer.displayName || dealer.email,
phone: dealer.phone || undefined,
department: dealer.department || undefined,
designation: dealer.designation || undefined,
}));
};
});
} catch (error) {
logger.error('[DealerService] Error fetching dealers:', error);
throw error;
@ -59,6 +66,7 @@ export async function getDealerByCode(dealerCode: string): Promise<DealerInfo |
const dealer = await User.findOne({
where: {
employeeId: dealerCode,
designation: { [Op.iLike]: 'dealer' } as any,
isActive: true,
},
});
@ -91,11 +99,8 @@ export async function getDealerByEmail(email: string): Promise<DealerInfo | null
const dealer = await User.findOne({
where: {
email: email.toLowerCase(),
designation: { [Op.iLike]: 'dealer' } as any,
isActive: true,
[Op.or]: [
{ designation: { [Op.iLike]: '%dealer%' } as any },
{ employeeId: { [Op.like]: 'RE-%' } as any },
],
},
});
@ -127,13 +132,7 @@ export async function searchDealers(searchTerm: string): Promise<DealerInfo[]> {
const dealers = await User.findAll({
where: {
[Op.and]: [
{
[Op.or]: [
{ designation: { [Op.iLike]: '%dealer%' } as any },
{ employeeId: { [Op.like]: 'RE-%' } as any },
{ department: { [Op.iLike]: '%dealer%' } as any },
],
},
{ designation: { [Op.iLike]: 'dealer' } as any },
{
[Op.or]: [
{ displayName: { [Op.iLike]: `%${searchTerm}%` } as any },

View File

@ -136,6 +136,80 @@ export class DealerClaimService {
// Create participants (initiator, dealer, department lead, finance - exclude system)
await this.createClaimParticipants(workflowRequest.requestId, userId, claimData.dealerEmail);
// Get initiator details for activity logging and notifications
const initiatorName = initiator.displayName || initiator.email || 'User';
// Log creation activity
await activityService.log({
requestId: workflowRequest.requestId,
type: 'created',
user: { userId: userId, name: initiatorName },
timestamp: new Date().toISOString(),
action: 'Claim request created',
details: `Claim request "${workflowRequest.title}" created by ${initiatorName} for dealer ${claimData.dealerName}`
});
// Send notification to INITIATOR confirming submission
await notificationService.sendToUsers([userId], {
title: 'Claim Request Submitted Successfully',
body: `Your claim request "${workflowRequest.title}" has been submitted successfully.`,
requestNumber: requestNumber,
requestId: workflowRequest.requestId,
url: `/request/${requestNumber}`,
type: 'request_submitted',
priority: 'MEDIUM'
});
// Get approval levels for notifications
// Step 1: Dealer Proposal Submission (first active step - log assignment at creation)
// Subsequent steps will have assignment logged when they become active (via approval service)
// Notify Step 1 (Dealer) - this is the first active step, so log assignment now
const dealerLevel = await ApprovalLevel.findOne({
where: {
requestId: workflowRequest.requestId,
levelNumber: 1 // Step 1: Dealer Proposal Submission
}
});
if (dealerLevel && dealerLevel.approverId) {
// Skip notifications for system processes
const approverEmail = dealerLevel.approverEmail || '';
const isSystemProcess = approverEmail.toLowerCase() === 'system@royalenfield.com'
|| approverEmail.toLowerCase().includes('system')
|| dealerLevel.approverId === 'system'
|| dealerLevel.approverName === 'System Auto-Process';
if (!isSystemProcess) {
// Send notification to Dealer (Step 1) for proposal submission
await notificationService.sendToUsers([dealerLevel.approverId], {
title: 'New Claim Request - Proposal Required',
body: `Claim request "${workflowRequest.title}" requires your proposal submission.`,
requestNumber: requestNumber,
requestId: workflowRequest.requestId,
url: `/request/${requestNumber}`,
type: 'assignment',
priority: 'HIGH',
actionRequired: true
});
// Log assignment activity for dealer (Step 1 - first active step)
await activityService.log({
requestId: workflowRequest.requestId,
type: 'assignment',
user: { userId: userId, name: initiatorName },
timestamp: new Date().toISOString(),
action: 'Assigned to dealer',
details: `Claim request assigned to dealer ${dealerLevel.approverName || dealerLevel.approverEmail || claimData.dealerName} for proposal submission`
});
} else {
logger.info(`[DealerClaimService] Skipping notification for system process: ${approverEmail} at Step 1`);
}
}
// Note: Step 2, 3, and subsequent steps will have assignment activities logged
// when they become active (when previous step is approved) via the approval service
logger.info(`[DealerClaimService] Created claim request: ${workflowRequest.requestNumber}`);
return workflowRequest;
} catch (error) {
@ -147,7 +221,7 @@ export class DealerClaimService {
/**
* Create 8-step approval levels for claim management
* Maps approvers based on step requirements:
* - Step 1 & 5: Dealer (external user via email)
* - Step 1 & 5: Dealer (treated as Okta/internal user - synced from Okta)
* - Step 2, 6 & 8: Initiator (requestor) - Step 8 is credit note action taken by initiator
* - Step 3: Department Lead/Manager (resolved from initiator's manager displayName via Okta search)
* - Step 4 & 7: System (auto-processed)
@ -198,7 +272,7 @@ export class DealerClaimService {
isAuto: false,
approverType: 'dealer' as const,
approverEmail: dealerEmail,
approverId: null, // Dealer may not exist in system, will use email
approverId: null, // Will be resolved by syncing dealer from Okta
},
{
level: 2,
@ -234,7 +308,7 @@ export class DealerClaimService {
isAuto: false,
approverType: 'dealer' as const,
approverEmail: dealerEmail,
approverId: null,
approverId: null, // Will be resolved by syncing dealer from Okta
},
{
level: 6,
@ -277,20 +351,29 @@ export class DealerClaimService {
approverName = 'System Auto-Process';
approverEmail = approverEmail || 'system@royalenfield.com';
} else if (step.approverType === 'dealer' && step.approverEmail) {
// Try to find dealer user - must exist in system
const dealerUser = await User.findOne({ where: { email: step.approverEmail } });
if (dealerUser) {
// Treat dealer as Okta user - sync from Okta if not in database
let dealerUser = await User.findOne({ where: { email: step.approverEmail.toLowerCase() } });
if (!dealerUser) {
logger.info(`[DealerClaimService] Dealer ${step.approverEmail} not found in DB, syncing from Okta`);
try {
dealerUser = await this.userService.ensureUserExists({
email: step.approverEmail.toLowerCase(),
}) as any;
logger.info(`[DealerClaimService] Successfully synced dealer ${step.approverEmail} from Okta`);
} catch (oktaError: any) {
logger.error(`[DealerClaimService] Failed to sync dealer from Okta: ${step.approverEmail}`, oktaError);
throw new Error(`Dealer email '${step.approverEmail}' not found in organization directory. Please verify the email address.`);
}
}
// Ensure dealerUser is not null (TypeScript check)
if (!dealerUser) {
throw new Error(`Dealer user could not be resolved for email: ${step.approverEmail}`);
}
approverId = dealerUser.userId;
approverName = dealerUser.displayName || dealerUser.email || 'Dealer';
approverEmail = dealerUser.email;
} else {
// Dealer not found - this should not happen if dealers are seeded
// Use initiator as fallback for now, but log a warning
logger.warn(`[DealerClaimService] Dealer ${step.approverEmail} not found in system, using initiator as fallback`);
approverId = initiatorId;
approverName = `Dealer (${step.approverEmail})`;
approverEmail = step.approverEmail;
}
} else if (step.approverType === 'initiator') {
approverId = initiatorId;
approverName = initiator.displayName || initiator.email || 'Requestor';
@ -376,11 +459,26 @@ export class DealerClaimService {
participantType: ParticipantType.INITIATOR,
});
// 2. Add Dealer (if exists and not system)
// 2. Add Dealer (treated as Okta/internal user - sync from Okta if needed)
if (dealerEmail && dealerEmail.toLowerCase() !== 'system@royalenfield.com') {
const dealerUser = await User.findOne({
let dealerUser = await User.findOne({
where: { email: dealerEmail.toLowerCase() },
});
if (!dealerUser) {
logger.info(`[DealerClaimService] Dealer ${dealerEmail} not found in DB for participants, syncing from Okta`);
try {
dealerUser = await this.userService.ensureUserExists({
email: dealerEmail.toLowerCase(),
}) as any;
logger.info(`[DealerClaimService] Successfully synced dealer ${dealerEmail} from Okta for participants`);
} catch (oktaError: any) {
logger.error(`[DealerClaimService] Failed to sync dealer from Okta for participants: ${dealerEmail}`, oktaError);
// Don't throw - dealer might be added later, but log the error
logger.warn(`[DealerClaimService] Skipping dealer participant creation for ${dealerEmail}`);
}
}
if (dealerUser) {
participantsToAdd.push({
userId: dealerUser.userId,
@ -1149,9 +1247,52 @@ export class DealerClaimService {
try {
const blockedAmount = ioData.blockedAmount || 0;
// Only proceed if blocking amount > 0
// If blocking amount > 0, proceed with SAP integration and blocking
// If blocking amount is 0 but ioNumber and ioRemark are provided, just save the IO details without blocking
if (blockedAmount <= 0) {
throw new Error('Blocked amount must be greater than 0');
// Allow saving IO details (ioNumber, ioRemark) even without blocking amount
// This is useful when Step 3 is approved but amount hasn't been blocked yet
if (ioData.ioNumber && (ioData.ioRemark !== undefined)) {
const organizedBy = organizedByUserId || null;
// Create or update Internal Order record with just IO details (no blocking)
const [internalOrder, created] = await InternalOrder.findOrCreate({
where: { requestId },
defaults: {
requestId,
ioNumber: ioData.ioNumber,
ioRemark: ioData.ioRemark || '',
ioAvailableBalance: ioData.availableBalance || 0,
ioBlockedAmount: 0,
ioRemainingBalance: ioData.remainingBalance || 0,
organizedBy: organizedBy || undefined,
organizedAt: new Date(),
status: IOStatus.PENDING,
}
});
if (!created) {
// Update existing IO record with new IO details
await internalOrder.update({
ioNumber: ioData.ioNumber,
ioRemark: ioData.ioRemark || '',
// Only update balance fields if provided
...(ioData.availableBalance !== undefined && { ioAvailableBalance: ioData.availableBalance }),
...(ioData.remainingBalance !== undefined && { ioRemainingBalance: ioData.remainingBalance }),
organizedBy: organizedBy || internalOrder.organizedBy,
organizedAt: new Date(),
});
}
logger.info(`[DealerClaimService] IO details saved (without blocking) for request: ${requestId}`, {
ioNumber: ioData.ioNumber,
ioRemark: ioData.ioRemark
});
return; // Exit early - no SAP blocking needed
} else {
throw new Error('Blocked amount must be greater than 0, or ioNumber and ioRemark must be provided');
}
}
// Validate IO number with SAP

View File

@ -967,6 +967,9 @@ export class WorkflowService {
closureDate: (wf as any).closureDate,
conclusionRemark: (wf as any).conclusionRemark,
closureType: closureType, // 'approved' or 'rejected' - indicates path to closure
workflowType: (wf as any).workflowType || null, // 'CLAIM_MANAGEMENT', 'NON_TEMPLATIZED', etc.
templateType: (wf as any).templateType || null, // 'CUSTOM', 'TEMPLATE', 'DEALER CLAIM'
templateId: (wf as any).templateId || null, // Reference to workflow_templates if using admin template
initiator: (wf as any).initiator,
department: (wf as any).initiator?.department,
totalLevels: (wf as any).totalLevels,