Merge pull request #976 from omnivore-app/895

#895 - Improve table used on the /settings/api page
This commit is contained in:
Jackson Harper
2022-07-22 10:56:30 -07:00
committed by GitHub
7 changed files with 150 additions and 156 deletions

View File

@ -356,6 +356,11 @@ export function authRouter() {
cors<express.Request>(corsConfig),
async (req: express.Request, res: express.Response) => {
const { email, password } = req.body
if (!email || !password) {
res.redirect(`${env.client.url}/email-login?errorCodes=AUTH_FAILED`)
return
}
const query = `
mutation login{
login(input: {
@ -418,6 +423,11 @@ export function authRouter() {
cors<express.Request>(corsConfig),
async (req: express.Request, res: express.Response) => {
const { email, password, name, username, bio } = req.body
if (!email || !password || !name || !username) {
res.redirect(`${env.client.url}/email-signup?errorCodes=BAD_DATA`)
return
}
const query = `
mutation signup {
signup(input: {
@ -425,7 +435,7 @@ export function authRouter() {
password: "${password}",
name: "${name}",
username: "${username}",
bio: "${bio}"
bio: "${bio ?? ''}"
}) {
__typename
... on SignupSuccess {

View File

@ -217,3 +217,22 @@ export const Button = styled('button', {
style: 'ctaWhite',
},
})
export const IconButton = styled(Button, {
variants: {
style: {
ctaWhite: {
color: 'red',
padding: '10px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
border: '1px solid $grayBorder',
boxSizing: 'border-box',
borderRadius: 6,
width: 40,
height: 40,
},
},
},
})

View File

@ -34,6 +34,7 @@ const Modal = styled(Content, {
boxShadow: theme.shadows.cardBoxShadow.toString(),
position: 'fixed',
'&:focus': { outline: 'none' },
zIndex:'1',
})
export const ModalContent = styled(Modal, {

View File

@ -1,10 +1,20 @@
import { Box, HStack, SpanBox, VStack } from './LayoutPrimitives'
import { isDarkTheme } from '../../lib/themeUpdater'
import {
Table as ResponsiveTable,
Thead,
Tbody,
Tr,
Th,
Td,
} from 'react-super-responsive-table'
import 'react-super-responsive-table/dist/SuperResponsiveTableStyle.css'
import { PencilSimple, Plus, Trash } from 'phosphor-react'
import { Box, SpanBox, VStack } from './LayoutPrimitives'
import { styled } from '../tokens/stitches.config'
import { StyledText } from './StyledText'
import { InfoLink } from './InfoLink'
import { Button } from './Button'
import { PencilSimple, Plus, Trash } from 'phosphor-react'
import { isDarkTheme } from '../../lib/themeUpdater'
import { IconButton } from './Button'
interface TableProps {
heading: string
@ -23,61 +33,25 @@ const HeaderWrapper = styled(Box, {
},
})
const TableCard = styled(Box, {
backgroundColor: '$grayBg',
display: 'flex',
alignItems: 'center',
padding: '10px 12px',
const StyledTable = styled(ResponsiveTable, {
margin: ' 0 auto',
border: '0.5px solid $grayBgActive',
backgroundColor: '$graySolid',
borderCollapse: 'collapse',
borderRadius: '5px',
width: '100%',
mt: '$3',
'&:hover': {
border: '0.5px solid #FFD234',
},
'@md': {
paddingLeft: '0',
},
})
const TableBody = styled(Tbody, {
backgroundColor: '$grayBg',
})
const TableHeading = styled(Box, {
backgroundColor: '$grayBgActive',
border: '1px solid rgba(0, 0, 0, 0.06)',
display: 'none',
alignItems: 'center',
padding: '14px 0 14px 40px',
borderRadius: '5px 5px 0px 0px',
width: '100%',
'@md': {
display: 'flex',
},
})
const Input = styled('input', {
backgroundColor: 'transparent',
color: '$grayTextContrast',
marginTop: '5px',
'&[disabled]': {
border: 'none',
},
})
const IconButton = styled(Button, {
variants: {
style: {
ctaWhite: {
color: 'red',
padding: '10px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
border: '1px solid $grayBorder',
boxSizing: 'border-box',
borderRadius: 6,
width: 40,
height: 40,
},
},
},
const TableRow = styled(Tr, {
border: '0 !important',
borderTop: '0.5px solid $grayBgActive !important',
})
export function Table(props: TableProps): JSX.Element {
@ -140,108 +114,87 @@ export function Table(props: TableProps): JSX.Element {
)}
</Box>
</HeaderWrapper>
<TableHeading>
{props.headers.map((header, index) => (
<Box
key={index}
css={{
flex: 'auto',
}}
>
<StyledText
key={index}
style="menuTitle"
css={{
color: '$grayTextContrast',
'@md': {
fontWeight: '600',
color: '$grayTextContrast',
textTransform: 'uppercase',
},
width: '240px',
}}
>
{header}
</StyledText>
</Box>
))}
<Box
css={{
width: '120px',
}}
></Box>
</TableHeading>
{Array.from(props.rows.keys()).map((key, index) => (
<TableCard
key={index}
css={{
'&:hover': {
background: 'rgba(255, 234, 159, 0.12)',
},
'@mdDown': {
borderTopLeftRadius: index === 0 ? '5px' : '',
borderTopRightRadius: index === 0 ? '5px' : '',
},
borderBottomLeftRadius: index == props.rows.size - 1 ? '5px' : '',
borderBottomRightRadius: index == props.rows.size - 1 ? '5px' : '',
padding: '10px 20px 10px 40px',
}}
>
<Box
css={{
display: 'flex',
width: '100%',
flexDirection: 'column',
'@md': {
flexDirection: 'row',
},
}}
>
{Object.values(props.rows.get(key) || {}).map((cell, index) => (
<HStack
key={index}
css={{
flex: 'auto',
display: 'flex',
padding: '4px 4px 4px 0px',
}}
>
<Input
type="text"
value={cell}
disabled
<StyledTable>
<Thead>
<Tr>
{props.headers.map((header: string, index: number) => (
<Th key={index}>
<SpanBox
css={{
width: '100%',
textTransform: 'uppercase',
display: 'flex',
fontWeight: 600,
padding: '20px 10px 20px 40px',
color: '$grayTextContrast',
fontSize: '$2',
}}
></Input>
</HStack>
>
{header}
</SpanBox>
</Th>
))}
{props.onEdit && (
<IconButton
style="ctaWhite"
css={{ mr: '$1', background: '$labelButtonsBg' }}
onClick={() => {
props.onEdit &&
props.onEdit({ ...props.rows.get(key), id: key })
}}
>
<PencilSimple size={24} color={iconColor} />
</IconButton>
)}
{props.onDelete && (
<IconButton
style="ctaWhite"
css={{ mr: '$1', background: '$labelButtonsBg' }}
onClick={() => {
props.onDelete && props.onDelete(key)
}}
>
<Trash size={16} color={iconColor} />
</IconButton>
)}
</Box>
</TableCard>
))}
</Tr>
</Thead>
<TableBody>
{Array.from(props.rows.keys()).map((key, index) => (
<TableRow key={index}>
{Object.values(props.rows.get(key) || {}).map((cell, index) => (
<Td key={index}>
<SpanBox
key={index}
css={{
display: 'flex',
justifyContent: 'left',
padding: '20px 10px 20px 40px',
color: '$grayTextContrast',
fontSize: '$1',
}}
>
{cell}
</SpanBox>
</Td>
))}
{props.onDelete && (
<Td>
<IconButton
style="ctaWhite"
css={{
mr: '$1',
background: '$labelButtonsBg',
margin: '20px 10px 20px 40px',
}}
onClick={() => {
props.onDelete && props.onDelete(key)
}}
>
<Trash size={16} color={iconColor} />
</IconButton>
</Td>
)}
{props.onEdit && (
<Td>
<IconButton
style="ctaWhite"
css={{
mr: '$1',
background: '$labelButtonsBg',
margin: '20px 10px 20px 40px',
}}
onClick={() => {
props.onEdit &&
props.onEdit({ ...props.rows.get(key), id: key })
}}
>
<PencilSimple size={24} color={iconColor} />
</IconButton>
</Td>
)}
</TableRow>
))}
</TableBody>
</StyledTable>
</VStack>
)
}

View File

@ -52,6 +52,7 @@ export function EmailLogin(): JSX.Element {
<BorderedFormInput
key="email"
type="text"
name="email"
value={email}
placeholder="Email"
onChange={(e) => { e.preventDefault(); setEmail(e.target.value); }}
@ -63,6 +64,7 @@ export function EmailLogin(): JSX.Element {
<BorderedFormInput
key="password"
type="password"
name="password"
value={password}
placeholder="Password"
onChange={(e) => setPassword(e.target.value)}

View File

@ -43,6 +43,7 @@
"react-colorful": "^5.5.1",
"react-dom": "^17.0.2",
"react-hot-toast": "^2.1.1",
"react-super-responsive-table": "^5.2.1",
"react-topbar-progress-indicator": "^4.1.1",
"react-twitter-widgets": "^1.10.0",
"swr": "^1.0.1",

View File

@ -10,6 +10,7 @@ import { revokeApiKeyMutation } from '../../lib/networking/mutations/revokeApiKe
import { PrimaryLayout } from '../../components/templates/PrimaryLayout'
import { Table } from '../../components/elements/Table'
import { FormInputProps } from '../../components/elements/FormElements'
import { FormModal } from '../../components/patterns/FormModal'
import { ConfirmationModal } from '../../components/patterns/ConfirmationModal'
@ -57,9 +58,10 @@ export default function Api(): JSX.Element {
usedAt: apiKey.usedAt
? new Date(apiKey.usedAt).toISOString()
: 'Never used',
expiresAt: new Date(apiKey.expiresAt).getTime() != neverExpiresDate.getTime()
? new Date(apiKey.expiresAt).toDateString()
: 'Never',
expiresAt:
new Date(apiKey.expiresAt).getTime() != neverExpiresDate.getTime()
? new Date(apiKey.expiresAt).toDateString()
: 'Never',
})
)
return rows
@ -117,7 +119,7 @@ export default function Api(): JSX.Element {
additionalDays = 365
break
case 'Never':
break;
break
}
const newExpires = additionalDays ? new Date() : neverExpiresDate
if (additionalDays) {
@ -126,7 +128,13 @@ export default function Api(): JSX.Element {
setExpiresAt(newExpires)
},
type: 'select',
options: ['in 7 days', 'in 30 days', 'in 90 days', 'in 1 year', 'Never'],
options: [
'in 7 days',
'in 30 days',
'in 90 days',
'in 1 year',
'Never',
],
value: defaultExpiresAt,
},
])