Repository: AOT-Technologies/forms-flow-ai-micro-front-ends
Pull request #560: added reusable custom hook
Author: login: abilpraju-aot
Labels: name: Review effort 3/5
Created: 2025-05-07T08:28:25Z | Merged: 2025-05-09T06:20:57Z

### **User description**
# Issue Tracking
added reusable hook for success 

https://jam.dev/c/cbc7dae8-e7d4-46c2-85cb-c7da2d6fb837
JIRA: 
Issue Type: BUG/ FEATURE

# Changes
<!-- 
What are the main changes in the PR?
Give a high-level description of the changes.
#Examples: Added a search feature, Renaming several fields, etc.
-->


___

### **PR Type**
Enhancement


___

### **Description**
- Introduce `useSuccessCountdown` custom hook

- Export hook in components and declarations

- Integrate countdown in TaskFilterModal save flow

- Remove custom `.btn-success` background override


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><details><summary>5 files</summary><table>
<tr>
  <td><strong>index.ts</strong><dd><code>Export useSuccessCountdown from components index</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
  <td><a href="https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/pull/560/files#diff-1ed9e53c9f8295b5b3021997184e36e78ae685c06246d9cd6e9c4be1c350b116">+1/-0</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>index.ts</strong><dd><code>Create customHooks index export file</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
  <td><a href="https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/pull/560/files#diff-21cd2b12cfbf5afb8ac3445d914c0916c8eb3029c7802c78fd257a07c9d546a0">+1/-0</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>useSuccessCountdown.ts</strong><dd><code>Add useSuccessCountdown hook implementation</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
  <td><a href="https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/pull/560/files#diff-d1d00b93321bd800c07b2a6f62be3d73c2851827cc3453a4cb3aaecb6c83401a">+75/-0</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>formsflow-components.ts</strong><dd><code>Include customHooks in package exports</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
  <td><a href="https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/pull/560/files#diff-8b833d791e1af7a3b3aeec5ece35735e68d79952c7258143bf8753f9353c8251">+1/-0</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>TaskFilterModal.tsx</strong><dd><code>Integrate success countdown in save button</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
  <td><a href="https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/pull/560/files#diff-1c7dec87181146c8c13605f6bfe68adabff715da99af9b536b52d665cead30bc">+10/-6</a>&nbsp; &nbsp; </td>

</tr>
</table></details></td></tr><tr><td><strong>Configuration changes</strong></td><td><details><summary>1 files</summary><table>
<tr>
  <td><strong>declarations.d.ts</strong><dd><code>Add useSuccessCountdown to module declarations</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
  <td><a href="https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/pull/560/files#diff-63b2bbba7d5a62c8231c9907f66a022c84a38eee502083d93cccacfb46c25b41">+2/-1</a>&nbsp; &nbsp; &nbsp; </td>

</tr>
</table></details></td></tr><tr><td><strong>Formatting</strong></td><td><details><summary>1 files</summary><table>
<tr>
  <td><strong>_button.scss</strong><dd><code>Remove custom btn-success background override</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
  <td><a href="https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/pull/560/files#diff-f12bb1f85d064234cfeacf9267346765300d55284af06aaf611248e94c1a0926">+0/-1</a>&nbsp; &nbsp; &nbsp; </td>

</tr>
</table></details></td></tr></tr></tbody></table>

___

> <details> <summary>  Need help?</summary><li>Type <code>/help how to ...</code> in the comments thread for any questions about PR-Agent usage.</li><li>Check out the <a href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a> for more information.</li></details>


Diff:
diff --git a/forms-flow-components/src/components/index.ts b/forms-flow-components/src/components/index.ts
index 28e587aaa..6935d02c4 100644
--- a/forms-flow-components/src/components/index.ts
+++ b/forms-flow-components/src/components/index.ts
@@ -29,4 +29,5 @@ export * from "./CustomComponents/DragandDropSort";
 export * from "./CustomComponents/DateFilter";
 export * from "./CustomComponents/FilterSortAction";
 export * from "./CustomComponents/SortableHeadder";
+export * from "../customHooks/useSuccessCountdown";
 
diff --git a/forms-flow-components/src/customHooks/index.ts b/forms-flow-components/src/customHooks/index.ts
new file mode 100644
index 000000000..d7e6b56b5
--- /dev/null
+++ b/forms-flow-components/src/customHooks/index.ts
@@ -0,0 +1 @@
+export { default as useSuccessCountdown } from './useSuccessCountdown';
\ No newline at end of file
diff --git a/forms-flow-components/src/customHooks/useSuccessCountdown.ts b/forms-flow-components/src/customHooks/useSuccessCountdown.ts
new file mode 100644
index 000000000..6fdbe87a1
--- /dev/null
+++ b/forms-flow-components/src/customHooks/useSuccessCountdown.ts
@@ -0,0 +1,75 @@
+import { useState, useEffect, useCallback } from "react";
+
+/**
+ * Configuration options for the success countdown
+ */
+interface SuccessCountdownOptions {
+  /** Duration of each countdown tick in milliseconds */
+  intervalDelay?: number;
+}
+
+/**
+ * State object for the success countdown
+ */
+interface SuccessState {
+  /** Whether the success message is currently showing */
+  showSuccess: boolean;
+  /** Current countdown value in seconds */
+  countdown: number;
+}
+
+/**
+ * Custom hook that provides a timed success state with countdown
+ * Useful for showing temporary success messages that automatically dismiss
+ * 
+ * @param options - Configuration options for the countdown
+ * @returns Object containing success state and function to trigger the countdown
+ */
+const useSuccessCountdown = (options: SuccessCountdownOptions = {}) => {
+  const { intervalDelay = 1000 } = options;
+
+  const [successState, setSuccessState] = useState<SuccessState>({
+    showSuccess: false,
+    countdown: 0,
+  });
+  
+  const [onCountdownEnd, setOnCountdownEnd] = useState<(() => void) | null>(null);
+
+  /**
+   * Starts the success countdown with the specified callback and duration
+   * 
+   * @param callback - Function to call when countdown reaches zero
+   * @param initialCount - Initial countdown value in seconds (default: 2)
+   */
+  const startSuccessCountdown = useCallback((
+    callback?: () => void,
+    initialCount: number = 2
+  ) => {
+    setSuccessState({ showSuccess: true, countdown: initialCount });
+    setOnCountdownEnd(() => callback || null);
+  }, []);
+
+  useEffect(() => {
+    if (successState.countdown >= 0 && successState.showSuccess) {
+      const interval = setInterval(() => {
+        setSuccessState((prev) => {
+          if (prev.countdown <= 0) {
+            clearInterval(interval);
+            if (onCountdownEnd) onCountdownEnd();
+            return { showSuccess: false, countdown: 0 };
+          }
+          return { ...prev, countdown: prev.countdown - 1 };
+        });
+      }, intervalDelay);
+
+      return () => clearInterval(interval);
+    }
+  }, [successState.countdown, successState.showSuccess, intervalDelay, onCountdownEnd]);
+
+  return {
+    successState,
+    startSuccessCountdown,
+  };
+};
+
+export default useSuccessCountdown;
\ No newline at end of file
diff --git a/forms-flow-components/src/formsflow-components.ts b/forms-flow-components/src/formsflow-components.ts
index 07635cbbc..981af8c83 100644
--- a/forms-flow-components/src/formsflow-components.ts
+++ b/forms-flow-components/src/formsflow-components.ts
@@ -1 +1,2 @@
 export * from './components';
+export * from './customHooks';
diff --git a/forms-flow-review/src/components/TaskFilterModal.tsx b/forms-flow-review/src/components/TaskFilterModal.tsx
index 0d5c14bb6..6f251924d 100644
--- a/forms-flow-review/src/components/TaskFilterModal.tsx
+++ b/forms-flow-review/src/components/TaskFilterModal.tsx
@@ -12,6 +12,7 @@ import {
   FormInput,
   CustomInfo,
   DragandDropSort,
+  useSuccessCountdown
 } from "@formsflow/components";
 import { removeTenantKey, trimFirstSlash } from "../helper/helper.js";
 import {
@@ -68,14 +69,17 @@ export const TaskFilterModal = ({ show, onClose, filter, canEdit }) => {
     firstResult,
     defaultFilter,
   } = useSelector((state: any) => state.task);
-
+  const { successState, startSuccessCountdown } = useSuccessCountdown();
   const userListData = userList.data ?? [];
   const tenantKey = useSelector((state: any) => state.tenants?.tenantId);
   const userRoles =
     StorageService.getParsedData(StorageService.User.USER_ROLE)
 
   const isCreateFilters = userRoles?.includes("create_filters");
-
+  let buttonVariant = "secondary"; // Default value
+  if (successState.showSuccess) {
+    buttonVariant = "success";
+  } 
   const assigneeOptions = useMemo(
     () =>
       userListData.map((user) => ({
@@ -370,12 +374,12 @@ export const TaskFilterModal = ({ show, onClose, filter, canEdit }) => {
           savedFilterId === defaultFilter ? null : savedFilterId;
         updateDefaultFilter(isDefaultFilter)
           .then((updateRes) =>
-            dispatch(setDefaultFilter(updateRes.data.defaultFilter))
+            dispatch(setDefaultFilter(updateRes.data.defaultFilter)),
+          startSuccessCountdown(onClose,2)
           )
           .catch((error) =>
             console.error("Error updating default filter:", error)
           );
-        onClose();
       })
       .catch((error) => console.error("Error saving filter:", error));
   };
@@ -569,9 +573,9 @@ export const TaskFilterModal = ({ show, onClose, filter, canEdit }) => {
       />
       <div className="pt-4">
         <CustomButton
-          variant="secondary"
+          variant={buttonVariant} 
           size="md"
-          label={t("Save This Filter")}
+          label={ successState.showSuccess ? `Saving (${successState.countdown})` : t("Save This Filter")}
           onClick={saveCurrentFilter}
           icon={<SaveIcon color={iconColor} />}
           dataTestId="save-task-filter"
diff --git a/forms-flow-review/src/declarations.d.ts b/forms-flow-review/src/declarations.d.ts
index 4a2d932b6..8a93a98b6 100644
--- a/forms-flow-review/src/declarations.d.ts
+++ b/forms-flow-review/src/declarations.d.ts
@@ -66,7 +66,8 @@ declare module "@formsflow/components" {
     CustomInfo,
     DragandDropSort,
     CustomSearch,
-    AddIcon
+    AddIcon,
+    useSuccessCountdown
   }: any;
 }
 
diff --git a/forms-flow-theme/scss/_button.scss b/forms-flow-theme/scss/_button.scss
index 6639d6dcd..eeebf1013 100644
--- a/forms-flow-theme/scss/_button.scss
+++ b/forms-flow-theme/scss/_button.scss
@@ -208,7 +208,6 @@ $danger-color: var(--ff-danger);
     }
   }
   &.btn-success { 
-    background-color: $primary !important;
     color: $white-color !important;
   }
   &.btn-danger {


Reviews:
user: login: Bonymol-aot
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T07:46:01Z
user: login: shuhaib-aot
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T05:01:38Z
user: login: shuhaib-aot
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T05:02:14Z
user: login: vinaayakh-aot
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T05:25:23Z
user: login: arun-s-aot
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T06:20:41Z

Review comments:
diff_hunk: @@ -370,12 +374,14 @@ export const TaskFilterModal = ({ show, onClose, filter, canEdit }) => {
           savedFilterId === defaultFilter ? null : savedFilterId;
         updateDefaultFilter(isDefaultFilter)
           .then((updateRes) =>
-            dispatch(setDefaultFilter(updateRes.data.defaultFilter))
+            dispatch(setDefaultFilter(updateRes.data.defaultFilter)),
+          startSuccessCountdown(() => {
+            onClose();
path: forms-flow-review/src/components/TaskFilterModal.tsx
user: login: shuhaib-aot
body: startSuccessCountdown(onClose,2)
created_at: 2025-05-09T05:01:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -370,12 +374,14 @@ export const TaskFilterModal = ({ show, onClose, filter, canEdit }) => {
           savedFilterId === defaultFilter ? null : savedFilterId;
         updateDefaultFilter(isDefaultFilter)
           .then((updateRes) =>
-            dispatch(setDefaultFilter(updateRes.data.defaultFilter))
+            dispatch(setDefaultFilter(updateRes.data.defaultFilter)),
+          startSuccessCountdown(() => {
+            onClose();
path: forms-flow-review/src/components/TaskFilterModal.tsx
user: login: shuhaib-aot
body: nice to have
created_at: 2025-05-09T05:02:14Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-05-07T08:29:46Z
body: ## PR Reviewer Guide 🔍

#### (Review updated until commit https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/commit/9a514fea36a60042f5834764d1b0892c2fea3199)


Here are some key observations to aid the review process:

<table>
<tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 3 🔵🔵🔵⚪⚪</td></tr>
<tr><td>🧪&nbsp;<strong>No relevant tests</strong></td></tr>
<tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr>
<tr><td>⚡&nbsp;<strong>Recommended focus areas for review</strong><br><br>

<details><summary><a href='https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/pull/560/files#diff-8b833d791e1af7a3b3aeec5ece35735e68d79952c7258143bf8753f9353c8251R1-R3'><strong>Duplicate Export</strong></a>

The file exports components twice (`export * from './components'` appears on lines 1 and 3), which is redundant and may cause confusion or unintended side effects.
</summary>

```typescript
export * from './components';
export * from './customHooks';
```

</details>

<details><summary><a href='https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/pull/560/files#diff-f12bb1f85d064234cfeacf9267346765300d55284af06aaf611248e94c1a0926R210-R212'><strong>Missing Background Style</strong></a>

The custom `.btn-success` rule no longer defines a background-color, relying on default styles. Verify that the success button still appears correctly in all themes.
</summary>

```scss
&.btn-success { 
  color: $white-color !important;
}
```

</details>

<details><summary><a href='https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/pull/560/files#diff-d1d00b93321bd800c07b2a6f62be3d73c2851827cc3453a4cb3aaecb6c83401aR1-R75'><strong>No Test Coverage</strong></a>

The new `useSuccessCountdown` hook has no accompanying tests. Consider adding unit tests to cover countdown behavior and callback invocation.
</summary>

```typescript
import { useState, useEffect, useCallback } from "react";

/**
 * Configuration options for the success countdown
 */
interface SuccessCountdownOptions {
  /** Duration of each countdown tick in milliseconds */
  intervalDelay?: number;
}

/**
 * State object for the success countdown
 */
interface SuccessState {
  /** Whether the success message is currently showing */
  showSuccess: boolean;
  /** Current countdown value in seconds */
  countdown: number;
}

/**
 * Custom hook that provides a timed success state with countdown
 * Useful for showing temporary success messages that automatically dismiss
 * 
 * @param options - Configuration options for the countdown
 * @returns Object containing success state and function to trigger the countdown
 */
const useSuccessCountdown = (options: SuccessCountdownOptions = {}) => {
  const { intervalDelay = 1000 } = options;

  const [successState, setSuccessState] = useState<SuccessState>({
    showSuccess: false,
    countdown: 0,
  });

  const [onCountdownEnd, setOnCountdownEnd] = useState<(() => void) | null>(null);

  /**
   * Starts the success countdown with the specified callback and duration
   * 
   * @param callback - Function to call when countdown reaches zero
   * @param initialCount - Initial countdown value in seconds (default: 2)
   */
  const startSuccessCountdown = useCallback((
    callback?: () => void,
    initialCount: number = 2
  ) => {
    setSuccessState({ showSuccess: true, countdown: initialCount });
    setOnCountdownEnd(() => callback || null);
  }, []);

  useEffect(() => {
    if (successState.countdown >= 0 && successState.showSuccess) {
      const interval = setInterval(() => {
        setSuccessState((prev) => {
          if (prev.countdown <= 0) {
            clearInterval(interval);
            if (onCountdownEnd) onCountdownEnd();
            return { showSuccess: false, countdown: 0 };
          }
          return { ...prev, countdown: prev.countdown - 1 };
        });
      }, intervalDelay);

      return () => clearInterval(interval);
    }
  }, [successState.countdown, successState.showSuccess, intervalDelay, onCountdownEnd]);

  return {
    successState,
    startSuccessCountdown,
  };
};

export default useSuccessCountdown;
```

</details>

</td></tr>
</table>

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-07T08:30:41Z
body: ## PR Code Suggestions ✨
<!-- 9a514fe -->

Latest suggestions up to 9a514fe
Explore these optional code suggestions:

<table><thead><tr><td><strong>Category</strong></td><td align=left><strong>Suggestion&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </strong></td><td align=center><strong>Impact</strong></td></tr><tbody><tr><td rowspan=2>Possible issue</td>
<td>



<details><summary>Store callback correctly</summary>

___


**The current setter wraps the callback in another function, preventing the actual <br>callback from being stored and invoked. Pass the callback directly to <br><code>setOnCountdownEnd</code> instead of wrapping it in a lambda.**

[forms-flow-components/src/customHooks/useSuccessCountdown.ts [49]](https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/pull/560/files#diff-d1d00b93321bd800c07b2a6f62be3d73c2851827cc3453a4cb3aaecb6c83401aR49-R49)

```diff
-setOnCountdownEnd(() => callback || null);
+setOnCountdownEnd(callback || null);
```
<details><summary>Suggestion importance[1-10]: 8</summary>

__

Why: The `setOnCountdownEnd` setter currently wraps the callback in a functional update causing incorrect storage; passing the callback directly ensures correct invocation on countdown end.


</details></details></td><td align=center>Medium

</td></tr><tr><td>



<details><summary>Fix promise chain</summary>

___


**The <code>.then</code> signature is misused by passing <code>startSuccessCountdown</code> as the error handler <br>and invoking it immediately. Combine both <code>dispatch</code> and the countdown call in the <br>same fulfillment callback block.**

[forms-flow-review/src/components/TaskFilterModal.tsx [377-378]](https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/pull/560/files#diff-1c7dec87181146c8c13605f6bfe68adabff715da99af9b536b52d665cead30bcR377-R378)

```diff
-).then((updateRes) =>
-    dispatch(setDefaultFilter(updateRes.data.defaultFilter)),
-  startSuccessCountdown(onClose,2)
-)
+).then((updateRes) => {
+  dispatch(setDefaultFilter(updateRes.data.defaultFilter));
+  startSuccessCountdown(onClose, 2);
+})
```
<details><summary>Suggestion importance[1-10]: 8</summary>

__

Why: The promise chain misuse leads to `startSuccessCountdown` being used as an error handler; combining both calls in the fulfillment block ensures proper execution after `updateDefaultFilter`.


</details></details></td><td align=center>Medium

</td></tr><tr><td rowspan=1>General</td>
<td>



<details><summary>Remove duplicate export</summary>

___


**The <code>components</code> module is exported twice, causing redundant re-exports. Remove the <br>duplicate <code>export * from './components';</code> at the end.**

[forms-flow-components/src/formsflow-components.ts [1-3]](https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/pull/560/files#diff-8b833d791e1af7a3b3aeec5ece35735e68d79952c7258143bf8753f9353c8251R1-R3)

```diff
 export * from './components';
 export * from './customHooks';
-export * from './components';
```
<details><summary>Suggestion importance[1-10]: 5</summary>

__

Why: Duplicate `export * from './components'` causes redundant re-exports; removing it improves module clarity without affecting functionality.


</details></details></td><td align=center>Low

</td></tr></tr></tbody></table>

___

#### Previous suggestions
<details><summary>Suggestions up to commit 283648d</summary>
<br><table><thead><tr><td><strong>Category</strong></td><td align=left><strong>Suggestion&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </strong></td><td align=center><strong>Impact</strong></td></tr><tbody><tr><td rowspan=1>Possible issue</td>
<td>



<details><summary>Fix promise .then chaining</summary>

___


**The <code>.then</code> call is using the second argument slot for <code>startSuccessCountdown</code> instead <br>of chaining it after a successful dispatch. Wrap both calls in a single fulfillment <br>callback block to ensure the countdown starts only after dispatch succeeds.**

[forms-flow-review/src/components/TaskFilterModal.tsx [375-381]](https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/pull/560/files#diff-1c7dec87181146c8c13605f6bfe68adabff715da99af9b536b52d665cead30bcR375-R381)

```diff
 updateDefaultFilter(isDefaultFilter)
-  .then((updateRes) =>
-    dispatch(setDefaultFilter(updateRes.data.defaultFilter)),
-  startSuccessCountdown(() => {
-    onClose();
-  }, 2)
-  )
+  .then((updateRes) => {
+    dispatch(setDefaultFilter(updateRes.data.defaultFilter));
+    startSuccessCountdown(() => {
+      onClose();
+    }, 2);
+  })
```
<details><summary>Suggestion importance[1-10]: 8</summary>

__

Why: The code misuses the second argument of `.then`, passing `startSuccessCountdown` as an error handler. Wrapping both calls in a single callback block ensures `startSuccessCountdown` runs after `dispatch(setDefaultFilter)`.


</details></details></td><td align=center>Medium

</td></tr><tr><td rowspan=2>General</td>
<td>



<details><summary>Register callback before countdown</summary>

___


**The callback may not be registered before the effect runs, causing it to be lost. <br>Swap the order of state updates so the callback is set before triggering the <br>countdown.**

[forms-flow-components/src/customHooks/useSuccessCountdown.ts [48-49]](https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/pull/560/files#diff-d1d00b93321bd800c07b2a6f62be3d73c2851827cc3453a4cb3aaecb6c83401aR48-R49)

```diff
+setOnCountdownEnd(() => callback || null);
 setSuccessState({ showSuccess: true, countdown: initialCount });
-setOnCountdownEnd(() => callback || null);
```
<details><summary>Suggestion importance[1-10]: 5</summary>

__

Why: Setting `onCountdownEnd` before updating `successState` guarantees the callback is registered before the effect runs. This avoids a potential race where the countdown effect could trigger before the callback is set.


</details></details></td><td align=center>Low

</td></tr><tr><td>



<details><summary>Prevent zero-value intervals</summary>

___


**The condition allows intervals to start even when countdown is zero. Change to <code>> 0</code> <br>so only positive countdown values trigger the timer.**

[forms-flow-components/src/customHooks/useSuccessCountdown.ts [53]](https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/pull/560/files#diff-d1d00b93321bd800c07b2a6f62be3d73c2851827cc3453a4cb3aaecb6c83401aR53-R53)

```diff
-if (successState.countdown >= 0 && successState.showSuccess) {
+if (successState.countdown > 0 && successState.showSuccess) {
```
<details><summary>Suggestion importance[1-10]: 3</summary>

__

Why: Changing `>= 0` to `> 0` prevents creating an unnecessary timer when the countdown is zero. This reduces overhead by avoiding an immediate interval clear on instantiation.


</details></details></td><td align=center>Low

</td></tr></tr></tbody></table>

</details>

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-09T05:12:14Z
body: **[Persistent review](https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/pull/560#issuecomment-2857643479)** updated to latest commit https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/commit/9a514fea36a60042f5834764d1b0892c2fea3199
author_association: NONE
user: login: sonarqubecloud[bot]
created_at: 2025-05-09T05:12:20Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=AOT-Technologies_forms-flow-ai-micro-front-ends&pullRequest=560) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=AOT-Technologies_forms-flow-ai-micro-front-ends&pullRequest=560&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=AOT-Technologies_forms-flow-ai-micro-front-ends&pullRequest=560&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=AOT-Technologies_forms-flow-ai-micro-front-ends&pullRequest=560&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=AOT-Technologies_forms-flow-ai-micro-front-ends&pullRequest=560&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=AOT-Technologies_forms-flow-ai-micro-front-ends&pullRequest=560&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=AOT-Technologies_forms-flow-ai-micro-front-ends&pullRequest=560)


author_association: NONE