Admin UI improvements See merge request soapbox-pub/soapbox!2015environments/review-develop-3zknud/deployments/1786
commit
eb63ec4d46
@ -0,0 +1,43 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import List, { ListItem } from './list';
|
||||||
|
|
||||||
|
interface IRadioGroup {
|
||||||
|
onChange: React.ChangeEventHandler
|
||||||
|
children: React.ReactElement<{ onChange: React.ChangeEventHandler }>[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const RadioGroup = ({ onChange, children }: IRadioGroup) => {
|
||||||
|
const childrenWithProps = React.Children.map(children, child =>
|
||||||
|
React.cloneElement(child, { onChange }),
|
||||||
|
);
|
||||||
|
|
||||||
|
return <List>{childrenWithProps}</List>;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface IRadioItem {
|
||||||
|
label: React.ReactNode,
|
||||||
|
hint?: React.ReactNode,
|
||||||
|
value: string,
|
||||||
|
checked: boolean,
|
||||||
|
onChange?: React.ChangeEventHandler,
|
||||||
|
}
|
||||||
|
|
||||||
|
const RadioItem: React.FC<IRadioItem> = ({ label, hint, checked = false, onChange, value }) => {
|
||||||
|
return (
|
||||||
|
<ListItem label={label} hint={hint}>
|
||||||
|
<input
|
||||||
|
type='radio'
|
||||||
|
checked={checked}
|
||||||
|
onChange={onChange}
|
||||||
|
value={value}
|
||||||
|
className='h-4 w-4 border-gray-300 text-primary-600 focus:ring-primary-500'
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
RadioGroup,
|
||||||
|
RadioItem,
|
||||||
|
};
|
@ -0,0 +1,57 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { FormattedNumber } from 'react-intl';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { Text } from 'soapbox/components/ui';
|
||||||
|
import { isNumber } from 'soapbox/utils/numbers';
|
||||||
|
|
||||||
|
interface IDashCounter {
|
||||||
|
count: number | undefined
|
||||||
|
label: React.ReactNode
|
||||||
|
to?: string
|
||||||
|
percent?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Displays a (potentially clickable) dashboard statistic. */
|
||||||
|
const DashCounter: React.FC<IDashCounter> = ({ count, label, to = '#', percent = false }) => {
|
||||||
|
|
||||||
|
if (!isNumber(count)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
className='bg-gray-200 dark:bg-gray-800 p-4 rounded flex flex-col items-center space-y-2 hover:-translate-y-1 transition-transform cursor-pointer'
|
||||||
|
to={to}
|
||||||
|
>
|
||||||
|
<Text align='center' size='2xl' weight='medium'>
|
||||||
|
<FormattedNumber
|
||||||
|
value={count}
|
||||||
|
style={percent ? 'unit' : undefined}
|
||||||
|
unit={percent ? 'percent' : undefined}
|
||||||
|
/>
|
||||||
|
</Text>
|
||||||
|
<Text align='center'>
|
||||||
|
{label}
|
||||||
|
</Text>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface IDashCounters {
|
||||||
|
children: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Wrapper container for dash counters. */
|
||||||
|
const DashCounters: React.FC<IDashCounters> = ({ children }) => {
|
||||||
|
return (
|
||||||
|
<div className='grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-2'>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
DashCounter,
|
||||||
|
DashCounters,
|
||||||
|
};
|
@ -1,67 +0,0 @@
|
|||||||
.dashcounters {
|
|
||||||
@apply grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-2 mb-4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashcounter {
|
|
||||||
@apply bg-gray-200 dark:bg-gray-800 p-4 rounded flex flex-col items-center space-y-2 hover:-translate-y-1 transition-transform cursor-pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashwidgets {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
margin: 0 -5px;
|
|
||||||
padding: 0 20px 20px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashwidget {
|
|
||||||
flex: 1;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
padding: 0 5px;
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 700;
|
|
||||||
color: hsla(var(--primary-text-color_hsl), 0.6);
|
|
||||||
padding-bottom: 8px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
border-bottom: 1px solid var(--accent-color--med);
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: var(--brand-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.unapproved-account {
|
|
||||||
padding: 15px 20px;
|
|
||||||
font-size: 14px;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
&__nickname {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__actions {
|
|
||||||
margin-left: auto;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
column-gap: 10px;
|
|
||||||
padding-left: 20px;
|
|
||||||
|
|
||||||
.svg-icon {
|
|
||||||
height: 24px;
|
|
||||||
width: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.logentry {
|
|
||||||
padding: 15px;
|
|
||||||
|
|
||||||
&__timestamp {
|
|
||||||
color: var(--primary-text-color--faint);
|
|
||||||
font-size: 13px;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in new issue