0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-17 23:44:39 -05:00

Added Recommendations settings design (#17871)

refs https://github.com/TryGhost/Product/issues/3773, https://github.com/TryGhost/Product/issues/3791

- Added static design for Recommendation list
- Added static design for "Add recommendation" flow
- Added static design for "Remove recommendation" flow
- Updated `Table` component with Hint and Separator
This commit is contained in:
Djordje Vlaisavljevic 2023-08-30 12:54:26 +01:00 committed by GitHub
parent ee8bb2a724
commit 00911326d1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 156 additions and 16 deletions

View file

@ -1,5 +1,7 @@
import Heading from './Heading';
import Hint from './Hint';
import React from 'react';
import Separator from './Separator';
import clsx from 'clsx';
interface TableProps {
@ -9,10 +11,12 @@ interface TableProps {
pageTitle?: string;
children?: React.ReactNode;
borderTop?: boolean;
hint?: string;
hintSeparator?: boolean;
className?: string;
}
const Table: React.FC<TableProps> = ({children, borderTop, pageTitle, className}) => {
const Table: React.FC<TableProps> = ({children, borderTop, hint, hintSeparator, pageTitle, className}) => {
const tableClasses = clsx(
(borderTop || pageTitle) && 'border-t border-grey-300',
'w-full',
@ -22,12 +26,27 @@ const Table: React.FC<TableProps> = ({children, borderTop, pageTitle, className}
return (
<>
{pageTitle && <Heading>{pageTitle}</Heading>}
<table className={tableClasses}>
<tbody>
{children}
</tbody>
</table>
<div>
{pageTitle && <Heading>{pageTitle}</Heading>}
<table className={tableClasses}>
<tbody>
{children}
</tbody>
</table>
{hint &&
<div className='mt-1'>
{hintSeparator && <Separator />}
<div className="flex justify-between">
<Hint>{hint}</Hint>
{/* // TODO: Finish pagination component */}
{/* <div className={`mt-1 flex items-center gap-2 text-xs text-grey-700`}>Showing 1-5 of 15
<button type='button'><Icon colorClass="text-green" name='chevron-left' size="xs" />
</button>
<button type="button"><Icon colorClass="text-green" name='chevron-right' size="xs" /></button>
</div> */}
</div>
</div>}
</div>
</>
);
};

View file

@ -1,5 +1,7 @@
import AddIntegrationModal from '../settings/advanced/integrations/AddIntegrationModal';
import AddNewsletterModal from '../settings/email/newsletters/AddNewsletterModal';
import AddRecommendationModal from '../settings/site/recommendations/AddRecommendationModal';
import AddRecommendationModalConfirm from '../settings/site/recommendations/AddRecommendationModalConfirm';
import AmpModal from '../settings/advanced/integrations/AmpModal';
import ChangeThemeModal from '../settings/site/ThemeModal';
import DesignModal from '../settings/site/DesignModal';
@ -123,6 +125,10 @@ const handleNavigation = (scroll: boolean = true) => {
NiceModal.show(PinturaModal);
} else if (pathName === 'integrations/add') {
NiceModal.show(AddIntegrationModal);
} else if (pathName === 'recommendations/add') {
NiceModal.show(AddRecommendationModal);
} else if (pathName === 'recommendations/add-confirm') {
NiceModal.show(AddRecommendationModalConfirm);
}
if (scroll) {

View file

@ -3,6 +3,7 @@ import React, {useState} from 'react';
import RecommendationList from './recommendations/RecommendationList';
import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
import TabView from '../../../admin-x-ds/global/TabView';
import useRouting from '../../../hooks/useRouting';
import useSettingGroup from '../../../hooks/useSettingGroup';
import {useBrowseRecommendations} from '../../../api/recommendations';
@ -13,9 +14,16 @@ const Recommendations: React.FC<{ keywords: string[] }> = ({keywords}) => {
} = useSettingGroup();
const {data: {recommendations} = {}} = useBrowseRecommendations();
const [selectedTab, setSelectedTab] = useState('your-recommendations');
const {updateRoute} = useRouting();
const openAddNewRecommendationModal = () => {
updateRoute('recommendations/add');
};
const buttons = (
<Button color='green' label='Add recommendation' link={true} onClick={() => {}} />
<Button color='green' label='Add recommendation' link={true} onClick={() => {
openAddNewRecommendationModal();
}} />
);
const tabs = [
@ -30,11 +38,11 @@ const Recommendations: React.FC<{ keywords: string[] }> = ({keywords}) => {
contents: (<RecommendationList recommendations={[]} />)
}
];
return (
<SettingGroup
customButtons={buttons}
description="Recommend sites to your audience, and get recommended by others."
description="Share favorite sites with your audience"
keywords={keywords}
navid='recommendations'
saveState={saveState}
@ -47,4 +55,4 @@ const Recommendations: React.FC<{ keywords: string[] }> = ({keywords}) => {
);
};
export default Recommendations;
export default Recommendations;

View file

@ -0,0 +1,43 @@
import Form from '../../../../admin-x-ds/global/form/Form';
import Modal from '../../../../admin-x-ds/global/modal/Modal';
import NiceModal from '@ebay/nice-modal-react';
import React from 'react';
import URLTextField from '../../../../admin-x-ds/global/form/URLTextField';
import useRouting from '../../../../hooks/useRouting';
interface AddRecommendationModalProps {}
const AddRecommendationModal: React.FC<AddRecommendationModalProps> = () => {
const {updateRoute} = useRouting();
const openAddNewRecommendationModal = () => {
updateRoute('recommendations/add-confirm');
};
// TODO: Add error message
return <Modal
afterClose={() => {
updateRoute('recommendations');
}}
okColor='black'
okLabel='Next'
size='sm'
testId='add-recommendation-modal'
title='Add recommendation'
>
<Form
marginBottom={false}
marginTop
>
<URLTextField
baseUrl=''
hint={<>Need inspiration? <a className='text-green' href="https://www.ghost.org/explore" rel="noopener noreferrer" target='_blank'>Explore thousands of sites</a> to recommend</>}
placeholder='https://www.example.com'
title='URL'
onChange={openAddNewRecommendationModal}
/>
</Form>
</Modal>;
};
export default NiceModal.create(AddRecommendationModal);

View file

@ -0,0 +1,45 @@
import Avatar from '../../../../admin-x-ds/global/Avatar';
import Form from '../../../../admin-x-ds/global/form/Form';
import Modal from '../../../../admin-x-ds/global/modal/Modal';
import NiceModal from '@ebay/nice-modal-react';
import React from 'react';
import TextArea from '../../../../admin-x-ds/global/form/TextArea';
import useRouting from '../../../../hooks/useRouting';
interface AddRecommendationModalProps {}
const AddRecommendationModal: React.FC<AddRecommendationModalProps> = () => {
const {updateRoute} = useRouting();
return <Modal
afterClose={() => {
updateRoute('recommendations');
}}
cancelLabel='Back'
okColor='black'
okLabel='Add'
size='sm'
testId='add-recommendation-modal'
title='Add recommendation'
>
<Form
marginBottom={false}
marginTop
>
<div className='mb-4 flex items-center gap-3 rounded-sm border border-grey-300 p-3'>
<Avatar image='https://www.shesabeast.co/content/images/size/w256h256/2022/08/transparent-icon-black-copy-gray-bar.png' labelColor='white' />
<div className={`flex grow flex-col`}>
<span className='mb-0.5 font-medium'>Shes A Beast</span>
<span className='text-xs leading-snug text-grey-700'>shesabeast.co</span>
</div>
</div>
<TextArea
clearBg={true}
rows={3}
title="Reason for recommending"
/>
</Form>
</Modal>;
};
export default NiceModal.create(AddRecommendationModal);

View file

@ -1,4 +1,7 @@
import Avatar from '../../../../admin-x-ds/global/Avatar';
import Button from '../../../../admin-x-ds/global/Button';
import ConfirmationModal from '../../../../admin-x-ds/global/modal/ConfirmationModal';
import NiceModal from '@ebay/nice-modal-react';
import NoValueLabel from '../../../../admin-x-ds/global/NoValueLabel';
import React from 'react';
import Table from '../../../../admin-x-ds/global/Table';
@ -11,16 +14,32 @@ interface RecommendationListProps {
}
const RecommendationItem: React.FC<{recommendation: Recommendation}> = ({recommendation}) => {
const action = <Button color='green' label='Delete' link onClick={() => {}} />;
const action = (
<Button color='red' label='Remove' link onClick={() => {
NiceModal.show(ConfirmationModal, {
title: 'Remove recommendation',
prompt: <>
<p>Your recommendation <strong>Lenny Nesletter</strong> will no longer be visible to your audience.</p>
</>,
okLabel: 'Remove',
onOk: async (modal) => {
modal?.remove();
}
});
}} />
);
const showDetails = () => {};
return (
<TableRow action={action} hideActions>
<TableCell onClick={showDetails}>
<div className={`flex grow flex-col`}>
<span className='font-medium'>{recommendation.title}</span>
<span className='whitespace-nowrap text-xs text-grey-700'>{recommendation.reason || 'No description'}</span>
<div className='group flex items-center gap-3 hover:cursor-pointer'>
<Avatar image='https://substackcdn.com/image/fetch/w_96,h_96,c_fill,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f2b2ad-681f-45e1-9496-db80f45e853d_403x403.png' labelColor='white' />
<div className={`flex grow flex-col`}>
<span className='mb-0.5 font-medium'>{recommendation.title}</span>
<span className='text-xs leading-snug text-grey-700'>{recommendation.reason || 'No reason'}</span>
</div>
</div>
</TableCell>
</TableRow>
@ -34,7 +53,7 @@ const RecommendationList: React.FC<RecommendationListProps> = ({recommendations}
</Table>;
} else {
return <NoValueLabel icon='mail-block'>
No recommendations found.
No recommendations yet.
</NoValueLabel>;
}
};