Skip to content

Commit 54e35e4

Browse files
authored
Merge pull request #131 from MetaCell/feature/ILEX-136
ILEX-136 replace elasticsearch with entity-check
2 parents d970185 + 32b4f66 commit 54e35e4

File tree

9 files changed

+405
-251
lines changed

9 files changed

+405
-251
lines changed

src/api/endpoints/apiActions.ts

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,41 @@ import { customInstance } from '../../../mock/mutator/customClient';
33

44
type SecondParameter<T extends (...args: any) => any> = Parameters<T>[1];
55

6-
export const createPostRequest = <T = any, D = any>(endpoint: string, headers : object) => {
7-
return (data?: D, options?: SecondParameter<typeof customInstance>) => {
6+
interface CustomRequestConfig extends AxiosRequestConfig {
7+
handleRedirect?: boolean;
8+
}
9+
10+
export const createPostRequest = <T = any, D = any>(endpoint: string, headers: object) => {
11+
return async (data?: D, options?: CustomRequestConfig) => {
12+
// Use fetch for handling redirect responses
13+
if (options?.handleRedirect) {
14+
const response = await fetch(endpoint, {
15+
method: 'POST',
16+
headers: {
17+
...headers,
18+
},
19+
body: JSON.stringify(data),
20+
credentials: 'include',
21+
redirect: 'manual'
22+
});
23+
24+
// Default response handling
25+
const text = await response.text();
26+
try {
27+
const json = JSON.parse(text);
28+
return {
29+
raw: json,
30+
status: response.status
31+
};
32+
} catch {
33+
return {
34+
raw: text,
35+
status: response.status
36+
};
37+
}
38+
}
39+
40+
// Default axios behavior for normal requests
841
return customInstance<T>(
942
{
1043
url: endpoint,

src/api/endpoints/apiService.ts

Lines changed: 16 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,9 @@ export const getSelectedTermLabel = async (searchTerm: string, group: string = '
9999
return label?.['@value'] || '';
100100
};
101101

102-
return {
103-
label: label ? getLabelValue(label) : undefined,
104-
actualGroup: group
102+
return {
103+
label: label ? getLabelValue(label) : undefined,
104+
actualGroup: group
105105
};
106106
} catch (err: any) {
107107
console.error(err.message);
@@ -110,7 +110,7 @@ export const getSelectedTermLabel = async (searchTerm: string, group: string = '
110110
try {
111111
const fallbackResponse = await createGetRequest<JsonLdResponse, any>(`/base/${searchTerm}.jsonld`)();
112112
const fallbackLabel = fallbackResponse['@graph']?.[0]?.['rdfs:label'];
113-
113+
114114
const getLabelValue = (label: LabelType): string => {
115115
if (typeof label === 'string') return label;
116116
if (Array.isArray(label)) {
@@ -122,9 +122,9 @@ export const getSelectedTermLabel = async (searchTerm: string, group: string = '
122122
return label?.['@value'] || '';
123123
};
124124

125-
return {
126-
label: fallbackLabel ? getLabelValue(fallbackLabel) : undefined,
127-
actualGroup: 'base'
125+
return {
126+
label: fallbackLabel ? getLabelValue(fallbackLabel) : undefined,
127+
actualGroup: 'base'
128128
};
129129
} catch (fallbackErr: any) {
130130
console.error('Fallback request also failed:', fallbackErr.message);
@@ -136,50 +136,9 @@ export const getSelectedTermLabel = async (searchTerm: string, group: string = '
136136
};
137137

138138
export const createNewEntity = async ({ group, data, session }: { group: string; data: any; session: string }) => {
139-
try {
140-
const endpoint = `/${group}${API_CONFIG.REAL_API.CREATE_NEW_ENTITY}`;
141-
const response = await createPostRequest<any, any>(
142-
endpoint,
143-
{ "Content-Type": "application/x-www-form-urlencoded" }
144-
)(data);
145-
146-
// If the response is HTML (a string), extract TMP ID
147-
if (typeof response === "string") {
148-
const match = response.match(/TMP:\d{9}/);
149-
if (match) {
150-
return {
151-
term: {
152-
id: `${match[0]}`,
153-
},
154-
raw: response,
155-
status: 200,
156-
};
157-
}
158-
}
159-
160-
// Otherwise, return response as-is
161-
return response;
162-
} catch (error) {
163-
if (error && typeof error === 'object' && 'response' in error && (error as any).response?.status === 409) {
164-
const match = (error as any)?.response?.data?.existing?.[0];
165-
if (match) {
166-
return {
167-
term: {
168-
id: `${match}`,
169-
},
170-
raw: (error as any)?.response,
171-
status: (error as any)?.response?.status,
172-
};
173-
}
174-
}
175-
176-
return {
177-
raw: (error as any)?.response,
178-
status: (error as any)?.response?.status,
179-
};
180-
}
181-
182-
};
139+
const endpoint = `/${group}${API_CONFIG.REAL_API.CREATE_NEW_ENTITY}`;
140+
return createPostRequest(endpoint, { 'Content-Type': 'application/json' })(data, { handleRedirect: true });
141+
}
183142

184143
export const createNewOntology = async ({
185144
groupname,
@@ -334,10 +293,10 @@ export const getTermDiscussions = async (group: string, variantID: string) => {
334293
};
335294

336295
export const getVariant = (group: string, term: string) => {
337-
return createGetRequest<any, any>(`/${group}/variant/${term}`, "application/json")();
296+
return createGetRequest<any, any>(`/${group}/variant/${term}`, "application/json")();
338297
};
339298

340-
export const getTermPredicates = async ({
299+
export const getTermPredicates = async ({
341300
groupname,
342301
termId,
343302
objToSub = true,
@@ -398,3 +357,7 @@ export const getTermHierarchies = async ({
398357

399358
return { triples };
400359
};
360+
361+
export const checkPotentialMatches = async (group: string, data: any) => {
362+
return createPostRequest<any, any>(`/${group}${API_CONFIG.REAL_API.CHECK_ENTITY}`, { "Content-Type": "application/json" })(data);
363+
};

src/components/TermEditor/NewTermSidebar.jsx

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import PropTypes from 'prop-types';
2+
import { useNavigate } from 'react-router-dom';
23
import { StartIcon, JoinRightIcon } from '../../Icons';
34
import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline';
45
import ArrowOutwardIcon from '@mui/icons-material/ArrowOutward';
@@ -8,9 +9,9 @@ import {
89
IconButton,
910
Tooltip,
1011
Stack,
11-
CircularProgress,
1212
Chip,
13-
Button
13+
Button,
14+
Skeleton
1415
} from '@mui/material';
1516
import { vars } from '../../theme/variables';
1617

@@ -49,17 +50,6 @@ const HOVER_INDICATOR_STYLES = {
4950
background: brand600
5051
};
5152

52-
const LoadingSpinner = () => (
53-
<Box sx={{
54-
display: 'flex',
55-
alignItems: 'center',
56-
justifyContent: 'center',
57-
width: '32rem'
58-
}}>
59-
<CircularProgress />
60-
</Box>
61-
);
62-
6353
const ExpandedHeader = ({ onToggle }) => (
6454
<Box
6555
width={1}
@@ -127,15 +117,43 @@ const EmptyState = () => (
127117
</Box>
128118
);
129119

130-
const ResultItem = ({ result, searchValue, onResultAction }) => {
120+
const ResultItemSkeleton = () => (
121+
<Box
122+
width={1}
123+
display="flex"
124+
flexDirection="column"
125+
px={1}
126+
py={1.5}
127+
gap={1}
128+
sx={{ borderBottom: `1px solid ${gray200}` }}
129+
>
130+
<Stack direction="column" spacing={1} sx={{ justifyContent: "space-between", alignItems: "flex-start" }}>
131+
<Stack direction="row" spacing={0.5}>
132+
<Skeleton variant="rounded" width={150} height={24} />
133+
<Skeleton variant="rounded" width={150} height={24} />
134+
</Stack>
135+
<Skeleton variant="rounded" width="100%" height={36} />
136+
<Stack direction="row" spacing={1}>
137+
<Skeleton variant="rounded" width={24} height={20} />
138+
<Skeleton variant="rounded" width={150} height={20} />
139+
</Stack>
140+
</Stack>
141+
</Box>
142+
);
143+
144+
const ResultItem = ({ result, searchValue, onResultAction, user }) => {
131145
const isExactMatch = result.label.toLowerCase() === searchValue?.toLowerCase();
146+
const navigate = useNavigate();
132147

133148
const getItemStyles = () => ({
134149
borderBottom: `1px solid ${gray200}`,
135150
position: 'relative',
151+
borderRadius: '0.5rem',
152+
cursor: isExactMatch ? 'default' : 'pointer',
136153
...(isExactMatch && EXACT_MATCH_STYLES),
137154
'&:hover': {
138155
...(!isExactMatch && {
156+
backgroundColor: gray200,
139157
'&:before': HOVER_INDICATOR_STYLES
140158
})
141159
}
@@ -150,6 +168,7 @@ const ResultItem = ({ result, searchValue, onResultAction }) => {
150168
px={1}
151169
py={1.5}
152170
gap={1}
171+
onClick={() => !isExactMatch && onResultAction(result)}
153172
sx={getItemStyles()}
154173
>
155174
<Stack
@@ -180,7 +199,7 @@ const ResultItem = ({ result, searchValue, onResultAction }) => {
180199
height: "auto",
181200
'&:hover': { backgroundColor: "transparent" }
182201
}}
183-
onClick={() => onResultAction(result)}
202+
onClick={() => navigate(`/${user.groupname}/${result.ilx}`)}
184203
>
185204
Go to term
186205
</Button>
@@ -211,18 +230,17 @@ export default function NewTermSidebar({
211230
results,
212231
isResultsEmpty,
213232
searchValue,
214-
onResultAction
233+
onResultAction,
234+
user
215235
}) {
216-
if (loading) {
217-
return <LoadingSpinner />;
218-
}
219236

220237
const sidebarStyles = {
221238
display: 'flex',
222239
flexDirection: 'column',
223240
borderLeft: `1px solid ${gray200}`,
224241
transition: SIDEBAR_STYLES.transition,
225242
p: 3,
243+
minWidth: open ? SIDEBAR_STYLES.expanded : SIDEBAR_STYLES.collapsed,
226244
maxWidth: open ? SIDEBAR_STYLES.expanded : SIDEBAR_STYLES.collapsed,
227245
overflowY: 'auto',
228246
'::-webkit-scrollbar': {
@@ -249,7 +267,11 @@ export default function NewTermSidebar({
249267
alignItems="center"
250268
gap={1}
251269
>
252-
{isResultsEmpty ? (
270+
{loading ? (
271+
Array.from(new Array(10)).map((_, index) => (
272+
<ResultItemSkeleton key={index} />
273+
))
274+
) : isResultsEmpty ? (
253275
<EmptyState />
254276
) : (
255277
<>
@@ -259,6 +281,7 @@ export default function NewTermSidebar({
259281
result={result}
260282
searchValue={searchValue}
261283
onResultAction={onResultAction}
284+
user={user}
262285
/>
263286
))}
264287
</>
@@ -282,6 +305,7 @@ ResultItem.propTypes = {
282305
result: PropTypes.object.isRequired,
283306
searchValue: PropTypes.string,
284307
onResultAction: PropTypes.func,
308+
user: PropTypes.object,
285309
};
286310

287311
NewTermSidebar.propTypes = {
@@ -292,4 +316,5 @@ NewTermSidebar.propTypes = {
292316
isResultsEmpty: PropTypes.bool.isRequired,
293317
searchValue: PropTypes.string.isRequired,
294318
onResultAction: PropTypes.func,
319+
user: PropTypes.object,
295320
};

0 commit comments

Comments
 (0)