mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-20 22:42:53 -05:00
Added detail page routing to AdminX demo app (#19101)
refs https://github.com/TryGhost/Product/issues/4183
This commit is contained in:
parent
ec332520eb
commit
9848469568
3 changed files with 261 additions and 236 deletions
10
apps/admin-x-demo/src/DetailPage.tsx
Normal file
10
apps/admin-x-demo/src/DetailPage.tsx
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import {Button} 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('')} /></>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DetailPage;
|
244
apps/admin-x-demo/src/ListPage.tsx
Normal file
244
apps/admin-x-demo/src/ListPage.tsx
Normal file
|
@ -0,0 +1,244 @@
|
||||||
|
import {Avatar, Button, ButtonGroup, DynamicTable, DynamicTableColumn, DynamicTableRow, Heading, Hint, Page, SortMenu, ViewContainer} from '@tryghost/admin-x-design-system';
|
||||||
|
import {useRouting} from '@tryghost/admin-x-framework/routing';
|
||||||
|
import {useState} from 'react';
|
||||||
|
|
||||||
|
const ListPage = () => {
|
||||||
|
const {updateRoute} = useRouting();
|
||||||
|
const [view, setView] = useState<string>('list');
|
||||||
|
|
||||||
|
const dummyActions = [
|
||||||
|
<Button label='Filter' onClick={() => {
|
||||||
|
alert('Clicked filter');
|
||||||
|
}} />,
|
||||||
|
<SortMenu
|
||||||
|
direction='desc'
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
id: 'date-added',
|
||||||
|
label: 'Date added',
|
||||||
|
selected: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'name',
|
||||||
|
label: 'Name'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'redemptions',
|
||||||
|
label: 'Open Rate'
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
position="left"
|
||||||
|
onDirectionChange={() => {}}
|
||||||
|
onSortChange={() => {}}
|
||||||
|
/>,
|
||||||
|
<Button icon='magnifying-glass' size='sm' onClick={() => {
|
||||||
|
alert('Clicked search');
|
||||||
|
}} />,
|
||||||
|
<ButtonGroup buttons={[
|
||||||
|
{
|
||||||
|
icon: 'listview',
|
||||||
|
size: 'sm',
|
||||||
|
iconColorClass: (view === 'list' ? 'text-black' : 'text-grey-500'),
|
||||||
|
onClick: () => {
|
||||||
|
setView('list');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'cardview',
|
||||||
|
size: 'sm',
|
||||||
|
iconColorClass: (view === 'card' ? 'text-black' : 'text-grey-500'),
|
||||||
|
onClick: () => {
|
||||||
|
setView('card');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]} clearBg={false} link />
|
||||||
|
];
|
||||||
|
|
||||||
|
const testColumns: DynamicTableColumn[] = [
|
||||||
|
{
|
||||||
|
title: 'Member',
|
||||||
|
noWrap: true,
|
||||||
|
minWidth: '1%',
|
||||||
|
maxWidth: '1%'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Status'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Open rate'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Location',
|
||||||
|
noWrap: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Created',
|
||||||
|
noWrap: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Signed up on post',
|
||||||
|
noWrap: true,
|
||||||
|
maxWidth: '150px'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Newsletter'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Billing period'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Email sent'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
hidden: true,
|
||||||
|
disableRowClick: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const testRows = (noOfRows: number) => {
|
||||||
|
const data: DynamicTableRow[] = [];
|
||||||
|
for (let i = 0; i < noOfRows; i++) {
|
||||||
|
data.push(
|
||||||
|
{
|
||||||
|
onClick: () => {
|
||||||
|
updateRoute('detail');
|
||||||
|
},
|
||||||
|
cells: [
|
||||||
|
(<div className='flex items-center gap-3 whitespace-nowrap pr-10'>
|
||||||
|
<Avatar image={`https://i.pravatar.cc/150?img=${i}`} />
|
||||||
|
<div>
|
||||||
|
{i % 3 === 0 && <div className='whitespace-nowrap text-md'>Jamie Larson</div>}
|
||||||
|
{i % 3 === 1 && <div className='whitespace-nowrap text-md'>Giana Septimus</div>}
|
||||||
|
{i % 3 === 2 && <div className='whitespace-nowrap text-md'>Zaire Bator</div>}
|
||||||
|
<div className='text-grey-700'>jamie@larson.com</div>
|
||||||
|
</div>
|
||||||
|
</div>),
|
||||||
|
'Free',
|
||||||
|
'40%',
|
||||||
|
'London, UK',
|
||||||
|
<div>
|
||||||
|
<div>22 June 2023</div>
|
||||||
|
<div className='text-grey-500'>5 months ago</div>
|
||||||
|
</div>,
|
||||||
|
'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
|
||||||
|
'Subscribed',
|
||||||
|
'Monthly',
|
||||||
|
'1,303',
|
||||||
|
<Button color='green' label='Edit' link onClick={() => {
|
||||||
|
alert('Clicked Edit in row:' + i);
|
||||||
|
}} />
|
||||||
|
]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const dummyCards = (noOfCards: number) => {
|
||||||
|
const cards = [];
|
||||||
|
|
||||||
|
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');
|
||||||
|
}}>
|
||||||
|
<Avatar image={`https://i.pravatar.cc/150?img=${i}`} size='xl' />
|
||||||
|
<div className='flex flex-col items-center'>
|
||||||
|
<Heading level={5}>
|
||||||
|
{i % 3 === 0 && 'Jamie Larson'}
|
||||||
|
{i % 3 === 1 && 'Giana Septimus'}
|
||||||
|
{i % 3 === 2 && 'Zaire Bator'}
|
||||||
|
</Heading>
|
||||||
|
<div className='mt-1 text-sm text-grey-700'>
|
||||||
|
{i % 3 === 0 && 'jamie@larson.com'}
|
||||||
|
{i % 3 === 1 && 'giana@septimus.com'}
|
||||||
|
{i % 3 === 2 && 'zaire@bator.com'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='flex w-full flex-col gap-4 border-t border-grey-300 pt-5'>
|
||||||
|
{i % 3 === 0 && (<>
|
||||||
|
<div className='flex gap-4'>
|
||||||
|
<div className='basis-1/2 text-center'>
|
||||||
|
<Heading level={6}>Open rate</Heading>
|
||||||
|
<div className='text-lg'>83%</div>
|
||||||
|
</div>
|
||||||
|
<div className='basis-1/2 text-center'>
|
||||||
|
<Heading level={6}>Click rate</Heading>
|
||||||
|
<div className='text-lg'>19%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>)}
|
||||||
|
{i % 3 === 1 && (<>
|
||||||
|
<div className='flex gap-4'>
|
||||||
|
<div className='basis-1/2 text-center'>
|
||||||
|
<Heading level={6}>Open rate</Heading>
|
||||||
|
<div className='text-lg'>68%</div>
|
||||||
|
</div>
|
||||||
|
<div className='basis-1/2 text-center'>
|
||||||
|
<Heading level={6}>Click rate</Heading>
|
||||||
|
<div className='text-lg'>21%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>)}
|
||||||
|
{i % 3 === 2 && (<>
|
||||||
|
<div className='flex gap-4'>
|
||||||
|
<div className='basis-1/2 text-center'>
|
||||||
|
<Heading level={6}>Open rate</Heading>
|
||||||
|
<div className='text-lg'>89%</div>
|
||||||
|
</div>
|
||||||
|
<div className='basis-1/2 text-center'>
|
||||||
|
<Heading level={6}>Click rate</Heading>
|
||||||
|
<div className='text-lg'>34%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return cards;
|
||||||
|
};
|
||||||
|
|
||||||
|
let contents = <></>;
|
||||||
|
switch (view) {
|
||||||
|
case 'list':
|
||||||
|
contents = <DynamicTable
|
||||||
|
cellClassName='text-sm'
|
||||||
|
columns={testColumns}
|
||||||
|
footer={
|
||||||
|
<Hint>30 members</Hint>
|
||||||
|
}
|
||||||
|
rows={testRows(30)}
|
||||||
|
stickyFooter
|
||||||
|
stickyHeader
|
||||||
|
/>;
|
||||||
|
break;
|
||||||
|
case 'card':
|
||||||
|
contents = <div className='grid grid-cols-4 gap-8 py-8'>{dummyCards(30)}</div>;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const demoPage = (
|
||||||
|
<Page>
|
||||||
|
<ViewContainer
|
||||||
|
actions={dummyActions}
|
||||||
|
primaryAction={{
|
||||||
|
title: 'About',
|
||||||
|
onClick: () => {
|
||||||
|
updateRoute('demo-modal');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
title='AdminX Demo App'
|
||||||
|
toolbarBorder={view === 'card'}
|
||||||
|
type='page'
|
||||||
|
>
|
||||||
|
{contents}
|
||||||
|
</ViewContainer>
|
||||||
|
</Page>
|
||||||
|
);
|
||||||
|
|
||||||
|
return demoPage;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ListPage;
|
|
@ -1,244 +1,15 @@
|
||||||
import {Avatar, Button, ButtonGroup, DynamicTable, DynamicTableColumn, DynamicTableRow, Heading, Hint, Page, SortMenu, ViewContainer} from '@tryghost/admin-x-design-system';
|
import DetailPage from './DetailPage';
|
||||||
|
import ListPage from './ListPage';
|
||||||
import {useRouting} from '@tryghost/admin-x-framework/routing';
|
import {useRouting} from '@tryghost/admin-x-framework/routing';
|
||||||
import {useState} from 'react';
|
|
||||||
|
|
||||||
const MainContent = () => {
|
const MainContent = () => {
|
||||||
const {updateRoute} = useRouting();
|
const {route} = useRouting();
|
||||||
const [view, setView] = useState<string>('list');
|
|
||||||
|
|
||||||
const dummyActions = [
|
if (route === 'detail') {
|
||||||
<Button label='Filter' onClick={() => {
|
return <DetailPage />;
|
||||||
alert('Clicked filter');
|
} else {
|
||||||
}} />,
|
return <ListPage />;
|
||||||
<SortMenu
|
|
||||||
direction='desc'
|
|
||||||
items={[
|
|
||||||
{
|
|
||||||
id: 'date-added',
|
|
||||||
label: 'Date added',
|
|
||||||
selected: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'name',
|
|
||||||
label: 'Name'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'redemptions',
|
|
||||||
label: 'Open Rate'
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
position="left"
|
|
||||||
onDirectionChange={() => {}}
|
|
||||||
onSortChange={() => {}}
|
|
||||||
/>,
|
|
||||||
<Button icon='magnifying-glass' size='sm' onClick={() => {
|
|
||||||
alert('Clicked search');
|
|
||||||
}} />,
|
|
||||||
<ButtonGroup buttons={[
|
|
||||||
{
|
|
||||||
icon: 'listview',
|
|
||||||
size: 'sm',
|
|
||||||
iconColorClass: (view === 'list' ? 'text-black' : 'text-grey-500'),
|
|
||||||
onClick: () => {
|
|
||||||
setView('list');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: 'cardview',
|
|
||||||
size: 'sm',
|
|
||||||
iconColorClass: (view === 'card' ? 'text-black' : 'text-grey-500'),
|
|
||||||
onClick: () => {
|
|
||||||
setView('card');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]} clearBg={false} link />
|
|
||||||
];
|
|
||||||
|
|
||||||
const testColumns: DynamicTableColumn[] = [
|
|
||||||
{
|
|
||||||
title: 'Member',
|
|
||||||
noWrap: true,
|
|
||||||
minWidth: '1%',
|
|
||||||
maxWidth: '1%'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Status'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Open rate'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Location',
|
|
||||||
noWrap: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Created',
|
|
||||||
noWrap: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Signed up on post',
|
|
||||||
noWrap: true,
|
|
||||||
maxWidth: '150px'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Newsletter'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Billing period'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Email sent'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '',
|
|
||||||
hidden: true,
|
|
||||||
disableRowClick: true
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const testRows = (noOfRows: number) => {
|
|
||||||
const data: DynamicTableRow[] = [];
|
|
||||||
for (let i = 0; i < noOfRows; i++) {
|
|
||||||
data.push(
|
|
||||||
{
|
|
||||||
onClick: () => {
|
|
||||||
alert('Clicked on row: ' + i);
|
|
||||||
},
|
|
||||||
cells: [
|
|
||||||
(<div className='flex items-center gap-3 whitespace-nowrap pr-10'>
|
|
||||||
<Avatar image={`https://i.pravatar.cc/150?img=${i}`} />
|
|
||||||
<div>
|
|
||||||
{i % 3 === 0 && <div className='whitespace-nowrap text-md'>Jamie Larson</div>}
|
|
||||||
{i % 3 === 1 && <div className='whitespace-nowrap text-md'>Giana Septimus</div>}
|
|
||||||
{i % 3 === 2 && <div className='whitespace-nowrap text-md'>Zaire Bator</div>}
|
|
||||||
<div className='text-grey-700'>jamie@larson.com</div>
|
|
||||||
</div>
|
|
||||||
</div>),
|
|
||||||
'Free',
|
|
||||||
'40%',
|
|
||||||
'London, UK',
|
|
||||||
<div>
|
|
||||||
<div>22 June 2023</div>
|
|
||||||
<div className='text-grey-500'>5 months ago</div>
|
|
||||||
</div>,
|
|
||||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
|
|
||||||
'Subscribed',
|
|
||||||
'Monthly',
|
|
||||||
'1,303',
|
|
||||||
<Button color='green' label='Edit' link onClick={() => {
|
|
||||||
alert('Clicked Edit in row:' + i);
|
|
||||||
}} />
|
|
||||||
]
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
const dummyCards = (noOfCards: number) => {
|
|
||||||
const cards = [];
|
|
||||||
|
|
||||||
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');
|
|
||||||
}}>
|
|
||||||
<Avatar image={`https://i.pravatar.cc/150?img=${i}`} size='xl' />
|
|
||||||
<div className='flex flex-col items-center'>
|
|
||||||
<Heading level={5}>
|
|
||||||
{i % 3 === 0 && 'Jamie Larson'}
|
|
||||||
{i % 3 === 1 && 'Giana Septimus'}
|
|
||||||
{i % 3 === 2 && 'Zaire Bator'}
|
|
||||||
</Heading>
|
|
||||||
<div className='mt-1 text-sm text-grey-700'>
|
|
||||||
{i % 3 === 0 && 'jamie@larson.com'}
|
|
||||||
{i % 3 === 1 && 'giana@septimus.com'}
|
|
||||||
{i % 3 === 2 && 'zaire@bator.com'}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='flex w-full flex-col gap-4 border-t border-grey-300 pt-5'>
|
|
||||||
{i % 3 === 0 && (<>
|
|
||||||
<div className='flex gap-4'>
|
|
||||||
<div className='basis-1/2 text-center'>
|
|
||||||
<Heading level={6}>Open rate</Heading>
|
|
||||||
<div className='text-lg'>83%</div>
|
|
||||||
</div>
|
|
||||||
<div className='basis-1/2 text-center'>
|
|
||||||
<Heading level={6}>Click rate</Heading>
|
|
||||||
<div className='text-lg'>19%</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>)}
|
|
||||||
{i % 3 === 1 && (<>
|
|
||||||
<div className='flex gap-4'>
|
|
||||||
<div className='basis-1/2 text-center'>
|
|
||||||
<Heading level={6}>Open rate</Heading>
|
|
||||||
<div className='text-lg'>68%</div>
|
|
||||||
</div>
|
|
||||||
<div className='basis-1/2 text-center'>
|
|
||||||
<Heading level={6}>Click rate</Heading>
|
|
||||||
<div className='text-lg'>21%</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>)}
|
|
||||||
{i % 3 === 2 && (<>
|
|
||||||
<div className='flex gap-4'>
|
|
||||||
<div className='basis-1/2 text-center'>
|
|
||||||
<Heading level={6}>Open rate</Heading>
|
|
||||||
<div className='text-lg'>89%</div>
|
|
||||||
</div>
|
|
||||||
<div className='basis-1/2 text-center'>
|
|
||||||
<Heading level={6}>Click rate</Heading>
|
|
||||||
<div className='text-lg'>34%</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return cards;
|
|
||||||
};
|
|
||||||
|
|
||||||
let contents = <></>;
|
|
||||||
switch (view) {
|
|
||||||
case 'list':
|
|
||||||
contents = <DynamicTable
|
|
||||||
cellClassName='text-sm'
|
|
||||||
columns={testColumns}
|
|
||||||
footer={
|
|
||||||
<Hint>30 members</Hint>
|
|
||||||
}
|
|
||||||
rows={testRows(30)}
|
|
||||||
stickyFooter
|
|
||||||
stickyHeader
|
|
||||||
/>;
|
|
||||||
break;
|
|
||||||
case 'card':
|
|
||||||
contents = <div className='grid grid-cols-4 gap-8 py-8'>{dummyCards(30)}</div>;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const demoPage = (
|
|
||||||
<Page>
|
|
||||||
<ViewContainer
|
|
||||||
actions={dummyActions}
|
|
||||||
primaryAction={{
|
|
||||||
title: 'About',
|
|
||||||
onClick: () => {
|
|
||||||
updateRoute('demo-modal');
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
title='AdminX Demo App'
|
|
||||||
toolbarBorder={view === 'card'}
|
|
||||||
type='page'
|
|
||||||
>
|
|
||||||
{contents}
|
|
||||||
</ViewContainer>
|
|
||||||
</Page>
|
|
||||||
);
|
|
||||||
|
|
||||||
return demoPage;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MainContent;
|
export default MainContent;
|
||||||
|
|
Loading…
Add table
Reference in a new issue