Implemented data export
This commit is contained in:
@@ -20,6 +20,8 @@ import {
|
||||
CreateProgrammeRequest,
|
||||
UpdateProgrammeRequest,
|
||||
Instrument,
|
||||
CreateInstrumentRequest,
|
||||
UpdateInstrumentRequest,
|
||||
Instruction,
|
||||
CreateInstructionRequest,
|
||||
UpdateInstructionRequest,
|
||||
@@ -225,7 +227,7 @@ export const updateOrganisation = async (payload: UpdateOrganisationRequest): Pr
|
||||
return res.data;
|
||||
};
|
||||
|
||||
export const getPortfolios = async (organisationId?: string | number): Promise<Portfolio[]> => {
|
||||
export const getOrganisationPortfolios = async (organisationId?: string | number): Promise<Portfolio[]> => {
|
||||
const targetOrgId =
|
||||
organisationId !== undefined && organisationId !== '' && organisationId !== null
|
||||
? organisationId
|
||||
@@ -235,8 +237,31 @@ export const getPortfolios = async (organisationId?: string | number): Promise<P
|
||||
return [];
|
||||
}
|
||||
const hashedOrgId = await toolsService.resolveOrganisationId(targetOrgId);
|
||||
const res = await apiClient.get<Portfolio[]>(`/v1/api/governance/organisations/${hashedOrgId}/portfolio`);
|
||||
return res.data || [];
|
||||
try {
|
||||
const res = await apiClient.get<Portfolio[]>(`/v1/api/governance/organisations/${hashedOrgId}/portfolio`);
|
||||
return res.data || [];
|
||||
} catch (err) {
|
||||
// If hashed ID fails or is not recognized, attempt with raw targetOrgId
|
||||
if (String(targetOrgId) !== String(hashedOrgId)) {
|
||||
try {
|
||||
const fallbackRes = await apiClient.get<Portfolio[]>(`/v1/api/governance/organisations/${targetOrgId}/portfolio`);
|
||||
return fallbackRes.data || [];
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
console.warn('[getOrganisationPortfolios] Failed to retrieve portfolios for organisation:', err);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
export const getPortfolios = async (
|
||||
arg?: string | number | Pagination
|
||||
): Promise<Portfolio[]> => {
|
||||
if (arg === undefined || arg === null || typeof arg === 'object') {
|
||||
return getAllPortfolios(typeof arg === 'object' ? arg : undefined);
|
||||
}
|
||||
return getOrganisationPortfolios(arg);
|
||||
};
|
||||
|
||||
export const getPortfolio = async (portfolioId: string | number): Promise<Portfolio> => {
|
||||
@@ -245,23 +270,42 @@ export const getPortfolio = async (portfolioId: string | number): Promise<Portfo
|
||||
return Array.isArray(res.data) ? res.data[0] : res.data;
|
||||
};
|
||||
|
||||
export const getAllPortfolios = async (): Promise<Portfolio[]> => {
|
||||
export const getAllPortfolios = async (
|
||||
pagination: Pagination = { page: 1, pageSize: 100, index: 0 }
|
||||
): Promise<Portfolio[]> => {
|
||||
try {
|
||||
const orgs = await getOrganisations(1, 10);
|
||||
if (orgs.length > 0) {
|
||||
const results = await Promise.allSettled(
|
||||
orgs.map((org) => getPortfolios(org.id))
|
||||
);
|
||||
const combined = results
|
||||
.filter((r): r is PromiseFulfilledResult<Portfolio[]> => r.status === 'fulfilled')
|
||||
.flatMap((r) => r.value);
|
||||
if (combined.length > 0) return combined;
|
||||
const res = await apiClient.post<Portfolio[]>('/v1/api/governance/organisations/portfolios', {
|
||||
index: pagination.index ? toIntegerId(pagination.index) : 0,
|
||||
page: pagination.page || 1,
|
||||
pageSize: pagination.pageSize || 100,
|
||||
});
|
||||
if (Array.isArray(res.data)) {
|
||||
return res.data;
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('[getAllPortfolios] POST /v1/api/governance/organisations/portfolios failed, attempting fallback:', err);
|
||||
try {
|
||||
const getRes = await apiClient.get<Portfolio[]>('/v1/api/governance/organisations/portfolios', {
|
||||
params: { page: pagination.page || 1, pageSize: pagination.pageSize || 100 },
|
||||
});
|
||||
if (Array.isArray(getRes.data)) {
|
||||
return getRes.data;
|
||||
}
|
||||
} catch {
|
||||
// Fallback to iterating organisations if bulk endpoint is unavailable
|
||||
const orgs = await getOrganisations(1, 10);
|
||||
if (orgs.length > 0) {
|
||||
const results = await Promise.allSettled(
|
||||
orgs.map((org) => getOrganisationPortfolios(org.id))
|
||||
);
|
||||
const combined = results
|
||||
.filter((r): r is PromiseFulfilledResult<Portfolio[]> => r.status === 'fulfilled')
|
||||
.flatMap((r) => r.value);
|
||||
if (combined.length > 0) return combined;
|
||||
}
|
||||
}
|
||||
const res = await apiClient.get<Portfolio[]>('/v1/api/governance/organisations/portfolio');
|
||||
return res.data || [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
export const getPortfolioProgrammes = async (portfolioId: string | number): Promise<Programme[]> => {
|
||||
@@ -314,7 +358,30 @@ export const getProgrammes = async (portfolioId: string | number): Promise<Progr
|
||||
|
||||
const LOCAL_INSTRUCTIONS_KEY = 'pfm_local_instructions';
|
||||
|
||||
export const getStoredInstructions = (): Instruction[] => {
|
||||
export const purgeStoredInstructionDuplicates = (apiItems: Instrument[]): void => {
|
||||
if (typeof window === 'undefined' || !Array.isArray(apiItems) || apiItems.length === 0) return;
|
||||
try {
|
||||
const raw = localStorage.getItem(LOCAL_INSTRUCTIONS_KEY);
|
||||
if (!raw) return;
|
||||
const local: Instrument[] = JSON.parse(raw);
|
||||
if (!Array.isArray(local) || local.length === 0) return;
|
||||
|
||||
const apiIds = new Set(apiItems.map((i) => String(i.id)));
|
||||
const apiKeys = new Set(apiItems.map((i) => `${i.programmeId}::${(i.name || '').toLowerCase().trim()}`));
|
||||
|
||||
const remaining = local.filter(
|
||||
(l) => !apiIds.has(String(l.id)) && !apiKeys.has(`${l.programmeId}::${(l.name || '').toLowerCase().trim()}`)
|
||||
);
|
||||
|
||||
if (remaining.length !== local.length) {
|
||||
localStorage.setItem(LOCAL_INSTRUCTIONS_KEY, JSON.stringify(remaining));
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Failed to purge stored instruction duplicates', e);
|
||||
}
|
||||
};
|
||||
|
||||
export const getStoredInstructions = (): Instrument[] => {
|
||||
if (typeof window === 'undefined') return [];
|
||||
try {
|
||||
const raw = localStorage.getItem(LOCAL_INSTRUCTIONS_KEY);
|
||||
@@ -324,11 +391,14 @@ export const getStoredInstructions = (): Instruction[] => {
|
||||
}
|
||||
};
|
||||
|
||||
export const saveStoredInstruction = (inst: Instruction): void => {
|
||||
export const saveStoredInstruction = (inst: Instrument): void => {
|
||||
if (typeof window === 'undefined') return;
|
||||
try {
|
||||
const current = getStoredInstructions();
|
||||
const idx = current.findIndex((i) => String(i.id) === String(inst.id));
|
||||
const idx = current.findIndex(
|
||||
(i) => String(i.id) === String(inst.id) ||
|
||||
(String(i.programmeId) === String(inst.programmeId) && (i.name || '').trim().toLowerCase() === (inst.name || '').trim().toLowerCase())
|
||||
);
|
||||
if (idx >= 0) {
|
||||
current[idx] = { ...current[idx], ...inst, updatedAt: new Date().toISOString() };
|
||||
} else {
|
||||
@@ -340,31 +410,35 @@ export const saveStoredInstruction = (inst: Instruction): void => {
|
||||
}
|
||||
};
|
||||
|
||||
export const getInstructions = async (programmeId: string | number): Promise<Instruction[]> => {
|
||||
let apiItems: Instruction[] = [];
|
||||
export const getInstruments = async (programmeId: string | number): Promise<Instrument[]> => {
|
||||
let apiItems: Instrument[] = [];
|
||||
let apiSucceeded = false;
|
||||
try {
|
||||
const hashedProgId = await toolsService.resolveProgrammeId(programmeId);
|
||||
const res = await apiClient.get<Instruction[]>(`/v1/api/governance/instruments/${hashedProgId}`);
|
||||
apiItems = res.data || [];
|
||||
const res = await apiClient.get<Instrument[]>(`/v1/api/governance/instruments/${hashedProgId}`);
|
||||
if (Array.isArray(res.data)) {
|
||||
apiItems = res.data;
|
||||
apiSucceeded = true;
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(`[getInstructions] Could not fetch instruments from API for prog ${programmeId}:`, err);
|
||||
console.warn(`[getInstruments] Could not fetch instruments from API for prog ${programmeId}:`, err);
|
||||
}
|
||||
|
||||
if (apiSucceeded) {
|
||||
purgeStoredInstructionDuplicates(apiItems);
|
||||
return apiItems;
|
||||
}
|
||||
|
||||
// Fallback to local items only when API failed
|
||||
const localItems = getStoredInstructions().filter(
|
||||
(item) => String(item.programmeId) === String(programmeId)
|
||||
);
|
||||
|
||||
const map = new Map<string, Instruction>();
|
||||
apiItems.forEach((item) => map.set(String(item.id), item));
|
||||
localItems.forEach((item) => map.set(String(item.id), item));
|
||||
|
||||
return Array.from(map.values());
|
||||
return localItems;
|
||||
};
|
||||
|
||||
export const getInstruments = getInstructions;
|
||||
export const getInstructions = getInstruments;
|
||||
|
||||
export const getAllInstructions = async (programmesList?: Programme[]): Promise<Instruction[]> => {
|
||||
const localItems = getStoredInstructions();
|
||||
export const getAllInstruments = async (programmesList?: Programme[]): Promise<Instrument[]> => {
|
||||
let progList = programmesList;
|
||||
if (!progList || progList.length === 0) {
|
||||
try {
|
||||
@@ -374,66 +448,103 @@ export const getAllInstructions = async (programmesList?: Programme[]): Promise<
|
||||
}
|
||||
}
|
||||
|
||||
const map = new Map<string, Instruction>();
|
||||
localItems.forEach((item) => map.set(String(item.id), item));
|
||||
const map = new Map<string, Instrument>();
|
||||
let hasApiData = false;
|
||||
|
||||
if (progList && progList.length > 0) {
|
||||
const results = await Promise.allSettled(
|
||||
progList.map((pr) => getInstructions(pr.id))
|
||||
progList.map((pr) => getInstruments(pr.id))
|
||||
);
|
||||
results.forEach((res) => {
|
||||
if (res.status === 'fulfilled' && Array.isArray(res.value)) {
|
||||
if (res.status === 'fulfilled' && Array.isArray(res.value) && res.value.length > 0) {
|
||||
hasApiData = true;
|
||||
res.value.forEach((inst) => map.set(String(inst.id), inst));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return Array.from(map.values());
|
||||
if (hasApiData || map.size > 0) {
|
||||
const allApiInstruments = Array.from(map.values());
|
||||
purgeStoredInstructionDuplicates(allApiInstruments);
|
||||
return allApiInstruments;
|
||||
}
|
||||
|
||||
return getStoredInstructions();
|
||||
};
|
||||
|
||||
export const createInstruction = async (payload: CreateInstructionRequest): Promise<Instruction> => {
|
||||
const newId = Date.now();
|
||||
const newInstruction: Instruction = {
|
||||
id: newId,
|
||||
export const getAllInstructions = getAllInstruments;
|
||||
|
||||
export const createInstrument = async (payload: CreateInstrumentRequest): Promise<Instrument> => {
|
||||
const code = payload.code || `INST-${Date.now().toString().slice(-4)}`;
|
||||
const body = {
|
||||
programmeId: toIntegerId(payload.programmeId),
|
||||
code: payload.code || `INST-${String(newId).slice(-4)}`,
|
||||
name: payload.name,
|
||||
code: code,
|
||||
description: payload.description,
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await apiClient.post<Instrument>('/v1/api/governance/instruments', body);
|
||||
if (res.data && res.data.id) {
|
||||
purgeStoredInstructionDuplicates([res.data]);
|
||||
return res.data;
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.warn('[createInstrument] API create failed, falling back to local storage:', err);
|
||||
const newId = Date.now();
|
||||
const fallbackInstruction: Instrument = {
|
||||
id: newId,
|
||||
programmeId: toIntegerId(payload.programmeId),
|
||||
code: code,
|
||||
name: payload.name,
|
||||
description: payload.description,
|
||||
status: payload.status !== undefined ? Number(payload.status) : ActivityStatus.Active,
|
||||
createdBy: payload.userId || 'system',
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
saveStoredInstruction(fallbackInstruction);
|
||||
return fallbackInstruction;
|
||||
}
|
||||
|
||||
// If API returned 200 without full body, return formatted item without saving duplicate to localStorage
|
||||
return {
|
||||
id: Date.now(),
|
||||
programmeId: toIntegerId(payload.programmeId),
|
||||
code: code,
|
||||
name: payload.name,
|
||||
description: payload.description,
|
||||
status: payload.status !== undefined ? Number(payload.status) : ActivityStatus.Active,
|
||||
createdBy: payload.userId || 'system',
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
try {
|
||||
await apiClient.post('/v1/api/governance/instruments', {
|
||||
programmeId: toIntegerId(payload.programmeId),
|
||||
code: newInstruction.code,
|
||||
name: newInstruction.name,
|
||||
description: newInstruction.description,
|
||||
status: newInstruction.status,
|
||||
});
|
||||
} catch (err: any) {
|
||||
try {
|
||||
await apiClient.post('/v1/api/governance/instructions', {
|
||||
programmeId: toIntegerId(payload.programmeId),
|
||||
code: newInstruction.code,
|
||||
name: newInstruction.name,
|
||||
description: newInstruction.description,
|
||||
status: newInstruction.status,
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn('[createInstruction] API create failed, persisted locally:', e);
|
||||
}
|
||||
}
|
||||
|
||||
saveStoredInstruction(newInstruction);
|
||||
return newInstruction;
|
||||
};
|
||||
|
||||
export const updateInstruction = async (payload: UpdateInstructionRequest): Promise<Instruction> => {
|
||||
const existing = getStoredInstructions().find((i) => String(i.id) === String(payload.instructionId));
|
||||
const updatedInstruction: Instruction = {
|
||||
id: payload.instructionId,
|
||||
export const createInstruction = createInstrument;
|
||||
|
||||
export const updateInstrument = async (payload: UpdateInstrumentRequest): Promise<Instrument> => {
|
||||
const targetId = payload.instructionId ?? payload.instrumentId ?? payload.id;
|
||||
const numId = toIntegerId(targetId);
|
||||
|
||||
const body = {
|
||||
instructionId: numId,
|
||||
code: payload.code || '',
|
||||
name: payload.name,
|
||||
description: payload.description,
|
||||
status: Number(payload.status),
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await apiClient.put<Instrument>('/v1/api/governance/instruments', body);
|
||||
if (res.data && res.data.id) {
|
||||
return res.data;
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('[updateInstrument] API update failed:', err);
|
||||
}
|
||||
|
||||
const existing = getStoredInstructions().find((i) => String(i.id) === String(targetId));
|
||||
const updatedInstrument: Instrument = {
|
||||
id: targetId!,
|
||||
programmeId: payload.programmeId ? toIntegerId(payload.programmeId) : (existing?.programmeId || 0),
|
||||
code: payload.code || existing?.code || null,
|
||||
name: payload.name,
|
||||
@@ -445,35 +556,14 @@ export const updateInstruction = async (payload: UpdateInstructionRequest): Prom
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
try {
|
||||
await apiClient.put('/v1/api/governance/instruments', {
|
||||
instrumentId: toIntegerId(payload.instructionId),
|
||||
instructionId: toIntegerId(payload.instructionId),
|
||||
programmeId: payload.programmeId ? toIntegerId(payload.programmeId) : undefined,
|
||||
code: payload.code,
|
||||
name: payload.name,
|
||||
description: payload.description,
|
||||
status: Number(payload.status),
|
||||
});
|
||||
} catch (err) {
|
||||
try {
|
||||
await apiClient.put('/v1/api/governance/instructions', {
|
||||
instructionId: toIntegerId(payload.instructionId),
|
||||
programmeId: payload.programmeId ? toIntegerId(payload.programmeId) : undefined,
|
||||
code: payload.code,
|
||||
name: payload.name,
|
||||
description: payload.description,
|
||||
status: Number(payload.status),
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn('[updateInstruction] API update failed, updated locally:', e);
|
||||
}
|
||||
if (existing) {
|
||||
saveStoredInstruction(updatedInstrument);
|
||||
}
|
||||
|
||||
saveStoredInstruction(updatedInstruction);
|
||||
return updatedInstruction;
|
||||
return updatedInstrument;
|
||||
};
|
||||
|
||||
export const updateInstruction = updateInstrument;
|
||||
|
||||
// ==========================================
|
||||
// 3. Project & Milestone Management APIs
|
||||
// ==========================================
|
||||
@@ -1610,6 +1700,29 @@ export const updateSiteVisit = async (payload: UpdateSiteVisitRequest): Promise<
|
||||
|
||||
const LOCAL_RULES_KEY = 'pfm_local_rules';
|
||||
|
||||
export const purgeStoredRuleDuplicates = (apiRules: Rule[]): void => {
|
||||
if (typeof window === 'undefined' || !Array.isArray(apiRules) || apiRules.length === 0) return;
|
||||
try {
|
||||
const raw = localStorage.getItem(LOCAL_RULES_KEY);
|
||||
if (!raw) return;
|
||||
const local: Rule[] = JSON.parse(raw);
|
||||
if (!Array.isArray(local) || local.length === 0) return;
|
||||
|
||||
const apiIds = new Set(apiRules.map((r) => String(r.id)));
|
||||
const apiKeys = new Set(apiRules.map((r) => `${r.programmeId}::${(r.name || '').toLowerCase().trim()}`));
|
||||
|
||||
const remaining = local.filter(
|
||||
(l) => !apiIds.has(String(l.id)) && !apiKeys.has(`${l.programmeId}::${(l.name || '').toLowerCase().trim()}`)
|
||||
);
|
||||
|
||||
if (remaining.length !== local.length) {
|
||||
localStorage.setItem(LOCAL_RULES_KEY, JSON.stringify(remaining));
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Failed to purge stored rule duplicates', e);
|
||||
}
|
||||
};
|
||||
|
||||
export const getStoredRules = (): Rule[] => {
|
||||
if (typeof window === 'undefined') return [];
|
||||
try {
|
||||
@@ -1624,7 +1737,10 @@ export const saveStoredRule = (rule: Rule): void => {
|
||||
if (typeof window === 'undefined') return;
|
||||
try {
|
||||
const current = getStoredRules();
|
||||
const idx = current.findIndex((r) => String(r.id) === String(rule.id));
|
||||
const idx = current.findIndex(
|
||||
(r) => String(r.id) === String(rule.id) ||
|
||||
(String(r.programmeId) === String(rule.programmeId) && (r.name || '').trim().toLowerCase() === (rule.name || '').trim().toLowerCase())
|
||||
);
|
||||
if (idx >= 0) {
|
||||
current[idx] = { ...current[idx], ...rule, updatedAt: new Date().toISOString() };
|
||||
} else {
|
||||
@@ -1640,15 +1756,28 @@ export const saveStoredRule = (rule: Rule): void => {
|
||||
// 15. Execution: Programme Rules APIs
|
||||
// ==========================================
|
||||
export const getRule = async (ruleId: string | number): Promise<Rule> => {
|
||||
const localRule = getStoredRules().find((r) => String(r.id) === String(ruleId));
|
||||
try {
|
||||
const hashedRuleId = await toolsService.encodeId(ruleId);
|
||||
const res = await apiClient.get<Rule>(`/v1/api/execution/rules/${hashedRuleId}`);
|
||||
return res.data || localRule!;
|
||||
if (res.data) {
|
||||
purgeStoredRuleDuplicates([res.data]);
|
||||
return res.data;
|
||||
}
|
||||
} catch {
|
||||
const localRule = getStoredRules().find((r) => String(r.id) === String(ruleId));
|
||||
if (localRule) return localRule;
|
||||
throw new Error('Rule not found');
|
||||
}
|
||||
throw new Error('Rule not found');
|
||||
};
|
||||
|
||||
const toValidUuid = (userId?: string | null): string => {
|
||||
if (!userId) return '00000000-0000-0000-0000-000000000000';
|
||||
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
||||
if (uuidPattern.test(userId)) return userId;
|
||||
const userSub = getCurrentUserSub();
|
||||
if (userSub && uuidPattern.test(userSub)) return userSub;
|
||||
return '00000000-0000-0000-0000-000000000000';
|
||||
};
|
||||
|
||||
export const getRules = async (
|
||||
@@ -1656,23 +1785,33 @@ export const getRules = async (
|
||||
programmeId?: string | number
|
||||
): Promise<Rule[]> => {
|
||||
let apiRules: Rule[] = [];
|
||||
let apiSucceeded = false;
|
||||
try {
|
||||
const res = await apiClient.post<Rule[]>('/v1/api/execution/programmes/rules', {
|
||||
index: pagination.index ? toIntegerId(pagination.index) : 0,
|
||||
page: pagination.page || 1,
|
||||
pageSize: pagination.pageSize || 100,
|
||||
});
|
||||
apiRules = res.data || [];
|
||||
if (Array.isArray(res.data)) {
|
||||
apiRules = res.data;
|
||||
apiSucceeded = true;
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('[getRules] Error fetching from backend API:', err);
|
||||
}
|
||||
|
||||
const localRules = getStoredRules();
|
||||
const map = new Map<string, Rule>();
|
||||
apiRules.forEach((r) => map.set(String(r.id), r));
|
||||
localRules.forEach((r) => map.set(String(r.id), r));
|
||||
if (apiSucceeded) {
|
||||
purgeStoredRuleDuplicates(apiRules);
|
||||
let result = apiRules;
|
||||
if (programmeId != null && programmeId !== '' && programmeId !== 'all') {
|
||||
result = result.filter((r) => String(r.programmeId) === String(programmeId));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
let combined = Array.from(map.values());
|
||||
// Fallback to local storage only if API call failed
|
||||
const localRules = getStoredRules();
|
||||
let combined = localRules;
|
||||
if (programmeId != null && programmeId !== '' && programmeId !== 'all') {
|
||||
combined = combined.filter((r) => String(r.programmeId) === String(programmeId));
|
||||
}
|
||||
@@ -1680,60 +1819,76 @@ export const getRules = async (
|
||||
};
|
||||
|
||||
export const createRule = async (payload: CreateRuleRequest): Promise<Rule> => {
|
||||
const newId = Date.now();
|
||||
const newRule: Rule = {
|
||||
id: newId,
|
||||
const validUserId = toValidUuid(payload.userId);
|
||||
const body = {
|
||||
programmeId: toIntegerId(payload.programmeId),
|
||||
userId: validUserId,
|
||||
name: payload.name,
|
||||
description: payload.description,
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await apiClient.post<Rule>('/v1/api/execution/rules', body);
|
||||
if (res.data && res.data.id) {
|
||||
purgeStoredRuleDuplicates([res.data]);
|
||||
return res.data;
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('[createRule] API create failed, falling back to local storage:', err);
|
||||
const newId = Date.now();
|
||||
const fallbackRule: Rule = {
|
||||
id: newId,
|
||||
programmeId: toIntegerId(payload.programmeId),
|
||||
name: payload.name,
|
||||
description: payload.description,
|
||||
status: ActivityStatus.Active,
|
||||
version: 1,
|
||||
createdBy: validUserId,
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
saveStoredRule(fallbackRule);
|
||||
return fallbackRule;
|
||||
}
|
||||
|
||||
return {
|
||||
id: Date.now(),
|
||||
programmeId: toIntegerId(payload.programmeId),
|
||||
name: payload.name,
|
||||
description: payload.description,
|
||||
status: ActivityStatus.Active,
|
||||
version: 1,
|
||||
createdBy: payload.userId,
|
||||
createdBy: validUserId,
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
try {
|
||||
await apiClient.post('/v1/api/execution/rules', {
|
||||
programmeId: toIntegerId(payload.programmeId),
|
||||
userId: payload.userId,
|
||||
name: payload.name,
|
||||
description: payload.description,
|
||||
});
|
||||
} catch (err) {
|
||||
console.warn('[createRule] API create failed, persisted locally:', err);
|
||||
}
|
||||
|
||||
saveStoredRule(newRule);
|
||||
return newRule;
|
||||
};
|
||||
|
||||
export const updateRule = async (payload: UpdateRuleRequest): Promise<void> => {
|
||||
const validUserId = toValidUuid(payload.userId);
|
||||
try {
|
||||
await apiClient.put('/v1/api/execution/rules', {
|
||||
ruleId: toIntegerId(payload.ruleId),
|
||||
userId: payload.userId,
|
||||
userId: validUserId,
|
||||
name: payload.name,
|
||||
description: payload.description,
|
||||
status: Number(payload.status),
|
||||
version: Number(payload.version),
|
||||
version: Number(payload.version || 1),
|
||||
...(payload.programmeId ? { programmeId: toIntegerId(payload.programmeId) } : {}),
|
||||
});
|
||||
} catch (err) {
|
||||
console.warn('[updateRule] API update failed, updated locally:', err);
|
||||
console.warn('[updateRule] API update failed:', err);
|
||||
const existing = getStoredRules().find((r) => String(r.id) === String(payload.ruleId));
|
||||
const updatedRule: Rule = {
|
||||
id: payload.ruleId,
|
||||
programmeId: payload.programmeId ? toIntegerId(payload.programmeId) : (existing?.programmeId || 0),
|
||||
name: payload.name,
|
||||
description: payload.description,
|
||||
status: Number(payload.status),
|
||||
version: Number(payload.version || 1),
|
||||
createdBy: existing?.createdBy || validUserId,
|
||||
createdAt: existing?.createdAt || new Date().toISOString(),
|
||||
updatedBy: validUserId,
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
saveStoredRule(updatedRule);
|
||||
}
|
||||
|
||||
const existing = getStoredRules().find((r) => String(r.id) === String(payload.ruleId));
|
||||
const updatedRule: Rule = {
|
||||
id: payload.ruleId,
|
||||
programmeId: payload.programmeId ? toIntegerId(payload.programmeId) : (existing?.programmeId || 0),
|
||||
name: payload.name,
|
||||
description: payload.description,
|
||||
status: Number(payload.status),
|
||||
version: Number(payload.version),
|
||||
createdBy: existing?.createdBy || payload.userId,
|
||||
createdAt: existing?.createdAt || new Date().toISOString(),
|
||||
updatedBy: payload.userId,
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
saveStoredRule(updatedRule);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user