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
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 {useState} from 'react';
|
||||
|
||||
const MainContent = () => {
|
||||
const {updateRoute} = useRouting();
|
||||
const [view, setView] = useState<string>('list');
|
||||
const {route} = useRouting();
|
||||
|
||||
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: () => {
|
||||
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;
|
||||
if (route === 'detail') {
|
||||
return <DetailPage />;
|
||||
} else {
|
||||
return <ListPage />;
|
||||
}
|
||||
|
||||
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;
|
||||
|
Loading…
Reference in New Issue
Block a user