diff --git a/frontend/src/views/Apps2.jsx b/frontend/src/views/Apps2.jsx index 828c6bbf..b4573574 100644 --- a/frontend/src/views/Apps2.jsx +++ b/frontend/src/views/Apps2.jsx @@ -29,6 +29,7 @@ const searchClient = algoliasearch( "db08e40265e2941b9a7d8f644b6e5240" ); +// AppCard Component const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl, deactivatedIndexes }) => { const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" || window.location.host === "localhost:3000"; const appUrl = isCloud ? `/apps/${data.id}` : `https://shuffler.io/apps/${data.id}`; @@ -42,7 +43,7 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl, height: 96, borderRadius: 8, boxShadow: "0px 0px 10px 0px rgba(0, 0, 0, 0.1)", - // marginBottom: 20, + marginBottom: 20, }; return ( @@ -169,13 +170,13 @@ const AppCard = ({ data, index, mouseHoverIndex, setMouseHoverIndex, globalUrl, // Component to fetch all public app from the algolia. -const Hits = memo(({ +const Hits = ({ userdata, hits, + algoliaSelectedCategories, setIsAnyAppActivated, searchQuery, globalUrl, - setIsLoggedIn, isLoading, isLoggedIn, }) => { @@ -193,6 +194,16 @@ const Hits = memo(({ } }; + useEffect(() => { + console.log("searchQuery", searchQuery) + console.log("hits", hits) + }, [searchQuery, hits]) + + + const filteredHits = hits?.filter(hit => { + if (algoliaSelectedCategories?.length === 0) return true; // If no categories are selected, show all hits + return hit.categories?.some(category => algoliaSelectedCategories.includes(category)); + }); // const fetchUserData = useCallback(async () => { // try { @@ -281,237 +292,242 @@ const Hits = memo(({ return () => clearTimeout(timer); }, []); - const memoizedHits = useMemo(() => hits, [hits]); return (
{!isLoading ? (
- {memoizedHits?.length === 0 && searchQuery.length >= 0 && showNoAppFound ? ( + {filteredHits?.length === 0 && searchQuery.length >= 0 && showNoAppFound ? ( No Apps Found ) : (
- {memoizedHits.map((data, index) => { - const appUrl = - isCloud - ? `/apps/${data.objectID}?queryID=${data.__queryID}` - : `https://shuffler.io/apps/${data.objectID}?queryID=${data.__queryID}`; + }}> +
+ {filteredHits?.map((data, index) => { + const appUrl = + isCloud + ? `/apps/${data.objectID}?queryID=${data.__queryID}` + : `https://shuffler.io/apps/${data.objectID}?queryID=${data.__queryID}`; - return ( - - - - + + { - setHoverEffect(index); - }} - onMouseLeave={() => { - setHoverEffect(-1); + textDecoration: "none", + color: "#f85a3e", }} > - - {data.name} -
-
{ + setHoverEffect(index); + }} + onMouseLeave={() => { + setHoverEffect(-1); + }} + > + + {data.name} - {(allActivatedAppIds && allActivatedAppIds.includes(data.objectID)) && } - {normalizedString(data.name)} -
- -
- {data.categories !== null - ? normalizedString(data.categories).join(", ") - : "NA"} -
+ />
-
- {hoverEffect === index && isCloud ? ( -
- {data.tags && ( - - - {data.tags.slice(0, 1).map((tag, tagIndex) => ( - - {normalizedString(tag)} - {tagIndex < 1 ? ", " : ""} - - ))} - - - )} -
- ) : ( -
- {data.tags && - data.tags.map((tag, tagIndex) => ( - - {normalizedString(tag)} - {tagIndex < data.tags.length - 1 ? ", " : ""} - - ))} -
- )} +
+ {(allActivatedAppIds && allActivatedAppIds.includes(data.objectID)) && } + {normalizedString(data.name)}
-
- {hoverEffect === index && isCloud && ( -
- {allActivatedAppIds && allActivatedAppIds.includes(data.objectID) ? ( - - ) : ( -
+
+
+ {hoverEffect === index && isCloud ? ( +
+ {data.tags && ( + + + {data.tags.slice(0, 1).map((tag, tagIndex) => ( + + {normalizedString(tag)} + {tagIndex < 1 ? ", " : ""} + + ))} + + + )} +
+ ) : ( +
+ {data.tags && + data.tags.map((tag, tagIndex) => ( + + {normalizedString(tag)} + {tagIndex < data.tags.length - 1 ? ", " : ""} + + ))} +
+ )} +
+
+ {hoverEffect === index && isCloud && ( +
+ {allActivatedAppIds && allActivatedAppIds.includes(data.objectID) ? ( + - )} -
- )} + onClick={(event) => { + handleActivateButton(event, data, "deactivate"); + }}> + Deactivate + + ) : ( + + )} +
+ )} +
-
- - -
- - - ); - }) - } + + + + + + ); + }) + } +
)} @@ -521,8 +537,9 @@ const Hits = memo(({ )}
); -}); +} +// Main Apps Component const Apps2 = (props) => { const { globalUrl, isLoaded, serverside, userdata, isLoggedIn } = props; let navigate = useNavigate(); @@ -530,11 +547,13 @@ const Apps2 = (props) => { const location = useLocation(); const [searchQuery, setSearchQuery] = useState(""); const [selectedCategory, setSelectedCategory] = useState(""); + const [algoliaSelectedCategories, setAlgoliaSelectedCategories] = useState([]); const [selectedLabel, setSelectedLabel] = useState(""); + const [algoliaSelectedLabels, setAlgoliaSelectedLabels] = useState([]); const [currTab, setCurrTab] = useState(0); const [categories, setCategories] = useState([]); const [labels, setLabels] = useState([]); - const [isLoading, setIsLoading] = useState(true); + const [isLoading, setIsLoading] = useState(false); const [userApps, setUserApps] = useState([]); const [orgApps, setOrgApps] = useState([]); const [selectedCategoryForUsersAndOgsApps, setselectedCategoryForUsersAndOgsApps] = useState([]); @@ -542,13 +561,12 @@ const Apps2 = (props) => { const [appsToShow, setAppsToShow] = useState([]); const [deactivatedIndexes, setDeactivatedIndexes] = React.useState([]); const [IsAnyAppActivated, setIsAnyAppActivated] = useState(false); - const [mouseHoverIndex, setMouseHoverIndex] = useState(-1); var counted = 0; const [showNoAppFound, setShowNoAppFound] = useState(false); - const CustomHits = connectHits(Hits) + // Set the current tab based on the query parameter useEffect(() => { const queryParams = new URLSearchParams(location.search); const tabParam = queryParams.get('tab'); @@ -560,10 +578,13 @@ const Apps2 = (props) => { } else { setCurrTab(2); } + } else { + setCurrTab(0); } }, [location.search]); + // Fetch apps based on the current tab : 0 -> org_apps, 1 -> my_apps, 2 -> all_apps useEffect(() => { const fetchApps = async () => { const baseUrl = globalUrl; @@ -589,9 +610,12 @@ const Apps2 = (props) => { const data = await response.json(); if (currTab === 1) { console.log("data from userApps", data) + setAppsToShow(data); setUserApps(data); setIsLoading(false); } else if (currTab === 0) { + console.log("data from orgApps", data) + setAppsToShow(data); setOrgApps(data); setIsLoading(false); } @@ -601,9 +625,20 @@ const Apps2 = (props) => { }; fetchApps(); - }, [currTab, globalUrl]); // Added globalUrl to dependencies to avoid stale closures + }, [currTab, globalUrl, location.search]); // Added globalUrl to dependencies to avoid stale closures + + useEffect(() => { + setSearchQuery(""); + }, [currTab]) + // Find top categories and tags based on the current tab + useEffect(() => { + if (currTab === 0) { + setCategories(findTopCategories()); + setLabels(findTopTags()); + } + }, [currTab, appsToShow]) useEffect(() => { @@ -612,13 +647,13 @@ const Apps2 = (props) => { } }, [serverside]); - + const findTopCategories = () => { const categoryCountMap = {}; - + const apps = currTab === 1 ? userApps : orgApps; // Check if userAndOrgsApp is an array before iterating over it and Find top 10 Category from the apps - if (Array.isArray(appsToShow)) { - appsToShow.forEach((app) => { + if (Array.isArray(apps)) { + apps.forEach((app) => { const categories = app.categories; if (categories && categories.length > 0) { @@ -643,9 +678,10 @@ const Apps2 = (props) => { const findTopTags = () => { const tagCountMap = {}; - - if (Array.isArray(appsToShow)) { - appsToShow.forEach((app) => { + const apps = currTab === 1 ? userApps : orgApps; + + if (Array.isArray(apps)) { + apps.forEach((app) => { const tags = app.tags; if (tags && tags.length > 0) { tags.forEach((tag) => { @@ -666,42 +702,47 @@ const Apps2 = (props) => { return topTags; }; - + const handleCreateApp = () => { navigate('/create-app'); }; - //Search app base on app name, category and tag - const filteredUserAppdata = Array.isArray(appsToShow) ? appsToShow.filter((app) => { - const matchesSearchQuery = ( - searchQuery === "" || - app.name.toLowerCase().includes(searchQuery.toLowerCase()) || - (app.tags && app.tags.some(tag => - tag.toLowerCase().includes(searchQuery.toLowerCase()) - )) || - (app.categories && app.categories.some((category) => - category.toLowerCase().includes(searchQuery.toLowerCase()) - )) - ); + useEffect(() => { + const apps = currTab === 1 ? userApps : orgApps; + // Search app based on app name, category, and tag + const filteredUserAppdata = Array.isArray(apps) ? apps.filter((app) => { - const matchesSelectedCategories = ( - selectedCategoryForUsersAndOgsApps.length === 0 || - (app.categories && app.categories.some(category => - selectedCategoryForUsersAndOgsApps.includes(category) - )) - ); + const matchesSearchQuery = ( + searchQuery === "" || // If searchQuery is empty, match all apps + app.name.toLowerCase().includes(searchQuery.toLowerCase()) || + (app.tags && app.tags.some(tag => + tag.toLowerCase().includes(searchQuery.toLowerCase()) + )) || + (app.categories && app.categories.some((category) => + category.toLowerCase().includes(searchQuery.toLowerCase()) + )) + ); - const matchesSelectedTags = ( - selectedTagsForUserAndOrgApps.length === 0 || - (app.tags && selectedTagsForUserAndOrgApps.some(tag => - app.tags.includes(tag) - )) - ); + const matchesSelectedCategories = ( + selectedCategory === "" || // If no category is selected, match all apps + (app.categories && app.categories.some(category => + selectedCategory.includes(category) + )) + ); + const matchesSelectedTags = ( + selectedLabel === "" || // If no label is selected, match all apps + (app.tags && app.tags.some(tag => + tag.toLowerCase().includes(selectedLabel.toLowerCase()) + )) + ); - return matchesSearchQuery && matchesSelectedCategories && matchesSelectedTags; - }) : []; + return matchesSearchQuery && matchesSelectedCategories && matchesSelectedTags; + }) : []; + + setAppsToShow(filteredUserAppdata); + }, [searchQuery, selectedCategory, selectedLabel]); const handleTabChange = (newTab) => { @@ -746,6 +787,104 @@ const Apps2 = (props) => { + const SearchBox = ({ refine, searchQuery, setSearchQuery }) => { + const inputRef = React.useRef(null); // Create a ref for the input field + + // Check for query in URL and set it + React.useEffect(() => { + if ( + window !== undefined && + window.location !== undefined && + window.location.search !== undefined && + window.location.search !== null + ) { + const urlSearchParams = new URLSearchParams(window.location.search); + const params = Object.fromEntries(urlSearchParams.entries()); + const foundQuery = params["q"]; + if (foundQuery !== null && foundQuery !== undefined) { + console.log("Got query: ", foundQuery); + refine(foundQuery); + setSearchQuery(foundQuery); // Use setSearchQuery to update state + } + } + }, [refine, setSearchQuery]); // Add dependencies + + // Use useEffect to focus the input when it mounts or when searchQuery changes + React.useEffect(() => { + if (inputRef.current) { + inputRef.current.focus(); + } + }, [searchQuery]); // Focus whenever searchQuery changes + + console.log("searchQuery", searchQuery); + + return ( + { + setSearchQuery(event.target.value); // Update the search query + removeQuery("q"); + refine(event.target.value); // Refine the search + }} + onKeyDown={(event) => { + if (event.key === "Enter") { + event.preventDefault(); + } + }} + limit={5} + style={{ width: '100%', borderRadius: '7px', fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)" }} + InputProps={{ + style: { + borderRadius: 8, + }, + endAdornment: ( + + {searchQuery?.length === 0 ? : ( + { + setSearchQuery(''); // Clear the search query + removeQuery("q"); + refine(''); // Clear the refinement + }} + /> + )} + + ), + }} + /> + ); + }; + + const CustomSearchBox = connectSearchBox(SearchBox); + const CustomHits = connectHits(Hits) + + + const handleCategoryChange = (selectedValue) => { + setAlgoliaSelectedCategories(prev => { + if (prev.includes(selectedValue)) { + // If the category is already selected, remove it + return prev.filter(category => category !== selectedValue); + } else { + // Otherwise, add it to the selected categories + return [...prev, selectedValue]; + } + }); + }; + + useEffect(() => { + console.log("algoliaSelectedCategories", algoliaSelectedCategories) + }, [algoliaSelectedCategories]) + + return (
@@ -765,38 +904,42 @@ const Apps2 = (props) => {
- { - setSearchQuery(event.currentTarget.value); - removeQuery("q"); - // refine(event.currentTarget.value); - }} - onKeyDown={(event) => { - if (event.key === "Enter") { - event.preventDefault(); - } - }} - limit={5} - style={{ width: '100%', borderRadius: '7px', fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)" }} - InputProps={{ - style: { - borderRadius: 8, - }, - endAdornment: ( - - { - searchQuery.length === 0 ? : - } - - ), - }} - /> - {/* */} + { + (currTab === 0 || currTab === 1) && + { + setSearchQuery(event.target.value); + }} + onKeyDown={(event) => { + if (event.key === "Enter") { + event.preventDefault(); + } + }} + limit={5} + style={{ width: '100%', borderRadius: '7px', fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)" }} + InputProps={{ + style: { + borderRadius: 8, + }, + endAdornment: ( + + { + searchQuery.length === 0 ? : + } + + ), + }} + /> + } + { + currTab === 2 && + + }
@@ -878,74 +1030,86 @@ const Apps2 = (props) => {
{ - currTab === 0 && - (orgApps.length > 0 ? ( -
- { - isLoading ?
- : - ( - filteredUserAppdata?.map((data, index) => ( - - )) - ) - } + currTab === 0 && ( +
+ {isLoading ? ( +
+ +
+ ) : ( + <> + {appsToShow?.length > 0 && appsToShow !== undefined && !isLoading ? ( +
+ {appsToShow.map((data, index) => ( + + ))} +
+ ) : ( +
+ No apps found +
+ )} + + )}
- ) :
No apps found
) } { currTab === 1 && (userApps.length > 0 ? ( -
- { - isLoading ?
- : - ( - userApps.map((data, index) => ( - - )) - ) - } +
+
+ { + isLoading ?
+ : + ( + userApps.map((data, index) => ( + + )) + ) + } +
- ) :
No apps found
+ ) : !isLoading && ( +
No apps found
+ ) ) } { currTab === 2 && + { hitsPerPage={5} globalUrl={globalUrl} searchQuery={searchQuery} + algoliaSelectedCategories={algoliaSelectedCategories} mouseHoverIndex={mouseHoverIndex} setMouseHoverIndex={setMouseHoverIndex} />