Skip to content

Commit c498930

Browse files
committed
Merge remote-tracking branch 'upstream/develop' into feature/FWF-4595-updated
2 parents 07e95e3 + 0be2137 commit c498930

File tree

86 files changed

+3908
-1497
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+3908
-1497
lines changed

CHANGELOG.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,37 @@
11
# Changelog for formsflow.ai
22

33
Mark items as `Added`, `Changed`, `Fixed`, `Modified`, `Removed`, `Untested Features`, `Upcoming Features`, `Known Issues`
4+
## 7.1.0 - 2025-07-01
5+
6+
`Added`
7+
8+
**forms-flow-components**
9+
* Added new reusable components:
10+
* Resizable table
11+
* Drag and drop component to hide and re-order
12+
* Button component with checkbox
13+
* Multi-select dropdown
14+
* BPMN diagram view
15+
* Date filter
16+
* Filter sort
17+
* New svg icons
18+
19+
**forms-flow-review**
20+
* Added new micro-frontend to handle reviewer journey
21+
22+
**forms-flow-submissions**
23+
* New micro-frontend to handle analyze submissions
24+
25+
`Modified`
26+
27+
**forms-flow-nav**
28+
* Modified sidebar menus and sub-menus
29+
30+
**forms-flow-admin**
31+
* Modified existing permissions
32+
33+
**forms-flow-theme**
34+
* Modified style changes to support new design
435

536
## 7.0.0 - 2025-01-10
637

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
[![forms-flow-admin-CD](https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/actions/workflows/forms-flow-admin-cd.yml/badge.svg)](https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/actions/workflows/forms-flow-admin-cd.yml)
77
[![forms-flow-service-CD](https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/actions/workflows/forms-flow-service.yml/badge.svg)](https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/actions/workflows/forms-flow-service.yml)
88
[![forms-flow-theme-CD](https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/actions/workflows/forms-flow-theme.yml/badge.svg)](https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/actions/workflows/forms-flow-theme.yml)
9-
9+
[![forms-flow-components-CD](https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/actions/workflows/forms-flow-component-cd.yml/badge.svg)](https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/actions/workflows/forms-flow-component-cd.yml)
10+
[![forms-flow-review-CD](https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/actions/workflows/forms-flow-review-cd.yml/badge.svg)](https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/actions/workflows/forms-flow-review-cd.yml)
11+
[![forms-flow-submissions-CD](https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/actions/workflows/forms-flow-submissions-cd.yml/badge.svg)](https://github.com/AOT-Technologies/forms-flow-ai-micro-front-ends/actions/workflows/forms-flow-submissions-cd.yml)
1012
forms-flow-ai-micro-front-ends is a collection of micro front-end applications to support formsflow.ai.
1113

1214
## components overview
@@ -31,6 +33,14 @@ This module contains the common style sheet shared by all micro-front-ends. This
3133

3234
This module contains reusable UI components.
3335

36+
6. forms-flow-review
37+
38+
This module contains all the functionalities related to reviewer journey including filters, tasks and all the related actions
39+
40+
7. forms-flow-submissions
41+
42+
This modules contains the functionalities related to analyze submissions
43+
3444
All the modules are built with `single-spa`, a javascript router for micro front-end microsevices.
3545

3646
## Prerequisites

forms-flow-admin/src/components/dashboard/index.tsx

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,32 @@ const AdminDashboard = React.memo((props : any) => {
1818
const [loading, setLoading] = React.useState(true);
1919
const [authReceived, setAuthReceived] = React.useState(true);
2020

21-
React.useEffect(() => {
22-
setTab("Dashboard");
23-
setLoading(true);
24-
fetchdashboards((data)=>{
21+
React.useEffect(() => {
22+
setTab("Dashboard");
23+
setLoading(true);
24+
25+
fetchdashboards(
26+
(data) => {
2527
setDashboards(data);
2628
setDashboardLoading(false);
27-
}, setError);
28-
fetchGroups((data)=>{
29+
},
30+
(error) => {
31+
setError(error);
32+
setDashboardLoading(false);
33+
}
34+
);
35+
36+
fetchGroups(
37+
(data) => {
2938
setGroups(data);
3039
setGroupLoading(false);
31-
}, setError);
32-
}, []);
40+
},
41+
(error) => {
42+
setError(error);
43+
setGroupLoading(false);
44+
}
45+
);
46+
}, []);
3347

3448
React.useEffect(()=>{
3549
if(!dashboardLoading && !groupLoading){

forms-flow-admin/src/components/dashboard/insightDashboard.scss

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
border: 1px solid #ECECEC;
1111
box-shadow: 0px 2px 8px rgba(66, 66, 66, 0.07);
1212
border-radius: 8px;
13-
overflow-y: scroll;
13+
overflow-y: auto !important;
14+
max-height: calc(100vh - 14rem);
1415
}
1516

1617
.container-admin .react-bootstrap-table-pagination-list{
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import React, { useState, useEffect, useRef, useMemo } from "react";
2+
import { Form } from "react-bootstrap";
3+
import { useTranslation } from "react-i18next";
4+
5+
interface Permission {
6+
name: string;
7+
description: string;
8+
depends_on: string[];
9+
category: string;
10+
order: number;
11+
}
12+
13+
interface PermissionTreeProps {
14+
permissions: Permission[];
15+
payload: {
16+
permissions: string[];
17+
};
18+
handlePermissionCheck: (name: string, depends_on: string[]) => void;
19+
setPayload: React.Dispatch<React.SetStateAction<{
20+
permissions: string[];
21+
}>>;
22+
}
23+
24+
const groupByCategory = (
25+
permissions: Permission[]
26+
): Record<string, Permission[]> => {
27+
const grouped: Record<string, Permission[]> = {};
28+
for (const perm of permissions) {
29+
const category = perm.category || "Other";
30+
grouped[category] ??= [];
31+
grouped[category].push(perm);
32+
}
33+
return grouped;
34+
};
35+
36+
const PermissionTree: React.FC<PermissionTreeProps> = ({
37+
permissions,
38+
payload,
39+
handlePermissionCheck,
40+
setPayload
41+
}) => {
42+
const { t } = useTranslation();
43+
const [expanded, setExpanded] = useState<Record<string, boolean>>({});
44+
const checkboxRefs = useRef<Record<string, HTMLInputElement | null>>({});
45+
46+
const isChecked = (name: string) => payload.permissions.includes(name);
47+
48+
const groupedPermissions = useMemo(
49+
() => groupByCategory(permissions),
50+
[permissions]
51+
);
52+
53+
const formatCategoryLabel = (category: string): string => {
54+
return `Access to ${category.charAt(0).toUpperCase()}${category
55+
.slice(1)
56+
.toLowerCase()}`;
57+
};
58+
59+
const sortPermissionsByOrder = (perms: Permission[]): Permission[] => {
60+
return [...perms].sort((a, b) => a.order - b.order);
61+
};
62+
63+
const removePermissionAndDeps = (perm: Permission, permissionsSet: Set<string>) => {
64+
permissionsSet.delete(perm.name);
65+
perm.depends_on.forEach(dep => permissionsSet.delete(dep));
66+
};
67+
68+
const addPermissionAndDeps = (perm: Permission, permissionsSet: Set<string>) => {
69+
permissionsSet.add(perm.name);
70+
perm.depends_on.forEach(dep => permissionsSet.add(dep));
71+
};
72+
73+
const handleParentCheck = (category: string, perms: Permission[]) => {
74+
setPayload(prev => {
75+
const newPermissions = new Set(prev.permissions);
76+
const allChecked = perms.every(perm => newPermissions.has(perm.name));
77+
78+
const updatePermissions = (perm: Permission) => {
79+
allChecked
80+
? removePermissionAndDeps(perm, newPermissions)
81+
: addPermissionAndDeps(perm, newPermissions);
82+
};
83+
84+
perms.forEach(updatePermissions);
85+
86+
return {
87+
...prev,
88+
permissions: Array.from(newPermissions)
89+
};
90+
});
91+
};
92+
93+
useEffect(() => {
94+
Object.entries(groupedPermissions).forEach(([category, perms]) => {
95+
const parentCheckbox = checkboxRefs.current[`parent-${category}`];
96+
if (!parentCheckbox) return;
97+
98+
const checkedCount = perms.filter((perm) => isChecked(perm.name)).length;
99+
100+
if (checkedCount === perms.length) {
101+
parentCheckbox.checked = true;
102+
parentCheckbox.indeterminate = false;
103+
} else if (checkedCount > 0) {
104+
parentCheckbox.checked = false;
105+
parentCheckbox.indeterminate = true;
106+
} else {
107+
parentCheckbox.checked = false;
108+
parentCheckbox.indeterminate = false;
109+
}
110+
});
111+
}, [payload.permissions]);
112+
113+
return (
114+
<div
115+
className="permission-tree custom-scroll"
116+
data-testid="permission-tree"
117+
aria-label="Permission Tree"
118+
>
119+
{Object.entries(groupedPermissions).map(([category, perms]) => {
120+
const sortedPerms = sortPermissionsByOrder(perms);
121+
const label = formatCategoryLabel(category);
122+
return (
123+
<div
124+
key={category}
125+
className="mb-3"
126+
data-testid={`permission-group-${category}`}
127+
>
128+
<Form.Check
129+
type="checkbox"
130+
id={`parent-${category}`}
131+
ref={(el) => (checkboxRefs.current[`parent-${category}`] = el)}
132+
label={t(label)}
133+
onChange={() => handleParentCheck(category, sortedPerms)}
134+
className="fw-bold"
135+
data-testid={`checkbox-parent-${category}`}
136+
aria-label={`Toggle all permissions in ${category}`}
137+
/>
138+
139+
<div className="tree-branch" data-testid={`tree-branch-${category}`}>
140+
{sortedPerms.map((perm, idx) => (
141+
<div
142+
key={perm.name}
143+
className={`tree-node ${
144+
idx === sortedPerms.length - 1 ? "last-child" : ""
145+
}`}
146+
data-testid={`tree-node-${perm.name}`}
147+
>
148+
<Form.Check
149+
type="checkbox"
150+
id={`child-${perm.name}`}
151+
label={t(perm.description)}
152+
checked={isChecked(perm.name)}
153+
onChange={() =>
154+
handlePermissionCheck(perm.name, perm.depends_on)
155+
}
156+
className="small"
157+
data-testid={`checkbox-child-${perm.name}`}
158+
aria-label={`Toggle permission: ${perm.description}`}
159+
/>
160+
</div>
161+
))}
162+
</div>
163+
</div>
164+
);
165+
})}
166+
</div>
167+
);
168+
};
169+
170+
export default PermissionTree;

forms-flow-admin/src/components/roles/roles.scss

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,71 @@
120120
border-top-right-radius:0;
121121
border-bottom-right-radius:0;
122122
}
123+
.role-modal-body {
124+
padding: var(--spacer-200) var(--spacer-250) !important;
125+
display: flex;
126+
gap: var(--spacer-150);
127+
flex-direction: column;
128+
}
129+
.role-details{
130+
display: flex;
131+
flex-direction: column;
132+
gap: var(--spacer-150);
133+
}
134+
135+
.permission-tree {
136+
max-height: 50vh;
137+
overflow: auto;
138+
.form-check.fw-bold {
139+
font-weight: var(--font-weight-lg);
140+
}
141+
142+
.tree-branch {
143+
position: relative;
144+
margin-left: .4rem;
145+
padding-left: var(--spacer-150);
146+
border-left: 0.125rem solid transparent;
147+
margin-top: var(--spacer-075);
148+
149+
&::before {
150+
content: "";
151+
position: absolute;
152+
top: 0;
153+
left: 0;
154+
bottom: 0.6rem;
155+
width: 0.031rem;
156+
background: var(--ff-primary);
157+
}
158+
}
159+
160+
.tree-node {
161+
position: relative;
162+
padding-left: var(--spacer-150);
163+
margin-bottom: var(--spacer-075);
164+
165+
&::before {
166+
content: "";
167+
position: absolute;
168+
top: 50%;
169+
left: -1.5rem;
170+
width: 1.5rem;
171+
height: 0.063rem;
172+
background-color: var(--ff-primary);
173+
}
174+
175+
&.last-child::after {
176+
content: "";
177+
position: absolute;
178+
bottom: 0;
179+
left: -1.5rem;
180+
width: 0.125rem;
181+
height: 45%;
182+
background-color: var(--ff-white);
183+
}
184+
}
185+
}
186+
187+
123188
@media (max-width: 768px) {
124189
.font-size{
125190
white-space: nowrap;

0 commit comments

Comments
 (0)