From e1e7354d852461e9891603084e72e705fe099e43 Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Thu, 6 Jan 2022 17:10:07 +0530 Subject: [PATCH] feat: search in community page --- school/fixtures/custom_field.json | 76 +++++++++---------- school/lms/widgets/MemberCard.html | 2 +- school/overrides/user.py | 72 +++++++++++++++++- school/public/css/style.css | 20 +++-- .../search_course/search_course.html | 2 +- .../templates/search_course/search_course.js | 4 +- school/www/cohorts/subgroup.html | 4 +- school/www/community/__init__.py | 0 school/www/community/index.html | 32 ++++++++ school/www/community/index.js | 58 ++++++++++++++ school/www/community/index.py | 17 +++++ school/www/courses/course.html | 2 +- school/www/courses/course.js | 2 +- school/www/profiles/profile.html | 3 +- 14 files changed, 239 insertions(+), 55 deletions(-) create mode 100644 school/www/community/__init__.py create mode 100644 school/www/community/index.html create mode 100644 school/www/community/index.js create mode 100644 school/www/community/index.py diff --git a/school/fixtures/custom_field.json b/school/fixtures/custom_field.json index b7cd06bd..5a964181 100644 --- a/school/fixtures/custom_field.json +++ b/school/fixtures/custom_field.json @@ -30,7 +30,7 @@ "label": "Country", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 19:14:11.571754", + "modified": "2021-12-31 19:14:11.571754", "module": null, "name": "User-country", "no_copy": 0, @@ -84,7 +84,7 @@ "label": "Acceptance for Terms of Use", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 19:15:34.932908", + "modified": "2021-12-31 19:15:34.932908", "module": null, "name": "User-verify_terms", "no_copy": 0, @@ -138,7 +138,7 @@ "label": "City", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 14:46:55.834145", + "modified": "2021-12-31 14:46:55.834145", "module": null, "name": "User-city", "no_copy": 0, @@ -192,7 +192,7 @@ "label": "College Name", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 14:46:55.834145", + "modified": "2021-12-31 14:46:55.834145", "module": null, "name": "User-college", "no_copy": 0, @@ -246,7 +246,7 @@ "label": "Branch", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 14:46:55.834145", + "modified": "2021-12-31 14:46:55.834145", "module": null, "name": "User-branch", "no_copy": 0, @@ -300,7 +300,7 @@ "label": "LinkedIn ID", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 14:46:55.834145", + "modified": "2021-12-31 14:46:55.834145", "module": null, "name": "User-linkedin", "no_copy": 0, @@ -354,7 +354,7 @@ "label": "Github ID", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 14:46:55.834145", + "modified": "2021-12-31 14:46:55.834145", "module": null, "name": "User-github", "no_copy": 0, @@ -408,7 +408,7 @@ "label": "Medium ID", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 14:46:55.834145", + "modified": "2021-12-31 14:46:55.834145", "module": null, "name": "User-medium", "no_copy": 0, @@ -462,7 +462,7 @@ "label": "Profession", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 14:46:55.834145", + "modified": "2021-12-31 14:46:55.834145", "module": null, "name": "User-profession", "no_copy": 0, @@ -516,7 +516,7 @@ "label": "Education Details", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 11:57:55.170625", + "modified": "2021-12-31 11:57:55.170625", "module": null, "name": "User-education_details", "no_copy": 0, @@ -570,7 +570,7 @@ "label": "Hide my Private Information from others", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 11:57:47.942967", + "modified": "2021-12-31 11:57:47.942967", "module": null, "name": "User-hide_my_private_information_from_others", "no_copy": 0, @@ -624,7 +624,7 @@ "label": "Profile Complete", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 11:00:13.792809", + "modified": "2021-12-31 11:00:13.792809", "module": null, "name": "User-profile_complete", "no_copy": 0, @@ -678,7 +678,7 @@ "label": "Cover Image", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 10:59:52.682112", + "modified": "2021-12-31 10:59:52.682112", "module": null, "name": "User-cover_image", "no_copy": 0, @@ -732,7 +732,7 @@ "label": "I am looking for a job", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-28 12:56:32.110403", + "modified": "2021-12-31 12:56:32.110403", "module": null, "name": "User-looking_for_job", "no_copy": 0, @@ -786,7 +786,7 @@ "label": "Education", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 11:58:56.052663", + "modified": "2021-12-31 11:58:56.052663", "module": null, "name": "User-education", "no_copy": 0, @@ -840,7 +840,7 @@ "label": "Work Experience Details", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 11:56:29.466560", + "modified": "2021-12-31 11:56:29.466560", "module": null, "name": "User-work_experience_details", "no_copy": 0, @@ -894,7 +894,7 @@ "label": "Work Experience", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 11:58:43.329371", + "modified": "2021-12-31 11:58:43.329371", "module": null, "name": "User-work_experience", "no_copy": 0, @@ -948,7 +948,7 @@ "label": "Volunteering or Internship", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 14:58:04.285835", + "modified": "2021-12-31 14:58:04.285835", "module": null, "name": "User-internship", "no_copy": 0, @@ -998,11 +998,11 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "volunteering_or_internship", + "insert_after": "internship", "label": "Certification Details", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 14:58:29.975380", + "modified": "2021-12-31 14:58:29.975380", "module": null, "name": "User-certification_details", "no_copy": 0, @@ -1056,7 +1056,7 @@ "label": "Certification", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 11:59:54.850517", + "modified": "2021-12-31 11:59:54.850517", "module": null, "name": "User-certification", "no_copy": 0, @@ -1110,7 +1110,7 @@ "label": "Skill Details", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 12:01:29.335323", + "modified": "2021-12-31 12:01:29.335323", "module": null, "name": "User-skill_details", "no_copy": 0, @@ -1164,7 +1164,7 @@ "label": "Skill", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 15:32:13.944672", + "modified": "2021-12-31 15:32:13.944672", "module": null, "name": "User-skill", "no_copy": 0, @@ -1218,7 +1218,7 @@ "label": "Career Preference Details", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 15:35:50.764368", + "modified": "2021-12-31 15:35:50.764368", "module": null, "name": "User-carrer_preference_details", "no_copy": 0, @@ -1272,7 +1272,7 @@ "label": "Preferred Functions", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 17:12:36.454519", + "modified": "2021-12-31 17:12:36.454519", "module": null, "name": "User-preferred_functions", "no_copy": 0, @@ -1326,7 +1326,7 @@ "label": "Preferred Location", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 17:12:40.105066", + "modified": "2021-12-31 17:12:40.105066", "module": null, "name": "User-preferred_location", "no_copy": 0, @@ -1380,7 +1380,7 @@ "label": "", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 12:47:32.673594", + "modified": "2021-12-31 12:47:32.673594", "module": null, "name": "User-career_preference_column", "no_copy": 0, @@ -1434,7 +1434,7 @@ "label": "Preferred Industries", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 17:12:31.604282", + "modified": "2021-12-31 17:12:31.604282", "module": null, "name": "User-preferred_industries", "no_copy": 0, @@ -1488,7 +1488,7 @@ "label": "Dream Companies", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 12:49:19.295124", + "modified": "2021-12-31 12:49:19.295124", "module": null, "name": "User-dream_companies", "no_copy": 0, @@ -1542,7 +1542,7 @@ "label": "Work Environment", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 12:49:46.685634", + "modified": "2021-12-31 12:49:46.685634", "module": null, "name": "User-work_environment", "no_copy": 0, @@ -1596,7 +1596,7 @@ "label": "Attire Preference", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 12:03:02.296214", + "modified": "2021-12-31 12:03:02.296214", "module": null, "name": "User-attire", "no_copy": 0, @@ -1650,7 +1650,7 @@ "label": "Collaboration Preference", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 12:02:49.680208", + "modified": "2021-12-31 12:02:49.680208", "module": null, "name": "User-collaboration", "no_copy": 0, @@ -1704,7 +1704,7 @@ "label": "Role Preference", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 16:10:37.349479", + "modified": "2021-12-31 16:10:37.349479", "module": null, "name": "User-role", "no_copy": 0, @@ -1758,7 +1758,7 @@ "label": "", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 16:45:46.776903", + "modified": "2021-12-31 16:45:46.776903", "module": null, "name": "User-work_environment_column", "no_copy": 0, @@ -1812,7 +1812,7 @@ "label": "Location Preference", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 12:02:04.328536", + "modified": "2021-12-31 12:02:04.328536", "module": null, "name": "User-location_preference", "no_copy": 0, @@ -1866,7 +1866,7 @@ "label": "Time Preference", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 16:16:37.885306", + "modified": "2021-12-31 16:16:37.885306", "module": null, "name": "User-time", "no_copy": 0, @@ -1920,7 +1920,7 @@ "label": "Company Type", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-22 12:01:41.342622", + "modified": "2021-12-31 12:01:41.342622", "module": null, "name": "User-company_type", "no_copy": 0, @@ -1943,4 +1943,4 @@ "unique": 0, "width": null } -] \ No newline at end of file +] diff --git a/school/lms/widgets/MemberCard.html b/school/lms/widgets/MemberCard.html index 89879cbb..94ffde12 100644 --- a/school/lms/widgets/MemberCard.html +++ b/school/lms/widgets/MemberCard.html @@ -1,5 +1,5 @@
- {% set avatar_class = "avatar-large" if not dimension_class else "avatar-large"%} + {% set avatar_class = "avatar-xl" if not dimension_class else "avatar-large"%} {{ widgets.Avatar(member=member, avatar_class=avatar_class) }}
{{ member.full_name }} diff --git a/school/overrides/user.py b/school/overrides/user.py index 5b452e06..4b1fd9b6 100644 --- a/school/overrides/user.py +++ b/school/overrides/user.py @@ -1,6 +1,6 @@ import frappe from frappe.core.doctype.user.user import User -from frappe.utils import cint, escape_html, random_string +from frappe.utils import cint, escape_html, random_string, unique import hashlib import random import re @@ -8,6 +8,7 @@ from frappe import _ from frappe.website.utils import is_signup_disabled import requests from frappe.geo.country_info import get_all +from school.widgets import Widgets class CustomUser(User): @@ -237,3 +238,72 @@ def get_country_code(): pass return {} + +@frappe.whitelist(allow_guest=True) +def search_users(start=0, text=""): + or_filters = get_or_filters(text) + count = len(get_users(or_filters, 0, 900000000, text)) + users = get_users(or_filters, start, 30, text) + user_details = get_user_details(users) + + return { + "user_details": user_details, + "start": cint(start) + 30, + "count": count + } + +def get_or_filters(text): + user_fields = ["first_name", "last_name", "full_name", "email", "preferred_location", "dream_companies"] + education_fields = ["institution_name", "location", "degree_type", "major"] + work_fields = ["title", "company"] + certification_fields = ["certification_name", "organization"] + + or_filters = [] + if text: + for field in user_fields: + or_filters.append(f"u.{field} like '%{text}%'") + for field in education_fields: + or_filters.append(f"ed.{field} like '%{text}%'") + for field in work_fields: + or_filters.append(f"we.{field} like '%{text}%'") + for field in certification_fields: + or_filters.append(f"c.{field} like '%{text}%'") + + or_filters.append(f"s.skill_name like '%{text}%'") + or_filters.append(f"pf.function like '%{text}%'") + or_filters.append(f"pi.industry like '%{text}%'") + + + return "AND {}".format(" OR ".join(or_filters)) if or_filters else "" + +def get_user_details(users): + user_details = [] + for user in users: + details = frappe.get_doc("User", user) + user_details.append(Widgets().MemberCard(member=details)) + + return user_details + +def get_users(or_filters, start, page_length, text): + + users = frappe.db.sql(""" + SELECT DISTINCT u.name + FROM `tabUser` u + LEFT JOIN `tabEducation Detail` ed + ON u.name = ed.parent + LEFT JOIN `tabWork Experience` we + ON u.name = we.parent + LEFT JOIN `tabCertification` c + ON u.name = c.parent + LEFT JOIN `tabSkills` s + ON u.name = s.parent + LEFT JOIN `tabPreferred Function` pf + ON u.name = pf.parent + LEFT JOIN `tabPreferred Industry` pi + ON u.name = pi.parent + WHERE u.enabled = True {or_filters} + ORDER BY u.creation desc + LIMIT {start}, {page_length} + """.format(or_filters = or_filters, start=start, page_length=page_length), as_dict=1) + + return users diff --git a/school/public/css/style.css b/school/public/css/style.css index cc9a22d9..1786a37d 100644 --- a/school/public/css/style.css +++ b/school/public/css/style.css @@ -546,7 +546,7 @@ input[type=checkbox] { .profile-parent-section { display: grid; grid-gap: 2rem; - grid-template-columns: 4fr 1fr; + grid-template-columns: 5fr 1.5fr; } @media (max-width: 768px) { @@ -645,7 +645,7 @@ input[type=checkbox] { display: flex; flex-direction: column; align-items: center; - padding: 20px 0px 16px; + padding: 1rem 0; } .member-card .talk-title { @@ -670,7 +670,8 @@ input[type=checkbox] { } .member-card-title { - margin: 12px 0px 4px; + margin-top: 0.75rem; + text-align: center; } .member-card-large .member-card-title { @@ -740,6 +741,11 @@ input[type=checkbox] { height: 88px; } +.member-card .avatar-xl { + width: 150px; + height: 150px; +} + .avatar-xl { width: 112px; height: 112px; @@ -799,16 +805,16 @@ input[type=checkbox] { } } -.mentors-section { +.member-parent { display: grid; - grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); -moz-column-gap: 32px; column-gap: 32px; row-gap: 32px; } @media (max-width: 600px) { - .mentors-section { + .member-parent { grid-template-columns: repeat(auto-fill, minmax(125px, 1fr)); -moz-column-gap: 24px; column-gap: 24px; @@ -1318,7 +1324,7 @@ pre { margin-left: 1.5rem; } -.search-course { +.search { background-image: url(/assets/frappe/icons/timeless/search.svg); border: 1px solid #C8CFD5; box-sizing: border-box; diff --git a/school/templates/search_course/search_course.html b/school/templates/search_course/search_course.html index fd736d73..1590ffec 100644 --- a/school/templates/search_course/search_course.html +++ b/school/templates/search_course/search_course.html @@ -2,7 +2,7 @@ {% set search_placeholder = frappe.db.get_single_value("LMS Settings", "search_placeholder") %} {% if show_search %} - +
× diff --git a/school/templates/search_course/search_course.js b/school/templates/search_course/search_course.js index 0126f8e9..f5d2c617 100644 --- a/school/templates/search_course/search_course.js +++ b/school/templates/search_course/search_course.js @@ -1,5 +1,5 @@ frappe.ready(() => { - $(".search-course").keyup((e) => { + $("#search-course").keyup((e) => { search_course(e); }); @@ -70,5 +70,5 @@ const fix_heading_styles = () => { const close_search_empty_state = (e) => { $(".search-empty-state").addClass("hide"); - $(".search-course").val(""); + $("#search-course").val(""); } diff --git a/school/www/cohorts/subgroup.html b/school/www/cohorts/subgroup.html index a7d69895..c64affbd 100644 --- a/school/www/cohorts/subgroup.html +++ b/school/www/cohorts/subgroup.html @@ -54,7 +54,7 @@
Mentors
{% set mentors = subgroup.get_mentors() %} {% if mentors %} -
+
{% for m in mentors %} {{ widgets.MemberCard(member=m, show_course_count=False, dimension_class="") }} {% endfor %} @@ -68,7 +68,7 @@ {% macro render_students() %} {% set students = subgroup.get_students() %} {% if students %} -
+
{% for student in students %} {{ widgets.MemberCard(member=student, show_course_count=False, dimension_class="") }} {% endfor %} diff --git a/school/www/community/__init__.py b/school/www/community/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/school/www/community/index.html b/school/www/community/index.html new file mode 100644 index 00000000..f8f15e09 --- /dev/null +++ b/school/www/community/index.html @@ -0,0 +1,32 @@ +{% extends "templates/base.html" %} +{% from "www/hackathons/macros/card.html" import null_card %} +{% block title %}{{ _('Community') }}{% endblock %} + +{% block content %} +
+
+ + + +
Community
+ +
+ × + +
{{ _("No results found") }}
+
{{ _("Try some other keyword or explore our community") }}
+
+ +
+ {% for user in user_details %} + {{ widgets.MemberCard(member=user, show_course_count=False) }} + {% endfor %} +
+ {% if user_count > user_details | length %} +
+
Load More
+
+ {% endif %} +
+
+{% endblock %} diff --git a/school/www/community/index.js b/school/www/community/index.js new file mode 100644 index 00000000..f84678f8 --- /dev/null +++ b/school/www/community/index.js @@ -0,0 +1,58 @@ +frappe.ready(() => { + + $("#load-more").click((e) => { + search(e); + }); + + $(".close-search-empty-state").click((e) => { + close_search_empty_state(e); + }); + + $("#search-user").keyup(function() { + let timer; + clearTimeout(timer); + timer = setTimeout(() => { search.apply(this, arguments); }, 300); + }); + +}); + +const search = (e) => { + $("#search-empty-state").addClass("hide"); + let start = $(e.currentTarget).data("start") + let input = $("#search-user").val(); + if ($(e.currentTarget).prop("nodeName") == "INPUT") + start = 0; + + frappe.call({ + method: "school.overrides.user.search_users", + args: { + "start": start, + "text": input + }, + callback: (data) => { + if ($(e.currentTarget).prop("nodeName") == "INPUT") + $(".member-parent").empty(); + + if (data.message.user_details.length) + $("#load-more").removeClass("hide"); + else + $("#search-empty-state").removeClass("hide"); + + $(".member-parent").append(data.message.user_details); + update_load_more_state(data); + } + }); +} + +const close_search_empty_state = (e) => { + $("#search-empty-state").addClass("hide"); + $("#search-user").val("").keyup(); +} + +const update_load_more_state = (data) => { + $("#load-more").data("start", data.message.start); + $("#load-more").data("count", data.message.count); + if ($(".member-card").length == $("#load-more").data("count")) { + $("#load-more").addClass("hide"); + } +} diff --git a/school/www/community/index.py b/school/www/community/index.py new file mode 100644 index 00000000..c9ceac6a --- /dev/null +++ b/school/www/community/index.py @@ -0,0 +1,17 @@ +import frappe + +def get_context(context): + context.user_count = frappe.db.count("User", {"enabled": True}) + users = frappe.get_all("User", + {"enabled": True}, + pluck="name", + start=0, + page_length=30, + order_by="creation desc") + + user_details = [] + for user in users: + details = frappe.get_doc("User", user) + user_details.append(details) + + context.user_details = user_details diff --git a/school/www/courses/course.html b/school/www/courses/course.html index 469dd262..ffe78469 100644 --- a/school/www/courses/course.html +++ b/school/www/courses/course.html @@ -216,7 +216,7 @@
Mentors
-
+
{% for mentor in course.get_mentors() %} {{ widgets.MemberCard(member=mentor, show_course_count=False, dimension_class="") }} {% endfor %} diff --git a/school/www/courses/course.js b/school/www/courses/course.js index 3f228e25..511f09d8 100644 --- a/school/www/courses/course.js +++ b/school/www/courses/course.js @@ -61,7 +61,7 @@ var check_mentor_request = () => { var hide_wrapped_mentor_cards = () => { var offset_top_prev; - $(".mentors-section .member-card").each(function () { + $(".member-parent .member-card").each(function () { var offset_top = $(this).offset().top; if (offset_top > offset_top_prev) { $(this).addClass('wrapped').slideUp("fast"); diff --git a/school/www/profiles/profile.html b/school/www/profiles/profile.html index e7bbaeea..fe67586e 100644 --- a/school/www/profiles/profile.html +++ b/school/www/profiles/profile.html @@ -254,8 +254,9 @@ {% endif %} {% if member.linkedin %} + {% set linkedin = member.linkedin[:-1] if member.linkedin[-1] == "/" else member.linkedin %} - {{ member.linkedin.split("/")[-1] }} + {{ linkedin.split("/")[-1] }} {% endif %} {% if member.medium %}