| import React, { useEffect } from 'react'; |
| import { QueryKeys } from 'librechat-data-provider'; |
| import { Controller, useForm } from 'react-hook-form'; |
| import { useQueryClient } from '@tanstack/react-query'; |
| import { Checkbox, Label, TextareaAutosize, Input, useToastContext } from '@librechat/client'; |
| import type { TConversationTag, TConversationTagRequest } from 'librechat-data-provider'; |
| import { useBookmarkContext } from '~/Providers/BookmarkContext'; |
| import { useConversationTagMutation } from '~/data-provider'; |
| import { useLocalize } from '~/hooks'; |
| import { cn, logger } from '~/utils'; |
|
|
| type TBookmarkFormProps = { |
| tags?: string[]; |
| bookmark?: TConversationTag; |
| conversationId?: string; |
| formRef: React.RefObject<HTMLFormElement>; |
| setOpen: React.Dispatch<React.SetStateAction<boolean>>; |
| mutation: ReturnType<typeof useConversationTagMutation>; |
| }; |
| const BookmarkForm = ({ |
| tags, |
| bookmark, |
| mutation, |
| conversationId, |
| setOpen, |
| formRef, |
| }: TBookmarkFormProps) => { |
| const localize = useLocalize(); |
| const queryClient = useQueryClient(); |
| const { showToast } = useToastContext(); |
| const { bookmarks } = useBookmarkContext(); |
|
|
| const { |
| register, |
| handleSubmit, |
| setValue, |
| getValues, |
| control, |
| formState: { errors }, |
| } = useForm<TConversationTagRequest>({ |
| mode: 'onBlur', |
| reValidateMode: 'onChange', |
| defaultValues: { |
| tag: bookmark?.tag ?? '', |
| description: bookmark?.description ?? '', |
| conversationId: conversationId ?? '', |
| addToConversation: conversationId != null && conversationId ? true : false, |
| }, |
| }); |
|
|
| useEffect(() => { |
| if (bookmark && bookmark.tag) { |
| setValue('tag', bookmark.tag); |
| setValue('description', bookmark.description ?? ''); |
| } |
| }, [bookmark, setValue]); |
|
|
| const onSubmit = (data: TConversationTagRequest) => { |
| logger.log('tag_mutation', 'BookmarkForm - onSubmit: data', data); |
| if (mutation.isLoading) { |
| return; |
| } |
| if (data.tag === bookmark?.tag && data.description === bookmark?.description) { |
| return; |
| } |
| if (data.tag != null && (tags ?? []).includes(data.tag)) { |
| showToast({ |
| message: localize('com_ui_bookmarks_create_exists'), |
| status: 'warning', |
| }); |
| return; |
| } |
| const allTags = |
| queryClient.getQueryData<TConversationTag[]>([QueryKeys.conversationTags]) ?? []; |
| if (allTags.some((tag) => tag.tag === data.tag && tag.tag !== bookmark?.tag)) { |
| showToast({ |
| message: localize('com_ui_bookmarks_create_exists'), |
| status: 'warning', |
| }); |
| return; |
| } |
|
|
| mutation.mutate(data); |
| setOpen(false); |
| }; |
|
|
| return ( |
| <form |
| ref={formRef} |
| className="mt-6" |
| aria-label="Bookmark form" |
| method="POST" |
| onSubmit={handleSubmit(onSubmit)} |
| > |
| <div className="flex w-full flex-col items-center gap-2"> |
| <div className="grid w-full items-center gap-2"> |
| <Label htmlFor="bookmark-tag" className="text-left text-sm font-medium"> |
| {localize('com_ui_bookmarks_title')} |
| </Label> |
| <Input |
| type="text" |
| id="bookmark-tag" |
| aria-label={ |
| bookmark ? localize('com_ui_bookmarks_edit') : localize('com_ui_bookmarks_new') |
| } |
| {...register('tag', { |
| required: localize('com_ui_field_required'), |
| maxLength: { |
| value: 128, |
| message: localize('com_ui_field_max_length', { |
| field: localize('com_ui_bookmarks_title'), |
| length: 128, |
| }), |
| }, |
| validate: (value) => { |
| return ( |
| value === bookmark?.tag || |
| bookmarks.every((bookmark) => bookmark.tag !== value) || |
| localize('com_ui_bookmarks_tag_exists') |
| ); |
| }, |
| })} |
| aria-invalid={!!errors.tag} |
| placeholder={ |
| bookmark ? localize('com_ui_bookmarks_edit') : localize('com_ui_bookmarks_new') |
| } |
| /> |
| {errors.tag && <span className="text-sm text-red-500">{errors.tag.message}</span>} |
| </div> |
| |
| <div className="mt-4 grid w-full items-center gap-2"> |
| <Label |
| id="bookmark-description-label" |
| htmlFor="bookmark-description" |
| className="text-left text-sm font-medium" |
| > |
| {localize('com_ui_bookmarks_description')} |
| </Label> |
| <TextareaAutosize |
| {...register('description', { |
| maxLength: { |
| value: 1048, |
| message: localize('com_ui_field_max_length', { |
| field: localize('com_ui_bookmarks_description'), |
| length: 1048, |
| }), |
| }, |
| })} |
| id="bookmark-description" |
| disabled={false} |
| className={cn( |
| 'flex h-10 max-h-[250px] min-h-[100px] w-full resize-none rounded-lg border border-input bg-transparent px-3 py-2 text-sm ring-offset-background focus-visible:outline-none', |
| )} |
| aria-labelledby="bookmark-description-label" |
| /> |
| </div> |
| {conversationId != null && conversationId && ( |
| <div className="mt-2 flex w-full items-center"> |
| <Controller |
| name="addToConversation" |
| control={control} |
| render={({ field }) => ( |
| <Checkbox |
| {...field} |
| checked={field.value} |
| onCheckedChange={field.onChange} |
| className="relative float-left mr-2 inline-flex h-4 w-4 cursor-pointer" |
| value={field.value?.toString()} |
| aria-label={localize('com_ui_bookmarks_add_to_conversation')} |
| /> |
| )} |
| /> |
| <button |
| type="button" |
| aria-label={localize('com_ui_bookmarks_add_to_conversation')} |
| className="form-check-label w-full cursor-pointer text-text-primary" |
| onClick={() => |
| setValue('addToConversation', !(getValues('addToConversation') ?? false), { |
| shouldDirty: true, |
| }) |
| } |
| > |
| <div className="flex select-none items-center"> |
| {localize('com_ui_bookmarks_add_to_conversation')} |
| </div> |
| </button> |
| </div> |
| )} |
| </div> |
| </form> |
| ); |
| }; |
|
|
| export default BookmarkForm; |
|
|