feat(reminders): specify recipients

This commit is contained in:
Daniel Hougaard
2025-04-19 06:59:22 +04:00
parent f1b1d6f480
commit 9982ade219
25 changed files with 295 additions and 18 deletions
@@ -83,6 +83,7 @@ export const useUpdateSecretV3 = ({
secretComment,
secretReminderRepeatDays,
secretReminderNote,
secretReminderRecipients,
newSecretName,
skipMultilineEncoding,
secretMetadata
@@ -93,6 +94,7 @@ export const useUpdateSecretV3 = ({
type,
secretReminderNote,
secretReminderRepeatDays,
secretReminderRecipients,
secretPath,
skipMultilineEncoding,
newSecretName,
+2
View File
@@ -42,6 +42,7 @@ export type SecretV3RawSanitized = {
comment?: string;
reminderRepeatDays?: number | null;
reminderNote?: string | null;
reminderRecipients?: string[] | null;
tags?: WsTag[];
createdAt: string;
updatedAt: string;
@@ -177,6 +178,7 @@ export type TUpdateSecretsV3DTO = {
secretReminderNote?: string | null;
tagIds?: string[];
secretMetadata?: { key: string; value: string }[];
secretReminderRecipients?: string[] | null;
};
export type TDeleteSecretsV3DTO = {
@@ -6,10 +6,28 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { twMerge } from "tailwind-merge";
import { z } from "zod";
import { Button, FormControl, Input, Modal, ModalContent, TextArea } from "@app/components/v2";
import {
Button,
FilterableSelect,
FormControl,
Input,
Modal,
ModalContent,
TextArea
} from "@app/components/v2";
import { useWorkspace } from "@app/context";
import { useGetWorkspaceUsers } from "@app/hooks/api";
const ReminderFormSchema = z.object({
note: z.string().optional().nullable(),
recipients: z
.array(
z.object({
label: z.string(),
value: z.string().uuid()
})
)
.optional(),
days: z
.number()
.min(1, { message: "Must be at least 1 day" })
@@ -22,6 +40,7 @@ interface ReminderFormProps {
isOpen: boolean;
repeatDays?: number | null;
note?: string | null;
recipients?: string[] | null;
onOpenChange: (isOpen: boolean, data?: TReminderFormSchema) => void;
}
@@ -29,8 +48,13 @@ export const CreateReminderForm = ({
isOpen,
onOpenChange,
repeatDays,
note
note,
recipients
}: ReminderFormProps) => {
const { currentWorkspace } = useWorkspace();
const { data: members = [] } = useGetWorkspaceUsers(currentWorkspace?.id);
const {
register,
control,
@@ -59,6 +83,19 @@ export const CreateReminderForm = ({
}
}, [isOpen]);
useEffect(() => {
// On initial load, filter the members to only include the recipients
if (members.length) {
const filteredMembers = members.filter((m) => recipients?.includes(m.id));
reset({
recipients: filteredMembers.map((m) => ({
label: m.user.username || m.user.email,
value: m.user.id
}))
});
}
}, [members, isOpen]);
return (
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
<ModalContent
@@ -74,6 +111,7 @@ export const CreateReminderForm = ({
render={({ field, fieldState }) => (
<>
<FormControl
isRequired
className="mb-0"
label="Reminder Interval (in days)"
isError={Boolean(fieldState.error)}
@@ -110,6 +148,37 @@ export const CreateReminderForm = ({
{...register("note")}
/>
</FormControl>
<Controller
control={control}
name="recipients"
render={({ field }) => (
<FormControl
tooltipText={
<div>
Select users to receive reminders.
<br />
<br /> If none are selected, all project members will receive the reminder.
</div>
}
label="Recipients"
className="mb-0"
>
<FilterableSelect
className="w-full"
placeholder="Select reminder recipients..."
isMulti
name="recipients"
options={members.map((member) => ({
label: member.user.username || member.user.email,
value: member.user.id
}))}
value={field.value}
onChange={field.onChange}
/>
</FormControl>
)}
/>
</div>
<div className="mt-7 flex items-center space-x-4">
<Button
@@ -220,11 +220,12 @@ export const SecretDetailSidebar = ({
const handleReminderSubmit = async (
reminderRepeatDays: number | null | undefined,
reminderNote: string | null | undefined
reminderNote: string | null | undefined,
reminderRecipients: string[] | undefined
) => {
await onSaveSecret(
secret,
{ ...secret, reminderRepeatDays, reminderNote, isReminderEvent: true },
{ ...secret, reminderRepeatDays, reminderNote, isReminderEvent: true, reminderRecipients },
() => {}
);
};
@@ -233,7 +234,7 @@ export const SecretDetailSidebar = ({
const secretReminderRepeatDays = watch("reminderRepeatDays");
const secretReminderNote = watch("reminderNote");
const secretReminderRecipients = watch("reminderRecipients");
const getModifiedByIcon = (userType: string | undefined | null) => {
switch (userType) {
case ActorType.USER:
@@ -290,14 +291,20 @@ export const SecretDetailSidebar = ({
<CreateReminderForm
repeatDays={secretReminderRepeatDays}
note={secretReminderNote}
recipients={secretReminderRecipients}
isOpen={createReminderFormOpen}
onOpenChange={(_, data) => {
setCreateReminderFormOpen.toggle();
if (data) {
const recipients = data.recipients?.length
? data.recipients.map((recipient) => recipient.value)
: undefined;
setValue("reminderRepeatDays", data.days, { shouldDirty: false });
setValue("reminderNote", data.note, { shouldDirty: false });
handleReminderSubmit(data.days, data.note);
setValue("reminderRecipients", recipients, { shouldDirty: false });
handleReminderSubmit(data.days, data.note, recipients);
}
}}
/>
@@ -85,6 +85,7 @@ export const SecretListView = ({
comment,
reminderRepeatDays,
reminderNote,
reminderRecipients,
tags,
skipMultilineEncoding,
newKey,
@@ -96,6 +97,7 @@ export const SecretListView = ({
comment: string;
reminderRepeatDays: number | null;
reminderNote: string | null;
reminderRecipients?: string[] | null;
tags: string[];
skipMultilineEncoding: boolean;
newKey: string;
@@ -131,6 +133,7 @@ export const SecretListView = ({
secretComment: comment,
secretReminderRepeatDays: reminderRepeatDays,
secretReminderNote: reminderNote,
secretReminderRecipients: reminderRecipients,
skipMultilineEncoding,
secretMetadata
});
@@ -172,6 +175,7 @@ export const SecretListView = ({
comment,
reminderRepeatDays,
reminderNote,
reminderRecipients,
secretMetadata,
isReminderEvent
} = modSecret;
@@ -189,6 +193,7 @@ export const SecretListView = ({
"skipMultilineEncoding",
"reminderRepeatDays",
"reminderNote",
"reminderRecipients",
"secretMetadata"
] as const
).every((el) => orgSecret[el] === modSecret[el]) && isSameTags;
@@ -224,6 +229,7 @@ export const SecretListView = ({
comment,
reminderRepeatDays,
reminderNote,
reminderRecipients,
secretId: orgSecret.id,
newKey: hasKeyChanged ? key : undefined,
skipMultilineEncoding: modSecret.skipMultilineEncoding,
@@ -47,6 +47,7 @@ export const formSchema = z.object({
.nullable()
.optional(),
reminderNote: z.string().trim().nullable().optional(),
reminderRecipients: z.array(z.string().uuid()).optional(),
secretMetadata: z
.object({
key: z.string().trim().min(1),