feat: integrated secret metadata to ui

This commit is contained in:
Sheen Capadngan
2024-12-19 22:02:20 +08:00
parent 8c98565715
commit ef688efc8d
13 changed files with 148 additions and 24 deletions
+4 -2
View File
@@ -84,7 +84,8 @@ export const useUpdateSecretV3 = ({
secretReminderRepeatDays,
secretReminderNote,
newSecretName,
skipMultilineEncoding
skipMultilineEncoding,
secretMetadata
}) => {
const { data } = await apiRequest.patch(`/api/v3/secrets/raw/${secretKey}`, {
workspaceId,
@@ -97,7 +98,8 @@ export const useUpdateSecretV3 = ({
newSecretName,
secretComment,
tagIds,
secretValue
secretValue,
secretMetadata
});
return data;
},
+2 -1
View File
@@ -67,7 +67,8 @@ export const mergePersonalSecrets = (rawSecrets: SecretV3Raw[]) => {
updatedAt: el.updatedAt,
version: el.version,
skipMultilineEncoding: el.skipMultilineEncoding,
path: el.secretPath
path: el.secretPath,
secretMetadata: el.secretMetadata
};
if (el.type === SecretType.Personal) {
+3
View File
@@ -48,6 +48,7 @@ export type SecretV3RawSanitized = {
overrideAction?: string;
folderId?: string;
skipMultilineEncoding?: boolean;
secretMetadata?: { key: string; value: string }[];
};
export type SecretV3Raw = {
@@ -63,6 +64,7 @@ export type SecretV3Raw = {
secretComment?: string;
secretReminderNote?: string;
secretReminderRepeatDays?: number;
secretMetadata?: { key: string; value: string }[];
skipMultilineEncoding?: boolean;
metadata?: Record<string, string>;
tags?: WsTag[];
@@ -148,6 +150,7 @@ export type TUpdateSecretsV3DTO = {
secretReminderRepeatDays?: number | null;
secretReminderNote?: string | null;
tagIds?: string[];
secretMetadata?: { key: string; value: string }[];
};
export type TDeleteSecretsV3DTO = {
@@ -8,7 +8,8 @@ import {
faClock,
faPlus,
faShare,
faTag
faTag,
faTrash
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod";
@@ -26,6 +27,7 @@ import {
DropdownMenuLabel,
DropdownMenuTrigger,
FormControl,
FormLabel,
IconButton,
Input,
Switch,
@@ -87,11 +89,16 @@ export const SecretDetailSidebar = ({
const { permission } = useProjectPermission();
const { fields, append, remove } = useFieldArray({
const tagFields = useFieldArray({
control,
name: "tags"
});
const metadataFormFields = useFieldArray({
control,
name: "secretMetadata"
});
const secretKey = secret?.key || "";
const selectedTags = watch("tags", []) || [];
const selectedTagsGroupById = selectedTags.reduce<Record<string, boolean>>(
@@ -153,10 +160,10 @@ export const SecretDetailSidebar = ({
if (selectedTagsGroupById?.[tag.id]) {
const tagPos = selectedTags.findIndex(({ id }) => id === tag.id);
if (tagPos !== -1) {
remove(tagPos);
tagFields.remove(tagPos);
}
} else {
append(tag);
tagFields.append(tag);
}
};
@@ -277,9 +284,73 @@ export const SecretDetailSidebar = ({
)}
/>
)}
<FormControl label="Metadata">
<div className="flex flex-col space-y-2">
{metadataFormFields.fields.map(({ id: metadataFieldId }, i) => (
<div key={metadataFieldId} className="flex items-end space-x-2">
<div className="flex-grow">
{i === 0 && <span className="text-xs text-mineshaft-400">Key</span>}
<Controller
control={control}
name={`secretMetadata.${i}.key`}
render={({ field, fieldState: { error } }) => (
<FormControl
isError={Boolean(error?.message)}
errorText={error?.message}
className="mb-0"
>
<Input {...field} className="max-h-8" />
</FormControl>
)}
/>
</div>
<div className="flex-grow">
{i === 0 && (
<FormLabel
label="Value"
className="text-xs text-mineshaft-400"
isOptional
/>
)}
<Controller
control={control}
name={`secretMetadata.${i}.value`}
render={({ field, fieldState: { error } }) => (
<FormControl
isError={Boolean(error?.message)}
errorText={error?.message}
className="mb-0"
>
<Input {...field} className="max-h-8" />
</FormControl>
)}
/>
</div>
<IconButton
ariaLabel="delete key"
className="bottom-0.5 max-h-8"
variant="outline_bg"
onClick={() => metadataFormFields.remove(i)}
>
<FontAwesomeIcon icon={faTrash} />
</IconButton>
</div>
))}
<div className="mt-2">
<Button
leftIcon={<FontAwesomeIcon icon={faPlus} />}
size="xs"
variant="outline_bg"
onClick={() => metadataFormFields.append({ key: "", value: "" })}
>
Add Key
</Button>
</div>
</div>
</FormControl>
<FormControl label="Tags" className="">
<div className="grid auto-cols-min grid-flow-col gap-2 overflow-hidden pt-2">
{fields.map(({ tagColor, id: formId, slug, id }) => (
{tagFields.fields.map(({ tagColor, id: formId, slug, id }) => (
<Tag
className="flex w-min items-center space-x-2"
key={formId}
@@ -78,7 +78,8 @@ export const SecretListView = ({
tags,
skipMultilineEncoding,
newKey,
secretId
secretId,
secretMetadata
}: Partial<{
value: string;
comment: string;
@@ -88,6 +89,7 @@ export const SecretListView = ({
skipMultilineEncoding: boolean;
newKey: string;
secretId: string;
secretMetadata?: { key: string; value: string }[];
}> = {}
) => {
if (operation === "delete") {
@@ -115,7 +117,8 @@ export const SecretListView = ({
secretReminderRepeatDays: reminderRepeatDays,
secretReminderNote: reminderNote,
skipMultilineEncoding,
newSecretName: newKey
newSecretName: newKey,
secretMetadata
});
return;
}
@@ -138,7 +141,10 @@ export const SecretListView = ({
const handleSaveSecret = useCallback(
async (
orgSecret: SecretV3RawSanitized,
modSecret: Omit<SecretV3RawSanitized, "tags"> & { tags?: { id: string }[] },
modSecret: Omit<SecretV3RawSanitized, "tags"> & {
tags?: { id: string }[];
secretMetadata?: { key: string; value: string }[];
},
cb?: () => void
) => {
const { key: oldKey } = orgSecret;
@@ -151,7 +157,8 @@ export const SecretListView = ({
tags,
comment,
reminderRepeatDays,
reminderNote
reminderNote,
secretMetadata
} = modSecret;
const hasKeyChanged = oldKey !== key && key;
@@ -166,7 +173,8 @@ export const SecretListView = ({
"comment",
"skipMultilineEncoding",
"reminderRepeatDays",
"reminderNote"
"reminderNote",
"secretMetadata"
] as const
).every((el) => orgSecret[el] === modSecret[el]) && isSameTags;
@@ -199,7 +207,8 @@ export const SecretListView = ({
reminderNote,
secretId: orgSecret.id,
newKey: hasKeyChanged ? key : undefined,
skipMultilineEncoding: modSecret.skipMultilineEncoding
skipMultilineEncoding: modSecret.skipMultilineEncoding,
secretMetadata
});
if (cb) cb();
}
@@ -47,7 +47,13 @@ export const formSchema = z.object({
.nullable()
.optional(),
reminderNote: z.string().trim().nullable().optional(),
secretMetadata: z
.object({
key: z.string().trim().min(1),
value: z.string().trim().default("")
})
.array()
.optional(),
tags: z
.object({
id: z.string(),