Task: create a login screen which takes userID and password, and invokes login API. If login is successful, it should redirect to home page. Home page shows a table grid with ticket information like ticket id, subject, assigned user, priority, created date. User should be able to search on the ticket's subject.
Mock the apis wherever needed. Use redux to store the session related info returned in login response. Optional: try using typescript if possible.
Solution:
Here’s a clean and simple React + Redux Toolkit example for the described use case:
✅ Features Covered:
- Login screen with userID & password
- Mock Login API
- Redirect to Home on successful login
- Home page shows a searchable ticket table
- Redux for storing session data
- React Router for navigation
🧰 Project Structure
src/
│── App.js
│── index.js
│── store.js
│── features/
│ └── authSlice.js
│── pages/
│ ├── LoginPage.jsx
│ └── HomePage.jsx
│── components/
│ └── TicketTable.jsx
🏪 store.js
import { configureStore } from '@reduxjs/toolkit';
import authReducer from './features/authSlice';
export const store = configureStore({
reducer: {
auth: authReducer,
},
});
🔐 features/authSlice.js
import { createSlice } from '@reduxjs/toolkit';
const initialState = {
user: null,
token: null,
};
const authSlice = createSlice({
name: 'auth',
initialState,
reducers: {
setUser: (state, action) => {
state.user = action.payload.user;
state.token = action.payload.token;
},
logout: (state) => {
state.user = null;
state.token = null;
},
},
});
export const { setUser, logout } = authSlice.actions;
export default authSlice.reducer;
📄 pages/LoginPage.jsx
import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import { setUser } from '../features/authSlice';
const mockLoginAPI = (userID, password) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (userID === 'admin' && password === '1234') {
resolve({ user: { id: 1, name: 'Admin' }, token: 'abcd1234' });
} else {
reject('Invalid credentials');
}
}, 800);
});
};
export default function LoginPage() {
const [userID, setUserID] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const dispatch = useDispatch();
const navigate = useNavigate();
const handleLogin = async (e) => {
e.preventDefault();
setError('');
try {
const response = await mockLoginAPI(userID, password);
dispatch(setUser(response));
navigate('/home');
} catch (err) {
setError(err);
}
};
return (
<div style={{ maxWidth: 300, margin: '50px auto' }}>
<h2>Login</h2>
<form onSubmit={handleLogin}>
<input
type="text"
placeholder="User ID"
value={userID}
onChange={(e) => setUserID(e.target.value)}
required
/>
<br /><br />
<input
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
<br /><br />
<button type="submit">Login</button>
</form>
{error && <p style={{ color: 'red' }}>{error}</p>}
</div>
);
}
🏡 pages/HomePage.jsx
import React, { useState } from 'react';
import TicketTable from '../components/TicketTable';
import { useSelector } from 'react-redux';
import { useNavigate } from 'react-router-dom';
export default function HomePage() {
const { user } = useSelector((state) => state.auth);
const [search, setSearch] = useState('');
const navigate = useNavigate();
const tickets = [
{ id: 1, subject: 'Login issue', assigned: 'John', priority: 'High', created: '2025-10-10' },
{ id: 2, subject: 'Payment failed', assigned: 'Alice', priority: 'Medium', created: '2025-10-11' },
{ id: 3, subject: 'Bug in dashboard', assigned: 'Bob', priority: 'Low', created: '2025-10-12' },
];
const filteredTickets = tickets.filter(t =>
t.subject.toLowerCase().includes(search.toLowerCase())
);
if (!user) {
navigate('/');
}
return (
<div style={{ maxWidth: 800, margin: '40px auto' }}>
<h2>Welcome, {user?.name}</h2>
<input
type="text"
placeholder="Search by subject..."
value={search}
onChange={(e) => setSearch(e.target.value)}
style={{ marginBottom: '20px', padding: '5px' }}
/>
<TicketTable tickets={filteredTickets} />
</div>
);
}
🧾 components/TicketTable.jsx
import React from 'react';
export default function TicketTable({ tickets }) {
return (
<table border="1" width="100%" cellPadding="8">
<thead>
<tr>
<th>ID</th>
<th>Subject</th>
<th>Assigned User</th>
<th>Priority</th>
<th>Created Date</th>
</tr>
</thead>
<tbody>
{tickets.map(ticket => (
<tr key={ticket.id}>
<td>{ticket.id}</td>
<td>{ticket.subject}</td>
<td>{ticket.assigned}</td>
<td>{ticket.priority}</td>
<td>{ticket.created}</td>
</tr>
))}
</tbody>
</table>
);
}
🌐 App.js
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import LoginPage from './pages/LoginPage';
import HomePage from './pages/HomePage';
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<LoginPage />} />
<Route path="/home" element={<HomePage />} />
</Routes>
</Router>
);
}
export default App;
🏁 index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import { store } from './store';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<App />
</Provider>
);
🧠 How it works:
- The
LoginPagemocks an API and dispatches user info to Redux. - On success, it redirects to
/home. - The
HomePageshows ticket data and lets users search by subject. - Redux stores session info, and routing ensures proper access control.