Skip to content
This repository was archived by the owner on Feb 21, 2025. It is now read-only.

Commit 1815c14

Browse files
Merge pull request #3 from foundersandcoders/AlexVOiceover/issue2
Implemented dropdowns
2 parents 78ac8ba + 6fe8c83 commit 1815c14

File tree

9 files changed

+295
-69
lines changed

9 files changed

+295
-69
lines changed

reactModStructure.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<img width="265" alt="image" src="https://github.com/user-attachments/assets/83b3b76e-3ea2-4c5e-932e-3786de8b82c6" />

src/components/ui/Dropdown.tsx

Lines changed: 30 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,46 @@
1-
import React, { useState } from 'react';
2-
1+
import React from 'react';
32
interface DropdownProps {
4-
label: string;
3+
label?: string;
54
options: string[];
65
onSelect: (value: string) => void;
7-
renderOption?: (option: string) => React.ReactNode; // Optional render function for custom option rendering
6+
includeAll?: boolean;
7+
placeholder?: string;
8+
className?: string;
89
}
910

1011
const Dropdown: React.FC<DropdownProps> = ({
1112
label,
1213
options,
1314
onSelect,
14-
renderOption,
15+
includeAll = false,
16+
placeholder,
17+
className,
1518
}) => {
16-
const [isOpen, setIsOpen] = useState(false);
17-
const [selectedOption, setSelectedOption] = useState('All');
18-
19-
const handleOptionClick = (option: string) => {
20-
setSelectedOption(option);
21-
onSelect(option);
22-
setIsOpen(false);
23-
};
19+
const displayedOptions = includeAll ? ['All', ...options] : options;
2420

2521
return (
26-
<div className='relative'>
27-
<button
28-
className='bg-gray-300 text-black px-4 py-2 rounded-lg hover:bg-gray-400'
29-
onClick={() => setIsOpen((prev) => !prev)}
30-
>
31-
{selectedOption === 'All' ? `${label}` : selectedOption}
32-
</button>
33-
{isOpen && (
34-
<ul className='absolute left-0 mt-2 bg-white border border-gray-300 rounded-md shadow-md z-10 text-black'>
35-
{options.map((option) => (
36-
<li
37-
key={option}
38-
className='px-4 py-2 hover:bg-gray-100 cursor-pointer'
39-
onClick={() => handleOptionClick(option)}
40-
>
41-
{renderOption ? renderOption(option) : option}
42-
</li>
43-
))}
44-
</ul>
22+
<div className='w-full'>
23+
{label && (
24+
<label className='block text-sm font-medium text-white mb-1'>
25+
{label}
26+
</label>
4527
)}
28+
<select
29+
className={`w-full p-2 border bg-gray-200 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 text-black ${className}`}
30+
onChange={(e) => onSelect(e.target.value)}
31+
defaultValue=''
32+
>
33+
{placeholder && (
34+
<option value='' disabled>
35+
{placeholder}
36+
</option>
37+
)}
38+
{displayedOptions.map((option) => (
39+
<option key={option} value={option}>
40+
{option}
41+
</option>
42+
))}
43+
</select>
4644
</div>
4745
);
4846
};
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import React from 'react';
2+
import Dropdown from './Dropdown';
3+
4+
interface SubjectDropdownProps {
5+
subject: string;
6+
descriptors: string[];
7+
onSelect: (value: string) => void;
8+
placeholder?: string;
9+
}
10+
11+
const SubjectDropdown: React.FC<SubjectDropdownProps> = ({
12+
subject,
13+
descriptors,
14+
onSelect,
15+
placeholder,
16+
}) => {
17+
// Build the final options array:
18+
// ["Bob", "Bob's social media presence", "Bob's coffee addiction", ...]
19+
const dropdownOptions = [
20+
subject,
21+
...descriptors.map((desc) => `${subject}'s ${desc}`),
22+
];
23+
24+
return (
25+
<Dropdown
26+
options={dropdownOptions}
27+
onSelect={onSelect}
28+
placeholder={placeholder}
29+
/>
30+
);
31+
};
32+
33+
export default SubjectDropdown;

src/components/ui/TextInputGen.tsx

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import React, { useState } from 'react';
2+
interface TextInputGenProps {
3+
subjectName?: string;
4+
onAccept: (value: string) => void;
5+
// onCancel: () => void;
6+
autocomplete: string[];
7+
}
8+
9+
const TextInputGen: React.FC<TextInputGenProps> = ({
10+
subjectName = '',
11+
onAccept,
12+
// onCancel,
13+
autocomplete,
14+
}) => {
15+
const [inputValue, setInputValue] = useState(`${subjectName}`);
16+
const [suggestions, setSuggestions] = useState<string[]>([]);
17+
const [selectedIndex, setSelectedIndex] = useState<number | null>(null);
18+
19+
// const typedDictionary: string[] = autocomplete;
20+
21+
// Show all descriptors on focus
22+
const handleFocus = () => {
23+
setSuggestions(autocomplete);
24+
setSelectedIndex(null);
25+
};
26+
27+
const handleInputChange = (value: string) => {
28+
setInputValue(value);
29+
setSelectedIndex(null); // Reset selected index
30+
31+
// Filter dictionary to find matches
32+
if (value.trim() === '') {
33+
setSuggestions(autocomplete);
34+
} else {
35+
const lowerCaseValue = value.toLowerCase();
36+
const filtered = autocomplete.filter((word) =>
37+
word.toLowerCase().startsWith(lowerCaseValue)
38+
);
39+
setSuggestions(filtered);
40+
}
41+
};
42+
43+
// const handleAccept = () => {
44+
// onAccept(inputValue);
45+
// setSuggestions([]); // Clear suggestions on accept
46+
// };
47+
48+
const handleSuggestionClick = (suggestion: string) => {
49+
setInputValue(`${subjectName}'s ${suggestion}`); // Update input with the selected suggestion
50+
setSuggestions([]); // Clear suggestions
51+
onAccept(`${subjectName}'s ${suggestion}`);
52+
};
53+
54+
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
55+
if (suggestions.length === 0) return;
56+
57+
if (e.key === 'ArrowDown') {
58+
setSelectedIndex((prev) =>
59+
prev === null || prev === suggestions.length - 1 ? 0 : prev + 1
60+
);
61+
} else if (e.key === 'ArrowUp') {
62+
setSelectedIndex((prev) =>
63+
prev === null || prev === 0 ? suggestions.length - 1 : prev - 1
64+
);
65+
} else if (e.key === 'Enter') {
66+
if (selectedIndex !== null) {
67+
handleSuggestionClick(suggestions[selectedIndex]);
68+
} else {
69+
onAccept(inputValue); // Accept typed text if no suggestion is highlighted
70+
setSuggestions([]);
71+
}
72+
e.preventDefault();
73+
}
74+
};
75+
76+
return (
77+
<div className='flex flex-col space-y-4 relative'>
78+
<input
79+
type='text'
80+
className='w-full p-2 border bg-gray-200 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 text-black'
81+
value={inputValue}
82+
onChange={(e) => handleInputChange(e.target.value)}
83+
onFocus={handleFocus}
84+
onKeyDown={handleKeyDown} // Added onKeyDown
85+
/>
86+
<span className='absolute right-2 text-gray-500 pointer-events-none'>
87+
{' '}
88+
</span>
89+
{/* Suggestions Dropdown */}
90+
{suggestions.length > 0 && (
91+
<ul className='left-0 w-full bg-white border border-gray-300 rounded-lg shadow-lg max-h-60 overflow-y-auto z-10 text-black text-left'>
92+
{suggestions.map((suggestion, index) => (
93+
<li
94+
key={index}
95+
className={`px-4 py-2 hover:bg-blue-100 cursor-pointer ${
96+
selectedIndex === index ? 'bg-blue-500 text-white' : ''
97+
}`}
98+
onClick={() => handleSuggestionClick(suggestion)}
99+
>
100+
{`${subjectName}'s ${suggestion}`}
101+
</li>
102+
))}
103+
</ul>
104+
)}
105+
106+
{/* <div className='flex justify-start space-x-2'>
107+
<button
108+
onClick={handleAccept}
109+
className='bg-blue-500 text-white px-4 py-2 rounded-lg hover:bg-blue-600'
110+
>
111+
Accept
112+
</button>
113+
<button
114+
onClick={onCancel}
115+
className='bg-gray-500 text-white px-4 py-2 rounded-lg hover:bg-gray-600'
116+
>
117+
Cancel
118+
</button>
119+
</div> */}
120+
</div>
121+
);
122+
};
123+
124+
export default TextInputGen;

src/context/AppContext.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ interface AppContextInterface {
99
setStatements: React.Dispatch<React.SetStateAction<PreStatement[]>>;
1010
employer: string;
1111
setEmployer: React.Dispatch<React.SetStateAction<string>>;
12+
descriptors: string[];
13+
setDescriptors: React.Dispatch<React.SetStateAction<string[]>>;
1214
}
1315

1416
// Create the actual context with a default value.
@@ -20,6 +22,8 @@ export const AppContext = createContext<AppContextInterface>({
2022
setEmployer: () => {},
2123
statements: [],
2224
setStatements: () => {},
25+
descriptors: [],
26+
setDescriptors: () => {},
2327
});
2428

2529
// 3) Create a Provider component that will wrap your app.
@@ -31,6 +35,7 @@ export const AppProvider: React.FC<AppProviderProps> = ({ children }) => {
3135
const [userName, setUserName] = useState('');
3236
const [employer, setEmployer] = useState('');
3337
const [statements, setStatements] = useState<PreStatement[]>([]);
38+
const [descriptors, setDescriptors] = useState<string[]>([]);
3439

3540
// Prepare the value to share across the app
3641
const value: AppContextInterface = {
@@ -40,6 +45,8 @@ export const AppProvider: React.FC<AppProviderProps> = ({ children }) => {
4045
setEmployer,
4146
statements,
4247
setStatements,
48+
descriptors,
49+
setDescriptors,
4350
};
4451

4552
return <AppContext.Provider value={value}>{children}</AppContext.Provider>;

src/data/subjects.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
[
2+
{
3+
"subject": "Alice",
4+
"descriptors": ["project goals", "gym routine", "artistic interests"]
5+
},
6+
{
7+
"subject": "Bob",
8+
"descriptors": [
9+
"chess club",
10+
"coffee addiction",
11+
"running hobby",
12+
"social media presence"
13+
]
14+
},
15+
{
16+
"subject": "Charlie",
17+
"descriptors": ["book collection", "travel plans", "family gatherings"]
18+
},
19+
{
20+
"subject": "Diana",
21+
"descriptors": [
22+
"volunteer work",
23+
"photography passion",
24+
"culinary experiments",
25+
"fitness goals"
26+
]
27+
},
28+
{
29+
"subject": "Eve",
30+
"descriptors": ["marketing ideas", "coding practice", "gardening hobby"]
31+
}
32+
]

src/pages/employees/EmployeesDetails.tsx

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,21 @@ import { AppContext } from '../../context/AppContext';
33
import { useNavigate } from 'react-router-dom';
44
import employers from '../../data/employers.json';
55
import defaultStatements from '../../data/defaultStatements.json';
6+
import descriptorsData from '../../data/subjects.json';
7+
import Dropdown from '../../components/ui/Dropdown';
68

79
const EmployeesDetails: React.FC = () => {
810
const navigate = useNavigate();
911

1012
// Use AppContext to get `userName` and `setUserName`
11-
const { userName, setUserName, employer, setEmployer, setStatements } =
12-
useContext(AppContext);
13+
const {
14+
userName,
15+
setUserName,
16+
employer,
17+
setEmployer,
18+
setStatements,
19+
setDescriptors,
20+
} = useContext(AppContext);
1321

1422
// Local state for the user’s name and the selected employer
1523
// const [employeeName, setEmployeeName] = useState('');
@@ -28,7 +36,20 @@ const EmployeesDetails: React.FC = () => {
2836
console.log('Populated Statements:', populatedStatements);
2937

3038
setStatements(populatedStatements);
31-
// setEmployer(employer); // Already handled via onChange
39+
40+
// A helper function to get descriptors by subject
41+
function getSubjectDescriptors(subject: string): string[] {
42+
const found = descriptorsData.find((d) => d.subject === subject);
43+
return found ? found.descriptors : [];
44+
}
45+
46+
// 1) Find the descriptors for the userName
47+
const subjectDescriptors = getSubjectDescriptors(userName);
48+
49+
// 2) Set them in context
50+
setDescriptors(subjectDescriptors);
51+
52+
console.log('Populated Descriptors:', subjectDescriptors);
3253

3354
// Navigate to the Sentence Builder route (e.g., /employees/statements)
3455
navigate('/employees/statements', {
@@ -61,18 +82,11 @@ const EmployeesDetails: React.FC = () => {
6182
{/* Employer dropdown */}
6283
<div>
6384
<label className='block mb-1'>Select Employer:</label>
64-
<select
65-
value={employer}
66-
onChange={(e) => setEmployer(e.target.value)}
67-
className='w-full border border-gray-400 rounded px-2 py-1 bg-gray-900 text-white'
68-
>
69-
<option value=''>-- Choose an employer --</option>
70-
{employers.map((employer) => (
71-
<option key={employer.company} value={employer.company}>
72-
{employer.company}
73-
</option>
74-
))}
75-
</select>
85+
<Dropdown
86+
options={employers.map((employer) => employer.company)}
87+
onSelect={(value) => setEmployer(value)}
88+
placeholder='Select Employer'
89+
/>
7690
</div>
7791

7892
{/* Next Button */}

0 commit comments

Comments
 (0)