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:
parent
ee8bb2a724
commit
00911326d1
6 changed files with 156 additions and 16 deletions
|
@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
|
@ -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);
|
|
@ -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'>She‘s 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);
|
|
@ -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>;
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue