Skip to content

Commit 7ff8f21

Browse files
authored
Feat: Global Search Phase 5 (#2060)
* Global Search: Add search page filters (#2008) * task: added sortby filter * task: rename sortby filter * task: add user filter * task: close menu on select * task: add select highlight * task: add date range to date filters * task: set params for daterange filter * task: set active filter text * task: remove date filter * task: pass active daterange value to daterange picker * fix: fixed ts error * Global Search: Search page wire filters (#2009) * task: added sortby filter * task: rename sortby filter * task: add user filter * task: close menu on select * task: add select highlight * task: add date range to date filters * task: set params for daterange filter * task: set active filter text * task: remove date filter * task: pass active daterange value to daterange picker * fix: fixed ts error * - do not open a new tab when filters are added - unpin specific tab when search queries are present - show search keyword on tab name for the search page * task: sort by function * task: user filter * task: background color update * task: spacing fix * task: date filter func * task: updated result count * task: fixed alignment * task: added no results for filter * Global Search: Advanced search button & modal (#2012) * task: added advanced search button * task: added filter button * chore: removed comment * task: added advanced search modal * task: fixed console errors * task: fixed console error * task: add form fields * task: added tooltips * Global Search: Wire advance search modal (#2015) * task: added advanced search button * task: added filter button * chore: removed comment * task: added advanced search modal * task: fixed console errors * task: fixed console error * task: add form fields * task: added tooltips * task: created hook for advance search data * task: moved search data to content search comp * task: added clear all function * task: wire user filter * task: import types * task: moved everything back into the advance search modal * task: moved date config to a separate file & added daterange modal * task: added date handling for single date picker * task: daterange handler * task: wired advance search page * task: wired advance search function * task: spacing and color update * fix: fixed search button not enabling * task: added cypress test scenarios * task: search page cypress tests * task: advance search modal cypress tests * task: added data-cy * task: updated cy get chaining * fix: height fix for the counter * task: added tooltips and changed date filter to modified at * task: vqa updates * task: adjusted border radius * Global Search: Save/remove recent searches (#2029) * task: always open the dropdown on click * task: add recent searches hook * task: add recent searches to dropdown * task: created separated component for global search items * task: updated subheader style * task: padding updates * task: cleanup * task: unfocus textfield when user selects an item * task: always show user typed value * task: removed comment * task: wire remove recent search * task: save a max of 50 recent search items * task: when user keyword exists as a saved recent search, render it as a recent search * task: hide suggestions when no user-typed keyword is present * task: fixed incorrect icon * task: remove manual JSON.parse/stringify * task: removed deep cloning * Global Search: Resource type filter (#2039) * task: added resource type filter * task: updated tooltip text * task: add resource type filter component in search page * Global Search Phase 4: Schema searching (#2041) * task: created util to get content title * task: changed how content result is being taken * task: created filtered models hook * task: show models in top suggestions * task: moved getting icon to utils * task: added model type keyword matching * task: changed model search hook * task: update comment * chore: added hook description * task: search models via user zuid * task: allow searching models via user name * task: changed how data is taken from rtk query * task: show models in search page results * task: render model list item * task: wire resource type filter * task: cleanup * task: added icons to dropdown options * Global Search: Include code files in top suggestions (#2048) * task: created util to get content title * task: changed how content result is being taken * task: created filtered models hook * task: show models in top suggestions * task: moved getting icon to utils * task: added model type keyword matching * task: changed model search hook * task: update comment * chore: added hook description * task: search models via user zuid * task: allow searching models via user name * task: changed how data is taken from rtk query * task: show models in search page results * task: render model list item * task: wire resource type filter * task: cleanup * task: create rtk query for scripts and stylesheets * task: add hook to filter code files * task: include code files in top suggestions * task: support view and snippet files for .html searches * task: remove unneeded special handling for .html search * task: add code to resource type filter dropdowns * task: fixed type errors * task: change type for resource types * task: add files to sesarch page results * task: add code file results to search page
1 parent 6774a09 commit 7ff8f21

File tree

11 files changed

+265
-36
lines changed

11 files changed

+265
-36
lines changed

src/shell/components/ContentSearch/components/config.ts

+3-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
PresetType,
33
DateFilterModalType,
44
} from "../../../components/Filters/DateFilter/types";
5+
import { ResourceType } from "../../../services/types";
56

67
interface PresetDate {
78
text: string;
@@ -57,11 +58,8 @@ export const CUSTOM_DATES: CustomDate[] = [
5758
},
5859
];
5960

60-
interface ResourceType {
61-
content: string;
62-
schema: string;
63-
}
64-
export const RESOURCE_TYPES: ResourceType = {
61+
export const RESOURCE_TYPES: Record<ResourceType, string> = {
6562
content: "Content Items",
6663
schema: "Models",
64+
code: "Code Files",
6765
};

src/shell/components/ContentSearch/index.tsx

+30-13
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,14 @@ import useRecentSearches from "../../hooks/useRecentSearches";
2727
import { GlobalSearchItem } from "./components/GlobalSearchItem";
2828
import { getContentTitle, getItemIcon } from "./utils";
2929
import { useSearchModelsByKeyword } from "../../hooks/useSearchModelsByKeyword";
30+
import { useSearchCodeFilesByKeywords } from "../../hooks/useSearchCodeFilesByKeyword";
31+
import { ResourceType } from "../../services/types";
3032

3133
const AdditionalDropdownOptions = ["RecentSearches", "AdvancedSearchButton"];
3234
const ElementId = "global-search-autocomplete";
3335

3436
export interface Suggestion {
35-
type: "schema" | "content";
37+
type: ResourceType;
3638
ZUID: string;
3739
title: string;
3840
updatedAt: string;
@@ -45,7 +47,8 @@ const ContentSearch: FC = () => {
4547
const [open, setOpen] = useState(false);
4648
const [isAdvancedSearchOpen, setIsAdvancedSearchOpen] = useState(false);
4749
const [recentSearches, addSearchTerm, deleteSearchTerm] = useRecentSearches();
48-
const [models, setKeyword] = useSearchModelsByKeyword();
50+
const [models, setModelKeyword] = useSearchModelsByKeyword();
51+
const [files, setFileKeyword] = useSearchCodeFilesByKeywords();
4952
const languages = useSelector((state: any) => state.languages);
5053
const history = useHistory();
5154
const location = useLocation();
@@ -74,26 +77,39 @@ const ContentSearch: FC = () => {
7477
};
7578
}) || [];
7679

77-
const modelSuggestions: Suggestion[] = models.map((model) => {
78-
return {
79-
type: "schema",
80-
ZUID: model.ZUID,
81-
title: model.label,
82-
updatedAt: model.updatedAt,
83-
url: `/schema/${model.ZUID}`,
84-
};
85-
});
80+
const modelSuggestions: Suggestion[] =
81+
models?.map((model) => {
82+
return {
83+
type: "schema",
84+
ZUID: model.ZUID,
85+
title: model.label,
86+
updatedAt: model.updatedAt,
87+
url: `/schema/${model.ZUID}`,
88+
};
89+
}) || [];
90+
91+
const codeFileSuggestions: Suggestion[] =
92+
files?.map((file) => {
93+
return {
94+
type: "code",
95+
ZUID: file.ZUID,
96+
title: file.fileName?.split("/").pop(), // Removes the file path from the file name
97+
updatedAt: file.updatedAt,
98+
url: `/code/file/views/${file.ZUID}`,
99+
};
100+
}) || [];
86101

87102
const consolidatedResults = [
88103
...contentSuggestions,
89104
...modelSuggestions,
105+
...codeFileSuggestions,
90106
].sort(
91107
(a, b) =>
92108
new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
93109
);
94110

95111
return consolidatedResults;
96-
}, [contents, models]);
112+
}, [contents, models, files]);
97113

98114
const options = useMemo(() => {
99115
// Only show the first 5 recent searches when user has not typed in a query
@@ -108,7 +124,8 @@ const ContentSearch: FC = () => {
108124
}, [suggestions, recentSearches, value]);
109125

110126
useEffect(() => {
111-
setKeyword(value);
127+
setModelKeyword(value);
128+
setFileKeyword(value);
112129
}, [value]);
113130

114131
//@ts-ignore TODO fix typing for useMetaKey

src/shell/components/ContentSearch/utils.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { isEmpty } from "lodash";
22
import { Database } from "@zesty-io/material";
3-
import { Create, SvgIconComponent } from "@mui/icons-material";
4-
import { ContentItem } from "../../services/types";
3+
import { Create, SvgIconComponent, CodeRounded } from "@mui/icons-material";
4+
import { ContentItem, ResourceType } from "../../services/types";
55

66
export const getContentTitle = (
77
content: ContentItem,
@@ -20,7 +20,7 @@ export const getContentTitle = (
2020
return langDisplay ? `${langDisplay}${title}` : title;
2121
};
2222

23-
export const getItemIcon = (type: "schema" | "content") => {
23+
export const getItemIcon = (type: ResourceType) => {
2424
let icon;
2525

2626
switch (type) {
@@ -32,6 +32,10 @@ export const getItemIcon = (type: "schema" | "content") => {
3232
icon = Database as SvgIconComponent;
3333
break;
3434

35+
case "code":
36+
icon = CodeRounded;
37+
break;
38+
3539
default:
3640
break;
3741
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { useState, useMemo } from "react";
2+
import { isEmpty } from "lodash";
3+
4+
import { WebView, Script, Stylesheet } from "../services/types";
5+
import {
6+
useGetWebViewsQuery,
7+
useGetStylesheetsQuery,
8+
useGetScriptsQuery,
9+
} from "../services/instance";
10+
11+
/**
12+
* This hook is used to easily filter code files via a keyword that matches the ff:
13+
* - File name
14+
* - File ZUID
15+
*/
16+
export type File = Pick<
17+
WebView | Script | Stylesheet,
18+
keyof (WebView | Script | Stylesheet)
19+
>;
20+
type UseSearchCodeFilesByKeywords = [File[], (searchTerm: string) => void];
21+
export const useSearchCodeFilesByKeywords: () => UseSearchCodeFilesByKeywords =
22+
() => {
23+
const [searchTerm, setSearchTerm] = useState("");
24+
const { data: webViews } = useGetWebViewsQuery({ status: "dev" });
25+
const { data: stylesheets } = useGetStylesheetsQuery();
26+
const { data: scripts } = useGetScriptsQuery();
27+
28+
// Create one array with all the files
29+
const allFiles: File[] = useMemo(() => {
30+
let files: File[] = [...(stylesheets ?? []), ...(scripts ?? [])];
31+
32+
if (!isEmpty(webViews)) {
33+
const views = webViews.map(
34+
({ contentModelType, contentModelZUID, customZNode, ...rest }) => {
35+
return rest;
36+
}
37+
);
38+
39+
files = [...files, ...views];
40+
}
41+
42+
return files;
43+
}, [webViews, stylesheets, scripts]);
44+
45+
// Filter files based on file name
46+
const filteredFiles: File[] = useMemo(() => {
47+
const term = searchTerm?.toLowerCase();
48+
49+
/**
50+
* Matches the ff:
51+
* - File name
52+
* - File ZUID
53+
*/
54+
return allFiles?.filter(
55+
(file) =>
56+
file.fileName?.toLowerCase().includes(term) || file.ZUID === term
57+
);
58+
}, [allFiles, searchTerm]);
59+
60+
return [filteredFiles, setSearchTerm];
61+
};

src/shell/services/instance.ts

+26-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
2+
23
import instanceZUID from "../../utility/instanceZUID";
34
import { getResponseData, prepareHeaders } from "./util";
45
import { resolveResourceType } from "../../utility/resolveResourceType";
@@ -16,6 +17,8 @@ import {
1617
Web,
1718
Meta,
1819
ContentNavItem,
20+
Stylesheet,
21+
Script,
1922
} from "./types";
2023
import { batchApiRequests } from "../../utility/batchApiRequests";
2124

@@ -36,6 +39,8 @@ export const instanceApi = createApi({
3639
"WebViews",
3740
"InstanceSettings",
3841
"SearchQuery",
42+
"Stylesheets",
43+
"Scripts",
3944
],
4045
endpoints: (builder) => ({
4146
// https://www.zesty.io/docs/instances/api-reference/content/models/items/publishings/#Get-All-Item-Publishings
@@ -332,8 +337,13 @@ export const instanceApi = createApi({
332337
],
333338
}),
334339
// https://www.zesty.io/docs/instances/api-reference/content/#Get-View(s)
335-
getWebViews: builder.query<WebView[], void>({
336-
query: () => `/web/views`,
340+
getWebViews: builder.query<WebView[], void | { status?: "live" | "dev" }>({
341+
query: (params) => {
342+
return {
343+
url: "web/views",
344+
params: params || {},
345+
};
346+
},
337347
transformResponse: getResponseData,
338348
providesTags: ["WebViews"],
339349
}),
@@ -393,6 +403,18 @@ export const instanceApi = createApi({
393403
query: () => `/env/nav`,
394404
transformResponse: getResponseData,
395405
}),
406+
// https://www.zesty.io/docs/instances/api-reference/web/stylesheets/#Get-Stylesheet(s)
407+
getStylesheets: builder.query<Stylesheet[], void>({
408+
query: () => `/web/stylesheets`,
409+
transformResponse: getResponseData,
410+
providesTags: ["Stylesheets"],
411+
}),
412+
// https://www.zesty.io/docs/instances/api-reference/web/scripts/#Get-Script(s)
413+
getScripts: builder.query<Script[], void>({
414+
query: () => `/web/scripts`,
415+
transformResponse: getResponseData,
416+
providesTags: ["Scripts"],
417+
}),
396418
}),
397419
});
398420

@@ -427,5 +449,7 @@ export const {
427449
useUpdateInstanceSettingMutation,
428450
useCreateContentItemMutation,
429451
useGetContentNavItemsQuery,
452+
useGetStylesheetsQuery,
453+
useGetScriptsQuery,
430454
useCreateInstanceSettingsMutation,
431455
} = instanceApi;

src/shell/services/types.ts

+32
Original file line numberDiff line numberDiff line change
@@ -406,3 +406,35 @@ export interface ContentNavItem {
406406
contentModelZUID?: string;
407407
parentZUID?: string;
408408
}
409+
410+
export interface Stylesheet {
411+
ZUID: string;
412+
status: "live" | "dev";
413+
type: string;
414+
fileName: string;
415+
code: string;
416+
lastEditedID: number;
417+
template: number;
418+
module: number;
419+
plugin: number;
420+
version: number;
421+
createdAt: string;
422+
updatedAt: string;
423+
}
424+
425+
export interface Script {
426+
ZUID: string;
427+
status: "live" | "dev";
428+
type: string;
429+
fileName: string;
430+
code: string;
431+
lastEditedID: number;
432+
template: number;
433+
module: number;
434+
plugin: number;
435+
version: number;
436+
createdAt: string;
437+
updatedAt: string;
438+
}
439+
440+
export type ResourceType = "schema" | "content" | "code";

src/shell/views/SearchPage/Filters/ResourceTypeFilter.tsx

+14-5
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,13 @@ import {
77
ListItemText,
88
SvgIcon,
99
} from "@mui/material";
10-
import { SvgIconComponent, EditRounded } from "@mui/icons-material";
10+
import {
11+
SvgIconComponent,
12+
EditRounded,
13+
CodeRounded,
14+
} from "@mui/icons-material";
1115
import { Database } from "@zesty-io/material";
16+
import { ResourceType } from "../../../services/types";
1217

1318
import { FilterButton } from "../../../components/Filters";
1419

@@ -26,12 +31,16 @@ const RESOURCE_TYPE_OPTIONS: ResourceTypeOptions = {
2631
text: "Models",
2732
icon: Database as SvgIconComponent,
2833
},
34+
code: {
35+
text: "Code Files",
36+
icon: CodeRounded,
37+
},
2938
};
3039

31-
export type ResourceType = "content" | "schema" | "";
40+
export type ResourceCodes = ResourceType | "";
3241
interface ResourceTypeFilter {
33-
onChange: (value: ResourceType | "") => void;
34-
value?: ResourceType;
42+
onChange: (value: ResourceCodes) => void;
43+
value?: ResourceCodes;
3544
}
3645
export const ResourceTypeFilter: FC<ResourceTypeFilter> = ({
3746
onChange,
@@ -41,7 +50,7 @@ export const ResourceTypeFilter: FC<ResourceTypeFilter> = ({
4150

4251
const getButtonText = (): string => {
4352
if (Boolean(value) && value in RESOURCE_TYPE_OPTIONS) {
44-
return RESOURCE_TYPE_OPTIONS[value as Exclude<ResourceType, "">].text;
53+
return RESOURCE_TYPE_OPTIONS[value as ResourceType].text;
4554
}
4655

4756
return "Resource Type";

src/shell/views/SearchPage/Filters/index.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import {
1010
} from "../../../components/Filters/";
1111
import { useGetUsersQuery } from "../../../services/accounts";
1212
import { DateFilterValue } from "../../../components/Filters/DateFilter";
13-
import { ResourceTypeFilter, ResourceType } from "./ResourceTypeFilter";
13+
import { ResourceTypeFilter } from "./ResourceTypeFilter";
14+
import { ResourceType } from "../../../services/types";
1415

1516
export const Filters = () => {
1617
const [params, setParams] = useParams();

0 commit comments

Comments
 (0)