Plazo único por Acta, respuestas de empresa con PDF, fecha comprometida, estados administrativos, cola y calendario por Acta.
3132 lines
106 KiB
TypeScript
3132 lines
106 KiB
TypeScript
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<string, unknown> | null;
|
|
afterData: Record<string, unknown> | null;
|
|
metadata: Record<string, unknown> | null;
|
|
}
|
|
|
|
export interface PageMeta { page: number; pageSize: number; total: number; totalPages: number }
|
|
interface Page<T> { 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<string, unknown>;
|
|
}
|
|
|
|
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<SourceDocumentSummary & { linkId: string; relationType: AssetSourceDocumentRelationType; linkNotes: string | null }>;
|
|
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<string, unknown>;
|
|
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<string, unknown>;
|
|
normalizedData: Record<string, unknown>;
|
|
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<string, unknown>;
|
|
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<string, unknown>;
|
|
planHash: string;
|
|
generatedBy: string | null;
|
|
generatedAt: string;
|
|
appliedBy: string | null;
|
|
appliedAt: string | null;
|
|
applicationSummary: Record<string, unknown> | null;
|
|
rolledBackBy: string | null;
|
|
rolledBackAt: string | null;
|
|
rollbackSummary: Record<string, unknown> | 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<string, unknown> | null;
|
|
organizationMemberships?: Array<Record<string, unknown>>;
|
|
externalIdentifiers?: Array<Record<string, unknown>>;
|
|
sourceDocuments?: Array<Record<string, unknown>>;
|
|
legalRights?: Array<Record<string, unknown>>;
|
|
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<string, unknown>;
|
|
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<string, unknown>;
|
|
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<string, unknown>;
|
|
}
|
|
|
|
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<FindingCatalogSelectionItem & {
|
|
typeDefaultEnabled: boolean;
|
|
assetOverride: boolean | null;
|
|
}>;
|
|
}
|
|
|
|
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<InspectionFinding> {
|
|
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<VerificationPlanningItem> {
|
|
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<string, unknown>;
|
|
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<void> | 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<T>(response: Response): Promise<T> {
|
|
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<T>(path: string, init: RequestInit = {}): Promise<T> {
|
|
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<T>(response);
|
|
}
|
|
|
|
async function refreshAccess(): Promise<void> {
|
|
if (!refreshPromise) {
|
|
refreshPromise = rawRequest<AuthResponse>('/auth/refresh', { method: 'POST' })
|
|
.then((response) => { csrfToken = response.csrfToken; })
|
|
.finally(() => { refreshPromise = null; });
|
|
}
|
|
return refreshPromise;
|
|
}
|
|
|
|
export async function apiRequest<T>(
|
|
path: string,
|
|
init: RequestInit = {},
|
|
retrySession = true,
|
|
): Promise<T> {
|
|
try {
|
|
return await rawRequest<T>(path, init);
|
|
} catch (error) {
|
|
if (
|
|
retrySession && error instanceof ApiError && error.status === 401 &&
|
|
path !== '/auth/login' && path !== '/auth/refresh'
|
|
) {
|
|
try {
|
|
await refreshAccess();
|
|
return await rawRequest<T>(path, init);
|
|
} catch (refreshError) {
|
|
csrfToken = null;
|
|
window.dispatchEvent(new Event('dhv2:unauthorized'));
|
|
throw refreshError;
|
|
}
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async function rawBlobRequest(path: string): Promise<Blob> {
|
|
const response = await fetch(`${API_BASE}${path}`, {
|
|
method: 'GET',
|
|
headers: { Accept: '*/*' },
|
|
credentials: 'include',
|
|
cache: 'no-store',
|
|
});
|
|
if (!response.ok) return decodeResponse<never>(response);
|
|
return response.blob();
|
|
}
|
|
|
|
async function apiBlobRequest(path: string): Promise<Blob> {
|
|
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<HealthResponse>('/health', {}, false); }
|
|
|
|
export async function login(identifier: string, password: string) {
|
|
const response = await apiRequest<AuthResponse>('/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<DashboardSummary>('/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<Page<AdministrativeUser>>(`/users?${query}`);
|
|
}
|
|
|
|
export function getUser(id: string) { return apiRequest<AdministrativeUser>(`/users/${id}`); }
|
|
|
|
export function createUser(input: {
|
|
username: string; email?: string; firstName: string; lastName: string;
|
|
password: string; mustChangePassword: boolean; roleIds: string[];
|
|
}) {
|
|
return apiRequest<AdministrativeUser>('/users', {
|
|
method: 'POST', body: JSON.stringify(input),
|
|
});
|
|
}
|
|
|
|
export function updateUser(id: string, input: {
|
|
username?: string; email?: string | null; firstName?: string; lastName?: string;
|
|
}) {
|
|
return apiRequest<AdministrativeUser>(`/users/${id}`, {
|
|
method: 'PATCH', body: JSON.stringify(input),
|
|
});
|
|
}
|
|
|
|
export function resetUserPassword(id: string, password: string, mustChangePassword = true) {
|
|
return apiRequest<AdministrativeUser>(`/users/${id}/reset-password`, {
|
|
method: 'POST',
|
|
body: JSON.stringify({ password, mustChangePassword }),
|
|
});
|
|
}
|
|
|
|
export function updateUserStatus(id: string, status: 'ACTIVE' | 'INACTIVE') {
|
|
return apiRequest<AdministrativeUser>(`/users/${id}/status`, {
|
|
method: 'PATCH', body: JSON.stringify({ status }),
|
|
});
|
|
}
|
|
|
|
export function replaceUserRoles(id: string, roleIds: string[]) {
|
|
return apiRequest<AdministrativeUser>(`/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<AdministrativeRole>('/roles', {
|
|
method: 'POST', body: JSON.stringify(input),
|
|
});
|
|
}
|
|
|
|
export function updateRole(id: string, input: { name: string; description: string }) {
|
|
return apiRequest<AdministrativeRole>(`/roles/${id}`, {
|
|
method: 'PATCH', body: JSON.stringify(input),
|
|
});
|
|
}
|
|
|
|
export function replaceRolePermissions(id: string, permissionIds: string[]) {
|
|
return apiRequest<AdministrativeRole>(`/roles/${id}/permissions`, {
|
|
method: 'PUT', body: JSON.stringify({ permissionIds }),
|
|
});
|
|
}
|
|
|
|
export function listAudit(params: Record<string, string | number | undefined>) {
|
|
const query = new URLSearchParams();
|
|
Object.entries(params).forEach(([key, value]) => {
|
|
if (value !== undefined && value !== '') query.set(key, String(value));
|
|
});
|
|
return apiRequest<Page<AuditEventSummary>>(`/audit?${query}`);
|
|
}
|
|
|
|
export function getAuditEvent(id: string) {
|
|
return apiRequest<AuditEventDetail>(`/audit/${id}`);
|
|
}
|
|
|
|
export async function listAssetTypes() {
|
|
return (await apiRequest<{ data: AssetType[] }>('/asset-types')).data;
|
|
}
|
|
|
|
export function getMasterBootstrapStatus() {
|
|
return apiRequest<MasterBootstrapStatus>('/asset-types/bootstrap-status');
|
|
}
|
|
|
|
export function bootstrapMasterDefaults() {
|
|
return apiRequest<MasterBootstrapResult>('/asset-types/bootstrap-defaults', {
|
|
method: 'POST',
|
|
});
|
|
}
|
|
|
|
export function getMasterEnrichmentStatus() {
|
|
return apiRequest<MasterEnrichmentStatus>('/asset-types/enrichment-status');
|
|
}
|
|
|
|
export function enrichMasterDefaults() {
|
|
return apiRequest<MasterBootstrapResult>('/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<AreaCompanyRelation>('/asset-operational-relations', {
|
|
method: 'POST', body: JSON.stringify(input),
|
|
});
|
|
}
|
|
|
|
export function endAreaCompanyRelation(id: string, reason: string) {
|
|
return apiRequest<AreaCompanyRelation>(`/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<AssetType>('/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<AssetType>(`/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<AssetType>(`/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<AssetType>(`/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<Page<AssetListItem>>(`/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<AssetDetail>(`/assets/${id}`);
|
|
}
|
|
|
|
export function getAssetDossier(id: string) {
|
|
return apiRequest<AssetDossier>(`/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<string, unknown>;
|
|
}) {
|
|
return apiRequest<AssetDetail>('/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<string, unknown>;
|
|
}) {
|
|
return apiRequest<AssetDetail>(`/assets/${id}`, {
|
|
method: 'PATCH', body: JSON.stringify(input),
|
|
});
|
|
}
|
|
|
|
export function updateAssetInformationStatus(
|
|
id: string,
|
|
informationStatus: AssetInformationStatus,
|
|
) {
|
|
return apiRequest<AssetDetail>(`/assets/${id}/information-status`, {
|
|
method: 'PATCH', body: JSON.stringify({ informationStatus }),
|
|
});
|
|
}
|
|
|
|
export function updateAssetOperationalStatus(id: string, status: AssetOperationalStatus) {
|
|
return apiRequest<AssetDetail>(`/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<AssetDetail>(`/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<Page<FieldDiscovery>>(`/assets/field-discoveries?${query}`);
|
|
}
|
|
|
|
export function approveFieldDiscovery(id: string, notes?: string | null) {
|
|
return apiRequest<FieldDiscovery>(`/assets/field-discoveries/${id}/approve`, { method: 'POST', body: JSON.stringify({ notes: notes || null }) });
|
|
}
|
|
|
|
export function rejectFieldDiscovery(id: string, reason: string) {
|
|
return apiRequest<FieldDiscovery>(`/assets/field-discoveries/${id}/reject`, { method: 'POST', body: JSON.stringify({ reason }) });
|
|
}
|
|
|
|
export function matchFieldDiscovery(id: string, matchedAssetId: string, reason: string) {
|
|
return apiRequest<FieldDiscovery>(`/assets/field-discoveries/${id}/match`, { method: 'POST', body: JSON.stringify({ matchedAssetId, reason }) });
|
|
}
|
|
|
|
export function getAssetRegistry(assetId: string) {
|
|
return apiRequest<AssetRegistry>(`/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<SourceDocumentSummary>('/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<AssetGeometry>(`/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<MapAssetFeatureCollection>(`/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<Page<AssetVersionSummary>>(`/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<Page<AssetVersionSummary>>(`/assets/${assetId}/versions?${query}`);
|
|
}
|
|
|
|
export function getAssetVersion(assetId: string, versionNumber: number) {
|
|
return apiRequest<AssetVersionDetail>(`/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<AssetMedia>(`/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<AssetMedia>(`/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<AssetProvenance>(`/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<AssetProvenance>(`/assets/${assetId}/provenance`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify(input),
|
|
});
|
|
}
|
|
|
|
export function verifyAssetProvenance(assetId: string) {
|
|
return apiRequest<AssetProvenance>(`/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<Page<TemporalAssetSummary> & { asOf: string }>(
|
|
`/temporal-assets?${query}`,
|
|
);
|
|
}
|
|
|
|
export function getTemporalAsset(assetId: string, at: string) {
|
|
const query = new URLSearchParams({ at });
|
|
return apiRequest<TemporalAssetDetail>(`/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<Page<SurveyCampaignListItem>>(`/survey-campaigns?${query}`);
|
|
}
|
|
|
|
export function getSurveyCampaign(id: string) {
|
|
return apiRequest<SurveyCampaign>(`/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<SurveyCampaign>('/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<SurveyCampaign>(`/survey-campaigns/${id}`, {
|
|
method: 'PATCH',
|
|
body: JSON.stringify(input),
|
|
});
|
|
}
|
|
|
|
export function updateSurveyCampaignStatus(id: string, status: SurveyCampaignStatus) {
|
|
return apiRequest<SurveyCampaign>(`/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<SurveyCampaign>(`/survey-campaigns/${campaignId}/targets`, {
|
|
method: 'POST',
|
|
body: JSON.stringify(input),
|
|
});
|
|
}
|
|
|
|
export function updateSurveyTarget(id: string, input: {
|
|
dueAt?: string | null;
|
|
instructions?: string | null;
|
|
}) {
|
|
return apiRequest<SurveyCampaign>(`/survey-campaign-targets/${id}`, {
|
|
method: 'PATCH',
|
|
body: JSON.stringify(input),
|
|
});
|
|
}
|
|
|
|
export function assignSurveyTarget(id: string, assignedUserId: string | null) {
|
|
return apiRequest<SurveyCampaign>(`/survey-campaign-targets/${id}/assignment`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify({ assignedUserId }),
|
|
});
|
|
}
|
|
|
|
export function updateSurveyTargetStatus(id: string, status: SurveyTargetStatus) {
|
|
return apiRequest<SurveyCampaign>(`/survey-campaign-targets/${id}/status`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify({ status }),
|
|
});
|
|
}
|
|
|
|
export function getSurveyExecution(targetId: string) {
|
|
return apiRequest<SurveyExecution>(`/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<SurveyExecution>(`/survey-campaign-targets/${targetId}/report`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify(input),
|
|
});
|
|
}
|
|
|
|
export function submitSurveyReport(targetId: string) {
|
|
return apiRequest<SurveyExecution>(`/survey-campaign-targets/${targetId}/report/submit`, {
|
|
method: 'POST',
|
|
});
|
|
}
|
|
|
|
export function reviewSurveyReport(
|
|
targetId: string,
|
|
decision: 'APPROVE' | 'REJECT',
|
|
notes: string | null,
|
|
) {
|
|
return apiRequest<SurveyExecution>(`/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<Page<InspectionVisitListItem>>(`/inspection-visits?${query}`);
|
|
}
|
|
|
|
export function getInspectionVisit(id: string) {
|
|
return apiRequest<InspectionVisit>(`/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<InspectionVisit>('/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<InspectionVisit>(`/inspection-visits/${id}`, {
|
|
method: 'PATCH',
|
|
body: JSON.stringify(input),
|
|
});
|
|
}
|
|
|
|
export function replaceInspectionVisitAssets(id: string, assetIds: string[]) {
|
|
return apiRequest<InspectionVisit>(`/inspection-visits/${id}/assets`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify({ assetIds }),
|
|
});
|
|
}
|
|
|
|
export function generateInspectionVisitChecklist(id: string) {
|
|
return apiRequest<InspectionVisit>(`/inspection-visits/${id}/checklist/generate`, {
|
|
method: 'POST',
|
|
});
|
|
}
|
|
|
|
export function excludeInspectionVisitAsset(id: string, assetId: string, reason: string) {
|
|
return apiRequest<InspectionVisit>(`/inspection-visits/${id}/assets/${assetId}/exclude`, {
|
|
method: 'POST',
|
|
body: JSON.stringify({ reason }),
|
|
});
|
|
}
|
|
|
|
export function includeInspectionVisitAsset(id: string, assetId: string) {
|
|
return apiRequest<InspectionVisit>(`/inspection-visits/${id}/assets/${assetId}/include`, {
|
|
method: 'POST',
|
|
});
|
|
}
|
|
|
|
export function replaceInspectionVisitTeam(
|
|
id: string,
|
|
leadInspectorUserId: string | null,
|
|
memberUserIds: string[],
|
|
) {
|
|
return apiRequest<InspectionVisit>(`/inspection-visits/${id}/team`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify({ leadInspectorUserId, memberUserIds }),
|
|
});
|
|
}
|
|
|
|
export function updateInspectionVisitStatus(
|
|
id: string,
|
|
status: InspectionVisitStatus,
|
|
reason?: string | null,
|
|
) {
|
|
return apiRequest<InspectionVisit>(`/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<Page<InspectionActListItem>>(
|
|
`/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<Page<InspectionActListItem>>(`/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<Page<InspectionReportListItem>>(`/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<Page<PendingInspectionReportItem>>(`/inspection-reports/pending?${query}`);
|
|
}
|
|
|
|
export function getInspectionReport(id: string) {
|
|
return apiRequest<InspectionReport>(`/inspection-reports/${id}`);
|
|
}
|
|
|
|
export function getInspectionReportReview(id: string) {
|
|
return apiRequest<InspectionReportReview>(`/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<InspectionReportReview>(`/inspection-reports/${id}/review/revisions`, { method: 'POST', body: form });
|
|
}
|
|
|
|
export function approveInspectionReport(id: string, note?: string) {
|
|
return apiRequest<InspectionReportReview>(`/inspection-reports/${id}/review/approve`, { method: 'POST', body: JSON.stringify({ note: note?.trim() || undefined }) });
|
|
}
|
|
|
|
export function signFinalInspectionReport(id: string) {
|
|
return apiRequest<InspectionReportReview>(`/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<InspectionAct>(`/inspection-acts/${id}`);
|
|
}
|
|
|
|
export function getInspectionClosure(actId: string) {
|
|
return apiRequest<InspectionClosure>(`/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<FindingCatalog>(`/finding-catalog${query.size ? `?${query}` : ''}`);
|
|
}
|
|
|
|
export function getFindingCatalogAdmin() {
|
|
return apiRequest<FindingAdminCatalog>('/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<FindingCatalogApplicable>(`/finding-catalog/applicable/${assetId}${query.size ? `?${query}` : ''}`);
|
|
}
|
|
|
|
export function getFindingCatalogAssetTypeSelection(assetTypeId: string) {
|
|
return apiRequest<FindingCatalogAssetTypeSelection>(`/finding-catalog/asset-types/${assetTypeId}/selection`);
|
|
}
|
|
|
|
export function replaceFindingCatalogAssetTypeSelection(assetTypeId: string, input: { enabledItemIds: string[]; reason: string }) {
|
|
return apiRequest<FindingCatalogAssetTypeSelection>(`/finding-catalog/asset-types/${assetTypeId}/selection`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify(input),
|
|
});
|
|
}
|
|
|
|
export function getFindingCatalogAssetSelection(assetId: string) {
|
|
return apiRequest<FindingCatalogAssetSelection>(`/finding-catalog/assets/${assetId}/selection`);
|
|
}
|
|
|
|
export function replaceFindingCatalogAssetSelection(assetId: string, input: { enabledItemIds: string[]; reason: string }) {
|
|
return apiRequest<FindingCatalogAssetSelection>(`/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<FindingCatalogProposal[]>(`/finding-catalog/proposals${query.size ? `?${query}` : ''}`);
|
|
}
|
|
|
|
export function reviewFindingCatalogProposal(id: string, input: {
|
|
decision: 'MATCH' | 'REJECT';
|
|
catalogItemId?: string;
|
|
notes?: string | null;
|
|
}) {
|
|
return apiRequest<FindingCatalogProposal>(`/finding-catalog/proposals/${id}/review`, {
|
|
method: 'PATCH',
|
|
body: JSON.stringify(input),
|
|
});
|
|
}
|
|
|
|
export function createFindingCategory(input: {
|
|
code: string;
|
|
name: string;
|
|
sortOrder: number;
|
|
}) {
|
|
return apiRequest<FindingAdminCategory>('/finding-catalog/categories', {
|
|
method: 'POST',
|
|
body: JSON.stringify(input),
|
|
});
|
|
}
|
|
|
|
export function updateFindingCategory(id: string, input: {
|
|
name?: string;
|
|
sortOrder?: number;
|
|
isActive?: boolean;
|
|
}) {
|
|
return apiRequest<FindingAdminCategory>(`/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<FindingAdminCatalogItem>('/finding-catalog/items', {
|
|
method: 'POST',
|
|
body: JSON.stringify(input),
|
|
});
|
|
}
|
|
|
|
export function updateFindingCatalogItem(id: string, input: FindingCatalogItemInput & {
|
|
isActive: boolean;
|
|
}) {
|
|
return apiRequest<FindingAdminCatalogItem>(`/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<InspectionFindingGlobalPage>(`/inspection-findings?${query}`);
|
|
}
|
|
|
|
export function getInspectionFinding(id: string) {
|
|
return apiRequest<InspectionFinding>(`/inspection-findings/${id}`);
|
|
}
|
|
|
|
export function getInspectionFindingVerificationHistory(id: string) {
|
|
return apiRequest<InspectionFindingVerificationEvent[]>(`/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<VerificationPlanningPage>(`/inspection-verifications?${query}`);
|
|
}
|
|
|
|
export function planVerificationVisit(input: {
|
|
findingIds: string[];
|
|
plannedStartAt: string;
|
|
notes?: string | null;
|
|
}) {
|
|
return apiRequest<PlannedVerificationVisitResult>('/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<InspectionFinding>(`/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<InspectionFinding>(`/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<InspectionFindingEvidence>(
|
|
`/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<InspectionFindingCommunication>(
|
|
`/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<Page<AssetImportBatch>>(`/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<AssetImportBatchDetail>(`/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<Page<AssetImportRow>>(`/asset-imports/${id}/rows?${query}`);
|
|
}
|
|
|
|
export function uploadAssetImport(input: { file: File; sourceLabel?: string; notes?: string; profileCode?: Exclude<AssetImportProfileCode, 'UNKNOWN'> }) {
|
|
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<AssetImportBatch>('/asset-imports', { method: 'POST', body });
|
|
}
|
|
|
|
export function reconcileAssetImport(id: string) {
|
|
return apiRequest<AssetImportBatch>(`/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<AssetImportPlan | null>(`/asset-imports/${id}/plan`);
|
|
}
|
|
|
|
export function generateAssetImportPlan(id: string, input: { operatorAssetId?: string; externalIdNamespace?: string }) {
|
|
return apiRequest<AssetImportPlan>(`/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<AssetImportPlan>(`/asset-imports/${id}/plan/items/${itemId}/resolve`, { method: 'POST', body: JSON.stringify(input) });
|
|
}
|
|
|
|
export function applySafeAssetImportPlan(id: string, planHash: string) {
|
|
return apiRequest<AssetImportPlan>(`/asset-imports/${id}/apply-safe`, { method: 'POST', body: JSON.stringify({ planHash }) });
|
|
}
|
|
|
|
export function applyAssetImportPlan(id: string, planHash: string) {
|
|
return apiRequest<AssetImportPlan>(`/asset-imports/${id}/apply`, { method: 'POST', body: JSON.stringify({ planHash }) });
|
|
}
|
|
|
|
export function rollbackAssetImportPlan(id: string, reason: string) {
|
|
return apiRequest<AssetImportPlan>(`/asset-imports/${id}/rollback`, { method: 'POST', body: JSON.stringify({ reason }) });
|
|
}
|
|
|
|
export function cancelAssetImport(id: string, reason: string) {
|
|
return apiRequest<AssetImportBatch>(`/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<InspectionReport>(`/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<DocumentDeliverySettings>('/document-delivery/settings');}
|
|
export function updateDocumentDeliverySettings(input:{officeEmail?:string|null;directorEmail?:string|null}){return apiRequest<DocumentDeliverySettings>('/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<DocumentDeliveryItem>(`/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<Record<ActAdministrationState, number>>; 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<ActAdministrationQueuePage>(`/act-administration/queue?${q}`);
|
|
}
|
|
export function getActAdministration(actId: string) { return apiRequest<ActAdministrationDetail>(`/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}`:''}`); }
|
|
|