Skip to content

Commit 90e55cc

Browse files
committed
feat: search bar, left section styles
1 parent 7730636 commit 90e55cc

File tree

5 files changed

+101
-142
lines changed

5 files changed

+101
-142
lines changed

src/components/NavigationBar/NavigationBar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const NavigationBar = () => {
2626

2727
return (
2828
<nav
29-
className="fixed top-0 w-full z-50 opacity-80 bg-black
29+
className="sticky top-0 w-full z-50 opacity-80 bg-black
3030
text-white py-2 px-6 flex justify-between items-center"
3131
style={{ height: '8vh' }}
3232
>

src/components/SearchBar/SearchBar.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,13 @@ const SearchBar: React.FC<Props> = ({ onSearch }) => {
3232

3333
return (
3434
<div className="mb-8">
35-
<h2 className="text-lg font-semibold mb-4">Search</h2>
3635
<input
3736
type="text"
3837
placeholder="Search themes..."
3938
value={query}
4039
onChange={handleChange}
4140
onKeyDown={handleKeyDown}
42-
className="w-full border rounded-md px-3 py-2"
41+
className="border rounded-md px-2 py-1 w-3/4 bg-accent-800 text-xs"
4342

4443
/>
4544
</div>

src/components/Themes/ThemeCard.tsx

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
import React, { useState } from 'react';
22

3-
import { Link } from 'react-router-dom';
4-
5-
import ThemeModal from './ThemeModal';
63
import { Theme } from '../../interfaces/Theme';
7-
import FavIcon from '../../assets/images/icon_favorite.svg';
8-
import GitHubIcon from '../../assets/images/icon_github_white.svg';
94

105
import '../../styles/theme_card.css';
116

@@ -19,43 +14,34 @@ type Props = {
1914
* Theme card component to hold the details of each theme in the themes page.
2015
*/
2116
const ThemeCard: React.FC<Props> = ({ theme, isPreviewed, onPreview }) => {
22-
const [isFav, setIsFav] = useState(false);
23-
const [viewDetails, setViewDetails] = useState(false);
24-
25-
const onViewDetails = () => {
26-
setViewDetails(true);
27-
};
28-
2917
const onClickPreview = () => {
3018
onPreview(theme.name);
3119
};
3220

33-
const [isCardSelected, setIsCardSelected] = useState(false);
34-
3521
const handleCheckboxChange = () => {
36-
setIsCardSelected(!isCardSelected);
22+
onClickPreview();
3723
};
3824

3925
return (
40-
<div className={`flex flex-row md:flex-col md:w-64 p-4 my-4 mx-2 border-2 rounded-xl ${isCardSelected ? 'border-blue-700' : 'border-accent-600'}`}>
26+
<div className={`flex flex-row md:flex-col md:w-64 p-4 my-4 mx-2 border-2 border-solid rounded-xl ${isPreviewed ? 'border-blue-700' : 'border-accent-600'}`}>
4127
<div className="flex-1 basis-1/5 md:basis-1/2 mr-3 flex flex-row overflow-hidden w-32 h-20 md:w-56 md:h-56 rounded-xl">
4228
<img
4329
src={theme.themeImg}
4430
alt={theme.name}
4531
className="w-60 h-60 object-cover object-left-top"
4632
/>
4733
</div>
48-
<div className="flex-1 mr-4 basis-3/5 md:basis-1/3 flex flex-col justify-center">
49-
<h2 className="text-accent-50">{theme.name}</h2>
50-
<span className="text-accent-300">{theme.description}</span>
51-
<span className="text-blue-500">More Info (i)</span>
34+
<div className="flex-1 mr-4 basis-3/5 md:basis-1/3 flex flex-col pt-4">
35+
<h2 className="text-accent-50 text-lg font-medium">{theme.name}</h2>
36+
<span className="text-accent-300 text-sm">{theme.description}</span>
5237
</div>
53-
<div className="flex-1 basis-1/5 md:basis-1/6 flex items-center">
38+
<div className="flex-1 basis-1/5 md:basis-1/6 flex flex-col">
39+
<span className="text-blue-500 text-sm my-4">More Info (i)</span>
5440
<label className="text-accent-50">
5541
Select
5642
<input
5743
type="checkbox"
58-
checked={isCardSelected}
44+
checked={isPreviewed}
5945
onChange={handleCheckboxChange}
6046
className="ml-2"
6147
/>

src/pages/Themes.tsx

Lines changed: 81 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -10,146 +10,111 @@ import useFetchData from '../hooks/useFetchThemes';
1010
import { Theme } from '../interfaces/Theme';
1111
import { Endpoints } from '../constants/Endpoints';
1212
import { useSearchParams } from 'react-router-dom';
13+
import DesktopThemePreview from '../components/Themes/DesktopThemePreview';
1314

1415
/**
1516
* Displays themes for users to search, browse and rate.
1617
* // todo: dynamically load themes as user scrolls instead of fetching wholesale from backend
1718
*/
1819
const Themes: React.FC = () => {
19-
//search param hook to access URL
20-
const [searchParams, setSearchParams] = useSearchParams();
20+
//search param hook to access URL
21+
const [searchParams, setSearchParams] = useSearchParams();
2122

22-
// search query for filtering themes to show
23-
const [searchQuery, setSearchQuery] = useState(
24-
() => searchParams.get('searchQuery') || ''
25-
);
23+
// search query for filtering themes to show
24+
const [searchQuery, setSearchQuery] = useState(
25+
() => searchParams.get('searchQuery') || ''
26+
);
2627

27-
// id of themes being selected to be preview (and applied to the interactive chatbot)
28-
const [previewIds, setPreviewIds] = useState<string[]>([]);
28+
// id of themes being selected to be preview (and applied to the interactive chatbot)
29+
const [previewIds, setPreviewIds] = useState<string[]>([]);
2930

30-
// theme data fetched from backend
31+
// theme data fetched from backend
3132

32-
const { themes, loading, error } = useFetchData(
33-
Endpoints.fetchApiThemes,
34-
30,
35-
1,
36-
searchQuery
37-
);
38-
/**
33+
const { themes, loading, error } = useFetchData(
34+
Endpoints.fetchApiThemes,
35+
30,
36+
1,
37+
searchQuery
38+
);
39+
40+
console.log(themes)
41+
/**
3942
* Handles setting of search query when user hits enter.
4043
*
4144
* @param query query user inputted
4245
*/
43-
const handleSearch = (query: string) => {
44-
if (query === '') {
45-
searchParams.delete('searchQuery');
46-
} else {
47-
searchParams.set('searchQuery', query);
48-
}
49-
setSearchParams(searchParams);
50-
setSearchQuery(query);
51-
};
46+
const handleSearch = (query: string) => {
47+
if (query === '') {
48+
searchParams.delete('searchQuery');
49+
} else {
50+
searchParams.set('searchQuery', query);
51+
}
52+
setSearchParams(searchParams);
53+
setSearchQuery(query);
54+
};
5255

53-
/**
56+
/**
5457
* Handles setting and unsetting of theme ids to preview.
5558
*
5659
* @param id id of theme to set or unset
5760
*/
58-
const onPreview = (id: string) => {
59-
setPreviewIds((prevPreviewId) =>
60-
prevPreviewId.includes(id)
61-
? prevPreviewId.filter((item) => item !== id)
62-
: [...prevPreviewId, id]
63-
);
64-
};
61+
const onPreview = (id: string) => {
62+
setPreviewIds((prevPreviewId) =>
63+
prevPreviewId.includes(id)
64+
? prevPreviewId.filter((item) => item !== id)
65+
: [...prevPreviewId, id]
66+
);
67+
};
6568

66-
/**
69+
/**
6770
* Clears all preview ids.
6871
*/
69-
const clearPreviewIds = () => {
70-
setPreviewIds([]);
71-
};
72-
73-
// flow for interactive chatbot
74-
const flow = {
75-
start: {
76-
message: (params: Params) => {
77-
params.injectMessage(
78-
'Hello 👋! Did you know? The order of specifying themes matters!'
79-
);
80-
return 'Try previewing some themes below, or click on those on the left! 😊';
81-
},
82-
checkboxes: { items: ['Minimal Midnight', 'Cyborg', 'Terminal'] },
83-
function: (params: Params) => {
84-
setPreviewIds(
85-
params.userInput.split(',').map((theme) => {
86-
if (theme === 'Minimal Midnight') {
87-
return 'minimal_midnight';
88-
} else if (theme === 'Cyborg') {
89-
return 'cyborg';
90-
} else {
91-
return 'terminal';
92-
}
93-
})
94-
);
95-
},
96-
chatDisabled: true,
97-
path: 'end',
98-
},
99-
end: {
100-
message: "What's next? 😊",
101-
options: ['Try Again', 'Check Documentation', 'Discord'],
102-
path: (params: Params) => {
103-
if (params.userInput === 'Try Again') {
104-
setPreviewIds([]);
105-
} else if (params.userInput === 'Discord') {
106-
window.open('https://discord.gg/6R4DK4G5Zh');
107-
} else if (params.userInput === 'Check Documentation') {
108-
window.open('https://react-chatbotify.com');
109-
} else {
110-
setPreviewIds([]);
111-
params.injectMessage(
112-
"Hmmm I'm not sure what you said, but let's try again!"
113-
);
114-
}
115-
return 'start';
116-
},
117-
},
118-
};
72+
const clearPreviewIds = () => {
73+
setPreviewIds([]);
74+
};
11975

120-
// todo: show a proper error message if themes are not able to be fetched
121-
if (error) {
122-
return <div>Error: {error.message}</div>;
123-
}
76+
// todo: show a proper error message if themes are not able to be fetched
77+
if (error) {
78+
return <div>Error: {error.message}</div>;
79+
}
12480

125-
return (
126-
<div className="bg-accent-950 flex h-screen w-full">
127-
{/* Main Content Section */}
128-
<div className="mt-20 overflow-y-scroll hide-scrollbar w-full">
129-
<div className="text-accent-50">
130-
{/* Headers */}
131-
<h1>Select Theme(s)</h1>
132-
<h2>
133-
You can select multiple themes and combine them however you like.
134-
</h2>
135-
<h2>How choosing multiple themes work (i)</h2>
136-
</div>
137-
<div className="w-full flex flex-col md:flex-row md:flex-wrap">
138-
{/* Card Content */}
139-
{themes.map((theme) => {
140-
return (
141-
<ThemeCard
142-
key={theme.id}
143-
theme={theme}
144-
isPreviewed={previewIds.includes(theme.id)}
145-
onPreview={() => onPreview(theme.id)}
146-
/>
147-
);
148-
})}
149-
</div>
150-
</div>
151-
</div>
152-
);
81+
return (
82+
<div className="bg-accent-950 flex h-screen w-full">
83+
{/* Main Content Section */}
84+
<div className="overflow-y-scroll hide-scrollbar w-full flex flex-col">
85+
<div className="ml-14 mt-6 text-accent-50">
86+
{/* Headers */}
87+
<h1 className="text-2xl font-semibold pb-3">Select Theme(s)</h1>
88+
<h2 className="text-accent-300 text-sm mb-2">
89+
You can select multiple themes and combine them however you like.
90+
</h2>
91+
{/* TODO: this will be a button that opens a modal or redirects */}
92+
<h2 className="text-blue-500 text-sm">How choosing multiple themes work (i)</h2>
93+
<div className="mt-4">
94+
<SearchBar onSearch={handleSearch} /></div>
95+
</div>
96+
<div className="flex flex-col md:grid md:grid-cols-[repeat(auto-fill,270px)] justify-center">
97+
{/* Card Content */}
98+
{themes.map((theme) => {
99+
return (
100+
<ThemeCard
101+
key={theme.id}
102+
theme={theme}
103+
isPreviewed={previewIds.includes(theme.id)}
104+
onPreview={() => onPreview(theme.id)}
105+
/>
106+
);
107+
})}
108+
</div>
109+
</div>
110+
{/* Drawer Section */}
111+
<DesktopThemePreview
112+
setPreviewIds={setPreviewIds}
113+
previewIds={previewIds}
114+
clearPreviewIds={clearPreviewIds}
115+
/>
116+
</div>
117+
);
153118
};
154119

155120
export default Themes;

src/utils.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ const downloadThemeContent = async (
3232
}
3333
}
3434

35+
// Function which takes preview ids formatted as 'word1_word2' and returns it formatted as 'Word1 Word2'
36+
const formatPreviewIdToTitleCase = (input: string): string => {
37+
return input
38+
.split('_')
39+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
40+
.join(' ');
41+
}
42+
3543
export {
36-
downloadThemeContent
44+
downloadThemeContent,
45+
formatPreviewIdToTitleCase
3746
}

0 commit comments

Comments
 (0)