Skip to content

Feat: Use-case settings layout#2663

Merged
MuhammadKhalilzadeh merged 1 commit intodevelopfrom
as-nov-10-settings-layout
Nov 12, 2025
Merged

Feat: Use-case settings layout#2663
MuhammadKhalilzadeh merged 1 commit intodevelopfrom
as-nov-10-settings-layout

Conversation

@Br0wnHammer
Copy link
Copy Markdown
Member

Describe your changes

This PR implements the new Use-case settings layout.

Fixes #2424

Please ensure all items are checked off before requesting a review:

  • I deployed the code locally.
  • I have performed a self-review of my code.
  • I have included the issue # in the PR.
  • I have labelled the PR correctly.
  • The issue I am working on is assigned to me.
  • I have avoided using hardcoded values to ensure scalability and maintain consistency across the application.
  • I have ensured that font sizes, color choices, and other UI elements are referenced from the theme.
  • My pull request is focused and addresses a single, specific feature.
  • If there are UI changes, I have attached a screenshot or video to this PR.
Screenshot 2025-11-12 at 00 24 12

@Br0wnHammer Br0wnHammer added this to the 1.7 milestone Nov 11, 2025
@Br0wnHammer Br0wnHammer self-assigned this Nov 11, 2025
@Br0wnHammer Br0wnHammer added enhancement New feature or request frontend Frontend related tasks/issues labels Nov 11, 2025
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Nov 11, 2025

Walkthrough

The ProjectSettings page undergoes a UI restructuring, introducing a centralized styles hook (Material-UI based) and reorganizing the form layout into Card-based sections (General Information, Team & Compliance). Field arrangements, styling, and component composition are updated while preserving existing functionality for owner management, risk classification, team member handling, and framework operations.

Changes

Cohort / File(s) Summary
Styling Infrastructure
Clients/src/presentation/pages/ProjectView/ProjectSettings/styles.ts
New file introducing useStyles() hook that centralizes Material-UI theme-based styling; defines root, card, sectionTitle, and saveButton style objects using theme spacing, palette, and shape properties
UI Restructuring
Clients/src/presentation/pages/ProjectView/ProjectSettings/index.tsx
Refactored page layout replacing inline form structure with Card-based sections (General Information, Team & Compliance); reorganized fields into grid-based rows with labeled headings; integrated owner change, risk classification, team member multi-select, and framework flows into new layout; moved styling to useStyles() hook; preserved save/delete actions with dynamic button styling

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Styling preservation: Verify all theme colors, spacing, and typography match existing design system across new Card sections and grid layout
  • Functionality validation: Confirm owner change modal, risk analysis trigger, team member autocomplete, and framework removal flows work identically in reorganized layout
  • Accessibility checks: Ensure form labels, aria attributes, and keyboard navigation are maintained in new grid structure
  • Responsive behavior: Test that new grid-based layout responds correctly to different viewport sizes (especially the two-column grouping mentioned in linked issue)

Poem

🐰 The settings, once scattered, now gather as cards,
In General and Compliance—organized guards,
With styles in a hook, all tidy and bright,
The form finds new rhythm, a UI delight!

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'Feat: Use-case settings layout' directly summarizes the main change—implementing a new UI layout for use-case settings.
Description check ✅ Passed The PR description follows the template with all required sections completed: changes described, issue number included, and all checklist items marked as done with a screenshot attached.
Linked Issues check ✅ Passed The code changes implement most core requirements from #2424: Geography field with continent options, Target Industry and Description fields, team members multi-select, and Risk & Compliance section are all present.
Out of Scope Changes check ✅ Passed Changes are focused on restructuring the ProjectSettings UI layout and adding required fields per #2424. No unrelated modifications to other features or components detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch as-nov-10-settings-layout

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 29f7d55 and b654a52.

📒 Files selected for processing (2)
  • Clients/src/presentation/pages/ProjectView/ProjectSettings/index.tsx (3 hunks)
  • Clients/src/presentation/pages/ProjectView/ProjectSettings/styles.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2024-11-18T05:28:01.115Z
Learnt from: MuhammadKhalilzadeh
Repo: bluewave-labs/verifywise PR: 254
File: Clients/src/presentation/components/Modals/Controlpane/index.tsx:22-24
Timestamp: 2024-11-18T05:28:01.115Z
Learning: In `Clients/src/presentation/components/Modals/Controlpane/index.tsx` (TypeScript React component), avoid changing prop types from specific types like `string` to `any` when the exact type is known. Maintaining explicit types ensures better type safety and code reliability.

Applied to files:

  • Clients/src/presentation/pages/ProjectView/ProjectSettings/styles.ts
  • Clients/src/presentation/pages/ProjectView/ProjectSettings/index.tsx
📚 Learning: 2024-10-16T16:18:22.983Z
Learnt from: sankettank66
Repo: bluewave-labs/verifywise PR: 94
File: Clients/src/presentation/pages/Authentication/Login/index.tsx:91-108
Timestamp: 2024-10-16T16:18:22.983Z
Learning: As per project requirements, client-side validation should not be added to the authentication views, including `Clients/src/presentation/pages/Authentication/Login/index.tsx` and similar components.

Applied to files:

  • Clients/src/presentation/pages/ProjectView/ProjectSettings/index.tsx
🧬 Code graph analysis (1)
Clients/src/presentation/pages/ProjectView/ProjectSettings/index.tsx (2)
Clients/src/presentation/pages/ProjectView/ProjectSettings/styles.ts (1)
  • useStyles (3-36)
Clients/src/presentation/utils/inputStyles.ts (1)
  • getAutocompleteStyles (183-240)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)

Comment on lines +1041 to 1207
{monitoredFrameworks.length > 0 && (
<>
{/* Applicable regulations Row */}
<Box>
<Typography sx={{ fontSize: 13, fontWeight: 500 }}>
Applicable regulations *
</Typography>
<Typography
sx={{
fontSize: "13px",
color: isComingSoon
? "text.secondary"
: "text.primary",
}}
sx={{ fontSize: 12, color: "#888", whiteSpace: "nowrap" }}
>
{option.name}
</Typography>
Add all monitored regulations and standards of the use case.
</Typography>
</Box>
);
}}
isOptionEqualToValue={(
option: { _id: number },
value: { _id: number },
) => option._id === value._id}
getOptionDisabled={(option: { name: string }) =>
option.name.includes("coming soon")
}
filterSelectedOptions
popupIcon={
<ChevronDown
size={16}
color={theme.palette.text.tertiary}
/>
}
renderInput={(params) => (
<TextField
{...params}
placeholder="Select regulations and standards"
sx={{
"& .MuiOutlinedInput-root": {
height: "34px",
padding: "0 10px",
display: "flex",
alignItems: "center",
},
"& .MuiInputBase-root": {
height: "34px !important",
padding: "0 10px !important",
display: "flex !important",
alignItems: "center !important",
justifyContent: "flex-start !important",
},
"& .MuiInputBase-input": {
padding: "0 !important",
margin: "0 !important",
fontSize: "13px",
lineHeight: "1 !important",
},
"& ::placeholder": {
fontSize: "13px",
},
}}
/>
<Stack>
<Autocomplete
multiple
id="monitored-regulations-and-standards-input"
size="small"
value={values.monitoredRegulationsAndStandards}
options={nonOrganizationalFrameworks.map((fw: Framework) => ({
_id: Number(fw.id),
name: fw.name,
}))}
onChange={handleOnMultiSelect(
"monitoredRegulationsAndStandards",
)}
getOptionLabel={(item: { _id: number; name: string }) =>
item.name
}
noOptionsText={
values.monitoredRegulationsAndStandards.length ===
nonOrganizationalFrameworks.length
? "All regulations selected"
: "No options"
}
renderOption={(
props: any,
option: { _id: number; name: string },
) => {
const isComingSoon = option.name.includes("coming soon");
return (
<Box
component="li"
{...props}
sx={{
opacity: isComingSoon ? 0.5 : 1,
cursor: isComingSoon ? "not-allowed" : "pointer",
"&:hover": {
backgroundColor: isComingSoon
? "transparent"
: undefined,
},
}}
>
<Typography
sx={{
fontSize: "13px",
color: isComingSoon
? "text.secondary"
: "text.primary",
}}
>
{option.name}
</Typography>
</Box>
);
}}
isOptionEqualToValue={(
option: { _id: number },
value: { _id: number },
) => option._id === value._id}
getOptionDisabled={(option: { name: string }) =>
option.name.includes("coming soon")
}
filterSelectedOptions
popupIcon={
<ChevronDown
size={16}
color={theme.palette.text.tertiary}
/>
}
renderInput={(params) => (
<TextField
{...params}
placeholder="Select regulations and standards"
sx={{
"& .MuiOutlinedInput-root": {
height: "34px",
padding: "0 10px",
display: "flex",
alignItems: "center",
},
"& .MuiInputBase-root": {
height: "34px !important",
padding: "0 10px !important",
display: "flex !important",
alignItems: "center !important",
justifyContent: "flex-start !important",
},
"& .MuiInputBase-input": {
padding: "0 !important",
margin: "0 !important",
fontSize: "13px",
lineHeight: "1 !important",
},
"& ::placeholder": {
fontSize: "13px",
},
}}
/>
)}
sx={{
...getAutocompleteStyles(theme, { hasError: !!errors.monitoredRegulationsAndStandards }),
width: "400px",
backgroundColor: theme.palette.background.main,
".MuiAutocomplete-clearIndicator": {
display: "none",
},
"& .MuiOutlinedInput-root": {
...getAutocompleteStyles(theme, { hasError: !!errors.monitoredRegulationsAndStandards })["& .MuiOutlinedInput-root"],
borderRadius: "4px",
},
"& .MuiChip-root": {
borderRadius: "4px",
"& .MuiChip-deleteIcon": {
display:
values.monitoredRegulationsAndStandards.length === 1
? "none"
: "flex",
},
},
}}
slotProps={{
paper: {
sx: {
"& .MuiAutocomplete-listbox": {
"& .MuiAutocomplete-option": {
fontSize: "13px",
color: "#1c2130",
paddingLeft: "9px",
paddingRight: "9px",
},
"& .MuiAutocomplete-option.Mui-focused": {
background: "#f9fafb",
},
},
"& .MuiAutocomplete-noOptions": {
fontSize: "13px",
paddingLeft: "9px",
paddingRight: "9px",
},
},
},
}}
/>
{removedFramework &&
values.monitoredRegulationsAndStandards.length === 1 && (
<Typography
variant="caption"
sx={{ color: "warning.main", fontWeight: 300, mt: 1 }}
>
Framework cannot be empty.
</Typography>
)}
</Stack>
</>
)}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Do not hide the frameworks selector when none are linked yet.

Wrapping the entire “Applicable regulations” block in monitoredFrameworks.length > 0 means the selector disappears as soon as a project has zero linked frameworks (for example, a brand-new use case). With the field hidden there is no path to add the first framework, so the feature is broken for exactly the scenario this flow must support. Please render the section whenever frameworks can be chosen, regardless of the current selection state. A simple fix is to drop the guard (or gate on the availability of options instead).

-                  {monitoredFrameworks.length > 0 && (
-                    <>
+                  <>
                     {/* Applicable regulations Row */}
                     <Box>
                       <Typography sx={{ fontSize: 13, fontWeight: 500 }}>
                         Applicable regulations *
                       </Typography>
...
                     </>
-                  )}
+                  </>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{monitoredFrameworks.length > 0 && (
<>
{/* Applicable regulations Row */}
<Box>
<Typography sx={{ fontSize: 13, fontWeight: 500 }}>
Applicable regulations *
</Typography>
<Typography
sx={{
fontSize: "13px",
color: isComingSoon
? "text.secondary"
: "text.primary",
}}
sx={{ fontSize: 12, color: "#888", whiteSpace: "nowrap" }}
>
{option.name}
</Typography>
Add all monitored regulations and standards of the use case.
</Typography>
</Box>
);
}}
isOptionEqualToValue={(
option: { _id: number },
value: { _id: number },
) => option._id === value._id}
getOptionDisabled={(option: { name: string }) =>
option.name.includes("coming soon")
}
filterSelectedOptions
popupIcon={
<ChevronDown
size={16}
color={theme.palette.text.tertiary}
/>
}
renderInput={(params) => (
<TextField
{...params}
placeholder="Select regulations and standards"
sx={{
"& .MuiOutlinedInput-root": {
height: "34px",
padding: "0 10px",
display: "flex",
alignItems: "center",
},
"& .MuiInputBase-root": {
height: "34px !important",
padding: "0 10px !important",
display: "flex !important",
alignItems: "center !important",
justifyContent: "flex-start !important",
},
"& .MuiInputBase-input": {
padding: "0 !important",
margin: "0 !important",
fontSize: "13px",
lineHeight: "1 !important",
},
"& ::placeholder": {
fontSize: "13px",
},
}}
/>
<Stack>
<Autocomplete
multiple
id="monitored-regulations-and-standards-input"
size="small"
value={values.monitoredRegulationsAndStandards}
options={nonOrganizationalFrameworks.map((fw: Framework) => ({
_id: Number(fw.id),
name: fw.name,
}))}
onChange={handleOnMultiSelect(
"monitoredRegulationsAndStandards",
)}
getOptionLabel={(item: { _id: number; name: string }) =>
item.name
}
noOptionsText={
values.monitoredRegulationsAndStandards.length ===
nonOrganizationalFrameworks.length
? "All regulations selected"
: "No options"
}
renderOption={(
props: any,
option: { _id: number; name: string },
) => {
const isComingSoon = option.name.includes("coming soon");
return (
<Box
component="li"
{...props}
sx={{
opacity: isComingSoon ? 0.5 : 1,
cursor: isComingSoon ? "not-allowed" : "pointer",
"&:hover": {
backgroundColor: isComingSoon
? "transparent"
: undefined,
},
}}
>
<Typography
sx={{
fontSize: "13px",
color: isComingSoon
? "text.secondary"
: "text.primary",
}}
>
{option.name}
</Typography>
</Box>
);
}}
isOptionEqualToValue={(
option: { _id: number },
value: { _id: number },
) => option._id === value._id}
getOptionDisabled={(option: { name: string }) =>
option.name.includes("coming soon")
}
filterSelectedOptions
popupIcon={
<ChevronDown
size={16}
color={theme.palette.text.tertiary}
/>
}
renderInput={(params) => (
<TextField
{...params}
placeholder="Select regulations and standards"
sx={{
"& .MuiOutlinedInput-root": {
height: "34px",
padding: "0 10px",
display: "flex",
alignItems: "center",
},
"& .MuiInputBase-root": {
height: "34px !important",
padding: "0 10px !important",
display: "flex !important",
alignItems: "center !important",
justifyContent: "flex-start !important",
},
"& .MuiInputBase-input": {
padding: "0 !important",
margin: "0 !important",
fontSize: "13px",
lineHeight: "1 !important",
},
"& ::placeholder": {
fontSize: "13px",
},
}}
/>
)}
sx={{
...getAutocompleteStyles(theme, { hasError: !!errors.monitoredRegulationsAndStandards }),
width: "400px",
backgroundColor: theme.palette.background.main,
".MuiAutocomplete-clearIndicator": {
display: "none",
},
"& .MuiOutlinedInput-root": {
...getAutocompleteStyles(theme, { hasError: !!errors.monitoredRegulationsAndStandards })["& .MuiOutlinedInput-root"],
borderRadius: "4px",
},
"& .MuiChip-root": {
borderRadius: "4px",
"& .MuiChip-deleteIcon": {
display:
values.monitoredRegulationsAndStandards.length === 1
? "none"
: "flex",
},
},
}}
slotProps={{
paper: {
sx: {
"& .MuiAutocomplete-listbox": {
"& .MuiAutocomplete-option": {
fontSize: "13px",
color: "#1c2130",
paddingLeft: "9px",
paddingRight: "9px",
},
"& .MuiAutocomplete-option.Mui-focused": {
background: "#f9fafb",
},
},
"& .MuiAutocomplete-noOptions": {
fontSize: "13px",
paddingLeft: "9px",
paddingRight: "9px",
},
},
},
}}
/>
{removedFramework &&
values.monitoredRegulationsAndStandards.length === 1 && (
<Typography
variant="caption"
sx={{ color: "warning.main", fontWeight: 300, mt: 1 }}
>
Framework cannot be empty.
</Typography>
)}
</Stack>
</>
)}
<>
{/* Applicable regulations Row */}
<Box>
<Typography sx={{ fontSize: 13, fontWeight: 500 }}>
Applicable regulations *
</Typography>
<Typography
sx={{ fontSize: 12, color: "#888", whiteSpace: "nowrap" }}
>
Add all monitored regulations and standards of the use case.
</Typography>
</Box>
<Stack>
<Autocomplete
multiple
id="monitored-regulations-and-standards-input"
size="small"
value={values.monitoredRegulationsAndStandards}
options={nonOrganizationalFrameworks.map((fw: Framework) => ({
_id: Number(fw.id),
name: fw.name,
}))}
onChange={handleOnMultiSelect(
"monitoredRegulationsAndStandards",
)}
getOptionLabel={(item: { _id: number; name: string }) =>
item.name
}
noOptionsText={
values.monitoredRegulationsAndStandards.length ===
nonOrganizationalFrameworks.length
? "All regulations selected"
: "No options"
}
renderOption={(
props: any,
option: { _id: number; name: string },
) => {
const isComingSoon = option.name.includes("coming soon");
return (
<Box
component="li"
{...props}
sx={{
opacity: isComingSoon ? 0.5 : 1,
cursor: isComingSoon ? "not-allowed" : "pointer",
"&:hover": {
backgroundColor: isComingSoon
? "transparent"
: undefined,
},
}}
>
<Typography
sx={{
fontSize: "13px",
color: isComingSoon
? "text.secondary"
: "text.primary",
}}
>
{option.name}
</Typography>
</Box>
);
}}
isOptionEqualToValue={(
option: { _id: number },
value: { _id: number },
) => option._id === value._id}
getOptionDisabled={(option: { name: string }) =>
option.name.includes("coming soon")
}
filterSelectedOptions
popupIcon={
<ChevronDown
size={16}
color={theme.palette.text.tertiary}
/>
}
renderInput={(params) => (
<TextField
{...params}
placeholder="Select regulations and standards"
sx={{
"& .MuiOutlinedInput-root": {
height: "34px",
padding: "0 10px",
display: "flex",
alignItems: "center",
},
"& .MuiInputBase-root": {
height: "34px !important",
padding: "0 10px !important",
display: "flex !important",
alignItems: "center !important",
justifyContent: "flex-start !important",
},
"& .MuiInputBase-input": {
padding: "0 !important",
margin: "0 !important",
fontSize: "13px",
lineHeight: "1 !important",
},
"& ::placeholder": {
fontSize: "13px",
},
}}
/>
)}
sx={{
...getAutocompleteStyles(theme, { hasError: !!errors.monitoredRegulationsAndStandards }),
width: "400px",
backgroundColor: theme.palette.background.main,
".MuiAutocomplete-clearIndicator": {
display: "none",
},
"& .MuiOutlinedInput-root": {
...getAutocompleteStyles(theme, { hasError: !!errors.monitoredRegulationsAndStandards })["& .MuiOutlinedInput-root"],
borderRadius: "4px",
},
"& .MuiChip-root": {
borderRadius: "4px",
"& .MuiChip-deleteIcon": {
display:
values.monitoredRegulationsAndStandards.length === 1
? "none"
: "flex",
},
},
}}
slotProps={{
paper: {
sx: {
"& .MuiAutocomplete-listbox": {
"& .MuiAutocomplete-option": {
fontSize: "13px",
color: "#1c2130",
paddingLeft: "9px",
paddingRight: "9px",
},
"& .MuiAutocomplete-option.Mui-focused": {
background: "#f9fafb",
},
},
"& .MuiAutocomplete-noOptions": {
fontSize: "13px",
paddingLeft: "9px",
paddingRight: "9px",
},
},
},
}}
/>
{removedFramework &&
values.monitoredRegulationsAndStandards.length === 1 && (
<Typography
variant="caption"
sx={{ color: "warning.main", fontWeight: 300, mt: 1 }}
>
Framework cannot be empty.
</Typography>
)}
</Stack>
</>
🤖 Prompt for AI Agents
In Clients/src/presentation/pages/ProjectView/ProjectSettings/index.tsx around
lines 1041 to 1207, the entire "Applicable regulations" block is conditionally
rendered only when monitoredFrameworks.length > 0 which hides the selector for
projects with zero linked frameworks and prevents adding the first framework;
remove that monitoredFrameworks.length > 0 guard and instead always render the
"Applicable regulations" section (or gate its rendering on the availability of
nonOrganizationalFrameworks options if needed), so the Autocomplete and
explanatory text are visible even when no frameworks are currently selected.

@gorkem-bwl
Copy link
Copy Markdown
Contributor

Thanks. LGTM

Copy link
Copy Markdown
Collaborator

@MuhammadKhalilzadeh MuhammadKhalilzadeh left a comment

Choose a reason for hiding this comment

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

Looks great, thank you @Br0wnHammer

@MuhammadKhalilzadeh MuhammadKhalilzadeh merged commit 1d57009 into develop Nov 12, 2025
6 checks passed
@MuhammadKhalilzadeh MuhammadKhalilzadeh deleted the as-nov-10-settings-layout branch November 12, 2025 05:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request frontend Frontend related tasks/issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

More fields for use cases

3 participants