Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(idea/frontend) add query params #1701

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { SailsFilterGroup } from '@/features/sails';

import { useMessagesToProgram, useMessagesFromProgram } from '../../api';
import { MessageCard } from '../message-card';
import { useSearchParams } from 'react-router-dom';

type Props = {
programId: HexString;
Expand Down Expand Up @@ -48,7 +49,28 @@ const DEFAULT_FILTER_VALUES = {
const ProgramMessages = ({ programId, sails }: Props) => {
const { account } = useAccount();

const [filters, setFilters] = useState(DEFAULT_FILTER_VALUES);
const [searchParams, setSearchParams] = useSearchParams();
const [filters, setFilters] = useState(() => {
const params = Object.fromEntries(searchParams.entries());
return {
[FILTER_NAME.OWNER]: params[FILTER_NAME.OWNER] || DEFAULT_FILTER_VALUES[FILTER_NAME.OWNER],
[FILTER_NAME.DIRECTION]: params[FILTER_NAME.DIRECTION] || DEFAULT_FILTER_VALUES[FILTER_NAME.DIRECTION],
[FILTER_NAME.SERVICE_NAME]: params[FILTER_NAME.SERVICE_NAME] || DEFAULT_FILTER_VALUES[FILTER_NAME.SERVICE_NAME],
[FILTER_NAME.FUNCTION_NAME]:
params[FILTER_NAME.FUNCTION_NAME] || DEFAULT_FILTER_VALUES[FILTER_NAME.FUNCTION_NAME],
};
});

useEffect(() => {
Object.entries(filters).forEach(([key, value]) => {
if (key in DEFAULT_FILTER_VALUES && value) {
searchParams.set(key, value);
} else {
searchParams.delete(key);
}
});
setSearchParams(searchParams, { replace: true });
}, [filters]);

const isToDirection = filters[FILTER_NAME.DIRECTION] === FILTER_VALUE.DIRECTION.TO;
const addressParam = filters[FILTER_NAME.OWNER] === FILTER_VALUE.OWNER.OWNER ? account?.decodedAddress : undefined;
Expand Down
27 changes: 23 additions & 4 deletions idea/frontend/src/features/program/hooks/use-program-filters.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,41 @@
import { useAccount } from '@gear-js/react-hooks';
import { useMemo, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { useEffect, useMemo, useState } from 'react';
import { useLocation, useSearchParams } from 'react-router-dom';

import { OwnerFilter } from '@/api/consts';

import { ProgramsParameters } from '../api';
import { DEFAULT_FILTER_VALUES } from '../consts';
import { DEFAULT_FILTER_VALUES, ProgramStatus } from '../consts';

type Location = {
state: Pick<ProgramsParameters, 'codeId'> | null;
};

function useProgramFilters(query: string) {
const location = useLocation() as Location;
const [searchParams, setSearchParams] = useSearchParams();
const { account } = useAccount();

// TODO: handle location.state in a params, not in state. right now code related programs are not working
const [filterValues, setFilterValues] = useState(DEFAULT_FILTER_VALUES);
const [filterValues, setFilterValues] = useState(() => {
const params = Object.fromEntries(searchParams.entries());
return {
owner: (params.owner || DEFAULT_FILTER_VALUES.owner) as OwnerFilter,
status: (params.status ? params.status.split(',') : DEFAULT_FILTER_VALUES.status) as ProgramStatus[],
};
});

useEffect(() => {
Object.entries(filterValues).forEach(([key, value]: [string, string | string[]]) => {
value = Array.isArray(value) ? value.join(',') : value;
if (key in DEFAULT_FILTER_VALUES && value) {
searchParams.set(key, value);
} else {
searchParams.delete(key);
}
});
setSearchParams(searchParams, { replace: true });
}, [filterValues]);

const filterParams = useMemo(() => {
const { codeId } = location.state || {};
Expand Down
23 changes: 21 additions & 2 deletions idea/frontend/src/features/voucher/hooks/use-voucher-filters.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
import { useAccount } from '@gear-js/react-hooks';
import { useMemo, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';

import { DEFAULT_FILTER_VALUES } from '../consts';
import { useSearchParams } from 'react-router-dom';

function useVoucherFilters() {
const { account } = useAccount();
const [values, setValues] = useState(DEFAULT_FILTER_VALUES);
const [searchParams, setSearchParams] = useSearchParams();
const [values, setValues] = useState(() => {
const params = Object.fromEntries(searchParams.entries());
return {
owner: params.owner || DEFAULT_FILTER_VALUES.owner,
status: params.status || DEFAULT_FILTER_VALUES.status,
};
});

useEffect(() => {
Object.entries(values).forEach(([key, value]) => {
if (key in DEFAULT_FILTER_VALUES && value) {
searchParams.set(key, value);
} else {
searchParams.delete(key);
}
});
setSearchParams(searchParams, { replace: true });
}, [values]);

const getOwnerParams = () => {
if (!account) return {};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { HexString } from '@gear-js/api';
import { useState } from 'react';
import { useEffect, useState } from 'react';

import { isHex } from '@/shared/helpers';
import { ProgramTabLayout, SearchForm } from '@/shared/ui';
Expand All @@ -8,13 +8,24 @@ import { useVouchers } from '../../api';
import { useVoucherFilters } from '../../hooks';
import { Vouchers } from '../vouchers';
import { VoucherFilters } from '../voucher-filters';
import { useSearchParams } from 'react-router-dom';

type Props = {
programId: HexString;
};

function ProgramVouchers({ programId }: Props) {
const [searchQuery, setSearchQuery] = useState('');
const [searchParams, setSearchParams] = useSearchParams();
const [searchQuery, setSearchQuery] = useState(searchParams.get('search') || '');

useEffect(() => {
if (searchQuery) {
searchParams.set('search', searchQuery);
} else {
searchParams.delete('search');
}
setSearchParams(searchParams, { replace: true });
}, [searchQuery]);
const [filterParams, handleFiltersSubmit] = useVoucherFilters();

const [vouchers, count, isLoading, hasMore, fetchMore, refetch] = useVouchers(searchQuery, filterParams, programId);
Expand All @@ -33,12 +44,21 @@ function ProgramVouchers({ programId }: Props) {
const renderSearch = () => (
<SearchForm
placeholder="Search by id..."
query={searchQuery}
getSchema={(schema) => schema.refine((value) => isHex(value), 'Value should be hex')}
onSubmit={(query) => setSearchQuery(query)}
/>
);

const renderFilters = () => <VoucherFilters onSubmit={handleFiltersSubmit} />;
const renderFilters = () => (
<VoucherFilters
onSubmit={handleFiltersSubmit}
values={{
owner: filterParams.owner ? 'by' : filterParams.spender ? 'to' : 'all',
status: filterParams.declined ? 'declined' : filterParams.expired ? 'expired' : 'active',
}}
/>
);

return (
<ProgramTabLayout
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import { useAccount } from '@gear-js/react-hooks';

type Props = {
onSubmit: (values: typeof DEFAULT_FILTER_VALUES) => void;
values: typeof DEFAULT_FILTER_VALUES;
};

function VoucherFilters({ onSubmit }: Props) {
function VoucherFilters({ onSubmit, values }: Props) {
const { account } = useAccount();

return (
<Filters initialValues={DEFAULT_FILTER_VALUES} onSubmit={onSubmit}>
<Filters initialValues={DEFAULT_FILTER_VALUES} values={values} onSubmit={onSubmit}>
<FilterGroup name="owner" onSubmit={onSubmit}>
<Radio name="owner" value="all" label="All vouchers" onSubmit={onSubmit} />

Expand Down
31 changes: 26 additions & 5 deletions idea/frontend/src/pages/codes/ui/Codes.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useAccount } from '@gear-js/react-hooks';
import { useState } from 'react';
import { useEffect, useState } from 'react';

import { CodeCard, useCodes, Code } from '@/features/code';
import { LocalCode, useLocalCodes } from '@/features/local-indexer';
Expand All @@ -10,6 +10,7 @@ import { List, SearchForm, Skeleton } from '@/shared/ui';
import CardPlaceholderSVG from '@/shared/assets/images/placeholders/card.svg?react';

import styles from './Codes.module.scss';
import { useSearchParams } from 'react-router-dom';

const DEFAULT_FILTER_VALUES = {
owner: 'all',
Expand All @@ -18,9 +19,29 @@ const DEFAULT_FILTER_VALUES = {
const Codes = () => {
const { account } = useAccount();
const { isDevChain } = useChain();
const [searchParams, setSearchParams] = useSearchParams();

const [searchQuery, setSearchQuery] = useState('');
const [filterValues, setFilterValues] = useState(DEFAULT_FILTER_VALUES);
const [searchQuery, setSearchQuery] = useState(searchParams.get('search') || '');
const [filterValues, setFilterValues] = useState(() => {
return {
owner: searchParams.get('owner') || DEFAULT_FILTER_VALUES.owner,
};
});

useEffect(() => {
if (searchQuery) {
searchParams.set('search', searchQuery);
} else {
searchParams.delete('search');
}

if (filterValues.owner) {
searchParams.set('owner', filterValues.owner);
} else {
searchParams.delete('owner');
}
setSearchParams(searchParams, { replace: true });
}, [searchQuery, filterValues]);

const filterParams = {
query: searchQuery,
Expand All @@ -38,7 +59,7 @@ const Codes = () => {
<div className={styles.container}>
<h2 className={styles.heading}>Codes: {codes.data?.count}</h2>

<SearchForm placeholder="Search by name, id..." onSubmit={setSearchQuery} />
<SearchForm placeholder="Search by name, id..." query={searchQuery} onSubmit={setSearchQuery} />

<List
items={codes.data?.result}
Expand All @@ -54,7 +75,7 @@ const Codes = () => {
/>

{!isDevChain && (
<Filters initialValues={DEFAULT_FILTER_VALUES} onSubmit={setFilterValues}>
<Filters initialValues={DEFAULT_FILTER_VALUES} values={filterValues} onSubmit={setFilterValues}>
<FilterGroup name="owner" onSubmit={setFilterValues}>
<Radio name="owner" value="all" label="All codes" onSubmit={setFilterValues} />

Expand Down
16 changes: 14 additions & 2 deletions idea/frontend/src/pages/mailbox/ui/Mailbox.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { HexString } from '@polkadot/util/types';
import { isHex } from '@polkadot/util';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import SimpleBar from 'simplebar-react';

import { useMailbox } from '@/features/mailbox';
Expand All @@ -10,12 +10,23 @@ import { SearchForm } from '@/shared/ui';
import { Messages } from './messages';
import { MessagesPlaceholder } from './messagesPlaceholder';
import styles from './Mailbox.module.scss';
import { useSearchParams } from 'react-router-dom';

const Mailbox = () => {
const claimMessage = useMessageClaim();
const { mailbox, removeMessage } = useMailbox();

const [searchQuery, setSearchQuery] = useState('');
const [searchParams, setSearchParams] = useSearchParams();
const [searchQuery, setSearchQuery] = useState(searchParams.get('search') || '');

useEffect(() => {
if (searchQuery) {
searchParams.set('search', searchQuery);
} else {
searchParams.delete('search');
}
setSearchParams(searchParams, { replace: true });
}, [searchQuery]);

const list = searchQuery ? mailbox?.filter(([message]) => message.id === searchQuery) : mailbox;
const isAnyMessage = list && list.length > 0;
Expand All @@ -34,6 +45,7 @@ const Mailbox = () => {
placeholder="Search by id..."
getSchema={(schema) => schema.refine((value) => isHex(value), 'Value should be hex')}
onSubmit={setSearchQuery}
query={searchQuery}
className={styles.form}
/>
</header>
Expand Down
13 changes: 10 additions & 3 deletions idea/frontend/src/pages/program/program.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { HexString } from '@polkadot/util/types';
import { Button } from '@gear-js/ui';
import cx from 'clsx';
import { useState } from 'react';
import { generatePath, useParams } from 'react-router-dom';
import { generatePath, useParams, useSearchParams } from 'react-router-dom';

import { useModal } from '@/hooks';
import { ProgramStatus, ProgramTable, useProgram } from '@/features/program';
Expand All @@ -28,6 +28,7 @@ type Params = {

const Program = () => {
const { programId } = useParams() as Params;
const [searchParams, setSearchParams] = useSearchParams({ tab: '0' });
const { showModal, closeModal } = useModal();

const { data: program, isLoading: isProgramLoading, refetch: refetchProgram } = useProgram(programId);
Expand All @@ -36,7 +37,7 @@ const Program = () => {
const isLoading = !isMetadataReady || isSailsLoading;
const isAnyQuery = sails ? Object.values(sails.services).some(({ queries }) => isAnyKey(queries)) : false;

const [tabIndex, setTabIndex] = useState(0);
const [tabIndex, setTabIndex] = useState(parseInt(searchParams.get('tab') || '0'));

const openUploadMetadataModal = () => {
if (!program) throw new Error('Program is not found');
Expand Down Expand Up @@ -67,7 +68,13 @@ const Program = () => {
<button
key={tab}
type="button"
onClick={() => setTabIndex(index)}
onClick={() =>
setTabIndex(() => {
searchParams.set('tab', index.toString());
setSearchParams(searchParams, { replace: true });
return index;
})
}
className={cx(styles.button, index === tabIndex && styles.active)}>
{tab}
</button>
Expand Down
17 changes: 14 additions & 3 deletions idea/frontend/src/pages/programs/ui/programsPage/ProgramsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
import { useState } from 'react';
import { useEffect, useState } from 'react';

import { ProgramFilters, Programs, useProgramFilters, usePrograms } from '@/features/program';
import { SearchForm } from '@/shared/ui';

import styles from './ProgramsPage.module.scss';
import { useSearchParams } from 'react-router-dom';

const ProgramsPage = () => {
const [searchQuery, setSearchQuery] = useState('');
const [searchParams, setSearchParams] = useSearchParams();
const [searchQuery, setSearchQuery] = useState(searchParams.get('search') || '');

useEffect(() => {
if (searchQuery) {
searchParams.set('search', searchQuery);
} else {
searchParams.delete('search');
}
setSearchParams(searchParams, { replace: true });
}, [searchQuery]);
const [defaultFilterValues, handleFiltersSubmit, filterParams] = useProgramFilters(searchQuery);
const programs = usePrograms(filterParams);

return (
<div className={styles.container}>
<h2 className={styles.heading}>Programs: {programs.data?.count}</h2>

<SearchForm placeholder="Search by name, code hash, id..." onSubmit={setSearchQuery} />
<SearchForm placeholder="Search by name, code hash, id..." query={searchQuery} onSubmit={setSearchQuery} />

<Programs
items={programs.data?.result}
Expand Down
Loading