Skip to content

Commit 76c0d46

Browse files
committed
feat: Added login page and wizard also posts
1 parent 8d32e56 commit 76c0d46

File tree

11 files changed

+171
-74
lines changed

11 files changed

+171
-74
lines changed

.env.development

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
VITE_API_URL=http://localhost:8080/
1+
VITE_API_URL=http://localhost:8080

data/descriptors.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"descriptors": [
3+
"Family",
4+
"Future Plans",
5+
"Financial Situation",
6+
"Career Goals",
7+
"Professional Development",
8+
"Work-Life Balance",
9+
"Job Satisfaction",
10+
"Team Collaboration",
11+
"Health & Wellness",
12+
"Personal Interests",
13+
"Skills & Competencies",
14+
"Training & Development"
15+
]
16+
}

data/preStatements.json

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,32 @@
11
[
22
{
33
"id": "1",
4-
"subject": "Eve",
54
"verb": "Support",
65
"object": "their team",
76
"isPublic": true
87
},
98
{
109
"id": "2",
11-
"subject": "Eve's coding practice",
10+
"descriptor": "coding practice",
1211
"verb": "Improve",
1312
"object": "software quality",
1413
"isPublic": false
1514
},
1615
{
1716
"id": "3",
18-
"subject": "Eve",
1917
"verb": "Create",
2018
"object": "innovative solutions",
2119
"isPublic": true
2220
},
2321
{
2422
"id": "4",
25-
"subject": "Eve's marketing ideas",
23+
"descriptor": "marketing ideas",
2624
"verb": "Engage",
2725
"object": "potential customers",
2826
"isPublic": true
2927
},
3028
{
3129
"id": "5",
32-
"subject": "Eve",
3330
"verb": "Learn",
3431
"object": "new technologies",
3532
"isPublic": false

src/App.tsx

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,25 @@
11
'use client';
22

3-
import StatementBuilder from './components/StatementBuilder';
4-
import StatementWizard from './components/StatementWizard';
5-
import StatementList from './components/StatementList';
3+
import React, { useState } from 'react';
64
import { StatementsProvider } from './context/StatementsProvider';
5+
import LoginPage from './components/LoginPage';
6+
import MainPage from './components/MainPage';
77

8-
const USERNAME = 'Eve';
8+
const App: React.FC = () => {
9+
// Local state to store the username from the login page.
10+
const [username, setUsername] = useState('');
911

10-
export default function Home() {
1112
return (
1213
<StatementsProvider>
13-
<main className='min-h-screen bg-gradient-to-b from-gray-50 to-gray-100 py-12'>
14-
<div className='container mx-auto px-4 max-w-3xl'>
15-
<h1 className='text-3xl font-bold mb-8 text-center'>
16-
Statement Builders for {USERNAME}
17-
</h1>
18-
19-
<div className='bg-white rounded-xl shadow-lg p-6 '>
20-
<StatementWizard username={USERNAME} />
21-
<StatementBuilder username={USERNAME} />
22-
<StatementList username={USERNAME} />
23-
</div>
24-
</div>
25-
</main>
14+
{username ? (
15+
// If username is set, show the main app page.
16+
<MainPage username={username} />
17+
) : (
18+
// Otherwise, show the login page.
19+
<LoginPage onSubmit={setUsername} />
20+
)}
2621
</StatementsProvider>
2722
);
28-
}
23+
};
24+
25+
export default App;

src/components/LoginPage.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
'use client';
2+
3+
import React, { useState } from 'react';
4+
import { Button } from './ui/button';
5+
import { Input } from './ui/input';
6+
7+
interface LoginPageProps {
8+
onSubmit: (username: string) => void;
9+
}
10+
11+
const LoginPage: React.FC<LoginPageProps> = ({ onSubmit }) => {
12+
const [name, setName] = useState('');
13+
14+
const handleSubmit = (e: React.FormEvent) => {
15+
e.preventDefault();
16+
if (name.trim()) {
17+
onSubmit(name.trim());
18+
}
19+
};
20+
21+
return (
22+
<div className='min-h-screen flex flex-col items-center justify-center p-4'>
23+
<h1 className='text-3xl font-bold mb-4'>Welcome!</h1>
24+
<p className='mb-4'>Please enter your name to continue:</p>
25+
<form onSubmit={handleSubmit} className='w-full max-w-sm'>
26+
<Input
27+
placeholder='Enter your name'
28+
value={name}
29+
onChange={(e) => setName(e.target.value)}
30+
/>
31+
<Button type='submit' className='mt-4 w-full'>
32+
Continue
33+
</Button>
34+
</form>
35+
</div>
36+
);
37+
};
38+
39+
export default LoginPage;

src/components/MainPage.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
'use client';
2+
3+
import React from 'react';
4+
import StatementBuilder from './StatementBuilder';
5+
import StatementWizard from './StatementWizard';
6+
import StatementList from './StatementList';
7+
8+
interface MainPageProps {
9+
username: string;
10+
}
11+
12+
const MainPage: React.FC<MainPageProps> = ({ username }) => {
13+
return (
14+
<main className='min-h-screen bg-gradient-to-b from-gray-50 to-gray-100 py-12 '>
15+
<div className='container mx-auto px-4'>
16+
<h1 className='text-3xl font-bold mb-8 text-center'>
17+
Statement Builders for {username}
18+
</h1>
19+
<div className='flex flex-col justify-center max-w-3xl mx-auto'>
20+
<div className='bg-white rounded-xl shadow-lg p-6'>
21+
<StatementWizard username={username} />
22+
<StatementBuilder username={username} />
23+
</div>
24+
25+
<StatementList username={username} />
26+
</div>
27+
</div>
28+
</main>
29+
);
30+
};
31+
32+
export default MainPage;

src/components/StatementBuilder.tsx

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,15 @@ import { Eye, EyeOff } from 'lucide-react';
1212
import { ConfirmationDialog } from './ui/confirmation-dialog';
1313
import { useStatements } from '../hooks/useStatements';
1414
import { postNewStatement } from '../api/statementsApi';
15-
16-
// We reuse the Statement type from your types file; if not, you can define it here:
17-
interface Statement {
18-
id: string;
19-
subject: string;
20-
verb: string;
21-
object: string;
22-
isPublic: boolean;
23-
}
15+
import type { Statement } from '../../types/types';
2416

2517
interface StatementBuilderProps {
2618
username: string;
2719
}
2820

2921
const StatementBuilder: React.FC<StatementBuilderProps> = ({ username }) => {
3022
// Get the current statements and the dispatch function from context.
31-
const { state, dispatch } = useStatements();
23+
const { dispatch, state } = useStatements();
3224
const { statements } = state;
3325

3426
// Local form states.
@@ -42,7 +34,7 @@ const StatementBuilder: React.FC<StatementBuilderProps> = ({ username }) => {
4234
statement: Statement | null;
4335
}>({ isOpen: false, statement: null });
4436

45-
// Callback for creation-mode verb selection.
37+
// Callback for verb selection.
4638
const handleVerbSelect = (selectedVerb: Verb) => {
4739
const presentTenseVerb = nlp(selectedVerb.name)
4840
.verbs()

src/components/StatementList.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,25 @@ const StatementList: React.FC<{ username: string }> = ({ username }) => {
5050
// If there are no statements, set some defaults.
5151
useEffect(() => {
5252
if (statements.length === 0) {
53-
dispatch({ type: 'SET_STATEMENTS', payload: preStatements });
53+
const newDefaults: Statement[] = (
54+
preStatements as Array<
55+
Omit<Statement, 'subject'> & { descriptor?: string }
56+
>
57+
).map((stmt) => {
58+
const subject = stmt.descriptor
59+
? `${username}'s ${stmt.descriptor}`
60+
: username;
61+
return {
62+
...stmt,
63+
subject,
64+
// Optionally, regenerate the id:
65+
id:
66+
Date.now().toString() + Math.random().toString(36).substring(2, 7),
67+
};
68+
});
69+
dispatch({ type: 'SET_STATEMENTS', payload: newDefaults });
5470
}
55-
}, [statements, dispatch]);
71+
}, [username, dispatch, statements.length]);
5672

5773
// Handler for editing verb selection in edit mode.
5874
const handleEditVerbSelect = (selectedVerb: Verb, statementId: string) => {

src/components/StatementWizard.tsx

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,19 @@ import { Button } from './ui/button';
1111
import { Input } from './ui/input';
1212
import { Plus, ArrowLeft, Eye, EyeOff } from 'lucide-react';
1313
import { motion, AnimatePresence } from 'framer-motion';
14-
import subjectsData from '../../data/subjects.json';
14+
import descriptorsData from '../../data/descriptors.json';
1515
import { verbData } from '../../utils/verbUtils';
1616
import { useStatements } from '../hooks/useStatements';
17-
import type { SubjectData } from '../../types/types';
18-
17+
import { postNewStatement } from '../api/statementsApi';
1918
import type React from 'react';
2019
import type { PreStatement, Statement } from '../../types/types';
2120

2221
type Step = 'closed' | 'who' | 'action' | 'what' | 'privacy';
2322

23+
interface DescriptorsData {
24+
descriptors: string[];
25+
}
26+
2427
const StatementWizard: React.FC<{ username: string }> = ({ username }) => {
2528
const { dispatch } = useStatements();
2629

@@ -36,17 +39,12 @@ const StatementWizard: React.FC<{ username: string }> = ({ username }) => {
3639

3740
// Filter subject tiles based on the provided username.
3841
const subjectTiles = useMemo(() => {
39-
// Cast imported JSON data to an array (ideally typed as SubjectData[])
40-
const subjects = subjectsData as SubjectData[];
41-
const userSubject = subjects.find(
42-
(subject) => subject.subject === username
43-
);
44-
if (!userSubject) return [];
42+
const data = descriptorsData as DescriptorsData;
4543
return [
46-
{ label: userSubject.subject, value: userSubject.subject },
47-
...userSubject.descriptors.map((descriptor: string) => ({
48-
label: `${userSubject.subject}'s ${descriptor}`,
49-
value: `${userSubject.subject}'s ${descriptor}`,
44+
{ label: username, value: username },
45+
...data.descriptors.map((descriptor: string) => ({
46+
label: `${username}'s ${descriptor}`,
47+
value: `${username}'s ${descriptor}`,
5048
})),
5149
];
5250
}, [username]);
@@ -152,13 +150,19 @@ const StatementWizard: React.FC<{ username: string }> = ({ username }) => {
152150
}
153151
};
154152

155-
// Instead of using an onComplete prop, we dispatch a new statement into context.
156-
const handleComplete = () => {
153+
const handleComplete = async () => {
157154
const newStatement: Statement = {
158155
...selection,
159156
id: Date.now().toString(),
160157
};
158+
159+
// Dispatch the new statement to the context.
161160
dispatch({ type: 'ADD_STATEMENT', payload: newStatement });
161+
162+
// Post the new statement to the backend.
163+
await postNewStatement(newStatement);
164+
165+
// Close the wizard.
162166
handleClose();
163167
};
164168

src/components/ui/subject-selector.tsx

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,19 @@ import React, { useState, useMemo } from 'react';
22
import { Check, ChevronsUpDown, Plus } from 'lucide-react';
33
import { cn } from '../../../lib/utils';
44
import { Button } from './button';
5-
import { Input } from './input';
65
import { Popover, PopoverContent, PopoverTrigger } from './popover';
7-
import subjectsData from '../../../data/subjects.json';
8-
import type { SubjectData } from '../../../types/types';
6+
import { Input } from './input';
7+
// Import descriptors from the new JSON file.
8+
import descriptorsData from '../../../data/descriptors.json';
99

10-
const subjects = subjectsData as SubjectData[];
10+
interface DescriptorsData {
11+
descriptors: string[];
12+
}
13+
14+
interface SubjectOption {
15+
label: string;
16+
value: string;
17+
}
1118

1219
interface SubjectSelectorProps {
1320
value: string;
@@ -22,36 +29,33 @@ const SubjectSelector: React.FC<SubjectSelectorProps> = ({
2229
onAddDescriptor,
2330
username,
2431
}) => {
25-
// Retrieve initial descriptors for the user.
26-
const initialDescriptors = useMemo(() => {
27-
const subjectData = subjects.find((s) => s.subject === username);
28-
return subjectData ? subjectData.descriptors : [];
32+
// Build the subject options using the entered name and descriptors from descriptors.json.
33+
const subjectTiles: SubjectOption[] = useMemo(() => {
34+
const data = descriptorsData as DescriptorsData;
35+
const baseOption = { label: username, value: username };
36+
const descriptorOptions = data.descriptors.map((descriptor) => ({
37+
label: `${username}'s ${descriptor}`,
38+
value: `${username}'s ${descriptor}`,
39+
}));
40+
return [baseOption, ...descriptorOptions];
2941
}, [username]);
3042

31-
// Local state for descriptors, search text, popover open state, etc.
32-
const [descriptors, setDescriptors] = useState<string[]>(initialDescriptors);
43+
// Local state for search, popover open state, and adding a new descriptor.
3344
const [open, setOpen] = useState(false);
3445
const [search, setSearch] = useState('');
3546
const [isAddingNew, setIsAddingNew] = useState(false);
3647
const [newDescriptor, setNewDescriptor] = useState('');
3748

38-
// Create the options list: the primary subject and each descriptor option.
49+
// Filter the options based on the search text.
3950
const options = useMemo(() => {
40-
const baseOption = { label: username, value: username };
41-
const descriptorOptions = descriptors.map((descriptor) => ({
42-
label: `${username}'s ${descriptor}`,
43-
value: `${username}'s ${descriptor}`,
44-
}));
45-
// Filter based on search text.
46-
return [baseOption, ...descriptorOptions].filter((opt) =>
51+
return subjectTiles.filter((opt) =>
4752
opt.label.toLowerCase().includes(search.toLowerCase())
4853
);
49-
}, [descriptors, search, username]);
54+
}, [subjectTiles, search]);
5055

5156
const handleAddNewDescriptor = () => {
5257
if (newDescriptor.trim()) {
5358
onAddDescriptor(newDescriptor);
54-
setDescriptors((prev) => [...prev, newDescriptor]);
5559
onChange(`${username}'s ${newDescriptor}`);
5660
setNewDescriptor('');
5761
setIsAddingNew(false);

0 commit comments

Comments
 (0)