Skip to content

Commit 81d1686

Browse files
authored
feat: lock CR UI once any reviewer has approved (#7056) (#7255)
1 parent 6c69f6e commit 81d1686

3 files changed

Lines changed: 67 additions & 36 deletions

File tree

api/poetry.lock

Lines changed: 5 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ flagsmith-ldap = { git = "https://github.com/flagsmith/flagsmith-ldap", tag = "v
213213
optional = true
214214

215215
[tool.poetry.group.workflows.dependencies]
216-
workflows-logic = { git = "https://github.com/flagsmith/flagsmith-workflows", tag = "v3.3.2" }
216+
workflows-logic = { git = "https://github.com/flagsmith/flagsmith-workflows", tag = "v3.4.0" }
217217

218218
[tool.poetry.group.licensing]
219219
optional = true

frontend/web/components/pages/ChangeRequestDetailPage.tsx

Lines changed: 61 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,8 @@ export const ChangeRequestPageInner: FC<ChangeRequestPageInnerType> = ({
531531
return `${matchingUser.first_name} ${matchingUser.last_name}`
532532
})
533533
const approved = !!approval && !!approval.approved_at
534+
// Locks the CR UI once any reviewer has approved.
535+
const hasApprovals = changeRequest.approvals.some((v) => !!v.approved_at)
534536
const isYourChangeRequest = changeRequest.user === AccountStore.getUser().id
535537

536538
const user =
@@ -548,7 +550,7 @@ export const ChangeRequestPageInner: FC<ChangeRequestPageInnerType> = ({
548550
<Button theme='secondary' onClick={deleteChangeRequest}>
549551
Delete
550552
</Button>
551-
{editChangeRequest && (
553+
{editChangeRequest && !hasApprovals && (
552554
<Button onClick={editChangeRequest} className='ml-2'>
553555
Edit
554556
</Button>
@@ -562,6 +564,14 @@ export const ChangeRequestPageInner: FC<ChangeRequestPageInnerType> = ({
562564
by {user ? `${user.first_name} ${user.last_name}` : 'Unknown user'}
563565
</PageTitle>
564566
<p className='mt-2'>{changeRequest.description}</p>
567+
{hasApprovals && isYours && !changeRequest.committed_at && (
568+
<div className='col-md-6 mb-4'>
569+
<InfoMessage>
570+
This change request has been approved and can no longer be edited.
571+
Delete it and create a new change request to make further changes.
572+
</InfoMessage>
573+
</div>
574+
)}
565575
{isScheduled && (
566576
<div className='col-md-6 mb-4'>
567577
<InfoMessage icon='calendar' title='Scheduled Change'>
@@ -584,15 +594,21 @@ export const ChangeRequestPageInner: FC<ChangeRequestPageInnerType> = ({
584594
<>
585595
{!Utils.getFlagsmithHasFeature('disable_users_as_reviewers') && (
586596
<div className='mb-4'>
587-
<SettingsButton onClick={() => setShowUsers(true)}>
588-
Assigned users
589-
</SettingsButton>
597+
{hasApprovals ? (
598+
<div className='font-weight-medium mb-2'>Assigned users</div>
599+
) : (
600+
<SettingsButton onClick={() => setShowUsers(true)}>
601+
Assigned users
602+
</SettingsButton>
603+
)}
590604
<Row className='mt-2'>
591605
{ownerUsers.length !== 0 &&
592606
ownerUsers.map((u) => (
593607
<Row
594608
key={u.id}
595-
onClick={() => removeOwner(u.id)}
609+
onClick={
610+
hasApprovals ? undefined : () => removeOwner(u.id)
611+
}
596612
className='chip'
597613
style={{
598614
marginBottom: 4,
@@ -602,53 +618,67 @@ export const ChangeRequestPageInner: FC<ChangeRequestPageInnerType> = ({
602618
<span className='font-weight-bold'>
603619
{u.first_name} {u.last_name}
604620
</span>
605-
<span className='chip-icon ion'>
606-
<IonIcon icon={close} />
607-
</span>
621+
{!hasApprovals && (
622+
<span className='chip-icon ion'>
623+
<IonIcon icon={close} />
624+
</span>
625+
)}
608626
</Row>
609627
))}
610628
</Row>
611-
<UserSelect
612-
users={orgUsers}
613-
value={ownerUsers && ownerUsers.map((v) => v.id)}
614-
onAdd={addOwner}
615-
onRemove={removeOwner}
616-
isOpen={showUsers}
617-
onToggle={() => setShowUsers(!showUsers)}
618-
/>
629+
{!hasApprovals && (
630+
<UserSelect
631+
users={orgUsers}
632+
value={ownerUsers && ownerUsers.map((v) => v.id)}
633+
onAdd={addOwner}
634+
onRemove={removeOwner}
635+
isOpen={showUsers}
636+
onToggle={() => setShowUsers(!showUsers)}
637+
/>
638+
)}
619639
</div>
620640
)}
621641
<div className='mb-4'>
622-
<SettingsButton onClick={() => setShowGroups(true)}>
623-
Assigned groups
624-
</SettingsButton>
642+
{hasApprovals ? (
643+
<div className='font-weight-medium mb-2'>Assigned groups</div>
644+
) : (
645+
<SettingsButton onClick={() => setShowGroups(true)}>
646+
Assigned groups
647+
</SettingsButton>
648+
)}
625649
<Row className='mt-2'>
626650
{!!ownerGroups?.length &&
627651
ownerGroups.map((g) => (
628652
<Row
629653
key={g.id}
630-
onClick={() => removeOwner(g.id, false)}
654+
onClick={
655+
hasApprovals ? undefined : () => removeOwner(g.id, false)
656+
}
631657
className='chip'
632658
style={{
633659
marginBottom: 4,
634660
marginTop: 4,
635661
}}
636662
>
637663
<span className='font-weight-bold'>{g.name}</span>
638-
<span className='chip-icon ion'>
639-
<IonIcon icon={close} />
640-
</span>
664+
{!hasApprovals && (
665+
<span className='chip-icon ion'>
666+
<IonIcon icon={close} />
667+
</span>
668+
)}
641669
</Row>
642670
))}
643671
</Row>
644-
<MyGroupsSelect
645-
orgId={AccountStore.getOrganisation().id}
646-
value={ownerGroups && ownerGroups.map((v) => v.id)}
647-
onAdd={addOwner}
648-
onRemove={removeOwner}
649-
isOpen={showGroups}
650-
onToggle={() => setShowGroups(!showGroups)}
651-
/>
672+
{!hasApprovals && (
673+
<MyGroupsSelect
674+
orgId={AccountStore.getOrganisation().id}
675+
value={ownerGroups && ownerGroups.map((v) => v.id)}
676+
onAdd={addOwner}
677+
onRemove={removeOwner}
678+
isOpen={showGroups}
679+
onToggle={() => setShowGroups(!showGroups)}
680+
/>
681+
)}
652682
</div>
653683
</>
654684
}

0 commit comments

Comments
 (0)