Skip to content

Commit 988de7f

Browse files
committed
Merge branch 'develop' of github.com:devtron-labs/dashboard into feat/node-list-usage-ui
2 parents 0f4a96a + 2f764e0 commit 988de7f

File tree

4 files changed

+132
-64
lines changed

4 files changed

+132
-64
lines changed

src/components/ClusterNodes/NodeDetails.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ import {
4444
RESOURCE_BROWSER_ROUTES,
4545
getUrlWithSearchParams,
4646
ResourceBrowserActionMenuEnum,
47+
UNSAVED_CHANGES_PROMPT_MESSAGE,
48+
usePrompt,
4749
} from '@devtron-labs/devtron-fe-common-lib'
48-
import { useParams, useLocation, useHistory, generatePath } from 'react-router-dom'
50+
import { useParams, useLocation, useHistory, generatePath, Prompt } from 'react-router-dom'
4951
import YAML from 'yaml'
5052
import * as jsonpatch from 'fast-json-patch'
5153
import { applyPatch } from 'fast-json-patch'
@@ -111,6 +113,10 @@ const NodeDetails = ({ lowercaseKindToResourceGroupMap, updateTabUrl }: ClusterL
111113
const queryParams = new URLSearchParams(location.search)
112114
const { push, replace } = useHistory()
113115

116+
const hasUnsavedChanges = (nodeDetail?.manifest ? YAMLStringify(nodeDetail.manifest) : '') !== modifiedManifest
117+
118+
usePrompt({ shouldPrompt: hasUnsavedChanges })
119+
114120
const getData = (_patchdata: jsonpatch.Operation[]) => {
115121
setLoader(true)
116122
setErrorResponseCode(null)
@@ -1073,6 +1079,7 @@ const NodeDetails = ({ lowercaseKindToResourceGroupMap, updateTabUrl }: ClusterL
10731079
<Progressing pageLoader size={32} />
10741080
) : (
10751081
<>
1082+
<Prompt when={hasUnsavedChanges} message={UNSAVED_CHANGES_PROMPT_MESSAGE} />
10761083
{renderNodeDetailsTabs()}
10771084
{renderTabContent()}
10781085
{showCordonNodeDialog && (

src/components/v2/appDetails/appDetails.type.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ export enum ManifestCodeEditorMode {
465465
EDIT = 'edit',
466466
APPLY_CHANGES = 'applyChanges',
467467
CANCEL = 'cancel',
468+
'REVIEW' = 'review',
468469
}
469470

470471
export type ManifestActionPropsType = Omit<

src/components/v2/appDetails/k8Resource/nodeDetail/NodeDetail.component.tsx

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,11 @@ const NodeDetailComponent = ({
370370
setManifestCodeEditorMode(ManifestCodeEditorMode.EDIT)
371371
}
372372

373+
const handleManifestReview = () => {
374+
setManifestFormConfigurationType(ConfigurationType.YAML)
375+
setManifestCodeEditorMode(ManifestCodeEditorMode.REVIEW)
376+
}
377+
373378
const handleManifestCompareWithDesired = () => setShowManifestCompareView(true)
374379

375380
const renderPodTerminal = (): JSX.Element => {
@@ -425,7 +430,7 @@ const NodeDetailComponent = ({
425430
{isManifestEditable && manifestCodeEditorMode && !showManifestCompareView && !isResourceMissing && (
426431
<>
427432
<div className="ml-12 mr-12 tab-cell-border" />
428-
{manifestCodeEditorMode === ManifestCodeEditorMode.EDIT ? (
433+
{manifestCodeEditorMode === ManifestCodeEditorMode.EDIT && (
429434
<div className="flex dc__gap-12">
430435
{ToggleManifestConfigurationMode && isManifestEditable && (
431436
<ToggleManifestConfigurationMode
@@ -435,6 +440,25 @@ const NodeDetailComponent = ({
435440
/>
436441
)}
437442

443+
<button
444+
type="button"
445+
className={`dc__unset-button-styles cb-5 fs-12 lh-1-5 fw-6 flex dc__gap-4 ${doesManifestGUIContainsError ? 'dc__disabled' : ''}`}
446+
onClick={handleManifestReview}
447+
disabled={doesManifestGUIContainsError}
448+
>
449+
<span>Review and save changes</span>
450+
</button>
451+
<button
452+
type="button"
453+
className="dc__unset-button-styles fs-12 lh-1-5 fw-6 flex cn-6"
454+
onClick={handleManifestCancel}
455+
>
456+
Cancel
457+
</button>
458+
</div>
459+
)}
460+
{manifestCodeEditorMode === ManifestCodeEditorMode.REVIEW && (
461+
<div className="flex dc__gap-12">
438462
<button
439463
type="button"
440464
className={`dc__unset-button-styles cb-5 fs-12 lh-1-5 fw-6 flex dc__gap-4 ${doesManifestGUIContainsError ? 'dc__disabled' : ''}`}
@@ -452,16 +476,18 @@ const NodeDetailComponent = ({
452476
Cancel
453477
</button>
454478
</div>
455-
) : (
456-
<button
457-
type="button"
458-
className="dc__unset-button-styles cb-5 fs-12 lh-1-5 fw-6 flex dc__gap-4"
459-
onClick={handleManifestEdit}
460-
>
461-
<ICPencil className="icon-dim-16 scb-5" />
462-
<span>Edit live manifest</span>
463-
</button>
464479
)}
480+
{manifestCodeEditorMode !== ManifestCodeEditorMode.EDIT &&
481+
manifestCodeEditorMode !== ManifestCodeEditorMode.REVIEW && (
482+
<button
483+
type="button"
484+
className="dc__unset-button-styles cb-5 fs-12 lh-1-5 fw-6 flex dc__gap-4"
485+
onClick={handleManifestEdit}
486+
>
487+
<ICPencil className="icon-dim-16 scb-5" />
488+
<span>Edit live manifest</span>
489+
</button>
490+
)}
465491
</>
466492
)}
467493
{manifestCodeEditorMode === ManifestCodeEditorMode.READ &&

src/components/v2/appDetails/k8Resource/nodeDetail/NodeDetailTabs/Manifest.component.tsx

Lines changed: 87 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { useEffect, useMemo, useState } from 'react'
18-
import { useHistory, useLocation, useParams, useRouteMatch } from 'react-router-dom'
17+
import { useEffect, useMemo, useRef, useState } from 'react'
18+
import { Prompt, useHistory, useLocation, useParams, useRouteMatch } from 'react-router-dom'
1919
import Tippy from '@tippyjs/react'
2020
import YAML from 'yaml'
2121

@@ -24,6 +24,7 @@ import {
2424
Checkbox,
2525
CHECKBOX_VALUE,
2626
CodeEditor,
27+
CodeEditorProps,
2728
ConditionalWrap,
2829
ConfigurationType,
2930
DeploymentAppTypes,
@@ -36,8 +37,10 @@ import {
3637
TOAST_ACCESS_DENIED,
3738
ToastManager,
3839
ToastVariantType,
40+
UNSAVED_CHANGES_PROMPT_MESSAGE,
3941
useEffectAfterMount,
4042
useMainContext,
43+
usePrompt,
4144
YAMLStringify,
4245
} from '@devtron-labs/devtron-fe-common-lib'
4346

@@ -140,9 +143,12 @@ const ManifestComponent = ({
140143
const [lockedKeys, setLockedKeys] = useState<string[]>(null)
141144
const [showLockedDiffModal, setShowLockedDiffModal] = useState(false)
142145

146+
const previousEditorState = useRef('')
147+
143148
const { isSuperAdmin } = useMainContext() // to show the cluster meta data at the bottom
144149
// Cancel is an intermediate state wherein edit is true
145150
const isEditMode =
151+
manifestCodeEditorMode === ManifestCodeEditorMode.REVIEW ||
146152
manifestCodeEditorMode === ManifestCodeEditorMode.EDIT ||
147153
manifestCodeEditorMode === ManifestCodeEditorMode.CANCEL
148154

@@ -683,6 +689,41 @@ const ManifestComponent = ({
683689
handleStickDynamicTabsToTop?.()
684690
}
685691

692+
const hasUnsavedChanges = previousEditorState.current !== getCodeEditorValue()
693+
694+
usePrompt({ shouldPrompt: hasUnsavedChanges })
695+
696+
const getCodeEditorProps = () => {
697+
if (!previousEditorState.current) {
698+
previousEditorState.current = getCodeEditorValue()
699+
}
700+
701+
if (showManifestCompareView) {
702+
return {
703+
diffView: true,
704+
originalValue: desiredManifest,
705+
modifiedValue: getCodeEditorValue(),
706+
onModifiedValueChange: handleEditorValueChange,
707+
} as CodeEditorProps<true>
708+
}
709+
710+
if (manifestCodeEditorMode === ManifestCodeEditorMode.REVIEW) {
711+
return {
712+
diffView: true,
713+
originalValue: previousEditorState.current,
714+
modifiedValue: getCodeEditorValue(),
715+
onModifiedValueChange: handleEditorValueChange,
716+
} as CodeEditorProps<true>
717+
}
718+
719+
return {
720+
diffView: false,
721+
value: getCodeEditorValue(),
722+
onChange: handleEditorValueChange,
723+
autoFocus: isEditMode,
724+
} as CodeEditorProps<false>
725+
}
726+
686727
const renderContent = () => {
687728
if (showGUIView) {
688729
return (
@@ -714,19 +755,7 @@ const ManifestComponent = ({
714755
height={isResourceBrowserView || isDynamicTabsStuck ? 'fitToParent' : '100%'}
715756
onSearchPanelOpen={handleStickDynamicTabsToTopWrapper}
716757
onSearchBarAction={handleStickDynamicTabsToTopWrapper}
717-
{...(showManifestCompareView
718-
? {
719-
diffView: true,
720-
originalValue: desiredManifest,
721-
modifiedValue: getCodeEditorValue(),
722-
onModifiedValueChange: handleEditorValueChange,
723-
}
724-
: {
725-
diffView: false,
726-
value: getCodeEditorValue(),
727-
onChange: handleEditorValueChange,
728-
autoFocus: isEditMode,
729-
})}
758+
{...getCodeEditorProps()}
730759
>
731760
{renderEditorInfo(true)}
732761

@@ -761,50 +790,55 @@ const ManifestComponent = ({
761790
)
762791
}
763792

764-
return isDeleted ? (
765-
<div className="h-100 flex-grow-1">
766-
<MessageUI msg="This resource no longer exists" size={32} />
767-
</div>
768-
) : (
769-
<div
770-
className={`flexbox-col flex-grow-1 ${isResourceBrowserView ? 'dc__overflow-auto' : ''}`}
771-
data-testid="app-manifest-container"
772-
style={{
773-
background: 'var(--terminal-bg)',
774-
}}
775-
>
776-
{error && !loading && <MessageUI msg="Manifest not available" size={24} />}
777-
{!error && (
793+
return (
794+
<>
795+
<Prompt when={hasUnsavedChanges} message={UNSAVED_CHANGES_PROMPT_MESSAGE} />
796+
{isDeleted ? (
797+
<div className="h-100 flex-grow-1">
798+
<MessageUI msg="This resource no longer exists" size={32} />
799+
</div>
800+
) : (
778801
<div
779-
className={`${
780-
manifestFormConfigurationType === ConfigurationType.GUI ? 'bg__primary' : ''
781-
} flexbox-col flex-grow-1 ${isResourceBrowserView ? 'dc__overflow-hidden' : ''}`}
802+
className={`flexbox-col flex-grow-1 ${isResourceBrowserView ? 'dc__overflow-auto' : ''}`}
803+
data-testid="app-manifest-container"
804+
style={{
805+
background: 'var(--terminal-bg)',
806+
}}
782807
>
783-
{isResourceMissing && !loading && !showManifestCompareView ? (
784-
<MessageUI
785-
msg="Manifest not available"
786-
size={24}
787-
isShowActionButton={showDesiredAndCompareManifest}
788-
actionButtonText="Recreate this resource"
789-
onActionButtonClick={recreateResource}
808+
{error && !loading && <MessageUI msg="Manifest not available" size={24} />}
809+
{!error && (
810+
<div
811+
className={`${
812+
manifestFormConfigurationType === ConfigurationType.GUI ? 'bg__primary' : ''
813+
} flexbox-col flex-grow-1 ${isResourceBrowserView ? 'dc__overflow-hidden' : ''}`}
814+
>
815+
{isResourceMissing && !loading && !showManifestCompareView ? (
816+
<MessageUI
817+
msg="Manifest not available"
818+
size={24}
819+
isShowActionButton={showDesiredAndCompareManifest}
820+
actionButtonText="Recreate this resource"
821+
onActionButtonClick={recreateResource}
822+
/>
823+
) : (
824+
renderContent()
825+
)}
826+
</div>
827+
)}
828+
829+
{showLockedDiffModal && ShowIneligibleChangesModal && (
830+
<ShowIneligibleChangesModal
831+
handleCallApplyChangesAPI={handleCallApplyChangesAPI}
832+
uneditedManifest={uneditedManifest}
833+
// NOTE: a check on modifiedManifest is made before this component is rendered
834+
editedManifest={YAML.parse(modifiedManifest)}
835+
handleModalClose={handleCloseShowLockedDiffModal}
836+
lockedKeys={lockedKeys}
790837
/>
791-
) : (
792-
renderContent()
793838
)}
794839
</div>
795840
)}
796-
797-
{showLockedDiffModal && ShowIneligibleChangesModal && (
798-
<ShowIneligibleChangesModal
799-
handleCallApplyChangesAPI={handleCallApplyChangesAPI}
800-
uneditedManifest={uneditedManifest}
801-
// NOTE: a check on modifiedManifest is made before this component is rendered
802-
editedManifest={YAML.parse(modifiedManifest)}
803-
handleModalClose={handleCloseShowLockedDiffModal}
804-
lockedKeys={lockedKeys}
805-
/>
806-
)}
807-
</div>
841+
</>
808842
)
809843
}
810844

0 commit comments

Comments
 (0)