From af804567a07895b3685728928355f7e6dab8787d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Herna=CC=81ndez?= Date: Fri, 12 Oct 2018 15:30:31 +0100 Subject: [PATCH 1/9] updated card design and infinite scroll with the new user structure --- index.html | 2 -- js/infinite_scroll.js | 84 ++++++++++++++++++++++++++++++++----------- js/search_bar.js | 6 ++-- 3 files changed, 66 insertions(+), 26 deletions(-) diff --git a/index.html b/index.html index 6b3318c..56a365d 100644 --- a/index.html +++ b/index.html @@ -8,8 +8,6 @@ CV App - diff --git a/js/infinite_scroll.js b/js/infinite_scroll.js index e16b63e..8b31d9c 100644 --- a/js/infinite_scroll.js +++ b/js/infinite_scroll.js @@ -1,27 +1,41 @@ const cardsContainer = document.querySelector('#cards-container'); const searchInput = document.getElementById("nav-input"); +// State variables to control activation of infinite scroll, page of users to request and store of all users requested let isFetchAllowed = true; +let currentUsersPage = 1; let loadedUsers = []; // Initial content load from API function fetchUsersData() { if (searchInput.value == '') { $.ajax({ - url: "https://jsonplaceholder.typicode.com/users" + url: `https://cv-mobile-api.herokuapp.com/api/users/pages/${currentUsersPage}` }).done((data) => { - // Show the loader while loading the content - // showLoader(); - // Iterate over the data array and extract the info for each user in a card variable - data.map( (user) => { - const card = renderCard( user.name, user.username, user.email, user.company.name, user.id); - // Add the card with the user info to the DOM - cardsContainer.innerHTML += card; - // Save the user inside a collections with all loaded users - loadedUsers.push(user); - }); - // Hide the loader after the content has loaded - hideLoader(); + + if (data.length === 10) { + data.map( (user) => { + const card = renderCard(user); + cardsContainer.innerHTML += card; + loadedUsers.push(user); + }); + // Add one to the current page of users for the next request if the page has 10 users + currentUsersPage++; + + } else if (data.length < 10) { + + if (data[0]._id !== loadedUsers[loadedUsers.length - 1]._id) { + data.map( (user) => { + const card = renderCard(user); + cardsContainer.innerHTML += card; + loadedUsers.push(user); + }); + isFetchAllowed = false; + } + } + + // Hide the loader after the content has loaded + hideLoader(); }); } } @@ -29,22 +43,50 @@ function fetchUsersData() { fetchUsersData(); // Create an html card template with the user data -function renderCard(name, userName, email, company, userId, highlight) { +function renderCard(user, highlight) { + const { + name, + username, + company, + email, + languages, + skills, + _id, + location, + experience, + profilePicture, + } = user; + var template_cards = ( - '
' + - 'Card image cap' + + '
' + + '' + name + ' Profile picture' + '
' + - '
' + name + '
' + - '

Username: ' + userName + '

' + - '

Email: ' + email + '

' + - '

Company: ' + company + '

' + - 'View Profile' + + '

' + name + '

' + + '

person ' + username + '

' + + '

email ' + email + '

' + + '

work ' + company + '

' + + '

today ' + experience + '

' + + '

translate ' + languages.join(', ') + '

' + + '

location_city ' + location.city + ', ' + location.country + '

' + + '
' + createBadges(skills) + '
' + + 'View Profile' + '
' + '
' ); + return template_cards; } +function createBadges(skills) { + const skillBadges = []; + + skills.forEach( skill => { + skillBadges.push(`
${skill}
`); + }); + + return skillBadges.join(''); +} + function showLoader() { loader.style.display = 'block'; } diff --git a/js/search_bar.js b/js/search_bar.js index d0167f9..e8cb959 100644 --- a/js/search_bar.js +++ b/js/search_bar.js @@ -18,7 +18,7 @@ searchInput.addEventListener("keyup", function(e) { function renderCategoryOfFilteredUsers(category, categoryName) { category.forEach( (user) => { - const card = renderCard( user.name, user.username, user.email, user.company.name, user.id, categoryName); + const card = renderCard( user, categoryName); cardsContainer.innerHTML += card; }) } @@ -56,8 +56,8 @@ searchInput.addEventListener("keyup", function(e) { // Empty the cardContainer with the filtered Results cardsContainer.innerHTML = ''; // Render the default users loaded with the initial ajax call and render them into the DOM again - loadedUsers.forEach((user, index) => { - const card = renderCard( user.name, user.username, user.email, user.company.name, index); + loadedUsers.forEach((user) => { + const card = renderCard(user); cardsContainer.innerHTML += card; }); } From f829d866f158f8eb89735adb74d7ebedf944303b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Herna=CC=81ndez?= Date: Sat, 13 Oct 2018 16:26:23 +0100 Subject: [PATCH 2/9] Fixed cards design, display new info into the profile view and merged edit and enter profile js files into one --- css/styles.css | 15 ++++++++++ html/profile.html | 42 ++++++++++++++++++--------- js/edit-profile.js | 14 --------- js/enterProfile.js | 27 ------------------ js/infinite_scroll.js | 20 +++++++------ js/user-profile.js | 66 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 120 insertions(+), 64 deletions(-) delete mode 100644 js/edit-profile.js delete mode 100644 js/enterProfile.js create mode 100644 js/user-profile.js diff --git a/css/styles.css b/css/styles.css index b1f1e31..01bb05c 100644 --- a/css/styles.css +++ b/css/styles.css @@ -76,3 +76,18 @@ html { transform: rotate(360deg); } } + +/* +PROFILE PAGE CUSTOM STYLES + */ + +#edit, +#save { + top: 10px; + right: 10px; +} + +#cancel { + top: 10px; + left: 10px; +} diff --git a/html/profile.html b/html/profile.html index 517bcc4..4134b1f 100644 --- a/html/profile.html +++ b/html/profile.html @@ -40,38 +40,53 @@
-
+
+ edit + save + cancel
- stack photo +
-

-
Personal information
-
    +

    +
    +
    • person -

      +

    • email -

      +

      +
    • +
    • + link +

    • work -

      +

      +
    • +
    • + today +

    • - location_city -

      Dublin, UK

      + translate +

      +
    • +
    • + public +

-
- +
+
@@ -82,8 +97,7 @@
Personal information
crossorigin="anonymous"> - - + \ No newline at end of file diff --git a/js/edit-profile.js b/js/edit-profile.js deleted file mode 100644 index 16e7277..0000000 --- a/js/edit-profile.js +++ /dev/null @@ -1,14 +0,0 @@ -function edit() { - var Button = document.getElementById("edit"); - if (Button.className == "w-25 border border-info mt-2 rounded") { - Button.innerHTML = "Save"; - Button.className += " editing"; - document.querySelectorAll('p').forEach(el => el.setAttribute('contenteditable', true)); - document.querySelector('h2').setAttribute('contenteditable', true); - } else { - document.querySelectorAll('p').forEach(el => { el.setAttribute('contenteditable', false); console.log(el.textContent); }); - document.querySelector('h2').setAttribute('contenteditable', false); - Button.className = "w-25 border border-info mt-2 rounded"; - Button.innerHTML = "Edit"; - } -}; \ No newline at end of file diff --git a/js/enterProfile.js b/js/enterProfile.js deleted file mode 100644 index 6e4ef71..0000000 --- a/js/enterProfile.js +++ /dev/null @@ -1,27 +0,0 @@ -$.ajax({ - url: "https://jsonplaceholder.typicode.com/users", - contentType: "application/json" - }).done(function (data){ - var userid = window.location.search; - - var filterUser = data.filter(function(user){ - return user.id == userid[userid.length - 1]; - }); - - var nameUser = document.querySelector("h2"); - nameUser.innerHTML = filterUser[0].name; - - var username = document.querySelector("#username"); - username.innerHTML = filterUser[0].username; - - var email = document.querySelector("#email"); - email.innerHTML = filterUser[0].email; - - var company = document.querySelector("#company"); - company.innerHTML = filterUser[0].company.name; - - // var city = document.querySelector("#city"); - // city.innerHTML = filterUser[0].city; - }); - - diff --git a/js/infinite_scroll.js b/js/infinite_scroll.js index 8b31d9c..6d27682 100644 --- a/js/infinite_scroll.js +++ b/js/infinite_scroll.js @@ -47,6 +47,7 @@ function renderCard(user, highlight) { const { name, username, + jobTitle, company, email, languages, @@ -58,17 +59,18 @@ function renderCard(user, highlight) { } = user; var template_cards = ( - '
' + - '' + name + ' Profile picture' + - '
' + - '

' + name + '

' + + '
' + + '' + name + ' Profile picture' + + '
' + + '

' + name + '

' + + '
' + jobTitle + '
' + '

person ' + username + '

' + - '

email ' + email + '

' + + // '

email ' + email + '

' + '

work ' + company + '

' + - '

today ' + experience + '

' + - '

translate ' + languages.join(', ') + '

' + - '

location_city ' + location.city + ', ' + location.country + '

' + - '
' + createBadges(skills) + '
' + + // '

today ' + experience + '

' + + // '

translate ' + languages.join(', ') + '

' + + '

public ' + location.city + ', ' + location.country + '

' + + // '
' + createBadges(skills) + '
' + 'View Profile' + '
' + '
' diff --git a/js/user-profile.js b/js/user-profile.js new file mode 100644 index 0000000..fd6c17d --- /dev/null +++ b/js/user-profile.js @@ -0,0 +1,66 @@ +// Get the user's id from the url search parameter +const userID = window.location.search.split('=')[1]; + +// Get the user's info from the API with the user's id +fetch(`https://cv-mobile-api.herokuapp.com/api/users/${userID}`) +.then( res => res.json() ) +.then( userData => { + // Set image src + userImg.src = userData.profilePicture; + userImg.alt = userData.name + ' picture'; + // Print user's name + document.querySelector('#name').innerHTML = userData.name; + // Print user's job title + document.querySelector('#jobTitle').innerHTML = userData.jobTitle; + // Print user's username + document.querySelector('#username').innerHTML = userData.username; + // Print user's email + document.querySelector('#email').innerHTML = userData.email; + // Print user's website + document.querySelector('#website').innerHTML = userData.website !== undefined ? userData.website : ' '; + // Print user's username + document.querySelector('#company').innerHTML = userData.company !== undefined ? userData.company : ' '; + // Print user's experience + document.querySelector('#experience').innerHTML = userData.experience !== undefined ? userData.experience : ' '; + // Print user's langs + document.querySelector('#langs').innerHTML = userData.languages.join(', '); + // Print user's location + document.querySelector('#location').innerHTML = userData.location.city + ', ' + userData.location.country; + // Print user's skills + document.querySelector('#skillContainer').innerHTML = createBadges(userData.skills); +}); + + +function createBadges(skills) { + const skillBadges = []; + + skills.forEach( skill => { + skillBadges.push(`
${skill}
`); + }); + + return skillBadges.join(''); +} + + +// Edit profile functionality +edit.addEventListener('click', openEditMode); + +let editModeActive = false; + +function openEditMode() { + + if (edit.innerHTML === 'edit') { + editModeActive = true; + edit.classList.add('d-none'); + save.classList.remove('d-none'); + cancel.classList.remove('d-none'); + document.querySelector('h2').setAttribute('contenteditable', true); + document.querySelectorAll('p').forEach(el => el.setAttribute('contenteditable', true)); + } else { + edit.classList.remove('text-success'); + cancel.classList.add('d-none'); + document.querySelector('h2').setAttribute('contenteditable', false); + document.querySelectorAll('p').forEach(el => { el.setAttribute('contenteditable', false) }); + editModeActive = false; + } +}; \ No newline at end of file From 4bd045830432cf59dfdbd1bc631befa98f914c28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Herna=CC=81ndez?= Date: Mon, 15 Oct 2018 03:36:27 +0100 Subject: [PATCH 3/9] handle edit mode open and close, and changes on user properties --- css/styles.css | 16 +++- html/profile.html | 27 ++++--- js/user-profile.js | 186 +++++++++++++++++++++++++++++++++++++-------- 3 files changed, 183 insertions(+), 46 deletions(-) diff --git a/css/styles.css b/css/styles.css index 01bb05c..a78b26b 100644 --- a/css/styles.css +++ b/css/styles.css @@ -13,13 +13,21 @@ html { padding-bottom: 5vh; overflow-y: scroll; } -[contenteditable='true'] { - border-bottom: solid #00f0b5 2px; -} -.profile-info { + +.user-info { border-bottom: 2px white; } +.user-info[contenteditable='true'] { + /* border-bottom: solid #00f0b5 2px; */ + color: #05c643 !important; +} + +.user-info.edited[contenteditable='true'] { + /* border-bottom: solid #f28202 2px; */ + color: #f28202 !important; +} + #loader { position: fixed; top: 50%; diff --git a/html/profile.html b/html/profile.html index 4134b1f..8b16be5 100644 --- a/html/profile.html +++ b/html/profile.html @@ -42,8 +42,8 @@
edit - save - cancel + save + cancel
@@ -52,41 +52,44 @@
-

-
+ +
  • person -

    +
  • email -

    +
  • link -

    +
  • work -

    +
  • today -

    +
  • translate -

    +
  • public -

    +
-
+
+

Skills

+
+
diff --git a/js/user-profile.js b/js/user-profile.js index fd6c17d..23fedf6 100644 --- a/js/user-profile.js +++ b/js/user-profile.js @@ -1,66 +1,192 @@ // Get the user's id from the url search parameter const userID = window.location.search.split('=')[1]; - +let currentUserInfo = ''; // Get the user's info from the API with the user's id fetch(`https://cv-mobile-api.herokuapp.com/api/users/${userID}`) .then( res => res.json() ) .then( userData => { + // Save a copy of the user info for editin mode comparison + currentUserInfo = userData; + renderUSerInfo(userData); +}); + +function renderUSerInfo(user) { // Set image src - userImg.src = userData.profilePicture; - userImg.alt = userData.name + ' picture'; + userImg.src = user.profilePicture; + userImg.alt = user.name + ' picture'; // Print user's name - document.querySelector('#name').innerHTML = userData.name; + document.querySelector('#name').innerHTML = user.name; // Print user's job title - document.querySelector('#jobTitle').innerHTML = userData.jobTitle; + document.querySelector('#jobTitle').innerHTML = user.jobTitle; // Print user's username - document.querySelector('#username').innerHTML = userData.username; + document.querySelector('#username').innerHTML = user.username; // Print user's email - document.querySelector('#email').innerHTML = userData.email; + document.querySelector('#email').innerHTML = user.email; // Print user's website - document.querySelector('#website').innerHTML = userData.website !== undefined ? userData.website : ' '; + document.querySelector('#website').innerHTML = user.website !== undefined ? user.website : ' '; // Print user's username - document.querySelector('#company').innerHTML = userData.company !== undefined ? userData.company : ' '; + document.querySelector('#company').innerHTML = user.company !== undefined ? user.company : ' '; // Print user's experience - document.querySelector('#experience').innerHTML = userData.experience !== undefined ? userData.experience : ' '; + $('#experience').replaceWith(``); + document.querySelector('#experience').innerHTML = user.experience !== undefined ? user.experience : ' '; // Print user's langs - document.querySelector('#langs').innerHTML = userData.languages.join(', '); + $('#languages').replaceWith(``); + document.querySelector('#languages').innerHTML = user.languages.join(', '); // Print user's location - document.querySelector('#location').innerHTML = userData.location.city + ', ' + userData.location.country; + document.querySelector('#location').innerHTML = user.location.city + ', ' + user.location.country; // Print user's skills - document.querySelector('#skillContainer').innerHTML = createBadges(userData.skills); -}); + document.querySelector('#skills').innerHTML = createBadges(user.skills); +} function createBadges(skills) { const skillBadges = []; skills.forEach( skill => { - skillBadges.push(`
${skill}
`); + skillBadges.push(`
${skill}
`); }); return skillBadges.join(''); } +// Edit mode state +let editModeStatus = false; +// Change interface icons on edit mode status +function checkEditStatus() { + editModeStatus = !editModeStatus; + if (editModeStatus) { + edit.classList.add('d-none'); + save.classList.remove('d-none'); + cancel.classList.remove('d-none'); + } else { + edit.classList.remove('d-none'); + save.classList.add('d-none'); + cancel.classList.add('d-none'); + } +} // Edit profile functionality edit.addEventListener('click', openEditMode); -let editModeActive = false; +function changeForSelect(property, options, multiple) { + const optionsArray = []; + + if (property === 'experience') { + options.map( option => optionsArray.push(returnOptionElement(option))); + } else { + options.map( option => optionsArray.push(returnOptionElement(option.label))); + } + + $(`#${property}`).replaceWith( + `` + ); + + function returnOptionElement(option) { + return ``; + } +} function openEditMode() { + checkEditStatus(); + // Enable to cancel the edits made + cancel.addEventListener('click', closeEditMode); + // Save profile changes functionality + save.addEventListener('click', saveProfileChanges); + // Replace experience and languages for inputs of type select + changeForSelect('experience', ["- 1 year", "1 - 3 years", "3 - 5 years", "+ 5 years"], false); + // Get all available languages from the api to create the select input dynamically + fetch(`https://cv-mobile-api.herokuapp.com/api/langs`) + .then( res => res.json() ).then(langs => { + changeForSelect('languages', langs, true); + // Make text input based editable + document.querySelectorAll('.user-info').forEach(el => { + el.addEventListener('input', handleTextChange); - if (edit.innerHTML === 'edit') { - editModeActive = true; - edit.classList.add('d-none'); - save.classList.remove('d-none'); - cancel.classList.remove('d-none'); - document.querySelector('h2').setAttribute('contenteditable', true); - document.querySelectorAll('p').forEach(el => el.setAttribute('contenteditable', true)); - } else { - edit.classList.remove('text-success'); - cancel.classList.add('d-none'); - document.querySelector('h2').setAttribute('contenteditable', false); - document.querySelectorAll('p').forEach(el => { el.setAttribute('contenteditable', false) }); - editModeActive = false; + if (el.id !== 'experience' && el.id !== 'languages') { + el.setAttribute('contenteditable', true); + } + }); + }); +}; + +// Close edit mode and return user info to the initial state +function closeEditMode() { + if (editModeStatus) { + checkEditStatus(); + document.querySelectorAll('.user-info').forEach(el => { + el.setAttribute('contenteditable', false); + el.classList.remove('edited'); + }); + renderUSerInfo(currentUserInfo); + } +} + +// Grab the changed info and send it to the API +function saveProfileChanges() { + if (editModeStatus) { + checkEditStatus(); + document.querySelectorAll('.edited').forEach( el => console.log(el.id, $(el).val() || $(el).html())); + } +} + +// Handle user's info content changes +function handleTextChange(e) { + + if (editModeStatus) { + const targetName = e.target.id; + const targetContent = $(this).val() || e.target.textContent; + + switch (targetName) { + case 'languages': + // Comprobar que el array que los idiomas es igual que un array a partir del string actual + const checkArr = []; + targetContent.forEach( el => currentUserInfo.languages.includes(el) ? checkArr.push(true) : checkArr.push(false)); + + if (checkArr.length !== currentUserInfo.languages.length || checkArr.includes(false)) { + e.target.classList.add('edited'); + e.target.style.color = '#f28202'; + } else { + e.target.classList.remove('edited'); + e.target.style.color = '#05c643'; + } + break; + + case 'location': + const modifiedLocation = { + city: targetContent.split(', ')[0], + country: targetContent.split(', ')[1], + state: currentUserInfo.location.state + } + if (currentUserInfo.location.city === modifiedLocation.city && currentUserInfo.location.country === modifiedLocation.country) { + e.target.classList.remove('edited'); + } else { + e.target.classList.add('edited'); + } + break; + + case 'experience': + console.log(this.value) + if (currentUserInfo.experience !== this.value) { + e.target.classList.add('edited'); + e.target.style.color = '#f28202'; + } else { + e.target.classList.remove('edited'); + e.target.style.color = '#05c643'; + } + + break; + + default: + if (currentUserInfo[targetName] !== targetContent) { + // Change the text color when the info has changed from the initial data + e.target.classList.add('edited'); + } else { + // Change the text color back to green when the info is equal to the initial data + e.target.classList.remove('edited'); + } + break; + } } -}; \ No newline at end of file +} \ No newline at end of file From 8a0178b35b5dece2bf14c6611acf821e43395985 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Herna=CC=81ndez?= Date: Mon, 15 Oct 2018 05:22:16 +0100 Subject: [PATCH 4/9] finished updating basic user info --- js/user-profile.js | 55 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/js/user-profile.js b/js/user-profile.js index 23fedf6..e0637da 100644 --- a/js/user-profile.js +++ b/js/user-profile.js @@ -6,7 +6,7 @@ fetch(`https://cv-mobile-api.herokuapp.com/api/users/${userID}`) .then( res => res.json() ) .then( userData => { // Save a copy of the user info for editin mode comparison - currentUserInfo = userData; + currentUserInfo = {...userData}; renderUSerInfo(userData); }); @@ -53,7 +53,7 @@ function createBadges(skills) { // Edit mode state let editModeStatus = false; // Change interface icons on edit mode status -function checkEditStatus() { +function changeEditModeStatus() { editModeStatus = !editModeStatus; if (editModeStatus) { edit.classList.add('d-none'); @@ -89,7 +89,7 @@ function changeForSelect(property, options, multiple) { } function openEditMode() { - checkEditStatus(); + changeEditModeStatus(); // Enable to cancel the edits made cancel.addEventListener('click', closeEditMode); // Save profile changes functionality @@ -114,7 +114,7 @@ function openEditMode() { // Close edit mode and return user info to the initial state function closeEditMode() { if (editModeStatus) { - checkEditStatus(); + changeEditModeStatus(); document.querySelectorAll('.user-info').forEach(el => { el.setAttribute('contenteditable', false); el.classList.remove('edited'); @@ -126,8 +126,50 @@ function closeEditMode() { // Grab the changed info and send it to the API function saveProfileChanges() { if (editModeStatus) { - checkEditStatus(); - document.querySelectorAll('.edited').forEach( el => console.log(el.id, $(el).val() || $(el).html())); + changeEditModeStatus(); + document.querySelectorAll('.edited').forEach( el => { + switch (el.id) { + case 'location': + currentUserInfo[el.id] = { + city: el.textContent.split(', ')[0], + country: el.textContent.split(', ')[1], + state: currentUserInfo.location.state + } + break; + + default: + currentUserInfo[el.id] = $(el).val() || $(el).html(); + break; + } + }); + + let formData = new FormData(); + + formData.append('name', currentUserInfo.name); + formData.append('username', currentUserInfo.username); + formData.append('jobTitle', currentUserInfo.jobTitle); + formData.append('email', currentUserInfo.email); + formData.append('website', currentUserInfo.website); + formData.append('city', currentUserInfo.location.city); + formData.append('state', currentUserInfo.location.state); + formData.append('country', currentUserInfo.location.country); + formData.append('languages', currentUserInfo.languages.join(', ')); + formData.append('skills', currentUserInfo.skills.join(', ')); + formData.append('company', currentUserInfo.company); + formData.append('experience', currentUserInfo.experience); + formData.append('birthDate', '1986-02-25T00:00:00.000Z'); + formData.append('gender', 'male'); + formData.append('profilePicture', currentUserInfo.profilePicture); + + // Send the data to the server + fetch(`https://cv-mobile-api.herokuapp.com/api/users/${userID}`, { + method: 'PUT', + body: formData + }).then( res => res.json()) + .then( jsonRes => { + console.log(jsonRes); + closeEditMode(); + }); } } @@ -167,7 +209,6 @@ function handleTextChange(e) { break; case 'experience': - console.log(this.value) if (currentUserInfo.experience !== this.value) { e.target.classList.add('edited'); e.target.style.color = '#f28202'; From 4346dd2cf814a45ed7b2aeb7d40affb10a372d8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Herna=CC=81ndez?= Date: Mon, 15 Oct 2018 13:03:30 +0100 Subject: [PATCH 5/9] fixed displaying updated user info after save changes --- js/user-profile.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/js/user-profile.js b/js/user-profile.js index e0637da..19258fe 100644 --- a/js/user-profile.js +++ b/js/user-profile.js @@ -14,6 +14,7 @@ function renderUSerInfo(user) { // Set image src userImg.src = user.profilePicture; userImg.alt = user.name + ' picture'; + userImg.style.backgroundColor = '#999'; // Print user's name document.querySelector('#name').innerHTML = user.name; // Print user's job title @@ -118,6 +119,7 @@ function closeEditMode() { document.querySelectorAll('.user-info').forEach(el => { el.setAttribute('contenteditable', false); el.classList.remove('edited'); + el.style.color = ''; }); renderUSerInfo(currentUserInfo); } @@ -126,7 +128,6 @@ function closeEditMode() { // Grab the changed info and send it to the API function saveProfileChanges() { if (editModeStatus) { - changeEditModeStatus(); document.querySelectorAll('.edited').forEach( el => { switch (el.id) { case 'location': @@ -166,8 +167,9 @@ function saveProfileChanges() { method: 'PUT', body: formData }).then( res => res.json()) - .then( jsonRes => { - console.log(jsonRes); + .then( updatedUser => { + updatedUser.profilePicture = currentUserInfo.profilePicture; + currentUserInfo = {...updatedUser}; closeEditMode(); }); } From d431a8565935d6a5222c49ddfbe15b0f6a9833b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Herna=CC=81ndez?= Date: Mon, 15 Oct 2018 15:20:47 +0100 Subject: [PATCH 6/9] updated quick search filter to find users with new properties and do it with more than one at a time --- js/infinite_scroll.js | 22 ++++++++++++---------- js/search_bar.js | 37 ++++++++++++++++++++++++++----------- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/js/infinite_scroll.js b/js/infinite_scroll.js index 6d27682..cd902e3 100644 --- a/js/infinite_scroll.js +++ b/js/infinite_scroll.js @@ -15,6 +15,7 @@ function fetchUsersData() { if (data.length === 10) { data.map( (user) => { + user.highlight = []; const card = renderCard(user); cardsContainer.innerHTML += card; loadedUsers.push(user); @@ -43,7 +44,7 @@ function fetchUsersData() { fetchUsersData(); // Create an html card template with the user data -function renderCard(user, highlight) { +function renderCard(user) { const { name, username, @@ -56,24 +57,25 @@ function renderCard(user, highlight) { location, experience, profilePicture, + highlight } = user; var template_cards = ( '
' + '' + name + ' Profile picture' + '
' + - '

' + name + '

' + - '
' + jobTitle + '
' + - '

person ' + username + '

' + - // '

email ' + email + '

' + - '

work ' + company + '

' + - // '

today ' + experience + '

' + - // '

translate ' + languages.join(', ') + '

' + - '

public ' + location.city + ', ' + location.country + '

' + - // '
' + createBadges(skills) + '
' + + '

' + name + '

' + + '
' + jobTitle + '
' + + '

person ' + username + '

' + + '

email ' + email + '

' + + '

work ' + company + '

' + + '

public ' + location.city + ', ' + location.country + '

' + 'View Profile' + '
' + '
' + // '

today ' + experience + '

' + + // '

translate ' + languages.join(', ') + '

' + + // '
' + createBadges(skills) + '
' + ); return template_cards; diff --git a/js/search_bar.js b/js/search_bar.js index e8cb959..8bbd521 100644 --- a/js/search_bar.js +++ b/js/search_bar.js @@ -5,22 +5,37 @@ searchInput.addEventListener("keyup", function(e) { // User data separated for type const filteredResults = { name: [], + jobTitle: [], username: [], email: [], company: [] } - function filterByData(input, reference, store, user) { - if (input.toLowerCase() === reference.slice(0, input.length).toLowerCase()) { + function filterByData(input, reference, store, user, coincidence) { + if (reference.toLowerCase().includes(input.toLowerCase())) { + user.highlight.includes(coincidence) ? '' : user.highlight.push(coincidence); store.push(user); + } else { + let position = user.highlight.indexOf(coincidence); + user.highlight.includes(coincidence) && position !== -1 ? user.highlight.splice(position, 1) : ''; } } - function renderCategoryOfFilteredUsers(category, categoryName) { - category.forEach( (user) => { - const card = renderCard( user, categoryName); + function renderFilteredResults() { + const userArray = []; + + for (key in filteredResults) { + filteredResults[key].forEach( user => { + if (userArray.indexOf(user) == -1) { + userArray.push(user); + } + }); + } + + userArray.forEach( user => { + const card = renderCard(user); cardsContainer.innerHTML += card; - }) + }); } if (searchTerm) { @@ -34,21 +49,20 @@ searchInput.addEventListener("keyup", function(e) { // Save user data inside an object for future comparison const userData = { name: user.name, + jobTitle: user.jobTitle, username: user.username, email: user.email, - company: user.company.name + company: user.company } // Grab each userData property and filter it into the searchResult object for (let dataType in userData) { - filterByData(searchTerm, userData[dataType], filteredResults[dataType], user); + filterByData(searchTerm, userData[dataType], filteredResults[dataType], user, dataType); } }); // Once the users have been filtered render them into the DOM - for (let category in filteredResults) { - renderCategoryOfFilteredUsers(filteredResults[category], category); - } + renderFilteredResults(); } else { // Allow infinite scroll again @@ -57,6 +71,7 @@ searchInput.addEventListener("keyup", function(e) { cardsContainer.innerHTML = ''; // Render the default users loaded with the initial ajax call and render them into the DOM again loadedUsers.forEach((user) => { + user.highlight = []; const card = renderCard(user); cardsContainer.innerHTML += card; }); From 02c2ed7bc926bcdc5a67fd7ece9b276ed31bb302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Herna=CC=81ndez?= Date: Mon, 15 Oct 2018 15:25:41 +0100 Subject: [PATCH 7/9] fixed users with no highlight property crashed infinite scroll --- js/infinite_scroll.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/js/infinite_scroll.js b/js/infinite_scroll.js index cd902e3..6f99641 100644 --- a/js/infinite_scroll.js +++ b/js/infinite_scroll.js @@ -64,12 +64,12 @@ function renderCard(user) { '
' + '' + name + ' Profile picture' + '
' + - '

' + name + '

' + - '
' + jobTitle + '
' + - '

person ' + username + '

' + - '

email ' + email + '

' + - '

work ' + company + '

' + - '

public ' + location.city + ', ' + location.country + '

' + + '

' + name + '

' + + '
' + jobTitle + '
' + + '

person ' + username + '

' + + '

email ' + email + '

' + + '

work ' + company + '

' + + '

public ' + location.city + ', ' + location.country + '

' + 'View Profile' + '
' + '
' From edd6d73b8cd5f39f563abc449465d9c55cb66724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Herna=CC=81ndez?= Date: Mon, 15 Oct 2018 18:52:11 +0100 Subject: [PATCH 8/9] added functionality to remove a user from the database --- css/styles.css | 5 +++++ html/profile.html | 1 + js/user-profile.js | 19 +++++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/css/styles.css b/css/styles.css index a78b26b..e2913fe 100644 --- a/css/styles.css +++ b/css/styles.css @@ -99,3 +99,8 @@ PROFILE PAGE CUSTOM STYLES top: 10px; left: 10px; } + +#remove { + bottom: 10px; + right: 10px; +} diff --git a/html/profile.html b/html/profile.html index 8b16be5..bd8bb19 100644 --- a/html/profile.html +++ b/html/profile.html @@ -44,6 +44,7 @@ edit save cancel + delete
diff --git a/js/user-profile.js b/js/user-profile.js index 19258fe..23a9e3e 100644 --- a/js/user-profile.js +++ b/js/user-profile.js @@ -60,10 +60,12 @@ function changeEditModeStatus() { edit.classList.add('d-none'); save.classList.remove('d-none'); cancel.classList.remove('d-none'); + remove.classList.remove('d-none'); } else { edit.classList.remove('d-none'); save.classList.add('d-none'); cancel.classList.add('d-none'); + remove.classList.add('d-none'); } } // Edit profile functionality @@ -95,8 +97,12 @@ function openEditMode() { cancel.addEventListener('click', closeEditMode); // Save profile changes functionality save.addEventListener('click', saveProfileChanges); + // Remove user profile functionality + remove.addEventListener('click', removeUser); + // Replace experience and languages for inputs of type select changeForSelect('experience', ["- 1 year", "1 - 3 years", "3 - 5 years", "+ 5 years"], false); + // Get all available languages from the api to create the select input dynamically fetch(`https://cv-mobile-api.herokuapp.com/api/langs`) .then( res => res.json() ).then(langs => { @@ -175,6 +181,19 @@ function saveProfileChanges() { } } +function removeUser() { + if (editModeStatus) { + fetch(`https://cv-mobile-api.herokuapp.com/api/users/${userID}`, { + method: 'DELETE' + }) + .then(data => data.json()) + .then(response => { + console.log(response); + window.location.pathname = '/index.html'; + }); + } +} + // Handle user's info content changes function handleTextChange(e) { From 1f32c9a0eb89b3c7b1b2fc9ff5fa94f8727ef222 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Herna=CC=81ndez?= Date: Mon, 15 Oct 2018 19:20:34 +0100 Subject: [PATCH 9/9] fixed card height issue --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 56a365d..8269751 100644 --- a/index.html +++ b/index.html @@ -42,7 +42,7 @@
-
+