diff --git a/index.html b/index.html index fcb6d5a..0488ca4 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,7 @@ - + diff --git a/package-lock.json b/package-lock.json index 96290ec..57b0b82 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54,6 +54,7 @@ "react-redux": "^9.2.0", "react-router-dom": "^7.9.4", "recharts": "^2.13.3", + "socket.io-client": "^4.8.1", "sonner": "^1.5.0", "tailwind-merge": "^2.5.4", "vaul": "^1.0.0" @@ -2914,6 +2915,12 @@ "win32" ] }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, "node_modules/@standard-schema/spec": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", @@ -4195,6 +4202,45 @@ "dev": true, "license": "MIT" }, + "node_modules/engine.io-client": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz", + "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.1.1" + } + }, + "node_modules/engine.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/es-cookie": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/es-cookie/-/es-cookie-1.3.2.tgz", @@ -5424,7 +5470,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/mz": { @@ -6490,6 +6535,68 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/socket.io-client": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", + "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/sonner": { "version": "1.7.4", "resolved": "https://registry.npmjs.org/sonner/-/sonner-1.7.4.tgz", @@ -7156,6 +7263,35 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/package.json b/package.json index 2fb95d3..3ad2dd4 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "react-redux": "^9.2.0", "react-router-dom": "^7.9.4", "recharts": "^2.13.3", + "socket.io-client": "^4.8.1", "sonner": "^1.5.0", "tailwind-merge": "^2.5.4", "vaul": "^1.0.0" diff --git a/public/royal_enfield_logo.png b/public/royal_enfield_logo.png new file mode 100644 index 0000000..e18d69a Binary files /dev/null and b/public/royal_enfield_logo.png differ diff --git a/public/royal_enfield_logo.svg b/public/royal_enfield_logo.svg new file mode 100644 index 0000000..12695d3 --- /dev/null +++ b/public/royal_enfield_logo.svg @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/service-worker.js b/public/service-worker.js new file mode 100644 index 0000000..a2286c9 --- /dev/null +++ b/public/service-worker.js @@ -0,0 +1,26 @@ +self.addEventListener('push', event => { + const data = event.data ? event.data.json() : {}; + const title = data.title || 'Notification'; + console.log('notification dat i recive', data); + const rawUrl = data.url || (data.requestNumber ? `/request/${data.requestNumber}` : '/'); + const absoluteUrl = /^https?:\/\//i.test(rawUrl) ? rawUrl : (self.location.origin + rawUrl); + const options = { + body: data.body || 'New message', + icon: '/royal_enfield_logo.png', + badge: '/royal_enfield_logo.png', + data: { url: absoluteUrl } + }; + console.log('options', options); + event.waitUntil(self.registration.showNotification(title, options)); +}); + +self.addEventListener('notificationclick', function (event) { + event.notification.close(); + const targetUrl = (event.notification && event.notification.data && event.notification.data.url) || (self.location.origin + '/'); + event.waitUntil((async () => { + // Always open a new window/tab to ensure SPA router picks up the correct path + if (clients.openWindow) return clients.openWindow(targetUrl); + })()); +}); + + diff --git a/src/App.tsx b/src/App.tsx index 6274510..eb4dd05 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -70,10 +70,17 @@ function AppRoutes({ onLogout }: AppProps) { navigate(`/${page}`); }; - const handleViewRequest = (requestId: string, requestTitle?: string) => { + const handleViewRequest = async (requestId: string, requestTitle?: string, status?: string) => { setSelectedRequestId(requestId); setSelectedRequestTitle(requestTitle || 'Unknown Request'); - navigate(`/request/${requestId}`); + + // Check if request is a draft - if so, route to edit form instead of detail view + const isDraft = status?.toLowerCase() === 'draft' || status === 'DRAFT'; + if (isDraft) { + navigate(`/edit-request/${requestId}`); + } else { + navigate(`/request/${requestId}`); + } }; const handleBack = () => { @@ -564,6 +571,19 @@ function AppRoutes({ onLogout }: AppProps) { } /> + {/* Edit Draft Request */} + + } + /> + {/* Claim Management Wizard */} void; + onConfirm: (description: string) => Promise | void; + defaultDescription?: string; + title?: string; + requestIdDisplay?: string; + requestTitle?: string; +}; + +export function ApprovalModal({ + open, + onClose, + onConfirm, + defaultDescription = '', + title = 'Approve Request', + requestIdDisplay, + requestTitle, +}: ApprovalModalProps) { + const [description, setDescription] = useState(defaultDescription); + const [submitting, setSubmitting] = useState(false); + const charsUsed = description?.length || 0; + const limitedDescription = useMemo(() => description.slice(0, 500), [description]); + + const handleSubmit = async () => { + try { + setSubmitting(true); + await onConfirm(limitedDescription); + onClose(); + } finally { + setSubmitting(false); + } + }; + + return ( + !v && onClose()}> + + +
+
+ +
+
+ {title} +

Please provide your approval comments and remarks

+
+
+
+ + {/* Request Info Card */} +
+
+ Request ID + + {requestIdDisplay || '—'} + +
+ {/* Title - keep original stacked style without spacing between */} +
+ Title +

{requestTitle || '—'}

+
+ {/* Action - label and badge inline without justify-between */} +
+ Action + APPROVE +
+
+ + {/* Comments */} +
+ +