Skip to content

Commit

Permalink
Merge pull request #5 from iVladyuser/accounts
Browse files Browse the repository at this point in the history
added contactThunk and logic
  • Loading branch information
iVladyuser authored Dec 2, 2023
2 parents d957883 + 7bc7369 commit 1ead3e9
Show file tree
Hide file tree
Showing 13 changed files with 115 additions and 137 deletions.
53 changes: 18 additions & 35 deletions src/components/ContactForm/ContactForm.jsx
Original file line number Diff line number Diff line change
@@ -1,71 +1,54 @@
import React, { useState } from 'react';
import { nanoid } from 'nanoid';
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { FormInput, Form, FormButton, FormLabel } from './ContactForm.styled';
import { contactsSelectors, contactsSlices } from 'redux/contacts';
import { contactsSelectors } from 'redux/contacts';
import { contactsThunk } from 'services';

const ContactForm = () => {
const [name, setName] = useState('');
const [number, setNumber] = useState('');

const dispatch = useDispatch();
const contacts = useSelector(contactsSelectors.selectContacts);

const onSubmitAddContact = e => {
e.preventDefault();
const data = {
const name = e.currentTarget.elements.contactName.value;
const number = e.currentTarget.elements.contactNumber.value;

const formData = {
name,
number: Number.parseFloat(number) || alert(`Number is not correct`),
number,
};
const newContact = { ...data, id: nanoid() };

const isExist = contacts.some(
({ name }) => name.toLowerCase() === newContact.name.toLowerCase()
({ name }) => name.toLowerCase() === formData.name.toLowerCase()
);

if (isExist) {
alert(`Oops, contact '${newContact.name}' is already in contacts!`);
alert(`Oops, contact '${formData.name}' is already in contacts!`);
return;
}

dispatch(contactsSlices.addContact(newContact));
setName('');
setNumber('');
};

const handleInputChange = e => {
const { name, value } = e.currentTarget;
switch (name) {
case 'name':
setName(value);
break;
case 'number':
setNumber(value);
break;

default:
break;
}
dispatch(contactsThunk.addContact(formData))
.unwrap()
.then(() => e.target.reset);
};

return (
<Form onSubmit={onSubmitAddContact}>
<FormLabel htmlFor="name">Name</FormLabel>
<FormInput
type="text"
name="name"
onChange={handleInputChange}
value={name}
name="contactName"
placeholder="Jacob Mercer"
pattern="^[a-zA-Zа-яА-Я]+(([' \-][a-zA-Zа-яА-Я ])?[a-zA-Zа-яА-Я]*)*$"
required
/>

<FormLabel htmlFor="number">Number</FormLabel>
<FormInput
type="tel"
name="number"
value={number}
onChange={handleInputChange}
name="contactNumber"
placeholder="761-23-96"
minLength={3}
pattern="\+?\d{1,4}?[ .\-\s]?\(?\d{1,3}?\)?[ .\-\s]?\d{1,4}[ .\-\s]?\d{1,4}[ .\-\s]?\d{1,9}"
required
/>
Expand Down
47 changes: 27 additions & 20 deletions src/components/ContactList/ContactList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,45 @@ import {
Info,
} from './ContactList.styled';
import { useDispatch, useSelector } from 'react-redux';
// import {
// deleteContact,
// getContacts,
// } from '../../redux/contacts/phoneBookSlice';
// import { selectFilteredContacts } from 'redux/contacts/contactsSelectors';
import { contactsSelectors, contactsSlices } from 'redux/contacts';
import { contactsSelectors } from 'redux/contacts';
import { contactsThunk } from 'services';
import { Loader } from 'components/Loader/Loader';

const ContactList = () => {
const visibleContacts = useSelector(contactsSelectors.selectFilteredContacts);
const dispatch = useDispatch();
const contacts = useSelector(contactsSelectors.selectContacts);
const isLoading = useSelector(contactsSelectors.selectContactsIsLoading);

useEffect(() => {
dispatch(contactsSlices.getContacts());
dispatch(contactsThunk.getContacts());
}, [dispatch]);

const handleDeleteContact = contactId => {
dispatch(contactsSlices.deleteContact(contactId));
dispatch(contactsThunk.deleteContact(contactId));
};

const showContacts = Array.isArray(contacts) && contacts.length > 0;

return (
<List>
{visibleContacts.map(({ name, phone, id }) => (
<ContactItem key={id}>
<CardWrapper>
<Info>{name}</Info>
<Info>{phone}</Info>
<ButtonDelete onClick={() => handleDeleteContact(id)}>
Delete
</ButtonDelete>
</CardWrapper>
</ContactItem>
))}
{isLoading && <Loader />}
{showContacts &&
contacts.map(({ id, name, number }) => {
return (
<ContactItem key={id}>
<CardWrapper>
<Info>{name}</Info>
<Info>{number}</Info>
<ButtonDelete
disabled={isLoading}
onClick={() => handleDeleteContact(id)}
>
Delete
</ButtonDelete>
</CardWrapper>
</ContactItem>
);
})}
</List>
);
};
Expand Down
2 changes: 0 additions & 2 deletions src/components/Filter/Filter.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import React from 'react';
import { FilterContainer, FilterLabel, FilterInput } from './Filter.styled';
import { useDispatch, useSelector } from 'react-redux';
// import { setFilterTerm } from '../../redux/contacts/filterSlice';
// import { selectContactsFilterTerm } from 'redux/contacts/contactsSelectors';
import { contactsSelectors, filterSlice } from 'redux/contacts';
const Filter = () => {
const dispatch = useDispatch();
Expand Down
10 changes: 4 additions & 6 deletions src/pages/ContactsPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import { contactsSelectors } from 'redux/contacts';
const ContactsPage = () => {
const isLoading = useSelector(contactsSelectors.selectContactsIsLoading);
const error = useSelector(contactsSelectors.selectContactsError);
const phoneBook = useSelector(contactsSelectors.selectContacts);
const contacts = useSelector(contactsSelectors.selectContacts);

const isContacts = contacts.length === 0 && !error && !isLoading;
return (
<div
style={{
Expand All @@ -21,11 +23,7 @@ const ContactsPage = () => {
<h1 style={{ color: '#3645ab', marginTop: '50px' }}>Phonebook</h1>
<ContactForm />
<h2>Contacts</h2>
{phoneBook.length === 0 && !error && !isLoading ? (
"You don't have any contacts yet"
) : (
<Filter />
)}
{isContacts ? "You don't have any contacts yet" : <Filter />}
{error ? <Error /> : <ContactList />}
{isLoading && <Loader />}
</div>
Expand Down
31 changes: 13 additions & 18 deletions src/redux/auth/authSlice.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { createSlice, isAnyOf } from '@reduxjs/toolkit';
import {
logOutThunk,
loginThunk,
refreshThunk,
registerThunk,
} from 'services/fetchAuth';
import { authThunk } from 'services';

const initialState = {
isLoading: false,
Expand All @@ -20,32 +15,32 @@ const authSlice = createSlice({
reducers: {},
extraReducers: builder =>
builder
.addCase(loginThunk.fulfilled, (state, { payload }) => {
.addCase(authThunk.loginThunk.fulfilled, (state, { payload }) => {
state.isLoading = false;
state.authenticated = true;
state.token = payload.token;
state.userData = payload.user;
})
.addCase(registerThunk.fulfilled, (state, { payload }) => {
.addCase(authThunk.registerThunk.fulfilled, (state, { payload }) => {
state.isLoading = false;
state.authenticated = true;
state.token = payload.token;
state.userData = payload.user;
})
.addCase(logOutThunk.fulfilled, () => {
.addCase(authThunk.logOutThunk.fulfilled, () => {
return initialState;
})
.addCase(refreshThunk.fulfilled, (state, { payload }) => {
.addCase(authThunk.refreshThunk.fulfilled, (state, { payload }) => {
state.isLoading = false;
state.authenticated = true;
state.userData = payload;
})
.addMatcher(
isAnyOf(
loginThunk.pending,
registerThunk.pending,
refreshThunk.pending,
logOutThunk.pending
authThunk.loginThunk.pending,
authThunk.registerThunk.pending,
authThunk.refreshThunk.pending,
authThunk.logOutThunk.pending
),
state => {
state.isLoading = true;
Expand All @@ -54,10 +49,10 @@ const authSlice = createSlice({
)
.addMatcher(
isAnyOf(
loginThunk.rejected,
registerThunk.rejected,
refreshThunk.rejected,
logOutThunk.rejected
authThunk.loginThunk.rejected,
authThunk.registerThunk.rejected,
authThunk.refreshThunk.rejected,
authThunk.logOutThunk.rejected
),
(state, { payload }) => {
state.isLoading = false;
Expand Down
10 changes: 5 additions & 5 deletions src/redux/contacts/contactsSelectors.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { createSelector } from '@reduxjs/toolkit';

export const selectContacts = state => state.contactsStore.contacts;
export const selectContactsIsLoading = state => state.contactsStore.isLoading;
export const selectContactsError = state => state.contactsStore.error;
export const selectContactsFilterTerm = state => state.filterStore.filterTerm;
export const selectContacts = state => state.phoneBook.contacts;
export const selectContactsIsLoading = state => state.phoneBook.isLoading;
export const selectContactsError = state => state.phoneBook.error;
export const selectContactsFilterTerm = state => state.filter.filterTerm;

export const selectFilteredContacts = createSelector(
[selectContacts, selectContactsFilterTerm],
(contacts, filterTerm) =>
contacts.filter(
contact =>
contact.name.toLowerCase().includes(filterTerm.toLowerCase().trim()) ||
contact.phone.toString().includes(filterTerm.toLowerCase().trim())
contact.number.toString().includes(filterTerm.toLowerCase().trim())
)
);
2 changes: 2 additions & 0 deletions src/redux/contacts/filterSlice.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ const filterSlice = createSlice({
export const { setFilterTerm } = filterSlice.actions;
// Reducer of slice
export const filterReducer = filterSlice.reducer;

export const selectFilter = state => state.filter;
2 changes: 1 addition & 1 deletion src/redux/contacts/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * as contactsSelectors from './contactsSelectors';
export * as contactsSlices from './phoneBookSlice';
export * as contactsSlice from './phoneBookSlice';
export * as filterSlice from './filterSlice';
44 changes: 3 additions & 41 deletions src/redux/contacts/phoneBookSlice.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,5 @@
import { createAsyncThunk, createSlice, isAnyOf } from '@reduxjs/toolkit';
import axios from 'axios';

const URL = 'https://655cdecb25b76d9884fe1656.mockapi.io/contacts';

export const getContacts = createAsyncThunk(
'contacts/fetchAll',
async (_, thunkApi) => {
try {
const response = await axios.get(URL);
return response.data;
} catch (err) {
return thunkApi.rejectWithValue(err.message);
}
}
);

export const deleteContact = createAsyncThunk(
'contacts/deleteContact',
async (contactId, thunkApi) => {
try {
const response = await axios.delete(`${URL}/${contactId}`);
return response.data;
} catch (err) {
return thunkApi.rejectWithValue(err.message);
}
}
);

export const addContact = createAsyncThunk(
'contacts/addContact',
async (newContact, thunkApi) => {
try {
const response = await axios.post(URL, newContact);
return response.data;
} catch (err) {
return thunkApi.rejectWithValue(err.message);
}
}
);
import { createSlice, isAnyOf } from '@reduxjs/toolkit';
import { getContacts, deleteContact, addContact } from 'services/fetchContacts';

const contactInitialState = {
contacts: [],
Expand Down Expand Up @@ -65,7 +27,7 @@ const phoneBookSlice = createSlice({
.addCase(addContact.fulfilled, (state, { payload }) => {
state.isLoading = false;
state.error = null;
state.contacts.push(payload);
state.contacts = [state.contacts, payload];
})
.addMatcher(
isAnyOf(getContacts.pending, deleteContact.pending, addContact.pending),
Expand Down
10 changes: 2 additions & 8 deletions src/redux/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,6 @@ import { contactsReducer } from './contacts/phoneBookSlice';
import { filterReducer } from './contacts/filterSlice';
import { authReducer } from './auth/authSlice';

const contactsConfig = {
key: 'contacts',
storage,
whitelist: ['contacts'],
};

const authConfig = {
key: 'auth',
storage,
Expand All @@ -29,8 +23,8 @@ const authConfig = {

export const store = configureStore({
reducer: {
contactsStore: persistReducer(contactsConfig, contactsReducer),
filterStore: filterReducer,
phoneBook: contactsReducer,
filter: filterReducer,
auth: persistReducer(authConfig, authReducer),
},
middleware: getDefaultMiddleware =>
Expand Down
1 change: 0 additions & 1 deletion src/services/fetchAuth.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ export const refreshThunk = createAsyncThunk(
const token = state.auth.token;
setToken(token);
const { data } = await instance.get('/users/current');
console.log(data);

return data;
} catch (err) {
Expand Down
Loading

0 comments on commit 1ead3e9

Please sign in to comment.