- Small:
+
+ Small:
+
- Medium (default):
+
+ Medium (default):
+
- Large:
+
+ Large:
+
);
}
-
diff --git a/client/src/webpages/dashboard/actions/ActionParametersEditor.tsx b/client/src/webpages/dashboard/actions/ActionParametersEditor.tsx
index b95c049..c9a99b2 100644
--- a/client/src/webpages/dashboard/actions/ActionParametersEditor.tsx
+++ b/client/src/webpages/dashboard/actions/ActionParametersEditor.tsx
@@ -1,6 +1,3 @@
-import { ChevronsUpDown, Plus, Trash2 } from 'lucide-react';
-import { useId, useMemo } from 'react';
-
import { Button } from '@/coop-ui/Button';
import { Checkbox } from '@/coop-ui/Checkbox';
import { Input } from '@/coop-ui/Input';
@@ -15,11 +12,13 @@ import {
} from '@/coop-ui/Select';
import { Switch } from '@/coop-ui/Switch';
import { cn } from '@/lib/utils';
+import { ChevronsUpDown, Plus, Trash2 } from 'lucide-react';
+import { useId, useMemo } from 'react';
import {
+ GQLActionParameterType,
type GQLActionParameterInput,
type GQLActionParameterOptionInput,
- GQLActionParameterType,
} from '../../../graphql/generated';
// Mirror of the server-side pattern in `actionParametersValidation.ts`. Allows
@@ -134,7 +133,11 @@ function ParameterRow({
return (
-
+
-
+
onChange({ min })}
/>
-
+
- onChange(options.filter((_, optionIndex) => optionIndex !== index))
+ onChange(
+ options.filter((_, optionIndex) => optionIndex !== index),
+ )
}
aria-label="Remove option"
>
@@ -756,7 +770,10 @@ export function validateDrafts(
// widgets in `DefaultValueInput`, so only range/membership can drift.
const dv = draft.defaultValue;
if (dv !== undefined) {
- if (draft.type === GQLActionParameterType.Number && typeof dv === 'number') {
+ if (
+ draft.type === GQLActionParameterType.Number &&
+ typeof dv === 'number'
+ ) {
if (draft.min !== undefined && dv < draft.min) {
return `${at}: default below min.`;
}
@@ -788,7 +805,10 @@ export function fromGraphQLParameters(
readonly description?: string | null;
readonly type: GQLActionParameterType;
readonly required: boolean;
- readonly options?: ReadonlyArray<{ readonly value: string; readonly label: string }> | null;
+ readonly options?: ReadonlyArray<{
+ readonly value: string;
+ readonly label: string;
+ }> | null;
readonly min?: number | null;
readonly max?: number | null;
readonly maxLength?: number | null;
@@ -819,7 +839,8 @@ export function useParameterDraftsFromAction<
T extends Parameters[0],
>(parameters: T | undefined): ActionParameterDraft[] | undefined {
return useMemo(
- () => (parameters === undefined ? undefined : fromGraphQLParameters(parameters)),
+ () =>
+ parameters === undefined ? undefined : fromGraphQLParameters(parameters),
[parameters],
);
}
diff --git a/client/src/webpages/dashboard/banks/MatchingBanksDashboard.tsx b/client/src/webpages/dashboard/banks/MatchingBanksDashboard.tsx
index 412dac1..a9de504 100644
--- a/client/src/webpages/dashboard/banks/MatchingBanksDashboard.tsx
+++ b/client/src/webpages/dashboard/banks/MatchingBanksDashboard.tsx
@@ -56,7 +56,6 @@ export default function MatchingBanksDashboard() {
? 'location'
: 'hash'
}`}
-
disabled={!canEditBanks}
disabledTooltipTitle="To create Matching Banks, you need Admin permissions."
disabledTooltipPlacement="bottomRight"
diff --git a/client/src/webpages/dashboard/banks/hash/HashBankForm.tsx b/client/src/webpages/dashboard/banks/hash/HashBankForm.tsx
index 8803af7..faa639e 100644
--- a/client/src/webpages/dashboard/banks/hash/HashBankForm.tsx
+++ b/client/src/webpages/dashboard/banks/hash/HashBankForm.tsx
@@ -1,32 +1,34 @@
-import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Button, Form, Input, Select, Slider, Switch, Tag } from 'antd';
-import { useNavigate, useParams } from 'react-router-dom';
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Helmet } from 'react-helmet-async';
+import { useNavigate, useParams } from 'react-router-dom';
+
+import FullScreenLoading from '../../../../components/common/FullScreenLoading';
+import CoopButton from '../../components/CoopButton';
+import CoopModal from '../../components/CoopModal';
+import FormHeader from '../../components/FormHeader';
+import FormSectionHeader from '../../components/FormSectionHeader';
+import NameDescriptionInput from '../../components/NameDescriptionInput';
+
import {
+ GQLHashBankByIdDocument,
+ namedOperations,
useGQLCreateHashBankMutation,
- useGQLUpdateHashBankMutation,
- useGQLUpdateExchangeCredentialsMutation,
- useGQLHashBankByIdQuery,
- useGQLExchangeApisQuery,
useGQLExchangeApiSchemaLazyQuery,
- namedOperations,
- GQLHashBankByIdDocument,
+ useGQLExchangeApisQuery,
+ useGQLHashBankByIdQuery,
+ useGQLUpdateExchangeCredentialsMutation,
+ useGQLUpdateHashBankMutation,
type GQLExchangeApiSchemaQuery,
} from '../../../../graphql/generated';
-import CoopModal from '../../components/CoopModal';
-import FormHeader from '../../components/FormHeader';
-import FormSectionHeader from '../../components/FormSectionHeader';
-import NameDescriptionInput from '../../components/NameDescriptionInput';
-import CoopButton from '../../components/CoopButton';
-import FullScreenLoading from '../../../../components/common/FullScreenLoading';
type SchemaField = GQLExchangeApiSchemaQuery['exchangeApiSchema'] extends
| infer S
| null
| undefined
? S extends { config_schema: { fields: ReadonlyArray } }
- ? F
- : never
+ ? F
+ : never
: never;
const EXCHANGE_DISPLAY_NAMES: Record = {
@@ -93,7 +95,7 @@ function DynamicSchemaFields({
(fieldName: string, field: SchemaField, raw: string) => {
onChange({ ...values, [fieldName]: coerceFieldValue(field, raw) });
},
- [values, onChange]
+ [values, onChange],
);
if (fields.length === 0) return null;
@@ -151,9 +153,7 @@ function DynamicSchemaFields({
) : (
(null);
- const [exchangeConfigValues, setExchangeConfigValues] = useState>({});
- const [exchangeCredValues, setExchangeCredValues] = useState>({});
+ const [selectedExchangeApi, setSelectedExchangeApi] = useState(
+ null,
+ );
+ const [exchangeConfigValues, setExchangeConfigValues] = useState<
+ Record
+ >({});
+ const [exchangeCredValues, setExchangeCredValues] = useState<
+ Record
+ >({});
const isCreating = id == null;
@@ -202,7 +208,7 @@ export default function HashBankForm() {
const exchangeApisQuery = useGQLExchangeApisQuery({ skip: !isCreating });
const exchangeApis = useMemo(
() => exchangeApisQuery.data?.exchangeApis ?? [],
- [exchangeApisQuery.data?.exchangeApis]
+ [exchangeApisQuery.data?.exchangeApis],
);
const [fetchSchema, schemaQuery] = useGQLExchangeApiSchemaLazyQuery();
@@ -211,7 +217,7 @@ export default function HashBankForm() {
const selectedApiInfo = useMemo(
() => exchangeApis.find((a) => a.name === selectedExchangeApi),
- [exchangeApis, selectedExchangeApi]
+ [exchangeApis, selectedExchangeApi],
);
useEffect(() => {
@@ -222,7 +228,9 @@ export default function HashBankForm() {
}
}, [selectedExchangeApi, fetchSchema]);
- const [editCredValues, setEditCredValues] = useState>({});
+ const [editCredValues, setEditCredValues] = useState>(
+ {},
+ );
const [showCredForm, setShowCredForm] = useState(false);
useEffect(() => {
@@ -381,15 +389,15 @@ export default function HashBankForm() {
const exchangeInput =
selectedExchangeApi && schema
? {
- api_name: selectedExchangeApi,
- config_json: JSON.stringify(exchangeConfigValues),
- credentials_json:
- schema.credentials_schema &&
+ api_name: selectedExchangeApi,
+ config_json: JSON.stringify(exchangeConfigValues),
+ credentials_json:
+ schema.credentials_schema &&
selectedApiInfo &&
!selectedApiInfo.has_auth
- ? JSON.stringify(exchangeCredValues)
- : undefined,
- }
+ ? JSON.stringify(exchangeCredValues)
+ : undefined,
+ }
: undefined;
createHashBank({
@@ -440,8 +448,10 @@ export default function HashBankForm() {
hideModal();
if (
- (createMutationParams.data?.createHashBank && 'data' in createMutationParams.data.createHashBank) ||
- (updateMutationParams.data?.updateHashBank && 'data' in updateMutationParams.data.updateHashBank)
+ (createMutationParams.data?.createHashBank &&
+ 'data' in createMutationParams.data.createHashBank) ||
+ (updateMutationParams.data?.updateHashBank &&
+ 'data' in updateMutationParams.data.updateHashBank)
) {
navigate(-1);
}
@@ -453,7 +463,7 @@ export default function HashBankForm() {
(f) =>
f.required &&
(exchangeConfigValues[f.name] == null ||
- exchangeConfigValues[f.name] === '')
+ exchangeConfigValues[f.name] === ''),
);
const hasRequiredCredsMissing =
@@ -465,10 +475,11 @@ export default function HashBankForm() {
(f) =>
f.required &&
(exchangeCredValues[f.name] == null ||
- exchangeCredValues[f.name] === '')
+ exchangeCredValues[f.name] === ''),
);
- const isExchangeIncomplete = Boolean(hasRequiredConfigMissing) || Boolean(hasRequiredCredsMissing);
+ const isExchangeIncomplete =
+ Boolean(hasRequiredConfigMissing) || Boolean(hasRequiredCredsMissing);
const modal = (
- {bank.exchange.has_auth ? 'Credentials Set' : 'Credentials Missing'}
+ {bank.exchange.has_auth
+ ? 'Credentials Set'
+ : 'Credentials Missing'}
{bank.exchange.last_fetch_succeeded === false && (
Fetch Failed
@@ -540,7 +553,8 @@ export default function HashBankForm() {
{bank.exchange.last_fetch_time && (
- Last fetch: {new Date(bank.exchange.last_fetch_time).toLocaleString()}
+ Last fetch:{' '}
+ {new Date(bank.exchange.last_fetch_time).toLocaleString()}
{bank.exchange.fetched_items != null && (
<> · {bank.exchange.fetched_items} items>
)}
@@ -548,7 +562,9 @@ export default function HashBankForm() {
)}
{bank.exchange.last_fetch_succeeded === false && (
- The last fetch from this exchange failed. Check that credentials are correct and the exchange service is reachable.
+ The last fetch from this exchange failed. Check that
+ credentials are correct and the exchange service is
+ reachable.
)}
@@ -581,15 +597,13 @@ export default function HashBankForm() {
f.required)
- .some(
- (f) =>
- editCredValues[f.name] == null ||
- editCredValues[f.name] === ''
- )
- }
+ disabled={schema.credentials_schema.fields
+ .filter((f) => f.required)
+ .some(
+ (f) =>
+ editCredValues[f.name] == null ||
+ editCredValues[f.name] === '',
+ )}
disabledTooltipTitle="Please fill in all required credential fields"
onClick={onUpdateCredentials}
/>
@@ -625,15 +639,18 @@ export default function HashBankForm() {
marks={{
0: 'Disabled',
0.5: '50%',
- 1: 'Enabled'
+ 1: 'Enabled',
}}
tooltip={{
- formatter: (value) => `${Math.round((value ?? 0) * 100)}%`
+ formatter: (value) => `${Math.round((value ?? 0) * 100)}%`,
}}
- style={{
- '--ant-slider-track-background-color': getSliderColor(enabledRatio),
- maxWidth: '100%',
- } as React.CSSProperties}
+ style={
+ {
+ '--ant-slider-track-background-color':
+ getSliderColor(enabledRatio),
+ maxWidth: '100%',
+ } as React.CSSProperties
+ }
onChange={(value) => {
setEnabledRatio(value);
}}
@@ -661,7 +678,9 @@ export default function HashBankForm() {
loading={exchangeApisQuery.loading}
className="max-w-md"
options={exchangeApis.map((api) => ({
- label: EXCHANGE_DISPLAY_NAMES[api.name] ?? api.name.replace(/_/g, ' '),
+ label:
+ EXCHANGE_DISPLAY_NAMES[api.name] ??
+ api.name.replace(/_/g, ' '),
value: api.name,
}))}
/>
@@ -702,12 +721,11 @@ export default function HashBankForm() {
)}
- {schema.credentials_schema &&
- selectedApiInfo?.has_auth && (
-
- Credentials for this exchange API are already configured.
-
- )}
+ {schema.credentials_schema && selectedApiInfo?.has_auth && (
+
+ Credentials for this exchange API are already configured.
+
+ )}
>
)}
diff --git a/client/src/webpages/dashboard/banks/hash/HashBanksDashboard.tsx b/client/src/webpages/dashboard/banks/hash/HashBanksDashboard.tsx
index 001a88c..96e1084 100644
--- a/client/src/webpages/dashboard/banks/hash/HashBanksDashboard.tsx
+++ b/client/src/webpages/dashboard/banks/hash/HashBanksDashboard.tsx
@@ -1,13 +1,9 @@
-import React, { useMemo, useState, useCallback } from 'react';
import { Tag } from 'antd';
-import { MouseEvent } from 'react';
-import { useNavigate } from 'react-router-dom';
+import React, { MouseEvent, useCallback, useMemo, useState } from 'react';
import { Helmet } from 'react-helmet-async';
-import {
- useGQLHashBanksQuery,
- useGQLDeleteHashBankMutation,
- namedOperations
-} from '../../../../graphql/generated';
+import { useNavigate } from 'react-router-dom';
+
+import FullScreenLoading from '../../../../components/common/FullScreenLoading';
import CoopModal from '../../components/CoopModal';
import EmptyDashboard from '../../components/EmptyDashboard';
import RowMutations, {
@@ -19,7 +15,12 @@ import {
} from '../../components/table/filters';
import { stringSort } from '../../components/table/sort';
import CustomTable from '../../components/table/Table';
-import FullScreenLoading from '../../../../components/common/FullScreenLoading';
+
+import {
+ namedOperations,
+ useGQLDeleteHashBankMutation,
+ useGQLHashBanksQuery,
+} from '../../../../graphql/generated';
const getStatusColor = (enabled_ratio: number) => {
if (enabled_ratio === 0) return 'red';
@@ -45,19 +46,25 @@ export default function HashBanksDashboard() {
const navigate = useNavigate();
- const editBank = useCallback((id: string, event: MouseEvent) => {
- // This ensures that the row's onClick isn't called because
- // the row is the parent component
- event.stopPropagation();
- navigate(`/dashboard/rules/banks/form/hash/${id}`);
- }, [navigate]);
+ const editBank = useCallback(
+ (id: string, event: MouseEvent) => {
+ // This ensures that the row's onClick isn't called because
+ // the row is the parent component
+ event.stopPropagation();
+ navigate(`/dashboard/rules/banks/form/hash/${id}`);
+ },
+ [navigate],
+ );
- const onDeleteBank = useCallback((id: string) => {
- deleteHashBank({
- variables: { id },
- refetchQueries: [namedOperations.Query.HashBanks],
- });
- }, [deleteHashBank]);
+ const onDeleteBank = useCallback(
+ (id: string) => {
+ deleteHashBank({
+ variables: { id },
+ refetchQueries: [namedOperations.Query.HashBanks],
+ });
+ },
+ [deleteHashBank],
+ );
const showModal = useCallback((id: string, event: MouseEvent) => {
// This ensures that the row's onClick isn't called because
@@ -69,15 +76,18 @@ export default function HashBanksDashboard() {
});
}, []);
- const mutations = useCallback((id: string) => {
- return (
-