Skip to content

feat(dev-portal-web): INFRA-1456 service user section - #7886

Open
SavelevMatthew wants to merge 4 commits into
mainfrom
feat/INFRA-1456/service-user-section
Open

feat(dev-portal-web): INFRA-1456 service user section#7886
SavelevMatthew wants to merge 4 commits into
mainfrom
feat/INFRA-1456/service-user-section

Conversation

@SavelevMatthew

@SavelevMatthew SavelevMatthew commented Jul 28, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features
    • Added a Service User section to B2B app settings.
    • Introduced B2B access-rights editing with grouped permission controls.
    • Added pending change messaging with a “view changes” diff, plus cross-environment permission comparison.
    • Extended the user registration flow to support both B2C and B2B app types.
  • UI Improvements
    • Enhanced the access-rights form styling (table row interactions, hint icon presentation, and layout tweaks).
  • Localization
    • Added English and Russian strings for the Service User section, including environment label grammatical variants.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 79d89d30-24ba-4db6-b96b-ca39f4c8e0ca

📥 Commits

Reviewing files that changed from the base of the PR and between 0d75125 and a8421af.

📒 Files selected for processing (4)
  • apps/dev-portal-web/domains/miniapp/components/B2BApp/edit/service-user/AccessRightSetForm.tsx
  • apps/dev-portal-web/domains/miniapp/components/B2BApp/edit/service-user/DiffContainer.tsx
  • apps/dev-portal-web/lang/en.json
  • apps/dev-portal-web/lang/ru.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/dev-portal-web/lang/ru.json
  • apps/dev-portal-web/domains/miniapp/components/B2BApp/edit/service-user/DiffContainer.tsx

📝 Walkthrough

Walkthrough

Adds a B2B service-user section with environment-aware registration, grouped access-right editing, GraphQL persistence, pending-change diffs, localization, and navigation wiring. Registration now refetches either B2C or B2B rights based on app type.

Changes

B2B service-user access rights

Layer / File(s) Summary
Access-right GraphQL contracts
apps/dev-portal-web/domains/miniapp/constants/..., apps/dev-portal-web/domains/miniapp/queries/..., apps/dev-portal-web/gql/index.ts
Defines grouped permissions, access-right queries and mutation, generated types, documents, and Apollo hooks.
Registration refetch integration
apps/dev-portal-web/domains/miniapp/components/B2CApp/..., apps/dev-portal-web/domains/miniapp/components/User/...
Passes appType through registration components and selects the matching B2C or B2B rights query for refetching.
Service-user section entry
apps/dev-portal-web/domains/miniapp/components/B2BApp/edit/service-user/..., apps/dev-portal-web/domains/miniapp/hooks/useB2BMenuItems.tsx, apps/dev-portal-web/pages/apps/b2b/[id].tsx
Adds environment selection, rights loading, registration/edit branching, and navigation wiring.
Permission editor and diff display
apps/dev-portal-web/domains/miniapp/components/B2BApp/edit/service-user/*, apps/dev-portal-web/lang/*
Adds grouped permission toggles, save handling, pending alerts, added/removed permission lists, localization, and form styling.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Operator
  participant ServiceUserSection
  participant AccessRightSetForm
  participant Apollo
  participant B2BAccessRightAPI
  Operator->>ServiceUserSection: select environment
  ServiceUserSection->>Apollo: query service-user rights
  Apollo->>B2BAccessRightAPI: appId and environment
  B2BAccessRightAPI-->>Apollo: rights and access-right sets
  Apollo-->>AccessRightSetForm: current permissions
  Operator->>AccessRightSetForm: toggle permissions and save
  AccessRightSetForm->>Apollo: create access-right set
  Apollo->>B2BAccessRightAPI: permission payload
  B2BAccessRightAPI-->>Apollo: created status and permissions
  Apollo-->>AccessRightSetForm: completion status
Loading

Possibly related PRs

Suggested labels: ✋🙂 Review please

Suggested reviewers: alex83xpert, dkoviazin

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding the dev-portal-web service user section.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/INFRA-1456/service-user-section

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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0d75125bc5

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const currentRightSet = useMemo(() => pendingRightSet ?? approvedRightSet, [approvedRightSet, pendingRightSet])

useEffect(() => {
if (currentRightSet) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reset the form when the environment has no right set

When switching from an environment that has a pending/approved right set to one that has a service user but no right set yet, this guard skips the effect and leaves both groupedPermissions and initialGroupedPermissions populated from the previous environment. The user then sees stale permissions for the new environment and the Save button can stay disabled until they make a change, so the first right set may be created from the wrong baseline. Reset to the all-false default whenever currentRightSet is absent for the selected environment.

Useful? React with 👍 / 👎.

Comment on lines +35 to +36
canReadBillingReceipts
canReadBillingReceiptFiles

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Include service mutation rights in the access form

The fragment that drives ShowedPermissions and GROUPED_PERMISSIONS stops at list permissions, so none of the existing B2B service permissions such as canExecuteRegisterExternalPayments, canExecuteRegisterMetersReadings, or canExecuteSetPaymentPosReceiptUrl can be viewed or requested from this new form. When an app needs those mutation rights, the owner has no way to enable them through the service-user section, and saving a replacement set only submits the displayed booleans, leaving these mutation permissions at their defaults.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

that's ok, we've decided to limit abilities for now

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🧹 Nitpick comments (3)
apps/dev-portal-web/domains/miniapp/components/B2BApp/edit/service-user/AccessRightSetForm.tsx (3)

196-211: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Avoid nested setState inside another state's updater.

setInitialGroupedPermissions is called as a side effect inside the updater function passed to setGroupedPermissions. Updater functions should be pure (React may invoke them more than once, e.g. under Strict Mode double-invocation), so triggering another state update from within one is an anti-pattern even though it's currently harmless since the computed value is deterministic.

Since the new value only depends on currentRightSet and the static GROUPED_PERMISSIONS shape (not on the previous groupedPermissions state), you can compute it once and call both setters directly.

♻️ Proposed refactor
     useEffect(() => {
-        if (currentRightSet) {
-            setGroupedPermissions(prev => {
-                const newValue = prev.map(row => ({
-                    group: row.group,
-                    permissions: row.permissions.map(permission => ({
-                        key: permission.key,
-                        value: currentRightSet[permission.key] ?? permission.value,
-                    })),
-                }))
-                setInitialGroupedPermissions(newValue)
-
-                return newValue
-            })
-        }
+        if (!currentRightSet) return
+        const newValue: Array<RowType> = Object.entries(GROUPED_PERMISSIONS).map(([group, permissions]) => ({
+            group: group as keyof typeof GROUPED_PERMISSIONS,
+            permissions: permissions.map(permission => ({
+                key: permission,
+                value: currentRightSet[permission] ?? false,
+            })),
+        }))
+        setInitialGroupedPermissions(newValue)
+        setGroupedPermissions(newValue)
     }, [currentRightSet])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/dev-portal-web/domains/miniapp/components/B2BApp/edit/service-user/AccessRightSetForm.tsx`
around lines 196 - 211, Refactor the useEffect handling currentRightSet so it
computes the updated permission groups from GROUPED_PERMISSIONS and
currentRightSet once, then calls setGroupedPermissions and
setInitialGroupedPermissions separately. Remove the nested
setInitialGroupedPermissions call from the setGroupedPermissions updater and
avoid relying on previous state.

126-131: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Controlled Checkbox has no onChange handler.

The checked prop is set without onChange. This triggers a React "controlled input without onChange" warning; toggling only works because the parent row's onClick happens to update state on click. Add an explicit (no-op or delegating) onChange so the component is self-consistent and warning-free.

♻️ Proposed fix
-            render: (value: boolean) => <Checkbox checked={value}/>,
+            render: (value: boolean, record) => <Checkbox checked={value} onChange={() => onChange(record)}/>,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/dev-portal-web/domains/miniapp/components/B2BApp/edit/service-user/AccessRightSetForm.tsx`
around lines 126 - 131, Update the Checkbox rendered by the table column’s
render function to include an explicit onChange handler alongside
checked={value}; use the appropriate no-op or existing state-delegating behavior
while preserving the parent row click flow.

231-243: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Inconsistent AntD imperative API: static notification vs. Modal.useModal().

Modal.useModal() (line 262) is used correctly to get a context-aware contextHolder, but notification.success (line 235) uses the static call. AntD 5 documentation explicitly recommends against the static form because it "cannot consume context like dynamic theme, and ConfigProvider data will not work." Consider switching to notification.useNotification() (or the App wrapper) for consistency and correct theme propagation, mirroring the Modal pattern already used here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/dev-portal-web/domains/miniapp/components/B2BApp/edit/service-user/AccessRightSetForm.tsx`
around lines 231 - 243, Update the onCompleted callback in AccessRightSetForm to
use the context-aware notification.useNotification() API instead of the static
notification.success call. Mirror the existing Modal.useModal() pattern by
obtaining the notification API and contextHolder, render that contextHolder with
the form, and invoke success through the returned notification instance while
preserving the existing message, description, and duration.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/dev-portal-web/lang/ru.json`:
- Line 320: Correct the Russian grammar in the labels for canReadTicketComments
and canReadTicketCommentFiles by changing “к заявками” to “к заявкам”, matching
the casing used by the corresponding canManage labels.
- Line 329: Correct the typo in the Russian translation value for
pages.apps.b2b.id.sections.serviceUser.accessRightSetForm.permissions.canManageMeterReportingPeriods.label,
changing “счачи” to “сдачи” while leaving the key and surrounding translations
unchanged.

---

Nitpick comments:
In
`@apps/dev-portal-web/domains/miniapp/components/B2BApp/edit/service-user/AccessRightSetForm.tsx`:
- Around line 196-211: Refactor the useEffect handling currentRightSet so it
computes the updated permission groups from GROUPED_PERMISSIONS and
currentRightSet once, then calls setGroupedPermissions and
setInitialGroupedPermissions separately. Remove the nested
setInitialGroupedPermissions call from the setGroupedPermissions updater and
avoid relying on previous state.
- Around line 126-131: Update the Checkbox rendered by the table column’s render
function to include an explicit onChange handler alongside checked={value}; use
the appropriate no-op or existing state-delegating behavior while preserving the
parent row click flow.
- Around line 231-243: Update the onCompleted callback in AccessRightSetForm to
use the context-aware notification.useNotification() API instead of the static
notification.success call. Mirror the existing Modal.useModal() pattern by
obtaining the notification API and contextHolder, render that contextHolder with
the form, and invoke success through the returned notification instance while
preserving the existing message, description, and duration.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f4de232f-0d47-472d-8c02-1f86518b98b9

📥 Commits

Reviewing files that changed from the base of the PR and between 66a7529 and 0d75125.

📒 Files selected for processing (15)
  • apps/dev-portal-web/domains/miniapp/components/B2BApp/edit/service-user/AccessRightSetForm.module.css
  • apps/dev-portal-web/domains/miniapp/components/B2BApp/edit/service-user/AccessRightSetForm.tsx
  • apps/dev-portal-web/domains/miniapp/components/B2BApp/edit/service-user/DiffContainer.tsx
  • apps/dev-portal-web/domains/miniapp/components/B2BApp/edit/service-user/ServiceUserSection.tsx
  • apps/dev-portal-web/domains/miniapp/components/B2CApp/edit/service-user/ServiceUserSection.tsx
  • apps/dev-portal-web/domains/miniapp/components/User/edit/RegisterUserForm.tsx
  • apps/dev-portal-web/domains/miniapp/components/User/edit/RegisterUserModal/index.tsx
  • apps/dev-portal-web/domains/miniapp/constants/b2bAppAccessRightSet.ts
  • apps/dev-portal-web/domains/miniapp/hooks/useB2BMenuItems.tsx
  • apps/dev-portal-web/domains/miniapp/queries/B2BAppAccessRight.graphql
  • apps/dev-portal-web/domains/miniapp/queries/B2BAppAccessRightSet.graphql
  • apps/dev-portal-web/gql/index.ts
  • apps/dev-portal-web/lang/en.json
  • apps/dev-portal-web/lang/ru.json
  • apps/dev-portal-web/pages/apps/b2b/[id].tsx

"pages.apps.b2b.id.sections.serviceUser.accessRightSetForm.permissions.canManageTickets.label": "Управление заявками",
"pages.apps.b2b.id.sections.serviceUser.accessRightSetForm.permissions.canReadTicketFiles.label": "Просмотр вложений к заявкам",
"pages.apps.b2b.id.sections.serviceUser.accessRightSetForm.permissions.canManageTicketFiles.label": "Управление вложениями к заявкам",
"pages.apps.b2b.id.sections.serviceUser.accessRightSetForm.permissions.canReadTicketComments.label": "Просмотр комментариев к заявками",

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Grammar: "к заявками" should be "к заявкам".

Both canReadTicketComments and canReadTicketCommentFiles labels use the wrong case ("к заявками" instead of dative "к заявкам"), inconsistent with the correctly-cased canManageTicketComments/canManageTicketCommentFiles labels right below them.

✏️ Proposed fix
-  "pages.apps.b2b.id.sections.serviceUser.accessRightSetForm.permissions.canReadTicketComments.label": "Просмотр комментариев к заявками",
+  "pages.apps.b2b.id.sections.serviceUser.accessRightSetForm.permissions.canReadTicketComments.label": "Просмотр комментариев к заявкам",
-  "pages.apps.b2b.id.sections.serviceUser.accessRightSetForm.permissions.canReadTicketCommentFiles.label": "Просмотр вложений в комментариях к заявками",
+  "pages.apps.b2b.id.sections.serviceUser.accessRightSetForm.permissions.canReadTicketCommentFiles.label": "Просмотр вложений в комментариях к заявкам",

Also applies to: 322-322

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/dev-portal-web/lang/ru.json` at line 320, Correct the Russian grammar in
the labels for canReadTicketComments and canReadTicketCommentFiles by changing
“к заявками” to “к заявкам”, matching the casing used by the corresponding
canManage labels.

"pages.apps.b2b.id.sections.serviceUser.accessRightSetForm.permissions.canReadMeterReadings.label": "Просмотр показаний приборов учёта",
"pages.apps.b2b.id.sections.serviceUser.accessRightSetForm.permissions.canManageMeterReadings.label": "Управление показаниями приборов учёта",
"pages.apps.b2b.id.sections.serviceUser.accessRightSetForm.permissions.canReadMeterReportingPeriods.label": "Просмотр периодов сдачи показаний",
"pages.apps.b2b.id.sections.serviceUser.accessRightSetForm.permissions.canManageMeterReportingPeriods.label": "Управление периодами счачи показаний",

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Typo: "счачи" → "сдачи".

✏️ Proposed fix
-  "pages.apps.b2b.id.sections.serviceUser.accessRightSetForm.permissions.canManageMeterReportingPeriods.label": "Управление периодами счачи показаний",
+  "pages.apps.b2b.id.sections.serviceUser.accessRightSetForm.permissions.canManageMeterReportingPeriods.label": "Управление периодами сдачи показаний",
📝 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
"pages.apps.b2b.id.sections.serviceUser.accessRightSetForm.permissions.canManageMeterReportingPeriods.label": "Управление периодами счачи показаний",
"pages.apps.b2b.id.sections.serviceUser.accessRightSetForm.permissions.canManageMeterReportingPeriods.label": "Управление периодами сдачи показаний",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/dev-portal-web/lang/ru.json` at line 329, Correct the typo in the
Russian translation value for
pages.apps.b2b.id.sections.serviceUser.accessRightSetForm.permissions.canManageMeterReportingPeriods.label,
changing “счачи” to “сдачи” while leaving the key and surrounding translations
unchanged.

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
6.8% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant