From ac221585af123192527044ab12976d031451efd4 Mon Sep 17 00:00:00 2001 From: ncomerci Date: Fri, 27 Oct 2023 14:38:44 -0300 Subject: [PATCH 1/3] feat: relevance sort in search --- src/back/routes/proposal.ts | 14 ++++---- src/clients/Governance.ts | 18 ++-------- .../Modal/BidVotingModal/BidVotingModal.tsx | 4 +-- src/components/Search/SortingMenu.tsx | 19 ++++++++--- src/entities/Proposal/model.ts | 34 +++++++------------ src/entities/Proposal/types.ts | 32 +++++++++++++++++ src/hooks/useProposalsSearchParams.ts | 6 ++-- src/intl/en.json | 3 +- src/pages/index.tsx | 4 +-- 9 files changed, 80 insertions(+), 54 deletions(-) diff --git a/src/back/routes/proposal.ts b/src/back/routes/proposal.ts index c06c3283b..16d53cd43 100644 --- a/src/back/routes/proposal.ts +++ b/src/back/routes/proposal.ts @@ -16,7 +16,7 @@ import CoauthorModel from '../../entities/Coauthor/model' import { CoauthorStatus } from '../../entities/Coauthor/types' import isDAOCommittee from '../../entities/Committee/isDAOCommittee' import { hasOpenSlots } from '../../entities/Committee/utils' -import { GrantRequest, getGrantRequestSchema } from '../../entities/Grant/types' +import { GrantRequest, SubtypeOptions, getGrantRequestSchema } from '../../entities/Grant/types' import { SUBMISSION_THRESHOLD_DRAFT, SUBMISSION_THRESHOLD_GOVERNANCE, @@ -49,6 +49,7 @@ import { ProposalRequiredVP, ProposalStatus, ProposalType, + SortingOrder, UpdateProposalStatusProposal, newProposalBanNameScheme, newProposalCatalystScheme, @@ -60,6 +61,7 @@ import { newProposalPitchScheme, newProposalPollScheme, newProposalTenderScheme, + toSearchSorting, updateProposalStatusScheme, } from '../../entities/Proposal/types' import { @@ -118,15 +120,15 @@ export default routes((route) => { export async function getProposals(req: WithAuth) { const query = req.query - const type = query.type && String(query.type) - const subtype = query.subtype && String(query.subtype) - const status = query.status && String(query.status) + const type = query.type ? (String(query.type) as ProposalType) : undefined + const subtype = query.subtype ? (String(query.subtype) as SubtypeOptions) : undefined + const status = query.status ? (String(query.status) as ProposalStatus) : undefined const user = query.user && String(query.user) const search = query.search && String(query.search) const timeFrame = query.timeFrame && String(query.timeFrame) const timeFrameKey = query.timeFrameKey && String(query.timeFrameKey) const coauthor = (query.coauthor && Boolean(query.coauthor)) || false - const order = query.order && String(query.order) === 'ASC' ? 'ASC' : 'DESC' + const order = toSearchSorting(String(query.order)) const snapshotIds = query.snapshotIds && String(query.snapshotIds) const subscribed = query.subscribed ? req.auth || '' : undefined const offset = query.offset && Number.isFinite(Number(query.offset)) ? Number(query.offset) : MIN_PROPOSAL_OFFSET @@ -436,7 +438,7 @@ export async function createProposalTender(req: WithAuth) { const tenderProposals = await ProposalModel.getProposalList({ linkedProposalId: configuration.linked_proposal_id, - order: 'ASC', + order: SortingOrder.ASC, }) if (hasTenderProcessFinished(tenderProposals)) { diff --git a/src/clients/Governance.ts b/src/clients/Governance.ts index 22ae4608b..44a552966 100644 --- a/src/clients/Governance.ts +++ b/src/clients/Governance.ts @@ -10,9 +10,10 @@ import { BadgeCreationResult, RevokeOrReinstateResult, UserBadges } from '../ent import { BidRequest, UnpublishedBidAttributes } from '../entities/Bid/types' import { Budget, BudgetWithContestants, CategoryBudget } from '../entities/Budget/types' import { CoauthorAttributes, CoauthorStatus } from '../entities/Coauthor/types' -import { GrantRequest, ProposalGrantCategory, SubtypeOptions } from '../entities/Grant/types' +import { GrantRequest, ProposalGrantCategory } from '../entities/Grant/types' import { CategorizedGrants, + FilterProposalList, NewProposalBanName, NewProposalCatalyst, NewProposalDraft, @@ -28,7 +29,6 @@ import { ProposalAttributes, ProposalCommentsInDiscourse, ProposalStatus, - ProposalType, } from '../entities/Proposal/types' import { QuarterBudgetAttributes } from '../entities/QuarterBudget/types' import { SubscriptionAttributes } from '../entities/Subscription/types' @@ -66,21 +66,9 @@ type NewProposalMap = { [`/proposals/hiring`]: NewProposalHiring } -export type GetProposalsFilter = { - user: string - type: ProposalType - subtype?: SubtypeOptions - status: ProposalStatus - subscribed: boolean | string - coauthor: boolean - search?: string | null - timeFrame?: string | null - timeFrameKey?: string | null - order?: 'ASC' | 'DESC' +export type GetProposalsFilter = FilterProposalList & { limit: number offset: number - snapshotIds?: string - linkedProposalId?: string } const getGovernanceApiUrl = () => { diff --git a/src/components/Modal/BidVotingModal/BidVotingModal.tsx b/src/components/Modal/BidVotingModal/BidVotingModal.tsx index 9d3267c16..b72c97d83 100644 --- a/src/components/Modal/BidVotingModal/BidVotingModal.tsx +++ b/src/components/Modal/BidVotingModal/BidVotingModal.tsx @@ -3,7 +3,7 @@ import { Close } from 'decentraland-ui/dist/components/Close/Close' import { Header } from 'decentraland-ui/dist/components/Header/Header' import { Modal, ModalProps } from 'decentraland-ui/dist/components/Modal/Modal' -import { ProposalAttributes, ProposalType } from '../../../entities/Proposal/types' +import { ProposalAttributes, ProposalType, SortingOrder } from '../../../entities/Proposal/types' import { SelectedVoteChoice } from '../../../entities/Votes/types' import useFormatMessage from '../../../hooks/useFormatMessage' import useProposals from '../../../hooks/useProposals' @@ -28,7 +28,7 @@ function BidVotingModal({ onCastVote, castingVote, linkedTenderId, proposalPageS const { proposals } = useProposals({ type: ProposalType.Bid, linkedProposalId: linkedTenderId, - order: 'ASC', + order: SortingOrder.ASC, }) const { selectedChoice, showBidVotingModal, showVotingError, retryTimer, showSnapshotRedirect } = proposalPageState const { snapshot_id: currentProposal } = proposal diff --git a/src/components/Search/SortingMenu.tsx b/src/components/Search/SortingMenu.tsx index f964267a8..0c2193aaa 100644 --- a/src/components/Search/SortingMenu.tsx +++ b/src/components/Search/SortingMenu.tsx @@ -5,6 +5,7 @@ import classNames from 'classnames' import { Dropdown } from 'decentraland-ui/dist/components/Dropdown/Dropdown' import { useMobileMediaQuery } from 'decentraland-ui/dist/components/Media/Media' +import { SortingOrder, toSearchSorting } from '../../entities/Proposal/types' import { getUrlFilters } from '../../helpers' import useFormatMessage from '../../hooks/useFormatMessage' import { navigate } from '../../utils/locations' @@ -16,8 +17,12 @@ const SORT_KEY = 'order' export default function SortingMenu() { const location = useLocation() const params = useMemo(() => new URLSearchParams(location.search), [location.search]) - const order = useMemo(() => (params.get('order') === 'ASC' ? 'ASC' : 'DESC'), [params]) - const arrowDirection = useMemo(() => (order === 'ASC' ? 'Upwards' : 'Downwards'), [order]) + const isSearching = useMemo(() => !!params.get('search'), [params]) + const order = useMemo( + () => toSearchSorting(params.get('order')) ?? (isSearching ? 'RELEVANCE' : SortingOrder.DESC), + [isSearching, params] + ) + const arrowDirection = useMemo(() => (order === SortingOrder.ASC ? 'Upwards' : 'Downwards'), [order]) const isMobile = useMobileMediaQuery() const t = useFormatMessage() @@ -29,13 +34,19 @@ export default function SortingMenu() { text={t(`navigation.search.sorting.${order}`) || ''} > + {isSearching && ( + navigate(getUrlFilters(SORT_KEY, params, 'RELEVANCE'))} + /> + )} navigate(getUrlFilters(SORT_KEY, params, 'DESC'))} + onClick={() => navigate(getUrlFilters(SORT_KEY, params, SortingOrder.DESC))} /> navigate(getUrlFilters(SORT_KEY, params, 'ASC'))} + onClick={() => navigate(getUrlFilters(SORT_KEY, params, SortingOrder.ASC))} /> diff --git a/src/entities/Proposal/model.ts b/src/entities/Proposal/model.ts index 83b81d555..310e1273d 100644 --- a/src/entities/Proposal/model.ts +++ b/src/entities/Proposal/model.ts @@ -21,31 +21,23 @@ import { OldGrantCategory, SubtypeAlternativeOptions, isGrantSubtype } from '../ import SubscriptionModel from '../Subscription/model' import tsquery from './tsquery' -import { ProposalAttributes, ProposalStatus, ProposalType, isProposalType } from './types' +import { + FilterProposalList, + ProposalAttributes, + ProposalStatus, + ProposalType, + SortingOrder, + isProposalType, +} from './types' import { SITEMAP_ITEMS_PER_PAGE, isProposalStatus } from './utils' -export type FilterProposalList = { - type: string - subtype?: string - user: string - status: string - subscribed: string - coauthor: boolean - search?: string - timeFrame?: string - timeFrameKey?: string - order?: 'ASC' | 'DESC' - snapshotIds?: string - linkedProposalId?: string -} - export type FilterPagination = { limit: number offset: number } const VALID_TIMEFRAME_KEYS = ['created_at', 'finish_at'] -const VALID_ORDER_DIRECTION = ['ASC', 'DESC'] +const VALID_ORDER_DIRECTION = Object.values(SortingOrder) export default class ProposalModel extends Model { static tableName = 'proposals' @@ -245,7 +237,7 @@ export default class ProposalModel extends Model { return 0 } - if (subscribed && !isEthereumAddress(subscribed)) { + if (subscribed && !isEthereumAddress(String(subscribed))) { return 0 } @@ -336,7 +328,7 @@ export default class ProposalModel extends Model { return [] } - if (subscribed && !isEthereumAddress(subscribed)) { + if (subscribed && !isEthereumAddress(String(subscribed))) { return [] } @@ -358,13 +350,13 @@ export default class ProposalModel extends Model { const timeFrame = this.parseTimeframe(filter.timeFrame) const timeFrameKey = filter.timeFrameKey || 'created_at' - const orderDirection = order || 'DESC' + const orderDirection = !order || order === 'RELEVANCE' ? SortingOrder.DESC : order if (!VALID_TIMEFRAME_KEYS.includes(timeFrameKey) || !VALID_ORDER_DIRECTION.includes(orderDirection)) { return [] } - const orderBy = search ? '"rank"' : `p.${timeFrameKey}` + const orderBy = search && (!order || order === 'RELEVANCE') ? '"rank"' : `p.${timeFrameKey}` const sqlSnapshotIds = snapshotIds?.split(',').map((id) => SQL`${id}`) const sqlSnapshotIdsJoin = sqlSnapshotIds ? join(sqlSnapshotIds) : null diff --git a/src/entities/Proposal/types.ts b/src/entities/Proposal/types.ts index 639bccc7b..12d62a840 100644 --- a/src/entities/Proposal/types.ts +++ b/src/entities/Proposal/types.ts @@ -14,6 +14,7 @@ import { PaymentToken, ProjectStatus, ProposalGrantCategory, + SubtypeOptions, VestingStartDate, } from '../Grant/types' import { IndexedUpdate } from '../Updates/types' @@ -68,6 +69,21 @@ export type ProposalAttributes = any> = { textsearch: SQLStatement | string | null | undefined } +export type FilterProposalList = { + user: string + type: ProposalType + subtype?: SubtypeOptions + status: ProposalStatus + subscribed: boolean | string + coauthor: boolean + search?: string | null + timeFrame?: string | null + timeFrameKey?: string | null + order?: SortingOrder | 'RELEVANCE' + snapshotIds?: string + linkedProposalId?: string +} + export enum ProposalStatus { Pending = 'pending', Active = 'active', @@ -94,6 +110,11 @@ export enum ProposalType { Bid = 'bid', } +export enum SortingOrder { + ASC = 'ASC', + DESC = 'DESC', +} + export type GovernanceProcessType = ProposalType.Poll | ProposalType.Draft | ProposalType.Governance export type BiddingProcessType = ProposalType.Pitch | ProposalType.Tender | ProposalType.Bid @@ -152,6 +173,17 @@ export function toPoiType(value: string | null | undefined): PoiType | null { return isPoiType(value) ? (value as PoiType) : null } +export function toSearchSorting(value: string | null | undefined): 'RELEVANCE' | SortingOrder | undefined { + switch (value) { + case 'RELEVANCE': + case SortingOrder.ASC: + case SortingOrder.DESC: + return value + default: + return undefined + } +} + export function getPoiTypeAction(poiType: PoiType) { return poiType.split('_')[0] // "add" | "remove" } diff --git a/src/hooks/useProposalsSearchParams.ts b/src/hooks/useProposalsSearchParams.ts index 2680d7050..fd4131781 100644 --- a/src/hooks/useProposalsSearchParams.ts +++ b/src/hooks/useProposalsSearchParams.ts @@ -1,7 +1,7 @@ import { useMemo } from 'react' import { SubtypeOptions, toGrantSubtype } from '../entities/Grant/types' -import { ProposalStatus, ProposalType, toProposalType } from '../entities/Proposal/types' +import { ProposalStatus, ProposalType, SortingOrder, toProposalType, toSearchSorting } from '../entities/Proposal/types' import { toProposalStatus } from '../entities/Proposal/utils' import { toProposalListPage } from '../utils/locations' @@ -14,7 +14,7 @@ export type SearchParams = { search: string searching: boolean timeFrame: string - order: 'ASC' | 'DESC' | undefined + order?: SortingOrder | 'RELEVANCE' page: number } @@ -27,7 +27,7 @@ export function useProposalsSearchParams(): SearchParams { const status = toProposalStatus(params.get('status'), () => undefined) const search = params.get('search') || '' const timeFrame = params.get('timeFrame') || '' - const order = params.get('order') ? (params.get('order') === 'ASC' ? 'ASC' : 'DESC') : undefined + const order = toSearchSorting(params.get('order')) const searching = !!search && search.length > 0 const page = toProposalListPage(params.get('page')) ?? undefined diff --git a/src/intl/en.json b/src/intl/en.json index 0bd6c385f..8199ae350 100644 --- a/src/intl/en.json +++ b/src/intl/en.json @@ -40,7 +40,8 @@ "filter": "Filter", "sorting": { "DESC": "Latest", - "ASC": "Oldest" + "ASC": "Oldest", + "RELEVANCE": "Relevance" }, "category_filter_title": "Filter by Category", "legacy_filter_title": "Filter by Legacy Grants", diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 4a7306afa..bf475d3e6 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -12,7 +12,7 @@ import OpenProposals from '../components/Home/OpenProposals' import LoadingView from '../components/Layout/LoadingView' import MaintenanceLayout from '../components/Layout/MaintenanceLayout' import Navigation, { NavigationTab } from '../components/Layout/Navigation' -import { ProposalStatus } from '../entities/Proposal/types' +import { ProposalStatus, SortingOrder } from '../entities/Proposal/types' import useFormatMessage from '../hooks/useFormatMessage' import useProposals from '../hooks/useProposals' import { isUnderMaintenance } from '../utils/maintenance' @@ -23,7 +23,7 @@ export default function HomePage() { const t = useFormatMessage() const { proposals: endingSoonProposals, isLoadingProposals } = useProposals({ - order: 'ASC', + order: SortingOrder.ASC, status: ProposalStatus.Active, timeFrameKey: 'finish_at', page: 1, From f73ceaeedaca5e5a9c3ac5fa8ebd6c00637e8601 Mon Sep 17 00:00:00 2001 From: ncomerci Date: Fri, 27 Oct 2023 16:24:36 -0300 Subject: [PATCH 2/3] requested changes --- src/back/routes/proposal.ts | 14 ++++---- src/components/Search/CategoryFilter.tsx | 2 +- src/components/Search/SortingMenu.tsx | 14 ++++---- src/entities/Grant/types.ts | 4 +-- src/entities/Proposal/model.ts | 4 +-- src/entities/Proposal/types.ts | 21 +++--------- src/entities/Proposal/utils.ts | 41 ++++++++++++++++++++++-- src/hooks/useProposalsSearchParams.ts | 12 +++---- src/pages/projects.tsx | 2 +- src/pages/submit/catalyst.tsx | 4 +-- src/pages/submit/poi.tsx | 4 +-- 11 files changed, 72 insertions(+), 50 deletions(-) diff --git a/src/back/routes/proposal.ts b/src/back/routes/proposal.ts index 16d53cd43..be03aa911 100644 --- a/src/back/routes/proposal.ts +++ b/src/back/routes/proposal.ts @@ -16,7 +16,7 @@ import CoauthorModel from '../../entities/Coauthor/model' import { CoauthorStatus } from '../../entities/Coauthor/types' import isDAOCommittee from '../../entities/Committee/isDAOCommittee' import { hasOpenSlots } from '../../entities/Committee/utils' -import { GrantRequest, SubtypeOptions, getGrantRequestSchema } from '../../entities/Grant/types' +import { GrantRequest, getGrantRequestSchema, toGrantSubtype } from '../../entities/Grant/types' import { SUBMISSION_THRESHOLD_DRAFT, SUBMISSION_THRESHOLD_GOVERNANCE, @@ -61,7 +61,6 @@ import { newProposalPitchScheme, newProposalPollScheme, newProposalTenderScheme, - toSearchSorting, updateProposalStatusScheme, } from '../../entities/Proposal/types' import { @@ -78,6 +77,9 @@ import { isValidName, isValidPointOfInterest, isValidUpdateProposalStatus, + toProposalStatus, + toProposalType, + toSortingOrder, } from '../../entities/Proposal/utils' import { SNAPSHOT_DURATION } from '../../entities/Snapshot/constants' import { validateUniqueAddresses } from '../../entities/Transparency/utils' @@ -120,15 +122,15 @@ export default routes((route) => { export async function getProposals(req: WithAuth) { const query = req.query - const type = query.type ? (String(query.type) as ProposalType) : undefined - const subtype = query.subtype ? (String(query.subtype) as SubtypeOptions) : undefined - const status = query.status ? (String(query.status) as ProposalStatus) : undefined + const type = toProposalType(String(query.type), () => undefined) + const subtype = toGrantSubtype(String(query.subtype), () => undefined) + const status = toProposalStatus(String(query.status), () => undefined) const user = query.user && String(query.user) const search = query.search && String(query.search) const timeFrame = query.timeFrame && String(query.timeFrame) const timeFrameKey = query.timeFrameKey && String(query.timeFrameKey) const coauthor = (query.coauthor && Boolean(query.coauthor)) || false - const order = toSearchSorting(String(query.order)) + const order = toSortingOrder(String(query.order), () => undefined) const snapshotIds = query.snapshotIds && String(query.snapshotIds) const subscribed = query.subscribed ? req.auth || '' : undefined const offset = query.offset && Number.isFinite(Number(query.offset)) ? Number(query.offset) : MIN_PROPOSAL_OFFSET diff --git a/src/components/Search/CategoryFilter.tsx b/src/components/Search/CategoryFilter.tsx index b3dcb3ea2..b7d214867 100644 --- a/src/components/Search/CategoryFilter.tsx +++ b/src/components/Search/CategoryFilter.tsx @@ -72,7 +72,7 @@ export default function CategoryFilter({ const t = useFormatMessage() const params = useURLSearchParams() const type = params.get(FILTER_KEY) - const currentSubtype = useMemo(() => toGrantSubtype(params.get('subtype')), [params]) + const currentSubtype = useMemo(() => toGrantSubtype(params.get('subtype'), () => null), [params]) const areProposals = isEqual(filterType, ProposalType) const filters = areProposals diff --git a/src/components/Search/SortingMenu.tsx b/src/components/Search/SortingMenu.tsx index 0c2193aaa..259c428d9 100644 --- a/src/components/Search/SortingMenu.tsx +++ b/src/components/Search/SortingMenu.tsx @@ -5,7 +5,8 @@ import classNames from 'classnames' import { Dropdown } from 'decentraland-ui/dist/components/Dropdown/Dropdown' import { useMobileMediaQuery } from 'decentraland-ui/dist/components/Media/Media' -import { SortingOrder, toSearchSorting } from '../../entities/Proposal/types' +import { SortingOrder } from '../../entities/Proposal/types' +import { toSortingOrder } from '../../entities/Proposal/utils' import { getUrlFilters } from '../../helpers' import useFormatMessage from '../../hooks/useFormatMessage' import { navigate } from '../../utils/locations' @@ -17,12 +18,9 @@ const SORT_KEY = 'order' export default function SortingMenu() { const location = useLocation() const params = useMemo(() => new URLSearchParams(location.search), [location.search]) - const isSearching = useMemo(() => !!params.get('search'), [params]) - const order = useMemo( - () => toSearchSorting(params.get('order')) ?? (isSearching ? 'RELEVANCE' : SortingOrder.DESC), - [isSearching, params] - ) - const arrowDirection = useMemo(() => (order === SortingOrder.ASC ? 'Upwards' : 'Downwards'), [order]) + const isSearching = !!params.get('search') + const order = toSortingOrder(params.get('order'), () => (isSearching ? 'RELEVANCE' : SortingOrder.DESC)) + const arrowDirection = order === SortingOrder.ASC ? 'Upwards' : 'Downwards' const isMobile = useMobileMediaQuery() const t = useFormatMessage() @@ -37,7 +35,7 @@ export default function SortingMenu() { {isSearching && ( navigate(getUrlFilters(SORT_KEY, params, 'RELEVANCE'))} + onClick={() => navigate(getUrlFilters(SORT_KEY, params, undefined))} /> )} (value: string | null | undefined, orElse: () => OrElse) { + return isGrantSubtype(value) ? (value as SubtypeOptions) : orElse() } export const GrantRequestGeneralInfoSchema = { diff --git a/src/entities/Proposal/model.ts b/src/entities/Proposal/model.ts index 310e1273d..0487b577a 100644 --- a/src/entities/Proposal/model.ts +++ b/src/entities/Proposal/model.ts @@ -350,13 +350,13 @@ export default class ProposalModel extends Model { const timeFrame = this.parseTimeframe(filter.timeFrame) const timeFrameKey = filter.timeFrameKey || 'created_at' - const orderDirection = !order || order === 'RELEVANCE' ? SortingOrder.DESC : order + const orderDirection = !order ? SortingOrder.DESC : order if (!VALID_TIMEFRAME_KEYS.includes(timeFrameKey) || !VALID_ORDER_DIRECTION.includes(orderDirection)) { return [] } - const orderBy = search && (!order || order === 'RELEVANCE') ? '"rank"' : `p.${timeFrameKey}` + const orderBy = search && !order ? '"rank"' : `p.${timeFrameKey}` const sqlSnapshotIds = snapshotIds?.split(',').map((id) => SQL`${id}`) const sqlSnapshotIdsJoin = sqlSnapshotIds ? join(sqlSnapshotIds) : null diff --git a/src/entities/Proposal/types.ts b/src/entities/Proposal/types.ts index 12d62a840..7274b29c9 100644 --- a/src/entities/Proposal/types.ts +++ b/src/entities/Proposal/types.ts @@ -79,7 +79,7 @@ export type FilterProposalList = { search?: string | null timeFrame?: string | null timeFrameKey?: string | null - order?: SortingOrder | 'RELEVANCE' + order?: SortingOrder snapshotIds?: string linkedProposalId?: string } @@ -161,26 +161,13 @@ export function isCatalystType(value: string | null | undefined): boolean { } } -export function toCatalystType(value: string | null | undefined): CatalystType | null { - return isCatalystType(value) ? (value as CatalystType) : null -} - -export function toProposalType(value: string | null | undefined): ProposalType | null { - return isProposalType(value) ? (value as ProposalType) : null -} - -export function toPoiType(value: string | null | undefined): PoiType | null { - return isPoiType(value) ? (value as PoiType) : null -} - -export function toSearchSorting(value: string | null | undefined): 'RELEVANCE' | SortingOrder | undefined { +export function isSortingOrder(value: string | null | undefined): boolean { switch (value) { - case 'RELEVANCE': case SortingOrder.ASC: case SortingOrder.DESC: - return value + return true default: - return undefined + return false } } diff --git a/src/entities/Proposal/utils.ts b/src/entities/Proposal/utils.ts index cc5cede32..2f3f84789 100644 --- a/src/entities/Proposal/utils.ts +++ b/src/entities/Proposal/utils.ts @@ -13,7 +13,18 @@ import { UpdateAttributes } from '../Updates/types' import { DISCOURSE_API } from '../User/utils' import { MAX_NAME_SIZE, MIN_NAME_SIZE } from './constants' -import { ProposalAttributes, ProposalStatus, ProposalType } from './types' +import { + CatalystType, + PoiType, + ProposalAttributes, + ProposalStatus, + ProposalType, + SortingOrder, + isCatalystType, + isPoiType, + isProposalType, + isSortingOrder, +} from './types' export const MIN_PROPOSAL_OFFSET = 0 export const MAX_PROPOSAL_LIMIT = 100 @@ -175,8 +186,32 @@ export function isProposalStatus(value: string | null | undefined): boolean { } } -export function toProposalStatus(value: string | null | undefined, orElse: () => any): ProposalStatus | any { - return isProposalStatus(value) ? (value as ProposalStatus) : orElse() +function toCustomType( + value: ValueType, + isType: (value: ValueType) => boolean, + orElse: () => OrElse +): FinalType | OrElse { + return isType(value) ? (value as unknown as FinalType) : orElse() +} + +export function toProposalStatus(value: string | null | undefined, orElse: () => OrElse) { + return toCustomType(value, isProposalStatus, orElse) +} + +export function toCatalystType(value: string | null | undefined, orElse: () => OrElse) { + return toCustomType(value, isCatalystType, orElse) +} + +export function toProposalType(value: string | null | undefined, orElse: () => OrElse) { + return toCustomType(value, isProposalType, orElse) +} + +export function toPoiType(value: string | null | undefined, orElse: () => OrElse) { + return toCustomType(value, isPoiType, orElse) +} + +export function toSortingOrder(value: string | null | undefined, orElse: () => OrElse) { + return toCustomType(value, isSortingOrder, orElse) } export function isProposalDeletable(proposalStatus?: ProposalStatus) { diff --git a/src/hooks/useProposalsSearchParams.ts b/src/hooks/useProposalsSearchParams.ts index fd4131781..0475f36bd 100644 --- a/src/hooks/useProposalsSearchParams.ts +++ b/src/hooks/useProposalsSearchParams.ts @@ -1,8 +1,8 @@ import { useMemo } from 'react' import { SubtypeOptions, toGrantSubtype } from '../entities/Grant/types' -import { ProposalStatus, ProposalType, SortingOrder, toProposalType, toSearchSorting } from '../entities/Proposal/types' -import { toProposalStatus } from '../entities/Proposal/utils' +import { ProposalStatus, ProposalType, SortingOrder } from '../entities/Proposal/types' +import { toProposalStatus, toProposalType, toSortingOrder } from '../entities/Proposal/utils' import { toProposalListPage } from '../utils/locations' import useURLSearchParams from './useURLSearchParams' @@ -14,7 +14,7 @@ export type SearchParams = { search: string searching: boolean timeFrame: string - order?: SortingOrder | 'RELEVANCE' + order?: SortingOrder page: number } @@ -22,12 +22,12 @@ export function useProposalsSearchParams(): SearchParams { const params = useURLSearchParams() return useMemo(() => { - const type = toProposalType(params.get('type')) ?? undefined - const subtype = toGrantSubtype(params.get('subtype')) ?? undefined + const type = toProposalType(params.get('type'), () => undefined) + const subtype = toGrantSubtype(params.get('subtype'), () => undefined) const status = toProposalStatus(params.get('status'), () => undefined) const search = params.get('search') || '' const timeFrame = params.get('timeFrame') || '' - const order = toSearchSorting(params.get('order')) + const order = toSortingOrder(params.get('order'), () => undefined) const searching = !!search && search.length > 0 const page = toProposalListPage(params.get('page')) ?? undefined diff --git a/src/pages/projects.tsx b/src/pages/projects.tsx index 04765304e..05441aac8 100644 --- a/src/pages/projects.tsx +++ b/src/pages/projects.tsx @@ -85,7 +85,7 @@ export default function ProjectsPage() { const params = useURLSearchParams() const type = toProjectTypeFilter(params.get('type')) const status = toProjectStatus(params.get('status')) - const subtype = toGrantSubtype(params.get('subtype')) + const subtype = toGrantSubtype(params.get('subtype'), () => undefined) const { projects, isLoadingProjects } = useProjects() const displayableProjects = useMemo( diff --git a/src/pages/submit/catalyst.tsx b/src/pages/submit/catalyst.tsx index bf1aa0b05..5c9c16a56 100644 --- a/src/pages/submit/catalyst.tsx +++ b/src/pages/submit/catalyst.tsx @@ -1,7 +1,7 @@ import NotFound from 'decentraland-gatsby/dist/components/Layout/NotFound' import ProposalSubmitCatalystPage from '../../components/Proposal/Submit/ProposalSubmitCatalystPage' -import { toCatalystType } from '../../entities/Proposal/types' +import { toCatalystType } from '../../entities/Proposal/utils' import useURLSearchParams from '../../hooks/useURLSearchParams' import './submit.css' @@ -10,7 +10,7 @@ export default function CatalystPage() { const params = useURLSearchParams() const request = params.get('request') - const catalystType = toCatalystType(request) + const catalystType = toCatalystType(request, () => null) if (catalystType !== null) { return diff --git a/src/pages/submit/poi.tsx b/src/pages/submit/poi.tsx index 313f577db..899b82278 100644 --- a/src/pages/submit/poi.tsx +++ b/src/pages/submit/poi.tsx @@ -1,7 +1,7 @@ import NotFound from 'decentraland-gatsby/dist/components/Layout/NotFound' import ProposalSubmitPoiPage from '../../components/Proposal/Submit/ProposalSubmitPoiPage' -import { toPoiType } from '../../entities/Proposal/types' +import { toPoiType } from '../../entities/Proposal/utils' import useURLSearchParams from '../../hooks/useURLSearchParams' import './submit.css' @@ -10,7 +10,7 @@ export default function Poi() { const params = useURLSearchParams() const request = params.get('request') - const poiType = toPoiType(request) + const poiType = toPoiType(request, () => null) if (poiType !== null) { return From 732b95da682572c3f0266b5b43ca6d77d3723fbc Mon Sep 17 00:00:00 2001 From: ncomerci Date: Tue, 31 Oct 2023 10:17:40 -0300 Subject: [PATCH 3/3] requested changes --- src/clients/Governance.ts | 4 ++-- src/entities/Proposal/model.ts | 8 ++++---- src/entities/Proposal/types.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/clients/Governance.ts b/src/clients/Governance.ts index 44a552966..8c44b9f57 100644 --- a/src/clients/Governance.ts +++ b/src/clients/Governance.ts @@ -13,7 +13,6 @@ import { CoauthorAttributes, CoauthorStatus } from '../entities/Coauthor/types' import { GrantRequest, ProposalGrantCategory } from '../entities/Grant/types' import { CategorizedGrants, - FilterProposalList, NewProposalBanName, NewProposalCatalyst, NewProposalDraft, @@ -28,6 +27,7 @@ import { ProjectWithUpdate, ProposalAttributes, ProposalCommentsInDiscourse, + ProposalListFilter, ProposalStatus, } from '../entities/Proposal/types' import { QuarterBudgetAttributes } from '../entities/QuarterBudget/types' @@ -66,7 +66,7 @@ type NewProposalMap = { [`/proposals/hiring`]: NewProposalHiring } -export type GetProposalsFilter = FilterProposalList & { +export type GetProposalsFilter = ProposalListFilter & { limit: number offset: number } diff --git a/src/entities/Proposal/model.ts b/src/entities/Proposal/model.ts index 9ed00a240..233e4c469 100644 --- a/src/entities/Proposal/model.ts +++ b/src/entities/Proposal/model.ts @@ -22,8 +22,8 @@ import SubscriptionModel from '../Subscription/model' import tsquery from './tsquery' import { - FilterProposalList, ProposalAttributes, + ProposalListFilter, ProposalStatus, ProposalType, SortingOrder, @@ -248,7 +248,7 @@ export default class ProposalModel extends Model { ) } - static async getProposalTotal(filter: Partial = {}): Promise { + static async getProposalTotal(filter: Partial = {}): Promise { const { user, subscribed, type, subtype, status, search, snapshotIds, coauthor, linkedProposalId } = filter if (user && !isEthereumAddress(user)) { return 0 @@ -324,7 +324,7 @@ export default class ProposalModel extends Model { } static async getProposalList( - filter: Partial = {} + filter: Partial = {} ): Promise<(ProposalAttributes & { coauthors?: string[] | null })[]> { const { user, @@ -367,7 +367,7 @@ export default class ProposalModel extends Model { const timeFrame = this.parseTimeframe(filter.timeFrame) const timeFrameKey = filter.timeFrameKey || 'created_at' - const orderDirection = !order ? SortingOrder.DESC : order + const orderDirection = order || SortingOrder.DESC if (!VALID_TIMEFRAME_KEYS.includes(timeFrameKey) || !VALID_ORDER_DIRECTION.includes(orderDirection)) { return [] diff --git a/src/entities/Proposal/types.ts b/src/entities/Proposal/types.ts index 7274b29c9..f56fc84cd 100644 --- a/src/entities/Proposal/types.ts +++ b/src/entities/Proposal/types.ts @@ -69,7 +69,7 @@ export type ProposalAttributes = any> = { textsearch: SQLStatement | string | null | undefined } -export type FilterProposalList = { +export type ProposalListFilter = { user: string type: ProposalType subtype?: SubtypeOptions