shaffuru files sync

This commit is contained in:
Lalit Deore
2026-05-07 15:27:32 +05:30
parent 0bc92c9fa7
commit 4cff6addde
54 changed files with 5857 additions and 3687 deletions
+93 -140
View File
@@ -1,10 +1,9 @@
import React, { useEffect, useState, useRef } from "react";
import React, { useEffect, useState, useRef, useMemo } from "react";
import theme from "../theme.jsx";
import ReactGA from "react-ga4";
import { Link } from "react-router-dom";
import { removeQuery } from "../components/ScrollToTop.jsx";
import { useMemo } from "react";
import { Tabs, Tab, Collapse } from "@mui/material";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
@@ -27,7 +26,7 @@ import {
InstantSearch,
Configure,
connectSearchBox,
connectHits,
connectInfiniteHits,
connectHitInsights,
RefinementList,
ClearRefinements,
@@ -39,6 +38,7 @@ import aa from "search-insights";
import { useLocation } from 'react-router-dom';
import "./FilterCSS.css";
import SearchContactForm from "../components/SearchContactForm.jsx";
import {
Zoom,
@@ -54,7 +54,7 @@ import {
const searchClient = algoliasearch(
"JNSS5CFDZZ",
"c8f882473ff42d41158430be09ec2b4e"
"eb5fd80aa6ed5ab4730d836cff3ea283"
);
//const searchClient = algoliasearch("L55H18ZINA", "a19be455e7e75ee8f20a93d26b9fc6d6")
@@ -77,62 +77,13 @@ const AppGrid = (props) => {
const xs =
parsedXs === undefined || parsedXs === null ? (isMobile ? 6 : 3) : parsedXs;
const [formMail, setFormMail] = React.useState("");
const [message, setMessage] = React.useState("");
const [formMessage, setFormMessage] = React.useState("");
const [deactivatedIndexes, setDeactivatedIndexes] = React.useState([]);
const buttonStyle = {
borderRadius: 30,
height: 50,
width: 220,
margin: isMobile ? "15px auto 15px auto" : 20,
fontSize: 18,
};
const innerColor = "rgba(255,255,255,0.65)";
const borderRadius = 3;
window.title = "Shuffle | Apps | Find and integrate any app";
const noImage = "/public/no_image.png";
const submitContact = (email, message) => {
const data = {
firstname: "",
lastname: "",
title: "",
companyname: "",
email: email,
phone: "",
message: message,
};
const errorMessage =
"Something went wrong. Please contact frikky@shuffler.io directly.";
fetch(globalUrl + "/api/v1/contact", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
})
.then((response) => response.json())
.then((response) => {
if (response?.success === true) {
setFormMessage(response.reason);
//toast("Thanks for submitting!")
} else {
setFormMessage(errorMessage);
}
setFormMail("");
setMessage("");
})
.catch((error) => {
setFormMessage(errorMessage);
console.log(error);
});
};
const SearchBox = ({ currentRefinement, refine, isSearchStalled, searchQuery, setSearchQuery }) => {
var defaultSearch = "";
@@ -148,10 +99,11 @@ const AppGrid = (props) => {
const params = Object.fromEntries(urlSearchParams.entries());
const foundQuery = params["q"];
if (foundQuery !== null && foundQuery !== undefined) {
console.log("Got query: ", foundQuery);
refine(foundQuery);
defaultSearch = foundQuery;
searchQuery = foundQuery
if (searchQuery !== foundQuery) {
setSearchQuery(foundQuery);
}
}
}
//}, [])
@@ -234,7 +186,13 @@ const AppGrid = (props) => {
onChange={(event) => {
const value = event.currentTarget.value;
setSearchQuery(value);
removeQuery("q");
const urlSearchParams = new URLSearchParams(window.location.search);
if (value) {
urlSearchParams.set("q", value);
} else {
urlSearchParams.delete("q");
}
window.history.replaceState({}, '', `${window.location.pathname}?${urlSearchParams.toString()}`);
debouncedRefine(value);
}}
onKeyDown={(event) => {
@@ -252,6 +210,21 @@ const AppGrid = (props) => {
const [currTab, setCurrTab] = useState(0);
const location = useLocation();
const conditionalSearchClient = useMemo(() => ({
...searchClient,
search(requests) {
if (currTab !== 0) {
return Promise.resolve({
results: requests.map(() => ({
hits: [], nbHits: 0, page: 0, nbPages: 0, hitsPerPage: 0,
processingTimeMS: 0, exhaustiveNbHits: true, query: "", params: "",
})),
});
}
return searchClient.search(requests);
},
}), [currTab]);
useEffect(() => {
const queryParams = new URLSearchParams(location.search);
const tabParam = queryParams.get('tab');
@@ -269,7 +242,6 @@ const AppGrid = (props) => {
const newQueryParam = newTab === 0 ? 'all_apps' : newTab === 1 ? 'org_apps' : 'my_apps';
const queryParams = new URLSearchParams(location.search);
queryParams.set('tab', newQueryParam);
queryParams.delete('q');
window.history.replaceState({}, '', `${location.pathname}?${queryParams.toString()}`);
};
@@ -281,6 +253,8 @@ const AppGrid = (props) => {
// Component to fetch all public app from the algolia.
const Hits = ({
hits,
hasMore,
refineNext,
insights,
setIsAnyAppActivated,
searchQuery
@@ -288,6 +262,32 @@ const AppGrid = (props) => {
const [mouseHoverIndex, setMouseHoverIndex] = useState(-1);
var counted = 0;
const [hoverEffect, setHoverEffect] = useState(-1);
const [isLoadingMore, setIsLoadingMore] = useState(false);
const loadMoreRef = useRef(null);
const scrollContainerRef = useRef(null);
const isFetchingMore = useRef(false);
useEffect(() => {
isFetchingMore.current = false;
setIsLoadingMore(false);
}, [hits.length]);
useEffect(() => {
return; // infinite scroll disabled
if (!loadMoreRef.current || !scrollContainerRef.current) return;
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting && hasMore && !isFetchingMore.current) {
isFetchingMore.current = true;
setIsLoadingMore(true);
refineNext();
}
},
{ root: scrollContainerRef.current, rootMargin: "200px" }
);
observer.observe(loadMoreRef.current);
return () => observer.disconnect();
}, [hasMore, refineNext]);
const normalizedString = (name) => {
if (typeof name === 'string') {
@@ -359,11 +359,11 @@ const AppGrid = (props) => {
} else {
//toast.success(`App ${type}d Successfully!`);
if (type === 'activate') {
setAllActivatedAppIds(prev => [...prev, data.objectID]);
setAllActivatedAppIds(prev => [...(prev || []), data.objectID]);
setIsAnyAppActivated(true);
}
if (type === 'deactivate') {
const updatedIds = allActivatedAppIds.filter(id => id !== data.objectID);
const updatedIds = (allActivatedAppIds || []).filter(id => id !== data.objectID);
setAllActivatedAppIds(updatedIds);
}
}
@@ -373,6 +373,16 @@ const AppGrid = (props) => {
});
}
const sortedHits = useMemo(() => {
const list = [...(hits || [])];
if (!allActivatedAppIds?.length) return list;
return list.sort((a, b) => {
const aActive = allActivatedAppIds.includes(a.objectID) ? 1 : 0;
const bActive = allActivatedAppIds.includes(b.objectID) ? 1 : 0;
return bActive - aActive;
});
}, [hits, hits?.length, allActivatedAppIds]);
let workflowDelay = 0;
const isHeader = true;
const paperStyle = {
@@ -404,6 +414,7 @@ const AppGrid = (props) => {
) : (
<Grid item spacing={2} justifyContent="flex-start">
<div
ref={scrollContainerRef}
style={{
gap: 16,
marginTop: 16,
@@ -418,7 +429,7 @@ const AppGrid = (props) => {
scrollbarColor: "#494949 #2f2f2f",
}}
>
{hits?.map((data, index) => {
{sortedHits.map((data, index) => {
const appUrl =
isCloud === true ?
`/apps/${data.objectID}`
@@ -629,6 +640,12 @@ const AppGrid = (props) => {
);
})
}
<div ref={loadMoreRef} style={{ width: "100%", height: 10 }} />
{isLoadingMore && (
<div style={{ width: "100%", display: "flex", justifyContent: "center", padding: 10 }}>
<CircularProgress size={24} />
</div>
)}
</div>
</Grid >
)}
@@ -926,7 +943,7 @@ const AppGrid = (props) => {
//Component to display all apps.
const AllApps = ({ setIsAnyAppActivated }) => {
var [searchQuery, setSearchQuery] = useState("");
var [searchQuery, setSearchQuery] = useState(() => new URLSearchParams(window.location.search).get('q') || "");
return (
<div
@@ -987,6 +1004,7 @@ const AppGrid = (props) => {
}}
onClick={() => {
setSearchQuery('');
removeQuery("q");
}}
/>
)}
@@ -1015,7 +1033,15 @@ const AppGrid = (props) => {
placeholder="Search your Activated or self-built apps"
id="shuffle_search_field"
onChange={(event) => {
setSearchQuery(event.currentTarget.value);
const value = event.currentTarget.value;
setSearchQuery(value);
const urlSearchParams = new URLSearchParams(window.location.search);
if (value) {
urlSearchParams.set("q", value);
} else {
urlSearchParams.delete("q");
}
window.history.replaceState({}, '', `${window.location.pathname}?${urlSearchParams.toString()}`);
}}
onKeyDown={(event) => {
if(event.key === "Enter") {
@@ -1592,7 +1618,7 @@ const AppGrid = (props) => {
//Component to fetch all apps created by user and Org
const UserAndOrgApps = ({ selectedCategoryForUsersAndOgsApps, selectedTagsForUserAndOrgApps, selectedOptionOfCreatedWith, setselectedCategoryForUsersAndOgsApps, setSelectedTagsForUserAndOrgApps, setSelectedOptionOfCreatedWith }) => {
const [searchQuery, setSearchQuery] = useState("");
const [searchQuery, setSearchQuery] = useState(() => new URLSearchParams(window.location.search).get('q') || "");
const [appsToShow, setAppsToShow] = useState([]);
useEffect(() => {
if (currTab === 1) {
@@ -2003,7 +2029,7 @@ const AppGrid = (props) => {
};
const CustomSearchBox = connectSearchBox(SearchBox);
const CustomHits = connectHits(Hits);
const CustomHits = connectInfiniteHits(Hits);
const DisplayAllAppsTab = () => {
const [selectedCategoryForUsersAndOgsApps, setselectedCategoryForUsersAndOgsApps] = useState([]);
@@ -2012,7 +2038,7 @@ const AppGrid = (props) => {
return (
<div>
<InstantSearch searchClient={searchClient} indexName="appsearch">
<InstantSearch key={currTab === 0 ? "active" : "inactive"} searchClient={conditionalSearchClient} indexName="appsearch">
<div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'center', paddingRight: 215 }}>
{currTab === 0 ? (
<FilterForAllApps />
@@ -2037,7 +2063,7 @@ const AppGrid = (props) => {
/>
</div>
<Configure clickAnalytics />
{currTab === 0 && <Configure clickAnalytics hitsPerPage={20} />}
</InstantSearch>
</div>
);
@@ -2060,80 +2086,7 @@ const AppGrid = (props) => {
>
<DisplayAllAppsTab />
{showSuggestion === true ? (
<div
style={{
paddingTop: 0,
maxWidth: isMobile ? "100%" : "60%",
margin: "auto",
}}
>
<Typography variant="h6" style={{ color: "white", marginTop: 50 }}>
Can't find what you're looking for?
</Typography>
<div
style={{
flex: "1",
display: "flex",
flexDirection: "row",
textAlign: "center",
}}
>
<TextField
required
style={{
flex: "1",
marginRight: 15,
backgroundColor: theme.palette.inputColor,
}}
InputProps={{
style: {
color: "#ffffff",
},
}}
color="primary"
fullWidth={true}
placeholder="Email (optional)"
type="email"
id="email-handler"
autoComplete="email"
margin="normal"
variant="outlined"
onChange={(e) => setFormMail(e.target.value)}
/>
<TextField
required
style={{ flex: "1", backgroundColor: theme.palette.inputColor }}
InputProps={{
style: {
color: "#ffffff",
},
}}
color="primary"
fullWidth={true}
placeholder="What apps do you want to see?"
type=""
id="standard-required"
margin="normal"
variant="outlined"
autoComplete="off"
onChange={(e) => setMessage(e.target.value)}
/>
</div>
<Button
variant="contained"
color="primary"
style={buttonStyle}
disabled={message.length === 0}
onClick={() => {
submitContact(formMail, message);
}}
>
Submit
</Button>
<Typography style={{ color: "white" }} variant="body2">
{formMessage}
</Typography>
</div>
<SearchContactForm globalUrl={globalUrl} isMobile={isMobile} tabName="apps" />
) : null}
</div>
</div>