diff --git a/packages/frontend/app/components/ui/EditableTable.tsx b/packages/frontend/app/components/ui/EditableTable.tsx new file mode 100644 index 0000000000..1e236da4fe --- /dev/null +++ b/packages/frontend/app/components/ui/EditableTable.tsx @@ -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(options) + const [value, setValue] = useState('') + + 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 ( + <> + + + + + {(optionsList || []).map((option, index) => ( + + + {option.showInput ? ( + + e.key === 'Enter' && + (e.preventDefault(), + editOption(index, e.currentTarget.value)) + } + defaultValue={option.value} + required={required} + /> + ) : ( + {option.value} + )} + + + {option.canEdit && !option.showInput ? ( + + ) : null} + {option.canDelete ? ( + + ) : null} + + + ))} + +
+
+ +
+ {description ? ( +
{description}
+ ) : null} + + + ) +} diff --git a/packages/frontend/app/routes/peers.$peerId.tsx b/packages/frontend/app/routes/peers.$peerId.tsx index 20885c65e9..ae7eb4390f 100644 --- a/packages/frontend/app/routes/peers.$peerId.tsx +++ b/packages/frontend/app/routes/peers.$peerId.tsx @@ -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') @@ -204,10 +205,15 @@ export default function ViewPeerPage() {
- ({ + label: token, + value: token, + canDelete: true, + canEdit: true + }))} error={response?.errors.http.fieldErrors.incomingAuthTokens} description={ <> @@ -435,7 +441,7 @@ export async function action({ request }: ActionFunctionArgs) { result.error.flatten().fieldErrors return json({ ...actionResponse }, { status: 400 }) } - + console.log(result.data.incomingAuthTokens) const response = await updatePeer({ id: result.data.id, http: {