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

[Workplace Search] Migrate Source containers and routers #83954

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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
EuiTitle,
} from '@elastic/eui';

import connectionIllustration from 'workplace_search/components/assets/connectionIllustration.svg';
import connectionIllustration from '../../../../assets/connection_illustration.svg';

interface ConfigurationIntroProps {
header: React.ReactNode;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React from 'react';

export const DisplaySettingsRouter: React.FC = () => <>Display Settings Placeholder</>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export { DisplaySettingsRouter } from './display_settings_router';
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export { Schema } from './schema';
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React from 'react';

export const Schema: React.FC = () => <>Schema Placeholder</>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React from 'react';

export const SchemaChangeErrors: React.FC = () => <>Schema Errors Placeholder</>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export { Overview } from './components/overview';
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React, { useEffect } from 'react';

import { useActions, useValues } from 'kea';
import { Link, Redirect } from 'react-router-dom';

import { EuiButton } from '@elastic/eui';
import { ADD_SOURCE_PATH, getSourcesPath } from '../../routes';

import { Loading } from '../../../shared/loading';
import { ContentSection } from '../../components/shared/content_section';
import { SourcesTable } from '../../components/shared/sources_table';
import { ViewContentHeader } from '../../components/shared/view_content_header';

import { SourcesLogic } from './sources_logic';

import { SourcesView } from './sources_view';

const ORG_LINK_TITLE = 'Add an organization content source';
const ORG_PAGE_TITLE = 'Manage organization content sources';
const ORG_PAGE_DESCRIPTION =
'Organization sources are available to the entire organization and can be shared to specific user groups. By default, newly created organization sources are added to the Default group.';
const ORG_HEADER_TITLE = 'Organization sources';
const ORG_HEADER_DESCRIPTION =
'Organization sources are available to the entire organization and can be assigned to specific user groups.';

export const OrganizationSources: React.FC = () => {
const { initializeSources, setSourceSearchability } = useActions(SourcesLogic);

useEffect(() => {
initializeSources();
}, []);

const { dataLoading, contentSources } = useValues(SourcesLogic);

if (dataLoading) return <Loading />;

if (contentSources.length === 0) return <Redirect to={getSourcesPath(ADD_SOURCE_PATH, true)} />;

const linkTitle = ORG_LINK_TITLE;
const headerTitle = ORG_HEADER_TITLE;
const headerDescription = ORG_HEADER_DESCRIPTION;
const sectionTitle = '';
const sectionDescription = '';

return (
<SourcesView>
{/* TODO: Figure out with design how to make this look better w/o 2 ViewContentHeaders */}
<ViewContentHeader title={ORG_PAGE_TITLE} description={ORG_PAGE_DESCRIPTION} />
<ViewContentHeader
title={headerTitle}
action={
<Link to={getSourcesPath(ADD_SOURCE_PATH, true)}>
<EuiButton fill color="primary" data-test-subj="AddSourceButton">
{linkTitle}
</EuiButton>
</Link>
}
description={headerDescription}
alignItems="flexStart"
/>

<ContentSection title={sectionTitle} description={sectionDescription}>
<SourcesTable
showDetails
isOrganization
onSearchableToggle={setSourceSearchability}
sources={contentSources}
/>
</ContentSection>
</SourcesView>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React, { useEffect } from 'react';

import { useActions, useValues } from 'kea';
import { Link } from 'react-router-dom';

import { EuiButton, EuiCallOut, EuiEmptyPrompt, EuiSpacer, EuiPanel } from '@elastic/eui';

import { LicensingLogic } from '../../../../applications/shared/licensing';

import { ADD_SOURCE_PATH } from '../../routes';

import noSharedSourcesIcon from '../../assets/share_circle.svg';

import { Loading } from '../../../shared/loading';
import { ContentSection } from '../../components/shared/content_section';
import { SourcesTable } from '../../components/shared/sources_table';
import { ViewContentHeader } from '../../components/shared/view_content_header';

import { AppLogic } from '../../app_logic';
import { SourcesView } from './sources_view';
import { SourcesLogic } from './sources_logic';

// TODO: Remove this after links in Kibana sidenav
interface SidebarLink {
title: string;
path?: string;
disabled?: boolean;
iconType?: string;
otherActivePath?: string;
dataTestSubj?: string;
onClick?(): void;
}

const PRIVATE_LINK_TITLE = 'Add a private content source';
const PRIVATE_CAN_CREATE_PAGE_TITLE = 'Manage private content sources';
const PRIVATE_VIEW_ONLY_PAGE_TITLE = 'Review Group Sources';
const PRIVATE_VIEW_ONLY_PAGE_DESCRIPTION =
'Review the status of all sources shared with your Group.';
const PRIVATE_CAN_CREATE_PAGE_DESCRIPTION =
'Review the status of all connected private sources, and manage private sources for your account.';
const PRIVATE_HEADER_TITLE = 'My private content sources';
const PRIVATE_HEADER_DESCRIPTION = 'Private content sources are available only to you.';
const PRIVATE_SHARED_SOURCES_TITLE = 'Shared content sources';

export const PrivateSources: React.FC = () => {
const { hasPlatinumLicense } = useValues(LicensingLogic);
const { initializeSources, setSourceSearchability, resetSourcesState } = useActions(SourcesLogic);

useEffect(() => {
initializeSources();
return resetSourcesState;
}, []);

const { dataLoading, contentSources, serviceTypes, privateContentSources } = useValues(
SourcesLogic
);

const {
account: { canCreatePersonalSources, groups },
} = useValues(AppLogic);

if (dataLoading) return <Loading />;

const sidebarLinks = [] as SidebarLink[];
const hasConfiguredConnectors = serviceTypes.some(({ configured }) => configured);
const canAddSources = canCreatePersonalSources && hasConfiguredConnectors;
if (canAddSources) {
sidebarLinks.push({
title: PRIVATE_LINK_TITLE,
iconType: 'plusInCircle',
path: ADD_SOURCE_PATH,
});
}

const headerAction = (
<Link to={ADD_SOURCE_PATH}>
<EuiButton fill color="primary" data-test-subj="AddSourceButton">
{PRIVATE_LINK_TITLE}
</EuiButton>
</Link>
);

const sourcesHeader = (
<ViewContentHeader
title={PRIVATE_HEADER_TITLE}
action={headerAction}
description={PRIVATE_HEADER_DESCRIPTION}
alignItems="flexStart"
/>
);

const privateSourcesTable = (
<ContentSection>
<SourcesTable
showDetails={true}
onSearchableToggle={setSourceSearchability}
sources={privateContentSources}
/>
</ContentSection>
);

const privateSourcesEmptyState = (
<ContentSection className="zero-state__private-sources">
<EuiPanel className="euiPanel--inset">
<EuiSpacer size="xxl" />
<EuiEmptyPrompt
iconType="lock"
title={<h2>You have no private sources</h2>}
body={
<p>
Select from the content sources below to create a private source, available only to
you
</p>
}
/>
<EuiSpacer size="xxl" />
</EuiPanel>
</ContentSection>
);

const sharedSourcesEmptyState = (
<ContentSection className="zero-state__private-sources">
<EuiPanel className="euiPanel--inset">
<EuiSpacer size="xxl" />
<EuiEmptyPrompt
iconType={noSharedSourcesIcon}
title={<h2>No content source available</h2>}
body={
<p>
Once content sources are shared with you, they will be displayed here, and available
via the search experience.
</p>
}
/>
<EuiSpacer size="xxl" />
</EuiPanel>
</ContentSection>
);

const hasPrivateSources = privateContentSources?.length > 0;
const privateSources = hasPrivateSources ? privateSourcesTable : privateSourcesEmptyState;

const groupsSentence = `${groups.slice(0, groups.length - 1).join(', ')}, and ${groups.slice(
-1
)}`;

const sharedSources = (
<ContentSection
title={PRIVATE_SHARED_SOURCES_TITLE}
description={`You have access to the following sources through the group${
groups.length === 1 ? '' : 's'
} ${groupsSentence}.`}
>
<SourcesTable showDetails={false} isOrganization={false} sources={contentSources} />
</ContentSection>
);

const licenseCallout = (
<>
<EuiCallOut title="Private Sources are no longer available" iconType="iInCircle">
<p>Contact your search experience administrator for more information.</p>
</EuiCallOut>
<EuiSpacer />
</>
);

const PAGE_TITLE = canCreatePersonalSources
? PRIVATE_CAN_CREATE_PAGE_TITLE
: PRIVATE_VIEW_ONLY_PAGE_TITLE;
const PAGE_DESCRIPTION = canCreatePersonalSources
? PRIVATE_CAN_CREATE_PAGE_DESCRIPTION
: PRIVATE_VIEW_ONLY_PAGE_DESCRIPTION;

const pageHeader = <ViewContentHeader title={PAGE_TITLE} description={PAGE_DESCRIPTION} />;

return (
<SourcesView>
{/* TODO: Figure out with design how to make this look better w/o 2 ViewContentHeaders */}
{pageHeader}
{hasPrivateSources && !hasPlatinumLicense && licenseCallout}
{canAddSources && sourcesHeader}
{canCreatePersonalSources && privateSources}
{contentSources.length > 0 ? sharedSources : sharedSourcesEmptyState}
</SourcesView>
);
};
Loading