From abba8aefdd8ea75eacbbcdf977b92d27c6f299f4 Mon Sep 17 00:00:00 2001 From: laxman h Date: Tue, 24 Mar 2026 21:27:46 +0530 Subject: [PATCH 1/7] added the new invoice flow where new lables added like hsn and part_ amount for gst invoice generation --- src/controllers/dealerClaim.controller.ts | 122 ++++++++++++++-------- src/services/dealerClaim.service.ts | 38 +++---- src/services/wfmFile.service.ts | 73 +++++++++++-- src/utils/helpers.ts | 90 ++++++++++++++++ 4 files changed, 253 insertions(+), 70 deletions(-) diff --git a/src/controllers/dealerClaim.controller.ts b/src/controllers/dealerClaim.controller.ts index fefb373..7d9e797 100644 --- a/src/controllers/dealerClaim.controller.ts +++ b/src/controllers/dealerClaim.controller.ts @@ -15,10 +15,11 @@ import crypto from 'crypto'; import { DealerClaimDetails } from '../models/DealerClaimDetails'; import { ClaimInvoice } from '../models/ClaimInvoice'; import { ClaimInvoiceItem } from '../models/ClaimInvoiceItem'; +import { ClaimCreditNote } from '../models/ClaimCreditNote'; import { ActivityType } from '../models/ActivityType'; import { Participant } from '../models/Participant'; import { sanitizeObject, sanitizePermissive } from '../utils/sanitizer'; -import { padDealerCode } from '../utils/helpers'; +import { buildWfmClaimCsvRow, padDealerCode, WFM_CLAIM_CSV_HEADERS } from '../utils/helpers'; import { costBreakupSchema, closedExpensesSchema, updateEInvoiceSchema, updateIOSchema } from '../validators/dealerClaim.validator'; export class DealerClaimController { @@ -1114,52 +1115,49 @@ export class DealerClaimController { const isNonGst = taxationType === 'Non GST' || taxationType === 'Non-GST'; // Construct CSV with pipe separator - const headers = [ - 'TRNS_UNIQ_NO', - 'CLAIM_NUMBER', - 'INV_NUMBER', - 'DEALER_CODE', - 'IO_NUMBER', - 'CLAIM_DOC_TYP', - 'CLAIM_TYPE', - 'CLAIM_DATE', - 'CLAIM_AMT' - ]; - - if (!isNonGst) { - headers.push('GST_AMT', 'GST_PERCENTAGE'); - } + const headers = isNonGst + ? [ + 'TRNS_UNIQ_NO', + 'CLAIM_NUMBER', + 'INV_NUMBER', + 'DEALER_CODE', + 'IO_NUMBER', + 'CLAIM_DOC_TYP', + 'CLAIM_TYPE', + 'CLAIM_DATE', + 'CLAIM_AMT' + ] + : [...WFM_CLAIM_CSV_HEADERS]; const rows = items.map(item => { - // For Non-GST, we hide HSN (often stored in transactionCode) - const trnsUniqNo = item.transactionCode || ''; - const claimNumber = requestNumber; - const invNumber = invoice?.invoiceNumber || ''; - const dealerCode = padDealerCode(claimDetails?.dealerCode || ''); - const ioNumber = internalOrder?.ioNumber || ''; - const claimDocTyp = sapRefNo; - const claimType = claimDetails?.activityType || ''; - const claimDate = invoice?.createdAt ? new Date(invoice.createdAt).toISOString().split('T')[0] : ''; - const claimAmt = item.assAmt; - - const rowItems = [ - trnsUniqNo, - claimNumber, - invNumber, - dealerCode, - ioNumber, - claimDocTyp, - claimType, - claimDate, - claimAmt - ]; - - if (!isNonGst) { - const totalTax = Number(item.igstAmt || 0) + Number(item.cgstAmt || 0) + Number(item.sgstAmt || 0) + Number(item.utgstAmt || 0); - rowItems.push(totalTax.toFixed(2), item.gstRt || 0); + if (isNonGst) { + const d = new Date(invoice?.invoiceDate || invoice?.createdAt || new Date()); + const claimDate = `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, '0')}${String(d.getDate()).padStart(2, '0')}`; + return [ + item.transactionCode || '', + requestNumber, + invoice?.invoiceNumber || '', + padDealerCode(claimDetails?.dealerCode || ''), + internalOrder?.ioNumber || '', + sapRefNo, + claimDetails?.activityType || '', + claimDate, + item.assAmt + ].join('|'); } - return rowItems.join('|'); + const row = buildWfmClaimCsvRow({ + item: item as any, + requestNumber, + invoiceNumber: invoice?.invoiceNumber || '', + invoiceDate: (invoice?.invoiceDate as Date) || (invoice?.createdAt as Date) || new Date(), + dealerCode: claimDetails?.dealerCode || '', + ioNumber: internalOrder?.ioNumber || '', + claimDocTyp: sapRefNo, + claimType: claimDetails?.activityType || '', + }); + + return headers.map((key) => String((row as any)[key] ?? '')).join('|'); }); const csvContent = [headers.join('|'), ...rows].join('\n'); @@ -1232,9 +1230,43 @@ export class DealerClaimController { } const { wfmFileService } = await import('../services/wfmFile.service'); - const creditNoteData = await wfmFileService.getCreditNoteDetails(claimDetails.dealerCode, requestNumber, isNonGst); + const existingCreditNote = await ClaimCreditNote.findOne({ where: { requestId } }); + if (existingCreditNote?.sapDocumentNumber || existingCreditNote?.creditNoteNumber) { + const payload = [{ + TRNS_UNIQ_NO: '', + CLAIM_NUMBER: requestNumber, + DOC_NO: existingCreditNote.sapDocumentNumber || existingCreditNote.creditNoteNumber || '', + MSG_TYP: existingCreditNote.status || '', + MESSAGE: existingCreditNote.errorMessage || '' + }]; + return ResponseHandler.success(res, payload, 'Credit note data fetched successfully'); + } - return ResponseHandler.success(res, creditNoteData, 'Credit note data fetched successfully'); + const { filePath, data: creditNoteData } = await wfmFileService.getCreditNoteDetailsWithPath( + claimDetails.dealerCode, + requestNumber, + isNonGst + ); + if (!creditNoteData.length) { + return ResponseHandler.success(res, [], 'Credit note data fetched successfully'); + } + + // Current requirement: process/store a single credit note per request. + const firstRow = creditNoteData[0] || {}; + const existingAmount = existingCreditNote?.creditNoteAmount ?? 0; + await ClaimCreditNote.upsert({ + requestId, + creditNoteNumber: firstRow.DOC_NO || firstRow.CREDIT_NOTE_NUMBER || existingCreditNote?.creditNoteNumber || undefined, + sapDocumentNumber: firstRow.DOC_NO || firstRow.CREDIT_NOTE_NUMBER || existingCreditNote?.sapDocumentNumber || undefined, + status: firstRow.MSG_TYP || existingCreditNote?.status || undefined, + errorMessage: firstRow.MESSAGE || existingCreditNote?.errorMessage || undefined, + creditNoteFilePath: filePath, + creditNoteAmount: Number(firstRow.CLAIM_AMT || firstRow.CREDIT_AMT || existingAmount || 0), + confirmedAt: new Date() + }); + wfmFileService.deleteCreditNoteOutgoingFileByPath(filePath); + + return ResponseHandler.success(res, [firstRow], 'Credit note data fetched successfully'); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; logger.error('[DealerClaimController] Error fetching credit note WFM data:', error); diff --git a/src/services/dealerClaim.service.ts b/src/services/dealerClaim.service.ts index c613d23..2e906d2 100644 --- a/src/services/dealerClaim.service.ts +++ b/src/services/dealerClaim.service.ts @@ -21,7 +21,7 @@ import { Document } from '../models/Document'; import { Dealer } from '../models/Dealer'; import { WorkflowService } from './workflow.service'; import { DealerClaimApprovalService } from './dealerClaimApproval.service'; -import { generateRequestNumber, padDealerCode } from '../utils/helpers'; +import { buildWfmClaimCsvRow, generateRequestNumber, padDealerCode } from '../utils/helpers'; import { Priority, WorkflowStatus, ApprovalStatus, ParticipantType } from '../types/common.types'; import { sapIntegrationService } from './sapIntegration.service'; import { pwcIntegrationService } from './pwcIntegration.service'; @@ -3625,16 +3625,9 @@ export class DealerClaimService { isNonGst = taxationType === 'Non GST' || taxationType === 'Non-GST'; } - const formatDate = (date: any) => { - const d = new Date(date); - const year = d.getFullYear(); - const month = String(d.getMonth() + 1).padStart(2, '0'); - const day = String(d.getDate()).padStart(2, '0'); - return `${year}${month}${day}`; - }; - const csvData = invoiceItems.map((item: any) => { - const row: any = { + if (isNonGst) { + return { TRNS_UNIQ_NO: item.transactionCode || '', CLAIM_NUMBER: requestNumber, INV_NUMBER: invoice.invoiceNumber || '', @@ -3642,17 +3635,24 @@ export class DealerClaimService { IO_NUMBER: internalOrder?.ioNumber || '', CLAIM_DOC_TYP: sapRefNo, CLAIM_TYPE: claimDetails.activityType, - CLAIM_DATE: formatDate(invoice.invoiceDate || new Date()), + CLAIM_DATE: (() => { + const d = new Date(invoice.invoiceDate || new Date()); + return `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, '0')}${String(d.getDate()).padStart(2, '0')}`; + })(), CLAIM_AMT: item.assAmt - }; - - if (!isNonGst) { - const totalTax = Number(item.igstAmt || 0) + Number(item.cgstAmt || 0) + Number(item.sgstAmt || 0) + Number(item.utgstAmt || 0); - row.GST_AMT = totalTax.toFixed(2); - row.GST_PERCENTAGE = item.gstRt; + }; } - - return row; + + return buildWfmClaimCsvRow({ + item, + requestNumber, + invoiceNumber: invoice.invoiceNumber || '', + invoiceDate: (invoice.invoiceDate as Date) || new Date(), + dealerCode: claimDetails.dealerCode, + ioNumber: internalOrder?.ioNumber || '', + claimDocTyp: sapRefNo, + claimType: claimDetails.activityType || '', + }); }); await wfmFileService.generateIncomingClaimCSV(csvData, `CN_${padDealerCode(claimDetails.dealerCode)}_${requestNumber}.csv`, isNonGst); diff --git a/src/services/wfmFile.service.ts b/src/services/wfmFile.service.ts index 4fa2194..4a49480 100644 --- a/src/services/wfmFile.service.ts +++ b/src/services/wfmFile.service.ts @@ -154,21 +154,67 @@ export class WFMFileService { return path.join(this.basePath, targetPath, fileName); } + private getOutgoingClaimsDir(isNonGst: boolean = false): string { + const targetPath = isNonGst ? this.outgoingNonGstClaimsPath : this.outgoingGstClaimsPath; + return path.join(this.basePath, targetPath); + } + + /** + * Build outgoing credit note file path for a dealer + request. + */ + getCreditNoteOutgoingFilePath(dealerCode: string, requestNumber: string, isNonGst: boolean = false): { fileName: string; filePath: string } { + const dealer = String(dealerCode || '').trim(); + const paddedDealer = dealer.padStart(6, '0'); + const exactCandidates = [ + `CN_${paddedDealer}_${requestNumber}.csv`, + `CN_${dealer}_${requestNumber}.csv`, + ]; + + for (const candidate of exactCandidates) { + const candidatePath = this.getOutgoingPath(candidate, isNonGst); + if (fs.existsSync(candidatePath)) { + return { fileName: candidate, filePath: candidatePath }; + } + } + + // Last fallback: pick any CN_*_.csv from target outgoing folder. + const outgoingDir = this.getOutgoingClaimsDir(isNonGst); + if (fs.existsSync(outgoingDir)) { + const escapedReq = requestNumber.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const reqFileRegex = new RegExp(`^CN_.*_${escapedReq}\\.csv$`, 'i'); + const matched = fs.readdirSync(outgoingDir).find((name) => reqFileRegex.test(name)); + if (matched) { + return { fileName: matched, filePath: path.join(outgoingDir, matched) }; + } + } + + // Keep deterministic default for callers when no file exists yet. + const fileName = exactCandidates[0]; + return { fileName, filePath: this.getOutgoingPath(fileName, isNonGst) }; + } + /** * Get credit note details from outgoing CSV */ async getCreditNoteDetails(dealerCode: string, requestNumber: string, isNonGst: boolean = false): Promise { - const fileName = `CN_${String(dealerCode).padStart(6, '0')}_${requestNumber}.csv`; - const filePath = this.getOutgoingPath(fileName, isNonGst); + const { data } = await this.getCreditNoteDetailsWithPath(dealerCode, requestNumber, isNonGst); + return data; + } + + /** + * Get credit note details and resolved file path from outgoing CSV. + */ + async getCreditNoteDetailsWithPath(dealerCode: string, requestNumber: string, isNonGst: boolean = false): Promise<{ fileName: string; filePath: string; data: any[] }> { + const { fileName, filePath } = this.getCreditNoteOutgoingFilePath(dealerCode, requestNumber, isNonGst); try { if (!fs.existsSync(filePath)) { - return []; + return { fileName, filePath, data: [] }; } const fileContent = fs.readFileSync(filePath, 'utf-8'); const lines = fileContent.split('\n').filter(line => line.trim() !== ''); - if (lines.length <= 1) return []; // Only headers or empty + if (lines.length <= 1) return { fileName, filePath, data: [] }; // Only headers or empty const headers = lines[0].split('|'); const data = lines.slice(1).map(line => { @@ -180,10 +226,25 @@ export class WFMFileService { return row; }); - return data; + return { fileName, filePath, data }; } catch (error) { logger.error(`[WFMFileService] Error reading credit note CSV: ${fileName}`, error); - return []; + return { fileName, filePath, data: [] }; + } + } + + /** + * Delete an outgoing credit note file once it has been persisted. + */ + deleteCreditNoteOutgoingFileByPath(filePath: string): void { + try { + if (fs.existsSync(filePath)) { + fs.unlinkSync(filePath); + logger.info(`[WFMFileService] Deleted processed credit note CSV: ${filePath}`); + } + } catch (error) { + logger.error('[WFMFileService] Error deleting processed credit note CSV:', filePath, error); + throw error; } } diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 6526e4f..036af2b 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -115,3 +115,93 @@ export const padDealerCode = (dealerCode: string | number): string => { if (dealerCode === null || dealerCode === undefined) return ''; return String(dealerCode).padStart(6, '0'); }; + +/** + * WFM incoming claim CSV headers in required fixed order. + */ +export const WFM_CLAIM_CSV_HEADERS = [ + 'TRNS_UNIQ_NO', + 'CLAIM_NUMBER', + 'INV_NUMBER', + 'INV_DATE', + 'DEALER_CODE', + 'IO_NUMBER', + 'CLAIM_DOC_TYP', + 'CLAIM_TYPE', + 'CLAIM_DATE', + 'HSN_CODE', + 'SAC_CODE', + 'PART_AMT', + 'LABOUR_AMT', + 'GST_AMT', + 'GST_PERCENTAGE', +] as const; + +type WfmInvoiceItemLike = { + transactionCode?: string; + hsnCd?: string; + assAmt?: number | string; + gstRt?: number | string; + igstAmt?: number | string; + cgstAmt?: number | string; + sgstAmt?: number | string; + utgstAmt?: number | string; + isServc?: string; +}; + +type BuildWfmRowInput = { + item: WfmInvoiceItemLike; + requestNumber: string; + invoiceNumber: string; + invoiceDate: Date; + dealerCode: string; + ioNumber: string; + claimDocTyp: string; + claimType: string; +}; + +/** + * Build one WFM incoming claim CSV row. + * Business rule: + * - Service line -> SAC_CODE + LABOUR_AMT, keep HSN_CODE + PART_AMT empty + * - Material line -> HSN_CODE + PART_AMT, keep SAC_CODE + LABOUR_AMT empty + */ +export const buildWfmClaimCsvRow = ({ + item, + requestNumber, + invoiceNumber, + invoiceDate, + dealerCode, + ioNumber, + claimDocTyp, + claimType, +}: BuildWfmRowInput): Record<(typeof WFM_CLAIM_CSV_HEADERS)[number], string | number> => { + const d = new Date(invoiceDate); + const invDate = `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, '0')}${String(d.getDate()).padStart(2, '0')}`; + const normalizedIsService = String(item.isServc || '').toUpperCase() === 'Y'; + const hsnOrSac = item.hsnCd || ''; + const assesableAmount = Number(item.assAmt || 0); + const totalTax = + Number(item.igstAmt || 0) + + Number(item.cgstAmt || 0) + + Number(item.sgstAmt || 0) + + Number(item.utgstAmt || 0); + + return { + TRNS_UNIQ_NO: item.transactionCode || '', + CLAIM_NUMBER: requestNumber || '', + INV_NUMBER: invoiceNumber || '', + INV_DATE: invDate, + DEALER_CODE: padDealerCode(dealerCode || ''), + IO_NUMBER: ioNumber || '', + CLAIM_DOC_TYP: claimDocTyp || '', + CLAIM_TYPE: claimType || '', + CLAIM_DATE: invDate, + HSN_CODE: normalizedIsService ? '' : hsnOrSac, + SAC_CODE: normalizedIsService ? hsnOrSac : '', + PART_AMT: normalizedIsService ? '' : assesableAmount.toFixed(2), + LABOUR_AMT: normalizedIsService ? assesableAmount.toFixed(2) : '', + GST_AMT: totalTax.toFixed(2), + GST_PERCENTAGE: item.gstRt ?? '', + }; +}; From f0435c47e462a9cc697b93b7d38d8481ed775193 Mon Sep 17 00:00:00 2001 From: laxman h Date: Wed, 25 Mar 2026 19:24:54 +0530 Subject: [PATCH 2/7] multiple device login restricted and in admin hsn sac code cofiguration added and csv file read approach changed to read at interval of 5 minutes with mutiple cred it note in single csv file --- env.example | 2 +- src/controllers/auth.controller.ts | 19 +- src/controllers/dealerClaim.controller.ts | 59 +++++-- src/controllers/hsnSacCode.controller.ts | 133 ++++++++++++++ src/jobs/creditNoteSyncJob.ts | 25 +++ src/middlewares/auth.middleware.ts | 24 ++- ...4500-add-user-session-and-hsn-sac-codes.ts | 84 +++++++++ ...75000-update-credit-notes-and-add-items.ts | 166 ++++++++++++++++++ src/models/ClaimCreditNote.ts | 100 ++--------- src/models/ClaimCreditNoteItem.ts | 83 +++++++++ src/models/HsnSacCode.ts | 95 ++++++++++ src/models/User.ts | 18 +- src/models/index.ts | 9 + src/routes/hsnSacCode.routes.ts | 56 ++++++ src/routes/index.ts | 2 + src/scripts/auto-setup.ts | 4 + src/scripts/migrate.ts | 3 +- src/server.ts | 2 + src/services/activityTypeSeed.service.ts | 20 +-- src/services/auth.service.ts | 76 ++++++-- src/services/creditNoteSync.service.ts | 161 +++++++++++++++++ src/services/dealerClaim.service.ts | 6 +- src/services/hsnSacCode.service.ts | 158 +++++++++++++++++ src/services/pwcIntegration.service.ts | 8 +- src/services/wfmFile.service.ts | 12 ++ src/types/common.types.ts | 6 + src/utils/responseHandler.ts | 10 +- src/validators/admin.validator.ts | 4 +- 28 files changed, 1198 insertions(+), 147 deletions(-) create mode 100644 src/controllers/hsnSacCode.controller.ts create mode 100644 src/jobs/creditNoteSyncJob.ts create mode 100644 src/migrations/20260325094500-add-user-session-and-hsn-sac-codes.ts create mode 100644 src/migrations/20260325175000-update-credit-notes-and-add-items.ts create mode 100644 src/models/ClaimCreditNoteItem.ts create mode 100644 src/models/HsnSacCode.ts create mode 100644 src/routes/hsnSacCode.routes.ts create mode 100644 src/services/creditNoteSync.service.ts create mode 100644 src/services/hsnSacCode.service.ts diff --git a/env.example b/env.example index daa321a..f884c88 100644 --- a/env.example +++ b/env.example @@ -138,4 +138,4 @@ SAP_DISABLE_SSL_VERIFY=false # WFM Archive configuration examples (if overrides are needed) # WFM_ARCHIVE_GST_CLAIMS_PATH=WFM-QRE\INCOMING\WFM_ARACHIVE\DLR_INC_CLAIMS_GST # WFM_FORM16_CREDIT_ARCHIVE_PATH=WFM-QRE\INCOMING\WFM_ARACHIVE\FORM16_CRDT - +#CREDIT_NOTE_SYNC_INTERVAL_MINUTES=1 diff --git a/src/controllers/auth.controller.ts b/src/controllers/auth.controller.ts index 1cde6a0..7ed7b49 100644 --- a/src/controllers/auth.controller.ts +++ b/src/controllers/auth.controller.ts @@ -23,7 +23,8 @@ export class AuthController { // Validate request body const validatedData = validateSSOCallback(req.body); - const result = await this.authService.handleSSOCallback(validatedData as any); + const userAgent = req.headers['user-agent'] || getRequestMetadata(req).userAgent; + const result = await this.authService.handleSSOCallback(validatedData as any, userAgent); // Log login activity const requestMeta = getRequestMetadata(req); @@ -180,7 +181,8 @@ export class AuthController { const { code, redirectUri } = validateTokenExchange(req.body); logger.info('Tanflow token exchange validation passed', { redirectUri }); - const result = await this.authService.exchangeTanflowCodeForTokens(code, redirectUri); + const userAgent = req.headers['user-agent'] || getRequestMetadata(req).userAgent; + const result = await this.authService.exchangeTanflowCodeForTokens(code, redirectUri, userAgent); // Log login activity const requestMeta = getRequestMetadata(req); @@ -395,6 +397,13 @@ export class AuthController { // Clear all cookies using multiple methods clearCookiesCompletely(); + if (userId !== 'unknown') { + const user = await this.authService.getUserProfile(userId); + if (user) { + await this.authService.updateUserProfile(userId, { sessionToken: null, lastLoginDevice: null }); + } + } + logger.info('User logout successful - cookies cleared', { userId: req.user?.userId || 'unknown', email: req.user?.email || 'unknown', @@ -452,7 +461,8 @@ export class AuthController { const { username, password } = validatePasswordLogin(req.body); - const result = await this.authService.authenticateWithPassword(username, password); + const userAgent = req.headers['user-agent'] || getRequestMetadata(req).userAgent; + const result = await this.authService.authenticateWithPassword(username, password, userAgent); // Log login activity const requestMeta = getRequestMetadata(req); @@ -535,7 +545,8 @@ export class AuthController { const { code, redirectUri } = validateTokenExchange(req.body); logger.info('Token exchange validation passed', { redirectUri }); - const result = await this.authService.exchangeCodeForTokens(code, redirectUri); + const userAgent = req.headers['user-agent'] || getRequestMetadata(req).userAgent; + const result = await this.authService.exchangeCodeForTokens(code, redirectUri, userAgent); // Log login activity const requestMeta = getRequestMetadata(req); diff --git a/src/controllers/dealerClaim.controller.ts b/src/controllers/dealerClaim.controller.ts index 7d9e797..b4c2797 100644 --- a/src/controllers/dealerClaim.controller.ts +++ b/src/controllers/dealerClaim.controller.ts @@ -16,6 +16,7 @@ import { DealerClaimDetails } from '../models/DealerClaimDetails'; import { ClaimInvoice } from '../models/ClaimInvoice'; import { ClaimInvoiceItem } from '../models/ClaimInvoiceItem'; import { ClaimCreditNote } from '../models/ClaimCreditNote'; +import { ClaimCreditNoteItem } from '../models/ClaimCreditNoteItem'; import { ActivityType } from '../models/ActivityType'; import { Participant } from '../models/Participant'; import { sanitizeObject, sanitizePermissive } from '../utils/sanitizer'; @@ -1230,10 +1231,29 @@ export class DealerClaimController { } const { wfmFileService } = await import('../services/wfmFile.service'); - const existingCreditNote = await ClaimCreditNote.findOne({ where: { requestId } }); + const existingCreditNote = await ClaimCreditNote.findOne({ + where: { requestId }, + include: [{ + model: ClaimCreditNoteItem, + as: 'items', + attributes: ['transactionNo'], + order: [['slNo', 'ASC']] + }] + }) as any; + if (existingCreditNote?.sapDocumentNumber || existingCreditNote?.creditNoteNumber) { + let displayTxn = existingCreditNote.transactionNo || ''; + const items = existingCreditNote.items || []; + if (items.length > 1) { + const first = items[0].transactionNo; + const last = items[items.length - 1].transactionNo; + if (first && last && first !== last) { + displayTxn = `${first} - ${last}`; + } + } + const payload = [{ - TRNS_UNIQ_NO: '', + TRNS_UNIQ_NO: displayTxn, CLAIM_NUMBER: requestNumber, DOC_NO: existingCreditNote.sapDocumentNumber || existingCreditNote.creditNoteNumber || '', MSG_TYP: existingCreditNote.status || '', @@ -1247,26 +1267,31 @@ export class DealerClaimController { requestNumber, isNonGst ); + if (!creditNoteData.length) { return ResponseHandler.success(res, [], 'Credit note data fetched successfully'); } - // Current requirement: process/store a single credit note per request. - const firstRow = creditNoteData[0] || {}; - const existingAmount = existingCreditNote?.creditNoteAmount ?? 0; - await ClaimCreditNote.upsert({ - requestId, - creditNoteNumber: firstRow.DOC_NO || firstRow.CREDIT_NOTE_NUMBER || existingCreditNote?.creditNoteNumber || undefined, - sapDocumentNumber: firstRow.DOC_NO || firstRow.CREDIT_NOTE_NUMBER || existingCreditNote?.sapDocumentNumber || undefined, - status: firstRow.MSG_TYP || existingCreditNote?.status || undefined, - errorMessage: firstRow.MESSAGE || existingCreditNote?.errorMessage || undefined, - creditNoteFilePath: filePath, - creditNoteAmount: Number(firstRow.CLAIM_AMT || firstRow.CREDIT_AMT || existingAmount || 0), - confirmedAt: new Date() - }); - wfmFileService.deleteCreditNoteOutgoingFileByPath(filePath); + // Process the file using the sync service (handles grouping and transactions) + const { creditNoteSyncService } = await import('../services/creditNoteSync.service'); + await creditNoteSyncService.processFile(filePath); - return ResponseHandler.success(res, [firstRow], 'Credit note data fetched successfully'); + // Return unified row with range if multiple rows exist for this claim + const claimRows = creditNoteData.filter(row => row.CLAIM_NUMBER === requestNumber); + if (claimRows.length === 0) { + return ResponseHandler.success(res, [], 'Credit note data fetched successfully'); + } + + const claimRow = { ...claimRows[0] }; + if (claimRows.length > 1) { + const first = claimRows[0].TRNS_UNIQ_NO; + const last = claimRows[claimRows.length - 1].TRNS_UNIQ_NO; + if (first && last && first !== last) { + claimRow.TRNS_UNIQ_NO = `${first} - ${last}`; + } + } + + return ResponseHandler.success(res, [claimRow], 'Credit note data fetched successfully'); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; logger.error('[DealerClaimController] Error fetching credit note WFM data:', error); diff --git a/src/controllers/hsnSacCode.controller.ts b/src/controllers/hsnSacCode.controller.ts new file mode 100644 index 0000000..6b515d9 --- /dev/null +++ b/src/controllers/hsnSacCode.controller.ts @@ -0,0 +1,133 @@ +import { Request, Response } from 'express'; +import { hsnSacCodeService } from '../services/hsnSacCode.service'; +import { ResponseHandler } from '../utils/responseHandler'; +import logger from '../utils/logger'; + +export class HsnSacCodeController { + /** + * Get HSN/SAC codes with pagination and search + */ + async getAllCodes(req: Request, res: Response): Promise { + try { + const onlyActive = req.query.active === 'true'; + const page = parseInt(req.query.page as string) || 1; + const limit = parseInt(req.query.limit as string) || 10; + const search = req.query.search as string; + + const result = await hsnSacCodeService.getAllCodes(onlyActive, page, limit, search); + + ResponseHandler.success( + res, + result.codes, + 'HSN/SAC codes fetched successfully', + 200, + result.pagination + ); + } catch (error: any) { + logger.error('Error in getAllCodes controller:', error); + ResponseHandler.error(res, 'Failed to fetch HSN/SAC codes', 500, error.message); + } + } + + /** + * Get code by ID + */ + async getCodeById(req: Request, res: Response): Promise { + try { + const { id } = req.params; + const code = await hsnSacCodeService.getCodeById(id); + if (!code) { + return ResponseHandler.error(res, 'HSN/SAC code not found', 404); + } + ResponseHandler.success(res, code, 'HSN/SAC code fetched successfully'); + } catch (error: any) { + logger.error('Error in getCodeById controller:', error); + ResponseHandler.error(res, 'Failed to fetch HSN/SAC code', 500, error.message); + } + } + + /** + * Create new code + */ + async createCode(req: Request, res: Response): Promise { + try { + const { code, type, gstRate, description, isActive } = req.body; + + if (!code || !type) { + return ResponseHandler.error(res, 'Code and type are required', 400); + } + + const newCode = await hsnSacCodeService.createCode({ + code, + type, + gstRate, + description, + isActive: isActive !== undefined ? isActive : true + }); + + ResponseHandler.success(res, newCode, 'HSN/SAC code created successfully', 201); + } catch (error: any) { + logger.error('Error in createCode controller:', error); + ResponseHandler.error(res, 'Failed to create HSN/SAC code', 500, error.message); + } + } + + /** + * Update code + */ + async updateCode(req: Request, res: Response): Promise { + try { + const { id } = req.params; + const updatedCode = await hsnSacCodeService.updateCode(id, req.body); + + if (!updatedCode) { + return ResponseHandler.error(res, 'HSN/SAC code not found', 404); + } + + ResponseHandler.success(res, updatedCode, 'HSN/SAC code updated successfully'); + } catch (error: any) { + logger.error('Error in updateCode controller:', error); + ResponseHandler.error(res, 'Failed to update HSN/SAC code', 500, error.message); + } + } + + /** + * Delete code + */ + async deleteCode(req: Request, res: Response): Promise { + try { + const { id } = req.params; + const success = await hsnSacCodeService.deleteCode(id); + + if (!success) { + return ResponseHandler.error(res, 'HSN/SAC code not found', 404); + } + + ResponseHandler.success(res, null, 'HSN/SAC code deleted successfully'); + } catch (error: any) { + logger.error('Error in deleteCode controller:', error); + ResponseHandler.error(res, 'Failed to delete HSN/SAC code', 500, error.message); + } + } + + /** + * Toggle active status + */ + async toggleActive(req: Request, res: Response): Promise { + try { + const { id } = req.params; + const updatedCode = await hsnSacCodeService.toggleActive(id); + + if (!updatedCode) { + return ResponseHandler.error(res, 'HSN/SAC code not found', 404); + } + + ResponseHandler.success(res, updatedCode, 'HSN/SAC code status toggled successfully'); + } catch (error: any) { + logger.error('Error in toggleActive controller:', error); + ResponseHandler.error(res, 'Failed to toggle HSN/SAC code status', 500, error.message); + } + } +} + +export const hsnSacCodeController = new HsnSacCodeController(); diff --git a/src/jobs/creditNoteSyncJob.ts b/src/jobs/creditNoteSyncJob.ts new file mode 100644 index 0000000..28ad186 --- /dev/null +++ b/src/jobs/creditNoteSyncJob.ts @@ -0,0 +1,25 @@ +import { creditNoteSyncService } from '../services/creditNoteSync.service'; +import logger from '../utils/logger'; + +/** + * Main sync function to process all outgoing files + * Delegates to creditNoteSyncService + */ +export async function syncCreditNotes(): Promise { + await creditNoteSyncService.syncCreditNotes(); +} + +/** + * Starts the background sync job + */ +export function startCreditNoteSyncJob(): void { + const intervalMinutes = Number(process.env.CREDIT_NOTE_SYNC_INTERVAL_MINUTES) || 5; + logger.info(`[CreditNoteSyncJob] Background job initialized (Interval: ${intervalMinutes}m)`); + + // Run once immediately on startup + syncCreditNotes().catch(err => logger.error('[CreditNoteSyncJob] Initial sync failed:', err)); + + setInterval(() => { + syncCreditNotes().catch(err => logger.error('[CreditNoteSyncJob] Periodic sync failed:', err)); + }, intervalMinutes * 60 * 1000); +} diff --git a/src/middlewares/auth.middleware.ts b/src/middlewares/auth.middleware.ts index 5f864a5..0695679 100644 --- a/src/middlewares/auth.middleware.ts +++ b/src/middlewares/auth.middleware.ts @@ -12,6 +12,7 @@ interface JwtPayload { employeeId: string; email: string; role: string; + sessionToken: string; iat: number; exp: number; } @@ -70,6 +71,15 @@ export const authenticateToken = async ( return; } + if (!decoded.sessionToken || decoded.sessionToken !== user.sessionToken) { + res.status(401).json({ + success: false, + errorCode: 'SESSION_SUPERSEDED', + message: `You have been logged out because an active session was detected from ${user.lastLoginDevice || 'another device'}.` + }); + return; + } + // Attach user info to request object req.user = { userId: user.userId, @@ -117,12 +127,14 @@ export const optionalAuth = async ( const user = await User.findByPk(decoded.userId); if (user && user.isActive) { - req.user = { - userId: user.userId, - email: user.email, - employeeId: user.employeeId || null, // Optional - schema not finalized - role: user.role // Keep uppercase: USER, MANAGEMENT, ADMIN - }; + if (!decoded.sessionToken || decoded.sessionToken === user.sessionToken) { + req.user = { + userId: user.userId, + email: user.email, + employeeId: user.employeeId || null, // Optional - schema not finalized + role: user.role // Keep uppercase: USER, MANAGEMENT, ADMIN + }; + } } } diff --git a/src/migrations/20260325094500-add-user-session-and-hsn-sac-codes.ts b/src/migrations/20260325094500-add-user-session-and-hsn-sac-codes.ts new file mode 100644 index 0000000..51867ae --- /dev/null +++ b/src/migrations/20260325094500-add-user-session-and-hsn-sac-codes.ts @@ -0,0 +1,84 @@ +import { QueryInterface, DataTypes } from 'sequelize'; + +module.exports = { + up: async (queryInterface: QueryInterface) => { + // 1. Add sessionToken to users table + await queryInterface.addColumn('users', 'sessionToken', { + type: DataTypes.UUID, + allowNull: true, + comment: 'Unique token for active session to restrict concurrent logins' + }); + + // 2. Add lastLoginDevice to users table + await queryInterface.addColumn('users', 'lastLoginDevice', { + type: DataTypes.STRING(255), + allowNull: true, + comment: 'Browser/Device string from User-Agent of the active session' + }); + + // 3. Create hsn_sac_codes table + await queryInterface.createTable('hsn_sac_codes', { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true, + }, + code: { + type: DataTypes.STRING(100), + allowNull: false, + comment: 'The HSN or SAC code value' + }, + type: { + type: DataTypes.ENUM('HSN', 'SAC'), + allowNull: false, + comment: 'Type of code: either HSN or SAC' + }, + gstRate: { + type: DataTypes.DECIMAL(5, 2), + allowNull: true, + field: 'gst_rate', + comment: 'Associated GST rate percentage' + }, + description: { + type: DataTypes.TEXT, + allowNull: true, + comment: 'Description of the code' + }, + isActive: { + type: DataTypes.BOOLEAN, + defaultValue: true, + allowNull: false, + field: 'is_active' + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + field: 'created_at' + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + field: 'updated_at' + } + }); + + // Add indexes to hsn_sac_codes + await queryInterface.addIndex('hsn_sac_codes', ['code']); + await queryInterface.addIndex('hsn_sac_codes', ['type']); + await queryInterface.addIndex('hsn_sac_codes', ['is_active']); + }, + + down: async (queryInterface: QueryInterface) => { + // Drop hsn_sac_codes table + await queryInterface.dropTable('hsn_sac_codes'); + + // Remove columns from users table + await queryInterface.removeColumn('users', 'lastLoginDevice'); + await queryInterface.removeColumn('users', 'sessionToken'); + + // Also drop the ENUM type created for hsn_sac_codes type + await queryInterface.sequelize.query('DROP TYPE IF EXISTS "enum_hsn_sac_codes_type";'); + } +}; diff --git a/src/migrations/20260325175000-update-credit-notes-and-add-items.ts b/src/migrations/20260325175000-update-credit-notes-and-add-items.ts new file mode 100644 index 0000000..692ffa8 --- /dev/null +++ b/src/migrations/20260325175000-update-credit-notes-and-add-items.ts @@ -0,0 +1,166 @@ +import { QueryInterface, DataTypes } from 'sequelize'; + +module.exports = { + up: async (queryInterface: QueryInterface) => { + // 1. Update claim_credit_notes table with idempotency checks + const tableDefinition = await queryInterface.describeTable('claim_credit_notes'); + + if (!tableDefinition.transaction_no) { + await queryInterface.addColumn('claim_credit_notes', 'transaction_no', { + type: DataTypes.STRING(100), + allowNull: true, + comment: 'Overall PWC transaction unique number' + }); + } + + if (!tableDefinition.tds_amount) { + await queryInterface.addColumn('claim_credit_notes', 'tds_amount', { + type: DataTypes.DECIMAL(15, 2), + allowNull: true, + defaultValue: 0, + comment: 'TDS amount for the credit note' + }); + } + + if (!tableDefinition.credit_amount) { + await queryInterface.addColumn('claim_credit_notes', 'credit_amount', { + type: DataTypes.DECIMAL(15, 2), + allowNull: true, + defaultValue: 0, + comment: 'Final credit amount after TDS' + }); + } + + // Drop redundant columns if they exist + const columnsToDrop = [ + 'gst_rate', 'gst_amt', 'cgst_rate', 'cgst_amt', + 'sgst_rate', 'sgst_amt', 'igst_rate', 'igst_amt', + 'utgst_rate', 'utgst_amt', 'cess_rate', 'cess_amt', + 'total_amt' + ]; + + for (const column of columnsToDrop) { + if (tableDefinition[column]) { + await queryInterface.removeColumn('claim_credit_notes', column); + } + } + + // 2. Create claim_credit_note_items table (Refined & Unified) + const allTables = await queryInterface.showAllTables(); + const tableExists = allTables.some(t => { + const name = typeof t === 'string' ? t : (t as any).tableName; + return name.toLowerCase() === 'claim_credit_note_items'; + }); + + if (!tableExists) { + await queryInterface.createTable('claim_credit_note_items', { + item_id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true, + }, + credit_note_id: { + type: DataTypes.UUID, + allowNull: false, + references: { + model: 'claim_credit_notes', + key: 'credit_note_id', + }, + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }, + sl_no: { + type: DataTypes.INTEGER, + allowNull: false, + }, + transaction_no: { + type: DataTypes.STRING(100), + allowNull: true, + comment: 'Per-item TRNS_UNIQ_NO' + }, + description: { + type: DataTypes.TEXT, + allowNull: true, + }, + hsn_cd: { + type: DataTypes.STRING(20), + allowNull: true, + }, + amount: { + type: DataTypes.DECIMAL(15, 2), + allowNull: false, + defaultValue: 0, + }, + claim_amount: { + type: DataTypes.DECIMAL(15, 2), + allowNull: true, + defaultValue: 0, + }, + tds_amount: { + type: DataTypes.DECIMAL(15, 2), + allowNull: true, + defaultValue: 0, + }, + credit_amount: { + type: DataTypes.DECIMAL(15, 2), + allowNull: true, + defaultValue: 0, + }, + created_at: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + updated_at: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + } + }); + + // Add index for performance + await queryInterface.addIndex('claim_credit_note_items', ['credit_note_id']); + await queryInterface.addIndex('claim_credit_note_items', ['transaction_no']); + } + }, + + down: async (queryInterface: QueryInterface) => { + // Drop the items table first + await queryInterface.dropTable('claim_credit_note_items'); + + // Re-add dropped columns to claim_credit_notes (if they were removed) + const tableDefinition = await queryInterface.describeTable('claim_credit_notes'); + const columnsToReAdd = [ + { name: 'gst_rate', type: DataTypes.DECIMAL(5, 2) }, + { name: 'gst_amt', type: DataTypes.DECIMAL(15, 2) }, + { name: 'cgst_rate', type: DataTypes.DECIMAL(5, 2) }, + { name: 'cgst_amt', type: DataTypes.DECIMAL(15, 2) }, + { name: 'sgst_rate', type: DataTypes.DECIMAL(5, 2) }, + { name: 'sgst_amt', type: DataTypes.DECIMAL(15, 2) }, + { name: 'igst_rate', type: DataTypes.DECIMAL(5, 2) }, + { name: 'igst_amt', type: DataTypes.DECIMAL(15, 2) }, + { name: 'utgst_rate', type: DataTypes.DECIMAL(5, 2) }, + { name: 'utgst_amt', type: DataTypes.DECIMAL(15, 2) }, + { name: 'cess_rate', type: DataTypes.DECIMAL(5, 2) }, + { name: 'cess_amt', type: DataTypes.DECIMAL(15, 2) }, + { name: 'total_amt', type: DataTypes.DECIMAL(15, 2) } + ]; + + for (const col of columnsToReAdd) { + if (!tableDefinition[col.name]) { + await queryInterface.addColumn('claim_credit_notes', col.name, { + type: col.type, + allowNull: true + }); + } + } + + // Remove added columns + const addedCols = ['credit_amount', 'tds_amount', 'transaction_no']; + for (const col of addedCols) { + if (tableDefinition[col]) { + await queryInterface.removeColumn('claim_credit_notes', col); + } + } + } +}; diff --git a/src/models/ClaimCreditNote.ts b/src/models/ClaimCreditNote.ts index 22128f0..91fa66c 100644 --- a/src/models/ClaimCreditNote.ts +++ b/src/models/ClaimCreditNote.ts @@ -10,19 +10,9 @@ interface ClaimCreditNoteAttributes { creditNoteNumber?: string; creditNoteDate?: Date; creditNoteAmount: number; - gstRate?: number; - gstAmt?: number; - cgstRate?: number; - cgstAmt?: number; - sgstRate?: number; - sgstAmt?: number; - igstRate?: number; - igstAmt?: number; - utgstRate?: number; - utgstAmt?: number; - cessRate?: number; - cessAmt?: number; - totalAmt?: number; + transactionNo?: string; + tdsAmount?: number; + creditAmount?: number; sapDocumentNumber?: string; creditNoteFilePath?: string; status?: string; @@ -35,7 +25,7 @@ interface ClaimCreditNoteAttributes { updatedAt: Date; } -interface ClaimCreditNoteCreationAttributes extends Optional { } +interface ClaimCreditNoteCreationAttributes extends Optional { } class ClaimCreditNote extends Model implements ClaimCreditNoteAttributes { public creditNoteId!: string; @@ -44,19 +34,9 @@ class ClaimCreditNote extends Model { } + +class HsnSacCode extends Model implements HsnSacCodeAttributes { + public id!: string; + public code!: string; + public type!: CodeType; + public gstRate?: number | null; + public description?: string | null; + public isActive!: boolean; + public readonly createdAt!: Date; + public readonly updatedAt!: Date; +} + +HsnSacCode.init({ + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true, + }, + code: { + type: DataTypes.STRING(100), + allowNull: false, + comment: 'The HSN or SAC code value' + }, + type: { + type: DataTypes.ENUM('HSN', 'SAC'), + allowNull: false, + comment: 'Type of code: either HSN or SAC' + }, + gstRate: { + type: DataTypes.DECIMAL(5, 2), + allowNull: true, + field: 'gst_rate', + comment: 'Associated GST rate percentage' + }, + description: { + type: DataTypes.TEXT, + allowNull: true, + comment: 'Description of the code' + }, + isActive: { + type: DataTypes.BOOLEAN, + defaultValue: true, + allowNull: false, + field: 'is_active' + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + field: 'created_at' + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + field: 'updated_at' + } +}, { + sequelize, + modelName: 'HsnSacCode', + tableName: 'hsn_sac_codes', + timestamps: true, + createdAt: 'created_at', + updatedAt: 'updated_at', + indexes: [ + { + fields: ['code'] + }, + { + fields: ['type'] + }, + { + fields: ['is_active'] + } + ] +}); + +export { HsnSacCode }; diff --git a/src/models/User.ts b/src/models/User.ts index 59db741..8cd5578 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -51,11 +51,13 @@ interface UserAttributes { isActive: boolean; role: UserRole; // RBAC: USER, MANAGEMENT, ADMIN lastLogin?: Date; + sessionToken?: string | null; + lastLoginDevice?: string | null; createdAt: Date; updatedAt: Date; } -interface UserCreationAttributes extends Optional { } +interface UserCreationAttributes extends Optional { } class User extends Model implements UserAttributes { public userId!: string; @@ -95,6 +97,8 @@ class User extends Model implements User public isActive!: boolean; public role!: UserRole; // RBAC: USER, MANAGEMENT, ADMIN public lastLogin?: Date; + public sessionToken?: string | null; + public lastLoginDevice?: string | null; public createdAt!: Date; public updatedAt!: Date; @@ -278,6 +282,18 @@ User.init( allowNull: true, field: 'last_login' }, + sessionToken: { + type: DataTypes.UUID, + allowNull: true, + field: 'sessionToken', + comment: 'Unique token for active session to restrict concurrent logins' + }, + lastLoginDevice: { + type: DataTypes.STRING(255), + allowNull: true, + field: 'lastLoginDevice', + comment: 'Browser/Device string from User-Agent of the active session' + }, createdAt: { type: DataTypes.DATE, allowNull: false, diff --git a/src/models/index.ts b/src/models/index.ts index 51d21de..ff5814f 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -29,6 +29,7 @@ import { WorkflowTemplate } from './WorkflowTemplate'; import { ClaimInvoice } from './ClaimInvoice'; import { ClaimInvoiceItem } from './ClaimInvoiceItem'; import { ClaimCreditNote } from './ClaimCreditNote'; +import { ClaimCreditNoteItem } from './ClaimCreditNoteItem'; import { Form16aSubmission } from './Form16aSubmission'; import { Form16CreditNote } from './Form16CreditNote'; import { Form16DebitNote } from './Form16DebitNote'; @@ -178,6 +179,13 @@ const defineAssociations = () => { sourceKey: 'id' }); + // ClaimCreditNote associations + ClaimCreditNote.hasMany(ClaimCreditNoteItem, { + as: 'items', + foreignKey: 'creditNoteId', + sourceKey: 'creditNoteId' + }); + // Note: belongsTo associations are defined in individual model files to avoid duplicate alias conflicts // Only hasMany associations from WorkflowRequest are defined here since they're one-way }; @@ -216,6 +224,7 @@ export { ClaimInvoice, ClaimInvoiceItem, ClaimCreditNote, + ClaimCreditNoteItem, Form16aSubmission, Form16CreditNote, Form16DebitNote, diff --git a/src/routes/hsnSacCode.routes.ts b/src/routes/hsnSacCode.routes.ts new file mode 100644 index 0000000..41a425b --- /dev/null +++ b/src/routes/hsnSacCode.routes.ts @@ -0,0 +1,56 @@ +import { Router } from 'express'; +import { hsnSacCodeController } from '../controllers/hsnSacCode.controller'; +import { authenticateToken } from '../middlewares/auth.middleware'; +import { requireAdmin } from '../middlewares/authorization.middleware'; + +const router = Router(); + +// All HSN/SAC routes require authentication +router.use(authenticateToken); + +/** + * @route GET /api/v1/hsn-sac + * @desc Get all HSN/SAC codes + * @access Private + */ +router.get('/', (req, res) => hsnSacCodeController.getAllCodes(req, res)); + +/** + * @route GET /api/v1/hsn-sac/:id + * @desc Get code by ID + * @access Private + */ +router.get('/:id', (req, res) => hsnSacCodeController.getCodeById(req, res)); + +// Admin only routes for modification +router.use(requireAdmin); + +/** + * @route POST /api/v1/hsn-sac + * @desc Create new HSN/SAC code + * @access Private/Admin + */ +router.post('/', (req, res) => hsnSacCodeController.createCode(req, res)); + +/** + * @route PATCH /api/v1/hsn-sac/:id + * @desc Update HSN/SAC code + * @access Private/Admin + */ +router.patch('/:id', (req, res) => hsnSacCodeController.updateCode(req, res)); + +/** + * @route DELETE /api/v1/hsn-sac/:id + * @desc Delete HSN/SAC code + * @access Private/Admin + */ +router.delete('/:id', (req, res) => hsnSacCodeController.deleteCode(req, res)); + +/** + * @route PATCH /api/v1/hsn-sac/:id/toggle-active + * @desc Toggle active status + * @access Private/Admin + */ +router.patch('/:id/toggle-active', (req, res) => hsnSacCodeController.toggleActive(req, res)); + +export default router; diff --git a/src/routes/index.ts b/src/routes/index.ts index 6cfef11..511e15c 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -34,6 +34,7 @@ import apiTokenRoutes from './apiToken.routes'; import antivirusRoutes from './antivirus.routes'; import dealerExternalRoutes from './dealerExternal.routes'; import form16Routes from './form16.routes'; +import hsnSacCodeRoutes from './hsnSacCode.routes'; const router = Router(); @@ -99,6 +100,7 @@ router.use('/dealers', generalApiLimiter, dealerRoutes); // 200 r router.use('/dealers-external', generalApiLimiter, dealerExternalRoutes); // 200 req/15min router.use('/form16', uploadLimiter, form16Routes); // 50 req/15min (file uploads: extract, submissions, 26as) router.use('/api-tokens', authLimiter, apiTokenRoutes); // 20 req/15min (sensitive — same as auth) +router.use('/hsn-sac', generalApiLimiter, hsnSacCodeRoutes); // 200 req/15min export default router; diff --git a/src/scripts/auto-setup.ts b/src/scripts/auto-setup.ts index 94cb546..35a302f 100644 --- a/src/scripts/auto-setup.ts +++ b/src/scripts/auto-setup.ts @@ -180,6 +180,8 @@ async function runMigrations(): Promise { const m63 = require('../migrations/20260317120001-add-form16-trns-uniq-no'); const m64 = require('../migrations/20260318100001-create-form16-debit-note-sap-responses'); const m65 = require('../migrations/20260318200001-add-sap-response-csv-fields'); + const m66 = require('../migrations/20260325094500-add-user-session-and-hsn-sac-codes'); + const m67 = require('../migrations/20260325175000-update-credit-notes-and-add-items'); const migrations = [ { name: '2025103000-create-users', module: m0 }, @@ -252,6 +254,8 @@ async function runMigrations(): Promise { { name: '20260317120001-add-form16-trns-uniq-no', module: m63 }, { name: '20260318100001-create-form16-debit-note-sap-responses', module: m64 }, { name: '20260318200001-add-sap-response-csv-fields', module: m65 }, + { name: '20260325094500-add-user-session-and-hsn-sac-codes', module: m66 }, + { name: '20260325175000-update-credit-notes-and-add-items', module: m67 }, ]; // Dynamically import sequelize after secrets are loaded diff --git a/src/scripts/migrate.ts b/src/scripts/migrate.ts index 7fba546..c890786 100644 --- a/src/scripts/migrate.ts +++ b/src/scripts/migrate.ts @@ -70,6 +70,7 @@ import * as m62 from '../migrations/20260317100001-create-form16-sap-responses'; import * as m63 from '../migrations/20260317120001-add-form16-trns-uniq-no'; import * as m64 from '../migrations/20260318100001-create-form16-debit-note-sap-responses'; import * as m65 from '../migrations/20260318200001-add-sap-response-csv-fields'; +import * as m66 from '../migrations/20260325094500-add-user-session-and-hsn-sac-codes'; interface Migration { name: string; @@ -147,7 +148,7 @@ const migrations: Migration[] = [ { name: '20260317120001-add-form16-trns-uniq-no', module: m63 }, { name: '20260318100001-create-form16-debit-note-sap-responses', module: m64 }, { name: '20260318200001-add-sap-response-csv-fields', module: m65 }, - + { name: '20260325094500-add-user-session-and-hsn-sac-codes', module: m66 }, ]; /** diff --git a/src/server.ts b/src/server.ts index e963e52..f42b260 100644 --- a/src/server.ts +++ b/src/server.ts @@ -119,6 +119,8 @@ const startServer = async (): Promise => { startForm16NotificationJobs(); const { startForm16ArchiveJob } = require('./services/form16Archive.service'); startForm16ArchiveJob(); + const { startCreditNoteSyncJob } = require('./jobs/creditNoteSyncJob'); + startCreditNoteSyncJob(); // Initialize queue metrics collection for Prometheus initializeQueueMetrics(); diff --git a/src/services/activityTypeSeed.service.ts b/src/services/activityTypeSeed.service.ts index 9a5a4dc..ce99906 100644 --- a/src/services/activityTypeSeed.service.ts +++ b/src/services/activityTypeSeed.service.ts @@ -9,19 +9,19 @@ import { ActivityType } from '@models/ActivityType'; */ const DEFAULT_ACTIVITY_TYPES = [ { title: 'Riders Mania Claims', itemCode: '1', taxationType: 'Non GST', sapRefNo: 'ZRDM', creditPostingOn: 'Spares' }, - { title: 'Marketing Cost – Bike to Vendor', itemCode: '2', taxationType: 'Non GST', sapRefNo: 'ZMBV', creditPostingOn: 'Vehicle' }, + { title: 'Marketing Cost – Bike to Vendor', itemCode: '2', taxationType: 'Non GST', sapRefNo: 'ZMBV', creditPostingOn: 'Vehicles' }, { title: 'Media Bike Service', itemCode: '3', taxationType: 'GST', sapRefNo: 'ZMBS', creditPostingOn: 'Spares' }, - { title: 'ARAI Motorcycle Liquidation', itemCode: '4', taxationType: 'GST', sapRefNo: 'ZAML', creditPostingOn: 'Vehicle' }, - { title: 'ARAI Certification – STA Approval CNR', itemCode: '5', taxationType: 'Non GST', sapRefNo: 'ZACS', creditPostingOn: 'Vehicle' }, + { title: 'ARAI Motorcycle Liquidation', itemCode: '4', taxationType: 'GST', sapRefNo: 'ZAML', creditPostingOn: 'Vehicles' }, + { title: 'ARAI Certification – STA Approval CNR', itemCode: '5', taxationType: 'Non GST', sapRefNo: 'ZACS', creditPostingOn: 'Vehicles' }, { title: 'Procurement of Spares/Apparel/GMA for Events', itemCode: '6', taxationType: 'GST', sapRefNo: 'ZPPE', creditPostingOn: 'Spares' }, - { title: 'Fuel for Media Bike Used for Event', itemCode: '7', taxationType: 'Non GST', sapRefNo: 'ZFMB', creditPostingOn: 'Vehicle' }, - { title: 'Motorcycle Buyback and Goodwill Support', itemCode: '8', taxationType: 'Non GST', sapRefNo: 'ZMBG', creditPostingOn: 'Vehicle' }, - { title: 'Liquidation of Used Motorcycle', itemCode: '9', taxationType: 'GST', sapRefNo: 'ZLUM', creditPostingOn: 'Vehicle' }, - { title: 'Motorcycle Registration CNR (Owned or Gifted by RE)', itemCode: '10', taxationType: 'GST', sapRefNo: 'ZMRC', creditPostingOn: 'Vehicle' }, - { title: 'Legal Claims Reimbursement', itemCode: '11', taxationType: 'Non GST', sapRefNo: 'ZLCR', creditPostingOn: 'Vehicle' }, + { title: 'Fuel for Media Bike Used for Event', itemCode: '7', taxationType: 'Non GST', sapRefNo: 'ZFMB', creditPostingOn: 'Vehicles' }, + { title: 'Motorcycle Buyback and Goodwill Support', itemCode: '8', taxationType: 'Non GST', sapRefNo: 'ZMBG', creditPostingOn: 'Vehicles' }, + { title: 'Liquidation of Used Motorcycle', itemCode: '9', taxationType: 'GST', sapRefNo: 'ZLUM', creditPostingOn: 'Vehicles' }, + { title: 'Motorcycle Registration CNR (Owned or Gifted by RE)', itemCode: '10', taxationType: 'GST', sapRefNo: 'ZMRC', creditPostingOn: 'Vehicles' }, + { title: 'Legal Claims Reimbursement', itemCode: '11', taxationType: 'Non GST', sapRefNo: 'ZLCR', creditPostingOn: 'Vehicles' }, { title: 'Service Camp Claims', itemCode: '12', taxationType: 'Non GST', sapRefNo: 'ZSCC', creditPostingOn: 'Spares' }, - { title: 'Corporate Claims – Institutional Sales PD', itemCode: '13', taxationType: 'Non GST', sapRefNo: 'ZCCN', creditPostingOn: 'Vehicle' }, - { title: 'Corporate Claims – Institutional Sales PD', itemCode: '14', taxationType: 'GST', sapRefNo: 'ZCCG', creditPostingOn: 'Vehicle' } + { title: 'Corporate Claims – Institutional Sales PD', itemCode: '13', taxationType: 'Non GST', sapRefNo: 'ZCCN', creditPostingOn: 'Vehicles' }, + { title: 'Corporate Claims – Institutional Sales PD', itemCode: '14', taxationType: 'GST', sapRefNo: 'ZCCG', creditPostingOn: 'Vehicles' } ]; /** diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts index ab822ec..c24df92 100644 --- a/src/services/auth.service.ts +++ b/src/services/auth.service.ts @@ -6,6 +6,27 @@ import { LoginResponse } from '../types/auth.types'; import logger, { logAuthEvent } from '../utils/logger'; import axios from 'axios'; import bcrypt from 'bcryptjs'; +import { v4 as uuidv4 } from 'uuid'; +import { emitToUser } from '../realtime/socket'; + +function parseDeviceFromUserAgent(ua?: string): string { + if (!ua) return 'Unknown Device'; + let browser = 'Unknown Browser'; + if (ua.includes('Firefox/')) browser = 'Firefox'; + else if (ua.includes('Chrome/') && !ua.includes('Edg/')) browser = 'Chrome'; + else if (ua.includes('Safari/') && !ua.includes('Chrome/')) browser = 'Safari'; + else if (ua.includes('Edg/')) browser = 'Edge'; + + let os = 'Unknown OS'; + if (ua.includes('Windows')) os = 'Windows'; + else if (ua.includes('Mac OS')) os = 'macOS'; + else if (ua.includes('Linux')) os = 'Linux'; + else if (ua.includes('Android')) os = 'Android'; + else if (ua.includes('iOS') || ua.includes('iPhone') || ua.includes('iPad')) os = 'iOS'; + + return `${browser} on ${os}`; +} + export class AuthService { /** @@ -240,7 +261,7 @@ export class AuthService { * Handle SSO callback from frontend * Creates new user or updates existing user based on employeeId */ - async handleSSOCallback(userData: SSOUserData): Promise { + async handleSSOCallback(userData: SSOUserData, userAgent?: string): Promise { try { // Validate required fields - email and oktaSub are required if (!userData.email || !userData.oktaSub) { @@ -272,11 +293,16 @@ export class AuthService { displayName = userData.email.split('@')[0] || 'User'; } + const sessionToken = uuidv4(); + const lastLoginDevice = parseDeviceFromUserAgent(userAgent); + // Prepare update/create data - always include required fields const userUpdateData: any = { email: userData.email, oktaSub: userData.oktaSub, lastLogin: new Date(), + sessionToken, + lastLoginDevice, isActive: true, }; @@ -315,6 +341,14 @@ export class AuthService { action: 'user_updated', updatedFields: Object.keys(userUpdateData), }); + + // Notify previous session via WebSocket to logout immediately + // This provides real-time "Last-In-Wins" enforcement + emitToUser(user.userId, 'SESSION_SUPERSEDED', { + reason: 'CONCURRENT_LOGIN', + device: lastLoginDevice, + timestamp: new Date().toISOString() + }); } else { // Create new user with required fields (email and oktaSub) user = await User.create({ @@ -335,7 +369,9 @@ export class AuthService { employeeNumber: userData.employeeNumber || userData.dealerCode || null, isActive: true, role: 'USER', - lastLogin: new Date() + lastLogin: new Date(), + sessionToken, + lastLoginDevice }); logAuthEvent('sso_callback', user.userId, { @@ -390,7 +426,8 @@ export class AuthService { userId: user.userId, employeeId: user.employeeId, email: user.email, - role: user.role // Keep uppercase: USER, MANAGEMENT, ADMIN + role: user.role, // Keep uppercase: USER, MANAGEMENT, ADMIN + sessionToken: user.sessionToken }; const options: SignOptions = { @@ -410,7 +447,8 @@ export class AuthService { const payload = { userId: user.userId, - type: 'refresh' + type: 'refresh', + sessionToken: user.sessionToken }; const options: SignOptions = { @@ -447,6 +485,10 @@ export class AuthService { throw new Error('User not found or inactive'); } + if (decoded.sessionToken !== user.sessionToken) { + throw new Error('Session expired due to login from another device'); + } + return this.generateAccessToken(user); } catch (error) { logAuthEvent('auth_failure', undefined, { @@ -497,7 +539,7 @@ export class AuthService { * 4. Create/update user in our database if needed * 5. Return our JWT tokens */ - async authenticateWithPassword(username: string, password: string): Promise { + async authenticateWithPassword(username: string, password: string, userAgent?: string): Promise { // Demo admin: admin@example.com / Admin@123 (works with or without .env; for dev/demo only) const DEMO_ADMIN_EMAIL = 'admin@example.com'; const DEFAULT_DEMO_ADMIN_HASH = '$2a$10$H4ikTC.HDZPM0iFxjBy2C./WlkbGbidipIiZlXIJx6QpcBazdf12K'; // bcrypt of "Admin@123" @@ -509,6 +551,9 @@ export class AuthService { const passwordMatch = await bcrypt.compare(password, hash); if (!passwordMatch) return null; let user = await User.findOne({ where: { email: adminEmail } }); + const sessionToken = uuidv4(); + const lastLoginDevice = parseDeviceFromUserAgent(userAgent); + if (!user) { user = await User.create({ email: adminEmail, @@ -521,10 +566,13 @@ export class AuthService { emailNotificationsEnabled: true, pushNotificationsEnabled: true, inAppNotificationsEnabled: true, + sessionToken, + lastLoginDevice, + lastLogin: new Date() }); logger.info('Demo admin user created on first login', { email: adminEmail }); } else { - await user.update({ lastLogin: new Date() }); + await user.update({ lastLogin: new Date(), sessionToken, lastLoginDevice }); } logger.info('Demo admin login successful', { email: adminEmail }); const accessToken = this.generateAccessToken(user); @@ -563,7 +611,7 @@ export class AuthService { displayName: 'Test Reflow Dealer', firstName: 'Test', lastName: 'Reflow', - }); + }, userAgent); }; // Fallback bcrypt hash for "Test@123" when .env hash is corrupted (dev only) @@ -608,7 +656,9 @@ export class AuthService { logger.warn('Local dealer login by email: user not found', { email }); return null; } - await user.update({ lastLogin: new Date() }); + const sessionToken = uuidv4(); + const lastLoginDevice = parseDeviceFromUserAgent(userAgent); + await user.update({ lastLogin: new Date(), sessionToken, lastLoginDevice }); logger.info('Local dealer login by email successful', { email }); const accessToken = this.generateAccessToken(user); const refreshToken = this.generateRefreshToken(user); @@ -735,7 +785,7 @@ export class AuthService { }); // Step 4: Create/update user in our database - const result = await this.handleSSOCallback(userData); + const result = await this.handleSSOCallback(userData, userAgent); logger.info('User authenticated successfully via password flow', { userId: result.user.userId, @@ -791,7 +841,7 @@ export class AuthService { * This is the FRONTEND callback URL (e.g., http://localhost:3000/login/callback), * NOT the backend URL. Okta verifies this matches to prevent redirect URI attacks. */ - async exchangeCodeForTokens(code: string, redirectUri: string): Promise { + async exchangeCodeForTokens(code: string, redirectUri: string, userAgent?: string): Promise { try { // Validate configuration if (!ssoConfig.oktaClientId || ssoConfig.oktaClientId.trim() === '') { @@ -926,7 +976,7 @@ export class AuthService { }); // Handle SSO callback to create/update user and generate our tokens - const result = await this.handleSSOCallback(userData); + const result = await this.handleSSOCallback(userData, userAgent); // Return our JWT tokens along with Okta tokens (store Okta refresh token for future use) return { @@ -970,7 +1020,7 @@ export class AuthService { * Exchange Tanflow authorization code for tokens * Similar to Okta flow but uses Tanflow IAM endpoints */ - async exchangeTanflowCodeForTokens(code: string, redirectUri: string): Promise { + async exchangeTanflowCodeForTokens(code: string, redirectUri: string, userAgent?: string): Promise { try { // Validate configuration if (!ssoConfig.tanflowClientId || ssoConfig.tanflowClientId.trim() === '') { @@ -1138,7 +1188,7 @@ export class AuthService { }); // Handle SSO callback to create/update user and generate our tokens - const result = await this.handleSSOCallback(userData); + const result = await this.handleSSOCallback(userData, userAgent); // Return our JWT tokens along with Tanflow tokens return { diff --git a/src/services/creditNoteSync.service.ts b/src/services/creditNoteSync.service.ts new file mode 100644 index 0000000..40c3687 --- /dev/null +++ b/src/services/creditNoteSync.service.ts @@ -0,0 +1,161 @@ +import fs from 'fs'; +import { wfmFileService } from './wfmFile.service'; +import { WorkflowRequest } from '../models/WorkflowRequest'; +import { ClaimCreditNote } from '../models/ClaimCreditNote'; +import { ClaimCreditNoteItem } from '../models/ClaimCreditNoteItem'; +import { sequelize } from '@config/database'; +import logger from '../utils/logger'; + +export class CreditNoteSyncService { + /** + * Main sync function to process all outgoing files + */ + async syncCreditNotes(): Promise { + try { + const gstFiles = wfmFileService.listOutgoingFiles(false); + const nonGstFiles = wfmFileService.listOutgoingFiles(true); + + const allFiles = [ + ...gstFiles.map(f => ({ path: f, isNonGst: false })), + ...nonGstFiles.map(f => ({ path: f, isNonGst: true })) + ]; + + if (allFiles.length === 0) return; + + logger.info(`[CreditNoteSyncService] Found ${allFiles.length} files to process`); + + for (const fileInfo of allFiles) { + await this.processFile(fileInfo.path); + } + } catch (error) { + logger.error('[CreditNoteSyncService] Error during sync:', error); + } + } + + /** + * Process a single CSV file + */ + async processFile(filePath: string): Promise { + try { + if (!fs.existsSync(filePath)) return false; + + const fileContent = fs.readFileSync(filePath, 'utf-8'); + const lines = fileContent.split(/\r?\n/).filter(l => l.trim() !== ''); + if (lines.length <= 1) { + // Empty or only headers - delete it + fs.unlinkSync(filePath); + logger.info(`[CreditNoteSyncService] Deleted empty/header-only file: ${filePath}`); + return true; + } + + const headers = lines[0].split('|').map(h => h.trim().toUpperCase()); + const rows = lines.slice(1).map(line => { + const values = line.split('|'); + const row: any = {}; + headers.forEach((h, i) => { row[h] = values[i]?.trim() || ''; }); + return row; + }); + + // Group rows by CLAIM_NUMBER + const groups: Record = {}; + rows.forEach(row => { + const claimNum = row.CLAIM_NUMBER; + if (!claimNum) return; + if (!groups[claimNum]) groups[claimNum] = []; + groups[claimNum].push(row); + }); + + // Process each group + let allProcessed = true; + for (const [claimNumber, rows] of Object.entries(groups)) { + const success = await this.processClaimGroup(claimNumber, rows, filePath); + if (!success) { + allProcessed = false; + logger.warn(`[CreditNoteSyncService] Failed to process claim group ${claimNumber} in file ${filePath}`); + } + } + + if (allProcessed && rows.length > 0) { + fs.unlinkSync(filePath); + logger.info(`[CreditNoteSyncService] Successfully processed and deleted file: ${filePath}`); + return true; + } + return false; + } catch (error) { + logger.error(`[CreditNoteSyncService] Error processing file ${filePath}:`, error); + return false; + } + } + + private async processClaimGroup(claimNumber: string, rows: any[], filePath: string): Promise { + const t = await sequelize.transaction(); + try { + // 1. Find the request by requestNumber (which is the CLAIM_NUMBER in CSV) + const request = await WorkflowRequest.findOne({ where: { requestNumber: claimNumber }, transaction: t }); + if (!request) { + logger.warn(`[CreditNoteSyncService] WorkflowRequest not found for claim number: ${claimNumber}`); + await t.rollback(); + // We return true here because we might still want to delete the file if other claims are processed + // or if this is a filtered/old claim we don't care about. + return true; + } + + const requestId = request.requestId; + + // 2. Calculate totals + let totalAmount = 0; + let totalTds = 0; + let totalCredit = 0; + rows.forEach(row => { + totalAmount += Number(row.CLAIM_AMT || row.CREDIT_AMT || 0); + totalTds += Number(row.TDS_AMT || 0); + totalCredit += Number(row.CREDIT_AMT || row.FINAL_AMT || 0); + }); + + const firstRow = rows[0]; + + // 3. Upsert Header + const [cnHeader] = await ClaimCreditNote.upsert({ + requestId, + creditNoteNumber: firstRow.DOC_NO || firstRow.CREDIT_NOTE_NUMBER || undefined, + sapDocumentNumber: firstRow.DOC_NO || firstRow.CREDIT_NOTE_NUMBER || undefined, + status: firstRow.MSG_TYP || 'CONFIRMED', + errorMessage: firstRow.MESSAGE || undefined, + creditNoteFilePath: filePath, + creditNoteAmount: totalAmount, + transactionNo: firstRow.TRNS_UNIQ_NO || undefined, + tdsAmount: totalTds, + creditAmount: totalCredit, + confirmedAt: new Date() + }, { transaction: t, returning: true }); + + // 4. Update Line Items + // Clear existing items + await ClaimCreditNoteItem.destroy({ where: { creditNoteId: cnHeader.creditNoteId }, transaction: t }); + + // Bulk create new items + const itemsToCreate = rows.map((row, index) => ({ + creditNoteId: cnHeader.creditNoteId, + slNo: index + 1, + transactionNo: row.TRNS_UNIQ_NO, + description: row.DESCRIPTION || row.MESSAGE || '', + hsnCd: row.HSN_CODE || row.SAC_CODE || '', + amount: Number(row.FINAL_AMT || row.CREDIT_AMT || 0), + claimAmount: Number(row.CLAIM_AMT || 0), + tdsAmount: Number(row.TDS_AMT || 0), + creditAmount: Number(row.FINAL_AMT || row.CREDIT_AMT || 0) + })); + + await ClaimCreditNoteItem.bulkCreate(itemsToCreate, { transaction: t }); + + await t.commit(); + return true; + } catch (error) { + if (t) await t.rollback(); + logger.error(`[CreditNoteSyncService] Error processing claim ${claimNumber}:`, error); + return false; + } + } +} + +export const creditNoteSyncService = new CreditNoteSyncService(); diff --git a/src/services/dealerClaim.service.ts b/src/services/dealerClaim.service.ts index 2e906d2..78416d5 100644 --- a/src/services/dealerClaim.service.ts +++ b/src/services/dealerClaim.service.ts @@ -10,6 +10,7 @@ import { InternalOrder, IOStatus } from '../models/InternalOrder'; import { ClaimBudgetTracking, BudgetStatus } from '../models/ClaimBudgetTracking'; import { ClaimInvoice } from '../models/ClaimInvoice'; import { ClaimCreditNote } from '../models/ClaimCreditNote'; +import { ClaimCreditNoteItem } from '../models/ClaimCreditNoteItem'; import { ClaimInvoiceItem } from '../models/ClaimInvoiceItem'; import { DealerCompletionExpense } from '../models/DealerCompletionExpense'; import { ApprovalLevel } from '../models/ApprovalLevel'; @@ -1249,9 +1250,10 @@ export class DealerClaimService { where: { requestId } }); - // Fetch Credit Note details + // Fetch Credit Note details with items const claimCreditNote = await ClaimCreditNote.findOne({ - where: { requestId } + where: { requestId }, + include: [{ model: ClaimCreditNoteItem, as: 'items' }] }); // Fetch Completion Expenses (individual expense items) diff --git a/src/services/hsnSacCode.service.ts b/src/services/hsnSacCode.service.ts new file mode 100644 index 0000000..ce0a852 --- /dev/null +++ b/src/services/hsnSacCode.service.ts @@ -0,0 +1,158 @@ +import { Op } from 'sequelize'; +import { HsnSacCode, HsnSacCodeAttributes, HsnSacCodeCreationAttributes } from '../models/HsnSacCode'; +import logger from '../utils/logger'; + +export interface PaginatedHsnSacCodes { + codes: HsnSacCode[]; + pagination: { + totalRecords: number; + totalPages: number; + currentPage: number; + limit: number; + }; +} + +export class HsnSacCodeService { + /** + * Get HSN/SAC codes with pagination and search + */ + async getAllCodes( + onlyActive: boolean = false, + page: number = 1, + limit: number = 10, + search?: string + ): Promise { + try { + const where: any = {}; + if (onlyActive) { + where.isActive = true; + } + + if (search && search.trim()) { + const searchTerm = `%${search.trim()}%`; + where[Op.or] = [ + { code: { [Op.like]: searchTerm } }, + { description: { [Op.like]: searchTerm } } + ]; + } + + const offset = (page - 1) * limit; + + const { count, rows } = await HsnSacCode.findAndCountAll({ + where, + order: [['type', 'ASC'], ['code', 'ASC']], + limit, + offset + }); + + return { + codes: rows, + pagination: { + totalRecords: count, + totalPages: Math.ceil(count / limit), + currentPage: page, + limit + } + }; + } catch (error) { + logger.error('Error fetching HSN/SAC codes:', error); + throw error; + } + } + + /** + * Get a single code by ID + */ + async getCodeById(id: string): Promise { + try { + return await HsnSacCode.findByPk(id); + } catch (error) { + logger.error(`Error fetching HSN/SAC code with id ${id}:`, error); + throw error; + } + } + + /** + * Create a new HSN/SAC code with duplicate check + */ + async createCode(data: HsnSacCodeCreationAttributes): Promise { + try { + // Check for duplicates + const existing = await HsnSacCode.findOne({ + where: { + code: data.code, + type: data.type + } + }); + + if (existing) { + throw new Error(`${data.type} code "${data.code}" already exists`); + } + + return await HsnSacCode.create(data); + } catch (error) { + logger.error('Error creating HSN/SAC code:', error); + throw error; + } + } + + /** + * Update an existing HSN/SAC code with duplicate check + */ + async updateCode(id: string, data: Partial): Promise { + try { + const code = await HsnSacCode.findByPk(id); + if (!code) return null; + + // If code or type is being updated, check for duplicates + if (data.code || data.type) { + const existing = await HsnSacCode.findOne({ + where: { + code: data.code || code.code, + type: data.type || code.type, + id: { [Op.ne]: id } + } + }); + + if (existing) { + throw new Error(`${data.type || code.type} code "${data.code || code.code}" already exists`); + } + } + + return await code.update(data); + } catch (error) { + logger.error(`Error updating HSN/SAC code with id ${id}:`, error); + throw error; + } + } + + /** + * Delete an HSN/SAC code + */ + async deleteCode(id: string): Promise { + try { + const result = await HsnSacCode.destroy({ where: { id } }); + return result > 0; + } catch (error) { + logger.error(`Error deleting HSN/SAC code with id ${id}:`, error); + throw error; + } + } + + /** + * Toggle active status + */ + async toggleActive(id: string): Promise { + try { + const code = await HsnSacCode.findByPk(id); + if (!code) return null; + + return await code.update({ isActive: !code.isActive }); + } catch (error) { + logger.error(`Error toggling active status for HSN/SAC code with id ${id}:`, error); + throw error; + } + } +} + +export const hsnSacCodeService = new HsnSacCodeService(); diff --git a/src/services/pwcIntegration.service.ts b/src/services/pwcIntegration.service.ts index 2c7a2a6..f74c01b 100644 --- a/src/services/pwcIntegration.service.ts +++ b/src/services/pwcIntegration.service.ts @@ -392,7 +392,7 @@ export class PWCIntegrationService { const payload = [ { - User_GSTIN: dealerGst, // Portal User ID (Dealer's own GST) + User_GSTIN: '27AAAPI3182M002', // Portal User ID (Dealer's own GST) Version: "1.01", IRN: "", SourceSystem: "RE_WORKFLOW", @@ -422,13 +422,13 @@ export class PWCIntegrationService { Dt: new Date().toLocaleDateString('en-GB') // DD/MM/YYYY }, SellerDtls: { - Gstin: dealerGst, // Actual dealer GST from local table + Gstin: '27AAAPI3182M002', // Actual dealer GST from local table LglNm: dealer?.dealerName || 'Dealer', TrdNm: dealer?.dealerName || 'Dealer', Addr1: dealer?.city || "Address Line 1", Loc: dealer?.city || "Location", - Pin: Number(dealer?.pincode || 600001), - Stcd: dealerStateCode, + Pin: 400001, + Stcd: '27', Ph: dealer?.phone || "9998887776", Em: dealer?.email || "Supplier@inv.com" }, diff --git a/src/services/wfmFile.service.ts b/src/services/wfmFile.service.ts index 4a49480..d5ee17c 100644 --- a/src/services/wfmFile.service.ts +++ b/src/services/wfmFile.service.ts @@ -159,6 +159,18 @@ export class WFMFileService { return path.join(this.basePath, targetPath); } + /** + * List all outgoing CSV files in the folder. + */ + listOutgoingFiles(isNonGst: boolean = false): string[] { + const outgoingDir = this.getOutgoingClaimsDir(isNonGst); + if (!fs.existsSync(outgoingDir)) return []; + + return fs.readdirSync(outgoingDir) + .filter(file => file.toLowerCase().endsWith('.csv')) + .map(file => path.join(outgoingDir, file)); + } + /** * Build outgoing credit note file path for a dealer + request. */ diff --git a/src/types/common.types.ts b/src/types/common.types.ts index f935645..11e78bd 100644 --- a/src/types/common.types.ts +++ b/src/types/common.types.ts @@ -39,6 +39,12 @@ export interface ApiResponse { message: string; data?: T; error?: string; + pagination?: { + totalRecords: number; + totalPages: number; + currentPage: number; + limit: number; + }; timestamp: Date; } diff --git a/src/utils/responseHandler.ts b/src/utils/responseHandler.ts index 09bae3e..6e218cb 100644 --- a/src/utils/responseHandler.ts +++ b/src/utils/responseHandler.ts @@ -7,16 +7,22 @@ export class ResponseHandler { res: Response, data: T, message: string = 'Success', - statusCode: number = 200 + statusCode: number = 200, + pagination?: { + totalRecords: number; + totalPages: number; + currentPage: number; + limit: number; + } ): void { const response: ApiResponse = { success: true, message, data, + pagination, timestamp: new Date(), }; - res.status(statusCode).json(response); } diff --git a/src/validators/admin.validator.ts b/src/validators/admin.validator.ts index 633137b..046b30d 100644 --- a/src/validators/admin.validator.ts +++ b/src/validators/admin.validator.ts @@ -58,8 +58,8 @@ export const createActivityTypeSchema = z.object({ errorMap: () => ({ message: 'Taxation type must be GST or Non GST' }), }), sapRefNo: z.string().min(1, 'SAP ref number (Claim Document Type) is required').max(50, 'SAP ref number too long'), - creditPostingOn: z.enum(['Spares', 'Vehicle', 'GMA', 'Apparel'], { - errorMap: () => ({ message: 'Credit posting on must be Spares, Vehicle, GMA or Apparel' }), + creditPostingOn: z.enum(['Spares', 'Vehicle', 'Vehicles', 'GMA', 'Apparel'], { + errorMap: () => ({ message: 'Credit posting on must be Spares, Vehicle, Vehicles, GMA or Apparel' }), }), }); From 4052f3fe1fdc29a65420aa4a1e705694029800b3 Mon Sep 17 00:00:00 2001 From: laxmanhalaki Date: Wed, 25 Mar 2026 20:12:34 +0530 Subject: [PATCH 3/7] invoice dummy gst removed --- src/services/pwcIntegration.service.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/services/pwcIntegration.service.ts b/src/services/pwcIntegration.service.ts index f74c01b..2c7a2a6 100644 --- a/src/services/pwcIntegration.service.ts +++ b/src/services/pwcIntegration.service.ts @@ -392,7 +392,7 @@ export class PWCIntegrationService { const payload = [ { - User_GSTIN: '27AAAPI3182M002', // Portal User ID (Dealer's own GST) + User_GSTIN: dealerGst, // Portal User ID (Dealer's own GST) Version: "1.01", IRN: "", SourceSystem: "RE_WORKFLOW", @@ -422,13 +422,13 @@ export class PWCIntegrationService { Dt: new Date().toLocaleDateString('en-GB') // DD/MM/YYYY }, SellerDtls: { - Gstin: '27AAAPI3182M002', // Actual dealer GST from local table + Gstin: dealerGst, // Actual dealer GST from local table LglNm: dealer?.dealerName || 'Dealer', TrdNm: dealer?.dealerName || 'Dealer', Addr1: dealer?.city || "Address Line 1", Loc: dealer?.city || "Location", - Pin: 400001, - Stcd: '27', + Pin: Number(dealer?.pincode || 600001), + Stcd: dealerStateCode, Ph: dealer?.phone || "9998887776", Em: dealer?.email || "Supplier@inv.com" }, From 919b92d10e16ca29a70e4d5dfa76727eb5c4f2d9 Mon Sep 17 00:00:00 2001 From: laxman h Date: Thu, 26 Mar 2026 10:21:51 +0530 Subject: [PATCH 4/7] data consumption credt_total_amount key mapped --- src/services/creditNoteSync.service.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/services/creditNoteSync.service.ts b/src/services/creditNoteSync.service.ts index 40c3687..a5d1507 100644 --- a/src/services/creditNoteSync.service.ts +++ b/src/services/creditNoteSync.service.ts @@ -107,9 +107,9 @@ export class CreditNoteSyncService { let totalTds = 0; let totalCredit = 0; rows.forEach(row => { - totalAmount += Number(row.CLAIM_AMT || row.CREDIT_AMT || 0); + totalAmount += Number(row.CREDITED_TOTAL_AMT || row.CLAIM_AMT || row.CREDIT_AMT || 0); totalTds += Number(row.TDS_AMT || 0); - totalCredit += Number(row.CREDIT_AMT || row.FINAL_AMT || 0); + totalCredit += Number(row.CREDITED_TOTAL_AMT || row.CREDIT_AMT || row.FINAL_AMT || 0); }); const firstRow = rows[0]; @@ -140,10 +140,10 @@ export class CreditNoteSyncService { transactionNo: row.TRNS_UNIQ_NO, description: row.DESCRIPTION || row.MESSAGE || '', hsnCd: row.HSN_CODE || row.SAC_CODE || '', - amount: Number(row.FINAL_AMT || row.CREDIT_AMT || 0), - claimAmount: Number(row.CLAIM_AMT || 0), + amount: Number(row.CREDITED_TOTAL_AMT || row.FINAL_AMT || row.CREDIT_AMT || 0), + claimAmount: Number(row.CREDITED_TOTAL_AMT || row.CLAIM_AMT || 0), tdsAmount: Number(row.TDS_AMT || 0), - creditAmount: Number(row.FINAL_AMT || row.CREDIT_AMT || 0) + creditAmount: Number(row.CREDITED_TOTAL_AMT || row.FINAL_AMT || row.CREDIT_AMT || 0) })); await ClaimCreditNoteItem.bulkCreate(itemsToCreate, { transaction: t }); From 88a86b867b14027aaff6d2628ea031208be93474 Mon Sep 17 00:00:00 2001 From: laxman h Date: Thu, 26 Mar 2026 14:38:18 +0530 Subject: [PATCH 5/7] new build added after front end caim amount visibility changes --- ...-CrzbbxDV.js => conclusionApi-CELRkQli.js} | 2 +- build/assets/index-BiBh7ROe.js | 64 ------------------- build/assets/index-Cealtfd6.js | 64 +++++++++++++++++++ build/assets/index-ZIAcQZA4.css | 1 - build/assets/index-qC8Q4GZ7.css | 1 + build/index.html | 46 ++++++------- 6 files changed, 89 insertions(+), 89 deletions(-) rename build/assets/{conclusionApi-CrzbbxDV.js => conclusionApi-CELRkQli.js} (90%) delete mode 100644 build/assets/index-BiBh7ROe.js create mode 100644 build/assets/index-Cealtfd6.js delete mode 100644 build/assets/index-ZIAcQZA4.css create mode 100644 build/assets/index-qC8Q4GZ7.css diff --git a/build/assets/conclusionApi-CrzbbxDV.js b/build/assets/conclusionApi-CELRkQli.js similarity index 90% rename from build/assets/conclusionApi-CrzbbxDV.js rename to build/assets/conclusionApi-CELRkQli.js index b571a9f..6b53eb1 100644 --- a/build/assets/conclusionApi-CrzbbxDV.js +++ b/build/assets/conclusionApi-CELRkQli.js @@ -1 +1 @@ -import{a as s}from"./index-BiBh7ROe.js";import"./radix-vendor-CLtqm-Ae.js";import"./charts-vendor-CmYZJIYl.js";import"./utils-vendor-BTBPSQfW.js";import"./ui-vendor-DgwXkk2Y.js";import"./socket-vendor-TjCxX7sJ.js";import"./redux-vendor-tbZCm13o.js";import"./router-vendor-HW_ujxKo.js";async function m(n){return(await s.post(`/conclusions/${n}/generate`)).data.data}async function f(n,t){return(await s.post(`/conclusions/${n}/finalize`,{finalRemark:t})).data.data}async function d(n){var t;try{return(await s.get(`/conclusions/${n}`)).data.data}catch(o){if(((t=o.response)==null?void 0:t.status)===404)return null;throw o}}export{f as finalizeConclusion,m as generateConclusion,d as getConclusion}; +import{a as s}from"./index-Cealtfd6.js";import"./radix-vendor-CLtqm-Ae.js";import"./charts-vendor-CmYZJIYl.js";import"./utils-vendor-BTBPSQfW.js";import"./ui-vendor-DgwXkk2Y.js";import"./socket-vendor-TjCxX7sJ.js";import"./redux-vendor-tbZCm13o.js";import"./router-vendor-HW_ujxKo.js";async function m(n){return(await s.post(`/conclusions/${n}/generate`)).data.data}async function f(n,t){return(await s.post(`/conclusions/${n}/finalize`,{finalRemark:t})).data.data}async function d(n){var t;try{return(await s.get(`/conclusions/${n}`)).data.data}catch(o){if(((t=o.response)==null?void 0:t.status)===404)return null;throw o}}export{f as finalizeConclusion,m as generateConclusion,d as getConclusion}; diff --git a/build/assets/index-BiBh7ROe.js b/build/assets/index-BiBh7ROe.js deleted file mode 100644 index 164b414..0000000 --- a/build/assets/index-BiBh7ROe.js +++ /dev/null @@ -1,64 +0,0 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/conclusionApi-CrzbbxDV.js","assets/radix-vendor-CLtqm-Ae.js","assets/charts-vendor-CmYZJIYl.js","assets/utils-vendor-BTBPSQfW.js","assets/ui-vendor-DgwXkk2Y.js","assets/socket-vendor-TjCxX7sJ.js","assets/redux-vendor-tbZCm13o.js","assets/router-vendor-HW_ujxKo.js"])))=>i.map(i=>d[i]); -var Of=Object.defineProperty;var $f=(t,s,a)=>s in t?Of(t,s,{enumerable:!0,configurable:!0,writable:!0,value:a}):t[s]=a;var Fl=(t,s,a)=>$f(t,typeof s!="symbol"?s+"":s,a);import{j as e,S as yh,R as vh,I as Nh,F as wh,a as Uf,T as Bf,P as Vf,C as zf,b as Hf,O as Ch,c as Sh,d as Gf,e as Wf,f as Yf,D as Kf,g as Qf,A as Xf,h as Jf,i as Zf,k as qf,l as eb,m as tb,n as sb,V as ab,o as rb,p as nb,q as ib,r as lb,s as ob,t as cb,u as db,v as mb,w as ub,x as xb,y as hb,z as pb,B as gb,E as fb,G as bb,H as jb,J as yb,K as vb,L as Nb,M as wb,N as Cb,Q as ic,U as lc,W as Sb,X as Ah,Y as Th,Z as kh,_ as Rh,$ as Ab,a0 as Tb,a1 as kb,a2 as Rb,a3 as Db,a4 as Fb,a5 as Ib,a6 as Pb,a7 as Eb,a8 as ln,a9 as xr,aa as Yn,ab as _b,ac as Mb,ad as Lb,ae as Ob}from"./radix-vendor-CLtqm-Ae.js";import{a as $b,r as u,d as Dh,e as on,B as To,C as Ui,X as Bi,Y as Vi,T as cn,L as zi,f as Pa,P as Fh,g as Ih,h as Ph,i as Ub,j as au,k as sl,l as al,b as Bb}from"./charts-vendor-CmYZJIYl.js";import{a as rl,f as Eh,s as da,e as oc,b as ru,c as Vb,d as La,g as Kn,i as cc,h as _h,j as qs,k as la,l as Il,m as Ya,n as zb,o as ko,p as Hb,q as et,r as Ro,t as Gb,u as Wb,v as Yb,w as Kb,x as Pl,y as nu,z as Qb,A as Mh,B as Lh,C as dc,D as mc,E as Xb,F as Jb,G as Oh,H as Xr,I as El,J as Zb,K as qb,L as ej}from"./utils-vendor-BTBPSQfW.js";import{u as G,A as js,S as fs,C as Ze,a as ps,L as ze,X as jt,H as tj,b as $h,U as zt,F as Re,c as $e,d as pn,R as Pt,e as gn,f as Br,P as ms,g as sj,h as aj,B as Es,i as lr,j as _l,k as uc,l as Oa,m as nl,n as il,o as gt,p as $a,q as Et,I as Vr,T as ts,r as at,s as Fa,t as rj,v as ss,M as zs,w as ll,x as Bt,y as Do,D as At,Z as ol,z as qt,E as nj,G as ij,J as lj,K as oj,N as cj,O as dj,Q as mj,V as uj,W as xj,Y as Uh,_ as hj,$ as pj,a0 as Bh,a1 as ya,a2 as ri,a3 as or,a4 as Hi,a5 as iu,a6 as ji,a7 as Gi,a8 as Vh,a9 as Ct,aa as gj,ab as Hs,ac as xc,ad as ws,ae as cl,af as zh,ag as vt,ah as fj,ai as bj,aj as Hh,ak as dl,al as Gh,am as jj,an as hc,ao as ys,ap as ka,aq as Wh,ar as ml,as as ul,at as Jt,au as yj,av as vj,aw as Yh,ax as Kh,ay as Ri,az as nr,aA as pc,aB as xl,aC as Qn,aD as Qh,aE as Xh,aF as gc,aG as hl,aH as pl,aI as Fo,aJ as Nj,aK as Os,aL as Jh,aM as Wi,aN as wj,aO as Cj,aP as Sj,aQ as gl,aR as fl,aS as fc,aT as Aj,aU as lu,aV as Ua,aW as zr,aX as Tj,aY as en,aZ as ou,a_ as kj,a$ as Rj,b0 as Zh,b1 as cu,b2 as qh,b3 as Dj,b4 as Fj,b5 as Ij,b6 as Pj,b7 as ep,b8 as Ej,b9 as _j,ba as Mj,bb as bl,bc as Lj,bd as Oj,be as $j,bf as Uj}from"./ui-vendor-DgwXkk2Y.js";import{l as Bj}from"./socket-vendor-TjCxX7sJ.js";import{c as fn,a as Vj}from"./redux-vendor-tbZCm13o.js";import{u as Cs,a as Ba,b as tp,c as zj,B as Hj,R as Gj,d as kt,O as Wj}from"./router-vendor-HW_ujxKo.js";(function(){const s=document.createElement("link").relList;if(s&&s.supports&&s.supports("modulepreload"))return;for(const n of document.querySelectorAll('link[rel="modulepreload"]'))r(n);new MutationObserver(n=>{for(const i of n)if(i.type==="childList")for(const l of i.addedNodes)l.tagName==="LINK"&&l.rel==="modulepreload"&&r(l)}).observe(document,{childList:!0,subtree:!0});function a(n){const i={};return n.integrity&&(i.integrity=n.integrity),n.referrerPolicy&&(i.referrerPolicy=n.referrerPolicy),n.crossOrigin==="use-credentials"?i.credentials="include":n.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function r(n){if(n.ep)return;n.ep=!0;const i=a(n);fetch(n.href,i)}})();var Io={},du=$b;Io.createRoot=du.createRoot,Io.hydrateRoot=du.hydrateRoot;var sp={exports:{}},ap={};var ni=u;function Yj(t,s){return t===s&&(t!==0||1/t===1/s)||t!==t&&s!==s}var Kj=typeof Object.is=="function"?Object.is:Yj,Qj=ni.useSyncExternalStore,Xj=ni.useRef,Jj=ni.useEffect,Zj=ni.useMemo,qj=ni.useDebugValue;ap.useSyncExternalStoreWithSelector=function(t,s,a,r,n){var i=Xj(null);if(i.current===null){var l={hasValue:!1,value:null};i.current=l}else l=i.current;i=Zj(function(){function d(h){if(!c){if(c=!0,m=h,h=r(h),n!==void 0&&l.hasValue){var g=l.value;if(n(g,h))return p=g}return p=h}if(g=p,Kj(m,h))return g;var b=r(h);return n!==void 0&&n(g,b)?(m=h,g):(m=h,p=b)}var c=!1,m,p,x=a===void 0?null:a;return[function(){return d(s())},x===null?void 0:function(){return d(x())}]},[s,a,r,n]);var o=Qj(t,i[0],i[1]);return Jj(function(){l.hasValue=!0,l.value=o},[o]),qj(o),o};sp.exports=ap;var ey=sp.exports;function ty(t){t()}function sy(){let t=null,s=null;return{clear(){t=null,s=null},notify(){ty(()=>{let a=t;for(;a;)a.callback(),a=a.next})},get(){const a=[];let r=t;for(;r;)a.push(r),r=r.next;return a},subscribe(a){let r=!0;const n=s={callback:a,next:null,prev:s};return n.prev?n.prev.next=n:t=n,function(){!r||t===null||(r=!1,n.next?n.next.prev=n.prev:s=n.prev,n.prev?n.prev.next=n.next:t=n.next)}}}}var mu={notify(){},get:()=>[]};function ay(t,s){let a,r=mu,n=0,i=!1;function l(b){m();const f=r.subscribe(b);let j=!1;return()=>{j||(j=!0,f(),p())}}function o(){r.notify()}function d(){g.onStateChange&&g.onStateChange()}function c(){return i}function m(){n++,a||(a=t.subscribe(d),r=sy())}function p(){n--,a&&n===0&&(a(),a=void 0,r.clear(),r=mu)}function x(){i||(i=!0,m())}function h(){i&&(i=!1,p())}const g={addNestedSub:l,notifyNestedSubs:o,handleChangeWrapper:d,isSubscribed:c,trySubscribe:x,tryUnsubscribe:h,getListeners:()=>r};return g}var ry=()=>typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u",ny=ry(),iy=()=>typeof navigator<"u"&&navigator.product==="ReactNative",ly=iy(),oy=()=>ny||ly?u.useLayoutEffect:u.useEffect,cy=oy(),Ml=Symbol.for("react-redux-context"),Ll=typeof globalThis<"u"?globalThis:{};function dy(){if(!u.createContext)return{};const t=Ll[Ml]??(Ll[Ml]=new Map);let s=t.get(u.createContext);return s||(s=u.createContext(null),t.set(u.createContext,s)),s}var cr=dy();function my(t){const{children:s,context:a,serverState:r,store:n}=t,i=u.useMemo(()=>{const d=ay(n);return{store:n,subscription:d,getServerState:r?()=>r:void 0}},[n,r]),l=u.useMemo(()=>n.getState(),[n]);cy(()=>{const{subscription:d}=i;return d.onStateChange=d.notifyNestedSubs,d.trySubscribe(),l!==n.getState()&&d.notifyNestedSubs(),()=>{d.tryUnsubscribe(),d.onStateChange=void 0}},[i,l]);const o=a||cr;return u.createElement(o.Provider,{value:i},s)}var uy=my;function bc(t=cr){return function(){return u.useContext(t)}}var rp=bc();function np(t=cr){const s=t===cr?rp:bc(t),a=()=>{const{store:r}=s();return r};return Object.assign(a,{withTypes:()=>a}),a}var xy=np();function hy(t=cr){const s=t===cr?xy:np(t),a=()=>s().dispatch;return Object.assign(a,{withTypes:()=>a}),a}var py=hy(),gy=(t,s)=>t===s;function fy(t=cr){const s=t===cr?rp:bc(t),a=(r,n={})=>{const{equalityFn:i=gy}=typeof n=="function"?{equalityFn:n}:n,l=s(),{store:o,subscription:d,getServerState:c}=l;u.useRef(!0);const m=u.useCallback({[r.name](x){return r(x)}}[r.name],[r]),p=ey.useSyncExternalStoreWithSelector(d.addNestedSub,o.getState,c||o.getState,m,i);return u.useDebugValue(p),p};return Object.assign(a,{withTypes:()=>a}),a}var by=fy(),Po=function(t,s){return Po=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,r){a.__proto__=r}||function(a,r){for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(a[n]=r[n])},Po(t,s)};function jy(t,s){if(typeof s!="function"&&s!==null)throw new TypeError("Class extends value "+String(s)+" is not a constructor or null");Po(t,s);function a(){this.constructor=t}t.prototype=s===null?Object.create(s):(a.prototype=s.prototype,new a)}var Yi=function(){return Yi=Object.assign||function(s){for(var a,r=1,n=arguments.length;r0&&g[g.length-1])||w[0]!==6&&w[0]!==2)){f=0;continue}if(w[0]===3&&(!g||w[1]>g[0]&&w[1]0?setTimeout(h,v):h(null)}}window.addEventListener("storage",j),m.addToWaiting(j);var y=setTimeout(j,Math.max(0,p-Date.now()))})];case 1:return x.sent(),[2]}})})},m.addToWaiting=function(p){this.removeFromWaiting(p),m.waiters!==void 0&&m.waiters.push(p)},m.removeFromWaiting=function(p){m.waiters!==void 0&&(m.waiters=m.waiters.filter(function(x){return x!==p}))},m.notifyWaiters=function(){m.waiters!==void 0&&m.waiters.slice().forEach(function(p){return p()})},m.prototype.releaseLock=function(p){return a(this,void 0,void 0,function(){return r(this,function(x){switch(x.label){case 0:return[4,this.releaseLock__private__(p)];case 1:return[2,x.sent()]}})})},m.prototype.releaseLock__private__=function(p){return a(this,void 0,void 0,function(){var x,h,g,b;return r(this,function(f){switch(f.label){case 0:return x=this.storageHandler===void 0?l:this.storageHandler,h=i+"-"+p,(g=x.getItemSync(h))===null?[2]:(b=JSON.parse(g)).id!==this.id?[3,2]:[4,Sr.default().lock(b.iat)];case 1:f.sent(),this.acquiredIatSet.delete(b.iat),x.removeItemSync(h),Sr.default().unlock(b.iat),m.notifyWaiters(),f.label=2;case 2:return[2]}})})},m.lockCorrector=function(p){for(var x=Date.now()-5e3,h=p,g=[],b=0;;){var f=h.keySync(b);if(f===null)break;g.push(f),b++}for(var j=!1,y=0;y.")},Ny=Yi(Yi({},vy),{buildAuthorizeUrl:Xs,buildLogoutUrl:Xs,getAccessTokenSilently:Xs,getAccessTokenWithPopup:Xs,getIdTokenClaims:Xs,loginWithRedirect:Xs,loginWithPopup:Xs,connectAccountWithRedirect:Xs,logout:Xs,handleRedirectCallback:Xs,getDpopNonce:Xs,setDpopNonce:Xs,generateDpopProof:Xs,createFetcher:Xs}),wy=u.createContext(Ny);(function(t){jy(s,t);function s(a,r){var n=t.call(this,r??a)||this;return n.error=a,n.error_description=r,Object.setPrototypeOf(n,s.prototype),n}return s})(Error);var Cy=function(t){return t===void 0&&(t=wy),u.useContext(t)};const Ol="idToken",$l="userData",Sy=()=>!0;class st{static setAccessToken(s){}static getAccessToken(){return null}static setRefreshToken(s){}static getRefreshToken(){return null}static setIdToken(s){sessionStorage.setItem(Ol,s)}static getIdToken(){return sessionStorage.getItem(Ol)}static setUserData(s){localStorage.setItem($l,JSON.stringify(s))}static getUserData(){const s=localStorage.getItem($l);if(!s)return null;try{return JSON.parse(s)}catch{return null}}static clearAll(){try{sessionStorage.setItem("__logout_in_progress__","true"),sessionStorage.setItem("__force_logout__","true")}catch(s){console.warn("Could not set logout flags:",s)}try{localStorage.removeItem($l),sessionStorage.removeItem(Ol)}catch(s){console.warn("Error clearing user data:",s)}{try{sessionStorage.setItem("__logout_in_progress__","true"),sessionStorage.setItem("__force_logout__","true")}catch{}return}}static hasAccessToken(){return!!this.getUserData()}static hasRefreshToken(){return!!this.getUserData()}static isLocalhost(){return window.location.hostname==="localhost"||window.location.hostname==="127.0.0.1"||window.location.hostname===""}static isProduction(){return Sy()}static setAuthError(s){s?sessionStorage.setItem("__auth_error__",s):sessionStorage.removeItem("__auth_error__")}static getAuthError(){return sessionStorage.getItem("__auth_error__")}}function Ul(t,s=5){if(!t)return!0;try{const a=t.split(".");if(a.length!==3||!a[1])return!0;const n=JSON.parse(atob(a[1])).exp*1e3,i=Date.now(),l=s*60*1e3;return n-i{if(t.data instanceof FormData){const s=t.headers;delete s["Content-Type"],s.common&&typeof s.common=="object"&&delete s.common["Content-Type"],s.post&&typeof s.post=="object"&&delete s.post["Content-Type"]}return t},t=>Promise.reject(t));fe.interceptors.response.use(t=>t,async t=>{var a,r,n,i;const s=t.config;if(t.code==="ERR_NETWORK"||t.code==="ECONNREFUSED"||(a=t.message)!=null&&a.includes("ERR_CONNECTION_REFUSED"),((r=t.response)==null?void 0:r.status)===401){if(((i=(n=t.response)==null?void 0:n.data)==null?void 0:i.errorCode)==="SESSION_SUPERSEDED"){const l=st.getIdToken(),o=sessionStorage.getItem("auth_provider")||(l!=null&&l.includes("tanflow")?"tanflow":"okta");return st.setAuthError("SESSION_SUPERSEDED"),G.error("You have been logged out because an active session was detected from another device.",{duration:2e3,id:"session-superseded-toast"}),setTimeout(async()=>{sessionStorage.setItem("__logout_in_progress__","true"),sessionStorage.setItem("__force_logout__","true"),o==="tanflow"&&l?cp(l):dp(l||void 0)},1e3),Promise.reject(t)}if(!s._retry){s._retry=!0;const l=!0;try{const o=st.getRefreshToken(),d=await rl.post(`${vc}/auth/refresh`,l?{}:{refreshToken:o},{withCredentials:!0}),m=(d.data.data||d.data).accessToken;return fe(s)}catch(o){return st.clearAll(),window.location.href="/",Promise.reject(o)}}}return Promise.reject(t)});async function ky(t,s){const r=(await fe.post("/auth/login",{username:t,password:s},{withCredentials:!0})).data,n=r.data||r;return n.user&&st.setUserData(n.user),n.accessToken&&n.refreshToken&&(st.setAccessToken(n.accessToken),st.setRefreshToken(n.refreshToken)),n}async function Ry(t,s){var a,r;try{const n=await fe.post("/auth/token-exchange",{code:t,redirectUri:s},{responseType:"json",headers:{"Content-Type":"application/json",Accept:"application/json"}});if(Array.isArray(n.data))throw console.error("❌ Response is an array (buffer issue):",{arrayLength:n.data.length,firstFew:n.data.slice(0,10),rawResponse:n}),new Error("Invalid response format: received array instead of JSON. Check Content-Type header.");const i=n.data,l=i.data||i;return l.user&&st.setUserData(l.user),l.idToken&&st.setIdToken(l.idToken),l.accessToken&&l.refreshToken&&(st.setAccessToken(l.accessToken),st.setRefreshToken(l.refreshToken)),l}catch(n){throw console.error("❌ Token exchange failed:",{message:n.message,response:(a=n.response)==null?void 0:a.data,status:(r=n.response)==null?void 0:r.status,code:t?`${t.substring(0,10)}...`:"MISSING",redirectUri:s}),n}}async function Dy(){var r;const t={},a=(await fe.post("/auth/refresh",t)).data;if((r=a.data)!=null&&r.accessToken||a.accessToken,a.success!==!1)return"cookie-based-auth";throw new Error("Failed to refresh token")}async function Ln(){const s=(await fe.get("/auth/me")).data;return s.data||s}async function Fy(){var t,s;try{await rl.post(`${vc}/auth/logout`,{},{withCredentials:!0})}catch(a){console.error("📡 Logout API error:",a),console.error("📡 Error details:",{message:a.message,status:(t=a.response)==null?void 0:t.status,data:(s=a.response)==null?void 0:s.data})}}let Nr=null;function Iy(){return"https://reflow-uat.royalenfield.com"}function $r(t){const s=Iy();return Nr||(Nr=Bj(s,{withCredentials:!0,transports:["websocket","polling"],path:"/socket.io",reconnection:!0,reconnectionDelay:1e3,reconnectionAttempts:5}),Nr.on("connect",()=>{}),Nr.on("connect_error",a=>{console.error("[Socket] Connection error:",a.message)}),Nr.on("disconnect",a=>{}),Nr)}function Eo(t,s,a){a?t.emit("join:request",{requestId:s,userId:a}):t.emit("join:request",s)}function mp(t,s){t.emit("leave:request",s)}function Nc(t,s){t.emit("join:user",{userId:s})}const up=u.createContext(void 0);function Py({children:t}){const[s,a]=u.useState(!1),[r,n]=u.useState(!0),[i,l]=u.useState(null),[o,d]=u.useState(null),[c,m]=u.useState(!1);u.useEffect(()=>{const v=sessionStorage.getItem("__logout_in_progress__"),w=sessionStorage.getItem("__force_logout__");if(v==="true"||w==="true"){sessionStorage.removeItem("__logout_in_progress__"),sessionStorage.removeItem("__force_logout__"),st.clearAll();try{localStorage.clear(),sessionStorage.clear()}catch(A){console.error("Error clearing storage:",A)}a(!1),l(null),n(!1),d(null);return}const S=new URLSearchParams(window.location.search),C=S.has("code"),P=sessionStorage.getItem("__logout_type__");if((S.has("logout")||S.has("okta_logged_out")||S.has("tanflow_logged_out")||P)&&!C){console.log("🚪 Logout parameter detected in URL, clearing all tokens and backend session"),Fy().catch(A=>console.warn("🚪 Final backend logout cleanup failed:",A)),st.clearAll(),sessionStorage.removeItem("__logout_type__"),sessionStorage.removeItem("auth_provider"),sessionStorage.removeItem("tanflow_auth_state"),sessionStorage.removeItem("__logout_in_progress__"),sessionStorage.removeItem("__force_logout__"),sessionStorage.removeItem("tanflow_logged_out"),localStorage.clear(),a(!1),l(null),n(!1);try{sessionStorage.setItem("__force_reauth_after_logout__","true")}catch(A){console.warn("Could not set force reauth flag:",A)}window.history.replaceState({},document.title,"/");return}window.location.pathname==="/login/callback"||window.location.pathname==="/login/tanflow/callback"||(st.getUserData(),c?n(!1):x())},[c]),u.useEffect(()=>{if(!s)return;const v=async()=>{try{await j()}catch(C){console.error("Silent refresh failed:",C)}},w=10*60*1e3,S=setInterval(v,w);return()=>clearInterval(S)},[s]),u.useEffect(()=>{if(!s||!(i!=null&&i.userId))return;const v=$r();Nc(v,i.userId);const w=S=>{console.log("📡 [Socket] Session superseded event received:",S),st.setAuthError("SESSION_SUPERSEDED"),G.error("Session Expired",{description:"You have been logged out because another session was started on a different device.",duration:4e3,id:"session-superseded-socket"}),setTimeout(()=>{b()},1e3)};return v.on("SESSION_SUPERSEDED",w),()=>{v.off("SESSION_SUPERSEDED",w)}},[s,i==null?void 0:i.userId]);const p=u.useRef(!1);u.useEffect(()=>{if(p.current||window.location.pathname!=="/login/callback")return;(async()=>{const w=new URLSearchParams(window.location.search);if(sessionStorage.getItem("auth_provider")==="tanflow")return;const C=sessionStorage.getItem("__logout_type__");if((w.has("logout")||w.has("tanflow_logged_out")||w.has("okta_logged_out")||C)&&!w.get("code")){console.log("🚪 Logout redirect detected in callback, redirecting to home");const N=new URLSearchParams;(w.has("tanflow_logged_out")||C==="tanflow")&&N.set("tanflow_logged_out","true"),(w.has("okta_logged_out")||C==="okta")&&N.set("okta_logged_out","true"),w.has("logout")&&N.set("logout",w.get("logout")||Date.now().toString());const _=N.toString()?`/?${N.toString()}`:"/?logout="+Date.now();window.location.replace(_);return}p.current=!0;const F=w.get("code"),A=w.get("error");if(window.history.replaceState({},document.title,"/login/callback"),A){d(new Error(`Authentication error: ${A}`)),n(!1),sessionStorage.removeItem("auth_provider");return}if(!F){n(!1),sessionStorage.removeItem("auth_provider");return}try{n(!0),a(!1),d(null);const N=`${window.location.origin}/login/callback`,_=await Ry(F,N);l(_.user),a(!0),d(null),sessionStorage.removeItem("auth_provider"),window.history.replaceState({},document.title,"/")}catch(N){console.error("❌ Token exchange error in AuthContext:",N),d(N),a(!1),l(null),sessionStorage.removeItem("auth_provider"),p.current=!1}finally{n(!1)}})()},[]);const x=async()=>{var w;if(c){n(!1);return}const v=!0;try{if(n(!0),v){const P=st.getUserData();try{const F=await Ln();l(F),st.setUserData(F),a(!0)}catch(F){if(((w=F==null?void 0:F.response)==null?void 0:w.status)===401)try{await j();const A=await Ln();l(A),st.setUserData(A),a(!0)}catch{st.clearAll(),a(!1),l(null)}else F!=null&&F.isConnectionError?P?(l(P),a(!0)):(a(!1),l(null)):(st.clearAll(),a(!1),l(null))}return}const S=st.getAccessToken(),C=st.getUserData();if(!S){a(!1),l(null),n(!1);return}if(Ul(S))try{await j();const P=st.getAccessToken();if(P&&!Ul(P)){const F=st.getUserData();if(F)l(F),a(!0);else try{const A=await Ln();l(A),st.setUserData(A),a(!0)}catch{st.clearAll(),a(!1),l(null)}}else st.clearAll(),a(!1),l(null)}catch{st.clearAll(),a(!1),l(null)}else if(C)l(C),a(!0);else try{const P=await Ln();l(P),st.setUserData(P),a(!0)}catch{st.clearAll(),a(!1),l(null)}}catch(S){console.error("Error checking auth status:",S),d(S),st.clearAll(),a(!1),l(null)}finally{n(!1)}},h=async()=>{try{d(null);const v="https://dev-830839.oktapreview.com",w="0oa2jgzvrpdwx2iqd0h8",S=`${window.location.origin}/login/callback`,C="code",P="openid profile email",F=Math.random().toString(36).substring(7);sessionStorage.setItem("auth_provider","okta");const A=new URLSearchParams(window.location.search),N=A.has("logout")||A.has("okta_logged_out")||A.has("tanflow_logged_out"),_=sessionStorage.getItem("__force_reauth_after_logout__")==="true",R=N||_;let B=`${v}/oauth2/default/v1/authorize?client_id=${w}&redirect_uri=${encodeURIComponent(S)}&response_type=${C}&scope=${encodeURIComponent(P)}&state=${F}`;R&&(B+="&prompt=login",sessionStorage.removeItem("__force_reauth_after_logout__")),window.location.href=B}catch(v){throw d(v),v}},g=async(v,w)=>{d(null),n(!0);try{const S=await ky(v,w);l(S.user),a(!0),window.history.replaceState({},document.title,"/")}catch(S){throw d(S),a(!1),l(null),S}finally{n(!1)}},b=async()=>{try{const v=st.getIdToken(),w=sessionStorage.getItem("auth_provider")||(v&&v.includes("tanflow")?"tanflow":null)||"okta";sessionStorage.setItem("__logout_in_progress__","true"),sessionStorage.setItem("__force_logout__","true"),m(!0),a(!1),l(null),d(null),n(!0);const S=sessionStorage.getItem("__logout_in_progress__"),C=sessionStorage.getItem("__force_logout__"),P=sessionStorage.getItem("auth_provider");if(st.clearAll(),S&&sessionStorage.setItem("__logout_in_progress__",S),C&&sessionStorage.setItem("__force_logout__",C),v&&st.setIdToken(v),P&&sessionStorage.setItem("auth_provider",P),await new Promise(F=>setTimeout(F,100)),w==="tanflow"&&v){console.log("🚪 Initiating Tanflow logout...");try{cp(v);return}catch(F){console.error("🚪 Tanflow logout error:",F)}}console.log("🚪 Using OKTA logout flow or fallback"),sessionStorage.removeItem("auth_provider"),dp(v||void 0)}catch(v){console.error("🚪 Logout error:",v);try{localStorage.clear(),sessionStorage.clear(),sessionStorage.setItem("__logout_in_progress__","true");const w=`${window.location.origin}/?okta_logged_out=true&logout=${Date.now()}`;window.location.replace(w)}catch{window.location.replace("/?logout="+Date.now())}}},f=async()=>{{if(s)return"cookie-based-auth";try{return await j(),s?"cookie-based-auth":null}catch{return null}}const v=st.getAccessToken();if(v&&!Ul(v))return v;try{return await j(),st.getAccessToken()}catch{return null}},j=async()=>{try{const w=await Dy();return}catch(w){throw st.clearAll(),a(!1),l(null),w}},y={isAuthenticated:s,isLoading:r,user:i,error:o,login:h,loginWithPassword:g,logout:b,getAccessTokenSilently:f,refreshTokenSilently:j};return e.jsx(up.Provider,{value:y,children:t})}function Ey({children:t}){return e.jsx(Py,{children:t})}function us(){const t=u.useContext(up);if(t===void 0)throw new Error("useAuth must be used within an AuthProvider");return t}function Ar(t){return(t==null?void 0:t.role)==="ADMIN"}function kn(t){return(t==null?void 0:t.role)==="MANAGEMENT"}function jl(t){return(t==null?void 0:t.role)==="MANAGEMENT"||(t==null?void 0:t.role)==="ADMIN"}const hu=t=>typeof t=="boolean"?`${t}`:t===0?"0":t,pu=Dh,wc=(t,s)=>a=>{var r;if((s==null?void 0:s.variants)==null)return pu(t,a==null?void 0:a.class,a==null?void 0:a.className);const{variants:n,defaultVariants:i}=s,l=Object.keys(n).map(c=>{const m=a==null?void 0:a[c],p=i==null?void 0:i[c];if(m===null)return null;const x=hu(m)||hu(p);return n[c][x]}),o=a&&Object.entries(a).reduce((c,m)=>{let[p,x]=m;return x===void 0||(c[p]=x),c},{}),d=s==null||(r=s.compoundVariants)===null||r===void 0?void 0:r.reduce((c,m)=>{let{class:p,className:x,...h}=m;return Object.entries(h).every(g=>{let[b,f]=g;return Array.isArray(f)?f.includes({...i,...o}[b]):{...i,...o}[b]===f})?[...c,p,x]:c},[]);return pu(t,l,d,a==null?void 0:a.class,a==null?void 0:a.className)},Cc="-",_y=t=>{const s=Ly(t),{conflictingClassGroups:a,conflictingClassGroupModifiers:r}=t;return{getClassGroupId:l=>{const o=l.split(Cc);return o[0]===""&&o.length!==1&&o.shift(),xp(o,s)||My(l)},getConflictingClassGroupIds:(l,o)=>{const d=a[l]||[];return o&&r[l]?[...d,...r[l]]:d}}},xp=(t,s)=>{var l;if(t.length===0)return s.classGroupId;const a=t[0],r=s.nextPart.get(a),n=r?xp(t.slice(1),r):void 0;if(n)return n;if(s.validators.length===0)return;const i=t.join(Cc);return(l=s.validators.find(({validator:o})=>o(i)))==null?void 0:l.classGroupId},gu=/^\[(.+)\]$/,My=t=>{if(gu.test(t)){const s=gu.exec(t)[1],a=s==null?void 0:s.substring(0,s.indexOf(":"));if(a)return"arbitrary.."+a}},Ly=t=>{const{theme:s,prefix:a}=t,r={nextPart:new Map,validators:[]};return $y(Object.entries(t.classGroups),a).forEach(([i,l])=>{_o(l,r,i,s)}),r},_o=(t,s,a,r)=>{t.forEach(n=>{if(typeof n=="string"){const i=n===""?s:fu(s,n);i.classGroupId=a;return}if(typeof n=="function"){if(Oy(n)){_o(n(r),s,a,r);return}s.validators.push({validator:n,classGroupId:a});return}Object.entries(n).forEach(([i,l])=>{_o(l,fu(s,i),a,r)})})},fu=(t,s)=>{let a=t;return s.split(Cc).forEach(r=>{a.nextPart.has(r)||a.nextPart.set(r,{nextPart:new Map,validators:[]}),a=a.nextPart.get(r)}),a},Oy=t=>t.isThemeGetter,$y=(t,s)=>s?t.map(([a,r])=>{const n=r.map(i=>typeof i=="string"?s+i:typeof i=="object"?Object.fromEntries(Object.entries(i).map(([l,o])=>[s+l,o])):i);return[a,n]}):t,Uy=t=>{if(t<1)return{get:()=>{},set:()=>{}};let s=0,a=new Map,r=new Map;const n=(i,l)=>{a.set(i,l),s++,s>t&&(s=0,r=a,a=new Map)};return{get(i){let l=a.get(i);if(l!==void 0)return l;if((l=r.get(i))!==void 0)return n(i,l),l},set(i,l){a.has(i)?a.set(i,l):n(i,l)}}},hp="!",By=t=>{const{separator:s,experimentalParseClassName:a}=t,r=s.length===1,n=s[0],i=s.length,l=o=>{const d=[];let c=0,m=0,p;for(let f=0;fm?p-m:void 0;return{modifiers:d,hasImportantModifier:h,baseClassName:g,maybePostfixModifierPosition:b}};return a?o=>a({className:o,parseClassName:l}):l},Vy=t=>{if(t.length<=1)return t;const s=[];let a=[];return t.forEach(r=>{r[0]==="["?(s.push(...a.sort(),r),a=[]):a.push(r)}),s.push(...a.sort()),s},zy=t=>({cache:Uy(t.cacheSize),parseClassName:By(t),..._y(t)}),Hy=/\s+/,Gy=(t,s)=>{const{parseClassName:a,getClassGroupId:r,getConflictingClassGroupIds:n}=s,i=[],l=t.trim().split(Hy);let o="";for(let d=l.length-1;d>=0;d-=1){const c=l[d],{modifiers:m,hasImportantModifier:p,baseClassName:x,maybePostfixModifierPosition:h}=a(c);let g=!!h,b=r(g?x.substring(0,h):x);if(!b){if(!g){o=c+(o.length>0?" "+o:o);continue}if(b=r(x),!b){o=c+(o.length>0?" "+o:o);continue}g=!1}const f=Vy(m).join(":"),j=p?f+hp:f,y=j+b;if(i.includes(y))continue;i.push(y);const v=n(b,g);for(let w=0;w0?" "+o:o)}return o};function Wy(){let t=0,s,a,r="";for(;t{if(typeof t=="string")return t;let s,a="";for(let r=0;rp(m),t());return a=zy(c),r=a.cache.get,n=a.cache.set,i=o,o(d)}function o(d){const c=r(d);if(c)return c;const m=Gy(d,a);return n(d,m),m}return function(){return i(Wy.apply(null,arguments))}}const Qt=t=>{const s=a=>a[t]||[];return s.isThemeGetter=!0,s},gp=/^\[(?:([a-z-]+):)?(.+)\]$/i,Ky=/^\d+\/\d+$/,Qy=new Set(["px","full","screen"]),Xy=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,Jy=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,Zy=/^(rgba?|hsla?|hwb|(ok)?(lab|lch))\(.+\)$/,qy=/^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,ev=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/,Ha=t=>dn(t)||Qy.has(t)||Ky.test(t),sr=t=>bn(t,"length",ov),dn=t=>!!t&&!Number.isNaN(Number(t)),Bl=t=>bn(t,"number",dn),Rn=t=>!!t&&Number.isInteger(Number(t)),tv=t=>t.endsWith("%")&&dn(t.slice(0,-1)),bt=t=>gp.test(t),ar=t=>Xy.test(t),sv=new Set(["length","size","percentage"]),av=t=>bn(t,sv,fp),rv=t=>bn(t,"position",fp),nv=new Set(["image","url"]),iv=t=>bn(t,nv,dv),lv=t=>bn(t,"",cv),Dn=()=>!0,bn=(t,s,a)=>{const r=gp.exec(t);return r?r[1]?typeof s=="string"?r[1]===s:s.has(r[1]):a(r[2]):!1},ov=t=>Jy.test(t)&&!Zy.test(t),fp=()=>!1,cv=t=>qy.test(t),dv=t=>ev.test(t),mv=()=>{const t=Qt("colors"),s=Qt("spacing"),a=Qt("blur"),r=Qt("brightness"),n=Qt("borderColor"),i=Qt("borderRadius"),l=Qt("borderSpacing"),o=Qt("borderWidth"),d=Qt("contrast"),c=Qt("grayscale"),m=Qt("hueRotate"),p=Qt("invert"),x=Qt("gap"),h=Qt("gradientColorStops"),g=Qt("gradientColorStopPositions"),b=Qt("inset"),f=Qt("margin"),j=Qt("opacity"),y=Qt("padding"),v=Qt("saturate"),w=Qt("scale"),S=Qt("sepia"),C=Qt("skew"),P=Qt("space"),F=Qt("translate"),A=()=>["auto","contain","none"],N=()=>["auto","hidden","clip","visible","scroll"],_=()=>["auto",bt,s],R=()=>[bt,s],B=()=>["",Ha,sr],D=()=>["auto",dn,bt],L=()=>["bottom","center","left","left-bottom","left-top","right","right-bottom","right-top","top"],I=()=>["solid","dashed","dotted","double","none"],T=()=>["normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity"],k=()=>["start","end","center","between","around","evenly","stretch"],z=()=>["","0",bt],O=()=>["auto","avoid","all","avoid-page","page","left","right","column"],$=()=>[dn,bt];return{cacheSize:500,separator:":",theme:{colors:[Dn],spacing:[Ha,sr],blur:["none","",ar,bt],brightness:$(),borderColor:[t],borderRadius:["none","","full",ar,bt],borderSpacing:R(),borderWidth:B(),contrast:$(),grayscale:z(),hueRotate:$(),invert:z(),gap:R(),gradientColorStops:[t],gradientColorStopPositions:[tv,sr],inset:_(),margin:_(),opacity:$(),padding:R(),saturate:$(),scale:$(),sepia:z(),skew:$(),space:R(),translate:R()},classGroups:{aspect:[{aspect:["auto","square","video",bt]}],container:["container"],columns:[{columns:[ar]}],"break-after":[{"break-after":O()}],"break-before":[{"break-before":O()}],"break-inside":[{"break-inside":["auto","avoid","avoid-page","avoid-column"]}],"box-decoration":[{"box-decoration":["slice","clone"]}],box:[{box:["border","content"]}],display:["block","inline-block","inline","flex","inline-flex","table","inline-table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row-group","table-row","flow-root","grid","inline-grid","contents","list-item","hidden"],float:[{float:["right","left","none","start","end"]}],clear:[{clear:["left","right","both","none","start","end"]}],isolation:["isolate","isolation-auto"],"object-fit":[{object:["contain","cover","fill","none","scale-down"]}],"object-position":[{object:[...L(),bt]}],overflow:[{overflow:N()}],"overflow-x":[{"overflow-x":N()}],"overflow-y":[{"overflow-y":N()}],overscroll:[{overscroll:A()}],"overscroll-x":[{"overscroll-x":A()}],"overscroll-y":[{"overscroll-y":A()}],position:["static","fixed","absolute","relative","sticky"],inset:[{inset:[b]}],"inset-x":[{"inset-x":[b]}],"inset-y":[{"inset-y":[b]}],start:[{start:[b]}],end:[{end:[b]}],top:[{top:[b]}],right:[{right:[b]}],bottom:[{bottom:[b]}],left:[{left:[b]}],visibility:["visible","invisible","collapse"],z:[{z:["auto",Rn,bt]}],basis:[{basis:_()}],"flex-direction":[{flex:["row","row-reverse","col","col-reverse"]}],"flex-wrap":[{flex:["wrap","wrap-reverse","nowrap"]}],flex:[{flex:["1","auto","initial","none",bt]}],grow:[{grow:z()}],shrink:[{shrink:z()}],order:[{order:["first","last","none",Rn,bt]}],"grid-cols":[{"grid-cols":[Dn]}],"col-start-end":[{col:["auto",{span:["full",Rn,bt]},bt]}],"col-start":[{"col-start":D()}],"col-end":[{"col-end":D()}],"grid-rows":[{"grid-rows":[Dn]}],"row-start-end":[{row:["auto",{span:[Rn,bt]},bt]}],"row-start":[{"row-start":D()}],"row-end":[{"row-end":D()}],"grid-flow":[{"grid-flow":["row","col","dense","row-dense","col-dense"]}],"auto-cols":[{"auto-cols":["auto","min","max","fr",bt]}],"auto-rows":[{"auto-rows":["auto","min","max","fr",bt]}],gap:[{gap:[x]}],"gap-x":[{"gap-x":[x]}],"gap-y":[{"gap-y":[x]}],"justify-content":[{justify:["normal",...k()]}],"justify-items":[{"justify-items":["start","end","center","stretch"]}],"justify-self":[{"justify-self":["auto","start","end","center","stretch"]}],"align-content":[{content:["normal",...k(),"baseline"]}],"align-items":[{items:["start","end","center","baseline","stretch"]}],"align-self":[{self:["auto","start","end","center","stretch","baseline"]}],"place-content":[{"place-content":[...k(),"baseline"]}],"place-items":[{"place-items":["start","end","center","baseline","stretch"]}],"place-self":[{"place-self":["auto","start","end","center","stretch"]}],p:[{p:[y]}],px:[{px:[y]}],py:[{py:[y]}],ps:[{ps:[y]}],pe:[{pe:[y]}],pt:[{pt:[y]}],pr:[{pr:[y]}],pb:[{pb:[y]}],pl:[{pl:[y]}],m:[{m:[f]}],mx:[{mx:[f]}],my:[{my:[f]}],ms:[{ms:[f]}],me:[{me:[f]}],mt:[{mt:[f]}],mr:[{mr:[f]}],mb:[{mb:[f]}],ml:[{ml:[f]}],"space-x":[{"space-x":[P]}],"space-x-reverse":["space-x-reverse"],"space-y":[{"space-y":[P]}],"space-y-reverse":["space-y-reverse"],w:[{w:["auto","min","max","fit","svw","lvw","dvw",bt,s]}],"min-w":[{"min-w":[bt,s,"min","max","fit"]}],"max-w":[{"max-w":[bt,s,"none","full","min","max","fit","prose",{screen:[ar]},ar]}],h:[{h:[bt,s,"auto","min","max","fit","svh","lvh","dvh"]}],"min-h":[{"min-h":[bt,s,"min","max","fit","svh","lvh","dvh"]}],"max-h":[{"max-h":[bt,s,"min","max","fit","svh","lvh","dvh"]}],size:[{size:[bt,s,"auto","min","max","fit"]}],"font-size":[{text:["base",ar,sr]}],"font-smoothing":["antialiased","subpixel-antialiased"],"font-style":["italic","not-italic"],"font-weight":[{font:["thin","extralight","light","normal","medium","semibold","bold","extrabold","black",Bl]}],"font-family":[{font:[Dn]}],"fvn-normal":["normal-nums"],"fvn-ordinal":["ordinal"],"fvn-slashed-zero":["slashed-zero"],"fvn-figure":["lining-nums","oldstyle-nums"],"fvn-spacing":["proportional-nums","tabular-nums"],"fvn-fraction":["diagonal-fractions","stacked-fractions"],tracking:[{tracking:["tighter","tight","normal","wide","wider","widest",bt]}],"line-clamp":[{"line-clamp":["none",dn,Bl]}],leading:[{leading:["none","tight","snug","normal","relaxed","loose",Ha,bt]}],"list-image":[{"list-image":["none",bt]}],"list-style-type":[{list:["none","disc","decimal",bt]}],"list-style-position":[{list:["inside","outside"]}],"placeholder-color":[{placeholder:[t]}],"placeholder-opacity":[{"placeholder-opacity":[j]}],"text-alignment":[{text:["left","center","right","justify","start","end"]}],"text-color":[{text:[t]}],"text-opacity":[{"text-opacity":[j]}],"text-decoration":["underline","overline","line-through","no-underline"],"text-decoration-style":[{decoration:[...I(),"wavy"]}],"text-decoration-thickness":[{decoration:["auto","from-font",Ha,sr]}],"underline-offset":[{"underline-offset":["auto",Ha,bt]}],"text-decoration-color":[{decoration:[t]}],"text-transform":["uppercase","lowercase","capitalize","normal-case"],"text-overflow":["truncate","text-ellipsis","text-clip"],"text-wrap":[{text:["wrap","nowrap","balance","pretty"]}],indent:[{indent:R()}],"vertical-align":[{align:["baseline","top","middle","bottom","text-top","text-bottom","sub","super",bt]}],whitespace:[{whitespace:["normal","nowrap","pre","pre-line","pre-wrap","break-spaces"]}],break:[{break:["normal","words","all","keep"]}],hyphens:[{hyphens:["none","manual","auto"]}],content:[{content:["none",bt]}],"bg-attachment":[{bg:["fixed","local","scroll"]}],"bg-clip":[{"bg-clip":["border","padding","content","text"]}],"bg-opacity":[{"bg-opacity":[j]}],"bg-origin":[{"bg-origin":["border","padding","content"]}],"bg-position":[{bg:[...L(),rv]}],"bg-repeat":[{bg:["no-repeat",{repeat:["","x","y","round","space"]}]}],"bg-size":[{bg:["auto","cover","contain",av]}],"bg-image":[{bg:["none",{"gradient-to":["t","tr","r","br","b","bl","l","tl"]},iv]}],"bg-color":[{bg:[t]}],"gradient-from-pos":[{from:[g]}],"gradient-via-pos":[{via:[g]}],"gradient-to-pos":[{to:[g]}],"gradient-from":[{from:[h]}],"gradient-via":[{via:[h]}],"gradient-to":[{to:[h]}],rounded:[{rounded:[i]}],"rounded-s":[{"rounded-s":[i]}],"rounded-e":[{"rounded-e":[i]}],"rounded-t":[{"rounded-t":[i]}],"rounded-r":[{"rounded-r":[i]}],"rounded-b":[{"rounded-b":[i]}],"rounded-l":[{"rounded-l":[i]}],"rounded-ss":[{"rounded-ss":[i]}],"rounded-se":[{"rounded-se":[i]}],"rounded-ee":[{"rounded-ee":[i]}],"rounded-es":[{"rounded-es":[i]}],"rounded-tl":[{"rounded-tl":[i]}],"rounded-tr":[{"rounded-tr":[i]}],"rounded-br":[{"rounded-br":[i]}],"rounded-bl":[{"rounded-bl":[i]}],"border-w":[{border:[o]}],"border-w-x":[{"border-x":[o]}],"border-w-y":[{"border-y":[o]}],"border-w-s":[{"border-s":[o]}],"border-w-e":[{"border-e":[o]}],"border-w-t":[{"border-t":[o]}],"border-w-r":[{"border-r":[o]}],"border-w-b":[{"border-b":[o]}],"border-w-l":[{"border-l":[o]}],"border-opacity":[{"border-opacity":[j]}],"border-style":[{border:[...I(),"hidden"]}],"divide-x":[{"divide-x":[o]}],"divide-x-reverse":["divide-x-reverse"],"divide-y":[{"divide-y":[o]}],"divide-y-reverse":["divide-y-reverse"],"divide-opacity":[{"divide-opacity":[j]}],"divide-style":[{divide:I()}],"border-color":[{border:[n]}],"border-color-x":[{"border-x":[n]}],"border-color-y":[{"border-y":[n]}],"border-color-s":[{"border-s":[n]}],"border-color-e":[{"border-e":[n]}],"border-color-t":[{"border-t":[n]}],"border-color-r":[{"border-r":[n]}],"border-color-b":[{"border-b":[n]}],"border-color-l":[{"border-l":[n]}],"divide-color":[{divide:[n]}],"outline-style":[{outline:["",...I()]}],"outline-offset":[{"outline-offset":[Ha,bt]}],"outline-w":[{outline:[Ha,sr]}],"outline-color":[{outline:[t]}],"ring-w":[{ring:B()}],"ring-w-inset":["ring-inset"],"ring-color":[{ring:[t]}],"ring-opacity":[{"ring-opacity":[j]}],"ring-offset-w":[{"ring-offset":[Ha,sr]}],"ring-offset-color":[{"ring-offset":[t]}],shadow:[{shadow:["","inner","none",ar,lv]}],"shadow-color":[{shadow:[Dn]}],opacity:[{opacity:[j]}],"mix-blend":[{"mix-blend":[...T(),"plus-lighter","plus-darker"]}],"bg-blend":[{"bg-blend":T()}],filter:[{filter:["","none"]}],blur:[{blur:[a]}],brightness:[{brightness:[r]}],contrast:[{contrast:[d]}],"drop-shadow":[{"drop-shadow":["","none",ar,bt]}],grayscale:[{grayscale:[c]}],"hue-rotate":[{"hue-rotate":[m]}],invert:[{invert:[p]}],saturate:[{saturate:[v]}],sepia:[{sepia:[S]}],"backdrop-filter":[{"backdrop-filter":["","none"]}],"backdrop-blur":[{"backdrop-blur":[a]}],"backdrop-brightness":[{"backdrop-brightness":[r]}],"backdrop-contrast":[{"backdrop-contrast":[d]}],"backdrop-grayscale":[{"backdrop-grayscale":[c]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[m]}],"backdrop-invert":[{"backdrop-invert":[p]}],"backdrop-opacity":[{"backdrop-opacity":[j]}],"backdrop-saturate":[{"backdrop-saturate":[v]}],"backdrop-sepia":[{"backdrop-sepia":[S]}],"border-collapse":[{border:["collapse","separate"]}],"border-spacing":[{"border-spacing":[l]}],"border-spacing-x":[{"border-spacing-x":[l]}],"border-spacing-y":[{"border-spacing-y":[l]}],"table-layout":[{table:["auto","fixed"]}],caption:[{caption:["top","bottom"]}],transition:[{transition:["none","all","","colors","opacity","shadow","transform",bt]}],duration:[{duration:$()}],ease:[{ease:["linear","in","out","in-out",bt]}],delay:[{delay:$()}],animate:[{animate:["none","spin","ping","pulse","bounce",bt]}],transform:[{transform:["","gpu","none"]}],scale:[{scale:[w]}],"scale-x":[{"scale-x":[w]}],"scale-y":[{"scale-y":[w]}],rotate:[{rotate:[Rn,bt]}],"translate-x":[{"translate-x":[F]}],"translate-y":[{"translate-y":[F]}],"skew-x":[{"skew-x":[C]}],"skew-y":[{"skew-y":[C]}],"transform-origin":[{origin:["center","top","top-right","right","bottom-right","bottom","bottom-left","left","top-left",bt]}],accent:[{accent:["auto",t]}],appearance:[{appearance:["none","auto"]}],cursor:[{cursor:["auto","default","pointer","wait","text","move","help","not-allowed","none","context-menu","progress","cell","crosshair","vertical-text","alias","copy","no-drop","grab","grabbing","all-scroll","col-resize","row-resize","n-resize","e-resize","s-resize","w-resize","ne-resize","nw-resize","se-resize","sw-resize","ew-resize","ns-resize","nesw-resize","nwse-resize","zoom-in","zoom-out",bt]}],"caret-color":[{caret:[t]}],"pointer-events":[{"pointer-events":["none","auto"]}],resize:[{resize:["none","y","x",""]}],"scroll-behavior":[{scroll:["auto","smooth"]}],"scroll-m":[{"scroll-m":R()}],"scroll-mx":[{"scroll-mx":R()}],"scroll-my":[{"scroll-my":R()}],"scroll-ms":[{"scroll-ms":R()}],"scroll-me":[{"scroll-me":R()}],"scroll-mt":[{"scroll-mt":R()}],"scroll-mr":[{"scroll-mr":R()}],"scroll-mb":[{"scroll-mb":R()}],"scroll-ml":[{"scroll-ml":R()}],"scroll-p":[{"scroll-p":R()}],"scroll-px":[{"scroll-px":R()}],"scroll-py":[{"scroll-py":R()}],"scroll-ps":[{"scroll-ps":R()}],"scroll-pe":[{"scroll-pe":R()}],"scroll-pt":[{"scroll-pt":R()}],"scroll-pr":[{"scroll-pr":R()}],"scroll-pb":[{"scroll-pb":R()}],"scroll-pl":[{"scroll-pl":R()}],"snap-align":[{snap:["start","end","center","align-none"]}],"snap-stop":[{snap:["normal","always"]}],"snap-type":[{snap:["none","x","y","both"]}],"snap-strictness":[{snap:["mandatory","proximity"]}],touch:[{touch:["auto","none","manipulation"]}],"touch-x":[{"touch-pan":["x","left","right"]}],"touch-y":[{"touch-pan":["y","up","down"]}],"touch-pz":["touch-pinch-zoom"],select:[{select:["none","text","all","auto"]}],"will-change":[{"will-change":["auto","scroll","contents","transform",bt]}],fill:[{fill:[t,"none"]}],"stroke-w":[{stroke:[Ha,sr,Bl]}],stroke:[{stroke:[t,"none"]}],sr:["sr-only","not-sr-only"],"forced-color-adjust":[{"forced-color-adjust":["auto","none"]}]},conflictingClassGroups:{overflow:["overflow-x","overflow-y"],overscroll:["overscroll-x","overscroll-y"],inset:["inset-x","inset-y","start","end","top","right","bottom","left"],"inset-x":["right","left"],"inset-y":["top","bottom"],flex:["basis","grow","shrink"],gap:["gap-x","gap-y"],p:["px","py","ps","pe","pt","pr","pb","pl"],px:["pr","pl"],py:["pt","pb"],m:["mx","my","ms","me","mt","mr","mb","ml"],mx:["mr","ml"],my:["mt","mb"],size:["w","h"],"font-size":["leading"],"fvn-normal":["fvn-ordinal","fvn-slashed-zero","fvn-figure","fvn-spacing","fvn-fraction"],"fvn-ordinal":["fvn-normal"],"fvn-slashed-zero":["fvn-normal"],"fvn-figure":["fvn-normal"],"fvn-spacing":["fvn-normal"],"fvn-fraction":["fvn-normal"],"line-clamp":["display","overflow"],rounded:["rounded-s","rounded-e","rounded-t","rounded-r","rounded-b","rounded-l","rounded-ss","rounded-se","rounded-ee","rounded-es","rounded-tl","rounded-tr","rounded-br","rounded-bl"],"rounded-s":["rounded-ss","rounded-es"],"rounded-e":["rounded-se","rounded-ee"],"rounded-t":["rounded-tl","rounded-tr"],"rounded-r":["rounded-tr","rounded-br"],"rounded-b":["rounded-br","rounded-bl"],"rounded-l":["rounded-tl","rounded-bl"],"border-spacing":["border-spacing-x","border-spacing-y"],"border-w":["border-w-s","border-w-e","border-w-t","border-w-r","border-w-b","border-w-l"],"border-w-x":["border-w-r","border-w-l"],"border-w-y":["border-w-t","border-w-b"],"border-color":["border-color-s","border-color-e","border-color-t","border-color-r","border-color-b","border-color-l"],"border-color-x":["border-color-r","border-color-l"],"border-color-y":["border-color-t","border-color-b"],"scroll-m":["scroll-mx","scroll-my","scroll-ms","scroll-me","scroll-mt","scroll-mr","scroll-mb","scroll-ml"],"scroll-mx":["scroll-mr","scroll-ml"],"scroll-my":["scroll-mt","scroll-mb"],"scroll-p":["scroll-px","scroll-py","scroll-ps","scroll-pe","scroll-pt","scroll-pr","scroll-pb","scroll-pl"],"scroll-px":["scroll-pr","scroll-pl"],"scroll-py":["scroll-pt","scroll-pb"],touch:["touch-x","touch-y","touch-pz"],"touch-x":["touch"],"touch-y":["touch"],"touch-pz":["touch"]},conflictingClassGroupModifiers:{"font-size":["leading"]}}},uv=Yy(mv);function Me(...t){return uv(Dh(t))}const Xn=wc("inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",{variants:{variant:{default:"bg-primary text-primary-foreground hover:bg-primary/90",destructive:"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",outline:"border bg-background text-foreground hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",secondary:"bg-secondary text-secondary-foreground hover:bg-secondary/80",ghost:"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",link:"text-primary underline-offset-4 hover:underline"},size:{default:"h-9 px-4 py-2 has-[>svg]:px-3",sm:"h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",lg:"h-10 rounded-md px-6 has-[>svg]:px-4",icon:"size-9 rounded-md"}},defaultVariants:{variant:"default",size:"default"}}),E=u.forwardRef(({className:t,variant:s,size:a,asChild:r=!1,...n},i)=>{const l=r?yh:"button";return e.jsx(l,{"data-slot":"button",className:Me(Xn({variant:s,size:a,className:t})),ref:i,...n})});E.displayName="Button";function Z({className:t,...s}){return e.jsx("div",{"data-slot":"card",className:Me("bg-card text-card-foreground flex flex-col gap-6 rounded-xl border",t),...s})}function le({className:t,...s}){return e.jsx("div",{"data-slot":"card-header",className:Me("@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 pt-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",t),...s})}function oe({className:t,...s}){return e.jsx("h4",{"data-slot":"card-title",className:Me("leading-none",t),...s})}function je({className:t,...s}){return e.jsx("p",{"data-slot":"card-description",className:Me("text-muted-foreground",t),...s})}function se({className:t,...s}){return e.jsx("div",{"data-slot":"card-content",className:Me("px-6 [&:last-child]:pb-6",t),...s})}const yl="/assets/Re_Logo-By51taPS.png",bu="/assets/landing_page_image-ClTD-4qZ.jpg";function xv(){var p,x;const{login:t,isLoading:s,error:a}=us(),[r,n]=u.useState(!1),[i,l]=u.useState(null),[o,d]=u.useState(!1);u.useEffect(()=>{const h=new Image;h.src=bu,h.onload=()=>d(!0),h.complete&&d(!0)},[]);const c=async()=>{const h=sessionStorage.getItem("__force_reauth_after_logout__");localStorage.clear(),sessionStorage.clear(),h&&sessionStorage.setItem("__force_reauth_after_logout__",h);try{sessionStorage.setItem("auth_provider","okta"),await t()}catch(g){console.error("OKTA LOGIN ERROR",g)}},m=()=>{l(null),localStorage.clear(),sessionStorage.clear(),sessionStorage.setItem("auth_provider","tanflow"),n(!0);try{Ay()}catch(h){const g=h instanceof Error?h.message:"Dealer login failed. Check console for details.";console.error("TANFLOW LOGIN ERROR",h),l(g),n(!1)}};return a&&console.error("Auth Error:",{message:a.message,error:a}),e.jsxs("div",{className:"min-h-screen flex items-center justify-center p-4 relative overflow-hidden",style:{backgroundImage:o?`url(${bu})`:"none",backgroundSize:"cover",backgroundPosition:"center",backgroundRepeat:"no-repeat"},children:[!o&&e.jsx("div",{className:"absolute inset-0 bg-gradient-to-br from-slate-900 to-slate-800"}),e.jsx("div",{className:"absolute inset-0 bg-black/50 backdrop-blur-[2px]","aria-hidden":!0}),e.jsx("div",{className:"absolute inset-0 bg-black/30","aria-hidden":!0}),e.jsxs(Z,{className:"w-full max-w-md shadow-2xl relative z-10 bg-gray-900/95 border border-gray-700 text-white",children:[e.jsx(le,{className:"space-y-1 text-center pb-6 pt-8",children:e.jsxs("div",{className:"flex flex-col items-center justify-center",children:[e.jsx("img",{src:yl,alt:"Royal Enfield",className:"h-9 w-auto max-w-[180px] object-contain mb-2"}),e.jsx("p",{className:"text-sm text-gray-400",children:"Approval Portal"})]})}),e.jsxs(se,{className:"space-y-5 pb-8 px-8",children:[a&&e.jsxs("div",{className:"bg-red-900/40 border border-red-700 text-red-200 px-4 py-3 rounded-lg text-sm",children:[e.jsx("p",{className:"font-medium",children:"Authentication Error"}),e.jsx("p",{children:a.message}),(((p=a.message)==null?void 0:p.includes("401"))||((x=a.message)==null?void 0:x.toLowerCase().includes("unauthorized")))&&e.jsxs("p",{className:"mt-2 text-xs text-red-300",children:["If you see 401 from Okta: your Okta admin must add this site’s URL to ",e.jsx("strong",{children:"Trusted Origins"})," and ensure the RE Employee application is active. Use ",e.jsx("strong",{children:"Dealer Login"})," if you are a dealer."]})]}),i&&e.jsxs("div",{className:"bg-amber-900/40 border border-amber-700 text-amber-200 px-4 py-3 rounded-lg text-sm",children:[e.jsx("p",{className:"font-medium",children:"Dealer Login (Tanflow)"}),e.jsx("p",{children:i})]}),e.jsx(E,{onClick:c,disabled:s||r,className:"w-full h-12 bg-re-red hover:bg-re-red/90 text-white font-semibold text-base border-0",size:"lg",children:s?e.jsx("div",{className:"h-5 w-5 animate-spin rounded-full border-2 border-white border-t-transparent"}):e.jsxs(e.Fragment,{children:[e.jsx(js,{className:"mr-2 h-5 w-5"}),"RE Employee Login"]})}),e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("span",{className:"flex-1 h-px bg-gray-600"}),e.jsx("span",{className:"text-sm text-gray-400 uppercase tracking-wide",children:"Or"}),e.jsx("span",{className:"flex-1 h-px bg-gray-600"})]}),e.jsx(E,{onClick:m,disabled:s||r,className:"w-full h-12 bg-blue-600 hover:bg-blue-700 text-white font-semibold text-base border-0",size:"lg",children:r?e.jsx("div",{className:"h-5 w-5 animate-spin rounded-full border-2 border-white border-t-transparent"}):e.jsxs(e.Fragment,{children:[e.jsx(fs,{className:"mr-2 h-5 w-5"}),"Dealer Login"]})}),e.jsxs("div",{className:"text-center pt-2",children:[e.jsx("p",{className:"text-sm text-gray-400",children:"Secure Single Sign On"}),e.jsx("p",{className:"text-xs text-gray-500 mt-1",children:"Choose your authentication provider."})]})]})]})]})}function bp(){const{isAuthenticated:t,isLoading:s,error:a,user:r}=us(),[n,i]=u.useState("exchanging");u.useEffect(()=>{if(a){i("error");return}if(s){const d=new URLSearchParams(window.location.search).get("code");i(d&&!r?"exchanging":r&&!t?"fetching":"exchanging")}else r&&t&&i("complete")},[t,s,a,r]);const l=()=>{switch(n){case"exchanging":return"Exchanging authorization code...";case"fetching":return"Fetching your profile...";case"complete":return"Authentication successful!";case"error":return"Authentication failed";default:return"Completing authentication..."}};return e.jsxs("div",{className:"min-h-screen flex items-center justify-center bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900",children:[e.jsx("div",{className:"absolute inset-0 bg-[url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjAgMCA2MCA2MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxnIGZpbGw9IiMxZTIxMmQiIGZpbGwtb3BhY2l0eT0iMC4wNSI+PGNpcmNsZSBjeD0iMzAiIGN5PSIzMCIgcj0iMzAiLz48L2c+PC9nPjwvc3ZnPg==')] opacity-20"}),e.jsxs("div",{className:"relative z-10 text-center px-4 max-w-md w-full",children:[e.jsx("div",{className:"mb-8",children:e.jsxs("div",{className:"flex flex-col items-center justify-center",children:[e.jsx("img",{src:yl,alt:"Royal Enfield Logo",className:"h-10 w-auto max-w-[168px] object-contain mb-2"}),e.jsx("p",{className:"text-xs text-gray-400 text-center truncate",children:"Approval Portal"})]})}),e.jsxs("div",{className:"bg-white/10 backdrop-blur-xl rounded-2xl p-8 shadow-2xl border border-white/20",children:[e.jsx("div",{className:"mb-6 flex justify-center",children:n==="error"?e.jsxs("div",{className:"relative",children:[e.jsx("div",{className:"absolute inset-0 animate-ping opacity-75",children:e.jsx(Ze,{className:"w-16 h-16 text-red-500"})}),e.jsx(Ze,{className:"w-16 h-16 text-red-500 relative"})]}):n==="complete"?e.jsxs("div",{className:"relative",children:[e.jsx("div",{className:"absolute inset-0 animate-ping opacity-75",children:e.jsx(ps,{className:"w-16 h-16 text-green-500"})}),e.jsx(ps,{className:"w-16 h-16 text-green-500 relative"})]}):e.jsxs("div",{className:"relative",children:[e.jsx(ze,{className:"w-16 h-16 animate-spin text-re-red"}),e.jsx("div",{className:"absolute inset-0 border-4 rounded-full border-re-red/20"}),e.jsx("div",{className:"absolute inset-0 border-4 border-transparent border-t-re-red rounded-full animate-spin"})]})}),e.jsxs("div",{className:"mb-6",children:[e.jsx("h2",{className:"text-xl font-semibold text-white mb-2",children:n==="complete"?"Welcome Back!":n==="error"?"Authentication Error":"Authenticating"}),e.jsx("p",{className:"text-slate-300 text-sm",children:l()})]}),n!=="error"&&e.jsxs("div",{className:"space-y-3 mb-6",children:[e.jsxs("div",{className:`flex items-center gap-3 text-sm transition-all duration-500 ${n==="exchanging"?"text-white":"text-slate-400"}`,children:[e.jsx("div",{className:`w-2 h-2 rounded-full transition-all duration-500 ${n==="exchanging"?"bg-re-red animate-pulse":"bg-slate-600"}`}),e.jsx("span",{children:"Validating credentials"})]}),e.jsxs("div",{className:`flex items-center gap-3 text-sm transition-all duration-500 ${n==="fetching"?"text-white":"text-slate-400"}`,children:[e.jsx("div",{className:`w-2 h-2 rounded-full transition-all duration-500 ${n==="fetching"?"bg-re-red animate-pulse":"bg-slate-600"}`}),e.jsx("span",{children:"Loading your profile"})]}),n==="complete"&&e.jsxs("div",{className:"flex items-center gap-3 text-sm transition-all duration-500 text-white",children:[e.jsx("div",{className:"w-2 h-2 rounded-full transition-all duration-500 bg-green-500"}),e.jsx("span",{children:"Setting up your session"})]})]}),n==="error"&&a&&e.jsxs("div",{className:"mt-6 p-4 bg-red-500/10 border border-red-500/20 rounded-lg",children:[e.jsx("p",{className:"text-red-400 text-sm",children:a.message||"An error occurred during authentication"}),e.jsx("button",{onClick:()=>{window.location.href="/"},className:"mt-4 text-sm text-red-400 hover:text-red-300 underline",children:"Return to login"})]}),n!=="error"&&n!=="complete"&&e.jsxs("div",{className:"mt-6",children:[e.jsx("div",{className:"h-1.5 bg-slate-700/50 rounded-full overflow-hidden",children:e.jsx("div",{className:"h-full bg-re-red rounded-full animate-pulse",style:{animation:"progress 2s ease-in-out infinite"}})}),e.jsx("style",{children:` - @keyframes progress { - 0%, 100% { width: 20%; } - 50% { width: 80%; } - } - `})]})]}),e.jsx("p",{className:"mt-6 text-slate-500 text-xs",children:n==="complete"?"Loading dashboard...":"Please wait while we secure your session"})]}),e.jsxs("div",{className:"absolute inset-0 overflow-hidden pointer-events-none",children:[e.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-re-red/5 rounded-full blur-3xl animate-pulse"}),e.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-re-red/5 rounded-full blur-3xl animate-pulse delay-1000"})]})]})}function hv(){const{isAuthenticated:t,isLoading:s,error:a,user:r}=us(),[n,i]=u.useState("exchanging"),[l,o]=u.useState(""),d=u.useRef(!1);u.useEffect(()=>{if(a){i("error");return}if(s){const p=new URLSearchParams(window.location.search).get("code");i(p&&!r?"exchanging":r&&!t?"fetching":"exchanging")}else r&&t&&(i("complete"),setTimeout(()=>{window.location.href="/"},1e3))},[t,s,a,r]),u.useEffect(()=>{if(d.current||window.location.pathname!=="/login/callback")return;const m=new URLSearchParams(window.location.search),p=m.get("code"),x=m.get("error");if(!p&&!x){console.log("🚪 Logout redirect detected: no code, no error - redirecting to home immediately"),d.current=!0;const b=new URLSearchParams;b.set("tanflow_logged_out","true"),b.set("logout",Date.now().toString());const f=`/?${b.toString()}`;console.log("🚪 Redirecting to:",f),window.location.replace(f);return}if(sessionStorage.getItem("auth_provider")!=="tanflow")return;(async()=>{d.current=!0;const b=new URLSearchParams(window.location.search),f=b.get("code"),j=b.get("state"),y=b.get("error");if(window.history.replaceState({},document.title,"/login/callback"),y){i("error"),sessionStorage.removeItem("auth_provider"),sessionStorage.removeItem("tanflow_auth_state");return}const v=sessionStorage.getItem("tanflow_auth_state");if(j&&j!==v){i("error"),sessionStorage.removeItem("auth_provider"),sessionStorage.removeItem("tanflow_auth_state");return}if(!f){i("error"),sessionStorage.removeItem("auth_provider"),sessionStorage.removeItem("tanflow_auth_state");return}try{i("exchanging");const w=await Ty(f,j||"");sessionStorage.removeItem("tanflow_auth_state"),i("fetching");const S=w.user||await Ln();if(S)st.setUserData(S),i("complete"),setTimeout(()=>{window.history.replaceState({},document.title,"/"),window.location.href="/"},1e3);else throw new Error("User data not received")}catch(w){console.error("Tanflow callback error:",w),i("error"),o(w.message||"Authentication failed"),sessionStorage.removeItem("auth_provider"),sessionStorage.removeItem("tanflow_auth_state")}})()},[]);const c=()=>{switch(n){case"exchanging":return"Exchanging authorization code...";case"fetching":return"Fetching your profile...";case"complete":return"Authentication successful!";case"error":return"Authentication failed";default:return"Completing authentication..."}};return e.jsxs("div",{className:"min-h-screen flex items-center justify-center bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900",children:[e.jsx("div",{className:"absolute inset-0 bg-[url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjAgMCA2MCA2MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxnIGZpbGw9IiMxZTIxMmQiIGZpbGwtb3BhY2l0eT0iMC4wNSI+PGNpcmNsZSBjeD0iMzAiIGN5PSIzMCIgcj0iMzAiLz48L2c+PC9nPjwvc3ZnPg==')] opacity-20"}),e.jsxs("div",{className:"relative z-10 text-center px-4 max-w-md w-full",children:[e.jsx("div",{className:"mb-8",children:e.jsxs("div",{className:"flex flex-col items-center justify-center",children:[e.jsx("img",{src:yl,alt:"Royal Enfield Logo",className:"h-10 w-auto max-w-[168px] object-contain mb-2"}),e.jsx("p",{className:"text-xs text-gray-400 text-center truncate",children:"Approval Portal"})]})}),e.jsxs("div",{className:"bg-white/10 backdrop-blur-xl rounded-2xl p-8 shadow-2xl border border-white/20",children:[e.jsx("div",{className:"mb-6 flex justify-center",children:n==="error"?e.jsxs("div",{className:"relative",children:[e.jsx("div",{className:"absolute inset-0 animate-ping opacity-75",children:e.jsx(Ze,{className:"w-16 h-16 text-red-500"})}),e.jsx(Ze,{className:"w-16 h-16 text-red-500 relative"})]}):n==="complete"?e.jsxs("div",{className:"relative",children:[e.jsx("div",{className:"absolute inset-0 animate-ping opacity-75",children:e.jsx(ps,{className:"w-16 h-16 text-green-500"})}),e.jsx(ps,{className:"w-16 h-16 text-green-500 relative"})]}):e.jsxs("div",{className:"relative",children:[e.jsx(ze,{className:"w-16 h-16 animate-spin text-re-red"}),e.jsx("div",{className:"absolute inset-0 border-4 rounded-full border-re-red/20"}),e.jsx("div",{className:"absolute inset-0 border-4 border-transparent border-t-re-red rounded-full animate-spin"})]})}),e.jsxs("div",{className:"mb-6",children:[e.jsx("h2",{className:"text-xl font-semibold text-white mb-2",children:n==="complete"?"Welcome Back!":n==="error"?"Authentication Error":"Authenticating"}),e.jsx("p",{className:"text-slate-300 text-sm",children:c()})]}),n!=="error"&&e.jsxs("div",{className:"space-y-3 mb-6",children:[e.jsxs("div",{className:`flex items-center gap-3 text-sm transition-all duration-500 ${n==="exchanging"?"text-white":"text-slate-400"}`,children:[e.jsx("div",{className:`w-2 h-2 rounded-full transition-all duration-500 ${n==="exchanging"?"bg-re-red animate-pulse":"bg-slate-600"}`}),e.jsx("span",{children:"Validating credentials"})]}),e.jsxs("div",{className:`flex items-center gap-3 text-sm transition-all duration-500 ${n==="fetching"?"text-white":"text-slate-400"}`,children:[e.jsx("div",{className:`w-2 h-2 rounded-full transition-all duration-500 ${n==="fetching"?"bg-re-red animate-pulse":"bg-slate-600"}`}),e.jsx("span",{children:"Loading your profile"})]}),n==="complete"&&e.jsxs("div",{className:"flex items-center gap-3 text-sm transition-all duration-500 text-white",children:[e.jsx("div",{className:"w-2 h-2 rounded-full transition-all duration-500 bg-green-500"}),e.jsx("span",{children:"Setting up your session"})]})]}),n==="error"&&l&&e.jsxs("div",{className:"mt-6 p-4 bg-red-500/10 border border-red-500/20 rounded-lg",children:[e.jsx("p",{className:"text-red-400 text-sm",children:l}),e.jsx("button",{onClick:()=>{window.location.href="/"},className:"mt-4 text-sm text-red-400 hover:text-red-300 underline",children:"Return to login"})]}),n!=="error"&&n!=="complete"&&e.jsxs("div",{className:"mt-6",children:[e.jsx("div",{className:"h-1.5 bg-slate-700/50 rounded-full overflow-hidden",children:e.jsx("div",{className:"h-full bg-re-red rounded-full animate-pulse",style:{animation:"progress 2s ease-in-out infinite"}})}),e.jsx("style",{children:` - @keyframes progress { - 0%, 100% { width: 20%; } - 50% { width: 80%; } - } - `})]})]}),e.jsx("p",{className:"mt-6 text-slate-500 text-xs",children:n==="complete"?"Loading dashboard...":"Please wait while we secure your session"})]}),e.jsxs("div",{className:"absolute inset-0 overflow-hidden pointer-events-none",children:[e.jsx("div",{className:"absolute top-1/4 left-1/4 w-96 h-96 bg-re-red/5 rounded-full blur-3xl animate-pulse"}),e.jsx("div",{className:"absolute bottom-1/4 right-1/4 w-96 h-96 bg-re-red/5 rounded-full blur-3xl animate-pulse delay-1000"})]})]})}const pv=wc("inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",{variants:{variant:{default:"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",secondary:"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",destructive:"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",outline:"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground"}},defaultVariants:{variant:"default"}});function re({className:t,variant:s,asChild:a=!1,...r}){const n=a?yh:"span";return e.jsx(n,{"data-slot":"badge",className:Me(pv({variant:s}),t),...r})}function gv({isOpen:t,onClose:s}){const{user:a,isAuthenticated:r,isLoading:n,error:i}=Cy();return u.useEffect(()=>{},[a,r,n,i]),t?e.jsx("div",{className:"fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4",children:e.jsxs(Z,{className:"w-full max-w-2xl max-h-[90vh] overflow-auto",children:[e.jsx(le,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(oe,{children:"Authentication Debug Information"}),e.jsx(E,{variant:"ghost",size:"icon",onClick:s,children:e.jsx(jt,{className:"h-4 w-4"})})]})}),e.jsxs(se,{className:"space-y-4",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"font-semibold",children:"Status:"}),e.jsx(re,{variant:r?"default":"destructive",children:n?"Loading...":r?"Authenticated":"Not Authenticated"})]}),i&&e.jsxs("div",{className:"bg-red-50 border border-red-200 rounded p-3",children:[e.jsx("p",{className:"text-sm text-red-700 font-semibold",children:"Error:"}),e.jsx("p",{className:"text-sm text-red-600",children:i.message})]}),a&&e.jsxs("div",{className:"space-y-2",children:[e.jsx("h4",{className:"font-semibold",children:"User Information:"}),e.jsx("pre",{className:"bg-gray-50 p-3 rounded text-xs overflow-auto",children:JSON.stringify(a,null,2)})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx("h4",{className:"font-semibold",children:"All Auth Claims:"}),e.jsx("pre",{className:"bg-gray-50 p-3 rounded text-xs overflow-auto",children:JSON.stringify({isAuthenticated:r,isLoading:n,error:(i==null?void 0:i.message)||null,hasUser:!!a},null,2)})]})]})]})}):null}const cs=u.forwardRef(({className:t,...s},a)=>e.jsx(vh,{ref:a,"data-slot":"avatar",className:Me("relative flex size-10 shrink-0 overflow-hidden rounded-full",t),...s}));cs.displayName=vh.displayName;const vl=u.forwardRef(({className:t,...s},a)=>e.jsx(Nh,{ref:a,"data-slot":"avatar-image",className:Me("aspect-square size-full",t),...s}));vl.displayName=Nh.displayName;const ds=u.forwardRef(({className:t,...s},a)=>e.jsx(wh,{ref:a,"data-slot":"avatar-fallback",className:Me("bg-muted flex size-full items-center justify-center rounded-full",t),...s}));ds.displayName=wh.displayName;function ju({...t}){return e.jsx(Uf,{"data-slot":"dropdown-menu",...t})}function yu({...t}){return e.jsx(Bf,{"data-slot":"dropdown-menu-trigger",...t})}function vu({className:t,sideOffset:s=4,...a}){return e.jsx(Vf,{children:e.jsx(zf,{"data-slot":"dropdown-menu-content",sideOffset:s,className:Me("bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",t),...a})})}function Vl({className:t,inset:s,variant:a="default",...r}){return e.jsx(Hf,{"data-slot":"dropdown-menu-item","data-inset":s,"data-variant":a,className:Me("focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",t),...r})}function jp({...t}){return e.jsx(Wf,{"data-slot":"alert-dialog",...t})}function fv({...t}){return e.jsx(Gf,{"data-slot":"alert-dialog-portal",...t})}const yp=u.forwardRef(({className:t,...s},a)=>e.jsx(Ch,{"data-slot":"alert-dialog-overlay",className:Me("data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",t),...s,ref:a}));yp.displayName=Ch.displayName;const Sc=u.forwardRef(({className:t,...s},a)=>e.jsxs(fv,{children:[e.jsx(yp,{}),e.jsx(Sh,{"data-slot":"alert-dialog-content",ref:a,className:Me("bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",t),...s})]}));Sc.displayName=Sh.displayName;function vp({className:t,...s}){return e.jsx("div",{"data-slot":"alert-dialog-header",className:Me("flex flex-col gap-2 text-center sm:text-left",t),...s})}function Np({className:t,...s}){return e.jsx("div",{"data-slot":"alert-dialog-footer",className:Me("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",t),...s})}function wp({className:t,...s}){return e.jsx(Yf,{"data-slot":"alert-dialog-title",className:Me("text-lg font-semibold",t),...s})}function Cp({className:t,...s}){return e.jsx(Kf,{"data-slot":"alert-dialog-description",className:Me("text-muted-foreground text-sm",t),...s})}function Sp({className:t,...s}){return e.jsx(Xf,{className:Me(Xn(),t),...s})}function Ap({className:t,...s}){return e.jsx(Qf,{className:Me(Xn({variant:"outline"}),t),...s})}const ir={async list(t){return(await fe.get("/notifications",{params:t})).data},async getUnreadCount(){return(await fe.get("/notifications/unread-count")).data},async markAsRead(t){return(await fe.patch(`/notifications/${t}/read`)).data},async markAllAsRead(){return(await fe.post("/notifications/mark-all-read")).data},async delete(t){return(await fe.delete(`/notifications/${t}`)).data}},bv=async()=>(await fe.get("/notifications/subscriptions")).data.data.subscriptions,Tp="https://reflow-uat.royalenfield.com/api/v1/";async function kp(t,s={}){const a=st.getAccessToken(),r={...s.headers||{}};s.body instanceof FormData||(r["Content-Type"]="application/json"),a&&(r.Authorization=`Bearer ${a}`);const n=await fetch(`${Tp}${t}`,{...s,headers:r,credentials:"include"}),i=n.headers.get("content-type"),o=(i==null?void 0:i.includes("application/json"))?await n.json():{message:await n.text()||"Request failed"};if(!n.ok){const d=new Error(o.message||`Request failed ${n.status}`);throw d.response={status:n.status,data:o},d}return o}async function Rp(){var a;const t=await fe.get("/form16/permissions"),s=((a=t.data)==null?void 0:a.data)??t.data;return{canViewForm16Submission:!!(s!=null&&s.canViewForm16Submission),canView26AS:!!(s!=null&&s.canView26AS)}}async function Dp(t){const s=new URLSearchParams;t!=null&&t.financialYear&&s.set("financialYear",t.financialYear),t!=null&&t.quarter&&s.set("quarter",t.quarter);const a=s.toString(),r=a?`/form16/credit-notes?${a}`:"/form16/credit-notes",{data:n}=await fe.get(r),i=(n==null?void 0:n.data)??n;return{creditNotes:(i==null?void 0:i.creditNotes)??[],total:(i==null?void 0:i.total)??0,summary:i==null?void 0:i.summary}}async function Fp(t){const s=new URLSearchParams;t!=null&&t.financialYear&&s.set("financialYear",t.financialYear),t!=null&&t.quarter&&s.set("quarter",t.quarter);const a=s.toString(),r=a?`/form16/debit-notes?${a}`:"/form16/debit-notes",{data:n}=await fe.get(r),i=(n==null?void 0:n.data)??n;return{debitNotes:(i==null?void 0:i.debitNotes)??[],total:(i==null?void 0:i.total)??0,summary:i==null?void 0:i.summary}}async function Ip(t){const{data:s}=await fe.get(`/form16/debit-notes/${t}/sap-response`),a=s&&typeof s=="object"&&"data"in s?s.data:s;if(!(a!=null&&a.sapResponse))throw new Error("SAP response not available");return{...a,url:`/api/v1/form16/debit-notes/${t}/sap-response/csv`}}async function Ac(t){const{data:s}=await fe.get(`/form16/credit-notes/${t}/sap-response`),a=s&&typeof s=="object"&&"data"in s?s.data:s;if(!(a!=null&&a.sapResponse))throw new Error("SAP response not available");return{...a,url:`/api/v1/form16/credit-notes/${t}/sap-response/csv`}}async function Pp(t){const{data:s}=await fe.get(`/form16/requests/${encodeURIComponent(t)}/credit-note`),a=(s==null?void 0:s.data)??s;return(a==null?void 0:a.creditNote)??null}async function jv(t){await fe.post(`/form16/requests/${encodeURIComponent(t)}/cancel-submission`)}async function yv(t){await fe.post(`/form16/requests/${encodeURIComponent(t)}/resubmission-needed`)}async function Nu(t){const{data:s}=await fe.get(`/form16/credit-notes/${t}`),a=s&&typeof s=="object"&&"data"in s?s.data:s;if(!(a!=null&&a.creditNote))throw new Error("Credit note not found");return a}async function vv(t){const s=new FormData;s.append("document",t);const a=await kp("/form16/extract",{method:"POST",body:s}),r=a,n=(r==null?void 0:r.data)??r,i=n==null?void 0:n.extractedData,l=n==null?void 0:n.ocrProvider;if(!i)throw new Error(a.message||"No extracted data returned");return{extractedData:i,ocrProvider:l}}async function Nv(t){const s=new FormData;s.append("document",t.file),s.append("financialYear",t.financialYear),s.append("quarter",t.quarter),s.append("form16aNumber",t.form16aNumber),s.append("tdsAmount",String(t.tdsAmount)),s.append("totalAmount",String(t.totalAmount)),s.append("tanNumber",t.tanNumber),s.append("deductorName",t.deductorName),t.version!=null&&s.append("version",String(t.version)),t.extractedData!=null&&s.append("ocrExtractedData",JSON.stringify(t.extractedData));const a=await kp("/form16/submissions",{method:"POST",body:s}),n=a.data??a;if(!(n!=null&&n.requestNumber))throw new Error(a.message||"Invalid response from server");return n}async function wv(t){const s=new URLSearchParams;t!=null&&t.financialYear&&s.set("financialYear",t.financialYear),t!=null&&t.quarter&&s.set("quarter",t.quarter),t!=null&&t.tanNumber&&s.set("tanNumber",t.tanNumber),t!=null&&t.search&&s.set("search",t.search),t!=null&&t.status&&s.set("status",t.status),t!=null&&t.assessmentYear&&s.set("assessmentYear",t.assessmentYear),t!=null&&t.sectionCode&&s.set("sectionCode",t.sectionCode),s.set("limit",String(t.limit)),(t==null?void 0:t.offset)!=null&&s.set("offset",String(t.offset));const a=s.toString(),r=a?`/form16/26as?${a}`:"/form16/26as",{data:n}=await fe.get(r),i=(n==null?void 0:n.data)??n;return{entries:(i==null?void 0:i.entries)??[],total:(i==null?void 0:i.total)??0,summary:(i==null?void 0:i.summary)??{totalRecords:0,booked:0,notBooked:0,pending:0,totalTaxDeducted:0}}}async function Cv(t){const s=new URLSearchParams;s.set("limit",String(t));const a=s.toString(),r=a?`/form16/26as/upload-history?${a}`:"/form16/26as/upload-history",{data:n}=await fe.get(r),i=(n==null?void 0:n.data)??n;return(i==null?void 0:i.history)??[]}const Sv=5*60*1e3;function Av(t,s){return new Promise((a,r)=>{const n=new FormData;n.append("file",t);const i=new XMLHttpRequest,l=`${Tp}/form16/26as/upload`,o=st.getAccessToken(),d=setTimeout(()=>{i.abort(),r(new Error("Upload timed out. Try a smaller file or try again."))},Sv);i.upload.addEventListener("progress",c=>{if(c.lengthComputable&&c.total>0){const m=Math.min(100,Math.round(c.loaded/c.total*100));s==null||s(m)}else s==null||s(0)}),i.addEventListener("load",()=>{clearTimeout(d),s==null||s(100);const c=i.getResponseHeader("content-type"),m=c==null?void 0:c.includes("application/json"),p=i.responseText;let x;try{x=m?JSON.parse(p):{message:p||"Request failed"}}catch{x={message:p||"Invalid response"}}if(i.status>=200&&i.status<300){const h=x.data??x;a({imported:(h==null?void 0:h.imported)??0,errors:Array.isArray(h==null?void 0:h.errors)?h.errors:[]})}else r(new Error(x.message||`Request failed ${i.status}`))}),i.addEventListener("error",()=>{clearTimeout(d),r(new Error("Network error during upload"))}),i.addEventListener("abort",()=>{clearTimeout(d),r(new Error("Upload was cancelled or timed out"))}),i.open("POST",l),o&&i.setRequestHeader("Authorization",`Bearer ${o}`),i.withCredentials=!0,i.send(n)})}async function Tv(t){const s=new URLSearchParams;t&&s.set("financialYear",t);const a=s.toString(),r=a?`/form16/non-submitted-dealers?${a}`:"/form16/non-submitted-dealers",{data:n}=await fe.get(r),i=(n==null?void 0:n.data)??n;return{summary:(i==null?void 0:i.summary)??{totalDealers:0,nonSubmittedCount:0,neverSubmittedCount:0,overdue90Count:0},dealers:(i==null?void 0:i.dealers)??[]}}async function kv(t){var l;const s=String(t.dealerCode??"").trim(),a=String(t.dealerId??"").trim(),r=String(t.email??"").trim();if(!s&&!a&&!r)throw new Error("Dealer identifier missing");const{data:n}=await fe.post("/form16/non-submitted-dealers/notify",{dealerCode:s||void 0,dealerId:a||void 0,email:r||void 0,financialYear:t.financialYear||void 0}),i=(l=n==null?void 0:n.data)==null?void 0:l.dealer;if(!i)throw new Error("No dealer returned");return i}async function Rv(t){const s=new URLSearchParams;t!=null&&t.status&&s.set("status",t.status),t!=null&&t.financialYear&&s.set("financialYear",t.financialYear),t!=null&&t.quarter&&s.set("quarter",t.quarter);const a=s.toString(),r=a?`/form16/dealer/submissions?${a}`:"/form16/dealer/submissions",{data:n}=await fe.get(r),i=(n==null?void 0:n.data)??n;return Array.isArray(i)?i:[]}async function Ep(){const{data:t}=await fe.get("/form16/dealer/pending-quarters"),s=(t==null?void 0:t.data)??t;return Array.isArray(s)?s:[]}function Gt({children:t,currentPage:s="dashboard",onNavigate:a,onNewRequest:r,onLogout:n}){const[i,l]=u.useState(!1),[o,d]=u.useState(()=>(s==null?void 0:s.startsWith("form16"))??!1),[c,m]=u.useState(!1),[p,x]=u.useState([]),[h,g]=u.useState(0),[b,f]=u.useState(!1),[j,y]=u.useState(null),{user:v}=us(),w=u.useMemo(()=>{try{const I=st.getUserData();return(I==null?void 0:I.jobTitle)==="Dealer"}catch(I){return console.error("[PageLayout] Error checking dealer status:",I),!1}},[]),S=(v==null?void 0:v.role)==="ADMIN",C=()=>{var I,T;try{if(v!=null&&v.displayName&&typeof v.displayName=="string"){const k=v.displayName.split(" ").filter(Boolean);return k.length>=2?`${((I=k[0])==null?void 0:I[0])||""}${((T=k[k.length-1])==null?void 0:T[0])||""}`.toUpperCase():v.displayName.substring(0,2).toUpperCase()}return v!=null&&v.email&&typeof v.email=="string"?v.email.substring(0,2).toUpperCase():"U"}catch(k){return console.error("[PageLayout] Error getting user initials:",k),"U"}},P=u.useMemo(()=>{const I=[{id:"dashboard",label:"Dashboard",icon:tj},{id:"requests",label:"All Requests",icon:$h,adminOnly:!1}];return w||I.push({id:"my-requests",label:"My Requests",icon:zt}),I.push({id:"open-requests",label:"Open Requests",icon:Re},{id:"closed-requests",label:"Closed Requests",icon:$e},{id:"shared-summaries",label:"Shared Summary",icon:pn}),I},[w,S]);u.useEffect(()=>{if(!(v!=null&&v.userId)){y(null);return}let I=!0;return Rp().then(T=>{I&&y(T)}).catch(T=>{I&&(y({canViewForm16Submission:!1,canView26AS:!1}),console.warn("[PageLayout] Form 16 permissions could not be loaded – Form 16 menu will be hidden.",T))}),()=>{I=!1}},[v==null?void 0:v.userId]);const F=j&&(j.canViewForm16Submission||j.canView26AS),A=!!(j!=null&&j.canViewForm16Submission),N=!!(j!=null&&j.canView26AS),_=s==="form16-credit-notes"||s==="form16-debit-notes"||s==="form16-transactions"||s==="form16-submit"||s==="form16-pending-submissions"||s==="form16-26as"||s==="form16-non-submitted-dealers",R=o||_,B=()=>{l(!i)},D=async I=>{var T;try{if(I.isRead||(await ir.markAsRead(I.notificationId),x(k=>k.map(z=>z.notificationId===I.notificationId?{...z,isRead:!0}:z)),g(k=>Math.max(0,k-1))),I.actionUrl&&a){const k=(T=I.metadata)==null?void 0:T.requestNumber;if(k){let z=`request/${k}`;(I.notificationType==="mention"||I.notificationType==="comment"||I.notificationType==="worknote")&&(z+="?tab=worknotes"),a(z)}}f(!1)}catch(k){console.error("[PageLayout] Error handling notification click:",k)}},L=async()=>{try{await ir.markAllAsRead(),x(I=>I.map(T=>({...T,isRead:!0}))),g(0)}catch(I){console.error("[PageLayout] Error marking all as read:",I)}};return u.useEffect(()=>{const I=v==null?void 0:v.userId;if(!I)return;let T=!0;(async()=>{var O,$;try{const M=await ir.list({page:1,limit:4,unreadOnly:!1});if(!T)return;const H=((O=M.data)==null?void 0:O.notifications)||[];x(H),g((($=M.data)==null?void 0:$.unreadCount)||0)}catch(M){console.error("[PageLayout] Failed to fetch notifications:",M)}})();const z=$r();if(z){Nc(z,I);const O=$=>{T&&(x(M=>[$.notification,...M].slice(0,4)),g(M=>M+1))};return z.on("notification:new",O),()=>{T=!1,z.off("notification:new",O)}}return()=>{T=!1}},[v]),u.useEffect(()=>{const I=()=>{window.innerWidth>=768?l(!0):l(!1)};return I(),window.addEventListener("resize",I),()=>window.removeEventListener("resize",I)},[]),e.jsxs("div",{className:"min-h-screen flex w-full bg-background",children:[i&&e.jsx("div",{className:"fixed inset-0 bg-black/50 z-40 md:hidden",onClick:()=>l(!1)}),e.jsx("aside",{className:` - fixed md:relative - inset-y-0 left-0 - w-64 - transform transition-transform duration-300 ease-in-out - ${i?"translate-x-0":"-translate-x-full"} - md:translate-x-0 - ${i?"md:w-64":"md:w-0"} - z-50 md:z-auto - flex-shrink-0 - border-r border-gray-800 bg-black - flex flex-col - overflow-hidden - `,children:e.jsxs("div",{className:`w-64 h-full flex flex-col overflow-hidden ${i?"":"md:hidden"}`,children:[e.jsx("div",{className:"p-4 border-b border-gray-800 flex-shrink-0",children:e.jsxs("div",{className:"flex flex-col items-center justify-center",children:[e.jsx("img",{src:yl,alt:"Royal Enfield Logo",className:"h-10 w-auto max-w-[168px] object-contain"}),e.jsx("p",{className:"text-xs text-gray-400 text-center mt-1 truncate",children:"RE Flow"})]})}),e.jsxs("div",{className:"p-3 flex-1 overflow-y-auto",children:[e.jsxs("div",{className:"space-y-2",children:[P.filter(I=>!I.adminOnly||(v==null?void 0:v.role)==="ADMIN").map(I=>e.jsxs("button",{onClick:()=>{a==null||a(I.id),window.innerWidth<768&&l(!1)},className:`w-full flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition-colors ${s===I.id?"bg-re-green text-white font-medium":"text-gray-300 hover:bg-gray-900 hover:text-white"}`,children:[e.jsx(I.icon,{className:"w-4 h-4 shrink-0"}),e.jsx("span",{className:"truncate",children:I.label})]},I.id)),F&&e.jsxs("div",{className:"pt-2 border-t border-gray-800",children:[e.jsxs("button",{type:"button",onClick:()=>d(!R),className:`w-full flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition-colors ${_?"bg-re-green text-white font-medium":"text-gray-300 hover:bg-gray-900 hover:text-white"}`,children:[e.jsx(Pt,{className:"w-4 h-4 shrink-0"}),e.jsx("span",{className:"truncate flex-1 text-left",children:"Form 16"}),R?e.jsx(gn,{className:"w-4 h-4 shrink-0"}):e.jsx(Br,{className:"w-4 h-4 shrink-0"})]}),R&&e.jsx("div",{className:"mt-1 ml-4 pl-2 border-l border-gray-700 space-y-0.5",children:w?e.jsx(e.Fragment,{children:A&&e.jsxs(e.Fragment,{children:[e.jsxs("button",{type:"button",onClick:()=>{a==null||a("/form16/submit"),window.innerWidth<768&&l(!1)},className:`w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-sm transition-colors ${s==="form16-submit"?"bg-re-green/80 text-white font-medium":"text-gray-400 hover:bg-gray-800 hover:text-white"}`,children:[e.jsx(Re,{className:"w-3.5 h-3.5 shrink-0"}),e.jsx("span",{className:"truncate",children:"Submit Form 16"})]}),e.jsxs("button",{type:"button",onClick:()=>{a==null||a("/form16/pending-submissions"),window.innerWidth<768&&l(!1)},className:`w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-sm transition-colors ${s==="form16-pending-submissions"?"bg-re-green/80 text-white font-medium":"text-gray-400 hover:bg-gray-800 hover:text-white"}`,children:[e.jsx(Re,{className:"w-3.5 h-3.5 shrink-0"}),e.jsx("span",{className:"truncate",children:"Pending Submissions"})]}),e.jsxs("button",{type:"button",onClick:()=>{a==null||a("/form16/credit-notes"),window.innerWidth<768&&l(!1)},className:`w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-sm transition-colors ${s==="form16-credit-notes"?"bg-re-green/80 text-white font-medium":"text-gray-400 hover:bg-gray-800 hover:text-white"}`,children:[e.jsx(Pt,{className:"w-3.5 h-3.5 shrink-0"}),e.jsx("span",{className:"truncate",children:"Credit Notes"})]})]})}):e.jsxs(e.Fragment,{children:[N&&e.jsxs("button",{type:"button",onClick:()=>{a==null||a("/form16/26as"),window.innerWidth<768&&l(!1)},className:`w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-sm transition-colors ${s==="form16-26as"?"bg-re-green/80 text-white font-medium":"text-gray-400 hover:bg-gray-800 hover:text-white"}`,children:[e.jsx(Re,{className:"w-3.5 h-3.5 shrink-0"}),e.jsx("span",{className:"truncate",children:"26AS Management"})]}),A&&e.jsxs(e.Fragment,{children:[e.jsxs("button",{type:"button",onClick:()=>{a==null||a("/form16/non-submitted-dealers"),window.innerWidth<768&&l(!1)},className:`w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-sm transition-colors ${s==="form16-non-submitted-dealers"?"bg-re-green/80 text-white font-medium":"text-gray-400 hover:bg-gray-800 hover:text-white"}`,children:[e.jsx(Pt,{className:"w-3.5 h-3.5 shrink-0"}),e.jsx("span",{className:"truncate",children:"Non-submitted Dealers"})]}),e.jsxs("button",{type:"button",onClick:()=>{a==null||a("/form16/transactions"),window.innerWidth<768&&l(!1)},className:`w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-sm transition-colors ${s==="form16-transactions"?"bg-re-green/80 text-white font-medium":"text-gray-400 hover:bg-gray-800 hover:text-white"}`,children:[e.jsx(Pt,{className:"w-3.5 h-3.5 shrink-0"}),e.jsx("span",{className:"truncate",children:"Transactions"})]})]})]})})]})]}),!w&&e.jsx("div",{className:"mt-6 pt-6 border-t border-gray-800 px-3",children:e.jsxs(E,{onClick:r,className:"w-full bg-re-green hover:bg-re-green/90 text-white text-sm font-medium",size:"sm",children:[e.jsx(ms,{className:"w-4 h-4 mr-2"}),"Raise New Request"]})})]})]})}),e.jsxs("div",{className:"flex-1 flex flex-col min-w-0",children:[e.jsxs("header",{className:"h-16 border-b border-gray-200 bg-white flex items-center justify-between px-6 shrink-0",children:[e.jsx("div",{className:"flex items-center gap-4 min-w-0 flex-1",children:e.jsx(E,{variant:"ghost",size:"icon",onClick:B,className:"shrink-0 h-10 w-10 sidebar-toggle",children:i?e.jsx(sj,{className:"w-5 h-5 text-gray-600"}):e.jsx(aj,{className:"w-5 h-5 text-gray-600"})})}),e.jsxs("div",{className:"flex items-center gap-4 shrink-0",children:[!w&&e.jsxs(E,{onClick:r,className:"bg-re-green hover:bg-re-green/90 text-white gap-2 hidden md:flex text-sm",size:"sm",children:[e.jsx(ms,{className:"w-4 h-4"}),"New Request"]}),e.jsxs(ju,{open:b,onOpenChange:f,children:[e.jsx(yu,{asChild:!0,children:e.jsxs(E,{variant:"ghost",size:"icon",className:"relative shrink-0 h-10 w-10",children:[e.jsx(Es,{className:"w-5 h-5"}),h>0&&e.jsx(re,{className:"absolute -top-1 -right-1 w-5 h-5 rounded-full bg-destructive text-destructive-foreground text-xs flex items-center justify-center p-0",children:h>9?"9+":h})]})}),e.jsxs(vu,{align:"end",className:"w-96 max-h-[500px]",children:[e.jsxs("div",{className:"p-3 border-b flex items-center justify-between sticky top-0 bg-white z-10",children:[e.jsx("h4",{className:"font-semibold text-base",children:"Notifications"}),h>0&&e.jsx(E,{variant:"ghost",size:"sm",className:"text-xs text-blue-600 hover:text-blue-700 h-auto p-1",onClick:I=>{I.stopPropagation(),L()},children:"Mark all as read"})]}),e.jsx("div",{className:"max-h-[400px] overflow-y-auto",children:p.length===0?e.jsxs("div",{className:"p-6 text-center",children:[e.jsx(Es,{className:"w-12 h-12 text-gray-300 mx-auto mb-2"}),e.jsx("p",{className:"text-sm text-gray-500",children:"No notifications yet"})]}):e.jsx("div",{className:"divide-y",children:p.map(I=>e.jsx("div",{className:`p-3 hover:bg-gray-50 cursor-pointer transition-colors ${I.isRead?"":"bg-blue-50"}`,onClick:()=>D(I),children:e.jsxs("div",{className:"flex gap-2",children:[!I.isRead&&e.jsx("div",{className:"w-2 h-2 rounded-full bg-blue-600 mt-1.5 shrink-0"}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx("p",{className:`text-sm ${I.isRead?"font-medium":"font-semibold"}`,children:I.title}),e.jsx("p",{className:"text-xs text-muted-foreground mt-0.5 line-clamp-2",children:I.message}),e.jsx("p",{className:"text-xs text-gray-400 mt-1",children:Eh(new Date(I.createdAt),{addSuffix:!0})})]})]})},I.notificationId))})}),p.length>0&&e.jsx("div",{className:"p-2 border-t",children:e.jsx(E,{variant:"ghost",className:"w-full text-sm text-blue-600 hover:text-blue-700",onClick:()=>{f(!1),a==null||a("notifications")},children:"View all notifications"})})]})]}),e.jsxs(ju,{children:[e.jsx(yu,{asChild:!0,children:e.jsxs(cs,{className:"cursor-pointer shrink-0 h-10 w-10",children:[e.jsx(vl,{src:(v==null?void 0:v.picture)||""}),e.jsx(ds,{className:"bg-re-green text-white text-sm",children:C()})]})}),e.jsxs(vu,{align:"end",children:[e.jsxs(Vl,{onClick:()=>a==null?void 0:a("profile"),children:[e.jsx(zt,{className:"w-4 h-4 mr-2"}),"Profile"]}),e.jsxs(Vl,{onClick:()=>a==null?void 0:a("settings"),children:[e.jsx(lr,{className:"w-4 h-4 mr-2"}),"Settings"]}),e.jsxs(Vl,{onClick:()=>m(!0),className:"text-red-600 focus:text-red-600",children:[e.jsx(_l,{className:"w-4 h-4 mr-2"}),"Logout"]})]})]})]})]}),e.jsx("main",{className:"flex-1 p-2 sm:p-4 lg:p-6 overflow-auto min-w-0",children:t})]}),e.jsx(jp,{open:c,onOpenChange:m,children:e.jsxs(Sc,{children:[e.jsxs(vp,{children:[e.jsxs(wp,{className:"flex items-center gap-2",children:[e.jsx(_l,{className:"w-5 h-5 text-red-600"}),"Confirm Logout"]}),e.jsx(Cp,{className:"pt-2",children:"Are you sure you want to logout? You will need to sign in again to access your account."})]}),e.jsxs(Np,{children:[e.jsx(Ap,{onClick:()=>m(!1),children:"Cancel"}),e.jsxs(Sp,{onClick:async()=>{if(m(!1),n)try{await n()}catch(I){console.error("🔴 Error calling onLogout:",I)}else console.error("🔴 ERROR: onLogout is undefined!")},className:"bg-red-600 hover:bg-red-700 text-white focus:ring-red-600",children:[e.jsx(_l,{className:"w-4 h-4 mr-2"}),"Logout"]})]})]})})]})}const ii=()=>py(),jn=by,Dv=()=>{try{const t=localStorage.getItem("dashboard_viewAsUser");return t?JSON.parse(t):!1}catch{return!1}},Fv={viewAsUser:Dv()},_p=fn({name:"dashboard",initialState:Fv,reducers:{setViewAsUser:(t,s)=>{t.viewAsUser=s.payload,localStorage.setItem("dashboard_viewAsUser",JSON.stringify(s.payload))}}}),{setViewAsUser:Iv}=_p.actions;function Pv(){const[t,s]=u.useState("all"),[a,r]=u.useState(void 0),[n,i]=u.useState(void 0),[l,o]=u.useState(!1),d=u.useCallback(p=>{const x=p;s(x),x!=="custom"?(r(void 0),i(void 0),o(!1)):o(!0)},[]),c=u.useCallback(p=>{if(a&&n){if(a>n){const x=a;r(n),i(x),p(n,x)}else p(a,n);o(!1)}},[a,n]),m=u.useCallback(()=>{r(void 0),i(void 0),o(!1),s("all")},[]);return{dateRange:t,customStartDate:a,customEndDate:n,showCustomDatePicker:l,setDateRange:s,setCustomStartDate:r,setCustomEndDate:i,setShowCustomDatePicker:o,handleDateRangeChange:d,handleApplyCustomDate:c,resetCustomDates:m}}function Ev(){const[t,s]=u.useState({page:1,totalPages:1,totalRecords:0}),[a,r]=u.useState({page:1,totalPages:1,totalRecords:0}),[n,i]=u.useState({page:1,totalPages:1,totalRecords:0}),[l,o]=u.useState({page:1,totalPages:1,totalRecords:0}),d=u.useCallback((f,j,y)=>{s({page:f,totalPages:j,totalRecords:y})},[]),c=u.useCallback((f,j,y)=>{r({page:f,totalPages:j,totalRecords:y})},[]),m=u.useCallback((f,j,y)=>{i({page:f,totalPages:j,totalRecords:y})},[]),p=u.useCallback((f,j,y)=>{o({page:f,totalPages:j,totalRecords:y})},[]),x=u.useCallback((f,j)=>{f>=1&&f<=t.totalPages&&(s(y=>({...y,page:f})),j(f))},[t.totalPages]),h=u.useCallback((f,j)=>{f>=1&&f<=a.totalPages&&(r(y=>({...y,page:f})),j(f))},[a.totalPages]),g=u.useCallback((f,j)=>{f>=1&&f<=n.totalPages&&(i(y=>({...y,page:f})),j(f))},[n.totalPages]),b=u.useCallback((f,j)=>{f>=1&&f<=l.totalPages&&(o(y=>({...y,page:f})),j(f))},[l.totalPages]);return{activity:t,critical:a,deadlines:n,approver:l,updateActivityPagination:d,updateCriticalPagination:c,updateDeadlinesPagination:m,updateApproverPagination:p,handleActivityPageChange:x,handleCriticalPageChange:h,handleDeadlinesPageChange:g,handleApproverPageChange:b}}class _v{async getKPIs(s,a,r,n){try{const i={dateRange:s};return s==="custom"&&a&&r&&(i.startDate=a.toISOString(),i.endDate=r.toISOString()),n&&(i.viewAsUser="true"),(await fe.get("/dashboard/kpis",{params:i})).data.data}catch(i){throw console.error("Failed to fetch KPIs:",i),i}}async getRequestStats(s,a,r,n,i,l,o,d,c,m,p,x,h){try{const g={dateRange:s};return s==="custom"&&a&&r&&(g.startDate=a,g.endDate=r),n&&n!=="all"&&(g.status=n),i&&i!=="all"&&(g.priority=i),l&&l!=="all"&&(g.templateType=l),o&&o!=="all"&&(g.department=o),d&&d!=="all"&&(g.initiator=d),c&&c!=="all"&&(g.approver=c),m&&(g.approverType=m),p&&(g.search=p),x&&x!=="all"&&(g.slaCompliance=x),h&&(g.viewAsUser="true"),(await fe.get("/dashboard/stats/requests",{params:g})).data.data}catch(g){throw console.error("Failed to fetch request stats:",g),g}}async getTATEfficiency(s){try{return(await fe.get("/dashboard/stats/tat-efficiency",{params:{dateRange:s}})).data.data}catch(a){throw console.error("Failed to fetch TAT efficiency:",a),a}}async getApproverLoad(s){try{return(await fe.get("/dashboard/stats/approver-load",{params:{dateRange:s}})).data.data}catch(a){throw console.error("Failed to fetch approver load:",a),a}}async getEngagementStats(s){try{return(await fe.get("/dashboard/stats/engagement",{params:{dateRange:s}})).data.data}catch(a){throw console.error("Failed to fetch engagement stats:",a),a}}async getAIInsights(s){try{return(await fe.get("/dashboard/stats/ai-insights",{params:{dateRange:s}})).data.data}catch(a){throw console.error("Failed to fetch AI insights:",a),a}}async getRecentActivity(s=1,a=10,r){try{const n={page:s,limit:a};r&&(n.viewAsUser="true");const i=await fe.get("/dashboard/activity/recent",{params:n});return{activities:i.data.data,pagination:i.data.pagination}}catch(n){throw console.error("Failed to fetch recent activity:",n),n}}async getCriticalRequests(s=1,a=10,r){try{const n={page:s,limit:a};r&&(n.viewAsUser="true");const i=await fe.get("/dashboard/requests/critical",{params:n});return{criticalRequests:i.data.data,pagination:i.data.pagination}}catch(n){throw console.error("Failed to fetch critical requests:",n),n}}async getUpcomingDeadlines(s=1,a=10,r){try{const n={page:s,limit:a};r&&(n.viewAsUser="true");const i=await fe.get("/dashboard/deadlines/upcoming",{params:n});return{deadlines:i.data.data,pagination:i.data.pagination}}catch(n){throw console.error("Failed to fetch upcoming deadlines:",n),n}}async getDepartmentStats(s,a,r){try{const n={dateRange:s};return s==="custom"&&a&&r&&(n.startDate=a.toISOString(),n.endDate=r.toISOString()),(await fe.get("/dashboard/stats/by-department",{params:n})).data.data}catch(n){throw console.error("Failed to fetch department stats:",n),n}}async getPriorityDistribution(s,a,r){try{const n={dateRange:s};return s==="custom"&&a&&r&&(n.startDate=a.toISOString(),n.endDate=r.toISOString()),(await fe.get("/dashboard/stats/priority-distribution",{params:n})).data.data}catch(n){throw console.error("Failed to fetch priority distribution:",n),n}}async getAIRemarkUtilization(s,a,r){try{const n={dateRange:s};return s==="custom"&&a&&r&&(n.startDate=a.toISOString(),n.endDate=r.toISOString()),(await fe.get("/dashboard/stats/ai-remark-utilization",{params:n})).data.data}catch(n){throw console.error("Failed to fetch AI remark utilization:",n),n}}async getApproverPerformance(s,a=1,r=10,n,i,l,o){try{const d={dateRange:s,page:a,limit:r||10};s==="custom"&&n&&i&&(d.startDate=n.toISOString(),d.endDate=i.toISOString()),l&&l!=="all"&&(d.priority=l),o&&o!=="all"&&(d.slaCompliance=o);const c=await fe.get("/dashboard/stats/approver-performance",{params:d});return{performance:c.data.data,pagination:c.data.pagination}}catch(d){throw console.error("Failed to fetch approver performance:",d),d}}async getLifecycleReport(s=1,a=50,r,n,i){try{const l={page:s,limit:a};r&&(l.dateRange=r),r==="custom"&&n&&i&&(l.startDate=n.toISOString(),l.endDate=i.toISOString());const o=await fe.get("/dashboard/reports/lifecycle",{params:l});return{lifecycleData:o.data.data,pagination:o.data.pagination}}catch(l){throw console.error("Failed to fetch lifecycle report:",l),l}}async getActivityLogReport(s=1,a=50,r,n,i,l,o,d,c){try{const m={page:s,limit:a,filterUserId:n,filterType:i,filterCategory:l,filterSeverity:o};r&&(m.dateRange=r),r==="custom"&&d&&c&&(m.startDate=d.toISOString(),m.endDate=c.toISOString());const p=await fe.get("/dashboard/reports/activity-log",{params:m});return{activities:p.data.data,pagination:p.data.pagination}}catch(m){throw console.error("Failed to fetch activity log report:",m),m}}async getDepartments(){try{return(await fe.get("/dashboard/metadata/departments")).data.data.departments||[]}catch(s){throw console.error("Failed to fetch departments:",s),s}}async getWorkflowAgingReport(s=7,a=1,r=50,n,i,l){try{const o={threshold:s,page:a,limit:r};n&&(o.dateRange=n),n==="custom"&&i&&l&&(o.startDate=i.toISOString(),o.endDate=l.toISOString());const d=await fe.get("/dashboard/reports/workflow-aging",{params:o});return{agingData:d.data.data,pagination:d.data.pagination}}catch(o){throw console.error("Failed to fetch workflow aging report:",o),o}}async getSingleApproverStats(s,a,r,n,i,l){try{const o={approverId:s};return a&&(o.dateRange=a),a==="custom"&&r&&n&&(o.startDate=r.toISOString(),o.endDate=n.toISOString()),i&&i!=="all"&&(o.priority=i),l&&l!=="all"&&(o.slaCompliance=l),(await fe.get("/dashboard/stats/single-approver",{params:o})).data.data}catch(o){throw console.error("Failed to fetch single approver stats:",o),o}}async getRequestsByApprover(s,a=1,r=50,n,i,l,o,d,c,m){try{const p={approverId:s,page:a,limit:r};n&&(p.dateRange=n),n==="custom"&&i&&l&&(p.startDate=i.toISOString(),p.endDate=l.toISOString()),o&&(p.status=o),d&&(p.priority=d),c&&(p.slaCompliance=c),m&&(p.search=m);const x=await fe.get("/dashboard/requests/by-approver",{params:p});return{requests:x.data.data,pagination:x.data.pagination}}catch(p){throw console.error("Failed to fetch requests by approver:",p),p}}}const Ot=new _v;function Mv({isAdmin:t,viewAsUser:s=!1,userId:a,dateRange:r,customStartDate:n,customEndDate:i,onPaginationUpdate:l}){const[o,d]=u.useState(null),[c,m]=u.useState([]),[p,x]=u.useState([]),[h,g]=u.useState([]),[b,f]=u.useState([]),[j,y]=u.useState([]),[v,w]=u.useState(null),[S,C]=u.useState([]),[P,F]=u.useState(!0),[A,N]=u.useState(!1),_=u.useRef(l);_.current=l;const R=u.useCallback(async(T=!1)=>{try{T?N(!0):F(!0);const k=[Ot.getKPIs(r,n,i,s),Ot.getRecentActivity(1,10,s),Ot.getCriticalRequests(1,10,s),Ot.getUpcomingDeadlines(1,10,s)],z=!t&&a?Ot.getRequestStats(r,n==null?void 0:n.toISOString(),i==null?void 0:i.toISOString(),void 0,void 0,void 0,void 0,a,void 0,void 0,void 0,void 0,s):null,O=t?[Ot.getDepartmentStats(r,n,i),Ot.getPriorityDistribution(r,n,i),Ot.getAIRemarkUtilization(r,n,i),Ot.getApproverPerformance(r,1,10,n,i)]:[],[$,M,H]=await Promise.all([Promise.all(k),z,Promise.all(O)]),X=$[0],W=$[1],U=$[2],V=$[3];if(!t&&M&&(X.requestVolume=M),d(X),m(W.activities),_.current.activity(W.pagination.currentPage,W.pagination.totalPages,W.pagination.totalRecords),x(U.criticalRequests),_.current.critical(U.pagination.currentPage,U.pagination.totalPages,U.pagination.totalRecords),y(V.deadlines),_.current.deadlines(V.pagination.currentPage,V.pagination.totalPages,V.pagination.totalRecords),t&&H.length>=4){const Q=H[0],te=H[1],K=H[2],ee=H[3];g(Q),f(te),w(K),C(ee.performance),_.current.approver(ee.pagination.currentPage,ee.pagination.totalPages,ee.pagination.totalRecords)}else t||(g([]),f([]),w(null),C([]))}catch(k){console.error("Failed to fetch dashboard data:",k)}finally{F(!1),N(!1)}},[t,s,a,r,n,i]),B=u.useCallback(async(T=1)=>{try{const k=await Ot.getRecentActivity(T,10,s);m(k.activities),_.current.activity(k.pagination.currentPage,k.pagination.totalPages,k.pagination.totalRecords)}catch(k){console.error("Failed to fetch recent activities:",k)}},[s]),D=u.useCallback(async(T=1)=>{try{const k=await Ot.getCriticalRequests(T,10,s);x(k.criticalRequests),_.current.critical(k.pagination.currentPage,k.pagination.totalPages,k.pagination.totalRecords)}catch(k){console.error("Failed to fetch critical requests:",k)}},[s]),L=u.useCallback(async(T=1)=>{try{const k=await Ot.getUpcomingDeadlines(T,10,s);y(k.deadlines),_.current.deadlines(k.pagination.currentPage,k.pagination.totalPages,k.pagination.totalRecords)}catch(k){console.error("Failed to fetch upcoming deadlines:",k)}},[s]),I=u.useCallback(async(T=1)=>{try{const k=await Ot.getApproverPerformance(r,T,10,n,i);C(k.performance),_.current.approver(k.pagination.currentPage,k.pagination.totalPages,k.pagination.totalRecords)}catch(k){console.error("Failed to fetch approver performance:",k)}},[r,n,i]);return{kpis:o,recentActivity:c,criticalRequests:p,departmentStats:h,priorityDistribution:b,upcomingDeadlines:j,aiRemarkUtilization:v,approverPerformance:S,loading:P,refreshing:A,fetchDashboardData:R,fetchRecentActivities:B,fetchCriticalRequests:D,fetchUpcomingDeadlines:L,fetchApproverPerformance:I}}const Ga={appName:"Royal Enfield Workflow Management",appVersion:"1.2.0",workingHours:{START_HOUR:9,END_HOUR:18,START_DAY:1,END_DAY:5,TIMEZONE:"Asia/Kolkata"},tat:{thresholds:{warning:50,critical:75,breach:100},testMode:!1},upload:{maxFileSizeMB:10,allowedFileTypes:["pdf","doc","docx","xls","xlsx","ppt","pptx","jpg","jpeg","png","gif"],maxFilesPerRequest:10},workflow:{maxApprovalLevels:10,maxParticipants:50,maxSpectators:20},workNotes:{maxMessageLength:2e3,maxAttachmentsPerNote:5,enableReactions:!0,enableMentions:!0},features:{ENABLE_AI_CONCLUSION:!0,ENABLE_TEMPLATES:!1,ENABLE_ANALYTICS:!0,ENABLE_EXPORT:!0},ui:{DEFAULT_THEME:"light",DEFAULT_LANGUAGE:"en",DATE_FORMAT:"DD/MM/YYYY",TIME_FORMAT:"12h",CURRENCY:"INR",CURRENCY_SYMBOL:"₹"}};class Lv{constructor(){Fl(this,"config",null);Fl(this,"loading",null)}async getConfig(){return this.config?this.config:this.loading?this.loading:(this.loading=this.fetchConfig(),this.config=await this.loading,this.loading=null,this.config)}async fetchConfig(){var s;try{const a=await fe.get("/config"),r=((s=a.data)==null?void 0:s.data)||a.data;return{...Ga,...r,workingHours:{...Ga.workingHours,...r.workingHours},tat:{...Ga.tat,...r.tat},upload:{...Ga.upload,...r.upload},workflow:{...Ga.workflow,...r.workflow},workNotes:{...Ga.workNotes,...r.workNotes},features:{...Ga.features,...r.features},ui:{...Ga.ui,...r.ui}}}catch(a){return console.error("[ConfigService] ⚠️ Failed to fetch config from server, using defaults:",a),Ga}}async refreshConfig(){return this.config=null,this.loading=null,this.getConfig()}getCachedConfig(){return this.config}isLoaded(){return this.config!==null}}const Ov=new Lv;let $v=9,Uv=18,Bv=1,Vv=5,wu=!1;async function zv(){if(!wu)try{const t=await Ov.getConfig();$v=t.workingHours.START_HOUR,Uv=t.workingHours.END_HOUR,Bv=t.workingHours.START_DAY,Vv=t.workingHours.END_DAY,wu=!0}catch{console.warn("[SLA Tracker] ⚠️ Using default working hours (9 AM - 6 PM)")}}zv().catch(()=>{});function Xt(t){if(t==null||t<0||t===0)return"0 hours";const s=8;if(t<1){const o=Math.round(t*60);return o>0?`${o}m`:"0 hours"}const a=Math.floor(t/s),r=Math.floor(t%s),n=Math.round(t%1*60);if(a>0){const o=a===1?"day":"days",d=r===1?"hour":"hours",c=n===1?"min":"m";return n>0?`${a} ${o} ${r} ${d} ${n}${c}`:`${a} ${o} ${r} ${d}`}const i=r===1?"hour":"hours",l=n===1?"min":"m";return n>0?`${r} ${i} ${n}${l}`:`${r} ${i}`}async function Hv(t,s,a){const r=[];let n=1,i=!0;const l=100;for(;i&&n<=l;){const x=await Ot.getApproverPerformance(t,n,100,s,a);x.performance&&x.performance.length>0?(r.push(...x.performance),n++,i=n<=x.pagination.totalPages):i=!1}const o=[["Approver Name","Total Approved","TAT Compliance (%)","Avg Response Time","Pending Count"].join(",")];r.forEach(x=>{const h=[`"${(x.approverName||"Unknown").replace(/"/g,'""')}"`,x.totalApproved||0,x.tatCompliancePercent||0,Xt(x.avgResponseHours),x.pendingCount||0];o.push(h.join(","))});const d=o.join(` -`),c=new Blob([d],{type:"text/csv;charset=utf-8;"}),m=document.createElement("a"),p=URL.createObjectURL(c);m.setAttribute("href",p),m.setAttribute("download",`approver-performance-report-${new Date().toISOString().split("T")[0]}.csv`),m.style.visibility="hidden",document.body.appendChild(m),m.click(),document.body.removeChild(m),URL.revokeObjectURL(p)}async function Gv(t,s,a){const r=await Ot.getDepartmentStats(t,s,a),n=[["Department","Total Requests","Approved","Rejected","In Progress","Approval Rate (%)"].join(",")];r.forEach(c=>{const m=[`"${(c.department||"Unknown").replace(/"/g,'""')}"`,c.totalRequests||0,c.approved||0,c.rejected||0,c.inProgress||0,c.approvalRate||0];n.push(m.join(","))});const i=n.join(` -`),l=new Blob([i],{type:"text/csv;charset=utf-8;"}),o=document.createElement("a"),d=URL.createObjectURL(l);o.setAttribute("href",d),o.setAttribute("download",`department-workflow-summary-${new Date().toISOString().split("T")[0]}.csv`),o.style.visibility="hidden",document.body.appendChild(o),o.click(),document.body.removeChild(o),URL.revokeObjectURL(d)}function Wv(){const[t,s]=u.useState(!1),[a,r]=u.useState(!1),n=u.useCallback(async(l,o,d)=>{try{s(!0),await Gv(l,o,d)}catch(c){console.error("Failed to export department stats:",c),alert("Failed to export department statistics. Please try again.")}finally{s(!1)}},[]),i=u.useCallback(async(l,o,d)=>{try{r(!0),await Hv(l,o,d)}catch(c){console.error("Failed to export approver performance:",c),alert("Failed to export approver performance data. Please try again.")}finally{r(!1)}},[]);return{exportingDeptStats:t,exportingApproverPerformance:a,handleExportDepartmentStats:n,handleExportApproverPerformance:i}}function os({className:t,...s}){return e.jsx(Jf,{"data-slot":"switch",className:Me("peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-switch-background focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",t),...s,children:e.jsx(Zf,{"data-slot":"switch-thumb",className:Me("bg-card dark:data-[state=unchecked]:bg-card-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0")})})}function ae({className:t,...s}){return e.jsx(qf,{"data-slot":"label",className:Me("flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",t),...s})}function Yv({isAdmin:t,effectiveIsAdmin:s,viewAsUser:a,onToggleView:r,quickActions:n,userDisplayName:i,userEmail:l}){const o=i||(l==null?void 0:l.split("@")[0])||"User";return e.jsxs(Z,{className:"relative overflow-hidden shadow-xl border-0","data-testid":"dashboard-hero",children:[e.jsx("div",{className:"absolute inset-0 bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900"}),e.jsxs(se,{className:"relative z-10 p-4 sm:p-6 lg:p-12",children:[t&&e.jsx("div",{className:"absolute top-4 right-4 sm:top-6 sm:right-6 z-20","data-testid":"view-toggle",children:e.jsxs("div",{className:"flex items-center gap-1.5 sm:gap-2 p-1.5 sm:p-2 bg-white/10 backdrop-blur-sm rounded-lg border border-white/20 shadow-lg",children:[e.jsxs("div",{className:`flex items-center gap-1 sm:gap-1.5 px-1.5 sm:px-2 py-0.5 sm:py-1 rounded transition-all cursor-pointer ${a?"opacity-60 hover:opacity-80":"bg-red-600/20 border border-red-600/50"}`,onClick:()=>r(!1),children:[e.jsx(uc,{className:`w-3 h-3 sm:w-3.5 sm:h-3.5 ${a?"text-gray-300":"text-red-600"}`}),e.jsx(ae,{htmlFor:"view-toggle-switch",className:`text-[10px] sm:text-xs font-medium cursor-pointer whitespace-nowrap ${a?"text-gray-300":"text-red-600"}`,children:"Org"})]}),e.jsx(os,{id:"view-toggle-switch",checked:a,onCheckedChange:r,className:"data-[state=checked]:bg-red-600 data-[state=unchecked]:bg-gray-600 shrink-0 scale-90 sm:scale-100","data-testid":"view-toggle-switch"}),e.jsxs("div",{className:`flex items-center gap-1 sm:gap-1.5 px-1.5 sm:px-2 py-0.5 sm:py-1 rounded transition-all cursor-pointer ${a?"bg-red-600/20 border border-red-600/50":"opacity-60 hover:opacity-80"}`,onClick:()=>r(!0),children:[e.jsx(zt,{className:`w-3 h-3 sm:w-3.5 sm:h-3.5 ${a?"text-red-600":"text-gray-300"}`}),e.jsx(ae,{htmlFor:"view-toggle-switch",className:`text-[10px] sm:text-xs font-medium cursor-pointer whitespace-nowrap ${a?"text-red-600":"text-gray-300"}`,children:"Personal"})]})]})}),e.jsx("div",{className:"flex flex-col lg:flex-row items-start lg:items-center justify-between gap-4 sm:gap-6",children:e.jsxs("div",{className:`text-white w-full lg:w-auto ${t?"pt-12 sm:pt-0":""}`,children:[e.jsx("div",{className:"flex items-center gap-3 sm:gap-4 mb-4 sm:mb-6",children:e.jsxs("div",{className:"pr-2 sm:pr-0",children:[e.jsxs("h1",{className:"text-2xl sm:text-3xl lg:text-4xl font-bold mb-1 sm:mb-2 text-white","data-testid":"hero-title",children:["Welcome, ",o,"!"]}),e.jsx("p",{className:"text-sm sm:text-lg lg:text-xl text-gray-200","data-testid":"hero-subtitle",children:s?"Organization-wide analytics and insights":"Track your requests and approvals"})]})}),e.jsx("div",{className:"flex flex-wrap gap-2 sm:gap-4 mt-4 sm:mt-8","data-testid":"quick-actions",children:n.map((d,c)=>e.jsxs(E,{onClick:d.action,className:`${d.color} text-white border-0 shadow-lg hover:shadow-xl transition-all duration-200`,size:window.innerWidth<640?"sm":"lg","data-testid":`quick-action-${d.label.toLowerCase().replace(/\s+/g,"-")}`,children:[e.jsx(d.icon,{className:"w-4 h-4 sm:w-5 sm:h-5 mr-1 sm:mr-2"}),d.label]},c))})]})})]})]})}function He({...t}){return e.jsx(eb,{"data-slot":"select",...t})}function Ge({...t}){return e.jsx(ab,{"data-slot":"select-value",...t})}function We({className:t,size:s="default",children:a,...r}){return e.jsxs(tb,{"data-slot":"select-trigger","data-size":s,className:Me("border-gray-400 data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground bg-white text-gray-900 flex w-full items-center justify-between gap-2 rounded-md border px-3 py-2 text-sm whitespace-nowrap transition-all outline-none disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4","hover:border-gray-500","focus-visible:border-re-light-green focus-visible:ring-0 focus-visible:outline-none","aria-invalid:ring-destructive/20 aria-invalid:border-destructive",t),...r,children:[a,e.jsx(sb,{asChild:!0,children:e.jsx(gn,{className:"size-4 opacity-50"})})]})}function Ye({className:t,children:s,position:a="popper",...r}){return e.jsx(rb,{children:e.jsxs(nb,{"data-slot":"select-content",className:Me("bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",a==="popper"&&"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",t),position:a,...r,children:[e.jsx(Kv,{}),e.jsx(ib,{className:Me("p-1",a==="popper"&&"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"),children:s}),e.jsx(Qv,{})]})})}function J({className:t,children:s,...a}){return e.jsxs(lb,{"data-slot":"select-item",className:Me("focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",t),...a,children:[e.jsx("span",{className:"absolute right-2 flex size-3.5 items-center justify-center",children:e.jsx(ob,{children:e.jsx(Oa,{className:"size-4"})})}),e.jsx(cb,{children:s})]})}function Kv({className:t,...s}){return e.jsx(db,{"data-slot":"select-scroll-up-button",className:Me("flex cursor-default items-center justify-center py-1",t),...s,children:e.jsx(nl,{className:"size-4"})})}function Qv({className:t,...s}){return e.jsx(mb,{"data-slot":"select-scroll-down-button",className:Me("flex cursor-default items-center justify-center py-1",t),...s,children:e.jsx(gn,{className:"size-4"})})}function Zt({className:t,orientation:s="horizontal",decorative:a=!0,...r}){return e.jsx(ub,{"data-slot":"separator-root",decorative:a,orientation:s,className:Me("bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",t),...r})}function sa({...t}){return e.jsx(xb,{"data-slot":"popover",...t})}function aa({...t}){return e.jsx(hb,{"data-slot":"popover-trigger",...t})}function ra({className:t,align:s="center",sideOffset:a=4,...r}){return e.jsx(pb,{children:e.jsx(gb,{"data-slot":"popover-content",align:s,sideOffset:a,className:Me("bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",t),...r})})}var ot=function(){return ot=Object.assign||function(s){for(var a,r=1,n=arguments.length;r1&&(d||!c),p=s>1&&(c||!d),x=function(){r&&i(r)},h=function(){n&&i(n)};return e.jsx(kN,{displayMonth:t.displayMonth,hideNext:m,hidePrevious:p,nextMonth:n,previousMonth:r,onPreviousClick:x,onNextClick:h})}function RN(t){var s,a=Yt(),r=a.classNames,n=a.disableNavigation,i=a.styles,l=a.captionLayout,o=a.components,d=(s=o==null?void 0:o.CaptionLabel)!==null&&s!==void 0?s:Op,c;return n?c=e.jsx(d,{id:t.id,displayMonth:t.displayMonth}):l==="dropdown"?c=e.jsx(Cu,{displayMonth:t.displayMonth,id:t.id}):l==="dropdown-buttons"?c=e.jsxs(e.Fragment,{children:[e.jsx(Cu,{displayMonth:t.displayMonth,displayIndex:t.displayIndex,id:t.id}),e.jsx(Su,{displayMonth:t.displayMonth,displayIndex:t.displayIndex,id:t.id})]}):c=e.jsxs(e.Fragment,{children:[e.jsx(d,{id:t.id,displayMonth:t.displayMonth,displayIndex:t.displayIndex}),e.jsx(Su,{displayMonth:t.displayMonth,id:t.id})]}),e.jsx("div",{className:r.caption,style:i.caption,children:c})}function DN(t){var s=Yt(),a=s.footer,r=s.styles,n=s.classNames.tfoot;return a?e.jsx("tfoot",{className:n,style:r.tfoot,children:e.jsx("tr",{children:e.jsx("td",{colSpan:8,children:a})})}):e.jsx(e.Fragment,{})}function FN(t,s,a){for(var r=a?dc(new Date):mc(new Date,{locale:t,weekStartsOn:s}),n=[],i=0;i<7;i++){var l=la(r,i);n.push(l)}return n}function IN(){var t=Yt(),s=t.classNames,a=t.styles,r=t.showWeekNumber,n=t.locale,i=t.weekStartsOn,l=t.ISOWeek,o=t.formatters.formatWeekdayName,d=t.labels.labelWeekday,c=FN(n,i,l);return e.jsxs("tr",{style:a.head_row,className:s.head_row,children:[r&&e.jsx("td",{style:a.head_cell,className:s.head_cell}),c.map(function(m,p){return e.jsx("th",{scope:"col",className:s.head_cell,style:a.head_cell,"aria-label":d(m,{locale:n}),children:o(m,{locale:n})},p)})]})}function PN(){var t,s=Yt(),a=s.classNames,r=s.styles,n=s.components,i=(t=n==null?void 0:n.HeadRow)!==null&&t!==void 0?t:IN;return e.jsx("thead",{style:r.head,className:a.head,children:e.jsx(i,{})})}function EN(t){var s=Yt(),a=s.locale,r=s.formatters.formatDay;return e.jsx(e.Fragment,{children:r(t.date,{locale:a})})}var Tc=u.createContext(void 0);function _N(t){if(!li(t.initialProps)){var s={selected:void 0,modifiers:{disabled:[]}};return e.jsx(Tc.Provider,{value:s,children:t.children})}return e.jsx(MN,{initialProps:t.initialProps,children:t.children})}function MN(t){var s=t.initialProps,a=t.children,r=s.selected,n=s.min,i=s.max,l=function(c,m,p){var x,h;(x=s.onDayClick)===null||x===void 0||x.call(s,c,m,p);var g=!!(m.selected&&n&&(r==null?void 0:r.length)===n);if(!g){var b=!!(!m.selected&&i&&(r==null?void 0:r.length)===i);if(!b){var f=r?Mp([],r):[];if(m.selected){var j=f.findIndex(function(y){return qs(c,y)});f.splice(j,1)}else f.push(c);(h=s.onSelect)===null||h===void 0||h.call(s,f,c,m,p)}}},o={disabled:[]};r&&o.disabled.push(function(c){var m=i&&r.length>i-1,p=r.some(function(x){return qs(x,c)});return!!(m&&!p)});var d={selected:r,onDayClick:l,modifiers:o};return e.jsx(Tc.Provider,{value:d,children:a})}function kc(){var t=u.useContext(Tc);if(!t)throw new Error("useSelectMultiple must be used within a SelectMultipleProvider");return t}function LN(t,s){var a=s||{},r=a.from,n=a.to;return r&&n?qs(n,t)&&qs(r,t)?void 0:qs(n,t)?{from:n,to:void 0}:qs(r,t)?void 0:Ro(r,t)?{from:t,to:n}:{from:r,to:t}:n?Ro(t,n)?{from:n,to:t}:{from:t,to:n}:r?_h(t,r)?{from:t,to:r}:{from:r,to:t}:{from:t,to:void 0}}var Rc=u.createContext(void 0);function ON(t){if(!oi(t.initialProps)){var s={selected:void 0,modifiers:{range_start:[],range_end:[],range_middle:[],disabled:[]}};return e.jsx(Rc.Provider,{value:s,children:t.children})}return e.jsx($N,{initialProps:t.initialProps,children:t.children})}function $N(t){var s=t.initialProps,a=t.children,r=s.selected,n=r||{},i=n.from,l=n.to,o=s.min,d=s.max,c=function(h,g,b){var f,j;(f=s.onDayClick)===null||f===void 0||f.call(s,h,g,b);var y=LN(h,r);(j=s.onSelect)===null||j===void 0||j.call(s,y,h,g,b)},m={range_start:[],range_end:[],range_middle:[],disabled:[]};if(i?(m.range_start=[i],l?(m.range_end=[l],qs(i,l)||(m.range_middle=[{after:i,before:l}])):m.range_end=[i]):l&&(m.range_start=[l],m.range_end=[l]),o&&(i&&!l&&m.disabled.push({after:Il(i,o-1),before:la(i,o-1)}),i&&l&&m.disabled.push({after:i,before:la(i,o-1)}),!i&&l&&m.disabled.push({after:Il(l,o-1),before:la(l,o-1)})),d){if(i&&!l&&(m.disabled.push({before:la(i,-d+1)}),m.disabled.push({after:la(i,d-1)})),i&&l){var p=Ya(l,i)+1,x=d-p;m.disabled.push({before:Il(i,x)}),m.disabled.push({after:la(l,x)})}!i&&l&&(m.disabled.push({before:la(l,-d+1)}),m.disabled.push({after:la(l,d-1)}))}return e.jsx(Rc.Provider,{value:{selected:r,onDayClick:c,modifiers:m},children:a})}function Dc(){var t=u.useContext(Rc);if(!t)throw new Error("useSelectRange must be used within a SelectRangeProvider");return t}function Di(t){return Array.isArray(t)?Mp([],t):t!==void 0?[t]:[]}function UN(t){var s={};return Object.entries(t).forEach(function(a){var r=a[0],n=a[1];s[r]=Di(n)}),s}var Ra;(function(t){t.Outside="outside",t.Disabled="disabled",t.Selected="selected",t.Hidden="hidden",t.Today="today",t.RangeStart="range_start",t.RangeEnd="range_end",t.RangeMiddle="range_middle"})(Ra||(Ra={}));var BN=Ra.Selected,Wa=Ra.Disabled,VN=Ra.Hidden,zN=Ra.Today,zl=Ra.RangeEnd,Hl=Ra.RangeMiddle,Gl=Ra.RangeStart,HN=Ra.Outside;function GN(t,s,a){var r,n=(r={},r[BN]=Di(t.selected),r[Wa]=Di(t.disabled),r[VN]=Di(t.hidden),r[zN]=[t.today],r[zl]=[],r[Hl]=[],r[Gl]=[],r[HN]=[],r);return t.fromDate&&n[Wa].push({before:t.fromDate}),t.toDate&&n[Wa].push({after:t.toDate}),li(t)?n[Wa]=n[Wa].concat(s.modifiers[Wa]):oi(t)&&(n[Wa]=n[Wa].concat(a.modifiers[Wa]),n[Gl]=a.modifiers[Gl],n[Hl]=a.modifiers[Hl],n[zl]=a.modifiers[zl]),n}var Bp=u.createContext(void 0);function WN(t){var s=Yt(),a=kc(),r=Dc(),n=GN(s,a,r),i=UN(s.modifiers),l=ot(ot({},n),i);return e.jsx(Bp.Provider,{value:l,children:t.children})}function Vp(){var t=u.useContext(Bp);if(!t)throw new Error("useModifiers must be used within a ModifiersProvider");return t}function YN(t){return!!(t&&typeof t=="object"&&"before"in t&&"after"in t)}function KN(t){return!!(t&&typeof t=="object"&&"from"in t)}function QN(t){return!!(t&&typeof t=="object"&&"after"in t)}function XN(t){return!!(t&&typeof t=="object"&&"before"in t)}function JN(t){return!!(t&&typeof t=="object"&&"dayOfWeek"in t)}function ZN(t,s){var a,r=s.from,n=s.to;if(r&&n){var i=Ya(n,r)<0;i&&(a=[n,r],r=a[0],n=a[1]);var l=Ya(t,r)>=0&&Ya(n,t)>=0;return l}return n?qs(n,t):r?qs(r,t):!1}function qN(t){return Oh(t)}function ew(t){return Array.isArray(t)&&t.every(Oh)}function tw(t,s){return s.some(function(a){if(typeof a=="boolean")return a;if(qN(a))return qs(t,a);if(ew(a))return a.includes(t);if(KN(a))return ZN(t,a);if(JN(a))return a.dayOfWeek.includes(t.getDay());if(YN(a)){var r=Ya(a.before,t),n=Ya(a.after,t),i=r>0,l=n<0,o=Ro(a.before,a.after);return o?l&&i:i||l}return QN(a)?Ya(t,a.after)>0:XN(a)?Ya(a.before,t)>0:typeof a=="function"?a(t):!1})}function Fc(t,s,a){var r=Object.keys(s).reduce(function(i,l){var o=s[l];return tw(t,o)&&i.push(l),i},[]),n={};return r.forEach(function(i){return n[i]=!0}),a&&!cc(t,a)&&(n.outside=!0),n}function sw(t,s){for(var a=da(t[0]),r=oc(t[t.length-1]),n,i,l=a;l<=r;){var o=Fc(l,s),d=!o.disabled&&!o.hidden;if(!d){l=la(l,1);continue}if(o.selected)return l;o.today&&!i&&(i=l),n||(n=l),l=la(l,1)}return i||n}var aw=365;function zp(t,s){var a=s.moveBy,r=s.direction,n=s.context,i=s.modifiers,l=s.retry,o=l===void 0?{count:0,lastFocused:t}:l,d=n.weekStartsOn,c=n.fromDate,m=n.toDate,p=n.locale,x={day:la,week:ko,month:La,year:Gb,startOfWeek:function(f){return n.ISOWeek?dc(f):mc(f,{locale:p,weekStartsOn:d})},endOfWeek:function(f){return n.ISOWeek?Mh(f):Lh(f,{locale:p,weekStartsOn:d})}},h=x[a](t,r==="after"?1:-1);r==="before"&&c?h=Wb([c,h]):r==="after"&&m&&(h=Yb([m,h]));var g=!0;if(i){var b=Fc(h,i);g=!b.disabled&&!b.hidden}return g?h:o.count>aw?o.lastFocused:zp(h,{moveBy:a,direction:r,context:n,modifiers:i,retry:ot(ot({},o),{count:o.count+1})})}var Hp=u.createContext(void 0);function rw(t){var s=ci(),a=Vp(),r=u.useState(),n=r[0],i=r[1],l=u.useState(),o=l[0],d=l[1],c=sw(s.displayMonths,a),m=n??(o&&s.isDateDisplayed(o))?o:c,p=function(){d(n),i(void 0)},x=function(f){i(f)},h=Yt(),g=function(f,j){if(n){var y=zp(n,{moveBy:f,direction:j,context:h,modifiers:a});qs(n,y)||(s.goToDate(y,n),x(y))}},b={focusedDay:n,focusTarget:m,blur:p,focus:x,focusDayAfter:function(){return g("day","after")},focusDayBefore:function(){return g("day","before")},focusWeekAfter:function(){return g("week","after")},focusWeekBefore:function(){return g("week","before")},focusMonthBefore:function(){return g("month","before")},focusMonthAfter:function(){return g("month","after")},focusYearBefore:function(){return g("year","before")},focusYearAfter:function(){return g("year","after")},focusStartOfWeek:function(){return g("startOfWeek","before")},focusEndOfWeek:function(){return g("endOfWeek","after")}};return e.jsx(Hp.Provider,{value:b,children:t.children})}function Ic(){var t=u.useContext(Hp);if(!t)throw new Error("useFocusContext must be used within a FocusProvider");return t}function nw(t,s){var a=Vp(),r=Fc(t,a,s);return r}var Pc=u.createContext(void 0);function iw(t){if(!Nl(t.initialProps)){var s={selected:void 0};return e.jsx(Pc.Provider,{value:s,children:t.children})}return e.jsx(lw,{initialProps:t.initialProps,children:t.children})}function lw(t){var s=t.initialProps,a=t.children,r=function(i,l,o){var d,c,m;if((d=s.onDayClick)===null||d===void 0||d.call(s,i,l,o),l.selected&&!s.required){(c=s.onSelect)===null||c===void 0||c.call(s,void 0,i,l,o);return}(m=s.onSelect)===null||m===void 0||m.call(s,i,i,l,o)},n={selected:s.selected,onDayClick:r};return e.jsx(Pc.Provider,{value:n,children:a})}function Gp(){var t=u.useContext(Pc);if(!t)throw new Error("useSelectSingle must be used within a SelectSingleProvider");return t}function ow(t,s){var a=Yt(),r=Gp(),n=kc(),i=Dc(),l=Ic(),o=l.focusDayAfter,d=l.focusDayBefore,c=l.focusWeekAfter,m=l.focusWeekBefore,p=l.blur,x=l.focus,h=l.focusMonthBefore,g=l.focusMonthAfter,b=l.focusYearBefore,f=l.focusYearAfter,j=l.focusStartOfWeek,y=l.focusEndOfWeek,v=function(T){var k,z,O,$;Nl(a)?(k=r.onDayClick)===null||k===void 0||k.call(r,t,s,T):li(a)?(z=n.onDayClick)===null||z===void 0||z.call(n,t,s,T):oi(a)?(O=i.onDayClick)===null||O===void 0||O.call(i,t,s,T):($=a.onDayClick)===null||$===void 0||$.call(a,t,s,T)},w=function(T){var k;x(t),(k=a.onDayFocus)===null||k===void 0||k.call(a,t,s,T)},S=function(T){var k;p(),(k=a.onDayBlur)===null||k===void 0||k.call(a,t,s,T)},C=function(T){var k;(k=a.onDayMouseEnter)===null||k===void 0||k.call(a,t,s,T)},P=function(T){var k;(k=a.onDayMouseLeave)===null||k===void 0||k.call(a,t,s,T)},F=function(T){var k;(k=a.onDayPointerEnter)===null||k===void 0||k.call(a,t,s,T)},A=function(T){var k;(k=a.onDayPointerLeave)===null||k===void 0||k.call(a,t,s,T)},N=function(T){var k;(k=a.onDayTouchCancel)===null||k===void 0||k.call(a,t,s,T)},_=function(T){var k;(k=a.onDayTouchEnd)===null||k===void 0||k.call(a,t,s,T)},R=function(T){var k;(k=a.onDayTouchMove)===null||k===void 0||k.call(a,t,s,T)},B=function(T){var k;(k=a.onDayTouchStart)===null||k===void 0||k.call(a,t,s,T)},D=function(T){var k;(k=a.onDayKeyUp)===null||k===void 0||k.call(a,t,s,T)},L=function(T){var k;switch(T.key){case"ArrowLeft":T.preventDefault(),T.stopPropagation(),a.dir==="rtl"?o():d();break;case"ArrowRight":T.preventDefault(),T.stopPropagation(),a.dir==="rtl"?d():o();break;case"ArrowDown":T.preventDefault(),T.stopPropagation(),c();break;case"ArrowUp":T.preventDefault(),T.stopPropagation(),m();break;case"PageUp":T.preventDefault(),T.stopPropagation(),T.shiftKey?b():h();break;case"PageDown":T.preventDefault(),T.stopPropagation(),T.shiftKey?f():g();break;case"Home":T.preventDefault(),T.stopPropagation(),j();break;case"End":T.preventDefault(),T.stopPropagation(),y();break}(k=a.onDayKeyDown)===null||k===void 0||k.call(a,t,s,T)},I={onClick:v,onFocus:w,onBlur:S,onKeyDown:L,onKeyUp:D,onMouseEnter:C,onMouseLeave:P,onPointerEnter:F,onPointerLeave:A,onTouchCancel:N,onTouchEnd:_,onTouchMove:R,onTouchStart:B};return I}function cw(){var t=Yt(),s=Gp(),a=kc(),r=Dc(),n=Nl(t)?s.selected:li(t)?a.selected:oi(t)?r.selected:void 0;return n}function dw(t){return Object.values(Ra).includes(t)}function mw(t,s){var a=[t.classNames.day];return Object.keys(s).forEach(function(r){var n=t.modifiersClassNames[r];if(n)a.push(n);else if(dw(r)){var i=t.classNames["day_".concat(r)];i&&a.push(i)}}),a}function uw(t,s){var a=ot({},t.styles.day);return Object.keys(s).forEach(function(r){var n;a=ot(ot({},a),(n=t.modifiersStyles)===null||n===void 0?void 0:n[r])}),a}function xw(t,s,a){var r,n,i,l=Yt(),o=Ic(),d=nw(t,s),c=ow(t,d),m=cw(),p=!!(l.onDayClick||l.mode!=="default");u.useEffect(function(){var C;d.outside||o.focusedDay&&p&&qs(o.focusedDay,t)&&((C=a.current)===null||C===void 0||C.focus())},[o.focusedDay,t,a,p,d.outside]);var x=mw(l,d).join(" "),h=uw(l,d),g=!!(d.outside&&!l.showOutsideDays||d.hidden),b=(i=(n=l.components)===null||n===void 0?void 0:n.DayContent)!==null&&i!==void 0?i:EN,f=e.jsx(b,{date:t,displayMonth:s,activeModifiers:d}),j={style:h,className:x,children:f,role:"gridcell"},y=o.focusTarget&&qs(o.focusTarget,t)&&!d.outside,v=o.focusedDay&&qs(o.focusedDay,t),w=ot(ot(ot({},j),(r={disabled:d.disabled,role:"gridcell"},r["aria-selected"]=d.selected,r.tabIndex=v||y?0:-1,r)),c),S={isButton:p,isHidden:g,activeModifiers:d,selectedDays:m,buttonProps:w,divProps:j};return S}function hw(t){var s=u.useRef(null),a=xw(t.date,t.displayMonth,s);return a.isHidden?e.jsx("div",{role:"gridcell"}):a.isButton?e.jsx(Ki,ot({name:"day",ref:s},a.buttonProps)):e.jsx("div",ot({},a.divProps))}function pw(t){var s=t.number,a=t.dates,r=Yt(),n=r.onWeekNumberClick,i=r.styles,l=r.classNames,o=r.locale,d=r.labels.labelWeekNumber,c=r.formatters.formatWeekNumber,m=c(Number(s),{locale:o});if(!n)return e.jsx("span",{className:l.weeknumber,style:i.weeknumber,children:m});var p=d(Number(s),{locale:o}),x=function(h){n(s,a,h)};return e.jsx(Ki,{name:"week-number","aria-label":p,className:l.weeknumber,style:i.weeknumber,onClick:x,children:m})}function gw(t){var s,a,r=Yt(),n=r.styles,i=r.classNames,l=r.showWeekNumber,o=r.components,d=(s=o==null?void 0:o.Day)!==null&&s!==void 0?s:hw,c=(a=o==null?void 0:o.WeekNumber)!==null&&a!==void 0?a:pw,m;return l&&(m=e.jsx("td",{className:i.cell,style:n.cell,children:e.jsx(c,{number:t.weekNumber,dates:t.dates})})),e.jsxs("tr",{className:i.row,style:n.row,children:[m,t.dates.map(function(p){return e.jsx("td",{className:i.cell,style:n.cell,role:"presentation",children:e.jsx(d,{displayMonth:t.displayMonth,date:p})},Hb(p))})]})}function Au(t,s,a){for(var r=a!=null&&a.ISOWeek?Mh(s):Lh(s,a),n=a!=null&&a.ISOWeek?dc(t):mc(t,a),i=Ya(r,n),l=[],o=0;o<=i;o++)l.push(la(n,o));var d=l.reduce(function(c,m){var p=a!=null&&a.ISOWeek?Xb(m):Jb(m,a),x=c.find(function(h){return h.weekNumber===p});return x?(x.dates.push(m),c):(c.push({weekNumber:p,dates:[m]}),c)},[]);return d}function fw(t,s){var a=Au(da(t),oc(t),s);if(s!=null&&s.useFixedWeeks){var r=zb(t,s);if(r<6){var n=a[a.length-1],i=n.dates[n.dates.length-1],l=ko(i,6-r),o=Au(ko(i,1),l,s);a.push.apply(a,o)}}return a}function bw(t){var s,a,r,n=Yt(),i=n.locale,l=n.classNames,o=n.styles,d=n.hideHead,c=n.fixedWeeks,m=n.components,p=n.weekStartsOn,x=n.firstWeekContainsDate,h=n.ISOWeek,g=fw(t.displayMonth,{useFixedWeeks:!!c,ISOWeek:h,locale:i,weekStartsOn:p,firstWeekContainsDate:x}),b=(s=m==null?void 0:m.Head)!==null&&s!==void 0?s:PN,f=(a=m==null?void 0:m.Row)!==null&&a!==void 0?a:gw,j=(r=m==null?void 0:m.Footer)!==null&&r!==void 0?r:DN;return e.jsxs("table",{id:t.id,className:l.table,style:o.table,role:"grid","aria-labelledby":t["aria-labelledby"],children:[!d&&e.jsx(b,{}),e.jsx("tbody",{className:l.tbody,style:o.tbody,children:g.map(function(y){return e.jsx(f,{displayMonth:t.displayMonth,dates:y.dates,weekNumber:y.weekNumber},y.weekNumber)})}),e.jsx(j,{displayMonth:t.displayMonth})]})}function jw(){return!!(typeof window<"u"&&window.document&&window.document.createElement)}var yw=jw()?u.useLayoutEffect:u.useEffect,Wl=!1,vw=0;function Tu(){return"react-day-picker-".concat(++vw)}function Nw(t){var s,a=t??(Wl?Tu():null),r=u.useState(a),n=r[0],i=r[1];return yw(function(){n===null&&i(Tu())},[]),u.useEffect(function(){Wl===!1&&(Wl=!0)},[]),(s=t??n)!==null&&s!==void 0?s:void 0}function ww(t){var s,a,r=Yt(),n=r.dir,i=r.classNames,l=r.styles,o=r.components,d=ci().displayMonths,c=Nw(r.id?"".concat(r.id,"-").concat(t.displayIndex):void 0),m=r.id?"".concat(r.id,"-grid-").concat(t.displayIndex):void 0,p=[i.month],x=l.month,h=t.displayIndex===0,g=t.displayIndex===d.length-1,b=!h&&!g;n==="rtl"&&(s=[h,g],g=s[0],h=s[1]),h&&(p.push(i.caption_start),x=ot(ot({},x),l.caption_start)),g&&(p.push(i.caption_end),x=ot(ot({},x),l.caption_end)),b&&(p.push(i.caption_between),x=ot(ot({},x),l.caption_between));var f=(a=o==null?void 0:o.Caption)!==null&&a!==void 0?a:RN;return e.jsxs("div",{className:p.join(" "),style:x,children:[e.jsx(f,{id:c,displayMonth:t.displayMonth,displayIndex:t.displayIndex}),e.jsx(bw,{id:m,"aria-labelledby":c,displayMonth:t.displayMonth})]},t.displayIndex)}function Cw(t){var s=Yt(),a=s.classNames,r=s.styles;return e.jsx("div",{className:a.months,style:r.months,children:t.children})}function Sw(t){var s,a,r=t.initialProps,n=Yt(),i=Ic(),l=ci(),o=u.useState(!1),d=o[0],c=o[1];u.useEffect(function(){n.initialFocus&&i.focusTarget&&(d||(i.focus(i.focusTarget),c(!0)))},[n.initialFocus,d,i.focus,i.focusTarget,i]);var m=[n.classNames.root,n.className];n.numberOfMonths>1&&m.push(n.classNames.multiple_months),n.showWeekNumber&&m.push(n.classNames.with_weeknumber);var p=ot(ot({},n.styles.root),n.style),x=Object.keys(r).filter(function(g){return g.startsWith("data-")}).reduce(function(g,b){var f;return ot(ot({},g),(f={},f[b]=r[b],f))},{}),h=(a=(s=r.components)===null||s===void 0?void 0:s.Months)!==null&&a!==void 0?a:Cw;return e.jsx("div",ot({className:m.join(" "),style:p,dir:n.dir,id:n.id,nonce:r.nonce,title:r.title,lang:r.lang},x,{children:e.jsx(h,{children:l.displayMonths.map(function(g,b){return e.jsx(ww,{displayIndex:b,displayMonth:g},b)})})}))}function Aw(t){var s=t.children,a=Xv(t,["children"]);return e.jsx(pN,{initialProps:a,children:e.jsx(SN,{children:e.jsx(iw,{initialProps:a,children:e.jsx(_N,{initialProps:a,children:e.jsx(ON,{initialProps:a,children:e.jsx(WN,{children:e.jsx(rw,{children:s})})})})})})})}function Tw(t){return e.jsx(Aw,ot({},t,{children:e.jsx(Sw,{initialProps:t})}))}function Fi({className:t,classNames:s,showOutsideDays:a=!0,...r}){return e.jsx(Tw,{showOutsideDays:a,className:Me("p-3",t),classNames:{months:"flex flex-col sm:flex-row gap-2",month:"flex flex-col gap-4",caption:"flex justify-center pt-1 relative items-center w-full",caption_label:"text-sm font-medium",nav:"flex items-center gap-1",nav_button:Me(Xn({variant:"outline"}),"size-7 bg-transparent p-0 opacity-50 hover:opacity-100"),nav_button_previous:"absolute left-1",nav_button_next:"absolute right-1",table:"w-full border-collapse space-x-1",head_row:"flex",head_cell:"text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",row:"flex w-full mt-2",cell:Me("relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-range-end)]:rounded-r-md",r.mode==="range"?"[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md":"[&:has([aria-selected])]:rounded-md"),day:Me(Xn({variant:"ghost"}),"size-8 p-0 font-normal aria-selected:opacity-100"),day_range_start:"day-range-start aria-selected:bg-primary aria-selected:text-primary-foreground",day_range_end:"day-range-end aria-selected:bg-primary aria-selected:text-primary-foreground",day_selected:"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",day_today:"bg-accent text-accent-foreground",day_outside:"day-outside text-muted-foreground aria-selected:text-muted-foreground",day_disabled:"text-muted-foreground opacity-50",day_range_middle:"aria-selected:bg-accent aria-selected:text-accent-foreground",day_hidden:"invisible",...s},components:{IconLeft:({className:n,...i})=>e.jsx(il,{className:Me("size-4",n),...i}),IconRight:({className:n,...i})=>e.jsx(Br,{className:Me("size-4",n),...i})},...r})}function ea({value:t,onChange:s,minDate:a,maxDate:r,placeholderText:n="dd/mm/yyyy",disabled:i=!1,className:l,wrapperClassName:o,error:d=!1,displayFormat:c="dd/MM/yyyy",id:m}){const[p,x]=u.useState(!1),h=u.useMemo(()=>{if(t){if(t instanceof Date)return Xr(t)?t:void 0;if(typeof t=="string")try{const j=El(t,"yyyy-MM-dd",new Date);return Xr(j)?j:void 0}catch{return}}},[t]),g=u.useMemo(()=>{if(a){if(a instanceof Date)return Xr(a)?a:void 0;if(typeof a=="string"){const j=El(a,"yyyy-MM-dd",new Date);return Xr(j)?j:void 0}}},[a]),b=u.useMemo(()=>{if(r){if(r instanceof Date)return Xr(r)?r:void 0;if(typeof r=="string"){const j=El(r,"yyyy-MM-dd",new Date);return Xr(j)?j:void 0}}},[r]),f=j=>{if(x(!1),!!s){if(!j){s(null);return}s(et(j,"yyyy-MM-dd"))}};return e.jsx("div",{className:Me("relative",o),children:e.jsxs(sa,{open:p,onOpenChange:x,children:[e.jsx(aa,{asChild:!0,children:e.jsxs(E,{id:m,disabled:i,variant:"outline",className:Me("w-full justify-start text-left font-normal",!h&&"text-muted-foreground",d&&"border-destructive ring-destructive/20",l),children:[e.jsx(gt,{className:"mr-2 h-4 w-4"}),h?et(h,c):e.jsx("span",{children:n})]})}),e.jsx(ra,{className:"w-auto p-0",align:"start",children:e.jsx(Fi,{mode:"single",selected:h,onSelect:f,disabled:j=>!!(g&&jb),initialFocus:!0})})]})})}function kw({isAdmin:t,dateRange:s,customStartDate:a,customEndDate:r,showCustomDatePicker:n,refreshing:i,onDateRangeChange:l,onCustomStartDateChange:o,onCustomEndDateChange:d,onShowCustomDatePickerChange:c,onApplyCustomDate:m,onResetCustomDates:p,onRefresh:x}){return e.jsx(Z,{className:"shadow-md","data-testid":"dashboard-filters-bar",children:e.jsx(se,{className:"p-4",children:e.jsxs("div",{className:"flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx($a,{className:"w-5 h-5 text-muted-foreground"}),e.jsx("h3",{className:"font-semibold text-gray-900",children:"Filters"}),t&&e.jsx(re,{variant:"outline",className:"bg-purple-50 text-purple-700 border-purple-200","data-testid":"management-badge",children:"Management View"})]}),e.jsxs("div",{className:"flex flex-wrap items-center gap-3 w-full sm:w-auto",children:[t?e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(gt,{className:"w-4 h-4 text-muted-foreground"}),e.jsxs(He,{value:s,onValueChange:l,"data-testid":"date-range-select",children:[e.jsx(We,{className:"w-[140px]",children:e.jsx(Ge,{placeholder:"Select period"})}),e.jsxs(Ye,{children:[e.jsx(J,{value:"all",children:"All Time"}),e.jsx(J,{value:"today",children:"Today"}),e.jsx(J,{value:"week",children:"This Week"}),e.jsx(J,{value:"month",children:"This Month"}),e.jsx(J,{value:"last7days",children:"Last 7 Days"}),e.jsx(J,{value:"last30days",children:"Last 30 Days"}),e.jsx(J,{value:"custom",children:"Custom Range"})]})]}),s==="custom"&&e.jsxs(sa,{open:n,onOpenChange:c,children:[e.jsx(aa,{asChild:!0,children:e.jsxs(E,{variant:"outline",size:"sm",className:"gap-2","data-testid":"custom-date-trigger",children:[e.jsx(gt,{className:"w-4 h-4"}),a&&r?`${et(a,"MMM d, yyyy")} - ${et(r,"MMM d, yyyy")}`:"Select dates"]})}),e.jsx(ra,{className:"w-auto p-4",align:"start",sideOffset:8,"data-testid":"custom-date-picker",children:e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(ae,{htmlFor:"start-date",className:"text-sm font-medium",children:"Start Date"}),e.jsx(ea,{value:a||null,onChange:h=>{const g=h?new Date(h):void 0;g?(o(g),r&&g>r&&d(g)):o(void 0)},maxDate:new Date,placeholderText:"dd/mm/yyyy",className:"w-full","data-testid":"start-date-input"})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(ae,{htmlFor:"end-date",className:"text-sm font-medium",children:"End Date"}),e.jsx(ea,{value:r||null,onChange:h=>{const g=h?new Date(h):void 0;g?(d(g),a&&gl(h),"data-testid":"date-range-select-user",children:[e.jsx(We,{className:"w-[140px]",children:e.jsx(Ge,{placeholder:"Select period"})}),e.jsxs(Ye,{children:[e.jsx(J,{value:"all",children:"All Time"}),e.jsx(J,{value:"today",children:"Today"}),e.jsx(J,{value:"week",children:"This Week"}),e.jsx(J,{value:"month",children:"This Month"}),e.jsx(J,{value:"last7days",children:"Last 7 Days"}),e.jsx(J,{value:"last30days",children:"Last 30 Days"}),e.jsx(J,{value:"custom",children:"Custom Range"})]})]}),s==="custom"&&e.jsxs(sa,{open:n,onOpenChange:c,children:[e.jsx(aa,{asChild:!0,children:e.jsxs(E,{variant:"outline",size:"sm",className:"gap-2","data-testid":"custom-date-trigger",children:[e.jsx(gt,{className:"w-4 h-4"}),a&&r?`${et(a,"MMM d, yyyy")} - ${et(r,"MMM d, yyyy")}`:"Select dates"]})}),e.jsx(ra,{className:"w-auto p-4",align:"start",sideOffset:8,"data-testid":"custom-date-picker",children:e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(ae,{htmlFor:"start-date-user",className:"text-sm font-medium",children:"Start Date"}),e.jsx(ea,{value:a||null,onChange:h=>{const g=h?new Date(h):void 0;g?(o(g),r&&g>r&&d(g)):o(void 0)},maxDate:new Date,placeholderText:"dd/mm/yyyy",className:"w-full","data-testid":"start-date-input-user"})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(ae,{htmlFor:"end-date-user",className:"text-sm font-medium",children:"End Date"}),e.jsx(ea,{value:r||null,onChange:h=>{const g=h?new Date(h):void 0;g?(d(g),a&&g