Repository: bcgov/cas-registration
Pull request #3103: chore: fix report history
Author: login: ayeshmcg
Created: 2025-03-27T05:52:15Z | Merged: 2025-03-28T00:32:25Z

Card: [#669](https://github.com/bcgov/cas-reporting/issues/669), [#661](https://github.com/bcgov/cas-reporting/issues/661)

Changes:-
- Fixed breadcumbs for report-history
- modified schema and report history service to display by date of submission in descending order
- fixed Format for the date of submission


Diff:
diff --git a/bc_obps/reporting/schema/report_history.py b/bc_obps/reporting/schema/report_history.py
index a927a04977..a802bf333b 100644
--- a/bc_obps/reporting/schema/report_history.py
+++ b/bc_obps/reporting/schema/report_history.py
@@ -16,8 +16,14 @@ class Meta:
 
     @staticmethod
     def resolve_version(obj: Any) -> str:
-        if obj.version_number == 1:
+        all_ids = [version.id for version in obj._meta.model.objects.filter(report_id=obj.report_id)]
+        max_id = max(all_ids)
+        min_id = min(all_ids)
+
+        if obj.id == max_id:
             return "Current Version"
+        elif obj.id == min_id:
+            return "Version 1"
         return f"Version {obj.version_number}"
 
     @staticmethod
diff --git a/bc_obps/reporting/service/report_history_dashboard_service.py b/bc_obps/reporting/service/report_history_dashboard_service.py
index 9d5392dfa5..a5f93f0a72 100644
--- a/bc_obps/reporting/service/report_history_dashboard_service.py
+++ b/bc_obps/reporting/service/report_history_dashboard_service.py
@@ -11,10 +11,6 @@ class ReportingHistoryDashboardService:
 
     @classmethod
     def get_report_versions_for_report_history_dashboard(cls, report_id: int) -> QuerySet[ReportVersion]:
-        """
-        Fetches report versions for a given report_id, assigns version numbers using SQL row_number(),
-        and lets the frontend or Ninja schema handle the naming logic.
-        """
         report_versions = (
             ReportVersion.objects.filter(report_id=report_id)
             .annotate(version_number=Window(expression=RowNumber(), order_by=F("id").desc()))
diff --git a/bc_obps/reporting/tests/service/test_report_history_dashboard_service.py b/bc_obps/reporting/tests/service/test_report_history_dashboard_service.py
index 63cbb03b11..1ee5342f93 100644
--- a/bc_obps/reporting/tests/service/test_report_history_dashboard_service.py
+++ b/bc_obps/reporting/tests/service/test_report_history_dashboard_service.py
@@ -31,4 +31,4 @@ def test_get_report_versions_for_report_history_dashboard(self):
         self.assertEqual(report_versions[1].id, self.report_version_1.id)
 
         self.assertEqual(ReportHistoryResponse.resolve_version(report_versions[0]), "Current Version")
-        self.assertEqual(ReportHistoryResponse.resolve_version(report_versions[1]), "Version 2")
+        self.assertEqual(ReportHistoryResponse.resolve_version(report_versions[1]), "Version 1")
diff --git a/bciers/apps/reporting/src/app/components/datagrid/models/reportHistory/reportHistoryColumns.tsx b/bciers/apps/reporting/src/app/components/datagrid/models/reportHistory/reportHistoryColumns.tsx
index f79cc5195b..56140f07e6 100644
--- a/bciers/apps/reporting/src/app/components/datagrid/models/reportHistory/reportHistoryColumns.tsx
+++ b/bciers/apps/reporting/src/app/components/datagrid/models/reportHistory/reportHistoryColumns.tsx
@@ -1,7 +1,7 @@
 "use client";
 import { GridColDef } from "@mui/x-data-grid";
 import ReportHistoryActionCell from "@reporting/src/app/components/reportHistory/ActionCell";
-import { formatDate } from "@reporting/src/app/utils/formatDate";
+import formatTimestamp from "@bciers/utils/src/formatTimestamp";
 
 export const OPERATOR_COLUMN_INDEX = 1;
 
@@ -21,7 +21,7 @@ const reportHistoryColumns = (): GridColDef[] => {
         if (!params.value) {
           return "";
         }
-        return formatDate(params.value, "YYYY-MM-DD HH:mm:ss");
+        return formatTimestamp(params.value);
       },
       width: 400,
     },
diff --git a/bciers/apps/reporting/src/app/components/operations/cells/MoreActionsCell.tsx b/bciers/apps/reporting/src/app/components/operations/cells/MoreActionsCell.tsx
index a91d39215e..f038321aff 100644
--- a/bciers/apps/reporting/src/app/components/operations/cells/MoreActionsCell.tsx
+++ b/bciers/apps/reporting/src/app/components/operations/cells/MoreActionsCell.tsx
@@ -75,7 +75,7 @@ const MoreActionsCell = (params: GridRenderCellParams) => {
             )}
           </MenuItem>
         )}
-        {!pending && (
+        {!pending && reportId && (
           <MenuItem
             onClick={async () =>
               router.push(`reports/report-history/${reportId}`)
@@ -84,6 +84,9 @@ const MoreActionsCell = (params: GridRenderCellParams) => {
             Report History
           </MenuItem>
         )}
+        {!pending && !reportId && (
+          <MenuItem disabled={true}>Report History</MenuItem>
+        )}
       </Menu>
     </div>
   );
diff --git a/bciers/apps/reporting/src/app/components/submitted/SubmittedForm.tsx b/bciers/apps/reporting/src/app/components/submitted/SubmittedForm.tsx
index 7d97c7d9be..58db62821a 100644
--- a/bciers/apps/reporting/src/app/components/submitted/SubmittedForm.tsx
+++ b/bciers/apps/reporting/src/app/components/submitted/SubmittedForm.tsx
@@ -2,7 +2,6 @@
 import { ReviewData } from "@reporting/src/app/components/finalReview/reviewDataFactory/factory";
 import FinalReviewForms from "@reporting/src/app/components/finalReview/formCustomization/FinalReviewForms";
 import { useRouter } from "next/navigation";
-import { baseUrlReports } from "@reporting/src/app/utils/constants";
 import { Button } from "@mui/material";
 
 interface Props {
@@ -11,12 +10,13 @@ interface Props {
 
 const SubmittedForm: React.FC<Props> = ({ data }) => {
   const router = useRouter();
+
   return (
     <div className="flex flex-col gap-6">
       <FinalReviewForms data={data} />
       <Button
         variant="outlined"
-        onClick={() => router.push(baseUrlReports)}
+        onClick={() => router.back()}
         sx={{ width: "10px" }}
       >
         Back
diff --git a/bciers/libs/components/src/navigation/Bread.tsx b/bciers/libs/components/src/navigation/Bread.tsx
index fc6f533ea8..26b85778f2 100644
--- a/bciers/libs/components/src/navigation/Bread.tsx
+++ b/bciers/libs/components/src/navigation/Bread.tsx
@@ -11,6 +11,7 @@ type TBreadCrumbProps = {
   defaultLinks?: { label: string; href: string }[];
   zone?: string;
 };
+
 // 🛠️ Custom function to validate UUID using regex
 const isValidUUID = (segment: string): boolean => {
   const uuidRegex =
@@ -32,12 +33,6 @@ function isNumeric(value: string): boolean {
   return !isNaN(Number(value));
 }
 
-// 🛠️ Function to determine valid crumb link
-function isValidLink(segment: string): boolean {
-  const invalidWords = ["confirm", "received", "request-access"];
-  return !invalidWords.some((word) => segment.toLowerCase().includes(word));
-}
-
 const liStyle = "inline text-white text-lg";
 const aStyle = "text-white text-lg";
 
@@ -69,9 +64,20 @@ export default function Bread({
   const slicedPathNames =
     lastLinkIndex !== -1 ? pathNames.slice(0, lastLinkIndex + 1) : pathNames;
 
-  // 🛠️ Function to transform path segment crumb content based on conditions: segmant paths; uuid; number
+  /**
+   * Transforms a path segment for use in the breadcrumb.
+   *
+   * This function processes each segment of the current URL path to determine
+   * how it should be displayed in the breadcrumb navigation. It omits numeric segments,
+   * applies special handling for UUIDs by checking for custom titles in the search params,
+   * and excludes segments like "facilities" when a "reports" segment is present.
+   *
+   * @param segment - The current URL path segment.
+   * @param index - The index of the current segment within the overall path.
+   * @returns The transformed segment as a string or null if the segment should be omitted.
+   */
   function transformPathSegment(segment: string, index: number): string | null {
-    // Check if "reports" is in the pathNames and the current segment is "Facilities"
+    // Omit "facilities" if "reports" exists in the path.
     if (
       pathNames.some((path) => path.toLowerCase() === "reports") &&
       segment.toLowerCase() === "facilities"
@@ -79,28 +85,52 @@ export default function Bread({
       return null;
     }
 
-    // Check if the current segment is an ID
+    // Check if the current segment is a valid UUID or numeric.
     if (isValidUUID(segment) || isNumeric(segment)) {
       const precedingSegment = pathNames[index - 1]
         ? unslugifyAndCapitalize(pathNames[index - 1])
         : "";
 
-      // Check if there is a title associated with the preceding segment
+      // If there is a title associated with the preceding segment, return it.
       if (
         precedingSegment &&
         crumbTitles[`${precedingSegment.toLowerCase()}_title`]
       )
         return crumbTitles[`${precedingSegment.toLowerCase()}_title`];
 
-      // If there's a title for the numeric/UUID segment itself, return it
-      // 🚨 If no title is found, omit the segment by returning null
+      // If there's a title for the numeric/UUID segment itself, return it.
+      // If no title is found, omit the segment by returning null.
       return crumbTitles?.title || null;
     }
 
-    // Return segment as-is
+    // For all other segments, return the segment as-is.
     return segment;
   }
 
+  // Build an array of visible breadcrumbs by filtering out any segments where transformPathSegment returns null.
+  // This ensures that only valid, displayable breadcrumb items are rendered (e.g. numeric segments are omitted).
+  const visibleCrumbs = slicedPathNames
+    // For each segment in slicedPathNames, create an object containing:
+    // - link: the original segment value,
+    // - index: its position in the array (used later to reconstruct the path),
+    // - content: the transformed value (processed based on whether capitalization is desired),
+    //   which may be null if the segment is to be omitted.
+    .map((link, index) => {
+      const content = capitalizeLinks
+        ? transformPathSegment(unslugifyAndCapitalize(link), index)
+        : transformPathSegment(link, index);
+      // If content is valid (not null), return an object with the link, its index, and the content.
+      // Otherwise, return null for this segment.
+      return content ? { link, index, content } : null;
+    })
+    // Filter out any null values from the array. This leaves us with only the breadcrumb items
+    // that have valid content, ensuring that no empty breadcrumb items (or trailing separators) are rendered.
+    .filter((crumb) => crumb !== null) as {
+    link: string;
+    index: number;
+    content: string;
+  }[];
+
   return (
     <div className="relative w-full">
       <div
@@ -120,29 +150,19 @@ export default function Bread({
                       ? unslugifyAndCapitalize(link.label)
                       : link.label}
                   </Link>
-                  {!isLastDefaultLink || pathNames.length > 0
+                  {!isLastDefaultLink || visibleCrumbs.length > 0
                     ? separator
                     : null}{" "}
                   {/* Conditionally render the separator */}
                 </li>
               );
             })}
-            {slicedPathNames.map((link, index) => {
-              if (!isValidLink(link)) return null; // Skip rendering invalid links
-              const isLastItem = index === slicedPathNames.length - 1;
-              const content = capitalizeLinks
-                ? transformPathSegment(unslugifyAndCapitalize(link), index)
-                : transformPathSegment(link, index);
-
-              // 🚨 Skip rendering if content is null (segment should be omitted)
-              if (!content) {
-                return null;
-              }
-
+            {visibleCrumbs.map((crumb, i) => {
+              const isLastItem = i === visibleCrumbs.length - 1;
               if (!isLastItem) {
-                // 🔗 create a link
+                // Create a link for all but the last breadcrumb item.
                 const path = `/${slicedPathNames
-                  .slice(0, index + 1)
+                  .slice(0, crumb.index + 1)
                   .join("/")}`;
                 const queryString = serializeSearchParams(searchParams);
                 const href = zone
@@ -150,25 +170,25 @@ export default function Bread({
                   : `${path}${queryString}`;
 
                 return (
-                  <li key={link} className={liStyle}>
+                  <li key={crumb.link} className={liStyle}>
                     <Link href={href} className={aStyle}>
-                      {content}
+                      {crumb.content}
                     </Link>
                     {separator}
                   </li>
                 );
               } else {
-                // Last item, no link, bold styling
+                // For the last breadcrumb item, do not add a separator and apply bold styling.
                 return (
                   <li
-                    key={link}
+                    key={crumb.link}
                     data-testid="breadcrumb-last-item"
                     className={liStyle}
                     style={{
                       fontWeight: "bold",
                     }}
                   >
-                    {content}
+                    {crumb.content}
                   </li>
                 );
               }


Reviews:
user: login: shon-button
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T16:43:51Z
user: login: ayeshmcg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T17:02:12Z
user: login: ayeshmcg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T17:04:54Z
user: login: shon-button
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T17:16:01Z
user: login: ayeshmcg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T17:20:16Z
user: login: shon-button
body: 🎉
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T17:59:23Z

Review comments:
diff_hunk: @@ -69,6 +69,14 @@ export default function Bread({
   const slicedPathNames =
     lastLinkIndex !== -1 ? pathNames.slice(0, lastLinkIndex + 1) : pathNames;
 
+  // If the current path includes "report-history" and the last segment of the path is numeric,
path: bciers/libs/components/src/navigation/Bread.tsx
user: login: shon-button
body: @ayeshmcg 

Instead of adding a hard-coded test for a numeric segment, since numeric already has transformation logic; perhaps, we can manage the extra separator displayed in the breadcrumb by adding a filter on null content.

Something like:
```

  const visibleCrumbs = slicedPathNames
    .map((link, index) => {
      const content = capitalizeLinks
        ? transformPathSegment(unslugifyAndCapitalize(link), index)
        : transformPathSegment(link, index);
      return content ? { link, index, content } : null;
    })
    .filter((crumb) => crumb !== null) as {
    link: string;
    index: number;
    content: string;
  }[];

```
within the file such as:
```
"use client";

import { usePathname, useSearchParams } from "next/navigation";
import Link from "@mui/material/Link";
import serializeSearchParams from "@bciers/utils/src/serializeSearchParams";

// 📐 type for breadcrumb props
type TBreadCrumbProps = {
  separator: React.ReactNode;
  capitalizeLinks: boolean;
  defaultLinks?: { label: string; href: string }[];
  zone?: string;
};

// 🛠️ Custom function to validate UUID using regex
const isValidUUID = (segment: string): boolean => {
  const uuidRegex =
    /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
  return uuidRegex.test(segment);
};

// 🛠️ Function to un-slugify and capitalize a string
function unslugifyAndCapitalize(segment: string): string {
  if (isValidUUID(segment)) return segment; // Do not capitalize UUIDs
  return segment
    .split("-")
    .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
    .join(" ");
}

// 🛠️ Function to check if a given value is numeric
function isNumeric(value: string): boolean {
  return !isNaN(Number(value));
}

// 🛠️ Function to determine valid crumb link
function isValidLink(segment: string): boolean {
  const invalidWords = ["confirm", "received", "request-access"];
  return !invalidWords.some((word) => segment.toLowerCase().includes(word));
}

const liStyle = "inline text-white text-lg";
const aStyle = "text-white text-lg";

export default function Bread({
  separator,
  capitalizeLinks,
  defaultLinks = [], // Default to an empty array if not provided
  zone = "", // Default to empty string if not provided
}: TBreadCrumbProps) {
  const paths = usePathname();
  const pathNames = paths.split("/").filter((path) => path);
  const searchParams = useSearchParams();
  const crumbTitles = Object.fromEntries(searchParams.entries());

  // Not showing breadcrumbs on the onboarding page
  const showBreadcrumb = paths !== "/onboarding";
  if (!showBreadcrumb) return null;

  // 🛠️ Function to check if a link is the last link
  const isLastBreadcrumbItem = (link: string, index: number) => {
    const lastLinkValues = ["register-an-operation"];
    return lastLinkValues.includes(link) || index === pathNames.length - 1;
  };

  // 🛠️ Find the index of the last breadcrumb item
  const lastLinkIndex = pathNames.findIndex((link, index) =>
    isLastBreadcrumbItem(link, index),
  );
  const slicedPathNames =
    lastLinkIndex !== -1 ? pathNames.slice(0, lastLinkIndex + 1) : pathNames;

  /**
   * Transforms a path segment for use in the breadcrumb.
   *
   * This function processes each segment of the current URL path to determine
   * how it should be displayed in the breadcrumb navigation. It omits numeric segments,
   * applies special handling for UUIDs by checking for custom titles in the search params,
   * and excludes segments like "facilities" when a "reports" segment is present.
   *
   * @param segment - The current URL path segment.
   * @param index - The index of the current segment within the overall path.
   * @returns The transformed segment as a string or null if the segment should be omitted.
   */
  function transformPathSegment(segment: string, index: number): string | null {
    // Omit "facilities" if "reports" exists in the path.
    if (
      pathNames.some((path) => path.toLowerCase() === "reports") &&
      segment.toLowerCase() === "facilities"
    ) {
      return null;
    }

    // Check if the current segment is a valid UUID or numeric.
    if (isValidUUID(segment) || isNumeric(segment)) {
      const precedingSegment = pathNames[index - 1]
        ? unslugifyAndCapitalize(pathNames[index - 1])
        : "";

      // If there is a title associated with the preceding segment, return it.
      if (
        precedingSegment &&
        crumbTitles[`${precedingSegment.toLowerCase()}_title`]
      )
        return crumbTitles[`${precedingSegment.toLowerCase()}_title`];

      // If there's a title for the numeric/UUID segment itself, return it.
      // If no title is found, omit the segment by returning null.
      return crumbTitles?.title || null;
    }

    // For all other segments, return the segment as-is.
    return segment;
  }

  // Build an array of visible breadcrumbs by filtering out any segments where transformPathSegment returns null.
  // This ensures that only valid, displayable breadcrumb items are rendered (e.g. numeric segments are omitted).
  const visibleCrumbs = slicedPathNames
    // For each segment in slicedPathNames, create an object containing:
    // - link: the original segment value,
    // - index: its position in the array (used later to reconstruct the path),
    // - content: the transformed value (processed based on whether capitalization is desired),
    //   which may be null if the segment is to be omitted.
    .map((link, index) => {
      const content = capitalizeLinks
        ? transformPathSegment(unslugifyAndCapitalize(link), index)
        : transformPathSegment(link, index);
      // If content is valid (not null), return an object with the link, its index, and the content.
      // Otherwise, return null for this segment.
      return content ? { link, index, content } : null;
    })
    // Filter out any null values from the array. This leaves us with only the breadcrumb items
    // that have valid content, ensuring that no empty breadcrumb items (or trailing separators) are rendered.
    .filter((crumb) => crumb !== null) as {
    link: string;
    index: number;
    content: string;
  }[];

  return (
    <div className="relative w-full">
      <div
        className={`bg-bc-bg-blue relative left-1/2 transform -translate-x-1/2 w-screen max-w-none`}
      >
        <nav
          aria-label="breadcrumbs"
          className="flex items-center bg-bc-bg-blue w-full max-w-page h-20 mx-auto"
        >
          <ol className="list-none pl-0 ml-4 sm:ml-6">
            {defaultLinks.map((link, index) => {
              const isLastDefaultLink = index === defaultLinks.length - 1;
              return (
                <li key={link.href} className={liStyle}>
                  <Link href={link.href} className={aStyle}>
                    {capitalizeLinks
                      ? unslugifyAndCapitalize(link.label)
                      : link.label}
                  </Link>
                  {!isLastDefaultLink || visibleCrumbs.length > 0
                    ? separator
                    : null}{" "}
                  {/* Conditionally render the separator */}
                </li>
              );
            })}
            {visibleCrumbs.map((crumb, i) => {
              const isLastItem = i === visibleCrumbs.length - 1;
              if (!isLastItem) {
                // Create a link for all but the last breadcrumb item.
                const path = `/${slicedPathNames
                  .slice(0, crumb.index + 1)
                  .join("/")}`;
                const queryString = serializeSearchParams(searchParams);
                const href = zone
                  ? `/${zone}${path}${queryString}`
                  : `${path}${queryString}`;

                return (
                  <li key={crumb.link} className={liStyle}>
                    <Link href={href} className={aStyle}>
                      {crumb.content}
                    </Link>
                    {separator}
                  </li>
                );
              } else {
                // For the last breadcrumb item, do not add a separator and apply bold styling.
                return (
                  <li
                    key={crumb.link}
                    data-testid="breadcrumb-last-item"
                    className={liStyle}
                    style={{
                      fontWeight: "bold",
                    }}
                  >
                    {crumb.content}
                  </li>
                );
              }
            })}
          </ol>
        </nav>
      </div>
    </div>
  );
}


``` 
created_at: 2025-03-27T16:12:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -75,7 +75,7 @@ const MoreActionsCell = (params: GridRenderCellParams) => {
             )}
           </MenuItem>
         )}
-        {!pending && (
+        {!pending && reportId && (
path: bciers/apps/reporting/src/app/components/operations/cells/MoreActionsCell.tsx
user: login: shon-button
body: @ayeshmcg 

Nice!

Perhaps we could also [Add confirmation message...](https://github.com/bcgov/cas-reporting/issues/670) 
Either now, or a later tech debt.
created_at: 2025-03-27T16:17:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -75,7 +75,7 @@ const MoreActionsCell = (params: GridRenderCellParams) => {
             )}
           </MenuItem>
         )}
-        {!pending && (
+        {!pending && reportId && (
path: bciers/apps/reporting/src/app/components/operations/cells/MoreActionsCell.tsx
user: login: ayeshmcg
body: Thanks @shon-button 
I added this I we want to show disabled option for report-history if there is no report id
created_at: 2025-03-27T17:02:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -69,6 +69,14 @@ export default function Bread({
   const slicedPathNames =
     lastLinkIndex !== -1 ? pathNames.slice(0, lastLinkIndex + 1) : pathNames;
 
+  // If the current path includes "report-history" and the last segment of the path is numeric,
path: bciers/libs/components/src/navigation/Bread.tsx
user: login: ayeshmcg
body: The current code, removes the number in the end and replaces with '', we are left with separator and reporthistory gets a link as it is the second last. Also we already have hard coded for facilities.
created_at: 2025-03-27T17:04:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -69,6 +69,14 @@ export default function Bread({
   const slicedPathNames =
     lastLinkIndex !== -1 ? pathNames.slice(0, lastLinkIndex + 1) : pathNames;
 
+  // If the current path includes "report-history" and the last segment of the path is numeric,
path: bciers/libs/components/src/navigation/Bread.tsx
user: login: shon-button
body: @ayeshmcg 

Right, the suggested code will remove the separator without hard-coding conditions for cases that can use the existing logic, such as numerics and UUIDs.  In this way we can reduce hard-coding to the special edge-cases, such as `report\facilities` segments.
created_at: 2025-03-27T17:16:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -69,6 +69,14 @@ export default function Bread({
   const slicedPathNames =
     lastLinkIndex !== -1 ? pathNames.slice(0, lastLinkIndex + 1) : pathNames;
 
+  // If the current path includes "report-history" and the last segment of the path is numeric,
path: bciers/libs/components/src/navigation/Bread.tsx
user: login: ayeshmcg
body: My bad @shon-button , I though it was highlighting the code where changes were requested, I used this suggestion and it works perfectly. thank you so much 
created_at: 2025-03-27T17:20:15Z
author_association: CONTRIBUTOR

PR comments:
user: login: shon-button
created_at: 2025-03-27T16:59:40Z
body: @ayeshmcg 

Looks great!

A few flags 🚩

- The `Back` button might need some refactoring in some forms, see [tech debt ticket](https://github.com/bcgov/cas-reporting/issues/671)
-  Possible need to refactor api `bc_obps/reporting/schema/report_history.py`  based on https://github.com/bcgov/cas-registration/pull/3073


author_association: CONTRIBUTOR
user: login: ayeshmcg
created_at: 2025-03-27T17:06:54Z
body: @shon-button We could rebase https://github.com/bcgov/cas-registration/pull/3073 once it is merged, since we didnt have in develop till now thats whey not yet implemented in this

author_association: CONTRIBUTOR
user: login: shon-button
created_at: 2025-03-27T17:57:44Z
body: Just capturing breadcrumb test results here: 

### ✅
![image](https://github.com/user-attachments/assets/36bd1850-a93d-47a2-941f-ede053663951)

![image](https://github.com/user-attachments/assets/d8d4c7ea-b876-4fbc-83f1-98c7dcae51cf)

![image](https://github.com/user-attachments/assets/56f56558-ade3-47a9-97dc-b895c4d1f8fb)

![image](https://github.com/user-attachments/assets/9b9ad362-cbe6-429e-86c1-3904c98a273c)

![image](https://github.com/user-attachments/assets/a030ea85-740b-46fa-83ec-85406189bfa0)

![image](https://github.com/user-attachments/assets/67466788-d92c-48fa-848e-3d1917796d24)

author_association: CONTRIBUTOR