Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
dc9f067
Implement IndexedDB for flow definitions and add revision handling logic
akanshaaa19 Jul 3, 2025
0b8f976
Refactor axios call in fetchLatestRevision to use axios.get and add I…
akanshaaa19 Jul 3, 2025
9b06d85
Refactor fetchLatestRevision to use fetch API and improve error handl…
akanshaaa19 Jul 14, 2025
a92d4b8
Implement IndexedDB for flow definitions and add revision handling logic
akanshaaa19 Jul 3, 2025
3b818b9
Refactor axios call in fetchLatestRevision to use axios.get and add I…
akanshaaa19 Jul 3, 2025
14f4b43
Refactor fetchLatestRevision to use fetch API and improve error handl…
akanshaaa19 Jul 14, 2025
4c6734b
Add deleteFlowDefinition function and integrate it into FlowEditor on…
akanshaaa19 Aug 5, 2025
c0e60d7
Merge branch 'enhancements/save-revision-before-publish' of github.co…
akanshaaa19 Aug 6, 2025
ad9317c
Remove unused import of deleteFlowDefinition in FlowEditor component
akanshaaa19 Aug 6, 2025
ce7138f
Merge branch 'master' into enhancements/save-revision-before-publish
akanshaaa19 Aug 6, 2025
2020d7b
Add publishFlowWithSuccess mock and update tests for flow publishing
akanshaaa19 Aug 6, 2025
581aebf
Merge branch 'enhancements/save-revision-before-publish' of github.co…
akanshaaa19 Aug 6, 2025
8417fb8
Refactor error handling and logging in FlowEditor helper and tests
akanshaaa19 Aug 6, 2025
7e3fe1b
Change loadfiles function from async to synchronous
akanshaaa19 Aug 6, 2025
cfdd620
Skip inactive flow test
akanshaaa19 Aug 6, 2025
cdb2729
Merge branch 'master' of github.com:glific/glific-frontend into enhan…
akanshaaa19 Oct 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 85 additions & 1 deletion src/components/floweditor/FlowEditor.helper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,93 @@

import Tooltip from 'components/UI/Tooltip/Tooltip';
import styles from './FlowEditor.module.css';
import axios from 'axios';

const glificBase = FLOW_EDITOR_API;

const DB_NAME = 'FlowDefinitionDB';
const VERSION = 1;
const STORE_NAME = 'flowDefinitions';
let dbInstance: IDBDatabase | null = null;

async function initDB(): Promise<IDBDatabase> {
if (dbInstance) {
return dbInstance;
}

return new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME, VERSION);

request.onerror = () => {
reject(new Error('Failed to open IndexedDB'));
};

request.onsuccess = () => {
dbInstance = request.result;
resolve(dbInstance);
};

request.onupgradeneeded = (event) => {
const db = (event.target as IDBOpenDBRequest).result;

if (!db.objectStoreNames.contains(STORE_NAME)) {
const store = db.createObjectStore(STORE_NAME, { keyPath: 'uuid' });
store.createIndex('timestamp', 'timeStamp', { unique: false });
}
};
});
}

export const getFlowDefinition = async (uuid: string): Promise<any | null> => {
const db = dbInstance || (await initDB());
if (!db) {
console.warn('Database not initialized. Call initDB() first.');
return null;
}

return new Promise((resolve, reject) => {
const transaction = db.transaction([STORE_NAME], 'readonly');
const store = transaction.objectStore(STORE_NAME);
const request = store.get(uuid);

request.onsuccess = () => {
const result = request.result;
resolve(result ? result : null);
};

request.onerror = () => {
reject(new Error('Failed to get flow definition'));
};
});
};

export const fetchLatestRevision = async (uuid: string) => {
let latestRevision;
const response = await axios(`${glificBase}revisions/${uuid}`);
if (response.data.results.length > 0) {
latestRevision = response.data.results.reduce((latest: any, current: any) => {
return new Date(latest.created_on) > new Date(current.created_on) ? latest : current;
});
}

return latestRevision;
};

export const postLatestRevision = async (uuid: any, definition: any) => {
const url = `${glificBase}revisions/${uuid}`;

try {
const response = await axios.post(url, definition);
if (response.status === 200) {
return true;
}

Check failure on line 86 in src/components/floweditor/FlowEditor.helper.tsx

View workflow job for this annotation

GitHub Actions / CI

Unhandled error

TypeError: Cannot read properties of undefined (reading 'data') ❯ fetchLatestRevision src/components/floweditor/FlowEditor.helper.tsx:86:5 ❯ checkLatestRevision src/components/floweditor/FlowEditor.tsx:260:3 ❯ handlePublishFlow src/components/floweditor/FlowEditor.tsx:277:9 This error originated in "src/components/floweditor/FlowEditor.test.tsx" test file. It doesn't mean the error was thrown inside the file itself, but while it was running. The latest test that might've caused the error is "publish flow which has error". It might mean one of the following: - The error was thrown, while Vitest was running this test. - If the error occurred after the test had been completed, this was the last documented test before it was thrown.
return false;
} catch (error) {
console.error('Error posting latest revision:', error);
return false;
}
};

export const setConfig = (uuid: any, isTemplate: boolean, skipValidation: boolean) => {
const services = JSON.parse(localStorage.getItem('organizationServices') || '{}');

Expand Down Expand Up @@ -109,7 +193,7 @@
return config;
};

export const loadfiles = (startFlowEditor: any) => {
export const loadfiles = async (startFlowEditor: any) => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Function signature change needs verification

The loadfiles function has been changed from synchronous to asynchronous, but the internal implementation remains synchronous. This could break existing callers.

Verify that all callers of loadfiles have been updated to handle the async nature:


🏁 Script executed:

#!/bin/bash
# Search for all usages of loadfiles function
rg -A 3 -B 3 "loadfiles\(" --type ts --type tsx

Length of output: 77


🏁 Script executed:

#!/bin/bash
# Search for all usages of loadfiles across TS/TSX files
rg -A 3 -B 3 "loadfiles\(" -g "*.ts" -g "*.tsx" || echo "No occurrences found."

Length of output: 535


Ensure callers handle loadfiles’ async return

The loadfiles function now returns a Promise, but its caller in FlowEditor.tsx still treats it as synchronous:

• File: src/components/floweditor/FlowEditor.tsx
Around the useEffect:

useEffect(() => {
  if (flowId) {
-     const files = loadfiles(() => {
+     (async () => {
+       const files = await loadfiles(() => {
        getFreeFlow({ variables: { id: flowId } });
      });
+       // …use files here…
+     })();
  }
}, [flowId]);

Please update this (and any other) call site to either await the async function or chain with .then(...), and adjust the useEffect body accordingly so you’re working with the resolved value instead of a Promise.

🤖 Prompt for AI Agents
In src/components/floweditor/FlowEditor.tsx near the useEffect hook, the call to
the async function loadfiles from FlowEditor.helper.tsx is currently treated
synchronously. Update the useEffect callback to either use an async function
with await when calling loadfiles or use loadfiles(...).then(...) to handle the
Promise resolution properly. This ensures the code works with the resolved value
instead of the Promise itself.

const files: Array<HTMLScriptElement | HTMLLinkElement> = [];
const filesToLoad: any = Manifest.files;

Expand Down
42 changes: 38 additions & 4 deletions src/components/floweditor/FlowEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,18 @@ import { Loading } from 'components/UI/Layout/Loading/Loading';
import Track from 'services/TrackService';
import { exportFlowMethod } from 'common/utils';
import styles from './FlowEditor.module.css';
import { checkElementInRegistry, getKeywords, loadfiles, setConfig } from './FlowEditor.helper';
import {
checkElementInRegistry,
fetchLatestRevision,
getFlowDefinition,
getKeywords,
loadfiles,
postLatestRevision,
setConfig,
} from './FlowEditor.helper';
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
import { BackdropLoader, FlowTranslation } from 'containers/Flow/FlowTranslation';
import dayjs from 'dayjs';

declare function showFlowEditor(node: any, config: any): void;

Expand Down Expand Up @@ -208,8 +217,10 @@ export const FlowEditor = () => {
Track('Flow opened');

return () => {
Object.keys(files).forEach((node: any) => {
Object.keys(files).forEach((node) => {
// @ts-ignore
if (files[node] && document.body.contains(files[node])) {
// @ts-ignore
document.body.removeChild(files[node]);
}
});
Expand All @@ -227,8 +238,31 @@ export const FlowEditor = () => {
return () => {};
}, [flowId]);

const handlePublishFlow = () => {
publishFlow({ variables: { uuid: params.uuid } });
const checkLatestRevision = async () => {
let revisionSaved = false;
if (uuid) {
const latestRevision = await fetchLatestRevision(uuid);

const flowDefinition = await getFlowDefinition(uuid);

const timeDifferenceSeconds = Math.abs(
dayjs(latestRevision.created_on).diff(dayjs(flowDefinition.timeStamp), 'seconds')
);

if (timeDifferenceSeconds > 300) {
revisionSaved = await postLatestRevision(uuid, flowDefinition.definition);
} else {
revisionSaved = true;
}
}
console.log(revisionSaved);
return revisionSaved;
};

const handlePublishFlow = async () => {
if (await checkLatestRevision()) {
publishFlow({ variables: { uuid: params.uuid } });
}
};

const handleCancelFlow = () => {
Expand Down
Loading