Skip to content

Commit

Permalink
[Workplace Search] Migrate Source containers and routers (#83954)
Browse files Browse the repository at this point in the history
* Initial copy/paste of components

No changes other than linting and the removal of the underscore.toSentenceSerial method in favor of the following:

new Intl.ListFormat().format(groups)

which does the same thing

* Redo groups sentence with more accepted solution

Turns out the previos commit’s solution is not widely accepted:

https://caniuse.com/?search=ListFormat

* Add placeholders for future components

Schema and DisplaySettings are Custom Source components that will be added later. These are stubs so the containers will render.

* Update component paths

* Remove AppView, SidebarNavigation and FlashMessages

Sidebar copy and breadcrumbs will be recreated at the top level in a separate PR

* Use Kibana’s hasPlatinumLicense over minimumPlatinumLicense

Also renames fpAccount to account, as it’s called in Kibana

* Fix typings

* Move sidebar content temporarily to top of page

This is temporary as noted by comments

* Fix missing images

* Fix routes

Had the base route and not the status route in the fetchSourceStatuses method

* Clean up last Sidebar with temp copy placement

This is so that CI will pass and we can get this merged in and do the actual wiring up of everything in a separate PR.

* Remove unused links

For CI to pass. Will add back in future PR

* Minify SVG illustration

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
scottybollinger and kibanamachine authored Nov 23, 2020
1 parent ad8ea02 commit c45fe18
Show file tree
Hide file tree
Showing 14 changed files with 697 additions and 6 deletions.
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

0 comments on commit c45fe18

Please sign in to comment.