From 5b850412ec36e808e6ab7db920833bbff67a916e Mon Sep 17 00:00:00 2001
From: RichardBao1 <54427922+RichardBao1@users.noreply.github.com>
Date: Thu, 5 Sep 2024 14:40:31 +1000
Subject: [PATCH] Featured job icons on front page (#484)

Co-authored-by: Richard  Bao <richardbao@192-168-1-100.tpgi.com.au>
---
 .github/workflows/ci.yml                      |  3 ++
 backend/src/dev.ts                            | 36 +++++++++----
 backend/src/student.ts                        | 11 +++-
 frontend/app/page.tsx                         | 10 ++--
 .../FeaturedJobCard/FeaturedJobCard.tsx       | 52 +++++++++++++------
 frontend/types/api.ts                         |  3 ++
 package-lock.json                             |  6 +++
 7 files changed, 88 insertions(+), 33 deletions(-)
 create mode 100644 package-lock.json

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7bdcff58a..1079c733b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -5,6 +5,9 @@ jobs:
   test:
     name: Test
     runs-on: ubuntu-latest
+    container:
+      image: docker:20.10 
+      options: --privileged 
     steps:
       - name: Checkout repository
         uses: actions/checkout@v4
diff --git a/backend/src/dev.ts b/backend/src/dev.ts
index 1bb4637cb..bc8f98de6 100644
--- a/backend/src/dev.ts
+++ b/backend/src/dev.ts
@@ -188,7 +188,7 @@ export default async function seedDB() {
 
   const job6 = new Job();
   job6.role = 'Backend Developer';
-  job6.description = 'Java is not poggers';
+  job6.description = 'Example job description';
   job6.applicationLink = 'https://sampleapplicationlink.net';
   job6.company = company;
   job6.approved = false;
@@ -203,8 +203,8 @@ export default async function seedDB() {
 
   // following two jobs are used for testing retrieving number of verified job posts
   const job7 = new Job();
-  job7.role = 'verified job 1';
-  job7.description = 'Java is not poggers';
+  job7.role = 'Security Engineer Intern at Foo';
+  job7.description = 'In the midst of the bustling city, a gentle breeze whispered through the trees, carrying with it the faint scent of blooming flowers. The sun, high above, cast soft shadows on the pavement, where people hurried by, each lost in their own thoughts. A dog barked in the distance, breaking the monotony of the day, while birds chirped melodiously from the branches. Amid the urban chaos, there was a sense of calm, a momentary pause in the relentless rush. Time seemed to slow, if only for a brief moment, as the world continued to spin quietly around them.';
   job7.applicationLink = 'https://sampleapplicationlink.net';
   job7.company = company;
   job7.approved = true;
@@ -219,8 +219,8 @@ export default async function seedDB() {
   job7.createdAt = new Date('1999-5-10');
 
   const job8 = new Job();
-  job8.role = 'verified job 1';
-  job8.description = 'Java is not poggers';
+  job8.role = 'Software Engineer Intern at Foo with some very very very very very very very long name so that I can test how it looks on front page';
+  job8.description = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';
   job8.applicationLink = 'https://sampleapplicationlink.net';
   job8.company = company;
   job8.approved = true;
@@ -234,7 +234,23 @@ export default async function seedDB() {
   job8.isPaid = true;
   job8.createdAt = new Date('1999-10-10');
 
-  companyAccount.company.jobs = [job1, job2, job3, job4, job5, job6, job7, job8];
+  const job9 = new Job();
+  job9.role = 'Software Engineer Graduate Role at Foo';
+  job9.description = 'Example job description';
+  job9.applicationLink = 'https://sampleapplicationlink.net';
+  job9.company = company;
+  job9.approved = true;
+  job9.expiry = new Date('2030-01-10');
+  job9.mode = JobMode.Remote;
+  job9.studentDemographic = [StudentDemographic.All];
+  job9.jobType = JobType.Intern;
+  job9.workingRights = [WorkingRights.AusCtz, WorkingRights.AusPermRes, WorkingRights.AusStudVisa];
+  job9.wamRequirements = WamRequirements.HD;
+  job9.additionalInfo = '';
+  job9.isPaid = true;
+  job9.createdAt = new Date('1984-10-10');
+
+  companyAccount.company.jobs = [job1, job2, job3, job4, job5, job6, job7, job8, job9];
 
   await AppDataSource.manager.save(companyAccount);
 
@@ -263,7 +279,7 @@ export default async function seedDB() {
   // normal approved job
   const companyAccount3Job1 = new Job();
   companyAccount3Job1.role = 'approved job';
-  companyAccount3Job1.description = 'Java is not poggers';
+  companyAccount3Job1.description = 'Example job description';
   companyAccount3Job1.applicationLink = 'https://sampleapplicationlink.net';
   companyAccount3Job1.company = company3;
   companyAccount3Job1.approved = true;
@@ -284,7 +300,7 @@ export default async function seedDB() {
   // approved but expired job
   const companyAccount3Job2 = new Job();
   companyAccount3Job2.role = 'expired job';
-  companyAccount3Job2.description = 'Java is not poggers';
+  companyAccount3Job2.description = 'Example job description';
   companyAccount3Job2.applicationLink = 'https://sampleapplicationlink.net';
   companyAccount3Job2.company = company3;
   companyAccount3Job2.approved = true;
@@ -305,7 +321,7 @@ export default async function seedDB() {
   // approved but hidden job
   const companyAccount3Job3 = new Job();
   companyAccount3Job3.role = 'hidden job';
-  companyAccount3Job3.description = 'Java is not poggers';
+  companyAccount3Job3.description = 'Example job description';
   companyAccount3Job3.applicationLink = 'https://sampleapplicationlink.net';
   companyAccount3Job3.company = company3;
   companyAccount3Job3.approved = true;
@@ -327,7 +343,7 @@ export default async function seedDB() {
   // approved by deleted job
   const companyAccount3Job4 = new Job();
   companyAccount3Job4.role = 'deleted job';
-  companyAccount3Job4.description = 'Java is not poggers';
+  companyAccount3Job4.description = 'Example job description';
   companyAccount3Job4.applicationLink = 'https://sampleapplicationlink.net';
   companyAccount3Job4.company = company3;
   companyAccount3Job4.approved = true;
diff --git a/backend/src/student.ts b/backend/src/student.ts
index 50dc002c8..7f6af5e5b 100644
--- a/backend/src/student.ts
+++ b/backend/src/student.ts
@@ -8,7 +8,7 @@ import StudentProfile from './entity/student_profile';
 import Helpers, { IResponseWithStatus } from './helpers';
 import { Logger, LogModule } from './logging';
 
-import { WorkingRights } from './types/job-field';
+import { JobMode, JobType, WorkingRights } from './types/job-field';
 
 import { StudentBase } from './types/shared';
 
@@ -140,8 +140,11 @@ export default class StudentFunctions {
           .createQueryBuilder()
           .select([
             'company.logo',
+            'company.location',
             'Job.id',
             'Job.role',
+            'Job.jobType',
+            'Job.mode',
             'Job.description',
             'Job.workingRights',
             'Job.applicationLink',
@@ -171,6 +174,9 @@ export default class StudentFunctions {
             workingRights: WorkingRights[];
             applicationLink: string;
             company: string;
+            companyLocation: string;
+            jobType: JobType;
+            mode: JobMode;
           } = {
             id: job.id,
             logo: job.company.logo ? job.company.logo.toString() : null,
@@ -179,6 +185,9 @@ export default class StudentFunctions {
             workingRights: job.workingRights,
             applicationLink: job.applicationLink,
             company: job.company.name,
+            companyLocation: job.company.location,
+            jobType: job.jobType,
+            mode: job.mode,
           };
           return newJob;
         });
diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx
index 8fe7041c7..43938d007 100644
--- a/frontend/app/page.tsx
+++ b/frontend/app/page.tsx
@@ -137,6 +137,7 @@ const HomePage = () => {
               pagination={{
                 clickable: true
               }}
+              autoHeight
               breakpoints={{
                 640: {
                   slidesPerView: 2
@@ -148,20 +149,17 @@ const HomePage = () => {
                   slidesPerView: 4
                 }
               }}
-              loop
-              autoplay
               modules={[Pagination, Navigation]}
-              style={{
-                padding: '0 25px'
-              }}
             >
               {featuredJobs.map((job) => (
                 <SwiperSlide key={job.id}>
                   <FeaturedJobCard
                     title={job.role}
                     description={job.description}
-                    workingRights={job.workingRights}
                     imgSrc={job.logo}
+                    jobType={job.jobType}
+                    mode={job.mode}
+                    location={job.companyLocation}
                   />
                 </SwiperSlide>
               ))}
diff --git a/frontend/components/FeaturedJobCard/FeaturedJobCard.tsx b/frontend/components/FeaturedJobCard/FeaturedJobCard.tsx
index 1d545e345..2f34438fe 100644
--- a/frontend/components/FeaturedJobCard/FeaturedJobCard.tsx
+++ b/frontend/components/FeaturedJobCard/FeaturedJobCard.tsx
@@ -1,23 +1,37 @@
 import React from 'react';
-import { faBuilding } from '@fortawesome/free-solid-svg-icons';
+import {
+  faAddressCard,
+  faBuilding,
+  faClock,
+  faLocationDot
+} from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import Image from 'next/image';
 import Link from 'next/link';
-import { WorkingRights } from 'constants/jobFields';
+import { JobMode, JobType } from 'constants/jobFields';
 import styles from './styles.module.css';
 
 type FeaturedJobCardProps = {
   title: string;
   description: string;
-  workingRights: string[];
   imgSrc: string;
+  jobType: string;
+  location: string;
+  mode: string;
 };
 
-const FeaturedJobCard = ({ title, description, workingRights, imgSrc }: FeaturedJobCardProps) => {
+const FeaturedJobCard = ({
+  title,
+  description,
+  imgSrc,
+  jobType,
+  location,
+  mode
+}: FeaturedJobCardProps) => {
   return (
     <Link href="/student/login">
-      <div className="flex mx-4 mt-8 mb-12 flex-col justify-between shadow-card rounded-lg bg-white relative hover-anim">
-        <div>
+      <div className="flex mx-4 mt-8 mb-12 flex-col justify-between shadow-card rounded-lg bg-white relative hover-anim max-h-[600px]">
+        <div className="flex-grow overflow-hidden">
           <div className="flex justify-center min-w-0 h-52 mx-5">
             {imgSrc ? (
               <Image
@@ -35,18 +49,24 @@ const FeaturedJobCard = ({ title, description, workingRights, imgSrc }: Featured
             )}
           </div>
           <h3 className="text-xl font-bold mx-4 mb-4">{title}</h3>
-          <div className="h-[100px] flex flex-row flex-wrap m-0 justify-center items-center mx-2 my-2 xs:flex-col">
-            {workingRights.map((tag) => (
-              <div
-                key={tag}
-                className="flex justify-center items-center rounded-md my-1 mx-1 px-2 h-6 bg-jb-tags text-base"
-              >
-                {WorkingRights[tag as keyof typeof WorkingRights]}
-              </div>
-            ))}
+
+          <div className="px-1">
+            <div className="flex items-center mx-4 my-1">
+              <FontAwesomeIcon className="w-4" icon={faClock} />
+              <p className="ml-2">{JobType[jobType as keyof typeof JobType]}</p>
+            </div>
+            <div className="flex items-center mx-4 my-1">
+              <FontAwesomeIcon className="w-4" icon={faLocationDot} />
+              <p className="ml-2">{location}</p>
+            </div>
+            <div className="flex items-center mx-4 my-1 mb-4">
+              <FontAwesomeIcon className="w-4" icon={faAddressCard} />
+              <p className="ml-2">{JobMode[mode as keyof typeof JobMode]}</p>
+            </div>
           </div>
+
           <p
-            className="text-base m-0 py-4 px-5 text-left text-jb-placeholder h-[200px] overflow-hidden text-ellipsis"
+            className="text-base m-0 py-3 px-5 text-left text-jb-placeholder h-[600px]"
             dangerouslySetInnerHTML={{
               __html: description
             }}
diff --git a/frontend/types/api.ts b/frontend/types/api.ts
index 1ad1da917..e5b76628d 100644
--- a/frontend/types/api.ts
+++ b/frontend/types/api.ts
@@ -42,6 +42,9 @@ export type FeaturedJob = {
   workingRights: WorkingRights[];
   applicationLink: string;
   company: string;
+  jobType: JobType;
+  mode: JobMode;
+  companyLocation: string;
 };
 
 export type FeaturedJobsPayload = {
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 000000000..1a22ea4bc
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,6 @@
+{
+  "name": "jobsboard",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {}
+}