From c2c935fd858b654dbb17d766875d10435d757441 Mon Sep 17 00:00:00 2001 From: enlineawork Date: Tue, 8 Sep 2026 06:28:08 -0300 Subject: [PATCH] noop --- web-v2/src/lib/api.ts | 3132 +---------------------------------------- 1 file changed, 1 insertion(+), 3131 deletions(-) diff --git a/web-v2/src/lib/api.ts b/web-v2/src/lib/api.ts index d4885d2..b3a4252 100644 --- a/web-v2/src/lib/api.ts +++ b/web-v2/src/lib/api.ts @@ -1,3131 +1 @@ -const API_BASE = '/api/v3'; - -export class ApiError extends Error { - constructor( - public readonly status: number, - public readonly code: string, - message: string, - public readonly requestId?: string, - ) { - super(message); - this.name = 'ApiError'; - } -} - -export interface AuthUser { - id: string; - username: string; - firstName: string; - lastName: string; - email: string | null; - mustChangePassword: boolean; - roles: string[]; - permissions: string[]; -} - -export interface AuthResponse { - user: AuthUser; - csrfToken: string; - accessExpiresInSeconds: number; -} - -export interface HealthResponse { - status: string; - service: string; - api: string; - version: string; - phase: string; - database: string; - time: string; - latencyMs: number; -} - -export interface DashboardSummary { - counts: { - totalAssets: number; - assetsNeedValidation: number; - assetsWithoutGeometry: number; - plannedInspections: number; - activeUsers: number; - inactiveUsers: number; - activeSessions: number; - openFindings: number; - awaitingCompanyResponse: number; - overdueCompanyResponses: number; - companyResponsesDueNext7Days: number; - awaitingVerificationSchedule: number; - overdueControls: number; - controlsNext30Days: number; - }; - recentAudit: Array<{ - id: string; - occurredAt: string; - actorUsername: string | null; - action: string; - entityType: string | null; - entityId: string | null; - }>; - upcomingControls: Array<{ - id: string; - code: string; - title: string; - nextControlOn: string; - actId: string; - actCode: string; - visitId: string; - visitCode: string; - assetId: string; - assetCode: string; - assetName: string; - }>; - generatedAt: string; -} - -export interface RoleSummary { id: string; code: string; name: string } - -export interface AdministrativeUser { - id: string; - username: string; - email: string | null; - firstName: string; - lastName: string; - status: 'ACTIVE' | 'INACTIVE'; - mustChangePassword: boolean; - failedLoginAttempts: number; - lockedUntil: string | null; - lastLoginAt: string | null; - passwordChangedAt: string | null; - createdAt: string; - updatedAt: string; - roles: RoleSummary[]; -} - -export interface Permission { id: string; code: string; description: string } - -export interface AdministrativeRole { - id: string; - code: string; - name: string; - description: string; - isSystem: boolean; - createdAt: string; - updatedAt: string; - userCount: number; - permissions: Permission[]; -} - -export interface AuditEventSummary { - id: string; - occurredAt: string; - actorUserId: string | null; - actorUsername: string | null; - action: string; - entityType: string | null; - entityId: string | null; - requestId: string | null; - source: 'WEB' | 'ANDROID' | 'SYSTEM' | 'IMPORT'; - ip: string | null; - hasDetails: boolean; -} - -export interface AuditEventDetail extends AuditEventSummary { - userAgent: string | null; - beforeData: Record | null; - afterData: Record | null; - metadata: Record | null; -} - -export interface PageMeta { page: number; pageSize: number; total: number; totalPages: number } -interface Page { data: T[]; meta: PageMeta } - -export type AssetInformationStatus = - | 'DRAFT' | 'PENDING_SURVEY' | 'SURVEYED' | 'VALIDATED' - | 'OBSERVED' | 'OUTDATED' | 'INACTIVE'; - -export type AssetOperationalStatus = - | 'UNKNOWN' | 'IN_SERVICE' | 'TEMPORARILY_OUT_OF_SERVICE' - | 'OUT_OF_SERVICE' | 'DECOMMISSIONED' | 'ABANDONED'; - -export type AssetDataOrigin = - | 'MANUAL' | 'FIELD_SURVEY' | 'PROVIDED_DOCUMENT' | 'IMPORT' | 'SYSTEM'; - -export type AssetTypeOperationalRole = 'GENERIC' | 'AREA' | 'COMPANY'; - -export interface AssetProvenance { - assetId: string; - origin: AssetDataOrigin; - sourceName: string | null; - sourceReference: string | null; - observedAt: string | null; - notes: string | null; - verifiedAt: string | null; - verifiedBy: string | null; - verifiedByUsername: string | null; - updatedAt: string; - updatedBy: string | null; - updatedByUsername: string | null; -} - -export type AssetAttributeDataType = - | 'TEXT' | 'NUMBER' | 'BOOLEAN' | 'DATE' | 'DATETIME' | 'SELECT'; - -export interface AssetAttributeDefinition { - id: string; - code: string; - name: string; - dataType: AssetAttributeDataType; - isRequired: boolean; - isActive: boolean; - unit: string | null; - options: string[] | null; - sortOrder: number; -} - -export interface AssetTypeSummary { - id: string; - code: string; - name: string; - isActive: boolean; - operationalRole: AssetTypeOperationalRole; -} - -export interface AssetType extends AssetTypeSummary { - description: string; - canBeRoot: boolean; - createdAt: string; - updatedAt: string; - assetCount: number; - allowedParentTypes: AssetTypeSummary[]; - attributes: AssetAttributeDefinition[]; -} - -export interface MasterBootstrapStatus { - presetCode: string; - presetName: string; - typeCount: number; - canApply: boolean; - reason: string | null; - types: Array<{ - code: string; - name: string; - operationalRole: AssetTypeOperationalRole; - parentCodes: string[]; - attributeCount: number; - }>; -} - -export interface MasterBootstrapResult { - presetCode: string; - presetName: string; - createdTypeCount: number; - createdAttributeCount: number; - createdParentRuleCount: number; - data: AssetType[]; -} - -export interface MasterEnrichmentStatus { - presetCode: string; - presetName: string; - typeCount: number; - canApply: boolean; - complete: boolean; - reason: string | null; - missingTypeCodes: string[]; - missingAttributeCount: number; - missingParentRuleCount: number; -} - -export interface AssetListItem { - id: string; - code: string; - name: string; - commonName: string | null; - type: { id: string; code: string; name: string }; - parent: { id: string; code: string; name: string } | null; - operationalArea: { id: string; code: string; name: string } | null; - operatorCompany: { id: string; code: string; name: string } | null; - informationStatus: AssetInformationStatus; - operationalStatus: AssetOperationalStatus; - childrenCount: number; - hasGeometry: boolean; - geometryType: AssetGeometryType | null; - mediaCount: number; - dataOrigin: AssetDataOrigin; - provenanceVerified: boolean; - currentVersion: number; - updatedAt: string; -} - -export interface AssetLineageItem { - id: string; - code: string; - name: string; - commonName: string | null; - type: { id: string; code: string; name: string }; -} - -export interface AssetDetail extends AssetListItem { - description: string | null; - createdAt: string; - createdBy: string | null; - updatedBy: string | null; - attributes: Array<{ - definitionId: string; - code: string; - name: string; - dataType: AssetAttributeDataType; - isRequired: boolean; - unit: string | null; - options: string[] | null; - sortOrder: number; - value: unknown; - }>; -} - -export type AssetDossierTimelineKind = - | 'INVENTORY_CHANGE' | 'INSPECTION' | 'ACT' | 'FINDING' | 'FINDING_CLOSED' - | 'COMMUNICATION' | 'PHOTO' | 'DOCUMENT' | 'REPORT' | 'SOURCE_DOCUMENT' | 'VERIFICATION'; - -export interface AssetDossierTimelineEvent { - id: string; - kind: AssetDossierTimelineKind; - occurredAt: string; - title: string; - description: string | null; - href?: string; - meta?: Record; -} - -export interface AssetDossier { - asset: { id: string; code: string; name: string; commonName: string | null }; - counters: { - inspections: number; acts: number; findings: number; openFindings: number; closedFindings: number; - evidence: number; documents: number; photos: number; reports: number; verifications: number; - }; - visits: Array<{ id: string; code: string; title: string; status: InspectionVisitStatus; plannedStartAt: string | null; actualStartedAt: string | null; actualClosedAt: string | null; createdAt: string }>; - acts: Array<{ id: string; visitId: string; code: string; status: InspectionActStatus; occurredAt: string; title: string; summary: string; closedAt: string | null; currentVersion: number; visitCode: string; visitTitle: string }>; - findings: Array<{ id: string; actId: string; code: string; status: InspectionFindingStatus; title: string; description: string; correctionDueOn: string | null; companyResponseReceivedOn: string | null; nextControlOn: string | null; closedAt: string | null; closureNotes: string | null; createdAt: string; updatedAt: string; actCode: string; actOccurredAt: string; visitId: string; visitCode: string; visitTitle: string }>; - evidence: Array<{ id: string; findingId: string; communicationId: string | null; kind: InspectionEvidenceKind; purpose: InspectionEvidencePurpose; originalName: string; title: string | null; description: string | null; capturedAt: string | null; createdAt: string; findingCode: string; findingTitle: string }>; - communications: Array<{ id: string; findingId: string; direction: InspectionFindingCommunication['direction']; channel: InspectionFindingCommunication['channel']; type: InspectionFindingCommunication['type']; occurredAt: string; subject: string; details: string | null; contactName: string | null; createdAt: string; findingCode: string; findingTitle: string }>; - verificationResults: Array<{ id: string; findingId: string; findingCode: string; findingTitle: string; visitId: string; visitCode: string; visitTitle: string; visitStatus: InspectionVisitStatus; targetControlOn: string | null; outcome: InspectionVerificationOutcome | null; resultNotes: string | null; verifiedAt: string | null; resultRecordedAt: string | null; rescheduledControlOn: string | null; evidenceCount: number }>; - documents: Array<{ id: string; documentType: SourceDocumentType; documentNumber: string | null; title: string; issuer: string | null; documentDate: string | null; externalReference: string | null; relationType: AssetSourceDocumentRelationType; notes: string | null; linkedAt: string }>; - inspectionReports: Array<{ id: string; code: string; status: InspectionReportStatus; pdfStatus: InspectionReportPdfStatus; title: string; generatedAt: string; frozenSha256: string; actId: string; actCode: string; visitId: string; visitCode: string }>; - reports: Array<{ id: string; documentType: SourceDocumentType; documentNumber: string | null; title: string; issuer: string | null; documentDate: string | null; externalReference: string | null; relationType: AssetSourceDocumentRelationType; notes: string | null; linkedAt: string }>; - media: Array<{ id: string; kind: AssetMediaKind; originalName: string; title: string | null; description: string | null; capturedAt: string | null; createdAt: string; source: 'WEB' | 'ANDROID' | 'IMPORT' }>; - versions: Array<{ id: string; versionNumber: number; changeType: AssetVersionChangeType; changedFields: string[]; occurredAt: string; actorUsername: string | null; source: string }>; - timeline: AssetDossierTimelineEvent[]; -} - -export interface AssetContextHistoryItem { - id: string; - assetId: string; - validFrom: string; - validUntil: string | null; - changeReason: string; - endReason: string | null; - assetVersionNumber: number; - source: string; - requestId: string | null; - createdAt: string; - parent: { id: string; code: string; name: string } | null; - operationalArea: { id: string; code: string; name: string } | null; - operatorCompany: { id: string; code: string; name: string } | null; - creator: { id: string; username: string; firstName: string; lastName: string } | null; - isCurrent: boolean; -} - -export type FieldDiscoveryStatus = 'PENDING' | 'APPROVED' | 'MATCHED' | 'REJECTED'; - -export interface FieldDiscovery { - id: string; - status: FieldDiscoveryStatus; - discoveryNotes: string | null; - observedAt: string; - reviewedAt: string | null; - reviewNotes: string | null; - createdAt: string; - asset: { - id: string; code: string; name: string; commonName: string | null; informationStatus: AssetInformationStatus; - typeId: string; typeName: string; parentId: string | null; operationalAreaId: string | null; operatorCompanyId: string | null; - }; - visit: { id: string; code: string; title: string; status: InspectionVisitStatus }; - creator: { id: string; username: string; firstName: string; lastName: string }; - reviewer: { id: string; username: string; firstName: string; lastName: string } | null; - matchedAsset: { id: string; code: string; name: string; commonName: string | null } | null; -} - -export interface OperationalAssetSummary { - id: string; - code: string; - name: string; - commonName: string | null; - typeName: string; -} - -export type AreaOrganizationRole = 'OPERATOR' | 'TECHNICAL_OPERATOR' | 'CONCESSIONAIRE' | 'PERMIT_HOLDER' | 'PARTICIPANT' | 'OTHER'; - -export interface AreaCompanyRelation { - id: string; - area: OperationalAssetSummary; - company: OperationalAssetSummary; - relationRole: AreaOrganizationRole; - participationPercent: number | null; - legalInstrument: string | null; - sourceDocumentId: string | null; - validFrom: string; - validUntil: string | null; - startReason: string; - endReason: string | null; - createdBy: { id: string; username: string } | null; - endedBy: { id: string; username: string } | null; - createdAt: string; - updatedAt: string; - active: boolean; - assignedAssetCount: number; -} - - -export type OrganizationKind = 'COMPANY' | 'UTE' | 'PUBLIC_ENTITY' | 'OTHER'; -export type SourceDocumentType = 'NOTE' | 'TECHNICAL_REPORT' | 'INSPECTION_ACT' | 'INVENTORY' | 'RESOLUTION' | 'DECREE' | 'CONTRACT' | 'SPREADSHEET' | 'OTHER'; -export type AssetSourceDocumentRelationType = 'SOURCE' | 'MENTIONS' | 'VALIDATES' | 'SUPERSEDES' | 'OTHER'; -export type OrganizationMembershipRole = 'MEMBER' | 'LEAD_MEMBER' | 'OTHER'; -export type AreaLegalRightType = 'EXPLOITATION_CONCESSION' | 'EXPLORATION_PERMIT' | 'TRANSPORT_CONCESSION' | 'OTHER'; -export type AreaLegalRightStatus = 'ACTIVE' | 'EXPIRED' | 'REVOKED' | 'PENDING'; -export type AreaLegalRightOrganizationRole = 'HOLDER' | 'PARTICIPANT' | 'OPERATOR' | 'OTHER'; - -export interface SourceDocumentSummary { - id: string; documentType: SourceDocumentType; documentNumber: string | null; title: string; - issuer: string | null; documentDate: string | null; externalReference: string | null; notes: string | null; - createdAt?: string; updatedAt?: string; -} -export interface AssetRegistry { - organizationProfile: null | { assetId: string; organizationKind: OrganizationKind; legalName: string | null; taxId: string | null; notificationEmail: string | null; notes: string | null; createdAt: string; updatedAt: string }; - organizationMemberships: Array<{ id: string; parent: { id: string; code: string; name: string }; member: { id: string; code: string; name: string }; role: OrganizationMembershipRole; participationPercent: number | null; validFrom: string; validUntil: string | null; sourceDocumentId: string | null; notes: string | null; endReason: string | null }>; - externalIdentifiers: Array<{ id: string; namespace: string; value: string; validFrom: string; validUntil: string | null; sourceDocumentId: string | null; notes: string | null; endReason: string | null }>; - sourceDocuments: Array; - legalRights: Array<{ id: string; rightType: AreaLegalRightType; name: string; instrumentNumber: string | null; validFrom: string | null; validUntil: string | null; status: AreaLegalRightStatus; sourceDocumentId: string | null; notes: string | null; organizations: Array<{ id: string; organizationId: string; organizationName: string; role: AreaLegalRightOrganizationRole; participationPercent: number | null; validFrom: string; validUntil: string | null; notes: string | null; endReason: string | null }> }>; -} - - -export type AssetImportBatchStatus = 'ANALYZED' | 'REVIEW_REQUIRED' | 'CANCELLED' | 'FAILED'; -export type AssetImportRowStatus = 'READY' | 'WARNING' | 'CONFLICT' | 'IGNORED'; -export type AssetImportProfileCode = 'MENDOZA_INVENTORY_V1' | 'MENDOZA_YACIMIENTOS_V1' | 'UNKNOWN'; - -export interface AssetImportBatch { - id: string; - originalName: string; - mimeType: string; - sizeBytes: number; - sha256: string; - profileCode: AssetImportProfileCode; - profileConfidence: number; - worksheetName: string | null; - headerRow: number | null; - totalRows: number; - readyRows: number; - warningRows: number; - conflictRows: number; - ignoredRows: number; - status: AssetImportBatchStatus; - sourceDocumentId: string | null; - sourceLabel: string | null; - notes: string | null; - analysis: Record; - uploadedBy: string | null; - uploadedByUsername: string | null; - analyzedAt: string; - createdAt: string; - updatedAt: string; -} - -export interface AssetImportBatchDetail extends AssetImportBatch { - topIssues: Array<{ code: string; count: number }>; - duplicateFiles: Array<{ id: string; originalName: string; createdAt: string; status: AssetImportBatchStatus }>; -} - -export interface AssetImportRow { - id: string; - worksheetName: string; - rowNumber: number; - status: AssetImportRowStatus; - suggestedAction: 'CREATE' | 'MATCH' | 'REVIEW' | 'IGNORE'; - rawData: Record; - normalizedData: Record; - issues: string[]; - fingerprint: string; - matchedAssetId: string | null; - importedAssetId: string | null; - createdAt: string; - updatedAt: string; -} - -export type AssetImportPlanStatus = 'REVIEW_REQUIRED' | 'READY' | 'APPLIED' | 'ROLLED_BACK' | 'SUPERSEDED' | 'FAILED'; -export type AssetImportPlanEntityKind = 'DEPARTMENT' | 'ORGANIZATION' | 'AREA' | 'AREA_DEPARTMENT_RELATION' | 'FIELD' | 'OPERATOR_RELATION' | 'LEGAL_RIGHT' | 'LEGAL_RIGHT_ORGANIZATION' | 'INSTALLATION' | 'LOCAL_STRUCTURE' | 'TECHNICAL_ASSET'; -export type AssetImportPlanAction = 'CREATE' | 'MATCH' | 'REVIEW' | 'IGNORE'; -export type AssetImportPlanItemStatus = 'PLANNED' | 'MATCHED' | 'REVIEW' | 'IGNORED' | 'APPLIED' | 'ROLLED_BACK' | 'FAILED'; - -export interface AssetImportPlanItem { - id: string; - planId: string; - itemOrder: number; - entityKey: string; - entityKind: AssetImportPlanEntityKind; - action: AssetImportPlanAction; - status: AssetImportPlanItemStatus; - assetTypeCode: string | null; - displayName: string; - generatedCode: string | null; - parentEntityKey: string | null; - matchedAssetId: string | null; - appliedAssetId: string | null; - appliedObjectId: string | null; - payload: Record; - sourceRowNumbers: number[]; - reviewCodes: string[]; - resolutionNote: string | null; - resolvedBy: string | null; - resolvedAt: string | null; -} - -export interface AssetImportPlan { - id: string; - batchId: string; - revision: number; - status: AssetImportPlanStatus; - externalIdNamespace: string | null; - operatorAssetId: string | null; - summary: Record; - planHash: string; - generatedBy: string | null; - generatedAt: string; - appliedBy: string | null; - appliedAt: string | null; - applicationSummary: Record | null; - rolledBackBy: string | null; - rolledBackAt: string | null; - rollbackSummary: Record | null; - supersededAt: string | null; - createdAt: string; - updatedAt: string; - masterStateStale: boolean; - masterStateHashAvailable: boolean; - masterStateCheckedAt: string; - previewItems: AssetImportPlanItem[]; - reviewItems: AssetImportPlanItem[]; -} - -export interface AssetImportReviewItem extends AssetImportPlanItem { - batchId: string; - batchName: string; - sourceLabel: string | null; - profileCode: AssetImportProfileCode; - planRevision: number; - planStatus: AssetImportPlanStatus; - dependency: boolean; -} - -export interface AssetImportOrganizationOption { - id: string; - code: string; - name: string; - legalName: string | null; -} - -export type Position = [number, number]; -export type AssetGeometryType = 'POINT' | 'LINESTRING' | 'POLYGON'; - -export type GeoJsonGeometry = - | { type: 'POINT'; coordinates: Position } - | { type: 'LINESTRING'; coordinates: Position[] } - | { type: 'POLYGON'; coordinates: Position[][] }; - -export interface AssetGeometry { - assetId: string; - geometry: GeoJsonGeometry; - geometryType: AssetGeometryType; - source: 'WEB' | 'ANDROID' | 'IMPORT' | 'SURVEY'; - accuracyM: number | null; - capturedAt: string | null; - deviceLabel: string | null; - createdAt: string; - updatedAt: string; - updatedBy: string | null; -} - -export interface MapAssetProperties { - id: string; - code: string; - name: string; - commonName?: string | null; - typeId: string; - typeCode: string; - typeName: string; - parentId: string | null; - parentName: string | null; - informationStatus: AssetInformationStatus; - geometryType: AssetGeometryType; - accuracyM: number | null; - capturedAt: string | null; - updatedAt: string; -} - -export interface MapAssetFeature { - type: 'Feature'; - id: string; - geometry: GeoJsonGeometry; - properties: MapAssetProperties; -} - -export interface MapAssetFeatureCollection { - type: 'FeatureCollection'; - features: MapAssetFeature[]; - meta: { count: number; truncated: boolean }; -} - -export type AssetMediaKind = 'PHOTO' | 'DOCUMENT'; - -export interface AssetMedia { - id: string; - assetId: string; - kind: AssetMediaKind; - originalName: string; - mimeType: 'image/jpeg' | 'image/png' | 'image/webp' | 'application/pdf'; - sizeBytes: number; - sha256: string; - title: string | null; - description: string | null; - capturedAt: string | null; - latitude: number | null; - longitude: number | null; - accuracyM: number | null; - source: 'WEB' | 'ANDROID' | 'IMPORT'; - uploadedBy: string | null; - uploadedByUsername: string | null; - createdAt: string; - updatedAt: string; -} - -export type AssetVersionChangeType = - | 'BASELINE' - | 'CREATED' - | 'UPDATED' - | 'CONTEXT_CHANGED' - | 'STATUS_CHANGED' - | 'OPERATIONAL_STATUS_CHANGED' - | 'REGISTRY_UPDATED' - | 'GEOMETRY_UPDATED' - | 'GEOMETRY_REMOVED' - | 'MEDIA_UPLOADED' - | 'MEDIA_UPDATED' - | 'MEDIA_REMOVED' - | 'PROVENANCE_BASELINE' - | 'PROVENANCE_UPDATED' - | 'PROVENANCE_VERIFIED'; - -export interface AssetVersionSnapshot { - id: string; - code: string; - name: string; - description: string | null; - type: { id: string; code: string; name: string; operationalRole?: AssetTypeOperationalRole }; - parent: { id: string; code: string; name: string } | null; - operationalArea?: { id: string; code: string; name: string } | null; - operatorCompany?: { id: string; code: string; name: string } | null; - informationStatus: AssetInformationStatus; - operationalStatus: AssetOperationalStatus; - organizationProfile?: Record | null; - organizationMemberships?: Array>; - externalIdentifiers?: Array>; - sourceDocuments?: Array>; - legalRights?: Array>; - attributes: Array<{ - definitionId: string; - code: string; - name: string; - dataType: AssetAttributeDataType; - isRequired: boolean; - unit: string | null; - options: string[] | null; - sortOrder: number; - value: unknown; - }>; - geometry: AssetGeometry | null; - media?: AssetMedia[]; - provenance?: { - origin: AssetDataOrigin; - sourceName: string | null; - sourceReference: string | null; - observedAt: string | null; - notes: string | null; - verifiedAt: string | null; - verifiedBy: string | null; - updatedAt: string; - updatedBy: string | null; - }; - createdAt: string; - updatedAt: string; - createdBy: string | null; - updatedBy: string | null; - currentVersion: number; -} - -export interface AssetVersionSummary { - id: string; - assetId: string; - assetCode: string; - assetName: string; - typeId: string; - typeName: string; - informationStatus: AssetInformationStatus; - operationalStatus: AssetOperationalStatus; - versionNumber: number; - changeType: AssetVersionChangeType; - changedFields: string[]; - occurredAt: string; - actorUserId: string | null; - actorUsername: string | null; - source: 'WEB' | 'ANDROID' | 'SYSTEM' | 'IMPORT'; - requestId: string | null; - isCurrent: boolean; -} - -export interface AssetVersionDetail extends AssetVersionSummary { - snapshot: AssetVersionSnapshot; -} - -export interface TemporalAssetSummary extends AssetVersionSummary { - effectiveUntil: string | null; -} - -export interface TemporalAssetDetail extends TemporalAssetSummary { - snapshot: AssetVersionSnapshot; - asOf: string; -} - -export type SurveyCampaignStatus = - | 'DRAFT' | 'PLANNED' | 'IN_PROGRESS' | 'COMPLETED' | 'CANCELLED'; - -export type SurveyTargetStatus = - | 'PENDING' | 'IN_PROGRESS' | 'SUBMITTED' | 'COMPLETED' | 'SKIPPED'; - -export interface SurveyPerson { - id: string; - username: string; - firstName: string; - lastName: string; -} - -export interface SurveyCampaignListItem { - id: string; - code: string; - name: string; - description: string | null; - status: SurveyCampaignStatus; - plannedStartAt: string | null; - plannedEndAt: string | null; - scopeAsset: { id: string; code: string; name: string } | null; - coordinator: SurveyPerson | null; - targetCount: number; - pendingCount: number; - inProgressCount: number; - submittedCount: number; - completedCount: number; - skippedCount: number; - createdAt: string; - updatedAt: string; -} - -export interface SurveyTarget { - id: string; - campaignId: string; - asset: { id: string; code: string; name: string; typeName: string }; - assignedUser: SurveyPerson | null; - status: SurveyTargetStatus; - dueAt: string | null; - instructions: string | null; - createdAt: string; - updatedAt: string; -} - -export interface SurveyCampaign extends SurveyCampaignListItem { - targets: SurveyTarget[]; -} - -export type SurveyReportOutcome = 'CONFIRMED' | 'CHANGES_RECORDED' | 'NOT_LOCATED'; -export type SurveyReportStatus = 'DRAFT' | 'SUBMITTED' | 'APPROVED' | 'REJECTED'; -export type SurveyReportVersionEvent = 'SUBMITTED' | 'APPROVED' | 'REJECTED'; - -export interface SurveyTargetReport { - id: string; - targetId: string; - outcome: SurveyReportOutcome | null; - status: SurveyReportStatus; - observedAt: string | null; - latitude: number | null; - longitude: number | null; - accuracyM: number | null; - notes: string | null; - assetVersionAtSubmission: number | null; - submittedAt: string | null; - submittedBy: SurveyPerson | null; - reviewedAt: string | null; - reviewedBy: SurveyPerson | null; - reviewNotes: string | null; - selectedMediaIds: string[]; - createdAt: string; - updatedAt: string; -} - -export interface SurveyReportMedia extends AssetMedia { - kind: 'PHOTO'; - selected: boolean; -} - -export interface SurveyReportVersion { - id: string; - versionNumber: number; - event: SurveyReportVersionEvent; - snapshot: Record; - actorUserId: string | null; - actorUsername: string | null; - createdAt: string; -} - -export interface SurveyExecution { - target: { - id: string; - status: SurveyTargetStatus; - dueAt: string | null; - instructions: string | null; - assignedUser: SurveyPerson | null; - }; - campaign: { - id: string; - code: string; - name: string; - status: SurveyCampaignStatus; - }; - asset: { - id: string; - code: string; - name: string; - typeName: string; - informationStatus: AssetInformationStatus; - currentVersion: number; - }; - report: SurveyTargetReport | null; - availableMedia: SurveyReportMedia[]; - versions: SurveyReportVersion[]; -} - -export type InspectionVisitStatus = - | 'DRAFT' | 'PLANNED' | 'IN_PROGRESS' | 'CLOSED' | 'CANCELLED'; - -export interface InspectionPerson { - id: string; - username: string; - firstName: string; - lastName: string; -} - -export interface InspectionAssetSummary { - id: string; - code: string; - name: string; - commonName?: string | null; - typeName: string; - operatorCompany?: { id: string; code: string; name: string } | null; - operationalArea?: { id: string; code: string; name: string } | null; -} - -export interface InspectionPlanningContextAsset { - id: string; - code: string; - name: string; -} - -export type InspectionVisitAssetPlanningSource = 'LEGACY' | 'AUTOMATIC' | 'PREVENTIVE' | 'VERIFICATION'; -export type InspectionChecklistItemKind = 'ANTECEDENT' | 'COMPANY_OVERDUE' | 'VERIFICATION_OVERDUE' | 'UPCOMING_CONTROL'; - -export interface InspectionPlannedAsset extends InspectionAssetSummary { - included: boolean; - planningSource: InspectionVisitAssetPlanningSource; - exclusionReason: string | null; - excludedAt: string | null; - excludedBy: InspectionPerson | null; -} - -export interface InspectionVisitChecklistItem { - id: string; - findingId: string; - findingCode: string; - findingTitle: string; - findingStatus: InspectionFindingStatus; - severity: number | null; - itemKind: InspectionChecklistItemKind; - referenceOn: string | null; - asset: InspectionAssetSummary; - assetIncluded: boolean; -} - -export interface InspectionVisitChecklistSummary { - generation: number; - generatedAt: string | null; - stale: boolean; - antecedents: number; - companyOverdue: number; - verificationOverdue: number; - upcomingControls: number; - actionableAssets: number; - excludedAssets: number; - items: InspectionVisitChecklistItem[]; -} - -export interface InspectionVisitListItem { - id: string; - code: string; - title: string; - objective: string | null; - status: InspectionVisitStatus; - scopeAsset: InspectionAssetSummary | null; - operationalArea: InspectionPlanningContextAsset | null; - operatorCompany: InspectionPlanningContextAsset | null; - leadInspector: InspectionPerson | null; - plannedStartAt: string | null; - plannedEndAt: string | null; - actualStartedAt: string | null; - actualClosedAt: string | null; - instructions: string | null; - cancellationReason: string | null; - checklistGeneration: number; - checklistGeneratedAt: string | null; - assetCount: number; - memberCount: number; - createdAt: string; - updatedAt: string; -} - -export type InspectionVerificationOutcome = 'RESOLVED' | 'NOT_RESOLVED' | 'REQUIRES_NEW_DATE'; - -export interface InspectionVerificationFindingSummary { - id: string; - code: string; - title: string; - status: InspectionFindingStatus; - nextControlOn: string | null; - assetId: string; - assetCode: string; - assetName: string; - targetControlOn: string | null; - outcome: InspectionVerificationOutcome | null; - resultNotes: string | null; - verifiedAt: string | null; - resultRecordedAt: string | null; - rescheduledControlOn: string | null; - verificationEvidenceCount: number; -} - -export interface InspectionVisit extends InspectionVisitListItem { - assets: InspectionAssetSummary[]; - planningAssets: InspectionPlannedAsset[]; - team: InspectionPerson[]; - verificationFindings: InspectionVerificationFindingSummary[]; - checklist: InspectionVisitChecklistSummary; -} - -export type InspectionActStatus = - | 'DRAFT' | 'READY' | 'CLOSED' | 'CANCELLED' | 'RECTIFIED'; - -export type InspectionActVersionEvent = - | 'CREATED' | 'UPDATED' | 'READY' | 'REOPENED' | 'CLOSED' | 'CANCELLED'; - -export interface InspectionActListItem { - id: string; - visitId: string; - visit: { - id: string; - code: string; - title: string; - status: InspectionVisitStatus; - actualStartedAt: string | null; - }; - actYear: number; - actNumber: number; - code: string; - status: InspectionActStatus; - occurredAt: string; - title: string; - summary: string; - observations: string | null; - currentVersion: number; - cancellationReason: string | null; - closedAt: string | null; - closedBy: string | null; - closureSha256: string | null; - assetCount: number; - findingCount: number; - companies: Array<{ id: string; code: string; name: string }>; - areas: Array<{ id: string; code: string; name: string }>; - report: null | { - id: string; - code: string; - status: InspectionReportStatus; - pdfStatus: InspectionReportPdfStatus; - generatedAt: string; - }; - createdBy: InspectionPerson | null; - updatedBy: InspectionPerson | null; - createdAt: string; - updatedAt: string; -} - -export interface InspectionActVersion { - id: string; - versionNumber: number; - event: InspectionActVersionEvent; - snapshot: Record; - actorUserId: string | null; - actorUsername: string | null; - createdAt: string; -} - -export interface InspectionAct extends InspectionActListItem { - assets: InspectionAssetSummary[]; - versions: InspectionActVersion[]; -} - -export type InspectionReportStatus = 'FROZEN' | 'CANCELLED'; -export type InspectionReportPdfStatus = 'PENDING' | 'READY' | 'FAILED'; -export type InspectionReportWordStatus = 'PENDING' | 'READY' | 'FAILED'; -export type InspectionReportReviewStatus = 'PENDING_REVIEW' | 'APPROVED' | 'SIGNED'; - -export interface InspectionReportListItem { - id: string; - visitId: string; - actId: string; - reportYear: number; - reportNumber: number; - code: string; - status: InspectionReportStatus; - pdfStatus: InspectionReportPdfStatus; - wordStatus: InspectionReportWordStatus; - wordGeneratedAt: string | null; - reviewStatus: InspectionReportReviewStatus; - currentRevisionNumber: number; - approvedAt: string | null; - signedAt: string | null; - title: string; - actVersion: number; - actClosureSha256: string; - frozenSha256: string; - generatedAt: string; - generatedBy: InspectionPerson; - act: { - id: string; - code: string; - title: string; - status: InspectionActStatus; - occurredAt: string; - closedAt: string | null; - }; - visit: { - id: string; - code: string; - title: string; - status: InspectionVisitStatus; - }; - companies: Array<{ id: string; code: string; name: string }>; - areas: Array<{ id: string; code: string; name: string }>; - findingCount: number; -} - -export interface InspectionReport extends InspectionReportListItem { - frozenSnapshot: Record; -} - -export type InspectionReportRevisionSource = 'AUTO' | 'DIRECTOR_UPLOAD'; - -export interface InspectionReportRevision { - id: string; - reportId: string; - revisionNumber: number; - source: InspectionReportRevisionSource; - originalName: string; - mimeType: string; - sizeBytes: number; - sha256: string; - changeSummary: string | null; - createdAt: string; - createdBy: InspectionPerson; -} - -export interface InspectionReportSignature { - id: string; - revisionId: string; - signedAt: string; - confirmationText: string; - signatureSha256: string; - signedBy: InspectionPerson; -} - -export interface InspectionReportReview { - reportId: string; - reportCode: string; - reviewStatus: InspectionReportReviewStatus; - currentRevisionNumber: number; - approvedRevisionId: string | null; - approvedAt: string | null; - reviewNote: string | null; - approvedBy: InspectionPerson | null; - signature: InspectionReportSignature | null; - revisions: InspectionReportRevision[]; -} - -export interface PendingInspectionReportItem { - actId: string; - visitId: string; - actCode: string; - actTitle: string; - actYear: number; - occurredAt: string; - closedAt: string; - closureSha256: string; - visitCode: string; - visitTitle: string; - companies: Array<{ id: string; code: string; name: string }>; - areas: Array<{ id: string; code: string; name: string }>; - findingCount: number; -} - -export type InspectionResponsibleAttendanceStatus = 'PRESENT' | 'ABSENT'; -export type InspectionResponsibleDocumentType = 'DNI' | 'CUIL' | 'PASSPORT' | 'OTHER'; -export type InspectionActSignatureStatus = 'SIGNED' | 'REFUSED' | 'ABSENT'; -export type InspectionActSignerType = 'INSPECTOR' | 'COMPANY_RESPONSIBLE'; -export type InspectionActUploadMode = 'IMMEDIATE' | 'DEFERRED'; - -export interface InspectionActResponsible { - actId: string; - attendanceStatus: InspectionResponsibleAttendanceStatus; - fullName: string | null; - documentType: InspectionResponsibleDocumentType | null; - documentNumber: string | null; - position: string | null; - email: string | null; - phone: string | null; - absenceReason: string | null; - updatedBy: string | null; - createdAt: string; - updatedAt: string; -} - -export interface InspectionActSignature { - id: string; - actId: string; - signerType: InspectionActSignerType; - signerUserId: string | null; - signerName: string; - documentType: InspectionResponsibleDocumentType | null; - documentNumber: string | null; - position: string | null; - status: InspectionActSignatureStatus; - reason: string | null; - companyManifestation: 'CONFORMITY' | 'DISSENT' | null; - companyStatement: string | null; - mimeType: string | null; - sizeBytes: number | null; - imageSha256: string | null; - consentText: string | null; - consentVersion: string | null; - consentAcceptedAt: string | null; - clientSignedAt: string | null; - signedAt: string | null; - latitude: number | null; - longitude: number | null; - accuracyM: number | null; - deviceLabel: string | null; - source: 'WEB' | 'ANDROID'; - preparedSha256: string; - signaturePayloadSha256: string; - uploadedBy: string; - uploadedByUsername: string; - createdAt: string; -} - -export interface InspectionClosure { - act: { - id: string; - code: string; - status: InspectionActStatus; - visitId: string; - currentVersion: number; - closedAt: string | null; - closedBy: string | null; - closureSha256: string | null; - }; - visit: { - id: string; - code: string; - status: InspectionVisitStatus; - actualClosedAt: string | null; - }; - responsible: InspectionActResponsible | null; - closure: null | { - schemaVersion: string; - preparedSha256: string; - preparedAt: string; - preparedBy: string; - finalSha256: string | null; - deviceClosedAt: string | null; - serverClosedAt: string | null; - uploadMode: InspectionActUploadMode | null; - closedBy: string | null; - isCurrent: boolean; - }; - signatures: InspectionActSignature[]; - consents: { - version: string; - inspector: string; - company: string; - }; -} - -export interface FindingCategory { - id: string; - code: string; - name: string; - sortOrder: number; -} - -export interface FindingCatalogItem { - id: string; - categoryId: string; - categoryName: string; - code: string; - sourceNumber: number; - title: string; - legalBasis: string | null; - glossary: string | null; - suggestedSeverity: number | null; - revision: number; -} - -export interface FindingCatalog { - categories: FindingCategory[]; - items: FindingCatalogItem[]; -} - -export interface FindingAdminCategory extends FindingCategory { - isActive: boolean; - itemCount: number; - activeItemCount: number; - createdAt: string; - updatedAt: string; -} - -export interface FindingAdminCatalogItem extends FindingCatalogItem { - categoryCode: string; - categoryActive: boolean; - importNote: string | null; - isActive: boolean; - usageCount: number; - createdAt: string; - updatedAt: string; -} - -export interface FindingAdminCatalog { - categories: FindingAdminCategory[]; - items: FindingAdminCatalogItem[]; -} - -export interface FindingCatalogSelectionItem { - id: string; - categoryId: string; - categoryName: string; - code: string; - sourceNumber: number; - title: string; - suggestedSeverity: number | null; - enabled: boolean; -} - -export interface FindingCatalogAssetTypeSelection { - assetType: { id: string; code: string; name: string; operationalRole: AssetTypeOperationalRole }; - configured: boolean; - reason: string | null; - items: FindingCatalogSelectionItem[]; -} - -export interface FindingCatalogAssetSelection { - asset: { - id: string; - code: string; - name: string; - assetTypeId: string; - assetTypeCode: string; - assetTypeName: string; - }; - typeConfigured: boolean; - typeReason: string | null; - items: Array; -} - -export interface FindingCatalogApplicable extends FindingCatalog { - asset: FindingCatalogAssetSelection['asset']; - typeConfigured: boolean; - configurationReason: string | null; - other: { enabled: true; code: 'OTHER'; label: string; help: string }; -} - -export type FindingCatalogProposalStatus = 'PENDING' | 'MATCHED' | 'REJECTED'; -export interface FindingCatalogProposal { - id: string; - findingId: string; - findingCode: string; - assetId: string; - assetCode: string; - assetName: string; - assetTypeId: string; - assetTypeCode: string; - assetTypeName: string; - proposedTitle: string; - proposedLegalBasis: string | null; - proposedSeverity: number | null; - description: string; - status: FindingCatalogProposalStatus; - resolvedCatalogItemId: string | null; - resolvedCatalogCode: string | null; - resolvedCatalogTitle: string | null; - officeNotes: string | null; - reviewedBy: string | null; - reviewedByUsername: string | null; - reviewedAt: string | null; - createdAt: string; - updatedAt: string; -} - -export type InspectionFindingWorkflow = - | 'ALL' - | 'OPEN' - | 'WAITING_COMPANY' - | 'COMPANY_OVERDUE' - | 'TO_SCHEDULE_VERIFICATION' - | 'TO_VERIFY' - | 'VERIFICATION_OVERDUE' - | 'READY_TO_CLOSE' - | 'CLOSED'; - -export interface InspectionFindingWorkflowCounters { - open: number; - waitingCompany: number; - companyOverdue: number; - companyDueNext7Days: number; - awaitingVerificationSchedule: number; - toVerify: number; - verificationOverdue: number; - verificationNext30Days: number; - readyToClose: number; - closed: number; -} - -export interface InspectionFindingGlobalPage extends Page { - counters: InspectionFindingWorkflowCounters; -} - -export type VerificationPlanningStatus = 'ALL' | 'UNPLANNED' | 'PLANNED'; - -export interface VerificationPlanningItem { - id: string; - code: string; - title: string; - status: 'OPEN'; - nextControlOn: string; - companyResponseReceivedOn: string; - asset: { id: string; code: string; name: string; typeName: string }; - company: { id: string; code: string; name: string } | null; - area: { id: string; code: string; name: string } | null; - act: { id: string; code: string }; - sourceVisit: { id: string; code: string; title: string }; - verificationVisit: null | { - id: string; - code: string; - title: string; - status: InspectionVisitStatus; - plannedStartAt: string | null; - plannedEndAt: string | null; - }; -} - -export interface VerificationPlanningCounters { - eligible: number; - unplanned: number; - overdueUnplanned: number; - dueNext30Days: number; - planned: number; -} - -export interface VerificationPlanningPage extends Page { - counters: VerificationPlanningCounters; -} - -export interface PlannedVerificationVisitResult { - visit: { - id: string; - code: string; - title: string; - status: InspectionVisitStatus; - plannedStartAt: string | null; - plannedEndAt: string | null; - }; - company: { id: string; code: string; name: string }; - area: { id: string; code: string; name: string }; - findingCount: number; - assetCount: number; -} - -export type InspectionFindingStatus = 'OPEN' | 'CLOSED' | 'VOIDED'; -export type InspectionFindingVerificationEventType = - | 'CONTROL_DATE_DEFINED' - | 'CONTROL_DATE_CHANGED' - | 'CONTROL_DATE_CLEARED' - | 'VISIT_PLANNED' - | 'RESULT_RECORDED'; - -export interface InspectionFindingVerificationEvent { - id: string; - eventType: InspectionFindingVerificationEventType; - occurredAt: string; - targetControlOn: string | null; - previousControlOn: string | null; - nextControlOn: string | null; - outcome: InspectionVerificationOutcome | null; - notes: string | null; - actorUserId: string | null; - actorUsername: string | null; - verificationVisit: null | { - id: string; - code: string; - title: string; - status: InspectionVisitStatus; - plannedStartAt: string | null; - }; - evidenceCount: number; -} -export type InspectionFindingVersionEvent = 'CREATED' | 'UPDATED' | 'FOLLOW_UP_UPDATED' | 'VERIFICATION_RECORDED'; -export type InspectionFindingOfficeStage = - | 'WAITING_COMPANY' - | 'COMPANY_OVERDUE' - | 'DEFINE_VERIFICATION' - | 'PLAN_VERIFICATION' - | 'VERIFICATION_PLANNED' - | 'RESCHEDULE_VERIFICATION' - | 'READY_TO_CLOSE' - | 'CLOSED'; - -export interface InspectionFindingOfficeWorkflow { - stage: InspectionFindingOfficeStage; - label: string; - action: string; - dueOn: string | null; - overdue: boolean; -} - -export interface InspectionFinding { - id: string; - actId: string; - assetId: string; - catalogItemId: string | null; - findingNumber: number; - code: string; - status: InspectionFindingStatus; - title: string; - description: string; - legalBasis: string | null; - glossary: string | null; - catalogRevision: number | null; - suggestedSeverity: number | null; - severity: number | null; - correctionDueOn: string | null; - responseDueBasis: 'FINDING_DATE' | 'REPORT_NOTIFICATION' | null; - responseDueDays: number | null; - responseDueBaseOn: string | null; - reportNotifiedOn: string | null; - companyResponse: string | null; - companyResponseReceivedOn: string | null; - companyCommittedCorrectionOn: string | null; - nextControlOn: string | null; - currentVersion: number; - closedAt: string | null; - closureNotes: string | null; - asset: InspectionAssetSummary; - catalog: { - id: string; - code: string; - categoryId: string; - categoryName: string; - sourceNumber: number; - revision: number; - suggestedSeverity: number | null; - } | null; - document: { - actId: string; - actCode: string; - actStatus: InspectionActStatus; - visitId: string; - visitCode: string; - visitTitle: string; - visitStatus: InspectionVisitStatus; - }; - verificationVisit: null | { - id: string; - code: string; - title: string; - status: InspectionVisitStatus; - plannedStartAt: string | null; - }; - latestVerification: null | { - visitId: string; - visitCode: string; - visitStatus: InspectionVisitStatus; - targetControlOn: string | null; - outcome: InspectionVerificationOutcome | null; - resultNotes: string | null; - verifiedAt: string | null; - resultRecordedAt: string | null; - rescheduledControlOn: string | null; - evidenceCount: number; - }; - verificationHistory?: InspectionFindingVerificationEvent[]; - createdAt: string; - updatedAt: string; - officeWorkflow?: InspectionFindingOfficeWorkflow; - versions?: Array<{ - id: string; - versionNumber: number; - event: InspectionFindingVersionEvent; - snapshot: Record; - actorUserId: string | null; - actorUsername: string | null; - createdAt: string; - }>; -} - -export type InspectionEvidenceKind = 'PHOTO' | 'DOCUMENT'; -export type InspectionEvidencePurpose = - | 'OBSERVATION' - | 'VERIFICATION' - | 'COMPANY_RESPONSE' - | 'COMMUNICATION_ATTACHMENT' - | 'OTHER_DOCUMENT'; - -export interface InspectionFindingCommunication { - id: string; - findingId: string; - direction: 'INBOUND' | 'OUTBOUND' | 'INTERNAL'; - channel: 'EMAIL' | 'IN_PERSON' | 'PHONE' | 'LETTER' | 'SYSTEM' | 'OTHER'; - type: 'COMPANY_RESPONSE' | 'AUTHORITY_NOTICE' | 'FOLLOW_UP' | 'OTHER'; - occurredAt: string; - subject: string; - details: string | null; - contactName: string | null; - contactEmail: string | null; - createdBy: string | null; - createdByUsername: string | null; - attachmentCount: number; - createdAt: string; -} - -export interface InspectionFindingEvidence { - id: string; - findingId: string; - communicationId: string | null; - verificationVisitId: string | null; - communication: { - id: string; - type: InspectionFindingCommunication['type']; - direction: InspectionFindingCommunication['direction']; - subject: string; - } | null; - kind: InspectionEvidenceKind; - purpose: InspectionEvidencePurpose; - originalName: string; - mimeType: string; - sizeBytes: number; - sha256: string; - title: string | null; - description: string | null; - capturedAt: string | null; - latitude: number | null; - longitude: number | null; - accuracyM: number | null; - deviceLabel: string | null; - source: 'WEB' | 'ANDROID' | 'IMPORT'; - uploadedBy: string | null; - uploadedByUsername: string | null; - createdAt: string; -} - -let csrfToken: string | null = null; -let refreshPromise: Promise | null = null; - -function readCsrfCookie(): string | null { - if (typeof document === 'undefined') return null; - const cookies = document.cookie.split(';').map((item) => item.trim()); - const exact = cookies.find((item) => item.startsWith('dhv2_csrf=')); - const fallback = cookies.find((item) => item.split('=', 1)[0]?.endsWith('_csrf')); - const raw = exact ?? fallback; - if (!raw) return null; - const separator = raw.indexOf('='); - return separator >= 0 ? decodeURIComponent(raw.slice(separator + 1)) : null; -} - -function isUnsafe(method: string): boolean { - return !['GET', 'HEAD', 'OPTIONS'].includes(method.toUpperCase()); -} - -async function decodeResponse(response: Response): Promise { - const body = await response.json().catch(() => null) as { - code?: string; - message?: string; - requestId?: string; - } | null; - if (!response.ok) { - throw new ApiError( - response.status, - body?.code ?? 'REQUEST_ERROR', - body?.message ?? `La solicitud falló con HTTP ${response.status}`, - body?.requestId, - ); - } - return body as T; -} - -async function rawRequest(path: string, init: RequestInit = {}): Promise { - const method = (init.method ?? 'GET').toUpperCase(); - const headers = new Headers(init.headers); - headers.set('Accept', 'application/json'); - if (init.body && !(init.body instanceof FormData)) { - headers.set('Content-Type', 'application/json'); - } - if (isUnsafe(method) && path !== '/auth/login') { - const token = csrfToken ?? readCsrfCookie(); - if (token) headers.set('x-csrf-token', token); - } - - const response = await fetch(`${API_BASE}${path}`, { - ...init, - method, - headers, - credentials: 'include', - cache: 'no-store', - }); - return decodeResponse(response); -} - -async function refreshAccess(): Promise { - if (!refreshPromise) { - refreshPromise = rawRequest('/auth/refresh', { method: 'POST' }) - .then((response) => { csrfToken = response.csrfToken; }) - .finally(() => { refreshPromise = null; }); - } - return refreshPromise; -} - -export async function apiRequest( - path: string, - init: RequestInit = {}, - retrySession = true, -): Promise { - try { - return await rawRequest(path, init); - } catch (error) { - if ( - retrySession && error instanceof ApiError && error.status === 401 && - path !== '/auth/login' && path !== '/auth/refresh' - ) { - try { - await refreshAccess(); - return await rawRequest(path, init); - } catch (refreshError) { - csrfToken = null; - window.dispatchEvent(new Event('dhv2:unauthorized')); - throw refreshError; - } - } - throw error; - } -} - -async function rawBlobRequest(path: string): Promise { - const response = await fetch(`${API_BASE}${path}`, { - method: 'GET', - headers: { Accept: '*/*' }, - credentials: 'include', - cache: 'no-store', - }); - if (!response.ok) return decodeResponse(response); - return response.blob(); -} - -async function apiBlobRequest(path: string): Promise { - try { - return await rawBlobRequest(path); - } catch (error) { - if (error instanceof ApiError && error.status === 401) { - try { - await refreshAccess(); - return await rawBlobRequest(path); - } catch (refreshError) { - csrfToken = null; - window.dispatchEvent(new Event('dhv2:unauthorized')); - throw refreshError; - } - } - throw error; - } -} - -export function getHealth() { return apiRequest('/health', {}, false); } - -export async function login(identifier: string, password: string) { - const response = await apiRequest('/auth/login', { - method: 'POST', - body: JSON.stringify({ identifier, password, deviceLabel: 'Panel web' }), - }, false); - csrfToken = response.csrfToken; - return response; -} - -export async function getMe() { - const response = await apiRequest<{ user: AuthUser }>('/auth/me'); - return response.user; -} - -export async function logout() { - await apiRequest<{ status: string }>('/auth/logout', { method: 'POST' }); - csrfToken = null; -} - -export function changePassword(currentPassword: string, newPassword: string) { - return apiRequest<{ status: string; mustChangePassword: false }>('/auth/change-password', { - method: 'POST', body: JSON.stringify({ currentPassword, newPassword }), - }); -} - -export function getDashboardSummary() { return apiRequest('/dashboard/summary'); } - -export function listUsers(params: { - page?: number; pageSize?: number; search?: string; status?: 'ACTIVE' | 'INACTIVE' | ''; -} = {}) { - const query = new URLSearchParams(); - query.set('page', String(params.page ?? 1)); - query.set('pageSize', String(params.pageSize ?? 20)); - if (params.search) query.set('search', params.search); - if (params.status) query.set('status', params.status); - return apiRequest>(`/users?${query}`); -} - -export function getUser(id: string) { return apiRequest(`/users/${id}`); } - -export function createUser(input: { - username: string; email?: string; firstName: string; lastName: string; - password: string; mustChangePassword: boolean; roleIds: string[]; -}) { - return apiRequest('/users', { - method: 'POST', body: JSON.stringify(input), - }); -} - -export function updateUser(id: string, input: { - username?: string; email?: string | null; firstName?: string; lastName?: string; -}) { - return apiRequest(`/users/${id}`, { - method: 'PATCH', body: JSON.stringify(input), - }); -} - -export function resetUserPassword(id: string, password: string, mustChangePassword = true) { - return apiRequest(`/users/${id}/reset-password`, { - method: 'POST', - body: JSON.stringify({ password, mustChangePassword }), - }); -} - -export function updateUserStatus(id: string, status: 'ACTIVE' | 'INACTIVE') { - return apiRequest(`/users/${id}/status`, { - method: 'PATCH', body: JSON.stringify({ status }), - }); -} - -export function replaceUserRoles(id: string, roleIds: string[]) { - return apiRequest(`/users/${id}/roles`, { - method: 'PUT', body: JSON.stringify({ roleIds }), - }); -} - -export async function listRoles() { - return (await apiRequest<{ data: AdministrativeRole[] }>('/roles')).data; -} - -export async function listPermissions() { - return (await apiRequest<{ data: Permission[] }>('/roles/permissions')).data; -} - -export function createRole(input: { - code: string; name: string; description: string; permissionIds: string[]; -}) { - return apiRequest('/roles', { - method: 'POST', body: JSON.stringify(input), - }); -} - -export function updateRole(id: string, input: { name: string; description: string }) { - return apiRequest(`/roles/${id}`, { - method: 'PATCH', body: JSON.stringify(input), - }); -} - -export function replaceRolePermissions(id: string, permissionIds: string[]) { - return apiRequest(`/roles/${id}/permissions`, { - method: 'PUT', body: JSON.stringify({ permissionIds }), - }); -} - -export function listAudit(params: Record) { - const query = new URLSearchParams(); - Object.entries(params).forEach(([key, value]) => { - if (value !== undefined && value !== '') query.set(key, String(value)); - }); - return apiRequest>(`/audit?${query}`); -} - -export function getAuditEvent(id: string) { - return apiRequest(`/audit/${id}`); -} - -export async function listAssetTypes() { - return (await apiRequest<{ data: AssetType[] }>('/asset-types')).data; -} - -export function getMasterBootstrapStatus() { - return apiRequest('/asset-types/bootstrap-status'); -} - -export function bootstrapMasterDefaults() { - return apiRequest('/asset-types/bootstrap-defaults', { - method: 'POST', - }); -} - -export function getMasterEnrichmentStatus() { - return apiRequest('/asset-types/enrichment-status'); -} - -export function enrichMasterDefaults() { - return apiRequest('/asset-types/enrich-defaults', { - method: 'POST', - }); -} - -export async function listOperationalAreas(parentId?: string) { - const query = parentId ? `?${new URLSearchParams({ parentId })}` : ''; - return (await apiRequest<{ data: OperationalAssetSummary[] }>(`/asset-operational-relations/areas${query}`)).data; -} - -export async function listOperationalCompanies() { - return (await apiRequest<{ data: OperationalAssetSummary[] }>('/asset-operational-relations/companies')).data; -} - -export async function listCompaniesForArea(areaId: string) { - return (await apiRequest<{ data: OperationalAssetSummary[] }>(`/asset-operational-relations/areas/${areaId}/companies`)).data; -} - -export async function listAreasForCompany(companyId: string) { - return (await apiRequest<{ data: OperationalAssetSummary[] }>(`/asset-operational-relations/companies/${companyId}/areas`)).data; -} - -export async function listAreaCompanyRelations(params: { - areaId?: string; - companyId?: string; - includeHistory?: boolean; -} = {}) { - const query = new URLSearchParams(); - if (params.areaId) query.set('areaId', params.areaId); - if (params.companyId) query.set('companyId', params.companyId); - if (params.includeHistory) query.set('includeHistory', 'true'); - const suffix = query.size ? `?${query}` : ''; - return (await apiRequest<{ data: AreaCompanyRelation[] }>(`/asset-operational-relations${suffix}`)).data; -} - -export function createAreaCompanyRelation(input: { areaId: string; companyId: string; relationRole?: AreaOrganizationRole; participationPercent?: number | null; legalInstrument?: string | null; sourceDocumentId?: string | null; reason: string }) { - return apiRequest('/asset-operational-relations', { - method: 'POST', body: JSON.stringify(input), - }); -} - -export function endAreaCompanyRelation(id: string, reason: string) { - return apiRequest(`/asset-operational-relations/${id}/end`, { - method: 'POST', body: JSON.stringify({ reason }), - }); -} - -export function createAssetType(input: { - code: string; - name: string; - description: string; - canBeRoot: boolean; - operationalRole: AssetTypeOperationalRole; - allowedParentTypeIds: string[]; -}) { - return apiRequest('/asset-types', { - method: 'POST', body: JSON.stringify(input), - }); -} - -export function updateAssetType(id: string, input: { - name?: string; - description?: string; - canBeRoot?: boolean; - isActive?: boolean; - operationalRole?: AssetTypeOperationalRole; - allowedParentTypeIds?: string[]; -}) { - return apiRequest(`/asset-types/${id}`, { - method: 'PATCH', body: JSON.stringify(input), - }); -} - -export function createAssetAttribute(typeId: string, input: { - code: string; - name: string; - dataType: AssetAttributeDataType; - isRequired: boolean; - unit?: string | null; - options?: string[]; - sortOrder: number; -}) { - return apiRequest(`/asset-types/${typeId}/attributes`, { - method: 'POST', body: JSON.stringify(input), - }); -} - -export function updateAssetAttribute( - typeId: string, - attributeId: string, - input: { - name?: string; - dataType?: AssetAttributeDataType; - isRequired?: boolean; - isActive?: boolean; - unit?: string | null; - options?: string[] | null; - sortOrder?: number; - }, -) { - return apiRequest(`/asset-types/${typeId}/attributes/${attributeId}`, { - method: 'PATCH', body: JSON.stringify(input), - }); -} - -export function listAssets(params: { - page?: number; - pageSize?: number; - search?: string; - typeId?: string; - status?: AssetInformationStatus | ''; - operationalStatus?: AssetOperationalStatus | ''; - needsValidation?: boolean; - hasGeometry?: boolean; - parentId?: string; - operationalAreaId?: string; - operatorCompanyId?: string; -} = {}) { - const query = new URLSearchParams(); - query.set('page', String(params.page ?? 1)); - query.set('pageSize', String(params.pageSize ?? 25)); - if (params.search) query.set('search', params.search); - if (params.typeId) query.set('typeId', params.typeId); - if (params.status) query.set('status', params.status); - if (params.operationalStatus) query.set('operationalStatus', params.operationalStatus); - if (params.needsValidation !== undefined) query.set('needsValidation', String(params.needsValidation)); - if (params.hasGeometry !== undefined) query.set('hasGeometry', String(params.hasGeometry)); - if (params.parentId) query.set('parentId', params.parentId); - if (params.operationalAreaId) query.set('operationalAreaId', params.operationalAreaId); - if (params.operatorCompanyId) query.set('operatorCompanyId', params.operatorCompanyId); - return apiRequest>(`/assets?${query}`); -} - -export async function listAssetTree(params: { - search?: string; - typeId?: string; - status?: AssetInformationStatus | ''; - operationalStatus?: AssetOperationalStatus | ''; - operationalAreaId?: string; - operatorCompanyId?: string; - needsValidation?: boolean; - hasGeometry?: boolean; -} = {}) { - const query = new URLSearchParams(); - if (params.search) query.set('search', params.search); - if (params.typeId) query.set('typeId', params.typeId); - if (params.status) query.set('status', params.status); - if (params.operationalStatus) query.set('operationalStatus', params.operationalStatus); - if (params.operationalAreaId) query.set('operationalAreaId', params.operationalAreaId); - if (params.operatorCompanyId) query.set('operatorCompanyId', params.operatorCompanyId); - if (params.needsValidation !== undefined) query.set('needsValidation', String(params.needsValidation)); - if (params.hasGeometry !== undefined) query.set('hasGeometry', String(params.hasGeometry)); - const suffix = query.toString() ? `?${query}` : ''; - return apiRequest<{ data: AssetListItem[]; meta: { count: number; truncated: boolean } }>(`/assets/tree${suffix}`); -} - -export async function listAssetTreeChildren(params: { - parentId?: string; search?: string; typeId?: string; status?: AssetInformationStatus | ''; - operationalStatus?: AssetOperationalStatus | ''; operationalAreaId?: string; operatorCompanyId?: string; - needsValidation?: boolean; hasGeometry?: boolean; limit?: number; -} = {}) { - const query = new URLSearchParams(); - if (params.parentId) query.set('parentId', params.parentId); - if (params.search) query.set('search', params.search); - if (params.typeId) query.set('typeId', params.typeId); - if (params.status) query.set('status', params.status); - if (params.operationalStatus) query.set('operationalStatus', params.operationalStatus); - if (params.operationalAreaId) query.set('operationalAreaId', params.operationalAreaId); - if (params.operatorCompanyId) query.set('operatorCompanyId', params.operatorCompanyId); - if (params.needsValidation !== undefined) query.set('needsValidation', String(params.needsValidation)); - if (params.hasGeometry !== undefined) query.set('hasGeometry', String(params.hasGeometry)); - query.set('limit', String(params.limit ?? 100)); - return apiRequest<{ data: AssetListItem[]; meta: { count: number; hasMore: boolean; parentId: string | null } }>(`/assets/tree-children?${query}`); -} - -export async function listAssetParentOptions( - childTypeId: string, - assetId?: string, - search?: string, -) { - const query = new URLSearchParams({ childTypeId }); - if (assetId) query.set('assetId', assetId); - if (search) query.set('search', search); - return (await apiRequest<{ data: AssetListItem[] }>(`/assets/parent-options?${query}`)).data; -} - -export function getAsset(id: string) { - return apiRequest(`/assets/${id}`); -} - -export function getAssetDossier(id: string) { - return apiRequest(`/assets/${id}/dossier`); -} - -export async function getAssetLineage(id: string) { - return (await apiRequest<{ data: AssetLineageItem[] }>(`/assets/${id}/lineage`)).data; -} - -export function createAsset(input: { - code: string; - name: string; - commonName?: string | null; - typeId: string; - parentId?: string | null; - operationalAreaId?: string | null; - operatorCompanyId?: string | null; - description?: string | null; - informationStatus?: AssetInformationStatus; - attributes: Record; -}) { - return apiRequest('/assets', { - method: 'POST', body: JSON.stringify(input), - }); -} - -export function updateAsset(id: string, input: { - typeId?: string; - code?: string; - name?: string; - commonName?: string | null; - parentId?: string | null; - operationalAreaId?: string | null; - operatorCompanyId?: string | null; - description?: string | null; - attributes?: Record; -}) { - return apiRequest(`/assets/${id}`, { - method: 'PATCH', body: JSON.stringify(input), - }); -} - -export function updateAssetInformationStatus( - id: string, - informationStatus: AssetInformationStatus, -) { - return apiRequest(`/assets/${id}/information-status`, { - method: 'PATCH', body: JSON.stringify({ informationStatus }), - }); -} - -export function updateAssetOperationalStatus(id: string, status: AssetOperationalStatus) { - return apiRequest(`/assets/${id}/operational-status`, { - method: 'PATCH', body: JSON.stringify({ status }), - }); -} - - -export async function listAssetContextHistory(assetId: string) { - return (await apiRequest<{ data: AssetContextHistoryItem[] }>(`/assets/${assetId}/context-history`)).data; -} - -export function changeAssetContext(assetId: string, input: { - parentId?: string | null; - operationalAreaId?: string | null; - operatorCompanyId?: string | null; - effectiveAt?: string; - reason: string; -}) { - return apiRequest(`/assets/${assetId}/context`, { - method: 'POST', body: JSON.stringify(input), - }); -} - -export function listFieldDiscoveries(params: { page?: number; pageSize?: number; status?: FieldDiscoveryStatus; search?: string } = {}) { - const query = new URLSearchParams(); - query.set('page', String(params.page ?? 1)); - query.set('pageSize', String(params.pageSize ?? 25)); - if (params.status) query.set('status', params.status); - if (params.search) query.set('search', params.search); - return apiRequest>(`/assets/field-discoveries?${query}`); -} - -export function approveFieldDiscovery(id: string, notes?: string | null) { - return apiRequest(`/assets/field-discoveries/${id}/approve`, { method: 'POST', body: JSON.stringify({ notes: notes || null }) }); -} - -export function rejectFieldDiscovery(id: string, reason: string) { - return apiRequest(`/assets/field-discoveries/${id}/reject`, { method: 'POST', body: JSON.stringify({ reason }) }); -} - -export function matchFieldDiscovery(id: string, matchedAssetId: string, reason: string) { - return apiRequest(`/assets/field-discoveries/${id}/match`, { method: 'POST', body: JSON.stringify({ matchedAssetId, reason }) }); -} - -export function getAssetRegistry(assetId: string) { - return apiRequest(`/assets/${assetId}/registry`); -} -export async function listSourceDocuments(search = '') { - const suffix = search.trim() ? `?search=${encodeURIComponent(search.trim())}` : ''; - return (await apiRequest<{ data: SourceDocumentSummary[] }>(`/source-documents${suffix}`)).data; -} -export function createSourceDocument(input: { documentType: SourceDocumentType; documentNumber?: string | null; title: string; issuer?: string | null; documentDate?: string | null; externalReference?: string | null; notes?: string | null }) { - return apiRequest('/source-documents', { method: 'POST', body: JSON.stringify(input) }); -} -export function linkAssetSourceDocument(assetId: string, documentId: string, input: { relationType: AssetSourceDocumentRelationType; notes?: string | null }) { - return apiRequest(`/assets/${assetId}/source-documents/${documentId}`, { method: 'POST', body: JSON.stringify(input) }); -} -export function addAssetExternalIdentifier(assetId: string, input: { namespace: string; value: string; validFrom?: string | null; sourceDocumentId?: string | null; notes?: string | null }) { - return apiRequest(`/assets/${assetId}/external-identifiers`, { method: 'POST', body: JSON.stringify(input) }); -} -export function endAssetExternalIdentifier(id: string, reason: string) { - return apiRequest(`/asset-external-identifiers/${id}/end`, { method: 'POST', body: JSON.stringify({ reason }) }); -} -export function upsertOrganizationProfile(assetId: string, input: { organizationKind: OrganizationKind; legalName?: string | null; taxId?: string | null; notificationEmail?: string | null; notes?: string | null }) { - return apiRequest(`/assets/${assetId}/organization-profile`, { method: 'PATCH', body: JSON.stringify(input) }); -} -export function addOrganizationMembership(organizationId: string, input: { memberOrganizationId: string; role: OrganizationMembershipRole; participationPercent?: number | null; validFrom?: string | null; sourceDocumentId?: string | null; notes?: string | null }) { - return apiRequest(`/organizations/${organizationId}/memberships`, { method: 'POST', body: JSON.stringify(input) }); -} -export function endOrganizationMembership(id: string, reason: string) { - return apiRequest(`/organization-memberships/${id}/end`, { method: 'POST', body: JSON.stringify({ reason }) }); -} -export function createAreaLegalRight(areaId: string, input: { rightType: AreaLegalRightType; name: string; instrumentNumber?: string | null; validFrom?: string | null; validUntil?: string | null; status?: AreaLegalRightStatus; sourceDocumentId?: string | null; notes?: string | null }) { - return apiRequest(`/areas/${areaId}/legal-rights`, { method: 'POST', body: JSON.stringify(input) }); -} -export function addAreaLegalRightOrganization(rightId: string, input: { organizationId: string; role: AreaLegalRightOrganizationRole; participationPercent?: number | null; validFrom?: string | null; notes?: string | null }) { - return apiRequest(`/area-legal-rights/${rightId}/organizations`, { method: 'POST', body: JSON.stringify(input) }); -} -export function endAreaLegalRightOrganization(id: string, reason: string) { - return apiRequest(`/area-legal-right-organizations/${id}/end`, { method: 'POST', body: JSON.stringify({ reason }) }); -} - -export async function getAssetGeometry(assetId: string) { - return (await apiRequest<{ data: AssetGeometry | null }>(`/assets/${assetId}/geometry`)).data; -} - -export function upsertAssetGeometry(assetId: string, input: { - geometry: GeoJsonGeometry; - accuracyM?: number | null; - capturedAt?: string | null; - deviceLabel?: string | null; -}) { - return apiRequest(`/assets/${assetId}/geometry`, { - method: 'PUT', body: JSON.stringify(input), - }); -} - -export function removeAssetGeometry(assetId: string) { - return apiRequest<{ status: 'removed' | 'absent' }>(`/assets/${assetId}/geometry`, { - method: 'DELETE', - }); -} - -export function getMapAssets(params: { - bbox?: string; - typeId?: string; - status?: AssetInformationStatus | ''; - geometryType?: AssetGeometryType | ''; -} = {}) { - const query = new URLSearchParams(); - if (params.bbox) query.set('bbox', params.bbox); - if (params.typeId) query.set('typeId', params.typeId); - if (params.status) query.set('status', params.status); - if (params.geometryType) query.set('geometryType', params.geometryType); - const suffix = query.size ? `?${query}` : ''; - return apiRequest(`/map/assets${suffix}`); -} - -export function listAssetVersions(params: { - page?: number; - pageSize?: number; - search?: string; - typeId?: string; - status?: AssetInformationStatus | ''; - changeType?: AssetVersionChangeType | ''; - from?: string; - to?: string; -} = {}) { - const query = new URLSearchParams(); - query.set('page', String(params.page ?? 1)); - query.set('pageSize', String(params.pageSize ?? 25)); - if (params.search) query.set('search', params.search); - if (params.typeId) query.set('typeId', params.typeId); - if (params.status) query.set('status', params.status); - if (params.changeType) query.set('changeType', params.changeType); - if (params.from) query.set('from', params.from); - if (params.to) query.set('to', params.to); - return apiRequest>(`/asset-versions?${query}`); -} - -export function listAssetVersionTimeline( - assetId: string, - params: { page?: number; pageSize?: number } = {}, -) { - const query = new URLSearchParams({ - page: String(params.page ?? 1), - pageSize: String(params.pageSize ?? 10), - }); - return apiRequest>(`/assets/${assetId}/versions?${query}`); -} - -export function getAssetVersion(assetId: string, versionNumber: number) { - return apiRequest(`/assets/${assetId}/versions/${versionNumber}`); -} - -export async function listAssetMedia(assetId: string) { - return (await apiRequest<{ data: AssetMedia[] }>(`/assets/${assetId}/media`)).data; -} - -export function uploadAssetMedia(assetId: string, input: { - file: File; - kind: AssetMediaKind; - title?: string; - description?: string; - capturedAt?: string; - latitude?: number; - longitude?: number; - accuracyM?: number; -}) { - const body = new FormData(); - body.append('file', input.file); - body.append('kind', input.kind); - if (input.title) body.append('title', input.title); - if (input.description) body.append('description', input.description); - if (input.capturedAt) body.append('capturedAt', input.capturedAt); - if (input.latitude !== undefined) body.append('latitude', String(input.latitude)); - if (input.longitude !== undefined) body.append('longitude', String(input.longitude)); - if (input.accuracyM !== undefined) body.append('accuracyM', String(input.accuracyM)); - return apiRequest(`/assets/${assetId}/media`, { - method: 'POST', - body, - }); -} - -export function updateAssetMedia(mediaId: string, input: { - title?: string | null; - description?: string | null; - capturedAt?: string | null; - latitude?: number | null; - longitude?: number | null; - accuracyM?: number | null; -}) { - return apiRequest(`/asset-media/${mediaId}`, { - method: 'PATCH', - body: JSON.stringify(input), - }); -} - -export function removeAssetMedia(mediaId: string) { - return apiRequest<{ status: 'removed' }>(`/asset-media/${mediaId}`, { - method: 'DELETE', - }); -} - -export function getAssetMediaBlob(mediaId: string, download = false) { - return apiBlobRequest(`/asset-media/${mediaId}/content${download ? '?download=1' : ''}`); -} - -export function getAssetProvenance(assetId: string) { - return apiRequest(`/assets/${assetId}/provenance`); -} - -export function updateAssetProvenance(assetId: string, input: { - origin: AssetDataOrigin; - sourceName?: string | null; - sourceReference?: string | null; - observedAt?: string | null; - notes?: string | null; -}) { - return apiRequest(`/assets/${assetId}/provenance`, { - method: 'PUT', - body: JSON.stringify(input), - }); -} - -export function verifyAssetProvenance(assetId: string) { - return apiRequest(`/assets/${assetId}/provenance/verify`, { - method: 'POST', - }); -} - -export function listTemporalAssets(params: { - at: string; - page?: number; - pageSize?: number; - search?: string; - typeId?: string; - status?: AssetInformationStatus | ''; -}) { - const query = new URLSearchParams({ - at: params.at, - page: String(params.page ?? 1), - pageSize: String(params.pageSize ?? 25), - }); - if (params.search) query.set('search', params.search); - if (params.typeId) query.set('typeId', params.typeId); - if (params.status) query.set('status', params.status); - return apiRequest & { asOf: string }>( - `/temporal-assets?${query}`, - ); -} - -export function getTemporalAsset(assetId: string, at: string) { - const query = new URLSearchParams({ at }); - return apiRequest(`/temporal-assets/${assetId}?${query}`); -} - -export function listSurveyCampaigns(params: { - page?: number; - pageSize?: number; - search?: string; - status?: SurveyCampaignStatus | ''; - coordinatorUserId?: string; -} = {}) { - const query = new URLSearchParams({ - page: String(params.page ?? 1), - pageSize: String(params.pageSize ?? 25), - }); - if (params.search) query.set('search', params.search); - if (params.status) query.set('status', params.status); - if (params.coordinatorUserId) query.set('coordinatorUserId', params.coordinatorUserId); - return apiRequest>(`/survey-campaigns?${query}`); -} - -export function getSurveyCampaign(id: string) { - return apiRequest(`/survey-campaigns/${id}`); -} - -export async function listSurveyAssignees() { - return (await apiRequest<{ data: SurveyPerson[] }>('/survey-campaigns/assignees')).data; -} - -export function createSurveyCampaign(input: { - code: string; - name: string; - description?: string | null; - plannedStartAt?: string | null; - plannedEndAt?: string | null; - scopeAssetId?: string | null; - coordinatorUserId?: string | null; -}) { - return apiRequest('/survey-campaigns', { - method: 'POST', - body: JSON.stringify(input), - }); -} - -export function updateSurveyCampaign(id: string, input: { - code?: string; - name?: string; - description?: string | null; - plannedStartAt?: string | null; - plannedEndAt?: string | null; - scopeAssetId?: string | null; - coordinatorUserId?: string | null; -}) { - return apiRequest(`/survey-campaigns/${id}`, { - method: 'PATCH', - body: JSON.stringify(input), - }); -} - -export function updateSurveyCampaignStatus(id: string, status: SurveyCampaignStatus) { - return apiRequest(`/survey-campaigns/${id}/status`, { - method: 'PUT', - body: JSON.stringify({ status }), - }); -} - -export function addSurveyTarget(campaignId: string, input: { - assetId: string; - assignedUserId?: string | null; - dueAt?: string | null; - instructions?: string | null; -}) { - return apiRequest(`/survey-campaigns/${campaignId}/targets`, { - method: 'POST', - body: JSON.stringify(input), - }); -} - -export function updateSurveyTarget(id: string, input: { - dueAt?: string | null; - instructions?: string | null; -}) { - return apiRequest(`/survey-campaign-targets/${id}`, { - method: 'PATCH', - body: JSON.stringify(input), - }); -} - -export function assignSurveyTarget(id: string, assignedUserId: string | null) { - return apiRequest(`/survey-campaign-targets/${id}/assignment`, { - method: 'PUT', - body: JSON.stringify({ assignedUserId }), - }); -} - -export function updateSurveyTargetStatus(id: string, status: SurveyTargetStatus) { - return apiRequest(`/survey-campaign-targets/${id}/status`, { - method: 'PUT', - body: JSON.stringify({ status }), - }); -} - -export function getSurveyExecution(targetId: string) { - return apiRequest(`/survey-campaign-targets/${targetId}/report`); -} - -export function saveSurveyReport(targetId: string, input: { - outcome: SurveyReportOutcome | null; - observedAt: string | null; - latitude: number | null; - longitude: number | null; - accuracyM: number | null; - notes: string | null; - mediaIds: string[]; -}) { - return apiRequest(`/survey-campaign-targets/${targetId}/report`, { - method: 'PUT', - body: JSON.stringify(input), - }); -} - -export function submitSurveyReport(targetId: string) { - return apiRequest(`/survey-campaign-targets/${targetId}/report/submit`, { - method: 'POST', - }); -} - -export function reviewSurveyReport( - targetId: string, - decision: 'APPROVE' | 'REJECT', - notes: string | null, -) { - return apiRequest(`/survey-campaign-targets/${targetId}/report/review`, { - method: 'POST', - body: JSON.stringify({ decision, notes }), - }); -} - -export function listInspectionVisits(params: { - page?: number; - pageSize?: number; - search?: string; - status?: InspectionVisitStatus | ''; - companyId?: string; - areaId?: string; - inspectorId?: string; - dateFrom?: string; - dateTo?: string; -}) { - const query = new URLSearchParams({ - page: String(params.page ?? 1), - pageSize: String(params.pageSize ?? 25), - }); - if (params.search) query.set('search', params.search); - if (params.status) query.set('status', params.status); - if (params.companyId) query.set('companyId', params.companyId); - if (params.areaId) query.set('areaId', params.areaId); - if (params.inspectorId) query.set('inspectorId', params.inspectorId); - if (params.dateFrom) query.set('dateFrom', params.dateFrom); - if (params.dateTo) query.set('dateTo', params.dateTo); - return apiRequest>(`/inspection-visits?${query}`); -} - -export function getInspectionVisit(id: string) { - return apiRequest(`/inspection-visits/${id}`); -} - -export async function listInspectionAssignees() { - return (await apiRequest<{ data: InspectionPerson[] }>('/inspection-visits/assignees')).data; -} - -export async function listInspectionPlanningAreas() { - return (await apiRequest<{ data: InspectionPlanningContextAsset[] }>('/inspection-visits/planning-context/areas')).data; -} - -export async function listInspectionPlanningOperators(areaId: string) { - return (await apiRequest<{ data: InspectionPlanningContextAsset[] }>(`/inspection-visits/planning-context/areas/${areaId}/operators`)).data; -} - -export function createInspectionVisit(input: { - operationalAreaId: string; - operatorCompanyId: string; - plannedStartAt: string; - leadInspectorUserId: string; -}) { - return apiRequest('/inspection-visits', { - method: 'POST', - body: JSON.stringify(input), - }); -} - -export function updateInspectionVisit(id: string, input: { - objective?: string | null; - scopeAssetId?: string | null; - operationalAreaId?: string | null; - operatorCompanyId?: string | null; - plannedStartAt?: string | null; - instructions?: string | null; -}) { - return apiRequest(`/inspection-visits/${id}`, { - method: 'PATCH', - body: JSON.stringify(input), - }); -} - -export function replaceInspectionVisitAssets(id: string, assetIds: string[]) { - return apiRequest(`/inspection-visits/${id}/assets`, { - method: 'PUT', - body: JSON.stringify({ assetIds }), - }); -} - -export function generateInspectionVisitChecklist(id: string) { - return apiRequest(`/inspection-visits/${id}/checklist/generate`, { - method: 'POST', - }); -} - -export function excludeInspectionVisitAsset(id: string, assetId: string, reason: string) { - return apiRequest(`/inspection-visits/${id}/assets/${assetId}/exclude`, { - method: 'POST', - body: JSON.stringify({ reason }), - }); -} - -export function includeInspectionVisitAsset(id: string, assetId: string) { - return apiRequest(`/inspection-visits/${id}/assets/${assetId}/include`, { - method: 'POST', - }); -} - -export function replaceInspectionVisitTeam( - id: string, - leadInspectorUserId: string | null, - memberUserIds: string[], -) { - return apiRequest(`/inspection-visits/${id}/team`, { - method: 'PUT', - body: JSON.stringify({ leadInspectorUserId, memberUserIds }), - }); -} - -export function updateInspectionVisitStatus( - id: string, - status: InspectionVisitStatus, - reason?: string | null, -) { - return apiRequest(`/inspection-visits/${id}/status`, { - method: 'PUT', - body: JSON.stringify({ status, reason: reason ?? null }), - }); -} - -export function listInspectionActs(visitId: string, params: { - page?: number; - pageSize?: number; - search?: string; - status?: InspectionActStatus | ''; -} = {}) { - const query = new URLSearchParams({ - page: String(params.page ?? 1), - pageSize: String(params.pageSize ?? 25), - }); - if (params.search) query.set('search', params.search); - if (params.status) query.set('status', params.status); - return apiRequest>( - `/inspection-visits/${visitId}/acts?${query}`, - ); -} - -export function listInspectionActsGlobal(params: { - page?: number; - pageSize?: number; - search?: string; - status?: InspectionActStatus | ''; - year?: number | ''; - companyId?: string; - areaId?: string; - inspectorId?: string; - dateFrom?: string; - dateTo?: string; -} = {}) { - const query = new URLSearchParams({ - page: String(params.page ?? 1), - pageSize: String(params.pageSize ?? 25), - }); - if (params.search) query.set('search', params.search); - if (params.status) query.set('status', params.status); - if (params.year) query.set('year', String(params.year)); - if (params.companyId) query.set('companyId', params.companyId); - if (params.areaId) query.set('areaId', params.areaId); - if (params.inspectorId) query.set('inspectorId', params.inspectorId); - if (params.dateFrom) query.set('dateFrom', params.dateFrom); - if (params.dateTo) query.set('dateTo', params.dateTo); - return apiRequest>(`/inspection-acts?${query}`); -} - -export function listInspectionReports(params: { - page?: number; - pageSize?: number; - search?: string; - year?: number | ''; - companyId?: string; - areaId?: string; - inspectorId?: string; - dateFrom?: string; - dateTo?: string; -} = {}) { - const query = new URLSearchParams({ - page: String(params.page ?? 1), - pageSize: String(params.pageSize ?? 25), - }); - if (params.search) query.set('search', params.search); - if (params.year) query.set('year', String(params.year)); - if (params.companyId) query.set('companyId', params.companyId); - if (params.areaId) query.set('areaId', params.areaId); - if (params.inspectorId) query.set('inspectorId', params.inspectorId); - if (params.dateFrom) query.set('dateFrom', params.dateFrom); - if (params.dateTo) query.set('dateTo', params.dateTo); - return apiRequest>(`/inspection-reports?${query}`); -} - -export function listPendingInspectionReports(params: { - page?: number; - pageSize?: number; - search?: string; - year?: number | ''; - companyId?: string; - areaId?: string; - inspectorId?: string; - dateFrom?: string; - dateTo?: string; -} = {}) { - const query = new URLSearchParams({ - page: String(params.page ?? 1), - pageSize: String(params.pageSize ?? 25), - }); - if (params.search) query.set('search', params.search); - if (params.year) query.set('year', String(params.year)); - if (params.companyId) query.set('companyId', params.companyId); - if (params.areaId) query.set('areaId', params.areaId); - if (params.inspectorId) query.set('inspectorId', params.inspectorId); - if (params.dateFrom) query.set('dateFrom', params.dateFrom); - if (params.dateTo) query.set('dateTo', params.dateTo); - return apiRequest>(`/inspection-reports/pending?${query}`); -} - -export function getInspectionReport(id: string) { - return apiRequest(`/inspection-reports/${id}`); -} - -export function getInspectionReportReview(id: string) { - return apiRequest(`/inspection-reports/${id}/review`); -} - -export function uploadInspectionReportRevision(id: string, file: File, changeSummary: string) { - const form = new FormData(); - form.append('file', file); - form.append('changeSummary', changeSummary); - return apiRequest(`/inspection-reports/${id}/review/revisions`, { method: 'POST', body: form }); -} - -export function approveInspectionReport(id: string, note?: string) { - return apiRequest(`/inspection-reports/${id}/review/approve`, { method: 'POST', body: JSON.stringify({ note: note?.trim() || undefined }) }); -} - -export function signFinalInspectionReport(id: string) { - return apiRequest(`/inspection-reports/${id}/review/sign-final`, { method: 'POST', body: JSON.stringify({ confirmation: true }) }); -} - -export function inspectionReportRevisionUrl(revisionId: string) { - return `${API_BASE}/inspection-report-revisions/${revisionId}/content`; -} - -export function getInspectionAct(id: string) { - return apiRequest(`/inspection-acts/${id}`); -} - -export function getInspectionClosure(actId: string) { - return apiRequest(`/inspection-acts/${actId}/closure`); -} - -export function getInspectionSignatureBlob(signatureId: string) { - return apiBlobRequest(`/inspection-act-signatures/${signatureId}/content`); -} - -export function getFindingCatalog(params: { categoryId?: string; search?: string } = {}) { - const query = new URLSearchParams(); - if (params.categoryId) query.set('categoryId', params.categoryId); - if (params.search) query.set('search', params.search); - return apiRequest(`/finding-catalog${query.size ? `?${query}` : ''}`); -} - -export function getFindingCatalogAdmin() { - return apiRequest('/finding-catalog/admin'); -} - -export function getApplicableFindingCatalog(assetId: string, params: { categoryId?: string; search?: string } = {}) { - const query = new URLSearchParams(); - if (params.categoryId) query.set('categoryId', params.categoryId); - if (params.search) query.set('search', params.search); - return apiRequest(`/finding-catalog/applicable/${assetId}${query.size ? `?${query}` : ''}`); -} - -export function getFindingCatalogAssetTypeSelection(assetTypeId: string) { - return apiRequest(`/finding-catalog/asset-types/${assetTypeId}/selection`); -} - -export function replaceFindingCatalogAssetTypeSelection(assetTypeId: string, input: { enabledItemIds: string[]; reason: string }) { - return apiRequest(`/finding-catalog/asset-types/${assetTypeId}/selection`, { - method: 'PUT', - body: JSON.stringify(input), - }); -} - -export function getFindingCatalogAssetSelection(assetId: string) { - return apiRequest(`/finding-catalog/assets/${assetId}/selection`); -} - -export function replaceFindingCatalogAssetSelection(assetId: string, input: { enabledItemIds: string[]; reason: string }) { - return apiRequest(`/finding-catalog/assets/${assetId}/selection`, { - method: 'PUT', - body: JSON.stringify(input), - }); -} - -export function listFindingCatalogProposals(params: { status?: FindingCatalogProposalStatus; assetTypeId?: string } = {}) { - const query = new URLSearchParams(); - if (params.status) query.set('status', params.status); - if (params.assetTypeId) query.set('assetTypeId', params.assetTypeId); - return apiRequest(`/finding-catalog/proposals${query.size ? `?${query}` : ''}`); -} - -export function reviewFindingCatalogProposal(id: string, input: { - decision: 'MATCH' | 'REJECT'; - catalogItemId?: string; - notes?: string | null; -}) { - return apiRequest(`/finding-catalog/proposals/${id}/review`, { - method: 'PATCH', - body: JSON.stringify(input), - }); -} - -export function createFindingCategory(input: { - code: string; - name: string; - sortOrder: number; -}) { - return apiRequest('/finding-catalog/categories', { - method: 'POST', - body: JSON.stringify(input), - }); -} - -export function updateFindingCategory(id: string, input: { - name?: string; - sortOrder?: number; - isActive?: boolean; -}) { - return apiRequest(`/finding-catalog/categories/${id}`, { - method: 'PATCH', - body: JSON.stringify(input), - }); -} - -export interface FindingCatalogItemInput { - categoryId: string; - sourceNumber: number; - title: string; - legalBasis?: string | null; - glossary?: string | null; - importNote?: string | null; - suggestedSeverity?: number | null; -} - -export function createFindingCatalogItem(input: FindingCatalogItemInput & { code: string }) { - return apiRequest('/finding-catalog/items', { - method: 'POST', - body: JSON.stringify(input), - }); -} - -export function updateFindingCatalogItem(id: string, input: FindingCatalogItemInput & { - isActive: boolean; -}) { - return apiRequest(`/finding-catalog/items/${id}`, { - method: 'PATCH', - body: JSON.stringify(input), - }); -} - -export async function listInspectionFindings(actId: string) { - return (await apiRequest<{ data: InspectionFinding[] }>( - `/inspection-acts/${actId}/findings`, - )).data; -} - -export function listInspectionFindingsGlobal(params: { - page?: number; - pageSize?: number; - search?: string; - workflow?: InspectionFindingWorkflow; - companyId?: string; - areaId?: string; - inspectorId?: string; - dateFrom?: string; - dateTo?: string; -} = {}) { - const query = new URLSearchParams({ - page: String(params.page ?? 1), - pageSize: String(params.pageSize ?? 25), - }); - if (params.search) query.set('search', params.search); - if (params.workflow) query.set('workflow', params.workflow); - if (params.companyId) query.set('companyId', params.companyId); - if (params.areaId) query.set('areaId', params.areaId); - if (params.inspectorId) query.set('inspectorId', params.inspectorId); - if (params.dateFrom) query.set('dateFrom', params.dateFrom); - if (params.dateTo) query.set('dateTo', params.dateTo); - return apiRequest(`/inspection-findings?${query}`); -} - -export function getInspectionFinding(id: string) { - return apiRequest(`/inspection-findings/${id}`); -} - -export function getInspectionFindingVerificationHistory(id: string) { - return apiRequest(`/inspection-findings/${id}/verification-history`); -} - -export function listVerificationPlanning(params: { - page?: number; - pageSize?: number; - search?: string; - planningStatus?: VerificationPlanningStatus; - companyId?: string; - areaId?: string; - dueFrom?: string; - dueTo?: string; -} = {}) { - const query = new URLSearchParams({ - page: String(params.page ?? 1), - pageSize: String(params.pageSize ?? 50), - }); - if (params.search) query.set('search', params.search); - if (params.planningStatus) query.set('planningStatus', params.planningStatus); - if (params.companyId) query.set('companyId', params.companyId); - if (params.areaId) query.set('areaId', params.areaId); - if (params.dueFrom) query.set('dueFrom', params.dueFrom); - if (params.dueTo) query.set('dueTo', params.dueTo); - return apiRequest(`/inspection-verifications?${query}`); -} - -export function planVerificationVisit(input: { - findingIds: string[]; - plannedStartAt: string; - notes?: string | null; -}) { - return apiRequest('/inspection-verifications/plan', { - method: 'POST', - body: JSON.stringify(input), - }); -} - -export function recordVerificationResult(visitId: string, findingId: string, input: { - outcome: InspectionVerificationOutcome; - notes: string; - verifiedAt?: string; - nextControlOn?: string; -}) { - return apiRequest<{ - findingId: string; - visitId: string; - targetControlOn: string | null; - outcome: InspectionVerificationOutcome; - notes: string; - verifiedAt: string; - recordedAt: string; - rescheduledControlOn: string | null; - nextControlOn: string | null; - findingStatus: InspectionFindingStatus; - visitStatus: InspectionVisitStatus; - }>(`/inspection-verifications/visits/${visitId}/findings/${findingId}/result`, { - method: 'PATCH', - body: JSON.stringify(input), - }); -} - -export function closeInspectionFinding(id: string, closureNotes: string) { - return apiRequest(`/inspection-findings/${id}/close`, { - method: 'PATCH', - body: JSON.stringify({ closureNotes }), - }); -} - -export function updateInspectionFindingFollowUp(id: string, input: { - responseDueBasis?: 'FINDING_DATE' | 'REPORT_NOTIFICATION' | null; - responseDueDays?: number | null; - reportNotifiedOn?: string | null; - companyResponse?: string | null; - companyResponseReceivedOn?: string | null; - companyCommittedCorrectionOn?: string | null; - nextControlOn?: string | null; -}) { - return apiRequest(`/inspection-findings/${id}/follow-up`, { - method: 'PATCH', - body: JSON.stringify(input), - }); -} - -export async function listInspectionFindingEvidence(findingId: string) { - return (await apiRequest<{ data: InspectionFindingEvidence[] }>( - `/inspection-findings/${findingId}/evidence`, - )).data; -} - -export function uploadInspectionFindingEvidence(findingId: string, input: { - file: File; - kind: InspectionEvidenceKind; - purpose: InspectionEvidencePurpose; - communicationId?: string; - verificationVisitId?: string; - title?: string; - description?: string; - capturedAt?: string; - latitude?: number; - longitude?: number; - accuracyM?: number; - deviceLabel?: string; -}) { - const body = new FormData(); - body.append('file', input.file); - body.append('kind', input.kind); - body.append('purpose', input.purpose); - if (input.communicationId) body.append('communicationId', input.communicationId); - if (input.verificationVisitId) body.append('verificationVisitId', input.verificationVisitId); - if (input.title) body.append('title', input.title); - if (input.description) body.append('description', input.description); - if (input.capturedAt) body.append('capturedAt', input.capturedAt); - if (input.latitude !== undefined) body.append('latitude', String(input.latitude)); - if (input.longitude !== undefined) body.append('longitude', String(input.longitude)); - if (input.accuracyM !== undefined) body.append('accuracyM', String(input.accuracyM)); - if (input.deviceLabel) body.append('deviceLabel', input.deviceLabel); - return apiRequest( - `/inspection-findings/${findingId}/evidence`, - { method: 'POST', body }, - ); -} - -export function getInspectionFindingEvidenceBlob(evidenceId: string, download = false) { - return apiBlobRequest( - `/inspection-finding-evidence/${evidenceId}/content${download ? '?download=1' : ''}`, - ); -} - -export async function listInspectionFindingCommunications(findingId: string) { - return (await apiRequest<{ data: InspectionFindingCommunication[] }>( - `/inspection-findings/${findingId}/communications`, - )).data; -} - -export function createInspectionFindingCommunication(findingId: string, input: { - direction: InspectionFindingCommunication['direction']; - channel: InspectionFindingCommunication['channel']; - type: InspectionFindingCommunication['type']; - occurredAt: string; - subject: string; - details?: string | null; - contactName?: string | null; - contactEmail?: string | null; -}) { - return apiRequest( - `/inspection-findings/${findingId}/communications`, - { method: 'POST', body: JSON.stringify(input) }, - ); -} - -export function listAssetImportBatches(params: { page?: number; pageSize?: number; status?: AssetImportBatchStatus | '' } = {}) { - const query = new URLSearchParams({ page: String(params.page ?? 1), pageSize: String(params.pageSize ?? 20) }); - if (params.status) query.set('status', params.status); - return apiRequest>(`/asset-imports?${query}`); -} - -export function listAssetImportReviews(params: { page?: number; pageSize?: number; kind?: 'ALL' | 'DIRECT' | 'DEPENDENCY' } = {}) { - const query = new URLSearchParams(); - if (params.page) query.set('page', String(params.page)); - if (params.pageSize) query.set('pageSize', String(params.pageSize)); - if (params.kind && params.kind !== 'ALL') query.set('kind', params.kind); - const suffix = query.toString() ? `?${query}` : ''; - return apiRequest<{ data: AssetImportReviewItem[]; meta: { page: number; pageSize: number; total: number; totalPages: number } }>(`/asset-imports/reviews${suffix}`); -} - -export function getAssetImportBatch(id: string) { - return apiRequest(`/asset-imports/${id}`); -} - -export function listAssetImportRows(id: string, params: { page?: number; pageSize?: number; status?: AssetImportRowStatus | ''; search?: string } = {}) { - const query = new URLSearchParams({ page: String(params.page ?? 1), pageSize: String(params.pageSize ?? 50) }); - if (params.status) query.set('status', params.status); - if (params.search) query.set('search', params.search); - return apiRequest>(`/asset-imports/${id}/rows?${query}`); -} - -export function uploadAssetImport(input: { file: File; sourceLabel?: string; notes?: string; profileCode?: Exclude }) { - const body = new FormData(); - body.append('file', input.file); - if (input.sourceLabel?.trim()) body.append('sourceLabel', input.sourceLabel.trim()); - if (input.notes?.trim()) body.append('notes', input.notes.trim()); - if (input.profileCode) body.append('profileCode', input.profileCode); - return apiRequest('/asset-imports', { method: 'POST', body }); -} - -export function reconcileAssetImport(id: string) { - return apiRequest(`/asset-imports/${id}/reconcile`, { method: 'POST' }); -} - -export function listAssetImportOrganizations(search?: string) { - const query = new URLSearchParams(); - if (search?.trim()) query.set('search', search.trim()); - const suffix = query.toString() ? `?${query}` : ''; - return apiRequest<{ data: AssetImportOrganizationOption[] }>(`/asset-imports/context/organizations${suffix}`); -} - -export function getAssetImportPlan(id: string) { - return apiRequest(`/asset-imports/${id}/plan`); -} - -export function generateAssetImportPlan(id: string, input: { operatorAssetId?: string; externalIdNamespace?: string }) { - return apiRequest(`/asset-imports/${id}/plan`, { method: 'POST', body: JSON.stringify(input) }); -} - -export function listAssetImportPlanItems(id: string, input: { entityKind?: AssetImportPlanEntityKind; action?: AssetImportPlanAction; page?: number; pageSize?: number } = {}) { - const query = new URLSearchParams(); - if (input.entityKind) query.set('entityKind', input.entityKind); - if (input.action) query.set('action', input.action); - if (input.page) query.set('page', String(input.page)); - if (input.pageSize) query.set('pageSize', String(input.pageSize)); - const suffix = query.toString() ? `?${query}` : ''; - return apiRequest<{ data: AssetImportPlanItem[]; meta: { page: number; pageSize: number; total: number; totalPages: number } }>(`/asset-imports/${id}/plan/items${suffix}`); -} - -export function resolveAssetImportPlanItem(id: string, itemId: string, input: { action: 'CREATE' | 'MATCH' | 'IGNORE'; matchedAssetId?: string; reason: string }) { - return apiRequest(`/asset-imports/${id}/plan/items/${itemId}/resolve`, { method: 'POST', body: JSON.stringify(input) }); -} - -export function applySafeAssetImportPlan(id: string, planHash: string) { - return apiRequest(`/asset-imports/${id}/apply-safe`, { method: 'POST', body: JSON.stringify({ planHash }) }); -} - -export function applyAssetImportPlan(id: string, planHash: string) { - return apiRequest(`/asset-imports/${id}/apply`, { method: 'POST', body: JSON.stringify({ planHash }) }); -} - -export function rollbackAssetImportPlan(id: string, reason: string) { - return apiRequest(`/asset-imports/${id}/rollback`, { method: 'POST', body: JSON.stringify({ reason }) }); -} - -export function cancelAssetImport(id: string, reason: string) { - return apiRequest(`/asset-imports/${id}/cancel`, { method: 'POST', body: JSON.stringify({ reason }) }); -} - - -export function inspectionReportWordUrl(id: string) { - return `${API_BASE}/inspection-reports/${id}/word`; -} - -export function generateInspectionReportWord(id: string) { - return apiRequest(`/inspection-reports/${id}/word`, { method: 'POST' }); -} - - -export interface DocumentDeliverySettings { officeEmail:string|null; directorEmail:string|null; updatedAt:string; smtpConfigured:boolean; mailFrom:string|null; } -export interface DocumentDeliveryItem { id:string; actId:string; reportId:string|null; documentKind:'ACT_PDF'|'REPORT_WORD'; recipientKind:'COMPANY'|'OFFICE'|'DIRECTOR'; recipientAssetId:string|null; recipientAssetName:string|null; recipientEmail:string|null; status:'PENDING'|'WAITING_RECIPIENT'|'WAITING_TRANSPORT'|'WAITING_ARTIFACT'|'SENT'|'FAILED'; attempts:number; lastAttemptAt:string|null; sentAt:string|null; providerMessageId:string|null; lastError:string|null; createdAt:string; actCode:string; reportCode:string|null; } -export function getDocumentDeliverySettings(){return apiRequest('/document-delivery/settings');} -export function updateDocumentDeliverySettings(input:{officeEmail?:string|null;directorEmail?:string|null}){return apiRequest('/document-delivery/settings',{method:'PATCH',body:JSON.stringify(input)});} -export function listDocumentDeliveries(){return apiRequest<{data:DocumentDeliveryItem[]}>('/document-delivery/outbox');} -export function retryDocumentDelivery(id:string){return apiRequest(`/document-delivery/outbox/${id}/retry`,{method:'POST'});} -export function retryPendingDocumentDeliveries(){return apiRequest<{processed:number}>('/document-delivery/outbox/retry-pending',{method:'POST'});} - -export type ActAdministrationState = 'NEW' | 'WAITING_RESPONSE' | 'DUE_SOON' | 'OVERDUE' | 'RESPONSE_RECEIVED' | 'VERIFICATION_PENDING' | 'COMMITMENT_OVERDUE' | 'REGULARIZED'; -export interface ActAdministrationQueueItem { - actId: string; actCode: string; actStatus: string; occurredAt: string; closedAt: string | null; - visitId: string; visitCode: string; areaId: string | null; companyId: string | null; areaName: string | null; companyName: string | null; - responseDueOn: string | null; deadlineReason: string | null; responseReceivedOn: string | null; committedCorrectionOn: string | null; - findingCount: number; openFindingCount: number; scheduledControlCount: number; adminState: ActAdministrationState; -} -export interface ActAdministrationQueuePage { data: ActAdministrationQueueItem[]; counters: Partial>; meta: PageMeta } -export interface ActAdministrationDetail { - act: ActAdministrationQueueItem; - deadlines: Array<{ id:string; responseDueOn:string; reason:string; createdAt:string }>; - responses: Array<{ id:string; receivedOn:string; details:string|null; committedCorrectionOn:string|null; contactName:string|null; contactEmail:string|null; originalName:string|null; sizeBytes:number|null; sha256:string|null; createdAt:string }>; - findings: Array<{ id:string; code:string; title:string; status:string; nextControlOn:string|null }>; -} -export function listActAdministrationQueue(params: { state?: ActAdministrationState | 'ALL'; areaId?: string; companyId?: string; page?: number; pageSize?: number } = {}) { - const q = new URLSearchParams({ page: String(params.page ?? 1), pageSize: String(params.pageSize ?? 25) }); - if (params.state && params.state !== 'ALL') q.set('state', params.state); - if (params.areaId) q.set('areaId', params.areaId); - if (params.companyId) q.set('companyId', params.companyId); - return apiRequest(`/act-administration/queue?${q}`); -} -export function getActAdministration(actId: string) { return apiRequest(`/inspection-acts/${actId}/administration`); } -export function setActResponseDeadline(actId: string, input: { responseDueOn: string; reason: string }) { return apiRequest(`/inspection-acts/${actId}/administration/deadline`, { method: 'PATCH', body: JSON.stringify(input) }); } -export function addActCompanyResponse(actId: string, input: { receivedOn: string; details?: string; committedCorrectionOn?: string; contactName?: string; contactEmail?: string; file?: File }) { - const body = new FormData(); body.append('receivedOn', input.receivedOn); - if (input.details) body.append('details', input.details); if (input.committedCorrectionOn) body.append('committedCorrectionOn', input.committedCorrectionOn); - if (input.contactName) body.append('contactName', input.contactName); if (input.contactEmail) body.append('contactEmail', input.contactEmail); if (input.file) body.append('file', input.file); - return apiRequest(`/inspection-acts/${actId}/administration/responses`, { method: 'POST', body }); -} -export function actCompanyResponseContentUrl(responseId: string) { return `${API_BASE}/act-company-responses/${responseId}/content`; } -export function listActAdministrationCalendar(from?: string, to?: string) { const q=new URLSearchParams(); if(from)q.set('from',from); if(to)q.set('to',to); return apiRequest<{from:string;to:string;data:Array<{type:'RESPONSE_DUE'|'COMMITMENT_DUE';date:string;actId:string;actCode:string;areaName:string|null;companyName:string|null;state:ActAdministrationState}>}>(`/act-administration/calendar${q.size?`?${q}`:''}`); } - +placeholder \ No newline at end of file