Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add initial tenants ui #3855

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ import { IntegrationsListPage } from './pages/integrations/IntegrationsListPage'
import { CreateProviderPage } from './pages/integrations/CreateProviderPage';
import { UpdateProviderPage } from './pages/integrations/UpdateProviderPage';
import { SelectProviderPage } from './pages/integrations/components/SelectProviderPage';
import { Tenants } from './pages/tenants/Tenants';
import { TenantsPage } from './pages/tenants/TenantsPage';

library.add(far, fas);

Expand Down Expand Up @@ -201,7 +201,7 @@ function App() {
<Route path=":channel/:stepUuid" element={<TemplateEditor />} />
</Route>
<Route path={ROUTES.WORKFLOWS} element={<WorkflowListPage />} />
<Route path={ROUTES.TENANTS} element={<Tenants />} />
<Route path={ROUTES.TENANTS} element={<TenantsPage />} />
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added the page part to make sure it is straightforward.

<Route path={ROUTES.GET_STARTED} element={<GetStarted />} />
<Route path={ROUTES.GET_STARTED_PREVIEW} element={<DigestPreview />} />
<Route path={ROUTES.QUICK_START_NOTIFICATION_CENTER} element={<NotificationCenter />} />
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/api/query.keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ interface IQueryKeys {
getApiKeys: string;
getInAppActive: string;
getTemplateById: (templateId?: string) => string;
tenantsList: string;
}

export const QueryKeys: IQueryKeys = Object.freeze({
Expand All @@ -30,4 +31,5 @@ export const QueryKeys: IQueryKeys = Object.freeze({
getApiKeys: 'getApiKeys',
getInAppActive: 'inAppActive',
getTemplateById: (templateId?: string) => `notificationById:${templateId}`,
tenantsList: 'tenantsList',
});
5 changes: 5 additions & 0 deletions apps/web/src/api/tenants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { api } from './api.client';

export function getTenants() {
return api.get('/v1/tenants');
}
20 changes: 20 additions & 0 deletions apps/web/src/hooks/useTenants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { UseQueryOptions, useQuery } from '@tanstack/react-query';

import { ITenantEntity } from '@novu/shared';

import { QueryKeys } from '../api/query.keys';
import { getTenants } from '../api/tenants';

export function useTenants(options: UseQueryOptions<ITenantEntity[], any, ITenantEntity[]> = {}) {
const { data, isLoading, ...rest } = useQuery<ITenantEntity[], any, ITenantEntity[]>(
[QueryKeys.tenantsList],
getTenants,
{ refetchOnMount: false, ...options }
);

return {
tenants: data,
loading: isLoading,
...rest,
};
}
15 changes: 0 additions & 15 deletions apps/web/src/pages/tenants/Tenants.tsx

This file was deleted.

21 changes: 21 additions & 0 deletions apps/web/src/pages/tenants/TenantsPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React, { useCallback } from 'react';
import { useNavigate } from 'react-router-dom';

import PageContainer from '../../components/layout/components/PageContainer';
import PageHeader from '../../components/layout/components/PageHeader';
import { TenantsList } from './components/list/TenantsList';

export function TenantsPage() {
const navigate = useNavigate();

const onAddTenantClickCallback = useCallback(() => {
// navigate();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we will navigate hit to the side bar that Gali is working on.

}, [navigate]);

return (
<PageContainer title="Tenants">
<PageHeader title="Tenants" />
<TenantsList onAddTenantClick={onAddTenantClickCallback} />
</PageContainer>
);
}
33 changes: 30 additions & 3 deletions apps/web/src/pages/tenants/components/list/TenantsList.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
export function TenantsList() {
return null;
}
import { Container } from '@mantine/core';

import { Table } from '../../../../design-system';
import PageContainer from '../../../../components/layout/components/PageContainer';
import { When } from '../../../../components/utils/When';
import { columns } from './columns';
import { useTenants } from '../../../../hooks/useTenants';
import { Toolbar } from './ToolBar';

export const TenantsList = ({ onAddTenantClick }: { onAddTenantClick: React.MouseEventHandler<HTMLButtonElement> }) => {
const { tenants, loading } = useTenants();
const hasTenants = tenants && tenants?.length > 0;

return (
<PageContainer
style={{
position: 'relative',
overflow: 'hidden',
}}
title="Tenants"
>
<Container fluid sx={{ padding: '0 30px 8px 30px' }}>
<Toolbar onAddTenantClick={onAddTenantClick} tenantLoading={loading} />
</Container>
<When truthy={hasTenants || loading}>
<Table loading={loading} data-test-id="tenants-list-table" columns={columns} data={tenants || []} />
</When>
</PageContainer>
);
};
49 changes: 49 additions & 0 deletions apps/web/src/pages/tenants/components/list/ToolBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import styled from '@emotion/styled';
import React from 'react';

import { Button, Text } from '../../../../design-system';

const Holder = styled.div`
display: flex;
justify-content: space-between;
`;

const ButtonStyled = styled(Button)`
margin-left: -8px;
`;

const PlusSquare = styled.div`
display: flex;
align-items: center;
justify-content: center;
width: 24px;
max-width: 24px;
height: 24px;
padding: 0;
font-size: 18px;
font-weight: 700;
border-radius: 4px;
`;

export const Toolbar = ({
tenantLoading,
onAddTenantClick,
}: {
tenantLoading: boolean;
onAddTenantClick: React.MouseEventHandler<HTMLButtonElement>;
}) => {
return (
<Holder>
<ButtonStyled
id="add-tenant"
onClick={onAddTenantClick}
disabled={tenantLoading}
data-test-id="add-tenant"
variant="subtle"
>
<PlusSquare data-square>+</PlusSquare>
<Text gradient>Add a tenant</Text>
</ButtonStyled>
</Holder>
);
};
48 changes: 48 additions & 0 deletions apps/web/src/pages/tenants/components/list/columns.tsx
djabarovgeorge marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { format } from 'date-fns';

import { ITenantEntity } from '@novu/shared';

import { IExtendedColumn, Tooltip, withCellLoading, Text } from '../../../../design-system';

const maxWidth = 60;

export const columns: IExtendedColumn<ITenantEntity>[] = [
{
accessor: 'name',
Header: 'Name',
Cell: withCellLoading(({ row: { original } }) => (
<Tooltip label={original.name}>
<div>
<Text rows={1}>{original.name || ''}</Text>
</div>
</Tooltip>
)),
},
{
accessor: 'identifier',
Header: 'Tenant identifier',
Cell: withCellLoading(({ row: { original } }) => (
<Tooltip label={original.name}>
<div>
<Text rows={1}>{original.name || ''}</Text>
</div>
</Tooltip>
)),
},
{
accessor: 'createdAt',
Header: 'Created at',
maxWidth: maxWidth,
Cell: withCellLoading(({ row: { original }, isLoading }) => {
return format(new Date(original.createdAt), 'dd/MM/yyyy HH:mm');
}),
},
{
accessor: 'updatedAt',
Header: 'Updated at',
djabarovgeorge marked this conversation as resolved.
Show resolved Hide resolved
maxWidth: maxWidth,
Cell: withCellLoading(({ row: { original }, isLoading }) => {
return format(new Date(original.updatedAt), 'dd/MM/yyyy HH:mm');
}),
},
];
1 change: 1 addition & 0 deletions libs/shared/src/entities/tenant/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './tenant.interface';
19 changes: 19 additions & 0 deletions libs/shared/src/entities/tenant/tenant.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { EnvironmentId, TenantCustomData, TenantId } from '../../types';

export interface ITenantEntity {
_id?: TenantId;

identifier: string;

name?: string;

deleted?: boolean;

createdAt: string;

updatedAt: string;

data?: TenantCustomData;

_environmentId: EnvironmentId;
}
1 change: 1 addition & 0 deletions libs/shared/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export * from './entities/subscriber';
export * from './entities/layout';
export * from './entities/notification-group';
export * from './entities/integration';
export * from './entities/tenant';
export * from './types';
export * from './dto';
export * from './consts';
Expand Down
2 changes: 2 additions & 0 deletions libs/shared/src/types/tenant/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { CustomDataType } from '../shared';

export type TenantCustomData = CustomDataType;

export type TenantId = string;