diff --git a/.changeset/eleven-pugs-help.md b/.changeset/eleven-pugs-help.md new file mode 100644 index 000000000000..61acd79f600b --- /dev/null +++ b/.changeset/eleven-pugs-help.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixes an issue where losing connection could break app's action buttons diff --git a/.changeset/friendly-kings-poke.md b/.changeset/friendly-kings-poke.md new file mode 100644 index 000000000000..b7299f4ebaa7 --- /dev/null +++ b/.changeset/friendly-kings-poke.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/rest-typings": patch +--- + +Allows users to fetch the `packageValue` of settings when calling `/settings` endpoint via `includeDefaults` query param. diff --git a/.changeset/gentle-moons-unite.md b/.changeset/gentle-moons-unite.md new file mode 100644 index 000000000000..7916cfb0bec1 --- /dev/null +++ b/.changeset/gentle-moons-unite.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixes an issue where room scroll position wasn't being restored when moving between rooms. diff --git a/.changeset/good-clouds-brush.md b/.changeset/good-clouds-brush.md new file mode 100644 index 000000000000..62e0760254d2 --- /dev/null +++ b/.changeset/good-clouds-brush.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixes an issue that prevented room history from loading under certain conditions. diff --git a/apps/meteor/app/api/server/v1/settings.ts b/apps/meteor/app/api/server/v1/settings.ts index 1c09e8c72937..6d2bbab89afd 100644 --- a/apps/meteor/app/api/server/v1/settings.ts +++ b/apps/meteor/app/api/server/v1/settings.ts @@ -12,6 +12,7 @@ import { isSettingsUpdatePropsActions, isSettingsUpdatePropsColor, isSettingsPublicWithPaginationProps, + isSettingsGetParams, } from '@rocket.chat/rest-typings'; import { Meteor } from 'meteor/meteor'; import type { FindOptions } from 'mongodb'; @@ -131,9 +132,10 @@ API.v1.addRoute( API.v1.addRoute( 'settings', - { authRequired: true }, + { authRequired: true, validateParams: isSettingsGetParams }, { async get() { + const { includeDefaults } = this.queryParams; const { offset, count } = await getPaginationItems(this.queryParams); const { sort, fields, query } = await this.parseJsonQuery(); @@ -147,6 +149,11 @@ API.v1.addRoute( ourQuery = Object.assign({}, query, ourQuery); + // Note: change this when `fields` gets removed + if (includeDefaults) { + fields.packageValue = 1; + } + const { settings, totalCount: total } = await fetchSettings(ourQuery, sort, offset, count, fields); return API.v1.success({ diff --git a/apps/meteor/client/apps/gameCenter/GameCenter.tsx b/apps/meteor/client/apps/gameCenter/GameCenter.tsx index 56252008e9f5..540482eefb5b 100644 --- a/apps/meteor/client/apps/gameCenter/GameCenter.tsx +++ b/apps/meteor/client/apps/gameCenter/GameCenter.tsx @@ -1,7 +1,7 @@ import type { IExternalComponent } from '@rocket.chat/apps-engine/definition/externalComponent'; import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { useState } from 'react'; -import type { ReactElement } from 'react'; +import type { MouseEvent, ReactElement } from 'react'; import GameCenterContainer from './GameCenterContainer'; import GameCenterList from './GameCenterList'; @@ -18,12 +18,12 @@ const GameCenter = (): ReactElement => { const result = useExternalComponentsQuery(); - const handleClose = useEffectEvent((e) => { + const handleClose = useEffectEvent((e: MouseEvent) => { preventSyntheticEvent(e); closeTab(); }); - const handleBack = useEffectEvent((e) => { + const handleBack = useEffectEvent((e: MouseEvent) => { setOpenedGame(undefined); preventSyntheticEvent(e); }); diff --git a/apps/meteor/client/components/SidebarToggler/SidebarToggler.tsx b/apps/meteor/client/components/SidebarToggler/SidebarToggler.tsx index fa30bba3f6b2..02d22fb86804 100644 --- a/apps/meteor/client/components/SidebarToggler/SidebarToggler.tsx +++ b/apps/meteor/client/components/SidebarToggler/SidebarToggler.tsx @@ -9,7 +9,7 @@ import { useEmbeddedLayout } from '../../hooks/useEmbeddedLayout'; const SideBarToggler = (): ReactElement => { const { sidebar } = useLayout(); const isLayoutEmbedded = useEmbeddedLayout(); - const unreadMessagesBadge = useSession('unread'); + const unreadMessagesBadge = useSession('unread') as number | string | undefined; const toggleSidebar = useEffectEvent(() => sidebar.toggle()); diff --git a/apps/meteor/client/components/SidebarToggler/SidebarTogglerBadge.tsx b/apps/meteor/client/components/SidebarToggler/SidebarTogglerBadge.tsx index c2671e686f17..5472d24a0a97 100644 --- a/apps/meteor/client/components/SidebarToggler/SidebarTogglerBadge.tsx +++ b/apps/meteor/client/components/SidebarToggler/SidebarTogglerBadge.tsx @@ -1,7 +1,12 @@ import { css } from '@rocket.chat/css-in-js'; import { Box, Badge } from '@rocket.chat/fuselage'; +import type { ReactNode } from 'react'; -const SidebarTogglerBadge = ({ children }: { children?: unknown }) => ( +type SidebarTogglerBadgeProps = { + children?: ReactNode; +}; + +const SidebarTogglerBadge = ({ children }: SidebarTogglerBadgeProps) => ( void; }; diff --git a/apps/meteor/client/components/avatar/RoomAvatarEditor.tsx b/apps/meteor/client/components/avatar/RoomAvatarEditor.tsx index 91d0438d7302..189dcd77e6c6 100644 --- a/apps/meteor/client/components/avatar/RoomAvatarEditor.tsx +++ b/apps/meteor/client/components/avatar/RoomAvatarEditor.tsx @@ -24,7 +24,7 @@ const RoomAvatarEditor = ({ disabled = false, room, roomAvatar, onChangeAvatar } const { t } = useTranslation(); const dispatchToastMessage = useToastMessageDispatch(); - const handleChangeAvatar = useEffectEvent(async (file) => { + const handleChangeAvatar = useEffectEvent(async (file: File) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onloadend = async (): Promise => { diff --git a/apps/meteor/client/components/message/content/attachments/QuoteAttachment.tsx b/apps/meteor/client/components/message/content/attachments/QuoteAttachment.tsx index 5bb9346229c6..4f44f1d810c6 100644 --- a/apps/meteor/client/components/message/content/attachments/QuoteAttachment.tsx +++ b/apps/meteor/client/components/message/content/attachments/QuoteAttachment.tsx @@ -37,7 +37,7 @@ type QuoteAttachmentProps = { export const QuoteAttachment = ({ attachment }: QuoteAttachmentProps): ReactElement => { const formatTime = useTimeAgo(); - const displayAvatarPreference = useUserPreference('displayAvatars'); + const displayAvatarPreference = useUserPreference('displayAvatars'); return ( <> diff --git a/apps/meteor/client/hooks/useAppActionButtons.ts b/apps/meteor/client/hooks/useAppActionButtons.ts index 566baf69b37c..58173987308a 100644 --- a/apps/meteor/client/hooks/useAppActionButtons.ts +++ b/apps/meteor/client/hooks/useAppActionButtons.ts @@ -1,6 +1,6 @@ import { type IUIActionButton, type UIActionButtonContext } from '@rocket.chat/apps-engine/definition/ui'; import { useDebouncedCallback } from '@rocket.chat/fuselage-hooks'; -import { useEndpoint, useStream, useUserId } from '@rocket.chat/ui-contexts'; +import { useConnectionStatus, useEndpoint, useStream, useUserId } from '@rocket.chat/ui-contexts'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useEffect } from 'react'; @@ -8,14 +8,15 @@ export const getIdForActionButton = ({ appId, actionId }: IUIActionButton): stri export const useAppActionButtons = (context?: TContext) => { const queryClient = useQueryClient(); - const apps = useStream('apps'); const uid = useUserId(); + const { status } = useConnectionStatus(); const getActionButtons = useEndpoint('GET', '/apps/actionButtons'); const result = useQuery({ - queryKey: ['apps', 'actionButtons'], + queryKey: ['apps', 'actionButtons', status], + enabled: status === 'connected', queryFn: () => getActionButtons(), ...(context && { diff --git a/apps/meteor/client/hooks/useClipboardWithToast.ts b/apps/meteor/client/hooks/useClipboardWithToast.ts index ee8abc123744..67e33d4d4b3c 100644 --- a/apps/meteor/client/hooks/useClipboardWithToast.ts +++ b/apps/meteor/client/hooks/useClipboardWithToast.ts @@ -9,6 +9,6 @@ export default function useClipboardWithToast(text: string): UseClipboardReturn return useClipboard(text, { onCopySuccess: useEffectEvent(() => dispatchToastMessage({ type: 'success', message: t('Copied') })), - onCopyError: useEffectEvent((e) => dispatchToastMessage({ type: 'error', message: e })), + onCopyError: useEffectEvent((e?: Error) => dispatchToastMessage({ type: 'error', message: e })), }); } diff --git a/apps/meteor/client/hooks/usePreventPropagation.ts b/apps/meteor/client/hooks/usePreventPropagation.ts index 6d9f38ad0dbf..19382bec8d10 100644 --- a/apps/meteor/client/hooks/usePreventPropagation.ts +++ b/apps/meteor/client/hooks/usePreventPropagation.ts @@ -2,7 +2,7 @@ import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import type { UIEvent } from 'react'; export const usePreventPropagation = (fn?: (e: UIEvent) => void): ((e: UIEvent) => void) => { - const preventClickPropagation = useEffectEvent((e): void => { + const preventClickPropagation = useEffectEvent((e: UIEvent): void => { e.stopPropagation(); fn?.(e); }); diff --git a/apps/meteor/client/lib/utils/timeAgo.ts b/apps/meteor/client/lib/utils/timeAgo.ts index 5a322096a258..d31a787a6062 100644 --- a/apps/meteor/client/lib/utils/timeAgo.ts +++ b/apps/meteor/client/lib/utils/timeAgo.ts @@ -9,7 +9,7 @@ import { t } from '../../../app/utils/lib/i18n'; const dayFormat = ['h:mm A', 'H:mm']; -export const timeAgo = async (date: MomentInput): Promise => { +export const timeAgo = (date: MomentInput) => { const clockMode = Tracker.nonreactive(() => getUserPreference(Meteor.userId(), 'clockMode', false) as number | boolean); const messageTimeFormat = Tracker.nonreactive(() => settings.get('Message_TimeFormat')); const sameDay = (typeof clockMode === 'number' ? dayFormat[clockMode - 1] : undefined) || messageTimeFormat; diff --git a/apps/meteor/client/omnichannel/additionalForms/BusinessHoursMultiple.tsx b/apps/meteor/client/omnichannel/additionalForms/BusinessHoursMultiple.tsx index 69912a1c3881..59f10af0c46b 100644 --- a/apps/meteor/client/omnichannel/additionalForms/BusinessHoursMultiple.tsx +++ b/apps/meteor/client/omnichannel/additionalForms/BusinessHoursMultiple.tsx @@ -6,13 +6,14 @@ import { useTranslation } from 'react-i18next'; import AutoCompleteDepartmentMultiple from '../../components/AutoCompleteDepartmentMultiple'; import { useHasLicenseModule } from '../../hooks/useHasLicenseModule'; +import type { BusinessHoursFormData } from '../../views/omnichannel/businessHours/BusinessHoursForm'; const BusinessHoursMultiple = ({ className }: { className?: ComponentProps['className'] }) => { const { t } = useTranslation(); const { control, formState: { errors }, - } = useFormContext(); + } = useFormContext(); const hasLicense = useHasLicenseModule('livechat-enterprise'); const enabledField = useUniqueId(); diff --git a/apps/meteor/client/omnichannel/additionalForms/CurrentChatTags.tsx b/apps/meteor/client/omnichannel/additionalForms/CurrentChatTags.tsx index 7d2892e94014..08bb02b8fb0a 100644 --- a/apps/meteor/client/omnichannel/additionalForms/CurrentChatTags.tsx +++ b/apps/meteor/client/omnichannel/additionalForms/CurrentChatTags.tsx @@ -1,7 +1,12 @@ import { useHasLicenseModule } from '../../hooks/useHasLicenseModule'; import AutoCompleteTagsMultiple from '../tags/AutoCompleteTagsMultiple'; -type CurrentChatTagsProps = { value: Array<{ value: string; label: string }>; handler: any; department?: string; viewAll?: boolean }; +type CurrentChatTagsProps = { + value: Array<{ value: string; label: string }>; + handler: (value: { label: string; value: string }[]) => void; + department?: string; + viewAll?: boolean; +}; const CurrentChatTags = ({ value, handler, department, viewAll }: CurrentChatTagsProps) => { const hasLicense = useHasLicenseModule('livechat-enterprise'); @@ -10,7 +15,14 @@ const CurrentChatTags = ({ value, handler, department, viewAll }: CurrentChatTag return null; } - return ; + return ( + + ); }; export default CurrentChatTags; diff --git a/apps/meteor/client/omnichannel/additionalForms/CustomFieldsAdditionalForm.tsx b/apps/meteor/client/omnichannel/additionalForms/CustomFieldsAdditionalForm.tsx index 3e94693bd037..4fa821b7a75c 100644 --- a/apps/meteor/client/omnichannel/additionalForms/CustomFieldsAdditionalForm.tsx +++ b/apps/meteor/client/omnichannel/additionalForms/CustomFieldsAdditionalForm.tsx @@ -7,6 +7,7 @@ import { useFormContext, Controller } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { useHasLicenseModule } from '../../hooks/useHasLicenseModule'; +import type { EditCustomFieldsFormData } from '../../views/omnichannel/customFields/EditCustomFields'; const checkIsOptionsValid = (value: string) => { if (!value || value.trim() === '') { @@ -22,7 +23,7 @@ const CustomFieldsAdditionalForm = ({ className }: { className?: ComponentProps< control, watch, formState: { errors }, - } = useFormContext(); + } = useFormContext(); const hasLicense = useHasLicenseModule('livechat-enterprise'); const { visibility, type } = watch(); diff --git a/apps/meteor/client/omnichannel/businessHours/useRemoveBusinessHour.tsx b/apps/meteor/client/omnichannel/businessHours/useRemoveBusinessHour.tsx index 3bb9fde92d0e..1b2c3a6c7e88 100644 --- a/apps/meteor/client/omnichannel/businessHours/useRemoveBusinessHour.tsx +++ b/apps/meteor/client/omnichannel/businessHours/useRemoveBusinessHour.tsx @@ -12,7 +12,7 @@ export const useRemoveBusinessHour = () => { const removeBusinessHour = useMethod('livechat:removeBusinessHour'); const queryClient = useQueryClient(); - const handleRemove = useEffectEvent((_id, type) => { + const handleRemove = useEffectEvent((_id: string, type: string) => { const onDeleteBusinessHour = async () => { try { await removeBusinessHour(_id, type); diff --git a/apps/meteor/client/omnichannel/cannedResponses/CannedResponseEdit.tsx b/apps/meteor/client/omnichannel/cannedResponses/CannedResponseEdit.tsx index b4b0ef40ba99..be7d1c3d8054 100644 --- a/apps/meteor/client/omnichannel/cannedResponses/CannedResponseEdit.tsx +++ b/apps/meteor/client/omnichannel/cannedResponses/CannedResponseEdit.tsx @@ -6,11 +6,11 @@ import { useQueryClient } from '@tanstack/react-query'; import { memo, useCallback } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; -import CannedResponseForm from './components/cannedResponseForm'; +import CannedResponseForm from './components/CannedResponseForm'; import { useRemoveCannedResponse } from './useRemoveCannedResponse'; import { Page, PageHeader, PageScrollableContentWithShadow, PageFooter } from '../../components/Page'; -type CannedResponseEditFormData = { +export type CannedResponseEditFormData = { _id: string; shortcut: string; text: string; diff --git a/apps/meteor/client/omnichannel/cannedResponses/CannedResponsesTable.tsx b/apps/meteor/client/omnichannel/cannedResponses/CannedResponsesTable.tsx index eed4eb0978fc..ea344a6a13e1 100644 --- a/apps/meteor/client/omnichannel/cannedResponses/CannedResponsesTable.tsx +++ b/apps/meteor/client/omnichannel/cannedResponses/CannedResponsesTable.tsx @@ -64,7 +64,7 @@ const CannedResponsesTable = () => { const handleAddNew = useEffectEvent(() => router.navigate('/omnichannel/canned-responses/new')); - const onRowClick = useEffectEvent((id, scope) => (): void => { + const onRowClick = useEffectEvent((id: string, scope: string) => (): void => { if (scope === 'global' && isMonitor && !isManager) { return dispatchToastMessage({ type: 'error', diff --git a/apps/meteor/client/omnichannel/cannedResponses/components/cannedResponseForm.tsx b/apps/meteor/client/omnichannel/cannedResponses/components/CannedResponseForm.tsx similarity index 98% rename from apps/meteor/client/omnichannel/cannedResponses/components/cannedResponseForm.tsx rename to apps/meteor/client/omnichannel/cannedResponses/components/CannedResponseForm.tsx index c4b02bb36721..507d4a95ce55 100644 --- a/apps/meteor/client/omnichannel/cannedResponses/components/cannedResponseForm.tsx +++ b/apps/meteor/client/omnichannel/cannedResponses/components/CannedResponseForm.tsx @@ -10,6 +10,7 @@ import CannedResponsesComposer from './CannedResponsesComposer/CannedResponsesCo import CannedResponsesComposerPreview from './CannedResponsesComposer/CannedResponsesComposerPreview'; import AutoCompleteDepartment from '../../../components/AutoCompleteDepartment'; import Tags from '../../../components/Omnichannel/Tags'; +import type { CannedResponseEditFormData } from '../CannedResponseEdit'; // TODO: refactor Tags field to get proper validation const CannedResponseForm = () => { @@ -21,7 +22,7 @@ const CannedResponseForm = () => { control, formState: { errors }, watch, - } = useFormContext(); + } = useFormContext(); const clickable = css` cursor: pointer; diff --git a/apps/meteor/client/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponseList.tsx b/apps/meteor/client/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponseList.tsx index 9859242af5f5..cd6a272ed50e 100644 --- a/apps/meteor/client/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponseList.tsx +++ b/apps/meteor/client/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponseList.tsx @@ -31,7 +31,7 @@ type CannedResponseListProps = { type: string; setType: Dispatch>; isRoomOverMacLimit: boolean; - onClickItem: (data: any) => void; + onClickItem: (data: any) => void; // FIXME: fix typings onClickCreate: (e: MouseEvent) => void; onClickUse: (e: MouseEvent, text: string) => void; reload: () => void; diff --git a/apps/meteor/client/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponseList.tsx b/apps/meteor/client/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponseList.tsx index 6ba47ef6142c..6f68bef0b0fa 100644 --- a/apps/meteor/client/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponseList.tsx +++ b/apps/meteor/client/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponseList.tsx @@ -1,3 +1,4 @@ +import type { IOmnichannelCannedResponse, ILivechatDepartment } from '@rocket.chat/core-typings'; import { useDebouncedValue, useLocalStorage, useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { useSetModal, useRouter } from '@rocket.chat/ui-contexts'; import type { ChangeEvent, MouseEvent } from 'react'; @@ -38,18 +39,24 @@ export const WrapCannedResponseList = () => { ); const { phase, items, itemCount } = useRecordList(cannedList); - const onClickItem = useEffectEvent((data) => { - const { _id: context } = data; - - router.navigate({ - name: router.getRouteName() ?? 'live', - params: { - id: room._id, - tab: 'canned-responses', - context, + const onClickItem = useEffectEvent( + ( + data: IOmnichannelCannedResponse & { + departmentName: ILivechatDepartment['name']; }, - }); - }); + ) => { + const { _id: context } = data; + + router.navigate({ + name: router.getRouteName() ?? 'live', + params: { + id: room._id, + tab: 'canned-responses', + context, + }, + }); + }, + ); const composer = useChat()?.composer; diff --git a/apps/meteor/client/omnichannel/cannedResponses/modals/CreateCannedResponse/CreateCannedResponseModal.tsx b/apps/meteor/client/omnichannel/cannedResponses/modals/CreateCannedResponse/CreateCannedResponseModal.tsx index 59fcfb4547b8..6bd2bb79f701 100644 --- a/apps/meteor/client/omnichannel/cannedResponses/modals/CreateCannedResponse/CreateCannedResponseModal.tsx +++ b/apps/meteor/client/omnichannel/cannedResponses/modals/CreateCannedResponse/CreateCannedResponseModal.tsx @@ -6,7 +6,7 @@ import { FormProvider, useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import GenericModal from '../../../../components/GenericModal'; -import CannedResponseForm from '../../components/cannedResponseForm'; +import CannedResponseForm from '../../components/CannedResponseForm'; type CreateCannedResponseModalFormData = { _id: string; diff --git a/apps/meteor/client/omnichannel/cannedResponses/useRemoveCannedResponse.tsx b/apps/meteor/client/omnichannel/cannedResponses/useRemoveCannedResponse.tsx index f6bcc964c609..5cb9cd8bda59 100644 --- a/apps/meteor/client/omnichannel/cannedResponses/useRemoveCannedResponse.tsx +++ b/apps/meteor/client/omnichannel/cannedResponses/useRemoveCannedResponse.tsx @@ -14,7 +14,7 @@ export const useRemoveCannedResponse = () => { const dispatchToastMessage = useToastMessageDispatch(); const removeCannedResponse = useMethod('removeCannedResponse'); - const handleDelete = useEffectEvent((id) => { + const handleDelete = useEffectEvent((id: string) => { const onDeleteCannedResponse: () => Promise = async () => { try { await removeCannedResponse(id); diff --git a/apps/meteor/client/omnichannel/reports/components/AgentsTable.tsx b/apps/meteor/client/omnichannel/reports/components/AgentsTable.tsx index e1effedb296d..dbb38a53d9ea 100644 --- a/apps/meteor/client/omnichannel/reports/components/AgentsTable.tsx +++ b/apps/meteor/client/omnichannel/reports/components/AgentsTable.tsx @@ -24,7 +24,7 @@ type AgentsTableProps = { export const AgentsTable = memo(({ data, sortBy, sortDirection, setSort }: AgentsTableProps) => { const { t } = useTranslation(); - const onHeaderClick = useEffectEvent((id) => { + const onHeaderClick = useEffectEvent((id: 'name' | 'total') => { setSort(id, sortDirection === 'asc' ? 'desc' : 'asc'); }); diff --git a/apps/meteor/client/omnichannel/slaPolicies/RemoveSlaButton.tsx b/apps/meteor/client/omnichannel/slaPolicies/RemoveSlaButton.tsx index 510f99e8710c..856a079ad544 100644 --- a/apps/meteor/client/omnichannel/slaPolicies/RemoveSlaButton.tsx +++ b/apps/meteor/client/omnichannel/slaPolicies/RemoveSlaButton.tsx @@ -1,6 +1,7 @@ import { IconButton } from '@rocket.chat/fuselage'; import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { useRoute, useEndpoint, useSetModal, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; +import type { MouseEvent } from 'react'; import { useTranslation } from 'react-i18next'; import GenericModal from '../../components/GenericModal'; @@ -14,7 +15,7 @@ const RemoveSlaButton = ({ _id, reload }: { _id: string; reload: () => void }) = const removeSLA = useEndpoint('DELETE', `/v1/livechat/sla/:slaId`, { slaId: _id }); - const handleDelete = useEffectEvent((e) => { + const handleDelete = useEffectEvent((e: MouseEvent) => { e.stopPropagation(); const onDeleteAgent = async (): Promise => { try { diff --git a/apps/meteor/client/omnichannel/slaPolicies/SlaTable.tsx b/apps/meteor/client/omnichannel/slaPolicies/SlaTable.tsx index e73993d6dfa5..e4d1dd46e19c 100644 --- a/apps/meteor/client/omnichannel/slaPolicies/SlaTable.tsx +++ b/apps/meteor/client/omnichannel/slaPolicies/SlaTable.tsx @@ -56,7 +56,7 @@ const SlaTable = ({ reload }: { reload: MutableRefObject<() => void> }) => { }, [reload, refetch]); const handleAddNew = useEffectEvent(() => router.navigate('/omnichannel/sla-policies/new')); - const onRowClick = useEffectEvent((id) => () => router.navigate(`/omnichannel/sla-policies/edit/${id}`)); + const onRowClick = useEffectEvent((id: string) => () => router.navigate(`/omnichannel/sla-policies/edit/${id}`)); const headers = ( <> diff --git a/apps/meteor/client/omnichannel/tags/TagsTable.tsx b/apps/meteor/client/omnichannel/tags/TagsTable.tsx index 40b31e8d3d97..2e513acabf24 100644 --- a/apps/meteor/client/omnichannel/tags/TagsTable.tsx +++ b/apps/meteor/client/omnichannel/tags/TagsTable.tsx @@ -27,7 +27,7 @@ const TagsTable = () => { const { current, itemsPerPage, setItemsPerPage: onSetItemsPerPage, setCurrent: onSetCurrent, ...paginationProps } = usePagination(); const { sortBy, sortDirection, setSort } = useSort<'name' | 'description'>('name'); - const onRowClick = useEffectEvent((id) => router.navigate(`/omnichannel/tags/edit/${id}`)); + const onRowClick = useEffectEvent((id: string) => router.navigate(`/omnichannel/tags/edit/${id}`)); const handleAddNew = useEffectEvent(() => router.navigate('/omnichannel/tags/new')); const handleDeleteTag = useRemoveTag(); diff --git a/apps/meteor/client/omnichannel/tags/useRemoveTag.tsx b/apps/meteor/client/omnichannel/tags/useRemoveTag.tsx index db62ac7c2342..4ae352c907f2 100644 --- a/apps/meteor/client/omnichannel/tags/useRemoveTag.tsx +++ b/apps/meteor/client/omnichannel/tags/useRemoveTag.tsx @@ -13,7 +13,7 @@ export const useRemoveTag = () => { const queryClient = useQueryClient(); const router = useRouter(); - const handleDeleteTag = useEffectEvent((tagId) => { + const handleDeleteTag = useEffectEvent((tagId: string) => { const handleDelete = async () => { try { await removeTag(tagId); diff --git a/apps/meteor/client/omnichannel/units/UnitEdit.tsx b/apps/meteor/client/omnichannel/units/UnitEdit.tsx index d1bae33242fa..f18ceddf8fa5 100644 --- a/apps/meteor/client/omnichannel/units/UnitEdit.tsx +++ b/apps/meteor/client/omnichannel/units/UnitEdit.tsx @@ -34,6 +34,19 @@ import { AsyncStatePhase } from '../../hooks/useAsyncState'; import { useDepartmentsByUnitsList } from '../../views/hooks/useDepartmentsByUnitsList'; import { useMonitorsList } from '../../views/hooks/useMonitorsList'; +type UnitEditFormData = { + name: string; + visibility: string; + departments: { + value: string; + label: string; + }[]; + monitors: { + value: string; + label: string; + }[]; +}; + type UnitEditProps = { unitData?: Serialized; unitMonitors?: Serialized[]; @@ -97,7 +110,7 @@ const UnitEdit = ({ unitData, unitMonitors, unitDepartments }: UnitEditProps) => formState: { errors, isDirty }, handleSubmit, watch, - } = useForm({ + } = useForm({ mode: 'onBlur', values: { name: unitData?.name || '', @@ -127,7 +140,7 @@ const UnitEdit = ({ unitData, unitMonitors, unitDepartments }: UnitEditProps) => return [...mappedMonitorsItems, ...pending]; }, [monitors, monitorsItems]); - const handleSave = useEffectEvent(async ({ name, visibility }) => { + const handleSave = useEffectEvent(async ({ name, visibility }: UnitEditFormData) => { const departmentsData = departments.map((department) => ({ departmentId: department.value })); const monitorsData = monitors.map((monitor) => ({ diff --git a/apps/meteor/client/omnichannel/units/UnitsTable.tsx b/apps/meteor/client/omnichannel/units/UnitsTable.tsx index d62855bc430e..b8dfa26e744d 100644 --- a/apps/meteor/client/omnichannel/units/UnitsTable.tsx +++ b/apps/meteor/client/omnichannel/units/UnitsTable.tsx @@ -51,7 +51,7 @@ const UnitsTable = () => { const queryHasChanged = defaultQuery !== hashKey([query]); const handleAddNew = useEffectEvent(() => router.navigate('/omnichannel/units/new')); - const onRowClick = useEffectEvent((id) => () => router.navigate(`/omnichannel/units/edit/${id}`)); + const onRowClick = useEffectEvent((id: string) => () => router.navigate(`/omnichannel/units/edit/${id}`)); const handleDelete = useRemoveUnit(); const headers = ( diff --git a/apps/meteor/client/omnichannel/units/useRemoveUnit.tsx b/apps/meteor/client/omnichannel/units/useRemoveUnit.tsx index c07dd84baf78..b85743ed4e3f 100644 --- a/apps/meteor/client/omnichannel/units/useRemoveUnit.tsx +++ b/apps/meteor/client/omnichannel/units/useRemoveUnit.tsx @@ -13,7 +13,7 @@ export const useRemoveUnit = () => { const queryClient = useQueryClient(); const removeUnit = useMethod('livechat:removeUnit'); - const handleDelete = useEffectEvent((id) => { + const handleDelete = useEffectEvent((id: string) => { const onDeleteAgent = async () => { try { await removeUnit(id); diff --git a/apps/meteor/client/sidebar/Item/Condensed.tsx b/apps/meteor/client/sidebar/Item/Condensed.tsx index 792c39903393..6dd5027b95ca 100644 --- a/apps/meteor/client/sidebar/Item/Condensed.tsx +++ b/apps/meteor/client/sidebar/Item/Condensed.tsx @@ -1,7 +1,7 @@ import { IconButton, Sidebar } from '@rocket.chat/fuselage'; import { useEffectEvent, usePrefersReducedMotion } from '@rocket.chat/fuselage-hooks'; import type { Keys as IconName } from '@rocket.chat/icons'; -import type { ReactElement } from 'react'; +import type { ReactElement, UIEvent } from 'react'; import { memo, useState } from 'react'; type CondensedProps = { @@ -24,8 +24,8 @@ const Condensed = ({ icon, title = '', avatar, actions, href, unread, menu, badg const isReduceMotionEnabled = usePrefersReducedMotion(); - const handleMenu = useEffectEvent((e) => { - setMenuVisibility(e.target.offsetWidth > 0 && Boolean(menu)); + const handleMenu = useEffectEvent((e: UIEvent) => { + setMenuVisibility(e.currentTarget.offsetWidth > 0 && Boolean(menu)); }); const handleMenuEvent = { [isReduceMotionEnabled ? 'onMouseEnter' : 'onTransitionEnd']: handleMenu, diff --git a/apps/meteor/client/sidebar/Item/Extended.tsx b/apps/meteor/client/sidebar/Item/Extended.tsx index e236f8660bc7..7a09e6d04d14 100644 --- a/apps/meteor/client/sidebar/Item/Extended.tsx +++ b/apps/meteor/client/sidebar/Item/Extended.tsx @@ -1,7 +1,7 @@ import { Sidebar, IconButton } from '@rocket.chat/fuselage'; import { useEffectEvent, usePrefersReducedMotion } from '@rocket.chat/fuselage-hooks'; import type { Keys as IconName } from '@rocket.chat/icons'; -import type { ReactNode } from 'react'; +import type { ReactNode, UIEvent } from 'react'; import { memo, useState } from 'react'; import { useShortTimeAgo } from '../../hooks/useTimeAgo'; @@ -45,8 +45,8 @@ const Extended = ({ const isReduceMotionEnabled = usePrefersReducedMotion(); - const handleMenu = useEffectEvent((e) => { - setMenuVisibility(e.target.offsetWidth > 0 && Boolean(menu)); + const handleMenu = useEffectEvent((e: UIEvent) => { + setMenuVisibility(e.currentTarget.offsetWidth > 0 && Boolean(menu)); }); const handleMenuEvent = { diff --git a/apps/meteor/client/sidebar/Item/Medium.tsx b/apps/meteor/client/sidebar/Item/Medium.tsx index f1f37047ece4..5ee19b1fed6e 100644 --- a/apps/meteor/client/sidebar/Item/Medium.tsx +++ b/apps/meteor/client/sidebar/Item/Medium.tsx @@ -1,6 +1,6 @@ import { Sidebar, IconButton } from '@rocket.chat/fuselage'; import { useEffectEvent, usePrefersReducedMotion } from '@rocket.chat/fuselage-hooks'; -import type { ReactNode } from 'react'; +import type { ReactNode, UIEvent } from 'react'; import { memo, useState } from 'react'; type MediumProps = { @@ -22,8 +22,8 @@ const Medium = ({ icon, title = '', avatar, actions, href, badges, unread, menu, const isReduceMotionEnabled = usePrefersReducedMotion(); - const handleMenu = useEffectEvent((e) => { - setMenuVisibility(e.target.offsetWidth > 0 && Boolean(menu)); + const handleMenu = useEffectEvent((e: UIEvent) => { + setMenuVisibility(e.currentTarget.offsetWidth > 0 && Boolean(menu)); }); const handleMenuEvent = { [isReduceMotionEnabled ? 'onMouseEnter' : 'onTransitionEnd']: handleMenu, diff --git a/apps/meteor/client/sidebar/RoomList/SideBarItemTemplateWithData.tsx b/apps/meteor/client/sidebar/RoomList/SideBarItemTemplateWithData.tsx index 9e22fe1ea318..8c8e4c6c814d 100644 --- a/apps/meteor/client/sidebar/RoomList/SideBarItemTemplateWithData.tsx +++ b/apps/meteor/client/sidebar/RoomList/SideBarItemTemplateWithData.tsx @@ -64,7 +64,7 @@ type RoomListRowProps = { actions: unknown; href: string; time?: Date; - menu?: ReactNode; + menu?: () => ReactNode; menuOptions?: unknown; subtitle?: ReactNode; titleIcon?: string; @@ -194,22 +194,21 @@ function SideBarItemTemplateWithData({ avatar={AvatarTemplate && } actions={actions} menu={ - !isIOsDevice && - !isAnonymous && - (!isQueued || (isQueued && isPriorityEnabled)) && - ((): ReactElement => ( - - )) + !isIOsDevice && !isAnonymous && (!isQueued || (isQueued && isPriorityEnabled)) + ? (): ReactElement => ( + + ) + : undefined } /> ); diff --git a/apps/meteor/client/sidebar/search/SearchList.tsx b/apps/meteor/client/sidebar/search/SearchList.tsx index 25a865c28f13..aa588b251959 100644 --- a/apps/meteor/client/sidebar/search/SearchList.tsx +++ b/apps/meteor/client/sidebar/search/SearchList.tsx @@ -6,7 +6,7 @@ import { escapeRegExp } from '@rocket.chat/string-helpers'; import { useUserPreference, useUserSubscriptions, useSetting, useTranslation, useMethod } from '@rocket.chat/ui-contexts'; import type { UseQueryResult } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query'; -import type { ReactElement, MutableRefObject, SetStateAction, Dispatch, FormEventHandler, Ref, MouseEventHandler } from 'react'; +import type { ReactElement, MutableRefObject, SetStateAction, Dispatch, FormEventHandler, Ref, MouseEventHandler, FormEvent } from 'react'; import { forwardRef, useState, useMemo, useEffect, useRef } from 'react'; import type { VirtuosoHandle } from 'react-virtuoso'; import { Virtuoso } from 'react-virtuoso'; @@ -166,7 +166,7 @@ const useSearchItems = (filterText: string): UseQueryResult<(ISubscription & IRo const useInput = (initial: string): { value: string; onChange: FormEventHandler; setValue: Dispatch> } => { const [value, setValue] = useState(initial); - const onChange = useEffectEvent((e) => { + const onChange = useEffectEvent((e: FormEvent) => { setValue(e.currentTarget.value); }); return { value, onChange, setValue }; @@ -231,7 +231,7 @@ const SearchList = forwardRef(function SearchList({ onClose }: SearchListProps, [avatarTemplate, extended, items, useRealName, sideBarItemTemplate, sidebarViewMode, t], ); - const changeSelection = useEffectEvent((dir) => { + const changeSelection = useEffectEvent((dir: 'up' | 'down') => { let nextSelectedElement = null; if (dir === 'up') { diff --git a/apps/meteor/client/sidebar/sections/AirGappedRestrictionBanner/AirGappedRestrictionWarning.tsx b/apps/meteor/client/sidebar/sections/AirGappedRestrictionBanner/AirGappedRestrictionWarning.tsx index ceda9d769336..9296f79392dd 100644 --- a/apps/meteor/client/sidebar/sections/AirGappedRestrictionBanner/AirGappedRestrictionWarning.tsx +++ b/apps/meteor/client/sidebar/sections/AirGappedRestrictionBanner/AirGappedRestrictionWarning.tsx @@ -14,8 +14,8 @@ const AirGappedRestrictionWarning = ({ isRestricted, remainingDays }: { isRestri } return ( - - This air-gapped workspace will enter read-only mode in <>{{ remainingDays }} days.{' '} + + This air-gapped workspace will enter read-only mode in {remainingDays} days.{' '} Connect it to the internet or upgrade to a premium plan to prevent this. diff --git a/apps/meteor/client/sidebar/sections/OmnichannelSection.tsx b/apps/meteor/client/sidebar/sections/OmnichannelSection.tsx index c5482119e7e0..2898f489ef40 100644 --- a/apps/meteor/client/sidebar/sections/OmnichannelSection.tsx +++ b/apps/meteor/client/sidebar/sections/OmnichannelSection.tsx @@ -23,7 +23,7 @@ const OmnichannelSection = () => { const queueListRoute = useRoute('livechat-queue'); const isWorkspaceOverMacLimit = useIsOverMacLimit(); - const handleRoute = useEffectEvent((route) => { + const handleRoute = useEffectEvent((route: string) => { sidebar.toggle(); switch (route) { diff --git a/apps/meteor/client/sidebarv2/Item/Condensed.tsx b/apps/meteor/client/sidebarv2/Item/Condensed.tsx index b294fd81c4a2..ffdf0eb20807 100644 --- a/apps/meteor/client/sidebarv2/Item/Condensed.tsx +++ b/apps/meteor/client/sidebarv2/Item/Condensed.tsx @@ -1,7 +1,7 @@ import { IconButton, SidebarV2Item, SidebarV2ItemAvatarWrapper, SidebarV2ItemMenu, SidebarV2ItemTitle } from '@rocket.chat/fuselage'; import { useEffectEvent, usePrefersReducedMotion } from '@rocket.chat/fuselage-hooks'; import type { Keys as IconName } from '@rocket.chat/icons'; -import type { HTMLAttributes, ReactElement } from 'react'; +import type { HTMLAttributes, ReactElement, UIEvent } from 'react'; import { memo, useState } from 'react'; type CondensedProps = { @@ -24,8 +24,8 @@ const Condensed = ({ icon, title, avatar, actions, unread, menu, badges, ...prop const isReduceMotionEnabled = usePrefersReducedMotion(); - const handleMenu = useEffectEvent((e) => { - setMenuVisibility(e.target.offsetWidth > 0 && Boolean(menu)); + const handleMenu = useEffectEvent((e: UIEvent) => { + setMenuVisibility(e.currentTarget.offsetWidth > 0 && Boolean(menu)); }); const handleMenuEvent = { [isReduceMotionEnabled ? 'onMouseEnter' : 'onTransitionEnd']: handleMenu, diff --git a/apps/meteor/client/sidebarv2/Item/Extended.tsx b/apps/meteor/client/sidebarv2/Item/Extended.tsx index 55fae2158cdb..12d999c5a3d9 100644 --- a/apps/meteor/client/sidebarv2/Item/Extended.tsx +++ b/apps/meteor/client/sidebarv2/Item/Extended.tsx @@ -11,7 +11,7 @@ import { } from '@rocket.chat/fuselage'; import { useEffectEvent, usePrefersReducedMotion } from '@rocket.chat/fuselage-hooks'; import type { Keys as IconName } from '@rocket.chat/icons'; -import type { HTMLAttributes, ReactNode } from 'react'; +import type { HTMLAttributes, ReactNode, UIEvent } from 'react'; import { memo, useState } from 'react'; import { useShortTimeAgo } from '../../hooks/useTimeAgo'; @@ -55,8 +55,8 @@ const Extended = ({ const isReduceMotionEnabled = usePrefersReducedMotion(); - const handleMenu = useEffectEvent((e) => { - setMenuVisibility(e.target.offsetWidth > 0 && Boolean(menu)); + const handleMenu = useEffectEvent((e: UIEvent) => { + setMenuVisibility(e.currentTarget.offsetWidth > 0 && Boolean(menu)); }); const handleMenuEvent = { [isReduceMotionEnabled ? 'onMouseEnter' : 'onTransitionEnd']: handleMenu, diff --git a/apps/meteor/client/sidebarv2/Item/Medium.tsx b/apps/meteor/client/sidebarv2/Item/Medium.tsx index 6200dd1e4954..0b4e04c031de 100644 --- a/apps/meteor/client/sidebarv2/Item/Medium.tsx +++ b/apps/meteor/client/sidebarv2/Item/Medium.tsx @@ -1,7 +1,7 @@ import { IconButton, SidebarV2Item, SidebarV2ItemAvatarWrapper, SidebarV2ItemMenu, SidebarV2ItemTitle } from '@rocket.chat/fuselage'; import { useEffectEvent, usePrefersReducedMotion } from '@rocket.chat/fuselage-hooks'; import type { Keys as IconName } from '@rocket.chat/icons'; -import type { HTMLAttributes, ReactNode } from 'react'; +import type { HTMLAttributes, ReactNode, UIEvent } from 'react'; import { memo, useState } from 'react'; type MediumProps = { @@ -23,8 +23,8 @@ const Medium = ({ icon, title, avatar, actions, badges, unread, menu, ...props } const isReduceMotionEnabled = usePrefersReducedMotion(); - const handleMenu = useEffectEvent((e) => { - setMenuVisibility(e.target.offsetWidth > 0 && Boolean(menu)); + const handleMenu = useEffectEvent((e: UIEvent) => { + setMenuVisibility(e.currentTarget.offsetWidth > 0 && Boolean(menu)); }); const handleMenuEvent = { [isReduceMotionEnabled ? 'onMouseEnter' : 'onTransitionEnd']: handleMenu, diff --git a/apps/meteor/client/sidebarv2/RoomList/SidebarItemTemplateWithData.tsx b/apps/meteor/client/sidebarv2/RoomList/SidebarItemTemplateWithData.tsx index 673f5357b187..17c75984177b 100644 --- a/apps/meteor/client/sidebarv2/RoomList/SidebarItemTemplateWithData.tsx +++ b/apps/meteor/client/sidebarv2/RoomList/SidebarItemTemplateWithData.tsx @@ -47,7 +47,7 @@ type RoomListRowProps = { actions: unknown; href: string; time?: Date; - menu?: ReactNode; + menu?: () => ReactNode; menuOptions?: unknown; subtitle?: ReactNode; titleIcon?: string; @@ -154,22 +154,21 @@ const SidebarItemTemplateWithData = ({ avatar={AvatarTemplate && } actions={actions} menu={ - !isIOsDevice && - !isAnonymous && - (!isQueued || (isQueued && isPriorityEnabled)) && - ((): ReactElement => ( - 0} - rid={rid} - unread={!!unread} - roomOpen={selected} - type={type} - cl={cl} - name={title} - hideDefaultOptions={isQueued} - /> - )) + !isIOsDevice && !isAnonymous && (!isQueued || (isQueued && isPriorityEnabled)) + ? (): ReactElement => ( + 0} + rid={rid} + unread={!!unread} + roomOpen={selected} + type={type} + cl={cl} + name={title} + hideDefaultOptions={isQueued} + /> + ) + : undefined } /> ); diff --git a/apps/meteor/client/sidebarv2/sections/AirGappedRestrictionBanner/AirGappedRestrictionWarning.tsx b/apps/meteor/client/sidebarv2/sections/AirGappedRestrictionBanner/AirGappedRestrictionWarning.tsx index 2ab97312a73e..b1def40e812c 100644 --- a/apps/meteor/client/sidebarv2/sections/AirGappedRestrictionBanner/AirGappedRestrictionWarning.tsx +++ b/apps/meteor/client/sidebarv2/sections/AirGappedRestrictionBanner/AirGappedRestrictionWarning.tsx @@ -15,7 +15,7 @@ const AirGappedRestrictionWarning = ({ isRestricted, remainingDays }: { isRestri return ( - This air-gapped workspace will enter read-only mode in <>{{ remainingDays }} days.{' '} + This air-gapped workspace will enter read-only mode in {remainingDays} days.{' '} Connect it to internet or upgrade to a premium plan to prevent this. diff --git a/apps/meteor/client/views/account/integrations/AccountIntegrationsPage.tsx b/apps/meteor/client/views/account/integrations/AccountIntegrationsPage.tsx index 2ac7e2918b21..88ea7fd5ca0e 100644 --- a/apps/meteor/client/views/account/integrations/AccountIntegrationsPage.tsx +++ b/apps/meteor/client/views/account/integrations/AccountIntegrationsPage.tsx @@ -37,7 +37,7 @@ const AccountIntegrationsPage = () => { }, }); - const handleSubmitForm = useEffectEvent(({ accountSelected }) => { + const handleSubmitForm = useEffectEvent(({ accountSelected }: { accountSelected: string }) => { removeMutation.mutate({ accountSelected }); }); diff --git a/apps/meteor/client/views/admin/emailInbox/EmailInboxForm.tsx b/apps/meteor/client/views/admin/emailInbox/EmailInboxForm.tsx index fa4f066e9242..552e6012d735 100644 --- a/apps/meteor/client/views/admin/emailInbox/EmailInboxForm.tsx +++ b/apps/meteor/client/views/admin/emailInbox/EmailInboxForm.tsx @@ -30,7 +30,31 @@ import AutoCompleteDepartment from '../../../components/AutoCompleteDepartment'; import GenericModal from '../../../components/GenericModal'; import { PageScrollableContentWithShadow } from '../../../components/Page'; -const EmailInboxForm = ({ inboxData }: { inboxData?: IEmailInboxPayload }): ReactElement => { +type EmailInboxFormData = { + active: boolean; + name: string; + email: string; + description?: string; + senderInfo?: string; + department?: string; + smtpServer: string; + smtpPort: string; + smtpUsername: string; + smtpPassword: string; + smtpSecure: boolean; + imapServer: string; + imapPort: string; + imapUsername: string; + imapPassword: string; + imapSecure: boolean; + imapRetries: string; +}; + +type EmailInboxFormProps = { + inboxData?: IEmailInboxPayload; +}; + +const EmailInboxForm = ({ inboxData }: EmailInboxFormProps): ReactElement => { const { t } = useTranslation(); const dispatchToastMessage = useToastMessageDispatch(); const setModal = useSetModal(); @@ -46,27 +70,27 @@ const EmailInboxForm = ({ inboxData }: { inboxData?: IEmailInboxPayload }): Reac control, handleSubmit, formState: { errors, isDirty }, - } = useForm({ + } = useForm({ values: { active: inboxData?.active ?? true, - name: inboxData?.name, - email: inboxData?.email, + name: inboxData?.name ?? '', + email: inboxData?.email ?? '', description: inboxData?.description, senderInfo: inboxData?.senderInfo, - department: inboxData?.department || '', + department: inboxData?.department, // SMTP - smtpServer: inboxData?.smtp.server, - smtpPort: inboxData?.smtp.port ?? 587, - smtpUsername: inboxData?.smtp.username, - smtpPassword: inboxData?.smtp.password, + smtpServer: inboxData?.smtp.server ?? '', + smtpPort: String(inboxData?.smtp.port ?? 587), + smtpUsername: inboxData?.smtp.username ?? '', + smtpPassword: inboxData?.smtp.password ?? '', smtpSecure: inboxData?.smtp.secure ?? false, // IMAP - imapServer: inboxData?.imap.server, - imapPort: inboxData?.imap.port ?? 993, - imapUsername: inboxData?.imap.username, - imapPassword: inboxData?.imap.password, + imapServer: inboxData?.imap.server ?? '', + imapPort: String(inboxData?.imap.port ?? 993), + imapUsername: inboxData?.imap.username ?? '', + imapPassword: inboxData?.imap.password ?? '', imapSecure: inboxData?.imap.secure ?? false, - imapRetries: inboxData?.imap.maxRetries ?? 10, + imapRetries: String(inboxData?.imap.maxRetries ?? 10), }, mode: 'all', }); @@ -110,10 +134,10 @@ const EmailInboxForm = ({ inboxData }: { inboxData?: IEmailInboxPayload }): Reac imapPassword, imapSecure, imapRetries, - }) => { + }: EmailInboxFormData) => { const smtp = { server: smtpServer, - port: parseInt(smtpPort), + port: parseInt(smtpPort, 10), username: smtpUsername, password: smtpPassword, secure: smtpSecure, @@ -121,11 +145,11 @@ const EmailInboxForm = ({ inboxData }: { inboxData?: IEmailInboxPayload }): Reac const imap = { server: imapServer, - port: parseInt(imapPort), + port: parseInt(imapPort, 10), username: imapUsername, password: imapPassword, secure: imapSecure, - maxRetries: parseInt(imapRetries), + maxRetries: parseInt(imapRetries, 10), }; const payload = { @@ -135,7 +159,7 @@ const EmailInboxForm = ({ inboxData }: { inboxData?: IEmailInboxPayload }): Reac email, description, senderInfo, - ...(department && { department: typeof department === 'string' ? department : department.value }), + ...(department && { department }), smtp, imap, }; @@ -150,7 +174,7 @@ const EmailInboxForm = ({ inboxData }: { inboxData?: IEmailInboxPayload }): Reac }, ); - const checkEmailExists = useEffectEvent(async (email) => { + const checkEmailExists = useEffectEvent(async (email: string) => { if (!email) { return; } diff --git a/apps/meteor/client/views/admin/integrations/incoming/EditIncomingWebhook.tsx b/apps/meteor/client/views/admin/integrations/incoming/EditIncomingWebhook.tsx index c23c61d892e2..50a698797b3b 100644 --- a/apps/meteor/client/views/admin/integrations/incoming/EditIncomingWebhook.tsx +++ b/apps/meteor/client/views/admin/integrations/incoming/EditIncomingWebhook.tsx @@ -12,7 +12,7 @@ import { useCreateIntegration } from '../hooks/useCreateIntegration'; import { useDeleteIntegration } from '../hooks/useDeleteIntegration'; import { useUpdateIntegration } from '../hooks/useUpdateIntegration'; -type EditIncomingWebhookFormData = { +export type EditIncomingWebhookFormData = { enabled: boolean; channel: string; username: string; diff --git a/apps/meteor/client/views/admin/integrations/incoming/IncomingWebhookForm.tsx b/apps/meteor/client/views/admin/integrations/incoming/IncomingWebhookForm.tsx index 2ccd413fb898..4df8be1cbeb2 100644 --- a/apps/meteor/client/views/admin/integrations/incoming/IncomingWebhookForm.tsx +++ b/apps/meteor/client/views/admin/integrations/incoming/IncomingWebhookForm.tsx @@ -24,6 +24,7 @@ import { useMemo } from 'react'; import { Controller, useFormContext } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; +import type { EditIncomingWebhookFormData } from './EditIncomingWebhook'; import useClipboardWithToast from '../../../../hooks/useClipboardWithToast'; import { useHighlightedCode } from '../../../../hooks/useHighlightedCode'; import { useExampleData } from '../hooks/useExampleIncomingData'; @@ -36,7 +37,7 @@ const IncomingWebhookForm = ({ webhookData }: { webhookData?: Serialized(); const { alias, emoji, avatar } = watch(); const url = absoluteUrl(`hooks/${webhookData?._id}/${webhookData?.token}`); diff --git a/apps/meteor/client/views/admin/integrations/outgoing/history/HistoryItem.tsx b/apps/meteor/client/views/admin/integrations/outgoing/history/HistoryItem.tsx index d9581600110c..74327d4968bf 100644 --- a/apps/meteor/client/views/admin/integrations/outgoing/history/HistoryItem.tsx +++ b/apps/meteor/client/views/admin/integrations/outgoing/history/HistoryItem.tsx @@ -3,6 +3,7 @@ import { Button, Icon, Box, AccordionItem, Field, FieldGroup, FieldLabel, FieldR import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { useMethod } from '@rocket.chat/ui-contexts'; import DOMPurify from 'dompurify'; +import type { MouseEvent } from 'react'; import { useTranslation } from 'react-i18next'; import { outgoingEvents } from '../../../../../../app/integrations/lib/outgoingEvents'; @@ -35,7 +36,7 @@ const HistoryItem = ({ data }: { data: Serialized }) => { const createdAt = typeof _createdAt === 'string' ? _createdAt : (_createdAt as Date).toISOString(); const updatedAt = typeof _updatedAt === 'string' ? _updatedAt : (_updatedAt as Date).toISOString(); - const handleClickReplay = useEffectEvent((e) => { + const handleClickReplay = useEffectEvent((e: MouseEvent) => { e.stopPropagation(); replayOutgoingIntegration({ integrationId, historyId: _id }); }); diff --git a/apps/meteor/client/views/admin/moderation/ModerationConsoleTable.tsx b/apps/meteor/client/views/admin/moderation/ModerationConsoleTable.tsx index bf57345490aa..816bec605bce 100644 --- a/apps/meteor/client/views/admin/moderation/ModerationConsoleTable.tsx +++ b/apps/meteor/client/views/admin/moderation/ModerationConsoleTable.tsx @@ -1,3 +1,4 @@ +import type { IUser } from '@rocket.chat/core-typings'; import { Pagination } from '@rocket.chat/fuselage'; import { useDebouncedValue, useMediaQuery, useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { useEndpoint, useRouter } from '@rocket.chat/ui-contexts'; @@ -62,7 +63,7 @@ const ModerationConsoleTable = () => { placeholderData: keepPreviousData, }); - const handleClick = useEffectEvent((id): void => { + const handleClick = useEffectEvent((id: IUser['_id']): void => { router.navigate({ pattern: '/admin/moderation/:tab?/:context?/:id?', params: { diff --git a/apps/meteor/client/views/admin/moderation/UserReports/ModConsoleUsersTable.tsx b/apps/meteor/client/views/admin/moderation/UserReports/ModConsoleUsersTable.tsx index e980be1fce75..87af70d21044 100644 --- a/apps/meteor/client/views/admin/moderation/UserReports/ModConsoleUsersTable.tsx +++ b/apps/meteor/client/views/admin/moderation/UserReports/ModConsoleUsersTable.tsx @@ -1,3 +1,4 @@ +import type { IUser } from '@rocket.chat/core-typings'; import { Pagination, States, StatesAction, StatesActions, StatesIcon, StatesTitle } from '@rocket.chat/fuselage'; import { useDebouncedValue, useMediaQuery, useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { useEndpoint, useRouter } from '@rocket.chat/ui-contexts'; @@ -58,7 +59,7 @@ const ModConsoleUsersTable = () => { placeholderData: keepPreviousData, }); - const handleClick = useEffectEvent((id): void => { + const handleClick = useEffectEvent((id: IUser['_id']): void => { router.navigate({ pattern: '/admin/moderation/:tab?/:context?/:id?', params: { tab: 'users', context: 'info', id }, diff --git a/apps/meteor/client/views/admin/moderation/helpers/DateRangePicker.tsx b/apps/meteor/client/views/admin/moderation/helpers/DateRangePicker.tsx index 80ff678b1780..ae4180ead2cc 100644 --- a/apps/meteor/client/views/admin/moderation/helpers/DateRangePicker.tsx +++ b/apps/meteor/client/views/admin/moderation/helpers/DateRangePicker.tsx @@ -1,6 +1,7 @@ import { Select, Box, type SelectOption } from '@rocket.chat/fuselage'; import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import moment, { type Moment } from 'moment'; +import type { Key } from 'react'; import { useMemo, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; @@ -25,7 +26,7 @@ const getWeekRange = (daysToSubtractFromStart: number, daysToSubtractFromEnd: nu const DateRangePicker = ({ onChange }: DateRangePickerProps) => { const { t } = useTranslation(); - const handleRange = useEffectEvent((range) => { + const handleRange = useEffectEvent((range: { start: string; end: string }) => { onChange(range); }); @@ -47,7 +48,7 @@ const DateRangePicker = ({ onChange }: DateRangePickerProps) => { }); }, [handleRange]); - const handleOptionClick = useEffectEvent((action) => { + const handleOptionClick = useEffectEvent((action: Key) => { switch (action) { case 'today': handleRange(getWeekRange(0, 0)); diff --git a/apps/meteor/client/views/admin/permissions/EditRolePage.tsx b/apps/meteor/client/views/admin/permissions/EditRolePage.tsx index 9778f4f4093d..d68998e5e09f 100644 --- a/apps/meteor/client/views/admin/permissions/EditRolePage.tsx +++ b/apps/meteor/client/views/admin/permissions/EditRolePage.tsx @@ -10,6 +10,14 @@ import RoleForm from './RoleForm'; import { ContextualbarFooter, ContextualbarScrollableContent } from '../../../components/Contextualbar'; import GenericModal from '../../../components/GenericModal'; +export type EditRolePageFormData = { + roleId: string; + name: string; + description: string; + scope: 'Users' | 'Subscriptions'; + mandatory2fa: boolean; +}; + const EditRolePage = ({ role, isEnterprise }: { role?: IRole; isEnterprise: boolean }): ReactElement => { const { t } = useTranslation(); const dispatchToastMessage = useToastMessageDispatch(); @@ -21,7 +29,7 @@ const EditRolePage = ({ role, isEnterprise }: { role?: IRole; isEnterprise: bool const updateRole = useEndpoint('POST', '/v1/roles.update'); const deleteRole = useEndpoint('POST', '/v1/roles.delete'); - const methods = useForm({ + const methods = useForm({ defaultValues: { roleId: role?._id, name: role?.name, @@ -40,7 +48,7 @@ const EditRolePage = ({ role, isEnterprise }: { role?: IRole; isEnterprise: bool } }); - const handleSave = useEffectEvent(async (data) => { + const handleSave = useEffectEvent(async (data: EditRolePageFormData) => { try { if (data.roleId) { await updateRole(data); diff --git a/apps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTableFilter.tsx b/apps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTableFilter.tsx index 8bc1fea42a4d..a86f97d66e95 100644 --- a/apps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTableFilter.tsx +++ b/apps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTableFilter.tsx @@ -1,6 +1,6 @@ import { TextInput } from '@rocket.chat/fuselage'; import { useEffectEvent, useDebouncedValue } from '@rocket.chat/fuselage-hooks'; -import type { ReactElement } from 'react'; +import type { FormEvent, ReactElement } from 'react'; import { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; @@ -13,7 +13,7 @@ const PermissionsTableFilter = ({ onChange }: { onChange: (debouncedFilter: stri onChange(debouncedFilter); }, [debouncedFilter, onChange]); - const handleFilter = useEffectEvent(({ currentTarget: { value } }) => { + const handleFilter = useEffectEvent(({ currentTarget: { value } }: FormEvent) => { setFilter(value); }); diff --git a/apps/meteor/client/views/admin/permissions/RoleForm.tsx b/apps/meteor/client/views/admin/permissions/RoleForm.tsx index a3b0eccbb407..5c725b042d23 100644 --- a/apps/meteor/client/views/admin/permissions/RoleForm.tsx +++ b/apps/meteor/client/views/admin/permissions/RoleForm.tsx @@ -1,10 +1,11 @@ import type { SelectOption } from '@rocket.chat/fuselage'; import { Field, FieldLabel, FieldRow, FieldError, FieldHint, TextInput, Select, ToggleSwitch } from '@rocket.chat/fuselage'; -import type { ReactElement } from 'react'; import { useMemo } from 'react'; import { useFormContext, Controller } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; +import type { EditRolePageFormData } from './EditRolePage'; + type RoleFormProps = { className?: string; editing?: boolean; @@ -12,13 +13,13 @@ type RoleFormProps = { isDisabled?: boolean; }; -const RoleForm = ({ className, editing = false, isProtected = false, isDisabled = false }: RoleFormProps): ReactElement => { +const RoleForm = ({ className, editing = false, isProtected = false, isDisabled = false }: RoleFormProps) => { const { t } = useTranslation(); const { register, control, formState: { errors }, - } = useFormContext(); + } = useFormContext(); const options: SelectOption[] = useMemo( () => [ @@ -54,9 +55,7 @@ const RoleForm = ({ className, editing = false, isProtected = false, isDisabled ( - } /> @@ -66,7 +65,7 @@ const RoleForm = ({ className, editing = false, isProtected = false, isDisabled } + render={({ field: { value, ...field } }) => } /> diff --git a/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRoleTable/UsersInRoleTable.tsx b/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRoleTable/UsersInRoleTable.tsx index d433d0b8f844..3d7d26f463cb 100644 --- a/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRoleTable/UsersInRoleTable.tsx +++ b/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRoleTable/UsersInRoleTable.tsx @@ -1,4 +1,4 @@ -import type { IRole, IRoom } from '@rocket.chat/core-typings'; +import type { IRole, IRoom, IUserInRole } from '@rocket.chat/core-typings'; import { Pagination } from '@rocket.chat/fuselage'; import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { useSetModal, useToastMessageDispatch, useEndpoint } from '@rocket.chat/ui-contexts'; @@ -61,9 +61,11 @@ const UsersInRoleTable = ({ rid, roleId, roleName, description }: UsersInRoleTab _updatedAt: new Date(user._updatedAt), })) || []; - const handleRemove = useEffectEvent((username) => { + const handleRemove = useEffectEvent((username: IUserInRole['username']) => { const remove = async () => { try { + if (!username) throw new Error('Username is required'); + await removeUserFromRoleEndpoint({ roleId, username, scope: rid }); dispatchToastMessage({ type: 'success', message: t('User_removed') }); queryClient.invalidateQueries({ diff --git a/apps/meteor/client/views/admin/permissions/hooks/useChangeRole.ts b/apps/meteor/client/views/admin/permissions/hooks/useChangeRole.ts index c94d34f0fa85..0537131c46d9 100644 --- a/apps/meteor/client/views/admin/permissions/hooks/useChangeRole.ts +++ b/apps/meteor/client/views/admin/permissions/hooks/useChangeRole.ts @@ -13,7 +13,7 @@ export const useChangeRole = ({ }): ((roleId: IRole['_id'], granted: boolean) => Promise) => { const dispatchToastMessage = useToastMessageDispatch(); - return useEffectEvent(async (roleId, granted) => { + return useEffectEvent(async (roleId: IRole['_id'], granted: boolean) => { try { if (granted) { await onRemove(permissionId, roleId); diff --git a/apps/meteor/client/views/admin/rooms/EditRoom.tsx b/apps/meteor/client/views/admin/rooms/EditRoom.tsx index 9b46b1e25e56..6fb943982d86 100644 --- a/apps/meteor/client/views/admin/rooms/EditRoom.tsx +++ b/apps/meteor/client/views/admin/rooms/EditRoom.tsx @@ -32,7 +32,7 @@ type EditRoomProps = { onDelete: () => void; }; -type EditRoomFormValues = { +type EditRoomFormData = { roomName: IRoom['name']; roomTopic: string; roomType: IRoom['t']; @@ -47,7 +47,7 @@ type EditRoomFormValues = { archived: boolean; }; -const getInitialValues = (room: Pick): EditRoomFormValues => ({ +const getInitialValues = (room: Pick): EditRoomFormData => ({ roomName: room.t === 'd' ? room.usernames?.join(' x ') : roomCoordinator.getRoomName(room.t, room), roomType: room.t, readOnly: !!room.ro, @@ -73,7 +73,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps) => { reset, handleSubmit, formState: { isDirty, errors, dirtyFields }, - } = useForm({ values: getInitialValues(room) }); + } = useForm({ values: getInitialValues(room) }); const { canViewName, @@ -96,17 +96,16 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps) => { const handleArchive = useArchiveRoom(room); - const handleUpdateRoomData = useEffectEvent(async ({ isDefault, favorite, ...formData }) => { + const handleUpdateRoomData = useEffectEvent(async ({ isDefault, favorite, ...formData }: EditRoomFormData) => { const data = getDirtyFields(formData, dirtyFields); delete data.archived; - delete data.favorite; try { await saveAction({ + ...data, rid: room._id, default: isDefault, favorite: { defaultValue: isDefault, favorite }, - ...data, }); dispatchToastMessage({ type: 'success', message: t('Room_updated_successfully') }); @@ -117,7 +116,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps) => { } }); - const handleSave = useEffectEvent((data) => + const handleSave = useEffectEvent((data: EditRoomFormData) => Promise.all([isDirty && handleUpdateRoomData(data), changeArchiving && handleArchive()].filter(Boolean)), ); diff --git a/apps/meteor/client/views/admin/settings/groups/VoipGroupPage/AssignAgentButton.tsx b/apps/meteor/client/views/admin/settings/groups/VoipGroupPage/AssignAgentButton.tsx index 4aaaa7c96186..29d2acb685cc 100644 --- a/apps/meteor/client/views/admin/settings/groups/VoipGroupPage/AssignAgentButton.tsx +++ b/apps/meteor/client/views/admin/settings/groups/VoipGroupPage/AssignAgentButton.tsx @@ -1,6 +1,7 @@ import { IconButton } from '@rocket.chat/fuselage'; import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { useSetModal } from '@rocket.chat/ui-contexts'; +import type { MouseEvent } from 'react'; import { useTranslation } from 'react-i18next'; import AssignAgentModal from './AssignAgentModal'; @@ -12,7 +13,7 @@ const AssignAgentButton = ({ extension, reload }: AssignAgentButtonProps) => { const { t } = useTranslation(); const setModal = useSetModal(); - const handleAssociation = useEffectEvent((e) => { + const handleAssociation = useEffectEvent((e: MouseEvent) => { e.stopPropagation(); setModal( setModal()} reload={reload} />); }); diff --git a/apps/meteor/client/views/admin/settings/groups/VoipGroupPage/AssignAgentModal.tsx b/apps/meteor/client/views/admin/settings/groups/VoipGroupPage/AssignAgentModal.tsx index 1757aeb77de5..0f81944b2daf 100644 --- a/apps/meteor/client/views/admin/settings/groups/VoipGroupPage/AssignAgentModal.tsx +++ b/apps/meteor/client/views/admin/settings/groups/VoipGroupPage/AssignAgentModal.tsx @@ -1,6 +1,7 @@ import { Button, Modal, Select, Field, FieldGroup, FieldLabel, FieldRow, Box } from '@rocket.chat/fuselage'; import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch, useEndpoint } from '@rocket.chat/ui-contexts'; +import type { FormEvent } from 'react'; import { useState, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -23,7 +24,7 @@ const AssignAgentModal = ({ existingExtension, closeModal, reload }: AssignAgent const assignAgent = useEndpoint('POST', '/v1/omnichannel/agent/extension'); - const handleAssignment = useEffectEvent(async (e) => { + const handleAssignment = useEffectEvent(async (e: FormEvent) => { e.preventDefault(); try { await assignAgent({ username: agent, extension }); @@ -33,7 +34,7 @@ const AssignAgentModal = ({ existingExtension, closeModal, reload }: AssignAgent reload(); closeModal(); }); - const handleAgentChange = useEffectEvent((e) => setAgent(e)); + const handleAgentChange = useEffectEvent((e: string) => setAgent(e)); const { value: availableExtensions, phase: state } = useEndpointData('/v1/omnichannel/extension', { params: query }); diff --git a/apps/meteor/client/views/admin/settings/groups/VoipGroupPage/RemoveAgentButton.tsx b/apps/meteor/client/views/admin/settings/groups/VoipGroupPage/RemoveAgentButton.tsx index ac39806afcb2..f2c13b5329e0 100644 --- a/apps/meteor/client/views/admin/settings/groups/VoipGroupPage/RemoveAgentButton.tsx +++ b/apps/meteor/client/views/admin/settings/groups/VoipGroupPage/RemoveAgentButton.tsx @@ -1,6 +1,7 @@ import { IconButton } from '@rocket.chat/fuselage'; import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { useSetModal, useToastMessageDispatch, useEndpoint } from '@rocket.chat/ui-contexts'; +import type { MouseEvent } from 'react'; import { useTranslation } from 'react-i18next'; import GenericModal from '../../../../../components/GenericModal'; @@ -23,7 +24,7 @@ const RemoveAgentButton = ({ username, reload }: RemoveAgentButtonProps) => { reload(); }); - const handleDelete = useEffectEvent((e) => { + const handleDelete = useEffectEvent((e: MouseEvent) => { e.stopPropagation(); const onDeleteAgent = async (): Promise => { try { diff --git a/apps/meteor/client/views/admin/subscription/SubscriptionCalloutLimits.tsx b/apps/meteor/client/views/admin/subscription/SubscriptionCalloutLimits.tsx index bd252acb3c49..a4e2ef55f0d6 100644 --- a/apps/meteor/client/views/admin/subscription/SubscriptionCalloutLimits.tsx +++ b/apps/meteor/client/views/admin/subscription/SubscriptionCalloutLimits.tsx @@ -25,8 +25,12 @@ export const SubscriptionCalloutLimits = () => { <> {start_fair_policy && ( - - Your workspace reached the <>{{ val: start_fair_policy.map(toTranslationKey) }} limit. + + Your workspace reached the <>{start_fair_policy.map(toTranslationKey)} limit. { {prevent_action && ( - - Your workspace exceeded the <>{{ val: prevent_action.map(toTranslationKey) }} license limit. + + Your workspace exceeded the <>{prevent_action.map(toTranslationKey)} license limit. { {disable_modules && ( - - Your workspace exceeded the <>{{ val: disable_modules.map(toTranslationKey) }} license limit. + + Your workspace exceeded the <>{disable_modules.map(toTranslationKey)} license limit. { {invalidate_license && ( - - Your workspace exceeded the <>{{ val: invalidate_license.map(toTranslationKey) }} license limit. + + Your workspace exceeded the <>{invalidate_license.map(toTranslationKey)} license limit. void; roleData: { roles: IRole[] } | undefined; - roleError: unknown; + roleError: Error | null; }; export type UserFormProps = Omit; @@ -366,7 +366,7 @@ const AdminUserForm = ({ userData, onReload, context, refetchUserFormData, roleD {t('Roles')} - {roleError && {roleError}} + {roleError && {roleError.message}} {!roleError && ( void; context: string; roleData: { roles: IRole[] } | undefined; - roleError: unknown; + roleError: Error | null; }; const AdminUserFormWithData = ({ uid, onReload, context, roleData, roleError }: AdminUserFormWithDataProps): ReactElement => { diff --git a/apps/meteor/client/views/admin/users/AdminUsersPage.tsx b/apps/meteor/client/views/admin/users/AdminUsersPage.tsx index 0a792ef75dde..534e591be932 100644 --- a/apps/meteor/client/views/admin/users/AdminUsersPage.tsx +++ b/apps/meteor/client/views/admin/users/AdminUsersPage.tsx @@ -115,8 +115,12 @@ const AdminUsersPage = (): ReactElement => { {preventAction?.includes('activeUsers') && ( - - Your workspace exceeded the <>{{ val: preventAction.map(toTranslationKey) }} license limit. + + Your workspace exceeded the <>{preventAction.map(toTranslationKey)} license limit. ).key !== undefined; }; - const handleClickOrKeyDown = useEffectEvent((id, e: MouseEvent | KeyboardEvent): void => { + const handleClickOrKeyDown = useEffectEvent((id: IUser['_id'], e: MouseEvent | KeyboardEvent): void => { e.stopPropagation(); const keyboardSubmitKeys = ['Enter', ' ']; diff --git a/apps/meteor/client/views/audit/components/forms/DateRangePicker.tsx b/apps/meteor/client/views/audit/components/forms/DateRangePicker.tsx index fbb5f18bfb5e..9d659169622e 100644 --- a/apps/meteor/client/views/audit/components/forms/DateRangePicker.tsx +++ b/apps/meteor/client/views/audit/components/forms/DateRangePicker.tsx @@ -1,7 +1,7 @@ import { Box, InputBox, Menu, Margins, Option } from '@rocket.chat/fuselage'; import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import moment from 'moment'; -import type { ReactElement, ComponentProps, SetStateAction } from 'react'; +import type { ReactElement, ComponentProps, SetStateAction, FormEvent } from 'react'; import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -122,11 +122,11 @@ const DateRangePicker = ({ value, onChange, ...props }: DateRangePickerProps): R onChange?.(newRange); }); - const handleChangeStart = useEffectEvent(({ currentTarget }) => { + const handleChangeStart = useEffectEvent(({ currentTarget }: FormEvent) => { dispatch({ newStart: currentTarget.value }); }); - const handleChangeEnd = useEffectEvent(({ currentTarget }) => { + const handleChangeEnd = useEffectEvent(({ currentTarget }: FormEvent) => { dispatch({ newEnd: currentTarget.value }); }); diff --git a/apps/meteor/client/views/directory/RoomTags.tsx b/apps/meteor/client/views/directory/RoomTags.tsx index 16627946dc55..610a0510f05c 100644 --- a/apps/meteor/client/views/directory/RoomTags.tsx +++ b/apps/meteor/client/views/directory/RoomTags.tsx @@ -1,9 +1,9 @@ -import type { IRoom } from '@rocket.chat/core-typings'; +import type { IRoom, Serialized } from '@rocket.chat/core-typings'; import { Box, Margins, Tag } from '@rocket.chat/fuselage'; import type { ReactElement } from 'react'; import { useTranslation } from 'react-i18next'; -const RoomTags = ({ room }: { room: IRoom }): ReactElement => { +const RoomTags = ({ room }: { room: Serialized }): ReactElement => { const { t } = useTranslation(); return ( diff --git a/apps/meteor/client/views/directory/tabs/channels/ChannelsTable/ChannelsTable.tsx b/apps/meteor/client/views/directory/tabs/channels/ChannelsTable/ChannelsTable.tsx index 91a55952bca3..bceb9f248e94 100644 --- a/apps/meteor/client/views/directory/tabs/channels/ChannelsTable/ChannelsTable.tsx +++ b/apps/meteor/client/views/directory/tabs/channels/ChannelsTable/ChannelsTable.tsx @@ -1,4 +1,4 @@ -import type { IRoom } from '@rocket.chat/core-typings'; +import type { IRoom, Serialized } from '@rocket.chat/core-typings'; import { Pagination, States, StatesIcon, StatesTitle, StatesActions, StatesAction } from '@rocket.chat/fuselage'; import { useMediaQuery } from '@rocket.chat/fuselage-hooks'; import { useRoute, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; @@ -113,7 +113,7 @@ const ChannelsTable = () => { {headers} {data.result.map((room) => ( - + } onClick={onClick} mediaQuery={mediaQuery} /> ))} diff --git a/apps/meteor/client/views/directory/tabs/channels/ChannelsTable/ChannelsTableRow.tsx b/apps/meteor/client/views/directory/tabs/channels/ChannelsTable/ChannelsTableRow.tsx index f2a894a5df6c..e149531988f3 100644 --- a/apps/meteor/client/views/directory/tabs/channels/ChannelsTable/ChannelsTableRow.tsx +++ b/apps/meteor/client/views/directory/tabs/channels/ChannelsTable/ChannelsTableRow.tsx @@ -1,4 +1,4 @@ -import type { IRoom, ITeam } from '@rocket.chat/core-typings'; +import type { IRoom, Serialized } from '@rocket.chat/core-typings'; import { Box, Avatar } from '@rocket.chat/fuselage'; import type { KeyboardEvent, MouseEvent } from 'react'; @@ -11,7 +11,7 @@ import RoomTags from '../../../RoomTags'; type ChannelsTableRowProps = { onClick: (name: IRoom['name'], type: IRoom['t']) => (e: KeyboardEvent | MouseEvent) => void; - room: IRoom & { belongsTo?: ITeam }; + room: Serialized; mediaQuery: boolean; }; diff --git a/apps/meteor/client/views/directory/tabs/teams/TeamsTable/TeamsTable.tsx b/apps/meteor/client/views/directory/tabs/teams/TeamsTable/TeamsTable.tsx index e4788d9e5b3e..131319991db3 100644 --- a/apps/meteor/client/views/directory/tabs/teams/TeamsTable/TeamsTable.tsx +++ b/apps/meteor/client/views/directory/tabs/teams/TeamsTable/TeamsTable.tsx @@ -1,4 +1,4 @@ -import type { IRoom } from '@rocket.chat/core-typings'; +import type { IRoom, Serialized } from '@rocket.chat/core-typings'; import { Pagination, States, StatesIcon, StatesTitle, StatesActions, StatesAction } from '@rocket.chat/fuselage'; import { useMediaQuery } from '@rocket.chat/fuselage-hooks'; import { useRoute, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; @@ -91,7 +91,7 @@ const TeamsTable = () => { {data.result.map((team) => ( & { roomsCount: number }} onClick={onClick} mediaQuery={mediaQuery} /> diff --git a/apps/meteor/client/views/directory/tabs/teams/TeamsTable/TeamsTableRow.tsx b/apps/meteor/client/views/directory/tabs/teams/TeamsTable/TeamsTableRow.tsx index 50e7a2d1cc7b..86f8ab99b0a6 100644 --- a/apps/meteor/client/views/directory/tabs/teams/TeamsTable/TeamsTableRow.tsx +++ b/apps/meteor/client/views/directory/tabs/teams/TeamsTable/TeamsTableRow.tsx @@ -1,4 +1,4 @@ -import type { IRoom } from '@rocket.chat/core-typings'; +import type { IRoom, Serialized } from '@rocket.chat/core-typings'; import { Box, Avatar } from '@rocket.chat/fuselage'; import type { KeyboardEvent, MouseEvent } from 'react'; @@ -11,7 +11,7 @@ import RoomTags from '../../../RoomTags'; type TeamsTableRowProps = { onClick: (name: IRoom['name'], type: IRoom['t']) => (e: KeyboardEvent | MouseEvent) => void; - team: IRoom & { roomsCount: number }; + team: Serialized; mediaQuery: boolean; }; diff --git a/apps/meteor/client/views/e2e/EnterE2EPasswordModal.tsx b/apps/meteor/client/views/e2e/EnterE2EPasswordModal.tsx index 888afa1d7b3e..0a439705e0eb 100644 --- a/apps/meteor/client/views/e2e/EnterE2EPasswordModal.tsx +++ b/apps/meteor/client/views/e2e/EnterE2EPasswordModal.tsx @@ -1,7 +1,7 @@ import { Box, PasswordInput, Field, FieldGroup, FieldRow, FieldError } from '@rocket.chat/fuselage'; import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import DOMPurify from 'dompurify'; -import type { ChangeEvent, ReactElement } from 'react'; +import type { ChangeEvent, FormEvent, ReactElement } from 'react'; import { useState, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; @@ -28,7 +28,7 @@ const EnterE2EPasswordModal = ({ [setPassword], ); - const handleConfirm = useEffectEvent((e): void => { + const handleConfirm = useEffectEvent((e: FormEvent): void => { e.preventDefault(); if (password === '') { setPasswordError(t('Invalid_pass')); diff --git a/apps/meteor/client/views/modal/uikit/hooks/useValues.ts b/apps/meteor/client/views/modal/uikit/hooks/useValues.ts deleted file mode 100644 index c2dbe247dcce..000000000000 --- a/apps/meteor/client/views/modal/uikit/hooks/useValues.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; -import type { Block, LayoutBlock } from '@rocket.chat/ui-kit'; -import { useReducer } from 'react'; - -type LayoutBlockWithElement = Extract; -type LayoutBlockWithElements = Extract; -type ElementFromLayoutBlock = LayoutBlockWithElement['element'] | LayoutBlockWithElements['elements'][number]; - -const hasElementInBlock = (block: LayoutBlock): block is LayoutBlockWithElement => 'element' in block; -const hasElementsInBlock = (block: LayoutBlock): block is LayoutBlockWithElements => 'elements' in block; -const hasInitialValueAndActionId = ( - element: ElementFromLayoutBlock, -): element is Extract & { initialValue: unknown } => - 'initialValue' in element && 'actionId' in element && typeof element.actionId === 'string' && !!element?.initialValue; - -const extractValue = (element: ElementFromLayoutBlock, obj: Record, blockId?: string) => { - if (hasInitialValueAndActionId(element)) { - obj[element.actionId] = { value: element.initialValue, blockId }; - } -}; - -const reduceBlocks = (obj: Record, block: LayoutBlock) => { - if (hasElementInBlock(block)) { - extractValue(block.element, obj, block.blockId); - } - if (hasElementsInBlock(block)) { - for (const element of block.elements) { - extractValue(element, obj, block.blockId); - } - } - - return obj; -}; - -export const useValues = (blocks: Block[]) => { - const reducer = useEffectEvent((values, { actionId, payload }) => ({ - ...values, - [actionId]: payload, - })); - - const initializer = useEffectEvent((blocks: LayoutBlock[]) => { - const obj: Record = {}; - - return blocks.reduce(reduceBlocks, obj); - }); - - return useReducer(reducer, blocks, initializer); -}; diff --git a/apps/meteor/client/views/oauth/components/AuthorizationFormPage.tsx b/apps/meteor/client/views/oauth/components/AuthorizationFormPage.tsx index 390795ce45fd..d4e9713e055a 100644 --- a/apps/meteor/client/views/oauth/components/AuthorizationFormPage.tsx +++ b/apps/meteor/client/views/oauth/components/AuthorizationFormPage.tsx @@ -60,8 +60,8 @@ const AuthorizationFormPage = ({ oauthApp, redirectUri, user }: AuthorizationFor

- - {{ appName: oauthApp.name }} + + {oauthApp.name}

diff --git a/apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx b/apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx index 0cbfc9ea95fd..f643e99a8ebb 100644 --- a/apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx +++ b/apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx @@ -1,4 +1,4 @@ -import type { ILivechatAgent, ILivechatDepartment, ILivechatDepartmentAgents } from '@rocket.chat/core-typings'; +import type { ILivechatAgent, ILivechatAgentStatus, ILivechatDepartment, ILivechatDepartmentAgents } from '@rocket.chat/core-typings'; import { Field, FieldLabel, @@ -32,6 +32,16 @@ import { import { UserInfoAvatar } from '../../../components/UserInfo'; import { MaxChatsPerAgent } from '../additionalForms'; +type AgentEditFormData = { + name: string | undefined; + username: string | undefined; + email: string | undefined; + departments: string[]; + status: ILivechatAgentStatus; + maxNumberSimultaneousChat: number; + voipExtension: string; +}; + type AgentEditProps = { agentData: Pick; userDepartments: (Pick & { departmentName: string })[]; @@ -81,7 +91,7 @@ const AgentEdit = ({ agentData, userDepartments, availableDepartments }: AgentEd const initialDepartmentValue = useMemo(() => userDepartments.map(({ departmentId }) => departmentId) || [], [userDepartments]); - const methods = useForm({ + const methods = useForm({ values: { name, username, @@ -103,7 +113,7 @@ const AgentEdit = ({ agentData, userDepartments, availableDepartments }: AgentEd const saveAgentInfo = useMethod('livechat:saveAgentInfo'); const saveAgentStatus = useEndpoint('POST', '/v1/livechat/agent.status'); - const handleSave = useEffectEvent(async ({ status, departments, ...data }) => { + const handleSave = useEffectEvent(async ({ status, departments, ...data }: AgentEditFormData) => { try { await saveAgentStatus({ agentId: agentData._id, status }); await saveAgentInfo(agentData._id, data, departments); diff --git a/apps/meteor/client/views/omnichannel/agents/AgentsTable/AgentsTable.tsx b/apps/meteor/client/views/omnichannel/agents/AgentsTable/AgentsTable.tsx index 448dee910c03..068745fe3824 100644 --- a/apps/meteor/client/views/omnichannel/agents/AgentsTable/AgentsTable.tsx +++ b/apps/meteor/client/views/omnichannel/agents/AgentsTable/AgentsTable.tsx @@ -39,7 +39,7 @@ const AgentsTable = () => { const [defaultQuery] = useState(hashKey([query])); const queryHasChanged = defaultQuery !== hashKey([query]); - const onHeaderClick = useEffectEvent((id) => { + const onHeaderClick = useEffectEvent((id: 'name' | 'username' | 'emails.address' | 'statusLivechat') => { if (sortBy === id) { setSort(id, sortDirection === 'asc' ? 'desc' : 'asc'); return; diff --git a/apps/meteor/client/views/omnichannel/analytics/DateRangePicker.tsx b/apps/meteor/client/views/omnichannel/analytics/DateRangePicker.tsx index 72859bfc7495..427591f4aeaa 100644 --- a/apps/meteor/client/views/omnichannel/analytics/DateRangePicker.tsx +++ b/apps/meteor/client/views/omnichannel/analytics/DateRangePicker.tsx @@ -2,7 +2,7 @@ import { Box, InputBox, Menu, Field, FieldLabel, FieldRow } from '@rocket.chat/f import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import type { Moment } from 'moment'; import moment from 'moment'; -import type { ComponentProps } from 'react'; +import type { ComponentProps, FormEvent } from 'react'; import { useState, useMemo, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; @@ -32,7 +32,7 @@ const DateRangePicker = ({ onChange = () => undefined, ...props }: DateRangePick const { start, end } = range; - const handleStart = useEffectEvent(({ currentTarget }) => { + const handleStart = useEffectEvent(({ currentTarget }: FormEvent) => { const rangeObj = { start: currentTarget.value, end: range.end, @@ -41,7 +41,7 @@ const DateRangePicker = ({ onChange = () => undefined, ...props }: DateRangePick onChange(rangeObj); }); - const handleEnd = useEffectEvent(({ currentTarget }) => { + const handleEnd = useEffectEvent(({ currentTarget }: FormEvent) => { const rangeObj = { end: currentTarget.value, start: range.start, @@ -50,7 +50,7 @@ const DateRangePicker = ({ onChange = () => undefined, ...props }: DateRangePick onChange(rangeObj); }); - const handleRange = useEffectEvent((range) => { + const handleRange = useEffectEvent((range: { start: string; end: string }) => { setRange(range); onChange(range); }); diff --git a/apps/meteor/client/views/omnichannel/analytics/InterchangeableChart.tsx b/apps/meteor/client/views/omnichannel/analytics/InterchangeableChart.tsx index 9708ddf2003a..8daa8481aa8a 100644 --- a/apps/meteor/client/views/omnichannel/analytics/InterchangeableChart.tsx +++ b/apps/meteor/client/views/omnichannel/analytics/InterchangeableChart.tsx @@ -57,32 +57,42 @@ const InterchangeableChart = ({ const loadData = useMethod('livechat:getAnalyticsChartData'); - const draw = useEffectEvent(async (params) => { - try { - const tooltipCallbacks = getChartTooltips(chartName); - if (!params?.daterange?.from || !params?.daterange?.to) { - return; - } - const result = await loadData(params); - if (!result?.chartLabel || !result?.dataLabels || !result?.dataPoints) { - throw new Error('Error! fetching chart data. Details: livechat:getAnalyticsChartData => Missing Data'); + const draw = useEffectEvent( + async (params: { + daterange: { + from: string; + to: string; + }; + chartOptions: { + name: string; + }; + }) => { + try { + const tooltipCallbacks = getChartTooltips(chartName); + if (!params?.daterange?.from || !params?.daterange?.to) { + return; + } + const result = await loadData(params); + if (!result?.chartLabel || !result?.dataLabels || !result?.dataPoints) { + throw new Error('Error! fetching chart data. Details: livechat:getAnalyticsChartData => Missing Data'); + } + (context.current || typeof context.current === 'undefined') && + canvas.current && + (context.current = await drawLineChart( + canvas.current, + context.current, + [result.chartLabel], + result.dataLabels, + [result.dataPoints], + { + tooltipCallbacks, + }, + )); + } catch (error) { + dispatchToastMessage({ type: 'error', message: error }); } - (context.current || typeof context.current === 'undefined') && - canvas.current && - (context.current = await drawLineChart( - canvas.current, - context.current, - [result.chartLabel], - result.dataLabels, - [result.dataPoints], - { - tooltipCallbacks, - }, - )); - } catch (error) { - dispatchToastMessage({ type: 'error', message: error }); - } - }); + }, + ); useEffect(() => { draw({ diff --git a/apps/meteor/client/views/omnichannel/appearance/AppearancePage.tsx b/apps/meteor/client/views/omnichannel/appearance/AppearancePage.tsx index b37e1ec0f5d4..e2d4780585e7 100644 --- a/apps/meteor/client/views/omnichannel/appearance/AppearancePage.tsx +++ b/apps/meteor/client/views/omnichannel/appearance/AppearancePage.tsx @@ -45,7 +45,7 @@ const AppearancePage = ({ settings }: { settings: Serialized[] }) => { const saveAction = useEndpoint('POST', '/v1/livechat/appearance'); - const methods = useForm({ defaultValues: reduceAppearance(settings) }); + const methods = useForm({ defaultValues: reduceAppearance(settings) }); const { reset, formState: { isDirty }, @@ -55,7 +55,7 @@ const AppearancePage = ({ settings }: { settings: Serialized[] }) => { const currentData = watch(); - const handleSave = useEffectEvent(async (data) => { + const handleSave = useEffectEvent(async (data: LivechatAppearanceSettings) => { const mappedAppearance = Object.entries(data) .map(([_id, value]) => ({ _id, value })) .filter((item) => item.value !== undefined) as { diff --git a/apps/meteor/client/views/omnichannel/currentChats/CurrentChatsRoute.tsx b/apps/meteor/client/views/omnichannel/currentChats/CurrentChatsRoute.tsx index c94cfbfd989f..730cecf1e400 100644 --- a/apps/meteor/client/views/omnichannel/currentChats/CurrentChatsRoute.tsx +++ b/apps/meteor/client/views/omnichannel/currentChats/CurrentChatsRoute.tsx @@ -13,7 +13,7 @@ const CurrentChatsRoute = (): ReactElement => { const canViewCurrentChats = usePermission('view-livechat-current-chats'); const router = useRouter(); - const onRowClick = useEffectEvent((_id) => { + const onRowClick = useEffectEvent((_id: string) => { router.navigate(`/omnichannel/current/${_id}`); }); diff --git a/apps/meteor/client/views/omnichannel/currentChats/FilterByText.tsx b/apps/meteor/client/views/omnichannel/currentChats/FilterByText.tsx index 914b8f608cc4..3d42313ff4d7 100644 --- a/apps/meteor/client/views/omnichannel/currentChats/FilterByText.tsx +++ b/apps/meteor/client/views/omnichannel/currentChats/FilterByText.tsx @@ -2,7 +2,7 @@ import { TextInput, Box, Select, InputBox } from '@rocket.chat/fuselage'; import { useEffectEvent, useLocalStorage } from '@rocket.chat/fuselage-hooks'; import { useSetModal, useToastMessageDispatch, useMethod } from '@rocket.chat/ui-contexts'; import moment from 'moment'; -import type { Dispatch, SetStateAction } from 'react'; +import type { Dispatch, FormEvent, Key, SetStateAction } from 'react'; import { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; @@ -42,13 +42,13 @@ const FilterByText = ({ setFilter, reload, customFields, setCustomFields, hasCus const [to, setTo] = useLocalStorage('to', ''); const [tags, setTags] = useLocalStorage('tags', []); - const handleGuest = useEffectEvent((e) => setGuest(e.target.value)); - const handleServedBy = useEffectEvent((e) => setServedBy(e)); - const handleStatus = useEffectEvent((e) => setStatus(e)); - const handleDepartment = useEffectEvent((e) => setDepartment(e)); - const handleFrom = useEffectEvent((e) => setFrom(e.target.value)); - const handleTo = useEffectEvent((e) => setTo(e.target.value)); - const handleTags = useEffectEvent((e) => setTags(e)); + const handleGuest = useEffectEvent((e: FormEvent) => setGuest(e.currentTarget.value)); + const handleServedBy = useEffectEvent((e: string) => setServedBy(e)); + const handleStatus = useEffectEvent((e: Key) => setStatus(e as string)); + const handleDepartment = useEffectEvent((e: string) => setDepartment(e)); + const handleFrom = useEffectEvent((e: FormEvent) => setFrom(e.currentTarget.value)); + const handleTo = useEffectEvent((e: FormEvent) => setTo(e.currentTarget.value)); + const handleTags = useEffectEvent((e: { label: string; value: string }[]) => setTags(e)); const reset = useEffectEvent(() => { setGuest(''); @@ -61,7 +61,7 @@ const FilterByText = ({ setFilter, reload, customFields, setCustomFields, hasCus setCustomFields(undefined); }); - const onSubmit = useEffectEvent((e) => e.preventDefault()); + const onSubmit = useEffectEvent((e: FormEvent) => e.preventDefault()); useEffect(() => { setFilter((data) => ({ diff --git a/apps/meteor/client/views/omnichannel/currentChats/RemoveChatButton.tsx b/apps/meteor/client/views/omnichannel/currentChats/RemoveChatButton.tsx index a028b29b073f..2558055f7ea3 100644 --- a/apps/meteor/client/views/omnichannel/currentChats/RemoveChatButton.tsx +++ b/apps/meteor/client/views/omnichannel/currentChats/RemoveChatButton.tsx @@ -1,6 +1,7 @@ import { IconButton } from '@rocket.chat/fuselage'; import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { useSetModal, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; +import type { MouseEvent } from 'react'; import { useTranslation } from 'react-i18next'; import { useRemoveCurrentChatMutation } from './hooks/useRemoveCurrentChatMutation'; @@ -18,7 +19,7 @@ const RemoveChatButton = ({ _id }: RemoveChatButtonProps) => { removeCurrentChatMutation.mutate(_id); }); - const handleDelete = useEffectEvent((e) => { + const handleDelete = useEffectEvent((e: MouseEvent) => { e.stopPropagation(); const onDeleteAgent = async (): Promise => { try { diff --git a/apps/meteor/client/views/omnichannel/customFields/CustomFieldsTable.tsx b/apps/meteor/client/views/omnichannel/customFields/CustomFieldsTable.tsx index 6c96bb87f859..6121fdc6ad49 100644 --- a/apps/meteor/client/views/omnichannel/customFields/CustomFieldsTable.tsx +++ b/apps/meteor/client/views/omnichannel/customFields/CustomFieldsTable.tsx @@ -28,7 +28,7 @@ const CustomFieldsTable = () => { const { sortBy, sortDirection, setSort } = useSort<'_id' | 'label' | 'scope' | 'visibility'>('_id'); const handleAddNew = useEffectEvent(() => router.navigate('/omnichannel/customfields/new')); - const onRowClick = useEffectEvent((id) => () => router.navigate(`/omnichannel/customfields/edit/${id}`)); + const onRowClick = useEffectEvent((id: string) => () => router.navigate(`/omnichannel/customfields/edit/${id}`)); const handleDelete = useRemoveCustomField(); diff --git a/apps/meteor/client/views/omnichannel/customFields/EditCustomFields.tsx b/apps/meteor/client/views/omnichannel/customFields/EditCustomFields.tsx index 42c62ec616bd..aa0877ad9bcc 100644 --- a/apps/meteor/client/views/omnichannel/customFields/EditCustomFields.tsx +++ b/apps/meteor/client/views/omnichannel/customFields/EditCustomFields.tsx @@ -30,6 +30,20 @@ import { import { CustomFieldsAdditionalForm } from '../additionalForms'; import { useRemoveCustomField } from './useRemoveCustomField'; +export type EditCustomFieldsFormData = { + field: string; + label: string; + scope: 'visitor' | 'room'; + visibility: boolean; + searchable: boolean; + regexp: string; + type: string; + required: boolean; + defaultValue: string; + options: string; + public: boolean; +}; + const getInitialValues = (customFieldData: Serialized | undefined) => ({ field: customFieldData?._id || '', label: customFieldData?.label || '', @@ -53,7 +67,7 @@ const EditCustomFields = ({ customFieldData }: { customFieldData?: Serialized({ mode: 'onBlur', values: getInitialValues(customFieldData) }); const { control, handleSubmit, @@ -62,7 +76,7 @@ const EditCustomFields = ({ customFieldData }: { customFieldData?: Serialized { + const handleSave = useEffectEvent(async ({ visibility, ...data }: EditCustomFieldsFormData) => { try { await saveCustomField(customFieldData?._id as unknown as string, { visibility: visibility ? 'visible' : 'hidden', diff --git a/apps/meteor/client/views/omnichannel/customFields/useRemoveCustomField.tsx b/apps/meteor/client/views/omnichannel/customFields/useRemoveCustomField.tsx index 4dd947ad8dbf..b075319cf510 100644 --- a/apps/meteor/client/views/omnichannel/customFields/useRemoveCustomField.tsx +++ b/apps/meteor/client/views/omnichannel/customFields/useRemoveCustomField.tsx @@ -12,7 +12,7 @@ export const useRemoveCustomField = () => { const removeCustomField = useMethod('livechat:removeCustomField'); const queryClient = useQueryClient(); - const handleDelete = useEffectEvent((id) => { + const handleDelete = useEffectEvent((id: string) => { const onDeleteAgent = async () => { try { await removeCustomField(id); diff --git a/apps/meteor/client/views/omnichannel/departments/DepartmentAgentsTable/AddAgent.tsx b/apps/meteor/client/views/omnichannel/departments/DepartmentAgentsTable/AddAgent.tsx index 976b62891a12..482397dff0fe 100644 --- a/apps/meteor/client/views/omnichannel/departments/DepartmentAgentsTable/AddAgent.tsx +++ b/apps/meteor/client/views/omnichannel/departments/DepartmentAgentsTable/AddAgent.tsx @@ -17,7 +17,7 @@ function AddAgent({ agentList, onAdd }: { agentList: IDepartmentAgent[]; onAdd: const dispatchToastMessage = useToastMessageDispatch(); - const handleAgent = useEffectEvent((e) => setUserId(e)); + const handleAgent = useEffectEvent((e: string) => setUserId(e)); const handleSave = useEffectEvent(async () => { if (!userId) { diff --git a/apps/meteor/client/views/omnichannel/departments/DepartmentAgentsTable/RemoveAgentButton.tsx b/apps/meteor/client/views/omnichannel/departments/DepartmentAgentsTable/RemoveAgentButton.tsx index fc26999d1f47..9b41cf23b41b 100644 --- a/apps/meteor/client/views/omnichannel/departments/DepartmentAgentsTable/RemoveAgentButton.tsx +++ b/apps/meteor/client/views/omnichannel/departments/DepartmentAgentsTable/RemoveAgentButton.tsx @@ -1,6 +1,7 @@ import { IconButton } from '@rocket.chat/fuselage'; import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { useSetModal, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; +import type { MouseEvent } from 'react'; import { useTranslation } from 'react-i18next'; import GenericModal from '../../../../components/GenericModal'; @@ -10,7 +11,7 @@ function RemoveAgentButton({ agentId, onRemove }: { agentId: string; onRemove: ( const dispatchToastMessage = useToastMessageDispatch(); const { t } = useTranslation(); - const handleDelete = useEffectEvent((e) => { + const handleDelete = useEffectEvent((e: MouseEvent) => { e.stopPropagation(); const onRemoveAgent = async () => { diff --git a/apps/meteor/client/views/omnichannel/departments/DepartmentsPage.tsx b/apps/meteor/client/views/omnichannel/departments/DepartmentsPage.tsx index bbc71c31b816..faa32004aa3b 100644 --- a/apps/meteor/client/views/omnichannel/departments/DepartmentsPage.tsx +++ b/apps/meteor/client/views/omnichannel/departments/DepartmentsPage.tsx @@ -14,10 +14,14 @@ const DepartmentsPage = () => { const context = useRouteParameter('context'); const id = useRouteParameter('id'); - const handleTabClick = useEffectEvent((tab) => - departmentsRoute.push({ - context: tab, - }), + const handleTabClick = useEffectEvent((tab: undefined | 'archived') => + departmentsRoute.push( + tab + ? { + context: tab, + } + : {}, + ), ); const onAddNew = useEffectEvent(() => diff --git a/apps/meteor/client/views/omnichannel/departments/DepartmentsTable/RemoveDepartmentModal.tsx b/apps/meteor/client/views/omnichannel/departments/DepartmentsTable/RemoveDepartmentModal.tsx index 2c7c150a581c..802739cc2ff2 100644 --- a/apps/meteor/client/views/omnichannel/departments/DepartmentsTable/RemoveDepartmentModal.tsx +++ b/apps/meteor/client/views/omnichannel/departments/DepartmentsTable/RemoveDepartmentModal.tsx @@ -1,7 +1,7 @@ import { Box, Input } from '@rocket.chat/fuselage'; import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch, useEndpoint } from '@rocket.chat/ui-contexts'; -import type { ChangeEvent, ReactElement } from 'react'; +import type { ChangeEvent, FormEvent, ReactElement } from 'react'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -21,7 +21,7 @@ const RemoveDepartmentModal = ({ _id = '', name, reset, onClose }: RemoveDepartm const removeDepartment = useEndpoint('DELETE', '/v1/livechat/department/:_id', { _id }); const dispatchToast = useToastMessageDispatch(); - const onSubmit = useEffectEvent(async (e) => { + const onSubmit = useEffectEvent(async (e: FormEvent) => { e.preventDefault(); try { diff --git a/apps/meteor/client/views/omnichannel/directory/calls/CallTable.tsx b/apps/meteor/client/views/omnichannel/directory/calls/CallTable.tsx index 8c2b4b2a98bc..4fd437aedac0 100644 --- a/apps/meteor/client/views/omnichannel/directory/calls/CallTable.tsx +++ b/apps/meteor/client/views/omnichannel/directory/calls/CallTable.tsx @@ -41,14 +41,14 @@ const CallTable = () => { 500, ); - const onRowClick = useEffectEvent((id, token) => { + const onRowClick = useEffectEvent((id: string, token?: string) => { directoryRoute.push( { tab: 'calls', context: 'info', id, }, - { token }, + token ? { token } : {}, ); }); diff --git a/apps/meteor/client/views/omnichannel/directory/chats/ChatsTable/ChatsTableRow.tsx b/apps/meteor/client/views/omnichannel/directory/chats/ChatsTable/ChatsTableRow.tsx index 2660702e0d26..c48ae5ca30fe 100644 --- a/apps/meteor/client/views/omnichannel/directory/chats/ChatsTable/ChatsTableRow.tsx +++ b/apps/meteor/client/views/omnichannel/directory/chats/ChatsTable/ChatsTableRow.tsx @@ -36,7 +36,7 @@ const ChatsTableRow = (room: IOmnichannelRoomWithDepartment) => { return onHold ? t('On_Hold_Chats') : t('Room_Status_Open'); }; - const onRowClick = useEffectEvent((id) => + const onRowClick = useEffectEvent((id: string) => directoryRoute.push({ tab: 'chats', context: 'info', diff --git a/apps/meteor/client/views/omnichannel/directory/contacts/ContactTableRow.tsx b/apps/meteor/client/views/omnichannel/directory/contacts/ContactTableRow.tsx index 356561d0975d..78304d132a13 100644 --- a/apps/meteor/client/views/omnichannel/directory/contacts/ContactTableRow.tsx +++ b/apps/meteor/client/views/omnichannel/directory/contacts/ContactTableRow.tsx @@ -26,7 +26,7 @@ const ContactTableRow = ({ _id, name, phones, contactManager, lastChat, channels })[0]; const onRowClick = useEffectEvent( - (id) => (): void => + (id: string) => (): void => directoryRoute.push({ id, tab: 'contacts', diff --git a/apps/meteor/client/views/omnichannel/managers/RemoveManagerButton.tsx b/apps/meteor/client/views/omnichannel/managers/RemoveManagerButton.tsx index 7c9e7df45cdc..da3960077e19 100644 --- a/apps/meteor/client/views/omnichannel/managers/RemoveManagerButton.tsx +++ b/apps/meteor/client/views/omnichannel/managers/RemoveManagerButton.tsx @@ -1,7 +1,7 @@ import { IconButton } from '@rocket.chat/fuselage'; import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { useSetModal, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; -import type { ReactElement } from 'react'; +import type { MouseEvent, ReactElement } from 'react'; import { useTranslation } from 'react-i18next'; import GenericModal from '../../../components/GenericModal'; @@ -18,7 +18,7 @@ const RemoveManagerButton = ({ _id, reload }: { _id: string; reload: () => void await deleteAction(); reload(); }); - const handleDelete = useEffectEvent((e) => { + const handleDelete = useEffectEvent((e: MouseEvent) => { e.stopPropagation(); const onDeleteManager = async (): Promise => { try { diff --git a/apps/meteor/client/views/omnichannel/queueList/QueueListFilter.tsx b/apps/meteor/client/views/omnichannel/queueList/QueueListFilter.tsx index b73966e5e276..c3022430fd31 100644 --- a/apps/meteor/client/views/omnichannel/queueList/QueueListFilter.tsx +++ b/apps/meteor/client/views/omnichannel/queueList/QueueListFilter.tsx @@ -1,6 +1,6 @@ import { Box, Select, Label } from '@rocket.chat/fuselage'; import { useEffectEvent, useLocalStorage } from '@rocket.chat/fuselage-hooks'; -import type { Dispatch, SetStateAction } from 'react'; +import type { Dispatch, FormEvent, Key, SetStateAction } from 'react'; import { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; @@ -23,11 +23,11 @@ export const QueueListFilter = ({ setFilter, ...props }: QueueListFilterProps) = const [status, setStatus] = useLocalStorage('status', 'online'); const [department, setDepartment] = useLocalStorage('department', 'all'); - const handleServedBy = useEffectEvent((e) => setServedBy(e)); - const handleStatus = useEffectEvent((e) => setStatus(e)); - const handleDepartment = useEffectEvent((e) => setDepartment(e)); + const handleServedBy = useEffectEvent((e: string) => setServedBy(e)); + const handleStatus = useEffectEvent((e: Key) => setStatus(e as string)); + const handleDepartment = useEffectEvent((e: string) => setDepartment(e)); - const onSubmit = useEffectEvent((e) => e.preventDefault()); + const onSubmit = useEffectEvent((e: FormEvent) => e.preventDefault()); useEffect(() => { const filters = { status } as { diff --git a/apps/meteor/client/views/omnichannel/realTimeMonitoring/RealTimeMonitoringPage.tsx b/apps/meteor/client/views/omnichannel/realTimeMonitoring/RealTimeMonitoringPage.tsx index 0f0650094a2b..5933825514b2 100644 --- a/apps/meteor/client/views/omnichannel/realTimeMonitoring/RealTimeMonitoringPage.tsx +++ b/apps/meteor/client/views/omnichannel/realTimeMonitoring/RealTimeMonitoringPage.tsx @@ -1,7 +1,7 @@ import type { SelectOption } from '@rocket.chat/fuselage'; import { Box, Select, Margins, Option } from '@rocket.chat/fuselage'; import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; -import type { MutableRefObject } from 'react'; +import type { Key, MutableRefObject } from 'react'; import { useRef, useState, useMemo, useEffect, Fragment } from 'react'; import { useTranslation } from 'react-i18next'; @@ -103,7 +103,7 @@ const RealTimeMonitoringPage = () => {