Charvee commited on
Commit
e8eb17b
·
verified ·
1 Parent(s): 36fccc0

Upload 10 files

Browse files
frontend-project/Button.css ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .btn {
2
+ padding: 10px 20px;
3
+ border: none;
4
+ border-radius: 4px;
5
+ font-size: 16px;
6
+ cursor: pointer;
7
+ transition: background-color 0.3s ease;
8
+ }
9
+
10
+ .btn-primary {
11
+ background-color: #007bff;
12
+ color: white;
13
+ }
14
+
15
+ .btn-primary:hover {
16
+ background-color: #0056b3;
17
+ }
18
+
19
+ .btn-secondary {
20
+ background-color: #6c757d;
21
+ color: white;
22
+ }
23
+
24
+ .btn-secondary:hover {
25
+ background-color: #545b62;
26
+ }
frontend-project/Button.stories.tsx ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ import { Meta, StoryObj } from '@storybook/react';
3
+ import { Button } from './Button';
4
+
5
+ const meta: Meta<typeof Button> = {
6
+ title: 'Components/Button',
7
+ component: Button,
8
+ parameters: {
9
+ layout: 'centered',
10
+ },
11
+ tags: ['autodocs'],
12
+ };
13
+
14
+ export default meta;
15
+ type Story = StoryObj<typeof Button>;
16
+
17
+ export const Primary: Story = {
18
+ args: {
19
+ label: 'Primary Button',
20
+ variant: 'primary',
21
+ },
22
+ };
23
+
24
+ export const Secondary: Story = {
25
+ args: {
26
+ label: 'Secondary Button',
27
+ variant: 'secondary',
28
+ },
29
+ };
30
+
31
+ export const WithClickHandler: Story = {
32
+ args: {
33
+ label: 'Click Me',
34
+ onClick: () => alert('Button clicked!'),
35
+ },
36
+ };
frontend-project/Button.test.tsx ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ import { render, screen, fireEvent } from '@testing-library/react';
3
+ import { Button } from './Button';
4
+
5
+ describe('Button Component', () => {
6
+ it('renders with label', () => {
7
+ render(<Button label="Click me" />);
8
+ expect(screen.getByText('Click me')).toBeInTheDocument();
9
+ });
10
+
11
+ it('calls onClick when clicked', () => {
12
+ const handleClick = jest.fn();
13
+ render(<Button label="Click me" onClick={handleClick} />);
14
+ fireEvent.click(screen.getByText('Click me'));
15
+ expect(handleClick).toHaveBeenCalledTimes(1);
16
+ });
17
+
18
+ it('applies variant class', () => {
19
+ render(<Button label="Secondary" variant="secondary" />);
20
+ const button = screen.getByText('Secondary');
21
+ expect(button).toHaveClass('btn-secondary');
22
+ });
23
+ });
frontend-project/Button.tsx ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ import './Button.css';
3
+
4
+ interface ButtonProps {
5
+ label: string;
6
+ onClick?: () => void;
7
+ variant?: 'primary' | 'secondary';
8
+ }
9
+
10
+ export const Button: React.FC<ButtonProps> = ({
11
+ label,
12
+ onClick,
13
+ variant = 'primary'
14
+ }) => {
15
+ return (
16
+ <button
17
+ className={`btn btn-${variant}`}
18
+ onClick={onClick}
19
+ >
20
+ {label}
21
+ </button>
22
+ );
23
+ };
frontend-project/userSlice.test.ts ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import userReducer, { setCurrentUser, addUser, removeUser, setLoading, setError } from './userSlice';
2
+ import { User } from './userSlice';
3
+
4
+ describe('userSlice', () => {
5
+ const mockUser: User = {
6
+ id: '1',
7
+ name: 'John Doe',
8
+ email: 'john@example.com',
9
+ };
10
+
11
+ it('should return the initial state', () => {
12
+ expect(userReducer(undefined, { type: 'unknown' })).toEqual({
13
+ currentUser: null,
14
+ users: [],
15
+ loading: false,
16
+ error: null,
17
+ });
18
+ });
19
+
20
+ it('should handle setCurrentUser', () => {
21
+ const actual = userReducer(undefined, setCurrentUser(mockUser));
22
+ expect(actual.currentUser).toEqual(mockUser);
23
+ });
24
+
25
+ it('should handle addUser', () => {
26
+ const actual = userReducer(undefined, addUser(mockUser));
27
+ expect(actual.users).toContainEqual(mockUser);
28
+ });
29
+
30
+ it('should handle removeUser', () => {
31
+ const state = {
32
+ currentUser: null,
33
+ users: [mockUser],
34
+ loading: false,
35
+ error: null,
36
+ };
37
+ const actual = userReducer(state, removeUser('1'));
38
+ expect(actual.users).not.toContainEqual(mockUser);
39
+ });
40
+
41
+ it('should handle setLoading', () => {
42
+ const actual = userReducer(undefined, setLoading(true));
43
+ expect(actual.loading).toBe(true);
44
+ });
45
+
46
+ it('should handle setError', () => {
47
+ const errorMessage = 'An error occurred';
48
+ const actual = userReducer(undefined, setError(errorMessage));
49
+ expect(actual.error).toBe(errorMessage);
50
+ });
51
+ });
frontend-project/userSlice.ts ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { createSlice, PayloadAction } from '@reduxjs/toolkit';
2
+
3
+ export interface User {
4
+ id: string;
5
+ name: string;
6
+ email: string;
7
+ }
8
+
9
+ interface UserState {
10
+ currentUser: User | null;
11
+ users: User[];
12
+ loading: boolean;
13
+ error: string | null;
14
+ }
15
+
16
+ const initialState: UserState = {
17
+ currentUser: null,
18
+ users: [],
19
+ loading: false,
20
+ error: null,
21
+ };
22
+
23
+ const userSlice = createSlice({
24
+ name: 'user',
25
+ initialState,
26
+ reducers: {
27
+ setCurrentUser: (state, action: PayloadAction<User>) => {
28
+ state.currentUser = action.payload;
29
+ },
30
+ addUser: (state, action: PayloadAction<User>) => {
31
+ state.users.push(action.payload);
32
+ },
33
+ removeUser: (state, action: PayloadAction<string>) => {
34
+ state.users = state.users.filter(user => user.id !== action.payload);
35
+ },
36
+ setLoading: (state, action: PayloadAction<boolean>) => {
37
+ state.loading = action.payload;
38
+ },
39
+ setError: (state, action: PayloadAction<string | null>) => {
40
+ state.error = action.payload;
41
+ },
42
+ },
43
+ });
44
+
45
+ export const { setCurrentUser, addUser, removeUser, setLoading, setError } = userSlice.actions;
46
+ export default userSlice.reducer;
frontend-project/userSlice.types.ts ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { User } from './userSlice';
2
+
3
+ export interface UserApiResponse {
4
+ success: boolean;
5
+ data: User | User[];
6
+ message?: string;
7
+ }
8
+
9
+ export interface UserFormData {
10
+ name: string;
11
+ email: string;
12
+ }
13
+
14
+ export interface UserFilters {
15
+ search?: string;
16
+ role?: string;
17
+ status?: 'active' | 'inactive';
18
+ }
19
+
20
+ export type UserSortField = 'name' | 'email' | 'createdAt';
21
+ export type UserSortOrder = 'asc' | 'desc';
22
+
23
+ export interface UserSortOptions {
24
+ field: UserSortField;
25
+ order: UserSortOrder;
26
+ }
frontend-project/webpack.config.js ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const path = require('path');
2
+
3
+ module.exports = {
4
+ entry: './src/index.js',
5
+ output: {
6
+ path: path.resolve(__dirname, 'dist'),
7
+ filename: 'bundle.js',
8
+ },
9
+ module: {
10
+ rules: [
11
+ {
12
+ test: /\.tsx?$/,
13
+ use: 'ts-loader',
14
+ exclude: /node_modules/,
15
+ },
16
+ {
17
+ test: /\.css$/,
18
+ use: ['style-loader', 'css-loader'],
19
+ },
20
+ ],
21
+ },
22
+ resolve: {
23
+ extensions: ['.tsx', '.ts', '.js'],
24
+ },
25
+ };
frontend-project/webpack.dev.js ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const { merge } = require('webpack-merge');
2
+ const common = require('./webpack.config.js');
3
+
4
+ module.exports = merge(common, {
5
+ mode: 'development',
6
+ devtool: 'inline-source-map',
7
+ devServer: {
8
+ contentBase: './dist',
9
+ hot: true,
10
+ port: 3000,
11
+ },
12
+ });
frontend-project/webpack.prod.js ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const { merge } = require('webpack-merge');
2
+ const common = require('./webpack.config.js');
3
+
4
+ module.exports = merge(common, {
5
+ mode: 'production',
6
+ devtool: 'source-map',
7
+ optimization: {
8
+ minimize: true,
9
+ splitChunks: {
10
+ chunks: 'all',
11
+ },
12
+ },
13
+ });