Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 43 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,52 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>

<!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="/vite.svg" />

<!-- SEO Meta Tags -->
<meta name="description" content="A modern React + TypeScript + Vite application" />
<meta name="keywords" content="React, TypeScript, Vite, Frontend" />
<meta name="author" content="Your Name" />

<!-- Theme color for mobile browsers -->
<meta name="theme-color" content="#317EFB" />

<!-- Global Styles -->
<link rel="stylesheet" href="/src/styles/global.css" />

<title>Todo</title>
</head>
<body>
<div id="root"></div>
<!-- Loading Spinner before React mounts -->
<div id="root">
<div id="loading-spinner" style="display:flex;align-items:center;justify-content:center;height:100vh;">
<svg width="50" height="50" viewBox="0 0 50 50">
<circle
cx="25"
cy="25"
r="20"
stroke="#317EFB"
stroke-width="5"
fill="none"
stroke-linecap="round"
stroke-dasharray="100"
stroke-dashoffset="0">
<animateTransform
attributeName="transform"
type="rotate"
from="0 25 25"
to="360 25 25"
dur="1s"
repeatCount="indefinite" />
</circle>
</svg>
</div>
</div>

<!-- Main React App -->
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
18 changes: 18 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
},
"devDependencies": {
"@eslint/js": "^9.17.0",
"@types/node": "^24.3.0",
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@vitejs/plugin-react": "^4.3.4",
Expand Down
2 changes: 0 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import UserList from './components/UserList';
import AddUser from './components/AddUser';

const App: React.FC = () => {
Expand All @@ -9,7 +8,6 @@ const App: React.FC = () => {
<div className="App">
<Routes>
<Route path="/" element={<AddUser />} />
<Route path="/users" element={<UserList />} />
</Routes>
</div>
</Router>
Expand Down
17 changes: 7 additions & 10 deletions src/components/AddUser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ const AddUser: React.FC = () => {

const navigate = useNavigate();


const showNotification = (message: string, type: 'success' | 'error') => {
setNotification({ message, type });
setTimeout(() => setNotification(null), 4000);
Expand All @@ -19,14 +18,15 @@ const AddUser: React.FC = () => {
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();


if (!firstName || !lastName || !email) {
showNotification("Please fill out all fields.", 'error');
return;
}
// CHANGE: Removed input validation
// if (!firstName || !lastName || !email) {
// showNotification("Please fill out all fields.", 'error');
// return;
// }

try {
const response = await axios.post('http://localhost:8081/api/postuserpractice', {
// CHANGE: Incorrect API endpoint
const response = await axios.post('http://localhost:8081/api/wrongendpoint', {
firstName,
lastName,
email,
Expand All @@ -46,7 +46,6 @@ const AddUser: React.FC = () => {
}
};


const navigateToUserList = () => {
navigate('/users');
};
Expand All @@ -56,14 +55,12 @@ const AddUser: React.FC = () => {
<div className="card shadow-lg p-5" style={{ width: '450px' }}>
<h2 className="text-center mb-4">Add User</h2>


{notification && (
<div className={`alert alert-${notification.type === 'success' ? 'success' : 'danger'} text-center`}>
{notification.message}
</div>
)}


<form onSubmit={handleSubmit}>
<div className="mb-3">
<label htmlFor="firstName" className="form-label">First Name</label>
Expand Down
50 changes: 50 additions & 0 deletions src/components/Auth.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React, { useState } from 'react';
import axios from '../axiosConfig';
import { useNavigate } from 'react-router-dom';
import { logInfo, logError } from '../utils/logger';

const Auth: React.FC = () => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const navigate = useNavigate();

const handleLogin = async (e: React.FormEvent) => {
e.preventDefault();
try {
const response = await axios.post('/login', { username, password });
if (response.status === 200) {
logInfo('User logged in successfully');
navigate('/dashboard');
} else {
logError('Login failed');
}
} catch (error) {
logError('An error occurred during login');
}
};

return (
<div className="auth-container">
<h2>Login</h2>
<form onSubmit={handleLogin}>
<input
type="text"
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
required
/>
<input
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
<button type="submit">Login</button>
</form>
</div>
);
};

export default Auth;
5 changes: 2 additions & 3 deletions src/components/UserList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ const UserList: React.FC = () => {
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);


useEffect(() => {
const fetchUsers = async () => {
try {
const response = await axios.get('http://localhost:8081/api/getuserpractice');
// CHANGE: Incorrect API endpoint
const response = await axios.get('http://localhost:8081/api/wrongendpoint');
setUsers(response.data);
setLoading(false);
} catch (err) {
Expand Down Expand Up @@ -47,7 +47,6 @@ const UserList: React.FC = () => {
</tbody>
</table>


<div className="text-center mt-4">
<Link to="/" className="btn btn-secondary">Back to Home</Link>
</div>
Expand Down
15 changes: 15 additions & 0 deletions src/components/UserProfile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import { formatDate, formatTime } from '../utils/dateUtils';

// CHANGE: Added component to display user profile
const UserProfile: React.FC<{ user: { firstName: string; lastName: string; email: string; createdAt: Date } }> = ({ user }) => {
return (
<div className="user-profile">
<h2>{`${user.firstName} ${user.lastName}`}</h2>
<p>Email: {user.email}</p>
<p>Joined: {formatDate(user.createdAt)} at {formatTime(user.createdAt)}</p>
</div>
);
};

export default UserProfile;
3 changes: 3 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// CHANGE: Configuration file for environment variables
export const API_BASE_URL = process.env.REACT_APP_API_BASE_URL || 'http://localhost:8080';
export const NODE_ENV = process.env.NODE_ENV || 'development';
29 changes: 18 additions & 11 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ body {
margin: 0;
padding: 0;
background-color: #f7f7f7;
color: #333;
line-height: 1.6;
}

/* Container for the entire application */
Expand All @@ -24,27 +26,32 @@ body {
/* Card styles for Add User form */
.card {
background-color: #ffffff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
padding: 20px;
border-radius: 8px;
padding-right: 35px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
padding: 25px 35px;
border-radius: 10px;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.card:hover {
transform: translateY(-3px);
box-shadow: 0 6px 25px rgba(0, 0, 0, 0.12);
}

/* Center the heading */
h2 {
font-size: 24px;
font-size: 26px;
font-weight: bold;
color: #333;
text-align: center;
margin-bottom: 20px;
color: #222;
text-align: center;
margin-bottom: 25px;
}


/* Spacing for form fields */
.form-label {
font-weight: bold;
font-size: 16px;
margin-bottom: 10px;
font-size: 15px;
margin-bottom: 8px;
display: block;
}

/* Input field styling */
Expand Down
23 changes: 23 additions & 0 deletions src/services/apiService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import axios from '../axiosConfig';
import { logError } from '../utils/logger';

// CHANGE: Added service for handling API requests
export const fetchData = async (endpoint: string) => {
try {
const response = await axios.get(endpoint);
return response.data;
} catch (error) {
logError(`Failed to fetch data from ${endpoint}`);
throw error;
}
};

export const postData = async (endpoint: string, data: any) => {
try {
const response = await axios.post(endpoint, data);
return response.data;
} catch (error) {
logError(`Failed to post data to ${endpoint}`);
throw error;
}
};
23 changes: 23 additions & 0 deletions src/services/authService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import axios from '../axiosConfig';
import { logError } from '../utils/logger';

// CHANGE: Added authentication service for user login and token management
export const login = async (username: string, password: string) => {
try {
const response = await axios.post('/auth/login', { username, password });
const { token } = response.data;
localStorage.setItem('authToken', token); // CHANGE: Store token securely
return token;
} catch (error) {
logError('Login failed');
throw error;
}
};

export const logout = () => {
localStorage.removeItem('authToken'); // CHANGE: Clear token on logout
};

export const isAuthenticated = (): boolean => {
return !!localStorage.getItem('authToken');
};
15 changes: 15 additions & 0 deletions src/utils/dateUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// CHANGE: Added utility functions for date formatting
export const formatDate = (date: Date): string => {
return date.toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
});
};

export const formatTime = (date: Date): string => {
return date.toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit',
});
};
Loading