Admin X demo detail page (#19105)

refs. https://github.com/TryGhost/Product/issues/4169

- the detail page for the Admin X proto app was empty
- the asc/desc selector of the SortMenu component in the design system needed a bit of refinement
- page toolbar was not set
This commit is contained in:
Peter Zimon 2023-11-23 08:18:41 +01:00 committed by GitHub
parent 876f13c075
commit 7f451b2627
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 162 additions and 15 deletions

View File

@ -1,10 +1,147 @@
import {Button} from '@tryghost/admin-x-design-system';
import {Avatar, Breadcrumbs, Button, Heading, Page, Toggle, ViewContainer} from '@tryghost/admin-x-design-system';
import {useRouting} from '@tryghost/admin-x-framework/routing';
const DetailPage: React.FC = () => {
const {updateRoute} = useRouting();
return <>Detail page <Button label='Back' onClick={() => updateRoute('')} /></>;
return (
<Page
breadCrumbs={
<Breadcrumbs
items={[
{
label: 'Members',
onClick: () => {
updateRoute('');
}
},
{
label: 'Emerson Vaccaro'
}
]}
onBack={() => {
updateRoute('');
}}
/>
}
fullBleedPage={false}
>
<ViewContainer
firstOnPage={false}
headerContent={
<div>
<Avatar bgColor='#A5D5F7' image='https://i.pravatar.cc/150' label='EV' labelColor='white' size='2xl' />
<Heading className='mt-2' level={1}>Emerson Vaccaro</Heading>
<div className=''>Colombus, OH</div>
</div>
}
primaryAction={
{
icon: 'ellipsis',
color: 'outline'
}
}
type='page'
>
<div className='grid grid-cols-3 border-b border-grey-200 pb-5 tablet:grid-cols-4'>
<div className='col-span-3 -ml-5 mb-5 hidden h-full gap-4 px-5 tablet:!visible tablet:col-span-1 tablet:mb-0 tablet:!flex tablet:flex-col tablet:gap-0'>
<span>Last seen on <strong>22 June 2023</strong></span>
<span className='tablet:mt-2'>Created on <strong>27 Jan 2021</strong></span>
</div>
<div className='flex h-full flex-col tablet:px-5'>
<Heading level={6}>Emails received</Heading>
<span className='mt-1 text-4xl font-bold leading-none'>181</span>
</div>
<div className='flex h-full flex-col tablet:px-5'>
<Heading level={6}>Emails opened</Heading>
<span className='mt-1 text-4xl font-bold leading-none'>104</span>
</div>
<div className='-mr-5 flex h-full flex-col tablet:px-5'>
<Heading level={6}>Average open rate</Heading>
<span className='mt-1 text-4xl font-bold leading-none'>57%</span>
</div>
</div>
<div className='grid grid-cols-2 items-baseline border-b border-grey-200 py-5 tablet:grid-cols-4'>
<div className='-ml-5 flex h-full flex-col gap-6 border-r border-grey-200 px-5'>
<div className='flex justify-between'>
<Heading level={5}>Member data</Heading>
<Button color='green' label='Edit' link />
</div>
<div>
<Heading level={6}>Name</Heading>
<div>Emerson Vaccaro</div>
</div>
<div>
<Heading level={6}>Email</Heading>
<div>emerson@vaccaro.com</div>
</div>
<div>
<Heading level={6}>Labels</Heading>
<div className='mt-2 flex gap-1'>
<div className='inline-block rounded-sm bg-grey-200 px-1.5 text-xs font-medium'>VIP</div>
<div className='inline-block rounded-sm bg-grey-200 px-1.5 text-xs font-medium'>Inner Circle</div>
</div>
</div>
<div>
<Heading level={6}>Notes</Heading>
<div className='text-grey-500'>No notes.</div>
</div>
</div>
<div className='flex h-full flex-col gap-6 border-grey-200 px-5 tablet:border-r'>
<Heading level={5}>Newsletters</Heading>
<div className='flex flex-col gap-3'>
<div className='flex items-center gap-2'>
<Toggle />
<span>Daily news</span>
</div>
<div className='flex items-center gap-2'>
<Toggle />
<span>Weekly roundup</span>
</div>
<div className='flex items-center gap-2'>
<Toggle checked />
<span>The Inner Circle</span>
</div>
<div className='mt-5 rounded border border-red p-4 text-sm text-red'>
This member cannot receive emails due to permanent failure (bounce).
</div>
</div>
</div>
<div className='-ml-5 flex h-full flex-col gap-6 border-r border-grey-200 px-5 pt-10 tablet:ml-0 tablet:pt-0'>
<Heading level={5}>Subscriptions</Heading>
<div className='flex items-center gap-3'>
<div className='flex h-16 w-16 flex-col items-center justify-center rounded-md bg-grey-200'>
<Heading level={5}>$5</Heading>
<span className='text-xs text-grey-700'>Yearly</span>
</div>
<div className='flex flex-col'>
<span className='font-semibold'>Gold</span>
<span className='text-sm text-grey-500'>Renews 21 Jan 2024</span>
</div>
</div>
</div>
<div className='-mr-5 flex h-full flex-col gap-6 px-5 pt-10 tablet:pt-0'>
<div className='flex justify-between'>
<Heading level={5}>Activity</Heading>
<Button color='green' label='View all' link />
</div>
<div className='flex flex-col text-sm'>
<span className='font-semibold'>Logged in</span>
<span className='text-sm text-grey-500'>13 days ago</span>
</div>
<div className='flex flex-col text-sm'>
<span className='font-semibold'>Subscribed to Daily News</span>
<span className='text-sm text-grey-500'>17 days ago</span>
</div>
<div className='flex flex-col text-sm'>
<span className='font-semibold'>Logged in</span>
<span className='text-sm text-grey-500'>21 days ago</span>
</div>
</div>
</div>
</ViewContainer>
</Page>
);
};
export default DetailPage;

View File

@ -1,4 +1,5 @@
import {Avatar, Button, ButtonGroup, DynamicTable, DynamicTableColumn, DynamicTableRow, Heading, Hint, Page, SortMenu, ViewContainer} from '@tryghost/admin-x-design-system';
import {Avatar, Button, ButtonGroup, DynamicTable, DynamicTableColumn, DynamicTableRow, Heading, Hint, Page, SortMenu, Tooltip, ViewContainer, showToast} from '@tryghost/admin-x-design-system';
import {Toaster} from 'react-hot-toast';
import {useRouting} from '@tryghost/admin-x-framework/routing';
import {useState} from 'react';
@ -8,7 +9,7 @@ const ListPage = () => {
const dummyActions = [
<Button label='Filter' onClick={() => {
alert('Clicked filter');
showToast({message: 'Were you really expecting a filter? 😛'});
}} />,
<SortMenu
direction='desc'
@ -31,9 +32,11 @@ const ListPage = () => {
onDirectionChange={() => {}}
onSortChange={() => {}}
/>,
<Tooltip content="Search members">
<Button icon='magnifying-glass' size='sm' onClick={() => {
alert('Clicked search');
}} />,
}} />
</Tooltip>,
<ButtonGroup buttons={[
{
icon: 'listview',
@ -141,7 +144,7 @@ const ListPage = () => {
for (let i = 0; i < noOfCards; i++) {
cards.push(
<div className='flex min-h-[20vh] cursor-pointer flex-col items-center gap-5 rounded-sm bg-grey-100 p-7 pt-9 transition-all hover:bg-grey-200' onClick={() => {
alert('Clicked');
updateRoute('detail');
}}>
<Avatar image={`https://i.pravatar.cc/150?img=${i}`} size='xl' />
<div className='flex flex-col items-center'>
@ -221,6 +224,7 @@ const ListPage = () => {
const demoPage = (
<Page>
<Toaster />
<ViewContainer
actions={dummyActions}
primaryAction={{

View File

@ -13,6 +13,7 @@ const DemoModal = NiceModal.create(() => {
}}
cancelLabel=''
okLabel='Close'
size='sm'
title='About'
onOk={() => {
updateRoute('');

View File

@ -1,7 +1,7 @@
import React from 'react';
import {ReactComponent as UserIcon} from '../assets/icons/single-user-fill.svg';
type AvatarSize = 'sm' | 'md' | 'lg' | 'xl';
type AvatarSize = 'sm' | 'md' | 'lg' | 'xl' | '2xl';
export interface AvatarProps {
image?: string;
@ -35,6 +35,10 @@ const Avatar: React.FC<AvatarProps> = ({image, label, labelColor, bgColor, size,
avatarSize = ' w-16 h-16 text-2xl ';
fallbackPosition = ' -mb-3 ';
break;
case '2xl':
avatarSize = ' w-20 h-20 text-2xl ';
fallbackPosition = ' -mb-3 ';
break;
default:
avatarSize = ' w-10 h-10 text-md ';
break;

View File

@ -39,7 +39,7 @@ const List: React.FC<ListProps> = ({
}) => {
const listClasses = clsx(
(borderTop || pageTitle) && 'border-t border-grey-300',
pageTitle && 'mt-14',
pageTitle && 'mt-5',
className
);

View File

@ -63,9 +63,9 @@ const SortMenu: React.FC<SortMenuProps> = ({
<button key={item.id} className="group relative mx-1 flex grow cursor-pointer items-center rounded-[2.5px] px-8 py-1.5 pr-12 text-left text-sm hover:bg-grey-100 dark:hover:bg-grey-800" type="button" onClick={() => {
handleSortChange(item.id);
}}>
{item.selected ? <Icon className='absolute left-2' name='check' size='sm' /> : null}
{item.selected ? <Icon className='absolute left-2' name='check' size='xs' /> : null}
{item.label}
{item.selected ? <button className='absolute right-2 flex h-6 w-6 cursor-pointer items-center justify-center rounded-full bg-grey-200 opacity-0 group-hover:bg-grey-300 group-hover:opacity-100' title={`${localDirection === 'asc' ? 'Ascending' : 'Descending'}`} type='button' onClick={handleSortDirection}>
{item.selected ? <button className='absolute right-1 flex h-6 w-6 cursor-pointer items-center justify-center rounded-full hover:bg-grey-300' title={`${localDirection === 'asc' ? 'Ascending' : 'Descending'}`} type='button' onClick={handleSortDirection}>
{localDirection === 'asc' ? <Icon name='arrow-up' size='xs' /> : <Icon name='arrow-down' size='xs' />}
</button> : null}
</button>

View File

@ -138,7 +138,7 @@ const Page: React.FC<PageProps> = ({
);
pageToolbarClassName = clsx(
'sticky top-0 z-50 flex h-18 w-full items-center justify-between gap-5 bg-white p-6 dark:bg-black',
'sticky top-0 z-50 flex h-22 min-h-[92px] w-full items-center justify-between gap-5 bg-white p-8 dark:bg-black',
!fullBleedToolbar && 'mx-auto max-w-7xl',
pageToolbarClassName
);

View File

@ -28,7 +28,7 @@ const PageHeader: React.FC<PageHeaderProps> = ({
children
}) => {
const containerClasses = clsx(
'z-50 h-[72px] p-5 px-7',
'z-50 h-22 min-h-[92px] p-8 px-6 tablet:px-12',
!children && 'flex items-center justify-between gap-3',
sticky && 'sticky top-0',
containerClassName

View File

@ -173,7 +173,7 @@ const ViewContainer: React.FC<ViewContainerProps> = ({
toolbarWrapperClassName = clsx(
'z-50',
type === 'page' && 'mx-auto w-full max-w-7xl bg-white px-[4vw] dark:bg-black tablet:px-12',
(type === 'page' && stickyHeader) && (firstOnPage ? 'sticky top-0 pt-8' : 'sticky top-18 pt-[3vmin]'),
(type === 'page' && stickyHeader) && (firstOnPage ? 'sticky top-0 pt-8' : 'sticky top-22 pt-[3vmin]'),
toolbarContainerClassName
);

View File

@ -219,6 +219,7 @@ module.exports = {
16: '6.4rem',
18: '7.2rem',
20: '8rem',
22: '9.2rem',
24: '9.6rem',
28: '11.2rem',
32: '12.8rem',