Skip to content

Commit 1706767

Browse files
Merge pull request #313 from AndreWohnsland/dev
Add virgin only search option to v2
2 parents b8880c4 + c2b993b commit 1706767

File tree

5 files changed

+52
-53
lines changed

5 files changed

+52
-53
lines changed

web_client/README.md

Lines changed: 10 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,19 @@
1-
# React + TypeScript + Vite
1+
# CocktailBerry Web Client
22

3-
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
3+
This is the frontend for the CocktailBerry, using Vite, React, and TypeScript.
44

5-
Currently, two official plugins are available:
5+
## Getting Started
66

7-
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
8-
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
7+
Install the dependencies (you could also use npm instead of yarn):
98

10-
## Expanding the ESLint configuration
11-
12-
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
13-
14-
- Configure the top-level `parserOptions` property like this:
15-
16-
```js
17-
export default tseslint.config({
18-
languageOptions: {
19-
// other options...
20-
parserOptions: {
21-
project: ['./tsconfig.node.json', './tsconfig.app.json'],
22-
tsconfigRootDir: import.meta.dirname,
23-
},
24-
},
25-
})
9+
```bash
10+
yarn install
2611
```
2712

28-
- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
29-
- Optionally add `...tseslint.configs.stylisticTypeChecked`
30-
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:
13+
## Running the Development Server
3114

32-
```js
33-
// eslint.config.js
34-
import react from 'eslint-plugin-react'
15+
To start the development server, run:
3516

36-
export default tseslint.config({
37-
// Set the react version
38-
settings: { react: { version: '18.3' } },
39-
plugins: {
40-
// Add the react plugin
41-
react,
42-
},
43-
rules: {
44-
// other rules...
45-
// Enable its recommended rules
46-
...react.configs.recommended.rules,
47-
...react.configs['jsx-runtime'].rules,
48-
},
49-
})
17+
```bash
18+
yarn dev
5019
```

web_client/src/components/cocktail/CocktailList.tsx

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ const CocktailList: React.FC = () => {
1717
const { data: cocktails, error, isLoading } = useCocktails(true, config.MAKER_MAX_HAND_INGREDIENTS || 0);
1818
const [selectedCocktail, setSelectedCocktail] = useState<Cocktail | null>(null);
1919
const [singleIngredientOpen, setSingleIngredientOpen] = useState(false);
20-
const [search, setSearch] = useState('');
20+
const [search, setSearch] = useState<string | null>(null);
21+
const [showOnlyVirginPossible, setShowOnlyVirginPossible] = useState(false);
2122
const { t } = useTranslation();
2223

2324
if (isLoading) return <LoadingData />;
@@ -31,6 +32,10 @@ const CocktailList: React.FC = () => {
3132
setSelectedCocktail(null);
3233
};
3334

35+
const handleHideToggle = () => {
36+
setShowOnlyVirginPossible(!showOnlyVirginPossible);
37+
};
38+
3439
let displayedCocktails = cocktails;
3540
if (search) {
3641
displayedCocktails = displayedCocktails?.filter(
@@ -39,10 +44,27 @@ const CocktailList: React.FC = () => {
3944
cocktail.ingredients.some((ingredient) => ingredient.name.toLowerCase().includes(search.toLowerCase())),
4045
);
4146
}
47+
if (search !== null && showOnlyVirginPossible) {
48+
displayedCocktails = displayedCocktails?.filter((cocktail) => cocktail.virgin_available);
49+
displayedCocktails = displayedCocktails?.map((cocktail) => {
50+
return { ...cocktail, only_virgin: true };
51+
});
52+
}
53+
54+
const virginToggleButton = (
55+
<button
56+
onClick={handleHideToggle}
57+
className={`flex items-center justify-center p-2 !border pointer-events-auto ${
58+
showOnlyVirginPossible ? 'button-secondary' : 'button-primary'
59+
}`}
60+
>
61+
<MdNoDrinks size={20} />
62+
</button>
63+
);
4264

4365
return (
4466
<div className='px-2 centered max-w-7xl'>
45-
<SearchBar search={search} setSearch={setSearch}></SearchBar>
67+
<SearchBar search={search} setSearch={setSearch} afterInput={virginToggleButton} />
4668
<div className='flex flex-wrap gap-3 justify-center items-center w-full mb-4'>
4769
{displayedCocktails
4870
?.sort((a, b) => a.name.localeCompare(b.name))

web_client/src/components/common/SearchBar.tsx

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,22 @@ import { useTranslation } from 'react-i18next';
33
import { FaEraser, FaSearch } from 'react-icons/fa';
44

55
interface SearchBarProps {
6-
search: string;
7-
setSearch: (value: string) => void;
6+
search: string | null;
7+
setSearch: (value: string | null) => void;
8+
afterInput?: React.ReactNode;
89
}
9-
10-
const SearchBar: React.FC<SearchBarProps> = ({ search, setSearch }) => {
11-
const [savedSearch, setSavedSearch] = React.useState(search);
10+
// note: the internal search should never be null, but we communicate to external component with null
11+
// if the search is hidden. This is to know externally if the search is shown or not, and should be applied.
12+
const SearchBar: React.FC<SearchBarProps> = ({ search, setSearch, afterInput }) => {
13+
const [savedSearch, setSavedSearch] = React.useState<string>(search ?? '');
1214
const [showSearch, setShowSearch] = React.useState(false);
1315
const { t } = useTranslation();
1416

1517
const handleHideToggle = () => {
18+
// it is currently shown, so need to save the search value and hide it
1619
if (showSearch) {
17-
setSavedSearch(search);
18-
setSearch('');
20+
setSavedSearch(search ?? '');
21+
setSearch(null);
1922
} else {
2023
setSearch(savedSearch);
2124
}
@@ -28,7 +31,7 @@ const SearchBar: React.FC<SearchBarProps> = ({ search, setSearch }) => {
2831
<input
2932
type='text'
3033
placeholder={t('search')}
31-
value={search}
34+
value={search ?? ''}
3235
onChange={(e) => setSearch(e.target.value)}
3336
className='input-base mr-1 w-full p-3 max-w-sm'
3437
hidden={!showSearch}
@@ -41,6 +44,11 @@ const SearchBar: React.FC<SearchBarProps> = ({ search, setSearch }) => {
4144
<FaEraser size={20} />
4245
</button>
4346
</div>
47+
{afterInput && (
48+
<div hidden={!showSearch} className='mr-1'>
49+
{afterInput}
50+
</div>
51+
)}
4452
<button
4553
onClick={handleHideToggle}
4654
className='button-primary flex items-center justify-center p-2 !border pointer-events-auto'

web_client/src/components/ingredient/IngredientList.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { useTranslation } from 'react-i18next';
1515
const IngredientList: React.FC = () => {
1616
const { data: ingredients, isLoading, error, refetch } = useIngredients();
1717
const [selectedIngredient, setSelectedIngredient] = useState<IngredientInput | null>(null);
18-
const [search, setSearch] = useState('');
18+
const [search, setSearch] = useState<string | null>(null);
1919
const { t } = useTranslation();
2020

2121
if (isLoading) return <LoadingData />;

web_client/src/components/recipe/RecipeList.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const RecipeList: React.FC = () => {
2525
const [selectedCocktail, setSelectedCocktail] = useState<CocktailInput | null>(null);
2626
const { data: ingredients, isLoading: ingredientsLoading, error: ingredientsError } = useIngredients();
2727
const fileInputRef = useRef<HTMLInputElement>(null);
28-
const [search, setSearch] = useState('');
28+
const [search, setSearch] = useState<string | null>(null);
2929
const { t } = useTranslation();
3030

3131
if (isLoading || ingredientsLoading) return <LoadingData />;

0 commit comments

Comments
 (0)