Skip to content

Commit

Permalink
Merge branch 'develop' into new/teams-room-creation-permissions
Browse files Browse the repository at this point in the history
  • Loading branch information
matheusbsilva137 authored Dec 26, 2023
2 parents 89b73a7 + caa7707 commit cd08f46
Show file tree
Hide file tree
Showing 18 changed files with 114 additions and 30 deletions.
5 changes: 5 additions & 0 deletions .changeset/green-turkeys-fry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@rocket.chat/meteor': patch
---

Fixed toolbox sub-menu not being displayed when in smaller resolutions
5 changes: 5 additions & 0 deletions .changeset/late-pots-travel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rocket.chat/meteor": minor
---

fix: Loading state for `Marketplace` related lists
5 changes: 5 additions & 0 deletions .changeset/thin-socks-brush.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rocket.chat/meteor": patch
---

Removed an old behavior that allowed visitors to be created with an empty token on `livechat/visitor` endpoint.
6 changes: 2 additions & 4 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,13 @@
-->

## Proposed changes (including videos or screenshots)
<!-- CHANGELOG -->
<!--
Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request.
If it fixes a bug or resolves a feature request, be sure to link to that issue below.
This description will appear in the release notes if we accept the contribution.
This description won't be displayed to our end users in the release notes, so feel free to add as much technical context as needed.
If the changes introduced in this pull request must be presented in the release notes, make sure to add a changeset file. Check our guidelines for adding a changeset to your pull request: https://developer.rocket.chat/contribute-to-rocket.chat/modes-of-contribution/participate-in-rocket.chat-development/development-workflow#4.-adding-changeset-to-your-pull-request
-->

<!-- END CHANGELOG -->

## Issue(s)
<!-- Link the issues being closed by or related to this PR. For example, you can use #594 if this PR closes issue number 594 -->

Expand Down
5 changes: 5 additions & 0 deletions apps/meteor/app/livechat/server/api/v1/visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ API.v1.addRoute('livechat/visitor', {
});

const { customFields, id, token, name, email, department, phone, username, connectionData } = this.bodyParams.visitor;

if (!token?.trim()) {
throw new Meteor.Error('error-invalid-token', 'Token cannot be empty', { method: 'livechat/visitor' });
}

const guest = {
token,
...(id && { id }),
Expand Down
9 changes: 3 additions & 6 deletions apps/meteor/client/contexts/AppsContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,24 @@ import { AsyncStatePhase } from '../lib/asyncState';
import type { App } from '../views/marketplace/types';

export type AppsContextValue = {
installedApps: AsyncState<{ apps: App[] }>;
marketplaceApps: AsyncState<{ apps: App[] }>;
privateApps: AsyncState<{ apps: App[] }>;
installedApps: Omit<AsyncState<{ apps: App[] }>, 'error'>;
marketplaceApps: Omit<AsyncState<{ apps: App[] }>, 'error'>;
privateApps: Omit<AsyncState<{ apps: App[] }>, 'error'>;
reload: () => Promise<void>;
};

export const AppsContext = createContext<AppsContextValue>({
installedApps: {
phase: AsyncStatePhase.LOADING,
value: undefined,
error: undefined,
},
marketplaceApps: {
phase: AsyncStatePhase.LOADING,
value: undefined,
error: undefined,
},
privateApps: {
phase: AsyncStatePhase.LOADING,
value: undefined,
error: undefined,
},
reload: () => Promise.resolve(),
});
23 changes: 20 additions & 3 deletions apps/meteor/client/providers/AppsProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,26 @@ import { AppClientOrchestratorInstance } from '../../ee/client/apps/orchestrator
import { AppsContext } from '../contexts/AppsContext';
import { useIsEnterprise } from '../hooks/useIsEnterprise';
import { useInvalidateLicense } from '../hooks/useLicense';
import type { AsyncState } from '../lib/asyncState';
import { AsyncStatePhase } from '../lib/asyncState';
import { useInvalidateAppsCountQueryCallback } from '../views/marketplace/hooks/useAppsCountQuery';
import type { App } from '../views/marketplace/types';

const sortByName = (apps: App[]): App[] => apps.sort((a, b) => (a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1));

const getAppState = (
loading: boolean,
apps: App[] | undefined,
): Omit<
AsyncState<{
apps: App[];
}>,
'error'
> => ({
phase: loading ? AsyncStatePhase.LOADING : AsyncStatePhase.RESOLVED,
value: { apps: apps || [] },
});

const AppsProvider: FC = ({ children }) => {
const isAdminUser = usePermission('manage-apps');

Expand Down Expand Up @@ -129,13 +143,16 @@ const AppsProvider: FC = ({ children }) => {
},
);

const [marketplaceAppsData, installedAppsData, privateAppsData] = store.data || [];
const { isLoading } = store;

return (
<AppsContext.Provider
children={children}
value={{
installedApps: { phase: AsyncStatePhase.RESOLVED, value: { apps: store.data?.[1] || [] } },
marketplaceApps: { phase: AsyncStatePhase.RESOLVED, value: { apps: store.data?.[0] || [] } },
privateApps: { phase: AsyncStatePhase.RESOLVED, value: { apps: store.data?.[2] || [] } },
installedApps: getAppState(isLoading, installedAppsData),
marketplaceApps: getAppState(isLoading, marketplaceAppsData),
privateApps: getAppState(isLoading, privateAppsData),
reload: async () => {
await Promise.all([queryClient.invalidateQueries(['marketplace'])]);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,16 @@ const AppsPageContent = (): ReactElement => {
context,
});

const noInstalledApps = appsResult.phase === AsyncStatePhase.RESOLVED && !isMarketplace && appsResult.value.totalAppsLength === 0;
const noInstalledApps = appsResult.phase === AsyncStatePhase.RESOLVED && !isMarketplace && appsResult.value?.totalAppsLength === 0;

const noMarketplaceOrInstalledAppMatches =
appsResult.phase === AsyncStatePhase.RESOLVED && (isMarketplace || isPremium) && appsResult.value.count === 0;
appsResult.phase === AsyncStatePhase.RESOLVED && (isMarketplace || isPremium) && appsResult.value?.count === 0;

const noInstalledAppMatches =
appsResult.phase === AsyncStatePhase.RESOLVED &&
context === 'installed' &&
appsResult.value.totalAppsLength !== 0 &&
appsResult.value.count === 0;
appsResult.value?.totalAppsLength !== 0 &&
appsResult.value?.count === 0;

const noAppRequests = context === 'requested' && appsResult?.value?.count === 0;

Expand Down Expand Up @@ -194,13 +194,13 @@ const AppsPageContent = (): ReactElement => {
}

if (noMarketplaceOrInstalledAppMatches) {
return <NoMarketplaceOrInstalledAppMatchesEmptyState shouldShowSearchText={appsResult.value.shouldShowSearchText} text={text} />;
return <NoMarketplaceOrInstalledAppMatchesEmptyState shouldShowSearchText={!!appsResult.value?.shouldShowSearchText} text={text} />;
}

if (noInstalledAppMatches) {
return (
<NoInstalledAppMatchesEmptyState
shouldShowSearchText={appsResult.value.shouldShowSearchText}
shouldShowSearchText={!!appsResult.value?.shouldShowSearchText}
text={text}
onButtonClick={handleReturn}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import FeaturedAppsSections from './FeaturedAppsSections';
type AppsPageContentBodyProps = {
isMarketplace: boolean;
isFiltered: boolean;
appsResult: { items: App[] } & { shouldShowSearchText: boolean } & PaginatedResult & { allApps: App[] } & { totalAppsLength: number };
appsResult?: { items: App[] } & { shouldShowSearchText: boolean } & PaginatedResult & { allApps: App[] } & { totalAppsLength: number };
itemsPerPage: 25 | 50 | 100;
current: number;
onSetItemsPerPage: React.Dispatch<React.SetStateAction<25 | 50 | 100>>;
Expand Down Expand Up @@ -43,8 +43,8 @@ const AppsPageContentBody = ({
<Box display='flex' flexDirection='column' overflow='hidden' height='100%' pi={24}>
{noErrorsOcurred && (
<Box overflowY='scroll' height='100%' ref={scrollableRef}>
{isMarketplace && !isFiltered && <FeaturedAppsSections appsListId={appsListId} appsResult={appsResult.allApps || []} />}
<AppsList appsListId={appsListId} apps={appsResult.items || []} title={isMarketplace ? t('All_Apps') : undefined} />
{isMarketplace && !isFiltered && <FeaturedAppsSections appsListId={appsListId} appsResult={appsResult?.allApps || []} />}
<AppsList appsListId={appsListId} apps={appsResult?.items || []} title={isMarketplace ? t('All_Apps') : undefined} />
</Box>
)}
</Box>
Expand Down
5 changes: 3 additions & 2 deletions apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ export const useFilteredApps = ({
sortingMethod: string;
status: string;
context?: string;
}): AsyncState<
{ items: App[] } & { shouldShowSearchText: boolean } & PaginatedResult & { allApps: App[] } & { totalAppsLength: number }
}): Omit<
AsyncState<{ items: App[] } & { shouldShowSearchText: boolean } & PaginatedResult & { allApps: App[] } & { totalAppsLength: number }>,
'error'
> => {
const value = useMemo(() => {
if (appsData.value === undefined) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { States, StatesIcon, StatesTitle, StatesSubtitle, StatesActions, StatesAction, StatesLink, Box } from '@rocket.chat/fuselage';
import { useRole, useRouter, useTranslation } from '@rocket.chat/ui-contexts';
import React from 'react';

import { Page, PageHeader, PageContent } from '../../../components/Page';

const BusinessHoursDisabledPage = () => {
const t = useTranslation();
const router = useRouter();
const isAdmin = useRole('admin');

return (
<Page>
<PageHeader title={t('Business_Hours')} />
<PageContent>
<Box display='flex' justifyContent='center' height='100%'>
<States>
<StatesIcon name='clock' />
<StatesTitle>{t('Business_hours_is_disabled')}</StatesTitle>
<StatesSubtitle>{t('Business_hours_is_disabled_description')}</StatesSubtitle>
{isAdmin && (
<StatesActions>
<StatesAction onClick={() => router.navigate('/admin/settings/Omnichannel')}>{t('Enable_business_hours')}</StatesAction>
</StatesActions>
)}
<StatesLink target='_blank' href='https://go.rocket.chat/omnichannel-docs'>
{t('Learn_more_about_business_hours')}
</StatesLink>
</States>
</Box>
</PageContent>
</Page>
);
};

export default BusinessHoursDisabledPage;
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
import { LivechatBusinessHourTypes } from '@rocket.chat/core-typings';
import { useRouteParameter, useRouter } from '@rocket.chat/ui-contexts';
import { useRouteParameter, useRouter, useSetting } from '@rocket.chat/ui-contexts';
import React, { useEffect } from 'react';

import BusinessHoursDisabledPage from './BusinessHoursDisabledPage';
import BusinessHoursMultiplePage from './BusinessHoursMultiplePage';
import EditBusinessHours from './EditBusinessHours';
import EditBusinessHoursWithData from './EditBusinessHoursWithData';
import { useIsSingleBusinessHours } from './useIsSingleBusinessHours';

const BusinessHoursRouter = () => {
const router = useRouter();
const context = useRouteParameter('context');
const id = useRouteParameter('id');
const type = useRouteParameter('type') as LivechatBusinessHourTypes;
const businessHoursEnabled = useSetting('Livechat_enable_business_hours');
const isSingleBH = useIsSingleBusinessHours();

const router = useRouter();

useEffect(() => {
if (isSingleBH) {
router.navigate('/omnichannel/businessHours/edit/default');
}
}, [isSingleBH, router, context, type]);

if (!businessHoursEnabled) {
return <BusinessHoursDisabledPage />;
}

if (context === 'edit' || isSingleBH) {
return type ? <EditBusinessHoursWithData type={type} id={id} /> : null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ const CurrentChatsPage = ({ id, onRowClick }: { id?: string; onRowClick: (_id: s
const { _id, fname, servedBy, ts, lm, department, open, onHold, priorityWeight } = room;
const getStatusText = (open: boolean, onHold: boolean): string => {
if (!open) return t('Closed');
return onHold ? t('On_Hold_Chats') : t('Open');
return onHold ? t('On_Hold_Chats') : t('Room_Status_Open');
};

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const FilterByText: FilterByTextType = ({ setFilter, reload, customFields, setCu
const statusOptions: [string, string][] = [
['all', t('All')],
['closed', t('Closed')],
['opened', t('Open')],
['opened', t('Room_Status_Open')],
['onhold', t('On_Hold_Chats')],
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ const RoomToolbox = ({ className }: RoomToolboxProps) => {
{featuredActions.map(mapToToolboxItem)}
{featuredActions.length > 0 && <HeaderToolboxDivider />}
{visibleActions.map(mapToToolboxItem)}
{(normalActions.length > 6 || roomToolboxExpanded) && (
{(normalActions.length > 6 || !roomToolboxExpanded) && (
<GenericMenu title={t('Options')} data-qa-id='ToolBox-Menu' sections={hiddenActions} placement='bottom-end' />
)}
</>
Expand Down
5 changes: 5 additions & 0 deletions apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"This_room_encryption_has_been_disabled_by__username_": "This room's encryption has been disabled by {{username}}",
"Third_party_login": "Third-party login",
"Enabled_E2E_Encryption_for_this_room": "enabled E2E Encryption for this room",
"Enable_business_hours": "Enable business hours",
"disabled": "disabled",
"Disabled_E2E_Encryption_for_this_room": "disabled E2E Encryption for this room",
"@username": "@username",
Expand Down Expand Up @@ -847,6 +848,8 @@
"Business_Hour_Removed": "Business Hour Removed",
"Business_Hours": "Business Hours",
"Business_hours_enabled": "Business hours enabled",
"Business_hours_is_disabled": "Business hours is disabled",
"Business_hours_is_disabled_description": "Enable business hours at the workspace admin panel to let customers know when you're available and when can they expect a response.",
"Business_hours_updated": "Business hours updated",
"busy": "busy",
"Busy": "Busy",
Expand Down Expand Up @@ -3055,6 +3058,7 @@
"Lead_capture_phone_regex": "Lead capture phone regex",
"Learn_more": "Learn more",
"Learn_more_about_agents": "Learn more about agents",
"Learn_more_about_business_hours": "Learn more about business hours",
"Learn_more_about_canned_responses": "Learn more about canned responses",
"Learn_more_about_contacts": "Learn more about contacts",
"Learn_more_about_current_chats": "Learn more about current chats",
Expand Down Expand Up @@ -4463,6 +4467,7 @@
"Room_password_changed_successfully": "Room password changed successfully",
"room_removed_read_only": "Room added writing permission by {{user_by}}",
"room_set_read_only": "Room set as Read Only by {{user_by}}",
"Room_Status_Open": "Open",
"room_removed_read_only_permission": "removed read only permission",
"room_set_read_only_permission": "set room to read only",
"Room_topic_changed_successfully": "Room topic changed successfully",
Expand Down
1 change: 1 addition & 0 deletions apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -3690,6 +3690,7 @@
"Room_password_changed_successfully": "A senha da sala foi alterada com sucesso",
"room_removed_read_only": "Permissão de escrita adicionada à sala por {{user_by}}",
"room_set_read_only": "Sala definida como somente leitura por {{user_by}}",
"Room_Status_Open": "Aberto",
"Room_topic_changed_successfully": "Tópico da sala alterado com sucesso",
"Room_type_changed_successfully": "Tipo de sala alterado com sucesso",
"Room_type_of_default_rooms_cant_be_changed": "Esta é uma sala padrão e o tipo não pode ser alterado; consulte o seu administrador.",
Expand Down
4 changes: 4 additions & 0 deletions apps/meteor/tests/end-to-end/api/livechat/09-visitors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ describe('LIVECHAT - visitors', function () {
const { body } = await request.post(api('livechat/visitor')).send({ visitor: {} });
expect(body).to.have.property('success', false);
});
it('should fail when token is an empty string', async () => {
const { body } = await request.post(api('livechat/visitor')).send({ visitor: { token: '' } });
expect(body).to.have.property('success', false);
});
it('should create a visitor', async () => {
const { body } = await request.post(api('livechat/visitor')).send({ visitor: { token: 'test' } });
expect(body).to.have.property('success', true);
Expand Down

0 comments on commit cd08f46

Please sign in to comment.