made compatible t take

serve frontend build
This commit is contained in:
laxman h 2026-04-27 19:52:56 +05:30
parent 5170ab6c5a
commit 7b417be427
24 changed files with 47 additions and 83 deletions

View File

@ -6,6 +6,7 @@
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "tsc && vite build", "build": "tsc && vite build",
"build:deploy": "vite build",
"lint": "eslint . --report-unused-disable-directives --max-warnings 0", "lint": "eslint . --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview", "preview": "vite preview",
"test": "jest", "test": "jest",

View File

@ -2,6 +2,8 @@ import client from './client';
import axios from 'axios'; import axios from 'axios';
import type { ConstitutionalChangeAction } from '@/lib/offboarding-actions'; import type { ConstitutionalChangeAction } from '@/lib/offboarding-actions';
const PUBLIC_API_BASE_URL = (import.meta as any).env?.VITE_API_URL || '/api';
export const API = { export const API = {
// Auth routes // Auth routes
login: (data: any) => client.post('/auth/login', data), login: (data: any) => client.post('/auth/login', data),
@ -70,8 +72,8 @@ export const API = {
deleteDocumentConfig: (id: string) => client.delete(`/onboarding/document-configs/${id}`), deleteDocumentConfig: (id: string) => client.delete(`/onboarding/document-configs/${id}`),
// Public Questionnaire // Public Questionnaire
getPublicQuestionnaire: (appId: string) => axios.get(`http://localhost:5000/api/questionnaire/public/${appId}`), // Direct axios to bypass interceptors if client has auth getPublicQuestionnaire: (appId: string) => axios.get(`${PUBLIC_API_BASE_URL}/questionnaire/public/${appId}`), // Direct axios to bypass interceptors if client has auth
submitPublicResponse: (data: any) => axios.post('http://localhost:5000/api/questionnaire/public/submit', data), submitPublicResponse: (data: any) => axios.post(`${PUBLIC_API_BASE_URL}/questionnaire/public/submit`, data),
// Assessment & Interviews // Assessment & Interviews
getAiSummary: (appId: string) => client.get(`/assessment/ai-summary/${appId}`), getAiSummary: (appId: string) => client.get(`/assessment/ai-summary/${appId}`),

View File

@ -1,6 +1,6 @@
import { create } from 'apisauce'; import { create } from 'apisauce';
const API_BASE_URL = (import.meta as any).env?.VITE_API_URL || 'http://localhost:5000/api'; const API_BASE_URL = (import.meta as any).env?.VITE_API_URL || '/api';
const client = create({ const client = create({
baseURL: API_BASE_URL, baseURL: API_BASE_URL,

View File

@ -59,9 +59,9 @@ const QuestionnaireForm: React.FC<QuestionnaireFormProps> = ({
return; return;
} }
const res = await API.getLatestQuestionnaire(); const res: any = await API.getLatestQuestionnaire();
if (res.data && res.data.data && res.data.data.questions) { if (res?.data?.data?.questions) {
// Normalize questions // Normalize questions
const normalized = res.data.data.questions.map((q: any) => ({ const normalized = res.data.data.questions.map((q: any) => ({
...q, ...q,

View File

@ -1,20 +1,13 @@
import React, { useState } from 'react'; import React from 'react';
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
DialogHeader,
DialogTitle, DialogTitle,
DialogDescription,
} from './dialog'; } from './dialog';
import { Button } from './button'; import { Button } from './button';
import { import {
Eye, Eye,
Download, Download,
RotateCw,
RefreshCw,
Plus,
Minus,
FileText
} from 'lucide-react'; } from 'lucide-react';
import { WIDE_DIALOG_CLASS } from '../../lib/dialogStyles'; import { WIDE_DIALOG_CLASS } from '../../lib/dialogStyles';
@ -35,9 +28,6 @@ export const DocumentPreviewModal: React.FC<DocumentPreviewModalProps> = ({
onClose, onClose,
document document
}) => { }) => {
const [zoomScale, setZoomScale] = useState(1);
const [rotation, setRotation] = useState(0);
const baseUrl = 'http://localhost:5000'; const baseUrl = 'http://localhost:5000';
const fileUrl = document ? `${baseUrl}${document.filePath.startsWith('/') ? '' : '/'}${document.filePath}` : ''; const fileUrl = document ? `${baseUrl}${document.filePath.startsWith('/') ? '' : '/'}${document.filePath}` : '';

View File

@ -1,20 +1,11 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { import {
LayoutDashboard,
Calendar,
FileText, FileText,
UserMinus,
RefreshCcw,
MapPin,
LogOut, LogOut,
Search,
ChevronLeft, ChevronLeft,
ChevronRight, ChevronRight,
Bell,
User, User,
RefreshCw, RefreshCw,
HelpCircle,
X
} from 'lucide-react'; } from 'lucide-react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { useNavigate, Routes, Route, useParams } from 'react-router-dom'; import { useNavigate, Routes, Route, useParams } from 'react-router-dom';

View File

@ -423,12 +423,6 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
const fnfAge = calculateAge(fnfCase.submittedOn); const fnfAge = calculateAge(fnfCase.submittedOn);
const canSendToStakeholders =
currentUser &&
["DD Lead", "DD Head", "NBH", "DD Admin", "Super Admin"].includes(
currentUser.role,
);
const canRespondToDepartment = (dept: any) => { const canRespondToDepartment = (dept: any) => {
if (!fnfCase || !dept) return false; if (!fnfCase || !dept) return false;
const role = String(currentUser?.role || "").toLowerCase(); const role = String(currentUser?.role || "").toLowerCase();

View File

@ -1,5 +1,5 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { IndianRupee, Calendar, Eye, Send, FileCheck, Loader2 } from 'lucide-react'; import { IndianRupee, Calendar, Eye, FileCheck, Loader2 } from 'lucide-react';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
@ -42,9 +42,6 @@ export function FnFPage({ currentUser, onViewDetails }: FnFPageProps) {
const [settlements, setSettlements] = useState<any[]>([]); const [settlements, setSettlements] = useState<any[]>([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const canSendToStakeholders = currentUser &&
['DD Lead', 'DD Head', 'NBH', 'DD Admin', 'Super Admin'].includes(currentUser.role);
useEffect(() => { useEffect(() => {
fetchSettlements(); fetchSettlements();
}, []); }, []);
@ -65,11 +62,6 @@ export function FnFPage({ currentUser, onViewDetails }: FnFPageProps) {
} }
}; };
const handleSendToStakeholders = (caseId: string) => {
console.log('Sending to stakeholders for case:', caseId);
toast.success('Notifications sent to all stakeholders');
};
if (loading) { if (loading) {
return ( return (
<div className="flex items-center justify-center p-12"> <div className="flex items-center justify-center p-12">

View File

@ -37,7 +37,7 @@ export const ASMDialog: React.FC<ASMDialogProps> = ({
asmStatus, setAsmStatus, selectedASMZone, setSelectedASMZone, asmStatus, setAsmStatus, selectedASMZone, setSelectedASMZone,
selectedASMRegion, setSelectedASMRegion, selectedASMStates, setSelectedASMStates, selectedASMRegion, setSelectedASMRegion, selectedASMStates, setSelectedASMStates,
selectedASMDistricts, setSelectedASMDistricts, onSave, selectedASMDistricts, setSelectedASMDistricts, onSave,
asmRoleCode, setAsmRoleCode, asmRoleCode,
userAssignedData, districtsAssignedToOthers, getDistrictsForSelectedState userAssignedData, districtsAssignedToOthers, getDistrictsForSelectedState
}) => { }) => {
const { zones, regionalOffices } = useSelector((state: RootState) => state.master); const { zones, regionalOffices } = useSelector((state: RootState) => state.master);

View File

@ -1,4 +1,4 @@
import { render, screen } from "@testing-library/react" import { screen } from "@testing-library/react"
import userEvent from "@testing-library/user-event" import userEvent from "@testing-library/user-event"
import { AllApplicationsPage } from "../pages/AllApplicationsPage" import { AllApplicationsPage } from "../pages/AllApplicationsPage"
import { renderWithProviders } from "./test-utils" import { renderWithProviders } from "./test-utils"
@ -10,12 +10,12 @@ describe("AllApplicationsPage - Robust Admin Verification", () => {
jest.clearAllMocks() jest.clearAllMocks()
}) })
const renderPage = (apps = []) => { const renderPage = (apps: any[] = []) => {
;(mockOnboardingService.getApplications as jest.Mock).mockResolvedValue({ ;(mockOnboardingService.getApplications as jest.Mock).mockResolvedValue({
success: true, success: true,
data: apps data: apps
}) })
return renderWithProviders(<AllApplicationsPage />, { return renderWithProviders(<AllApplicationsPage onViewDetails={jest.fn()} />, {
preloadedState: { preloadedState: {
auth: { auth: {
user: { id: "admin-1", role: "DD Admin" } as any, user: { id: "admin-1", role: "DD Admin" } as any,

View File

@ -1,4 +1,4 @@
import { render, screen, waitFor } from "@testing-library/react" import { screen, waitFor } from "@testing-library/react"
import userEvent from "@testing-library/user-event" import userEvent from "@testing-library/user-event"
import { ApplicationDetails } from "../pages/ApplicationDetails" import { ApplicationDetails } from "../pages/ApplicationDetails"
import { renderWithProvidersAndRoute } from "./test-utils" import { renderWithProvidersAndRoute } from "./test-utils"

View File

@ -1,4 +1,4 @@
import { render, screen, waitFor } from "@testing-library/react" import { screen } from "@testing-library/react"
import userEvent from "@testing-library/user-event" import userEvent from "@testing-library/user-event"
import { ApplicationsPage } from "../pages/ApplicationsPage" import { ApplicationsPage } from "../pages/ApplicationsPage"
import { renderWithProviders } from "./test-utils" import { renderWithProviders } from "./test-utils"
@ -10,7 +10,7 @@ describe("ApplicationsPage - Robust Feature Verification", () => {
jest.clearAllMocks() jest.clearAllMocks()
}) })
const renderPage = (apps = []) => { const renderPage = (apps: any[] = []) => {
;(mockOnboardingService.getApplications as jest.Mock).mockResolvedValue({ ;(mockOnboardingService.getApplications as jest.Mock).mockResolvedValue({
success: true, success: true,
data: apps data: apps

View File

@ -1,4 +1,4 @@
import { render, screen, waitFor } from "@testing-library/react" import { screen, waitFor } from "@testing-library/react"
import userEvent from "@testing-library/user-event" import userEvent from "@testing-library/user-event"
import { ApplicationDetails } from "../pages/ApplicationDetails" import { ApplicationDetails } from "../pages/ApplicationDetails"
import { renderWithProvidersAndRoute } from "./test-utils" import { renderWithProvidersAndRoute } from "./test-utils"

View File

@ -1,4 +1,4 @@
import { render, screen, waitFor } from "@testing-library/react" import { screen, waitFor } from "@testing-library/react"
import userEvent from "@testing-library/user-event" import userEvent from "@testing-library/user-event"
import { FDDApplicationDetails } from "../pages/FDDApplicationDetails" import { FDDApplicationDetails } from "../pages/FDDApplicationDetails"
import { renderWithProvidersAndRoute } from "./test-utils" import { renderWithProvidersAndRoute } from "./test-utils"

View File

@ -1,4 +1,4 @@
import { render, screen } from "@testing-library/react" import { screen } from "@testing-library/react"
import userEvent from "@testing-library/user-event" import userEvent from "@testing-library/user-event"
import { NonOpportunitiesPage } from "../pages/NonOpportunitiesPage" import { NonOpportunitiesPage } from "../pages/NonOpportunitiesPage"
import { renderWithProviders } from "./test-utils" import { renderWithProviders } from "./test-utils"
@ -10,7 +10,7 @@ describe("NonOpportunitiesPage - Lead generation filtering", () => {
jest.clearAllMocks() jest.clearAllMocks()
}) })
const renderPage = (apps = []) => { const renderPage = (apps: any[] = []) => {
;(mockOnboardingService.getApplications as jest.Mock).mockResolvedValue({ ;(mockOnboardingService.getApplications as jest.Mock).mockResolvedValue({
success: true, success: true,
data: apps, data: apps,

View File

@ -1,4 +1,4 @@
import { render, screen, waitFor } from "@testing-library/react" import { screen, waitFor } from "@testing-library/react"
import userEvent from "@testing-library/user-event" import userEvent from "@testing-library/user-event"
import { OpportunityRequestsPage } from "../pages/OpportunityRequestsPage" import { OpportunityRequestsPage } from "../pages/OpportunityRequestsPage"
import { renderWithProviders } from "./test-utils" import { renderWithProviders } from "./test-utils"
@ -14,12 +14,12 @@ describe("OpportunityRequestsFlow - Lead Triage Verification", () => {
jest.clearAllMocks() jest.clearAllMocks()
}) })
const renderPage = (apps = []) => { const renderPage = (apps: any[] = []) => {
;(mockOnboardingService.getApplications as jest.Mock).mockResolvedValue({ ;(mockOnboardingService.getApplications as jest.Mock).mockResolvedValue({
success: true, success: true,
data: apps data: apps
}) })
return renderWithProviders(<OpportunityRequestsPage />, { return renderWithProviders(<OpportunityRequestsPage onViewDetails={jest.fn()} />, {
preloadedState: { preloadedState: {
auth: { auth: {
user: { id: "admin-1", role: "DD Admin" } as any, user: { id: "admin-1", role: "DD Admin" } as any,

View File

@ -1 +1 @@
export const SHARED_APP_STATUSES = { SUBMITTED: "Submitted" }; export { TEST_STRINGS, GLOBAL_MOCK_IDS, SHARED_APP_STATUSES } from "@/testing/constants";

View File

@ -49,19 +49,18 @@ export const globalMockAdminService = {
* Helper to prepare global mocks in any test file. * Helper to prepare global mocks in any test file.
*/ */
export const setupUnifiedMocks = () => { export const setupUnifiedMocks = () => {
jest.mock("@/services/onboarding.service", () => ({ onboardingService: globalMockOnboardingService })); return {
jest.mock("@/services/collaboration.service", () => ({ collaborationService: globalMockCollaborationService })); onboardingService: globalMockOnboardingService,
// Mapping worknoteService to collaborationService as they share logic in this codebase collaborationService: globalMockCollaborationService,
jest.mock("@/services/worknote.service", () => ({ worknoteService: globalMockCollaborationService })); worknoteService: globalMockCollaborationService,
jest.mock("@/services/eor.service", () => ({ eorService: globalMockEORService })); eorService: globalMockEORService,
jest.mock("@/services/admin.service", () => ({ adminService: globalMockAdminService })); adminService: globalMockAdminService,
jest.mock("@/api/API", () => ({
API: { API: {
...globalMockOnboardingService, ...globalMockOnboardingService,
getFddAssignment: jest.fn().mockResolvedValue({ success: true, data: {} }), getFddAssignment: jest.fn().mockResolvedValue({ success: true, data: {} }),
uploadDocument: jest.fn().mockResolvedValue({ success: true }), uploadDocument: jest.fn().mockResolvedValue({ success: true }),
submitFddReport: jest.fn().mockResolvedValue({ success: true }), submitFddReport: jest.fn().mockResolvedValue({ success: true }),
flagNonResponsive: jest.fn().mockResolvedValue({ success: true }), flagNonResponsive: jest.fn().mockResolvedValue({ success: true }),
} },
})); };
}; };

View File

@ -2,7 +2,6 @@ import React, { PropsWithChildren } from 'react'
import { render } from '@testing-library/react' import { render } from '@testing-library/react'
import type { RenderOptions } from '@testing-library/react' import type { RenderOptions } from '@testing-library/react'
import { configureStore } from '@reduxjs/toolkit' import { configureStore } from '@reduxjs/toolkit'
import type { PreloadedState } from '@reduxjs/toolkit'
import { Provider } from 'react-redux' import { Provider } from 'react-redux'
import { MemoryRouter, Routes, Route } from 'react-router-dom' import { MemoryRouter, Routes, Route } from 'react-router-dom'
@ -14,7 +13,7 @@ import { MEMORY_ROUTER_TEST_FUTURE } from '@/testing/reactRouterTest'
// This type interface extends the default options for render from RTL, as well // This type interface extends the default options for render from RTL, as well
// as allows the user to specify other things such as initialState, store. // as allows the user to specify other things such as initialState, store.
interface ExtendedRenderOptions extends Omit<RenderOptions, 'queries'> { interface ExtendedRenderOptions extends Omit<RenderOptions, 'queries'> {
preloadedState?: PreloadedState<RootState> preloadedState?: Partial<RootState>
store?: ReturnType<typeof setupStore> store?: ReturnType<typeof setupStore>
} }
@ -24,13 +23,13 @@ interface ProvidersAndRouteOptions extends ExtendedRenderOptions {
routePath: string routePath: string
} }
export function setupStore(preloadedState?: PreloadedState<RootState>) { export function setupStore(preloadedState?: Partial<RootState>) {
return configureStore({ return configureStore({
reducer: { reducer: {
auth: authReducer, auth: authReducer,
master: masterReducer, master: masterReducer,
}, } as any,
preloadedState preloadedState: preloadedState as any
}) })
} }

View File

@ -30,7 +30,7 @@ const getStatusColor = (status: string) => {
const getApiErrorMessage = (error: any, fallback: string) => const getApiErrorMessage = (error: any, fallback: string) =>
error?.response?.data?.message || error?.data?.message || error?.message || fallback; error?.response?.data?.message || error?.data?.message || error?.message || fallback;
export function DealerRelocationPage({ currentUser, onViewDetails }: DealerRelocationPageProps) { export function DealerRelocationPage({ onViewDetails }: DealerRelocationPageProps) {
const [isDialogOpen, setIsDialogOpen] = useState(false); const [isDialogOpen, setIsDialogOpen] = useState(false);
const [selectedOutlet, setSelectedOutlet] = useState<any | null>(null); const [selectedOutlet, setSelectedOutlet] = useState<any | null>(null);
const [newCity, setNewCity] = useState(''); const [newCity, setNewCity] = useState('');
@ -54,7 +54,6 @@ export function DealerRelocationPage({ currentUser, onViewDetails }: DealerReloc
const [requests, setRequests] = useState<any[]>([]); const [requests, setRequests] = useState<any[]>([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [submitting, setSubmitting] = useState(false); const [submitting, setSubmitting] = useState(false);
const [profile, setProfile] = useState<any>(null);
useEffect(() => { useEffect(() => {
fetchData(); fetchData();
@ -73,7 +72,6 @@ export function DealerRelocationPage({ currentUser, onViewDetails }: DealerReloc
const relocationRes = await dealerService.getRelocationRequests(); const relocationRes = await dealerService.getRelocationRequests();
setOutlets(dashboard.outlets || []); setOutlets(dashboard.outlets || []);
setProfile(dashboard.profile);
setRequests(relocationRes.requests || []); setRequests(relocationRes.requests || []);
} catch (error) { } catch (error) {
console.error('Fetch relocation data error:', error); console.error('Fetch relocation data error:', error);
@ -121,11 +119,6 @@ export function DealerRelocationPage({ currentUser, onViewDetails }: DealerReloc
} }
}; };
const handleOpenRelocationDialog = (outlet: any) => {
setSelectedOutlet(outlet);
setIsDialogOpen(true);
};
const handleSubmitRequest = async (e: React.FormEvent) => { const handleSubmitRequest = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();

View File

@ -1,9 +1,9 @@
import { FileText, Plus, Eye, Calendar, User, Building2, Store, MapPin, CheckCircle, Clock, RefreshCcw, Loader2 } from 'lucide-react'; import { FileText, Eye, Calendar, Building2, Store, MapPin, CheckCircle, Clock, Loader2 } from 'lucide-react';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
@ -27,7 +27,7 @@ const getStatusColor = (status: string) => {
return 'bg-slate-100 text-slate-700 border-slate-300'; return 'bg-slate-100 text-slate-700 border-slate-300';
}; };
export function DealerResignationPage({ currentUser, onViewDetails }: DealerResignationPageProps) { export function DealerResignationPage({ onViewDetails }: DealerResignationPageProps) {
const [isDialogOpen, setIsDialogOpen] = useState(false); const [isDialogOpen, setIsDialogOpen] = useState(false);
const [selectedOutlet, setSelectedOutlet] = useState<any | null>(null); const [selectedOutlet, setSelectedOutlet] = useState<any | null>(null);
const [resignationType, setResignationType] = useState(''); const [resignationType, setResignationType] = useState('');

View File

@ -30,7 +30,7 @@ const getStatusColor = (status: string) => {
return 'bg-blue-100 text-blue-700 border-blue-300'; return 'bg-blue-100 text-blue-700 border-blue-300';
}; };
export function ResignationPage({ currentUser, onViewDetails }: ResignationPageProps) { export function ResignationPage({ onViewDetails }: ResignationPageProps) {
const [resignations, setResignations] = useState<any[]>([]); const [resignations, setResignations] = useState<any[]>([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);

View File

@ -1,7 +1,6 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'; import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Bell, Check, Clock, ChevronLeft, ChevronRight } from 'lucide-react'; import { Bell, Check, Clock, ChevronLeft, ChevronRight } from 'lucide-react';
import { notificationService, Notification } from '@/services/notification.service'; import { notificationService, Notification } from '@/services/notification.service';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';

View File

@ -4,6 +4,10 @@ import { defineConfig } from "vite"
export default defineConfig({ export default defineConfig({
plugins: [react()], plugins: [react()],
build: {
outDir: "../backend/build",
emptyOutDir: true,
},
resolve: { resolve: {
alias: { alias: {
"@": path.resolve(__dirname, "./src"), "@": path.resolve(__dirname, "./src"),