Compare commits
10 commits
d825f8842a
...
b42c9f1833
Author | SHA1 | Date | |
---|---|---|---|
b42c9f1833 | |||
f6b67cfbb2 | |||
806d72e58a | |||
8cbb624f1f | |||
512b258131 | |||
0a138375dc | |||
a8064668b8 | |||
9773239b4f | |||
1ab1d204af | |||
e47510f912 |
|
@ -32,11 +32,13 @@ header {
|
|||
width: calc(100vw - 30px);
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
main {
|
||||
flex-grow: 1;
|
||||
background: linear-gradient(220deg, var(--jf-gradient-color-secondary-dark), var(--jf-gradient-color-primary-dark));
|
||||
padding: 7px 15px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
@ -103,3 +105,33 @@ h1:hover {
|
|||
font-size: 17px;
|
||||
}
|
||||
}
|
||||
|
||||
.search-result-item {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
|
||||
img {
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
section {
|
||||
flex: 1;
|
||||
padding: 8px 0 0 8px;
|
||||
display: flex;
|
||||
justify-content: space-evenly;text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.search-result-item-interaction {
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
.search-result-item-text {
|
||||
flex: 1;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.search-result-item-action {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
|
|
@ -4,10 +4,11 @@ import Search from './components/search/Search'
|
|||
|
||||
import jellyfin from './assets/jellyfin.svg'
|
||||
import './App.css'
|
||||
import SearchResult from './components/search/SearchResult';
|
||||
|
||||
function App() {
|
||||
const [searchString, setSearchString] = useState("");
|
||||
const [searchResults, setSearchResults] = useState([]);
|
||||
const [searchResult, setSearchResult] = useState<{}[]>([]);
|
||||
const [itemChoiceByUser, setItemChoiceByUser] = useState({});
|
||||
const [count, setCount] = useState(0)
|
||||
|
||||
|
@ -23,14 +24,16 @@ function App() {
|
|||
Wishlist
|
||||
</h1>
|
||||
<Search
|
||||
setSearchResults={setSearchResults}
|
||||
setSearchResult={setSearchResult}
|
||||
searchString={searchString}
|
||||
setSearchString={setSearchString}
|
||||
/>
|
||||
</header>
|
||||
<main>
|
||||
<div>
|
||||
content
|
||||
<SearchResult
|
||||
items={searchResult}
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
<div className="mobile-keyboard-mock" />
|
||||
|
|
|
@ -7,37 +7,37 @@ import debounce from "lodash/debounce";
|
|||
import searchApiCall from "../../helper/omdbHelper.ts";
|
||||
|
||||
interface SearchProps {
|
||||
setSearchResults: Function,
|
||||
setSearchResult: Function,
|
||||
setSearchString: Function,
|
||||
searchString: string,
|
||||
}
|
||||
|
||||
const Search = ({ setSearchResults, setSearchString, searchString }: SearchProps) => {
|
||||
const Search = ({ setSearchResult, setSearchString, searchString }: SearchProps) => {
|
||||
const inputRef = useRef() as MutableRefObject<HTMLInputElement>;
|
||||
|
||||
const debouncedSearch = useMemo(
|
||||
() => debounce(() => {
|
||||
searchApiCall({ searchString: searchString, setter: setSearchResults });
|
||||
searchApiCall({ searchString: searchString, setter: setSearchResult });
|
||||
}, 300),
|
||||
[searchString, setSearchResults]
|
||||
[searchString, setSearchResult]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (searchString.length > 0) {
|
||||
debouncedSearch();
|
||||
} else {
|
||||
setSearchResults([]);
|
||||
setSearchResult([]);
|
||||
}
|
||||
}, [searchString, debouncedSearch, setSearchResults]);
|
||||
}, [searchString, debouncedSearch, setSearchResult]);
|
||||
|
||||
const resetSearchTerm = () => {
|
||||
inputRef.current.focus();
|
||||
setSearchString("");
|
||||
setSearchResults([]);
|
||||
setSearchResult([]);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="search-area">
|
||||
<div className="search-bar">
|
||||
<input
|
||||
value={searchString}
|
||||
type="text"
|
||||
|
|
25
fe/src/components/search/SearchResult.tsx
Normal file
25
fe/src/components/search/SearchResult.tsx
Normal file
|
@ -0,0 +1,25 @@
|
|||
import SearchResultItem from "./SearchResultItem";
|
||||
|
||||
interface SearchResultProps {
|
||||
items: object[],
|
||||
}
|
||||
|
||||
const SearchResult = ({ items }: SearchResultProps) => (
|
||||
<div className="search-result-wrapper">
|
||||
{items.length > 0 && items.map((item) => {
|
||||
const { Title, Poster, imdbID, Year, Type } = item;
|
||||
return (
|
||||
<SearchResultItem
|
||||
title={Title}
|
||||
image={Poster}
|
||||
imdbId={imdbID}
|
||||
key={Poster}
|
||||
year={Year}
|
||||
type={Type}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
|
||||
export default SearchResult;
|
48
fe/src/components/search/SearchResultItem.tsx
Normal file
48
fe/src/components/search/SearchResultItem.tsx
Normal file
|
@ -0,0 +1,48 @@
|
|||
interface SearchResultItemProps {
|
||||
title: string,
|
||||
image: string,
|
||||
imdbId: string,
|
||||
year: string,
|
||||
type: string,
|
||||
clickHandler?: Function,
|
||||
}
|
||||
|
||||
const itemStatusInLibrary = {
|
||||
available: "✅",
|
||||
requested: "⏳",
|
||||
missing: " ",
|
||||
}
|
||||
|
||||
// todo add link to movie if available
|
||||
const SearchResultItem = ({
|
||||
title,
|
||||
image,
|
||||
imdbId,
|
||||
year,
|
||||
type,
|
||||
clickHandler,
|
||||
}: SearchResultItemProps) => (
|
||||
<div
|
||||
className="search-result-item"
|
||||
onClick={() => clickHandler({
|
||||
title,
|
||||
imdbId,
|
||||
year,
|
||||
})}
|
||||
>
|
||||
<img src={image} alt={title} />
|
||||
<section>
|
||||
<div className="search-result-item-interaction">
|
||||
{Object.values(itemStatusInLibrary)[Math.floor(Math.random() * Object.values(itemStatusInLibrary).length)]}
|
||||
</div>
|
||||
<span className="search-result-item-text">
|
||||
{year.substring(0, 4)} - {title}
|
||||
</span>
|
||||
<div className="search-result-item-action">
|
||||
🔗
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default SearchResultItem;
|
Loading…
Reference in a new issue