Skip to content

Commit

Permalink
feat(frontend): allow editing of multiple incoming auth tokens with `…
Browse files Browse the repository at this point in the history
…EditableTable`
  • Loading branch information
furkan-bilgin committed Sep 2, 2024
1 parent 8dc334b commit 5aab138
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 3 deletions.
154 changes: 154 additions & 0 deletions packages/frontend/app/components/ui/EditableTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import type { ReactNode } from 'react'
import { useEffect, useState } from 'react'
import { Input } from './Input'
import { Table } from './Table'
import { FieldError } from './FieldError'
import { Button } from './Button'

type EditableTableProps = {
name: string
label: string
options: EditableTableOption[]
error?: string | string[]
description?: ReactNode
valueFormatter?: (values: string[]) => string
required?: boolean
}

type EditableTableOption = {
label: string
value: string
canDelete?: boolean
canEdit?: boolean
showInput?: boolean
}

export const EditableTable = ({
name,
label,
options,
error,
description = undefined,
valueFormatter = (values) => values.join(','),
required = false
}: EditableTableProps) => {
const [optionsList, setOptionsList] = useState<EditableTableOption[]>(options)
const [value, setValue] = useState<string>('')

const toggleEditInput = (index: number) => {
setOptionsList(
optionsList.map((option, i) => {
if (i === index) {
return {
...option,
showInput: true
}
}
return option
})
)
}

const editOption = (index: number, value: string) => {
if (!value) {
deleteOption(index)
return
}
setOptionsList(
optionsList.map((option, i) => {
if (i === index) {
return {
...option,
showInput: false,
value
}
}
return option
})
)
}

const deleteOption = (index: number) => {
setOptionsList(optionsList.filter((_, i) => i !== index))
}

const addOption = () => {
setOptionsList([
...optionsList,
{ label: '', value: '', canDelete: true, canEdit: true, showInput: true }
])
}

useEffect(() => {
setValue(getValue())
}, [optionsList])

const getValue = () => {
return valueFormatter(optionsList.map((option) => option.value))
}

return (
<>
<Input
type='hidden'
name={name}
value={value}
required={required}
label={label}
/>
<Table>
<Table.Head columns={['Token', 'Action']} />
<Table.Body>
{(optionsList || []).map((option, index) => (
<Table.Row key={index}>
<Table.Cell key={0}>
{option.showInput ? (
<Input
type='text'
onKeyDown={(e) =>
e.key === 'Enter' &&
(e.preventDefault(),
editOption(index, e.currentTarget.value))
}
defaultValue={option.value}
required={required}
/>
) : (
<span>{option.value}</span>
)}
</Table.Cell>
<Table.Cell key={1}>
{option.canEdit && !option.showInput ? (
<Button
aria-label='save http information'
onClick={() => toggleEditInput(index)}
>
Edit
</Button>
) : null}
{option.canDelete ? (
<Button
className='ml-2'
aria-label='delete http information'
onClick={() => deleteOption(index)}
>
Delete
</Button>
) : null}
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
<div className='flex justify-end mt-2'>
<Button aria-label='add http information' onClick={() => addOption()}>
Add
</Button>
</div>
{description ? (
<div className='font-medium text-sm'>{description}</div>
) : null}
<FieldError error={error} />
</>
)
}
11 changes: 8 additions & 3 deletions packages/frontend/app/routes/peers.$peerId.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
import type { ZodFieldErrors } from '~/shared/types'
import { formatAmount } from '~/shared/utils'
import { checkAuthAndRedirect } from '../lib/kratos_checks.server'
import { EditableTable } from '~/components/ui/EditableTable'

export async function loader({ request, params }: LoaderFunctionArgs) {
const cookies = request.headers.get('cookie')
Expand Down Expand Up @@ -204,10 +205,15 @@ export default function ViewPeerPage() {
<fieldset disabled={currentPageAction}>
<div className='w-full p-4 space-y-3'>
<Input type='hidden' name='id' value={peer.id} />
<Input
<EditableTable
name='incomingAuthTokens'
label='Incoming Auth Tokens'
placeholder='Accepts a comma separated list of tokens'
options={(peer.incomingTokens || []).map((token) => ({
label: token,
value: token,
canDelete: true,
canEdit: true
}))}
error={response?.errors.http.fieldErrors.incomingAuthTokens}
description={
<>
Expand Down Expand Up @@ -435,7 +441,6 @@ export async function action({ request }: ActionFunctionArgs) {
result.error.flatten().fieldErrors
return json({ ...actionResponse }, { status: 400 })
}

const response = await updatePeer({
id: result.data.id,
http: {
Expand Down

0 comments on commit 5aab138

Please sign in to comment.