diff --git a/frontend/src/hooks/useNavigationBlocker.tsx b/frontend/src/hooks/useNavigationBlocker.tsx index 442849a37..da461066b 100644 --- a/frontend/src/hooks/useNavigationBlocker.tsx +++ b/frontend/src/hooks/useNavigationBlocker.tsx @@ -1,24 +1,34 @@ import { useCallback } from "react"; import { useBlocker } from "@tanstack/react-router"; +import { BatchContext, useBatchModeActions } from "@app/pages/secret-manager/SecretDashboardPage/SecretMainPage.store"; type TNavigationBlockerReturn = { isBlocked: boolean; }; -export const useNavigationBlocker = ( - shouldBlock: boolean, - message: string = "Are you sure you want to leave? You may have unsaved changes." -): TNavigationBlockerReturn => { +export const useNavigationBlocker = ({ + shouldBlock = false, + message = "Are you sure you want to leave? You may have unsaved changes.", + context +}: { + shouldBlock: boolean; + message: string; + context: BatchContext; +}): TNavigationBlockerReturn => { + const { clearAllPendingChanges } = useBatchModeActions(); const blockerFn = useCallback(() => { if (!shouldBlock) return false; // eslint-disable-next-line no-alert const confirmed = window.confirm(message); + if (confirmed) { + clearAllPendingChanges(context); + } return !confirmed; - }, [shouldBlock, message]); + }, [shouldBlock, message, context]); - useBlocker(blockerFn); + useBlocker(blockerFn, shouldBlock); return { isBlocked: shouldBlock diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/SecretMainPage.store.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/SecretMainPage.store.tsx index 343596116..71b912dd0 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/SecretMainPage.store.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/SecretMainPage.store.tsx @@ -346,7 +346,7 @@ const createBatchModeStore: StateCreator if (change.resourceType === "secret") { const existingSecret = state.existingSecretKeys.has(change.secretKey) || - newChanges.secrets.some((s) => s.secretKey === change.secretKey); + newChanges.secrets.some((s) => (s.secretKey === change.secretKey && s.type !== PendingAction.Create) || (change.type === PendingAction.Create && change.originalKey !== change.secretKey && s.secretKey === change.secretKey)); if (change.type === PendingAction.Create && existingSecret) { return { pendingChanges: newChanges }; diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretItem.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretItem.tsx index dd41ebafa..ae6f70d61 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretItem.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretItem.tsx @@ -200,21 +200,9 @@ export const SecretItem = memo( isAutoSavingRef.current = true; try { - const hasDirectReferences = importedBy?.some(({ folders }) => - folders?.some(({ secrets }) => - secrets?.some(({ referencedSecretKey }) => referencedSecretKey === secret.key) - ) - ); - - if (hasDirectReferences) { - await onSaveSecret(secret, { ...secret, ...data }, () => { - reset(); - }); - } else { - await onSaveSecret(secret, { ...secret, ...data }, () => { - reset(); - }); - } + await onSaveSecret(secret, { ...secret, ...data }, () => { + reset(); + }); } catch (error) { console.error("Auto-save failed:", error); } finally { @@ -268,7 +256,7 @@ export const SecretItem = memo( ); const isReadOnlySecret = - isReadOnly || isRotatedSecret || (isPending && pendingAction !== PendingAction.Update); + isReadOnly || isRotatedSecret || (isPending && pendingAction === PendingAction.Delete); const { secretValueHidden } = secret; @@ -355,7 +343,6 @@ export const SecretItem = memo( isDirty && "border-primary-400/50", isRotatedSecret && "bg-mineshaft-700/60", isPending && "bg-mineshaft-700/60", - isInAutoSaveMode && "border-primary-400/75 bg-primary-900/20", pendingAction === PendingAction.Delete && "border-l-2 border-l-red-600/75", pendingAction === PendingAction.Update && "border-l-2 border-l-yellow-600/75", pendingAction === PendingAction.Create && "border-l-2 border-l-green-600/75" @@ -421,14 +408,6 @@ export const SecretItem = memo( tabIndex={0} role="button" > - {isInAutoSaveMode && ( - -
- -
-
- )} - {secretValueHidden && !isOverriden && ( )} + {pendingAction === PendingAction.Create && ( +
+ + + {(isAllowed) => ( + + + + + + + + )} + + + Add tags to this secret + {tags.map((tag) => { + const { id: tagId, slug, color } = tag; + + const isTagSelected = selectedTagsGroupById?.[tagId]; + return ( + handleTagSelect(tag)} + key={`${secret.id}-${tagId}`} + icon={ + isTagSelected && ( + + ) + } + iconPos="right" + > +
+
+ {slug} +
+ + ); + })} + + + + + +
+ )}
{isInAutoSaveMode ? ( @@ -726,11 +793,7 @@ export const SecretItem = memo( animate={{ x: 0, opacity: 1 }} exit={{ x: -10, opacity: 0 }} > - -
- -
-
+
) : !isDirty ? ( isPending ? ( @@ -757,19 +820,21 @@ export const SecretItem = memo( - handleDeletePending(secret)} - > - - + + handleDeletePending(secret)} + > + + + ) : ( diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretListView.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretListView.tsx index aa18a3a23..cf64bcf73 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretListView.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretListView.tsx @@ -87,8 +87,15 @@ export const SecretListView = ({ const { toggle: toggleSelectedSecret } = useSelectedSecretActions(); const { isBatchMode, pendingChanges } = useBatchMode(); useNavigationBlocker( - pendingChanges.secrets.length > 0 || pendingChanges.folders.length > 0, - "You have unsaved work. Are you sure you want to leave?" + { + shouldBlock: pendingChanges.secrets.length > 0 || pendingChanges.folders.length > 0, + message: "You have unsaved changes. If you leave now, your work will be lost. Do you want to continue?", + context: { + workspaceId, + environment, + secretPath + } + } ); const { addPendingChange } = useBatchModeActions(); @@ -316,7 +323,7 @@ export const SecretListView = ({ secretValue: value || "", secretComment: comment || "", skipMultilineEncoding: modSecret.skipMultilineEncoding || false, - tags: tags?.map((tag) => ({ id: tag.id, slug: tag.name || "" })) || [], + tags: tags?.map((tag) => ({ id: tag.id, slug: tag.name || tag.slug || "" })) || [], secretMetadata: secretMetadata || [], timestamp: Date.now(), resourceType: "secret", diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretListView.utils.ts b/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretListView.utils.ts index 129b53c06..53bf28d08 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretListView.utils.ts +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretListView.utils.ts @@ -13,7 +13,8 @@ import { faLock, faProjectDiagram, faShare, - faTags + faTags, + faTrash } from "@fortawesome/free-solid-svg-icons"; import { z } from "zod"; @@ -82,7 +83,8 @@ export enum FontAwesomeSpriteName { ReplicatedSecretKey = "secret-replicated", ShareSecret = "share-secret", KeyLock = "key-lock", - SecretReferenceTree = "secret-reference-tree" + SecretReferenceTree = "secret-reference-tree", + Trash = "trash" } // this is an optimization technique @@ -101,5 +103,6 @@ export const FontAwesomeSpriteSymbols = [ { icon: faClone, symbol: FontAwesomeSpriteName.ReplicatedSecretKey }, { icon: faShare, symbol: FontAwesomeSpriteName.ShareSecret }, { icon: faLock, symbol: FontAwesomeSpriteName.KeyLock }, - { icon: faProjectDiagram, symbol: FontAwesomeSpriteName.SecretReferenceTree } + { icon: faProjectDiagram, symbol: FontAwesomeSpriteName.SecretReferenceTree }, + { icon: faTrash, symbol: FontAwesomeSpriteName.Trash } ];