{entitySchema === 'person' && entity.datasets?.length ? (
-
+
{t('screenings:match.datasets.title')}
@@ -56,7 +62,7 @@ export function FreeformMatchCard({ entity, defaultOpen, searchTerm }: FreeformM
) : null}
-
+
@@ -65,7 +71,17 @@ export function FreeformMatchCard({ entity, defaultOpen, searchTerm }: FreeformM
export default FreeformMatchCard;
-function DataContent({ entityId, searchTerm, isOpen }: { entityId: string; searchTerm?: string; isOpen: boolean }) {
+export function FreeFormMatchCardDataContent({
+ entityId,
+ searchTerm,
+ isOpen,
+ withTopics = false,
+}: {
+ entityId: string;
+ searchTerm?: string;
+ isOpen: boolean;
+ withTopics?: boolean;
+}) {
const { t } = useTranslation(screeningsI18n);
const enrichedData = useGetEnrichedDataQuery({ entityId }, isOpen);
if (enrichedData.isLoading) return
;
@@ -74,6 +90,7 @@ function DataContent({ entityId, searchTerm, isOpen }: { entityId: string; searc
if (!entity) return
{t('screenings:match.enriched_data_error')}
;
return (
+ {withTopics && }
);
diff --git a/packages/app-builder/src/components/Screenings/FreeformSearch/FreeformSearchForm.tsx b/packages/app-builder/src/components/Screenings/FreeformSearch/FreeformSearchForm.tsx
index d6e9b09fd3..0ee23c402d 100644
--- a/packages/app-builder/src/components/Screenings/FreeformSearch/FreeformSearchForm.tsx
+++ b/packages/app-builder/src/components/Screenings/FreeformSearch/FreeformSearchForm.tsx
@@ -28,7 +28,10 @@ import { EntitySearchFormProvider } from './entity-search-form-context';
import { DEFAULT_LIMIT, LimitPopover } from './LimitPopover';
interface FreeformSearchFormProps {
- onSearchComplete: (results: ScreeningMatchPayload[], searchInputs: FreeformSearchInput) => void;
+ onSearchComplete: (
+ result: { id: string; matches: ScreeningMatchPayload[] },
+ searchInputs: FreeformSearchInput,
+ ) => void;
listConfig: ListConfigFilters;
}
diff --git a/packages/app-builder/src/components/Screenings/FreeformSearch/FreeformSearchPage.tsx b/packages/app-builder/src/components/Screenings/FreeformSearch/FreeformSearchPage.tsx
index 5ed8fc9965..ce11bd99bd 100644
--- a/packages/app-builder/src/components/Screenings/FreeformSearch/FreeformSearchPage.tsx
+++ b/packages/app-builder/src/components/Screenings/FreeformSearch/FreeformSearchPage.tsx
@@ -8,6 +8,7 @@ import { FreeformSearchForm } from './FreeformSearchForm';
import { FreeformSearchResults } from './FreeformSearchResults';
export interface FreeformSearchState {
+ searchId: string;
results: ScreeningMatchPayload[];
inputs: {
entityType: SearchableSchema;
@@ -29,13 +30,14 @@ export const FreeformSearchPage: FunctionComponent
= ({
const [searchTerm, setSearchTerm] = useState(undefined);
const handleSearchComplete = useCallback(
- (data: ScreeningMatchPayload[], inputs: FreeformSearchInput) => {
- setResults(data);
+ (result: { id: string; matches: ScreeningMatchPayload[] }, inputs: FreeformSearchInput) => {
+ setResults(result.matches);
setCurrentLimit(inputs.limit);
// Extract the 'name' field value as the search term for highlighting
setSearchTerm(inputs.fields.name as string | undefined);
onSearchComplete?.({
- results: data,
+ searchId: result.id,
+ results: result.matches,
inputs: {
entityType: inputs.entityType,
fields: inputs.fields as Record,
diff --git a/packages/app-builder/src/components/Screenings/FreeformSearch/SaveSearch.tsx b/packages/app-builder/src/components/Screenings/FreeformSearch/SaveSearch.tsx
index 3b2b567cb2..a356dcb0fb 100644
--- a/packages/app-builder/src/components/Screenings/FreeformSearch/SaveSearch.tsx
+++ b/packages/app-builder/src/components/Screenings/FreeformSearch/SaveSearch.tsx
@@ -2,11 +2,11 @@ import { FormErrorOrDescription } from '@app-builder/components/Form/Tanstack/Fo
import { FormInput } from '@app-builder/components/Form/Tanstack/FormInput';
import { FormLabel } from '@app-builder/components/Form/Tanstack/FormLabel';
import { useEntityName } from '@app-builder/hooks/useEntityName';
-import { useSaveFreeformSearchMutation } from '@app-builder/queries/screening/freeform-search';
+// import { useSaveFreeformSearchMutation } from '@app-builder/queries/screening/freeform-search';
import { getFieldErrors, handleSubmit } from '@app-builder/utils/form';
import { useForm } from '@tanstack/react-form';
import { useState } from 'react';
-import toast from 'react-hot-toast';
+// import toast from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
import { Button, Modal } from 'ui-design-system';
import { Icon } from 'ui-icons';
@@ -20,29 +20,29 @@ const saveSearchFormSchema = z.object({
export const SaveSearch = ({ search }: { search: FreeformSearchState }) => {
const { t } = useTranslation(['screenings', 'common']);
const [open, setOpen] = useState(false);
- const saveSearchMutation = useSaveFreeformSearchMutation();
+ // const saveSearchMutation = useSaveFreeformSearchMutation();
const { getEntityName } = useEntityName();
const form = useForm({
defaultValues: { name: '' },
onSubmit: ({ value, formApi }) => {
if (!formApi.state.isValid) return;
- saveSearchMutation
- .mutateAsync({
- name: value.name,
- inputs: search.inputs,
- results: search.results,
- })
- .then((res) => {
- if (res.success) {
- toast.success(t('screenings:freeform_search.save.success'));
- setOpen(false);
- form.reset();
- } else {
- toast.error(t('common:errors.unknown'));
- }
- })
- .catch(() => toast.error(t('common:errors.unknown')));
+ // saveSearchMutation
+ // .mutateAsync({
+ // name: value.name,
+ // inputs: search.inputs,
+ // results: search.results,
+ // })
+ // .then((res) => {
+ // if (res.success) {
+ // toast.success(t('screenings:freeform_search.save.success'));
+ // setOpen(false);
+ // form.reset();
+ // } else {
+ // toast.error(t('common:errors.unknown'));
+ // }
+ // })
+ // .catch(() => toast.error(t('common:errors.unknown')));
},
validators: {
onSubmit: saveSearchFormSchema,
@@ -124,10 +124,10 @@ export const SaveSearch = ({ search }: { search: FreeformSearchState }) => {
{t('common:cancel')}
- */}
diff --git a/packages/app-builder/src/components/Screenings/FreeformSearch/ViewSavedResults.tsx b/packages/app-builder/src/components/Screenings/FreeformSearch/ViewSavedResults.tsx
index b562a213ff..c7360674b6 100644
--- a/packages/app-builder/src/components/Screenings/FreeformSearch/ViewSavedResults.tsx
+++ b/packages/app-builder/src/components/Screenings/FreeformSearch/ViewSavedResults.tsx
@@ -1,27 +1,24 @@
+import { CursorPaginationButtons, usePaginationsButton } from '@app-builder/components/Decisions/PaginationButtons';
import { DateRangeFilter } from '@app-builder/components/Filters';
import { PanelContainer, PanelContent, PanelFooter, PanelRoot } from '@app-builder/components/Panel/Panel';
+import { IconDot, SEARCH_ENTITIES, SearchableSchema } from '@app-builder/constants/screening-entity';
+import { type PaginationParams } from '@app-builder/models/pagination';
import { type SavedScreeningSearch } from '@app-builder/models/screening';
-import { useSavedFreeformSearchesQuery } from '@app-builder/queries/screening/freeform-search';
+import {
+ useGetFreeformSearchQuery,
+ useSavedFreeformSearchesQuery,
+} from '@app-builder/queries/screening/freeform-search';
import { useOrganizationDetails } from '@app-builder/services/organization/organization-detail';
import { useOrganizationUsers } from '@app-builder/services/organization/organization-users';
import { formatDateTimeWithoutPresets, formatDuration, useFormatLanguage } from '@app-builder/utils/format';
+import { omitUndefined } from '@app-builder/utils/omit-undefined';
import { useDebouncedCallbackRef } from '@marble/shared';
-import { type ReactNode, useMemo, useState } from 'react';
+import { ScreeningConfigBodySectionDto } from 'marble-api';
+import { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
-import { Temporal } from 'temporal-polyfill';
-import {
- Avatar,
- Button,
- Collapsible,
- cn,
- ExpandableGroupTagLine,
- Input,
- MenuCommand,
- Separator,
- Tag,
-} from 'ui-design-system';
+import { Avatar, Button, Collapsible, cn, Input, MenuCommand, Separator, Tag } from 'ui-design-system';
import { Icon } from 'ui-icons';
-import { FreeformMatchCard } from './FreeformMatchCard';
+import FreeformMatchCard from './FreeformMatchCard';
interface StaticDateRangeFilter {
type: 'static';
@@ -34,17 +31,17 @@ interface DynamicDateRangeFilter {
}
type DateRangeFilterValue = StaticDateRangeFilter | DynamicDateRangeFilter | null;
-function toIsoRange(value: DateRangeFilterValue): { fromDate?: string; toDate?: string } {
- if (!value) return {};
- if (value.type === 'static') {
- return { fromDate: value.startDate || undefined, toDate: value.endDate || undefined };
- }
- const now = Temporal.Now.zonedDateTimeISO();
- return {
- fromDate: now.add(value.fromNow).toInstant().toString(),
- toDate: now.toInstant().toString(),
- };
-}
+// function toIsoRange(value: DateRangeFilterValue): { fromDate?: string; toDate?: string } {
+// if (!value) return {};
+// if (value.type === 'static') {
+// return { fromDate: value.startDate || undefined, toDate: value.endDate || undefined };
+// }
+// const now = Temporal.Now.zonedDateTimeISO();
+// return {
+// fromDate: now.add(value.fromNow).toInstant().toString(),
+// toDate: now.toInstant().toString(),
+// };
+// }
const PAGE_SIZES = [25, 50, 100] as const;
type PageSize = (typeof PAGE_SIZES)[number];
@@ -54,36 +51,49 @@ export const ViewSavedResults = () => {
const [open, setOpen] = useState(false);
const [nameInput, setNameInput] = useState('');
- const [name, setName] = useState('');
+ // const [name, setName] = useState('');
const [dateRange, setDateRange] = useState(null);
const [ownerId, setOwnerId] = useState(undefined);
- const [page, setPage] = useState(1);
- const [limit, setLimit] = useState(25);
+ const [paginationParams, setPaginationParams] = useState({ limit: 25 });
+
+ const filterValues = useMemo(
+ () =>
+ omitUndefined({
+ userId: ownerId,
+ isSaved: true,
+ }),
+ [ownerId],
+ );
- const applyName = useDebouncedCallbackRef((value: string) => {
- setName(value);
- setPage(1);
+ const applyName = useDebouncedCallbackRef((_value: string) => {
+ // setName(value);
+ setPaginationParams((prev) => ({ limit: prev.limit ?? 25 }));
}, 300);
- const { fromDate, toDate } = useMemo(() => toIsoRange(dateRange), [dateRange]);
+ const resetPagination = () => {
+ setPaginationParams((prev) => ({ limit: prev.limit ?? 25 }));
+ };
- const query = useSavedFreeformSearchesQuery({
- name: name || undefined,
- fromDate,
- toDate,
- ownerId,
- page,
- limit,
- });
+ // const { fromDate, toDate } = useMemo(() => toIsoRange(dateRange), [dateRange]);
+ const query = useSavedFreeformSearchesQuery(
+ omitUndefined({
+ ...filterValues,
+ ...paginationParams,
+ }),
+ );
const data = query.data?.success ? query.data.data : undefined;
- const items = data?.items ?? [];
- const total = data?.total ?? 0;
+ const items = data?.data ?? [];
+ const hasNextPage = data?.has_next_page ?? false;
+ const limit = (paginationParams.limit ?? 25) as PageSize;
- const rangeStart = items.length > 0 ? (page - 1) * limit + 1 : 0;
- const rangeEnd = (page - 1) * limit + items.length;
- const hasPrev = page > 1;
- const hasNext = page * limit < total;
+ const paginationItems = useMemo(() => items.map((item) => ({ id: item.id, createdAt: item.created_at })), [items]);
+
+ const paginationState = usePaginationsButton({
+ filterValues,
+ items: paginationItems,
+ initialOffsetId: paginationParams.offsetId,
+ });
return (
<>
@@ -121,14 +131,14 @@ export const ViewSavedResults = () => {
value={dateRange}
onChange={(v) => {
setDateRange(v);
- setPage(1);
+ resetPagination();
}}
/>
{
setOwnerId(v);
- setPage(1);
+ resetPagination();
}}
/>
@@ -158,19 +168,25 @@ export const ViewSavedResults = () => {