From b1b8f202c9944fd2612cea5224443e97fe5aa883 Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Tue, 28 Dec 2021 22:26:52 +0530 Subject: [PATCH] feat: job posting --- .../doctype/job_opportunity/__init__.py | 0 .../job_opportunity/job_opportunity.js | 8 + .../job_opportunity/job_opportunity.json | 142 ++++++++++++++++++ .../job_opportunity/job_opportunity.py | 8 + .../job_opportunity/test_job_opportunity.py | 8 + .../web_form/job_opportunity/__init__.py | 0 .../job_opportunity/job_opportunity.js | 7 + .../job_opportunity/job_opportunity.json | 137 +++++++++++++++++ .../job_opportunity/job_opportunity.py | 5 + school/fixtures/custom_field.json | 56 ++++++- school/hooks.py | 3 +- school/lms/web_form/profile/profile.js | 14 +- school/lms/web_form/profile/profile.json | 14 +- school/lms/widgets/CourseCard.html | 4 +- school/public/css/style.css | 38 ++++- school/public/icons/course.svg | 4 + school/www/jobs/__init__.py | 0 school/www/jobs/index.html | 34 +++++ school/www/jobs/index.py | 12 ++ school/www/jobs/job.html | 43 ++++++ school/www/jobs/job.py | 9 ++ school/www/profiles/profile.html | 17 ++- 22 files changed, 539 insertions(+), 24 deletions(-) create mode 100644 school/community/doctype/job_opportunity/__init__.py create mode 100644 school/community/doctype/job_opportunity/job_opportunity.js create mode 100644 school/community/doctype/job_opportunity/job_opportunity.json create mode 100644 school/community/doctype/job_opportunity/job_opportunity.py create mode 100644 school/community/doctype/job_opportunity/test_job_opportunity.py create mode 100644 school/community/web_form/job_opportunity/__init__.py create mode 100644 school/community/web_form/job_opportunity/job_opportunity.js create mode 100644 school/community/web_form/job_opportunity/job_opportunity.json create mode 100644 school/community/web_form/job_opportunity/job_opportunity.py create mode 100644 school/public/icons/course.svg create mode 100644 school/www/jobs/__init__.py create mode 100644 school/www/jobs/index.html create mode 100644 school/www/jobs/index.py create mode 100644 school/www/jobs/job.html create mode 100644 school/www/jobs/job.py diff --git a/school/community/doctype/job_opportunity/__init__.py b/school/community/doctype/job_opportunity/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/school/community/doctype/job_opportunity/job_opportunity.js b/school/community/doctype/job_opportunity/job_opportunity.js new file mode 100644 index 00000000..77f917b0 --- /dev/null +++ b/school/community/doctype/job_opportunity/job_opportunity.js @@ -0,0 +1,8 @@ +// Copyright (c) 2021, Frappe and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Job Opportunity', { + // refresh: function(frm) { + + // } +}); diff --git a/school/community/doctype/job_opportunity/job_opportunity.json b/school/community/doctype/job_opportunity/job_opportunity.json new file mode 100644 index 00000000..a0a5d887 --- /dev/null +++ b/school/community/doctype/job_opportunity/job_opportunity.json @@ -0,0 +1,142 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "format: JOB-{#####}", + "creation": "2021-12-27 16:53:32.316215", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "job_title", + "location", + "column_break_5", + "type", + "status", + "section_break_6", + "description", + "company_details_section", + "company_name", + "company_website", + "column_break_11", + "company_logo", + "application_link" + ], + "fields": [ + { + "fieldname": "job_title", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Job Title", + "reqd": 1 + }, + { + "fieldname": "location", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Location", + "reqd": 1 + }, + { + "fieldname": "type", + "fieldtype": "Select", + "label": "Type", + "options": "Full Time\nPart Time\nFreelance\nContract", + "reqd": 1 + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "default": "Pending", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "options": "Pending\nApproved\nRejected\nClosed" + }, + { + "fieldname": "description", + "fieldtype": "Text Editor", + "label": "Description", + "reqd": 1 + }, + { + "fieldname": "company_details_section", + "fieldtype": "Section Break", + "label": "Company Details" + }, + { + "fieldname": "company_name", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Company Name", + "reqd": 1 + }, + { + "fieldname": "company_website", + "fieldtype": "Data", + "label": "Company Website", + "reqd": 1 + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "fieldname": "application_link", + "fieldtype": "Data", + "label": "Application Form Link", + "reqd": 1 + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, + { + "fieldname": "company_logo", + "fieldtype": "Attach Image", + "label": "Company Logo" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2021-12-27 19:46:50.881715", + "modified_by": "Administrator", + "module": "Community", + "name": "Job Opportunity", + "naming_rule": "Expression", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "All", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/school/community/doctype/job_opportunity/job_opportunity.py b/school/community/doctype/job_opportunity/job_opportunity.py new file mode 100644 index 00000000..994f6e29 --- /dev/null +++ b/school/community/doctype/job_opportunity/job_opportunity.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class JobOpportunity(Document): + pass diff --git a/school/community/doctype/job_opportunity/test_job_opportunity.py b/school/community/doctype/job_opportunity/test_job_opportunity.py new file mode 100644 index 00000000..1823b779 --- /dev/null +++ b/school/community/doctype/job_opportunity/test_job_opportunity.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe and Contributors +# See license.txt + +# import frappe +import unittest + +class TestJobOpportunity(unittest.TestCase): + pass diff --git a/school/community/web_form/job_opportunity/__init__.py b/school/community/web_form/job_opportunity/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/school/community/web_form/job_opportunity/job_opportunity.js b/school/community/web_form/job_opportunity/job_opportunity.js new file mode 100644 index 00000000..274a84c1 --- /dev/null +++ b/school/community/web_form/job_opportunity/job_opportunity.js @@ -0,0 +1,7 @@ +frappe.ready(function() { + frappe.web_form.after_save = () => { + setTimeout(() => { + window.location.href = `/jobs`; + }) + } +}) diff --git a/school/community/web_form/job_opportunity/job_opportunity.json b/school/community/web_form/job_opportunity/job_opportunity.json new file mode 100644 index 00000000..a1fa648c --- /dev/null +++ b/school/community/web_form/job_opportunity/job_opportunity.json @@ -0,0 +1,137 @@ +{ + "accept_payment": 0, + "allow_comments": 0, + "allow_delete": 0, + "allow_edit": 1, + "allow_incomplete": 0, + "allow_multiple": 1, + "allow_print": 0, + "amount": 0.0, + "amount_based_on_field": 0, + "apply_document_permissions": 0, + "button_label": "Save", + "creation": "2021-12-27 17:02:12.461145", + "custom_css": "[data-doctype=\"Web Form\"] {\n max-width: 720px;\n margin: 6rem auto;\n}", + "doc_type": "Job Opportunity", + "docstatus": 0, + "doctype": "Web Form", + "idx": 0, + "is_standard": 1, + "login_required": 1, + "max_attachment_size": 0, + "modified": "2021-12-27 18:18:29.558856", + "modified_by": "Administrator", + "module": "Community", + "name": "job-opportunity", + "owner": "Administrator", + "payment_button_label": "Buy Now", + "published": 1, + "route": "job-opportunity", + "route_to_success_link": 1, + "show_attachments": 0, + "show_in_grid": 0, + "show_sidebar": 0, + "sidebar_items": [], + "success_message": "", + "success_url": "/jobs", + "title": "Job Opportunity", + "web_form_fields": [ + { + "allow_read_on_all_link_options": 0, + "fieldname": "job_title", + "fieldtype": "Data", + "hidden": 0, + "label": "Job Title", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 1, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "location", + "fieldtype": "Data", + "hidden": 0, + "label": "Location", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 1, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "type", + "fieldtype": "Select", + "hidden": 0, + "label": "Type", + "max_length": 0, + "max_value": 0, + "options": "Full Time\nPart Time\nFreelance\nContract", + "read_only": 0, + "reqd": 1, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "company_name", + "fieldtype": "Data", + "hidden": 0, + "label": "Company Name", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 1, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "company_website", + "fieldtype": "Data", + "hidden": 0, + "label": "Company Website", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 1, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "company_logo", + "fieldtype": "Attach Image", + "hidden": 0, + "label": "Company Logo", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "application_link", + "fieldtype": "Data", + "hidden": 0, + "label": "Application Form Link", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 1, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "description", + "fieldtype": "Text Editor", + "hidden": 0, + "label": "Description", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 1, + "show_in_filter": 0 + } + ] +} \ No newline at end of file diff --git a/school/community/web_form/job_opportunity/job_opportunity.py b/school/community/web_form/job_opportunity/job_opportunity.py new file mode 100644 index 00000000..e1ada619 --- /dev/null +++ b/school/community/web_form/job_opportunity/job_opportunity.py @@ -0,0 +1,5 @@ +import frappe + +def get_context(context): + # do your magic here + pass diff --git a/school/fixtures/custom_field.json b/school/fixtures/custom_field.json index 61ac7717..b7cd06bd 100644 --- a/school/fixtures/custom_field.json +++ b/school/fixtures/custom_field.json @@ -701,6 +701,60 @@ "unique": 0, "width": null }, + { + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": null, + "description": null, + "docstatus": 0, + "doctype": "Custom Field", + "dt": "User", + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "looking_for_job", + "fieldtype": "Check", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "insert_after": "cover_image", + "label": "I am looking for a job", + "length": 0, + "mandatory_depends_on": null, + "modified": "2021-12-28 12:56:32.110403", + "module": null, + "name": "User-looking_for_job", + "no_copy": 0, + "non_negative": 0, + "options": null, + "parent": null, + "parentfield": null, + "parenttype": null, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "translatable": 0, + "unique": 0, + "width": null + }, { "allow_in_quick_entry": 0, "allow_on_submit": 0, @@ -1889,4 +1943,4 @@ "unique": 0, "width": null } -] +] \ No newline at end of file diff --git a/school/hooks.py b/school/hooks.py index abb372e1..5f305c20 100644 --- a/school/hooks.py +++ b/school/hooks.py @@ -147,7 +147,8 @@ website_route_rules = [ {"from_route": "/courses//subgroups//", "to_route": "cohorts/subgroup"}, {"from_route": "/courses//subgroups///", "to_route": "cohorts/subgroup"}, {"from_route": "/courses//join///", "to_route": "cohorts/join"}, - {"from_route": "/users", "to_route": "profiles/profile"} + {"from_route": "/users", "to_route": "profiles/profile"}, + {"from_route": "/jobs/", "to_route": "jobs/job"} ] website_redirects = [ diff --git a/school/lms/web_form/profile/profile.js b/school/lms/web_form/profile/profile.js index f76ec156..ea12f0ef 100644 --- a/school/lms/web_form/profile/profile.js +++ b/school/lms/web_form/profile/profile.js @@ -14,12 +14,14 @@ frappe.ready(function () { frappe.web_form.validate = () => { let information_missing; const data = frappe.web_form.get_values(); - data.work_experience && data.work_experience.length && data.work_experience.forEach(exp => { - if (!exp.current && !exp.to_date) { - information_missing = true - frappe.msgprint('To Date is mandatory in Work Experience.'); - } - }); + if (data && data.work_experience && data.work_experience.length) { + data.work_experience.forEach(exp => { + if (!exp.current && !exp.to_date) { + information_missing = true + frappe.msgprint('To Date is mandatory in Work Experience.'); + } + }); + } if (information_missing) return false; diff --git a/school/lms/web_form/profile/profile.json b/school/lms/web_form/profile/profile.json index a91bc666..343437fb 100644 --- a/school/lms/web_form/profile/profile.json +++ b/school/lms/web_form/profile/profile.json @@ -21,7 +21,7 @@ "is_standard": 1, "login_required": 1, "max_attachment_size": 0, - "modified": "2021-12-21 15:50:31.707751", + "modified": "2021-12-28 16:09:47.675523", "modified_by": "Administrator", "module": "LMS", "name": "profile", @@ -455,6 +455,18 @@ "reqd": 0, "show_in_filter": 0 }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "looking_for_job", + "fieldtype": "Check", + "hidden": 0, + "label": "I am looking for a job", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { "allow_read_on_all_link_options": 0, "description": "Private Information includes your Mobile Number, Email Address, Grade Type, Grade and Work Environment Preferences", diff --git a/school/lms/widgets/CourseCard.html b/school/lms/widgets/CourseCard.html index 0c84e952..b639f11d 100644 --- a/school/lms/widgets/CourseCard.html +++ b/school/lms/widgets/CourseCard.html @@ -9,7 +9,7 @@
{{ tag }}
{% endfor %} {% if membership and not read_only %} - {% if progress < 100 %}  
{{ frappe.utils.rounded(progress) }}% + {% if progress < 100 %}
{{ frappe.utils.rounded(progress) }}% {{ _("Completed") }}
{% else %} @@ -39,7 +39,7 @@ {% endif %}
-
{{ course.title }}
+
{{ course.title }}
{{ widgets.Avatar(member=course.get_instructor(), avatar_class="avatar-small") }} diff --git a/school/public/css/style.css b/school/public/css/style.css index f4edb3ec..cc9a22d9 100644 --- a/school/public/css/style.css +++ b/school/public/css/style.css @@ -47,6 +47,9 @@ input[type=checkbox] { position: relative; top: 1rem; left: 1rem; +} + +.course-image .course-tags { width: 95%; } @@ -114,20 +117,23 @@ input[type=checkbox] { } } -.course-card-title { +.card-heading { font-weight: 600; - font-size: 18px; + font-size: 1.5rem; line-height: 156%; letter-spacing: -0.014em; color: var(--text-color-dark); - align-self: stretch; +} + +.course-card-title { + font-size: 1.125rem; margin-bottom: 1.5rem; height: 56px; } @media (max-width: 360px) { - .course-card-title { - font-size: 14px; + .card-heading { + font-size: 0.875rem; } } @@ -1119,7 +1125,7 @@ input[type=checkbox] { margin-top: 3rem; } -.institute-name { +.bold-title { font-weight: bold; color: var(--text-color-dark); } @@ -1463,3 +1469,23 @@ pre { width: 50%; justify-content: space-between; } + +.job-card { + display: flex; + justify-content: space-between; + align-items: center; + padding-right: 1rem; +} + +.job-card-info { + padding: 2rem 1rem 1rem; +} + +.job-description-section { + padding: 1rem; +} + +.company-logo { + background-position: left; + background-size: 88px; +} diff --git a/school/public/icons/course.svg b/school/public/icons/course.svg new file mode 100644 index 00000000..a54ddb12 --- /dev/null +++ b/school/public/icons/course.svg @@ -0,0 +1,4 @@ + + + + diff --git a/school/www/jobs/__init__.py b/school/www/jobs/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/school/www/jobs/index.html b/school/www/jobs/index.html new file mode 100644 index 00000000..a70ae488 --- /dev/null +++ b/school/www/jobs/index.html @@ -0,0 +1,34 @@ +{% extends "templates/base.html" %} +{% from "www/hackathons/macros/card.html" import null_card %} +{% block title %}{{ 'Job Openings' }}{% endblock %} + +{% block content %} +
+
+ Post a Job +
Job Openings
+
+ {% for job in jobs %} +
+
+
+
+
{{ job.type }}
+
{{ job.location }}
+
+
+
{{ job.job_title }}
+ {{ job.company_name }} +
+
+
+ +
+
+ +
+ {% endfor %} +
+
+
+{% endblock %} diff --git a/school/www/jobs/index.py b/school/www/jobs/index.py new file mode 100644 index 00000000..fee62bc7 --- /dev/null +++ b/school/www/jobs/index.py @@ -0,0 +1,12 @@ +import frappe + +def get_context(context): + context.jobs = frappe.get_all("Job Opportunity", + { + "status": "Approved" + }, + [ + "job_title", "location", "type", "company_name", + "company_logo", "name" + ]) + diff --git a/school/www/jobs/job.html b/school/www/jobs/job.html new file mode 100644 index 00000000..66c8a474 --- /dev/null +++ b/school/www/jobs/job.html @@ -0,0 +1,43 @@ +{% extends "templates/base.html" %} +{% from "www/hackathons/macros/card.html" import null_card %} +{% block title %}{{ 'Job Openings' }}{% endblock %} + +{% block content %} + +
+
+ {{ BreadCrumb(job) }} +
+
+
+ +
{{ job.type }}
+
{{ job.location }}
+
+
+
{{ job.job_title }}
+ {{ job.company_name }} +
Posted On: {{ frappe.utils.format_date(job.creation, "medium") }}
+
+
+
+ +
+
+
+
{{ job.description }}
+ Apply Now +
+
+
+
+ +{% endblock %} + +{% macro BreadCrumb(job) %} + +{% endmacro %} diff --git a/school/www/jobs/job.py b/school/www/jobs/job.py new file mode 100644 index 00000000..7db026ca --- /dev/null +++ b/school/www/jobs/job.py @@ -0,0 +1,9 @@ +import frappe + +def get_context(context): + try: + job = frappe.form_dict["job"] + except KeyError: + frappe.local.flags.redirect_location = "/jobs" + raise frappe.Redirect + context.job = frappe.get_doc("Job Opportunity", job) diff --git a/school/www/profiles/profile.html b/school/www/profiles/profile.html index 07ab6f03..9124e2f5 100644 --- a/school/www/profiles/profile.html +++ b/school/www/profiles/profile.html @@ -32,6 +32,9 @@ {% if member.get_authored_courses() | length %}
Creator
{% endif %} + {% if member.looking_for_job %} +
Open Network
+ {% endif %}
{% if frappe.session.user == member.email %} @@ -78,20 +81,20 @@
{% if enrollment %}
- - {{ enrollment }} Enrolled + + {{ enrollment }} {% if enrollment > 1 %} Courses {% else %} Course {% endif %} Taken
{% endif %} {% if reviews %}
- {{ reviews }} Created + {{ reviews }} {% if reviews > 1 %} Courses {% else %} Course {% endif %} Reviewed
{% endif %} {% if mentorship %}
- {{ mentorship }} Mentored + {{ mentorship }} {% if mentorship > 1 %} Courses {% else %} Course {% endif %} Mentored
{% endif %}
@@ -288,7 +291,7 @@
{% for edu in member.education %}
-
{{ edu.institution_name }}
+
{{ edu.institution_name }}
{{ edu.degree_type }} {{ edu.major }} {% if not member.hide_private %} @@ -316,7 +319,7 @@
{% for work in work_details %}
-
{{ work.title }}
+
{{ work.title }}
{{ work.company }}
{{ frappe.utils.format_date(work.from_date, "MMM YYYY") }} - {% if work.to_date %} {{ frappe.utils.format_date(work.to_date, "MMM YYYY") }} {% else %} Present {% endif %}
@@ -336,7 +339,7 @@
{% for cert in member.certification %}
-
{{ cert.certification_name }}
+
{{ cert.certification_name }}
{{ cert.organization }}
{{ frappe.utils.format_date(cert.issue_date, "MMM YYYY") }} {% if cert.expiration_date %} - {{ frappe.utils.format_date(cert.expiration_date, "MMM YYYY") }} {% endif %}