diff --git a/.forceignore b/.forceignore new file mode 100644 index 0000000..c267c60 --- /dev/null +++ b/.forceignore @@ -0,0 +1,11 @@ +# List files or directories below to ignore them when running force:source:push, force:source:pull, and force:source:status +# More information: https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_exclude_source.htm +# + + +# LWC configuration files +**/jsconfig.json +**/.eslintrc.json + +# LWC Jest +**/__tests__/** \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f5f33eb --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +# This file is used for Git repositories to specify intentionally untracked files that Git should ignore. +# If you are not using git, you can delete this file. For more information see: https://git-scm.com/docs/gitignore +# For useful gitignore templates see: https://github.com/github/gitignore + +# Salesforce cache +.sf/ +.sfdx/ +.localdevserver/ +deploy-options.json + +# LWC VSCode autocomplete +**/lwc/jsconfig.json + +# LWC Jest coverage reports +coverage/ + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Dependency directories +node_modules/ + +# Eslint cache +.eslintcache + +# MacOS system files +.DS_Store + +# Windows system files +Thumbs.db +ehthumbs.db +[Dd]esktop.ini +$RECYCLE.BIN/ + +# Local environment variables +.env + +# Python Salesforce Functions +**/__pycache__/ +**/.venv/ +**/venv/ diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..feac116 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npm run precommit \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..8cccc6e --- /dev/null +++ b/.prettierignore @@ -0,0 +1,11 @@ +# List files or directories below to ignore them when running prettier +# More information: https://prettier.io/docs/en/ignore.html +# + +**/staticresources/** +.localdevserver +.sfdx +.sf +.vscode + +coverage/ \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..18039a0 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,17 @@ +{ + "trailingComma": "none", + "plugins": [ + "prettier-plugin-apex", + "@prettier/plugin-xml" + ], + "overrides": [ + { + "files": "**/lwc/**/*.html", + "options": { "parser": "lwc" } + }, + { + "files": "*.{cmp,page,component}", + "options": { "parser": "html" } + } + ] +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..7e6cb10 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "salesforce.salesforcedx-vscode", + "redhat.vscode-xml", + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "financialforce.lana" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..e07e391 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Apex Replay Debugger", + "type": "apex-replay", + "request": "launch", + "logFile": "${command:AskForLogFileName}", + "stopOnEntry": true, + "trace": true + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..76decfb --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "search.exclude": { + "**/node_modules": true, + "**/bower_components": true, + "**/.sfdx": true + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..afcda4a --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# Salesforce DX Project: Next Steps + +Now that you’ve created a Salesforce DX project, what’s next? Here are some documentation resources to get you started. + +## How Do You Plan to Deploy Your Changes? + +Do you want to deploy a set of changes, or create a self-contained application? Choose a [development model](https://developer.salesforce.com/tools/vscode/en/user-guide/development-models). + +## Configure Your Salesforce DX Project + +The `sfdx-project.json` file contains useful configuration information for your project. See [Salesforce DX Project Configuration](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_ws_config.htm) in the _Salesforce DX Developer Guide_ for details about this file. + +## Read All About It + +- [Salesforce Extensions Documentation](https://developer.salesforce.com/tools/vscode/) +- [Salesforce CLI Setup Guide](https://developer.salesforce.com/docs/atlas.en-us.sfdx_setup.meta/sfdx_setup/sfdx_setup_intro.htm) +- [Salesforce DX Developer Guide](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_intro.htm) +- [Salesforce CLI Command Reference](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference.htm) diff --git a/config/project-scratch-def.json b/config/project-scratch-def.json new file mode 100644 index 0000000..bb72192 --- /dev/null +++ b/config/project-scratch-def.json @@ -0,0 +1,13 @@ +{ + "orgName": "Demo company", + "edition": "Developer", + "features": ["EnableSetPasswordInApi"], + "settings": { + "lightningExperienceSettings": { + "enableS1DesktopEnabled": true + }, + "mobileSettings": { + "enableS1EncryptedStoragePref2": false + } + } +} diff --git a/force-app/main/default/appMenus/AppSwitcher.appMenu-meta.xml b/force-app/main/default/appMenus/AppSwitcher.appMenu-meta.xml new file mode 100644 index 0000000..bf441cb --- /dev/null +++ b/force-app/main/default/appMenus/AppSwitcher.appMenu-meta.xml @@ -0,0 +1,103 @@ + + + + Job_Application_Tracker + CustomApplication + + + standard__Platform + CustomApplication + + + standard__Sales + CustomApplication + + + standard__Service + CustomApplication + + + standard__Marketing + CustomApplication + + + standard__ServiceConsole + CustomApplication + + + standard__AppLauncher + CustomApplication + + + standard__Community + CustomApplication + + + standard__Sites + CustomApplication + + + standard__Chatter + CustomApplication + + + standard__Content + CustomApplication + + + standard__Insights + CustomApplication + + + standard__LightningSalesConsole + CustomApplication + + + standard__LightningService + CustomApplication + + + standard__LightningSales + CustomApplication + + + standard__LightningInstrumentation + CustomApplication + + + standard__SalesforceCMS + CustomApplication + + + standard__AllTabSet + CustomApplication + + + standard__QueueManagement + CustomApplication + + + standard__DataManager + CustomApplication + + + standard__RevenueCloudConsole + CustomApplication + + + standard__LightningScheduler + CustomApplication + + + standard__LightningBolt + CustomApplication + + + standard__FlowsApp + CustomApplication + + + standard__ExpressionSetConsole + CustomApplication + + diff --git a/force-app/main/default/appMenus/Salesforce1.appMenu-meta.xml b/force-app/main/default/appMenus/Salesforce1.appMenu-meta.xml new file mode 100644 index 0000000..d623590 --- /dev/null +++ b/force-app/main/default/appMenus/Salesforce1.appMenu-meta.xml @@ -0,0 +1,71 @@ + + + + EinsteinInsights + StandardAppMenuItem + + + Feed + StandardAppMenuItem + + + MyDay + StandardAppMenuItem + + + Dashboards + StandardAppMenuItem + + + Tasks + StandardAppMenuItem + + + Search + StandardAppMenuItem + + + People + StandardAppMenuItem + + + Groups + StandardAppMenuItem + + + Reports + StandardAppMenuItem + + + Events + StandardAppMenuItem + + + ProcessInstanceWorkitem + StandardAppMenuItem + + + PendingInterviews + StandardAppMenuItem + + + LightningInstrumentation + StandardAppMenuItem + + + LightningSchedulerSetupAssistant + StandardAppMenuItem + + + ProductCatalog + StandardAppMenuItem + + + ProductCategory + StandardAppMenuItem + + + MobileHome + StandardAppMenuItem + + diff --git a/force-app/main/default/applications/Job_Application_Tracker.app-meta.xml b/force-app/main/default/applications/Job_Application_Tracker.app-meta.xml new file mode 100644 index 0000000..5c1ef0b --- /dev/null +++ b/force-app/main/default/applications/Job_Application_Tracker.app-meta.xml @@ -0,0 +1,64 @@ + + + + View + Action override created by Lightning App Builder during activation. + Job_Application_Record_Page + Large + false + Flexipage + Job_Application__c + + + View + Action override created by Lightning App Builder during activation. + Job_Application_Record_Page + Small + false + Flexipage + Job_Application__c + + + #0070D2 + false + + Small + Large + false + false + false + + Console + Job_Application__c + standard-Event + standard-Account + standard-Contact + standard-report + standard-Dashboard + Jooble_Job_Board_Import + Lightning + Job_Application_Tracker_UtilityBar + + + Job_Application__c + + + Jooble_Job_Board_Import + + + standard-Account + + + standard-Contact + + + standard-Dashboard + + + standard-Event + + + standard-report + + + diff --git a/force-app/main/default/aura/.eslintrc.json b/force-app/main/default/aura/.eslintrc.json new file mode 100644 index 0000000..226a5a2 --- /dev/null +++ b/force-app/main/default/aura/.eslintrc.json @@ -0,0 +1,8 @@ +{ + "plugins": ["@salesforce/eslint-plugin-aura"], + "extends": ["plugin:@salesforce/eslint-plugin-aura/recommended"], + "rules": { + "vars-on-top": "off", + "no-unused-expressions": "off" + } +} diff --git a/force-app/main/default/classes/CleanUpStaleJobApplications.cls b/force-app/main/default/classes/CleanUpStaleJobApplications.cls new file mode 100644 index 0000000..2c15086 --- /dev/null +++ b/force-app/main/default/classes/CleanUpStaleJobApplications.cls @@ -0,0 +1,31 @@ +global class CleanUpStaleJobApplications implements Database.Batchable, Database.Stateful { + + global Database.QueryLocator start(Database.BatchableContext BC) { + // Query to find stale job applications + return Database.getQueryLocator([ + SELECT Id, Job_Application_Status__c, Follow_up_date__c, Notes__c + FROM Job_Application__c + WHERE Job_Application_Status__c != 'Closed' + AND Job_Application_Status__c != 'Accepted' + AND Follow_up_date__c <= :System.today().addDays(-30) + ]); + } + + global void execute(Database.BatchableContext BC, List scope) { + List applicationsToUpdate = new List(); + + for (Job_Application__c application : scope) { + application.Job_Application_Status__c = 'Closed'; // Update status to Closed + application.Notes__c = 'Closed by automated process'; // Update notes + applicationsToUpdate.add(application); + } + + if (!applicationsToUpdate.isEmpty()) { + update applicationsToUpdate; // Perform the update + } + } + + global void finish(Database.BatchableContext BC) { + System.debug('CleanUpStaleJobApplications batch job completed successfully.'); + } +} \ No newline at end of file diff --git a/force-app/main/default/classes/CleanUpStaleJobApplications.cls-meta.xml b/force-app/main/default/classes/CleanUpStaleJobApplications.cls-meta.xml new file mode 100644 index 0000000..998805a --- /dev/null +++ b/force-app/main/default/classes/CleanUpStaleJobApplications.cls-meta.xml @@ -0,0 +1,5 @@ + + + 62.0 + Active + diff --git a/force-app/main/default/classes/CleanUpStaleJobApplicationsTest.cls b/force-app/main/default/classes/CleanUpStaleJobApplicationsTest.cls new file mode 100644 index 0000000..dab4a55 --- /dev/null +++ b/force-app/main/default/classes/CleanUpStaleJobApplicationsTest.cls @@ -0,0 +1,41 @@ +@isTest +private class CleanUpStaleJobApplicationsTest { + @isTest + static void testBatchJob() { + // Create test job applications + List applications = new List(); + for (Integer i = 0; i < 5; i++) { + applications.add(new Job_Application__c( + Job_Application_Status__c = 'Saved', // Change if necessary + Follow_up_date__c = System.today().addDays(-31) // Stale application + )); + } + insert applications; + + // Start the batch job + Test.startTest(); + CleanUpStaleJobApplications batchJob = new CleanUpStaleJobApplications(); + Database.executeBatch(batchJob); + Test.stopTest(); + + // Verify that the applications were updated + List updatedApps = [SELECT Id, Job_Application_Status__c, Notes__c FROM Job_Application__c]; + for (Job_Application__c app : updatedApps) { + System.assertEquals('Closed', app.Job_Application_Status__c, 'The status should be updated to Closed for stale applications.'); + System.assertEquals('Closed by automated process', app.Notes__c, 'Notes should indicate closure by the automated process.'); + System.debug('Updated Application: ' + app.Id + ' Status: ' + app.Job_Application_Status__c); + } + } + + @isTest + static void testScheduler() { + // Schedule the job + Test.startTest(); + String jobId = System.schedule('Test Schedule Clean Up', '0 0 0 ? * MON-FRI', new ScheduleCleanUpStaleJobApplications()); + Test.stopTest(); + + // Check if the job has been scheduled + List triggers = [SELECT Id, NextFireTime FROM CronTrigger WHERE Id = :jobId]; + System.assert(!triggers.isEmpty(), 'The job should be scheduled.'); + } +} \ No newline at end of file diff --git a/force-app/main/default/classes/CleanUpStaleJobApplicationsTest.cls-meta.xml b/force-app/main/default/classes/CleanUpStaleJobApplicationsTest.cls-meta.xml new file mode 100644 index 0000000..998805a --- /dev/null +++ b/force-app/main/default/classes/CleanUpStaleJobApplicationsTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 62.0 + Active + diff --git a/force-app/main/default/classes/CustomException.cls b/force-app/main/default/classes/CustomException.cls new file mode 100644 index 0000000..4bbbb7f --- /dev/null +++ b/force-app/main/default/classes/CustomException.cls @@ -0,0 +1 @@ +public class CustomException extends Exception {} diff --git a/force-app/main/default/classes/CustomException.cls-meta.xml b/force-app/main/default/classes/CustomException.cls-meta.xml new file mode 100644 index 0000000..998805a --- /dev/null +++ b/force-app/main/default/classes/CustomException.cls-meta.xml @@ -0,0 +1,5 @@ + + + 62.0 + Active + diff --git a/force-app/main/default/classes/EventHelper.cls b/force-app/main/default/classes/EventHelper.cls new file mode 100644 index 0000000..f5fb054 --- /dev/null +++ b/force-app/main/default/classes/EventHelper.cls @@ -0,0 +1,126 @@ +public class EventHelper { + + public static void validateNoOverlapAndFetchJobPosition(List newMeetings) { + Map jobPositionMap = new Map(); + Map companyNameMap = new Map(); + Set jobAppIds = new Set(); + + // Gather Job Application IDs from new meetings + for (Event meeting : newMeetings) { + if (meeting.WhatId != null) { + jobAppIds.add(meeting.WhatId); + } + } + + // Query Job Applications related to those IDs + Map jobApplications = new Map( + [SELECT Id, Job_Position__c, Company_Name__c FROM Job_Application__c WHERE Id IN :jobAppIds] + ); + + // Map job position and company name to each event + for (Event meeting : newMeetings) { + if (meeting.WhatId != null && jobApplications.containsKey(meeting.WhatId)) { + jobPositionMap.put(meeting.Id, jobApplications.get(meeting.WhatId).Job_Position__c); + companyNameMap.put(meeting.Id, jobApplications.get(meeting.WhatId).Company_Name__c); + } + } + + // Prepare new meeting times in minutes + Map> newMeetingTimesInMinutes = new Map>(); + for (Event meeting : newMeetings) { + Map times = new Map(); + times.put('start', convertToMinutes(meeting.StartDateTime)); + times.put('end', convertToMinutes(meeting.EndDateTime)); + newMeetingTimesInMinutes.put(meeting.Id, times); + } + + // Fetch existing meetings + List existingMeetings = [SELECT Id, StartDateTime, EndDateTime, Type + FROM Event + WHERE StartDateTime < :Datetime.now().addDays(30) + AND (Type = 'Phone Screen' OR Type = 'Interview')]; + + // Check for overlaps + for (Event newMeeting : newMeetings) { + Long newStart = newMeetingTimesInMinutes.get(newMeeting.Id).get('start'); + Long newEnd = newMeetingTimesInMinutes.get(newMeeting.Id).get('end'); + + for (Event existingMeeting : existingMeetings) { + // Skip the same event + if (existingMeeting.Id == newMeeting.Id) { + continue; + } + + Long existingStart = convertToMinutes(existingMeeting.StartDateTime); + Long existingEnd = convertToMinutes(existingMeeting.EndDateTime); + + // Check for overlap condition + if ((newStart < existingEnd) && (newEnd > existingStart)) { + // Prepare the error message + String jobPosition = jobPositionMap.containsKey(newMeeting.Id) ? jobPositionMap.get(newMeeting.Id) : 'N/A'; + newMeeting.addError('This interview for the position ' + jobPosition + + ' overlaps with an existing meeting. Please check your Calendar!'); + break; // Exit the loop after adding the error + } + } + } + } + + + public static void sendUpcomingEventEmails(List newMeetings) { + List emails = new List(); + + // Prepare emails for events happening within 24 hours + for (Event meeting : newMeetings) { + if (meeting.StartDateTime != null && meeting.StartDateTime < Datetime.now().addHours(24)) { + + // Retrieve job details if available + String jobPosition = 'N/A'; + String accountName = 'N/A'; + + if (meeting.WhatId != null) { + List jobApps = [ + SELECT Job_Position__c, Company_Name__c, Company_Name__r.Name + FROM Job_Application__c + WHERE Id = :meeting.WhatId + ]; + + if (!jobApps.isEmpty()) { + Job_Application__c jobApp = jobApps[0]; + jobPosition = jobApp.Job_Position__c != null ? jobApp.Job_Position__c : 'N/A'; + accountName = jobApp.Company_Name__r != null ? jobApp.Company_Name__r.Name : 'N/A'; + } + } + + + List users = [SELECT Email FROM User WHERE Id = :meeting.OwnerId]; + + String recipientEmail = 'default@example.com'; // Use a default email in case the user is not found + if (!users.isEmpty()) { + recipientEmail = users[0].Email; + } + + String eventType = meeting.Type; + + Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage(); + email.setToAddresses(new String[] { recipientEmail }); + email.setSubject('Upcoming Interview Reminder'); + email.setPlainTextBody( + 'This is a reminder for your upcoming ' + eventType + ' for the ' + jobPosition + + ' position with ' + accountName + ', scheduled on ' + meeting.StartDateTime.format() + '.' + ); + + emails.add(email); + } + } + + if (!emails.isEmpty()) { + Messaging.sendEmail(emails); + } +} + + public static Long convertToMinutes(Datetime dt) { + Long epochMillis = dt.getTime(); + return epochMillis / 60000; + } +} diff --git a/force-app/main/default/classes/EventHelper.cls-meta.xml b/force-app/main/default/classes/EventHelper.cls-meta.xml new file mode 100644 index 0000000..998805a --- /dev/null +++ b/force-app/main/default/classes/EventHelper.cls-meta.xml @@ -0,0 +1,5 @@ + + + 62.0 + Active + diff --git a/force-app/main/default/classes/EventTriggerHelperTest.cls b/force-app/main/default/classes/EventTriggerHelperTest.cls new file mode 100644 index 0000000..f597fd4 --- /dev/null +++ b/force-app/main/default/classes/EventTriggerHelperTest.cls @@ -0,0 +1,150 @@ +@isTest +private class EventTriggerHelperTest { + + @isTest + static void testNoOverlap() { + // Create test data + Event existingMeeting = new Event( + Subject = 'Existing Meeting', + StartDateTime = Datetime.now().addHours(1), + EndDateTime = Datetime.now().addHours(2) + ); + insert existingMeeting; + + Event newMeeting = new Event( + Subject = 'New Meeting', + StartDateTime = Datetime.now().addHours(3), + EndDateTime = Datetime.now().addHours(4) + ); + + Test.startTest(); + try { + EventHelper.validateNoOverlapAndFetchJobPosition(new List{newMeeting}); + insert newMeeting; + Assert.areEqual(true, true, 'No overlap detected as expected'); + } catch(Exception e) { + Assert.areEqual(false, true, 'Unexpected error occurred: ' + e.getMessage()); + } + Test.stopTest(); + } + + /*@isTest +static void testWithOverlap() { + // Create test data + Event existingMeeting = new Event( + Subject = 'Existing Meeting', + StartDateTime = Datetime.now().addHours(1), + EndDateTime = Datetime.now().addHours(2) + ); + insert existingMeeting; + + Event overlappingMeeting = new Event( + Subject = 'Overlapping Meeting', + StartDateTime = Datetime.now().addHours(1).addMinutes(30), + EndDateTime = Datetime.now().addHours(2).addMinutes(30) + ); + + Test.startTest(); + try { + // This line should throw an exception if there is an overlap + EventHelper.validateNoOverlapAndFetchJobPosition(new List{overlappingMeeting}); + // If we reach this point, it means there was no overlap detected, which is unexpected + Assert.fail('Expected an error due to overlap, but none was thrown'); + } catch(DmlException e) { + // Validate the overlap error message + Assert.isTrue(e.getMessage().contains('overlaps with an existing meeting'), + 'Expected overlap error message: ' + e.getMessage()); + } + Test.stopTest(); +}*/ + + + + @isTest + static void testMultipleEvents() { + // Create test data + Event existingMeeting = new Event( + Subject = 'Existing Meeting', + StartDateTime = Datetime.now().addHours(1), + EndDateTime = Datetime.now().addHours(2) + ); + insert existingMeeting; + + Event newMeeting1 = new Event( + Subject = 'New Meeting 1', + StartDateTime = Datetime.now().addHours(3), + EndDateTime = Datetime.now().addHours(4) + ); + + Event newMeeting2 = new Event( + Subject = 'New Meeting 2', + StartDateTime = Datetime.now().addHours(1).addMinutes(30), + EndDateTime = Datetime.now().addHours(2).addMinutes(30) + ); + + Test.startTest(); + try { + // Validate no overlap + EventHelper.validateNoOverlapAndFetchJobPosition(new List{newMeeting1, newMeeting2}); + + // If there is no overlap, this means the code execution should continue + insert new List{newMeeting1, newMeeting2}; + + // If we reach this point, it means there was no overlap, so assert that the expected outcome was achieved + Assert.isTrue(true, 'No overlap detected as expected.'); + } catch(DmlException e) { + // If an exception is thrown, it indicates there was an overlap + Assert.isTrue(e.getMessage().contains('overlaps with an existing meeting'), + 'Expected overlap error message: ' + e.getMessage()); + } + Test.stopTest(); + } + + + @isTest + static void testConvertToMinutes() { + Datetime testDate = Datetime.newInstance(2023, 1, 1, 12, 0, 0); + Long minutes = EventHelper.convertToMinutes(testDate); + Assert.areNotEqual(null, minutes, 'Minutes should not be null'); + Assert.isTrue(minutes > 0, 'Minutes should be positive'); + } + + /*@isTest +static void testSendUpcomingEventEmails() { + // Create a test Job Application + Job_Application__c jobApp = new Job_Application__c( + Job_Position__c = 'Software Engineer', + Company_Name__c = 'Acme Corp' + ); + insert jobApp; + + // Create a test User + User testUser = new User( + Username = 'testuser@example.com', + Email = 'testuser@example.com', + Alias = 'tuser', + ProfileId = [SELECT Id FROM Profile WHERE Name = 'Standard User' LIMIT 1].Id, + LastName = 'User', + FirstName = 'Test', + IsActive = true + ); + insert testUser; + + // Create a test Event linked to the Job Application and the User + Event meeting = new Event( + WhatId = jobApp.Id, + OwnerId = testUser.Id, + StartDateTime = Datetime.now().addHours(1), + Type = 'Interview' + ); + insert meeting; + + // Now call the method you're testing + EventHelper.sendUpcomingEventEmails(new List { meeting }); + + // Add assertions here to verify expected email sent, etc. +}*/ + + + +} diff --git a/force-app/main/default/classes/EventTriggerHelperTest.cls-meta.xml b/force-app/main/default/classes/EventTriggerHelperTest.cls-meta.xml new file mode 100644 index 0000000..998805a --- /dev/null +++ b/force-app/main/default/classes/EventTriggerHelperTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 62.0 + Active + diff --git a/force-app/main/default/classes/JobApplicationHelper.cls b/force-app/main/default/classes/JobApplicationHelper.cls new file mode 100644 index 0000000..fa2df1e --- /dev/null +++ b/force-app/main/default/classes/JobApplicationHelper.cls @@ -0,0 +1,158 @@ +public with sharing class JobApplicationHelper { + public static void processJobApplications(List newJobAppList, Map oldJobAppMap) { + List tasksToInsert = new List(); + Set processedJobAppIds = new Set(); + + // Fetch existing tasks related to job applications + Set existingTaskSubjects = new Set(); + for (Task existingTask : [SELECT Id, WhatId, Subject, Type, Application_Status_Snapshot__c FROM Task WHERE WhatId IN :newJobAppList]) { + existingTaskSubjects.add(existingTask.Subject + '|' + existingTask.Application_Status_Snapshot__c); + processedJobAppIds.add(existingTask.WhatId); + } + + List subjectList = new List{ + 'Check if the job description aligns with your interests and values', + 'Review the highlighted skills to see if the role is a good fit', + 'Research the company or role and mark your excitement level', + 'Find and research someone who works at the company and add them as a contact', + 'Set up an informational interview to learn more about the role/company', + 'Identify potential referrals to help get your application on the top of the pile', + 'Customize your work achievements using the job description keywords', + 'Submit your application on the company website if possible', + 'Reach out to the hiring manager or recruiter', + 'Follow up on your application via email weekly', + 'Continue identifying and saving similar job opportunities', + 'Set up weekly networking calls to explore similar companies/roles', + 'Prepare your blurb or “tell me about yourself” response', + 'Practice answering behavioral interview questions', + 'Research the company and your interviewers', + 'Set up your virtual interview space and test your tech', + 'Send thank you emails within 24 hours', + 'Research your market value and know your numbers', + 'Prepare your negotiation scripts', + 'Evaluate your offer and decline or accept', + 'Plan your resignation if applicable', + 'Take some time to relax and recharge', + 'Prepare for your first day of onboarding', + 'Send a follow-up email thanking the interviewer and asking for feedback', + 'Review your notes and reflect on areas of improvement' + }; + + + for (Job_Application__c newApplication : newJobAppList) { + Job_Application__c oldApp = oldJobAppMap != null ? oldJobAppMap.get(newApplication.Id) : null; + + Boolean isStatusChanged = oldApp == null || newApplication.Job_Application_Status__c != oldApp.Job_Application_Status__c; + + // If the application has been processed before and the status didn't change, skip it + if (!isStatusChanged && processedJobAppIds.contains(newApplication.Id)) { + continue; + } + + if (isStatusChanged || !processedJobAppIds.contains(newApplication.Id)) { + switch on newApplication.Job_Application_Status__c { + when 'Saved' { + if (!processedJobAppIds.contains(newApplication.Id)) { + processedJobAppIds.add(newApplication.Id); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Research', 'Normal', subjectList[0]); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Research', 'Normal', subjectList[1]); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Research', 'Low', subjectList[2]); + + } + } + when 'Applying' { + processedJobAppIds.add(newApplication.Id); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Networking', 'High', subjectList[3]); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Networking', 'High', subjectList[4]); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Networking', 'High', subjectList[5]); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Application Process', 'Normal', subjectList[6]); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Application Process', 'High', subjectList[7]); + } + when 'Applied' { + processedJobAppIds.add(newApplication.Id); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Application Process', 'High', subjectList[8]); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Application Process', 'Normal', subjectList[9]); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Research', 'Low', subjectList[10]); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Networking', 'Normal', subjectList[11]); + } + when 'Interviewing' { + processedJobAppIds.add(newApplication.Id); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Interview Preparation', 'High', subjectList[12]); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Interview Preparation', 'Normal', subjectList[13]); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Interview Preparation', 'Normal', subjectList[14]); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Interview Preparation', 'High', subjectList[15]); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Interview Preparation', 'High', subjectList[16]); + } + when 'Negotiating' { + processedJobAppIds.add(newApplication.Id); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Offer Negotiation', 'Normal', subjectList[17]); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Offer Negotiation', 'Normal', subjectList[18]); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Offer Negotiation', 'High', subjectList[19]); + } + when 'Accepted' { + processedJobAppIds.add(newApplication.Id); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Onboarding & Reflection', 'Normal', subjectList[20]); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Onboarding & Reflection', 'Low', subjectList[21]); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Onboarding & Reflection', 'Normal', subjectList[22]); + } + when 'Closed' { + processedJobAppIds.add(newApplication.Id); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Email', 'Normal', subjectList[23]); + addTaskIfNotExists(tasksToInsert, existingTaskSubjects, newApplication, 'Onboarding & Reflection', 'Low', subjectList[24]); + } + when else { + System.debug('No tasks created for unrecognized status: ' + newApplication.Job_Application_Status__c); + throw new CustomException('Unrecognized job application status: ' + newApplication.Job_Application_Status__c); + } + } + } + } + + TaskManager.createTasksForApplication(tasksToInsert); + } + + private static void addTaskIfNotExists(List tasksToInsert, Set existingTaskSubjects, Job_Application__c application, String taskType, String priority, String subject) { + String key = subject + '|' + application.Job_Application_Status__c; + // Check if a task with the same subject and Application Status Snapshot already exists + if (!existingTaskSubjects.contains(key)) { + tasksToInsert.add(TaskManager.prepareTask(application, taskType, priority, subject)); + } + } + + public static void setPrimaryContact(List jobAppList) { + Set accountIds = new Set(); + + for (Job_Application__c jobApp : jobAppList) { + if (jobApp.Primary_Contact__c == null && jobApp.Company_Name__c != null) { + accountIds.add(jobApp.Company_Name__c); + } + } + + if (accountIds.isEmpty()) { + return; + } + + Map accountIdToContact = new Map([ + SELECT Id, FirstName, AccountId + FROM Contact + WHERE AccountId IN :accountIds + ORDER BY FirstName ASC // Or any other relevant field + ]); + + Map contactsByAccount = new Map(); + for (Contact contact : accountIdToContact.values()) { + if (!contactsByAccount.containsKey(contact.AccountId)) { + contactsByAccount.put(contact.AccountId, contact); + } + } + + for (Job_Application__c jobApp : jobAppList) { + if (jobApp.Primary_Contact__c == null) { + Contact primaryContact = contactsByAccount.get(jobApp.Company_Name__c); + if (primaryContact != null) { + jobApp.Primary_Contact__c = primaryContact.Id; + } + } + } + } +} \ No newline at end of file diff --git a/force-app/main/default/classes/JobApplicationHelper.cls-meta.xml b/force-app/main/default/classes/JobApplicationHelper.cls-meta.xml new file mode 100644 index 0000000..7a51829 --- /dev/null +++ b/force-app/main/default/classes/JobApplicationHelper.cls-meta.xml @@ -0,0 +1,5 @@ + + + 58.0 + Active + diff --git a/force-app/main/default/classes/JobApplicationHelperTest.cls b/force-app/main/default/classes/JobApplicationHelperTest.cls new file mode 100644 index 0000000..4788237 --- /dev/null +++ b/force-app/main/default/classes/JobApplicationHelperTest.cls @@ -0,0 +1,631 @@ +@isTest +private class JobApplicationHelperTest { + @TestSetup + static void setup(){ + TestDataFactory.createJobApplications(400, 'Saved', 2); + } + + @isTest + static void testProcessJobApplications_Saved() { + // Retrieve the updated job applications + List updatedAppList = [SELECT Id, Job_Application_Status__c, OwnerId FROM Job_Application__c]; + + // Call the method to be tested + Test.startTest(); + JobApplicationHelper.processJobApplications(updatedAppList, null); + Test.stopTest(); + + // Retrieve the created tasks + List createdTasks = [SELECT Id, Type, ActivityDate, Priority, OwnerId, WhatId, Status, Subject FROM Task]; + + // Assertions + // Ensure that each job application has three related tasks + Map> tasksByJobApp = new Map>(); + for (Task task : createdTasks) { + if (!tasksByJobApp.containsKey(task.WhatId)) { + tasksByJobApp.put(task.WhatId, new List()); + } + tasksByJobApp.get(task.WhatId).add(task); + } + + for (Id appId : tasksByJobApp.keySet()) { + List tasksForApp = tasksByJobApp.get(appId); + + // Check that there are exactly 3 tasks for each job application with the "Saved" status + Assert.areEqual(3, tasksForApp.size(), 'There should be 3 tasks created for each job application with the "Saved" status'); + + // Check the details of each task + Boolean check1 = false; + Boolean check2 = false; + Boolean check3 = false; + + for (Task task : tasksForApp) { + if (task.Type == 'Research' && task.Priority == 'Normal' && task.Subject == 'Check if the job description aligns with your interests and values') { + check1 = true; + } else if (task.Type == 'Research' && task.Priority == 'Normal' && task.Subject == 'Review the highlighted skills to see if the role is a good fit') { + check2 = true; + } else if (task.Type == 'Research' && task.Priority == 'Low' && task.Subject == 'Research the company or role and mark your excitement level') { + check3 = true; + } + } + + // Assert that all three tasks were created correctly + Assert.isTrue(check1, 'Task 1 (Check if the job description aligns with your interests and values) should be created.'); + Assert.isTrue(check2, 'Task 2 (Review the highlighted skills to see if the role is a good fit) should be created.'); + Assert.isTrue(check3, 'Task 3 (Research the company or role and mark your excitement level) should be created.'); + } + } + + @isTest + static void testProcessJobApplications_Applying() { + // Retrieve Job Applications from TestSetup + List oldAppList = [SELECT Id, Job_Application_Status__c FROM Job_Application__c]; + + //Prepare map of old job applications with original status + Map oldJobAppMap = new Map(oldAppList); + + // Use TestDataFactory to update job applications to 'Applying' status + TestDataFactory.updateJobApplicationStatus(oldAppList, 'Applying'); + + // Call the method to be tested + Test.startTest(); + JobApplicationHelper.processJobApplications(oldAppList, oldJobAppMap); + Test.stopTest(); + + // Retrieve the created tasks + List createdTasks = [SELECT Id, Type, ActivityDate, Priority, OwnerId, WhatId, Status, Subject FROM Task]; + + // Assertions + // Ensure that each job application has three related tasks + Map> tasksByJobApp = new Map>(); + for (Task task : createdTasks) { + if (!tasksByJobApp.containsKey(task.WhatId)) { + tasksByJobApp.put(task.WhatId, new List()); + } + tasksByJobApp.get(task.WhatId).add(task); + } + + for (Id appId : tasksByJobApp.keySet()) { + List tasksForApp = tasksByJobApp.get(appId); + + // Check that there are exactly 3 tasks for each job application with the "Saved" status + Assert.areEqual(8, tasksForApp.size(), 'There should be 8 tasks created for each job application after updating to "Applying" status'); + + // Separate tasks into "Saved" and "Applying" groups based on their task subjects + Integer savedTaskCount = 0; + Integer applyingTaskCount = 0; + + for (Task task : tasksForApp) { + if (task.Subject.contains('aligns with your interests') || + task.Subject.contains('skills to see if the role is a good fit') || + task.Subject.contains('mark your excitement level')) { + // Count tasks from the "Saved" status + savedTaskCount++; + } else { + // Count tasks from the "Applying" status + applyingTaskCount++; + } + } + + // Ensure that there are 3 tasks from 'Saved' status (but don't recheck their details) + Assert.areEqual(3, savedTaskCount, 'There should still be 3 tasks from the "Saved" status'); + + // Ensure that there are 5 tasks from 'Applying' status and check their details + Assert.areEqual(5, applyingTaskCount, 'There should be 5 new tasks from the "Applying" status'); + + // Check the details of the "Applying" tasks + Boolean check1 = false; + Boolean check2 = false; + Boolean check3 = false; + Boolean check4 = false; + Boolean check5 = false; + + for (Task task : tasksForApp) { + if (task.Type == 'Networking' && task.Priority == 'High' && task.Subject == 'Find and research someone who works at the company and add them as a contact') { + check1 = true; + } else if (task.Type == 'Networking' && task.Priority == 'High' && task.Subject == 'Set up an informational interview to learn more about the role/company') { + check2 = true; + } else if (task.Type == 'Networking' && task.Priority == 'High' && task.Subject == 'Identify potential referrals to help get your application on the top of the pile') { + check3 = true; + } else if (task.Type == 'Application Process' && task.Priority == 'Normal' && task.Subject == 'Customize your work achievements using the job description keywords') { + check4 = true; + } else if (task.Type == 'Application Process' && task.Priority == 'High' && task.Subject == 'Submit your application on the company website if possible') { + check5 = true; + } + } + + // Assert that all five "Applying" tasks were created correctly + Assert.isTrue(check1, 'Task 1 (Find and research someone who works at the company and add them as a contact) should be created.'); + Assert.isTrue(check2, 'Task 2 (Set up an informational interview to learn more about the role/company) should be created.'); + Assert.isTrue(check3, 'Task 3 (Identify potential referrals to help get your application on the top of the pile) should be created.'); + Assert.isTrue(check4, 'Task 4 (Customize your work achievements using the job description keywords) should be created.'); + Assert.isTrue(check5, 'Task 5 (Submit your application on the company website if possible) should be created.'); + } + } + + @isTest + static void testProcessJobApplications_Applied() { + // Retrieve Job Applications from TestSetup + List oldAppList = [SELECT Id, Job_Application_Status__c FROM Job_Application__c]; + + //Prepare map of old job applications with original status + Map oldJobAppMap = new Map(oldAppList); + + // Use TestDataFactory to update job applications to 'Applied' status + TestDataFactory.updateJobApplicationStatus(oldAppList, 'Applied'); + + // Call the method to be tested + Test.startTest(); + JobApplicationHelper.processJobApplications(oldAppList, oldJobAppMap); + Test.stopTest(); + + // Retrieve the created tasks + List createdTasks = [SELECT Id, Type, ActivityDate, Priority, OwnerId, WhatId, Status, Subject FROM Task]; + + // Assertions + // Ensure that each job application has three related tasks + Map> tasksByJobApp = new Map>(); + for (Task task : createdTasks) { + if (!tasksByJobApp.containsKey(task.WhatId)) { + tasksByJobApp.put(task.WhatId, new List()); + } + tasksByJobApp.get(task.WhatId).add(task); + } + + for (Id appId : tasksByJobApp.keySet()) { + List tasksForApp = tasksByJobApp.get(appId); + + // Check that there are exactly 3 tasks for each job application with the "Saved" status + Assert.areEqual(7, tasksForApp.size(), 'There should be 7 tasks created for each job application after updating to "Applied" status'); + + // Separate tasks into "Saved" and "Applying" groups based on their task subjects + Integer savedTaskCount = 0; + Integer appliedTaskCount = 0; + + for (Task task : tasksForApp) { + if (task.Subject.contains('aligns with your interests') || + task.Subject.contains('skills to see if the role is a good fit') || + task.Subject.contains('mark your excitement level')) { + // Count tasks from the "Saved" status + savedTaskCount++; + } else { + // Count tasks from the "Applied" Status + appliedTaskCount++; + } + } + + // Ensure that there are 3 tasks from 'Saved' status (but don't recheck their details) + Assert.areEqual(3, savedTaskCount, 'There should still be 3 tasks from the "Saved" status'); + + // Ensure that there are 4 tasks from 'Applied' status and check their details + Assert.areEqual(4, appliedTaskCount, 'There should be 4 new tasks from the "Applied" status'); + + // Check the details of the "Applying" tasks + Boolean check1 = false; + Boolean check2 = false; + Boolean check3 = false; + Boolean check4 = false; + + for (Task task : tasksForApp) { + if (task.Type == 'Application Process' && task.Priority == 'High' && task.Subject == 'Reach out to the hiring manager or recruiter') { + check1 = true; + } else if (task.Type == 'Application Process' && task.Priority == 'Normal' && task.Subject == 'Follow up on your application via email weekly') { + check2 = true; + } else if (task.Type == 'Research' && task.Priority == 'Low' && task.Subject == 'Continue identifying and saving similar job opportunities') { + check3 = true; + } else if (task.Type == 'Networking' && task.Priority == 'Normal' && task.Subject == 'Set up weekly networking calls to explore similar companies/roles') { + check4 = true; + } + } + + // Assert that all five "Applying" tasks were created correctly + Assert.isTrue(check1, 'Task 1 (Reach out to the hiring manager or recruiter) should be created.'); + Assert.isTrue(check2, 'Task 2 (Follow up on your application via email weekly) should be created.'); + Assert.isTrue(check3, 'Task 3 (Continue identifying and saving similar job opportunities) should be created.'); + Assert.isTrue(check4, 'Task 4 (Set up weekly networking calls to explore similar companies/roles) should be created.'); + } + } + + @isTest + static void testProcessJobApplications_Interviewing() { + // Retrieve Job Applications from TestSetup + List oldAppList = [SELECT Id, Job_Application_Status__c FROM Job_Application__c]; + + //Prepare map of old job applications with original status + Map oldJobAppMap = new Map(oldAppList); + + // Use TestDataFactory to update job applications to 'Interviewing' status + TestDataFactory.updateJobApplicationStatus(oldAppList, 'Interviewing'); + + // Call the method to be tested + Test.startTest(); + JobApplicationHelper.processJobApplications(oldAppList, oldJobAppMap); + Test.stopTest(); + + // Retrieve the created tasks + List createdTasks = [SELECT Id, Type, ActivityDate, Priority, OwnerId, WhatId, Status, Subject FROM Task]; + + // Assertions + // Ensure that each job application has three related tasks + Map> tasksByJobApp = new Map>(); + for (Task task : createdTasks) { + if (!tasksByJobApp.containsKey(task.WhatId)) { + tasksByJobApp.put(task.WhatId, new List()); + } + tasksByJobApp.get(task.WhatId).add(task); + } + + for (Id appId : tasksByJobApp.keySet()) { + List tasksForApp = tasksByJobApp.get(appId); + + // Check that there are exactly 3 tasks for each job application with the "Saved" status + Assert.areEqual(8, tasksForApp.size(), 'There should be 8 tasks created for each job application after updating to "Interviewing" status'); + + // Separate tasks into "Saved" and "Interviewing" groups based on their task subjects + Integer savedTaskCount = 0; + Integer interviewingTaskCount = 0; + + for (Task task : tasksForApp) { + if (task.Subject.contains('aligns with your interests') || + task.Subject.contains('skills to see if the role is a good fit') || + task.Subject.contains('mark your excitement level')) { + // Count tasks from the "Saved" status + savedTaskCount++; + } else { + // Count tasks from the "Interviewing" Status + interviewingTaskCount++; + } + } + + // Ensure that there are 3 tasks from 'Saved' status (but don't recheck their details) + Assert.areEqual(3, savedTaskCount, 'There should still be 3 tasks from the "Saved" status'); + + // Ensure that there are 5 tasks from 'Interviewing' status and check their details + Assert.areEqual(5, interviewingTaskCount, 'There should be 5 new tasks from the "Interviewing" status'); + + // Check the details of the "Interviewing" tasks + Boolean check1 = false; + Boolean check2 = false; + Boolean check3 = false; + Boolean check4 = false; + Boolean check5 = false; + + for (Task task : tasksForApp) { + if (task.Type == 'Interview Preparation' && task.Priority == 'High' && task.Subject == 'Prepare your blurb or “tell me about yourself” response') { + check1 = true; + } else if (task.Type == 'Interview Preparation' && task.Priority == 'Normal' && task.Subject == 'Practice answering behavioral interview questions') { + check2 = true; + } else if (task.Type == 'Interview Preparation' && task.Priority == 'Normal' && task.Subject == 'Research the company and your interviewers') { + check3 = true; + } else if (task.Type == 'Interview Preparation' && task.Priority == 'High' && task.Subject == 'Set up your virtual interview space and test your tech') { + check4 = true; + } else if (task.Type == 'Interview Preparation' && task.Priority == 'High' && task.Subject == 'Send thank you emails within 24 hours') { + check4 = true; + } + } + + // Assert that all four "Interviewing" tasks were created correctly + Assert.isTrue(check1, 'Task 1 (Prepare your blurb or “tell me about yourself” response) should be created.'); + Assert.isTrue(check2, 'Task 2 (Practice answering behavioral interview questions) should be created.'); + Assert.isTrue(check3, 'Task 3 (Research the company and your interviewers) should be created.'); + Assert.isTrue(check4, 'Task 4 (Send thank you emails within 24 hours) should be created.'); + } + } + + @isTest + static void testProcessJobApplications_Negotiating() { + // Retrieve Job Applications from TestSetup + List oldAppList = [SELECT Id, Job_Application_Status__c FROM Job_Application__c]; + + //Prepare map of old job applications with original status + Map oldJobAppMap = new Map(oldAppList); + + // Use TestDataFactory to update job applications to 'Negotiating' status + TestDataFactory.updateJobApplicationStatus(oldAppList, 'Negotiating'); + + // Call the method to be tested + Test.startTest(); + JobApplicationHelper.processJobApplications(oldAppList, oldJobAppMap); + Test.stopTest(); + + // Retrieve the created tasks + List createdTasks = [SELECT Id, Type, ActivityDate, Priority, OwnerId, WhatId, Status, Subject FROM Task]; + + // Assertions + // Ensure that each job application has three related tasks + Map> tasksByJobApp = new Map>(); + for (Task task : createdTasks) { + if (!tasksByJobApp.containsKey(task.WhatId)) { + tasksByJobApp.put(task.WhatId, new List()); + } + tasksByJobApp.get(task.WhatId).add(task); + } + + for (Id appId : tasksByJobApp.keySet()) { + List tasksForApp = tasksByJobApp.get(appId); + + // Check that there are exactly 3 tasks for each job application with the "Saved" status + Assert.areEqual(6, tasksForApp.size(), 'There should be 6 tasks created for each job application after updating to "Negotiating" status'); + + // Separate tasks into "Saved" and "Negotiating" groups based on their task subjects + Integer savedTaskCount = 0; + Integer negotiatingTaskCount = 0; + + for (Task task : tasksForApp) { + if (task.Subject.contains('aligns with your interests') || + task.Subject.contains('skills to see if the role is a good fit') || + task.Subject.contains('mark your excitement level')) { + // Count tasks from the "Saved" status + savedTaskCount++; + } else { + // Count tasks from the "Negotiating" Status + negotiatingTaskCount++; + } + } + + // Ensure that there are 3 tasks from 'Saved' status (but don't recheck their details) + Assert.areEqual(3, savedTaskCount, 'There should still be 3 tasks from the "Saved" status'); + + // Ensure that there are 3 tasks from 'Negotiating' status and check their details + Assert.areEqual(3, negotiatingTaskCount, 'There should be 3 new tasks from the "Negotiating" status'); + + // Check the details of the "Negotiating" tasks + Boolean check1 = false; + Boolean check2 = false; + Boolean check3 = false; + + for (Task task : tasksForApp) { + if (task.Type == 'Offer Negotiation' && task.Priority == 'Normal' && task.Subject == 'Research your market value and know your numbers') { + check1 = true; + } else if (task.Type == 'Offer Negotiation' && task.Priority == 'Normal' && task.Subject == 'Prepare your negotiation scripts') { + check2 = true; + } else if (task.Type == 'Offer Negotiation' && task.Priority == 'High' && task.Subject == 'Evaluate your offer and decline or accept') { + check3 = true; + } + } + + // Assert that all three "Negotiating" tasks were created correctly + Assert.isTrue(check1, 'Task 1 (Research your market value and know your numbers) should be created.'); + Assert.isTrue(check2, 'Task 2 (Prepare your negotiation scripts) should be created.'); + Assert.isTrue(check3, 'Task 3 (Evaluate your offer and decline or accept) should be created.'); + } + } + + @isTest + static void testProcessJobApplications_Accepted() { + // Retrieve Job Applications from TestSetup + List oldAppList = [SELECT Id, Job_Application_Status__c FROM Job_Application__c]; + + //Prepare map of old job applications with original status + Map oldJobAppMap = new Map(oldAppList); + + // Use TestDataFactory to update job applications to 'Accepted' status + TestDataFactory.updateJobApplicationStatus(oldAppList, 'Accepted'); + + // Call the method to be tested + Test.startTest(); + JobApplicationHelper.processJobApplications(oldAppList, oldJobAppMap); + Test.stopTest(); + + // Retrieve the created tasks + List createdTasks = [SELECT Id, Type, ActivityDate, Priority, OwnerId, WhatId, Status, Subject FROM Task]; + + // Assertions + // Ensure that each job application has three related tasks + Map> tasksByJobApp = new Map>(); + for (Task task : createdTasks) { + if (!tasksByJobApp.containsKey(task.WhatId)) { + tasksByJobApp.put(task.WhatId, new List()); + } + tasksByJobApp.get(task.WhatId).add(task); + } + + for (Id appId : tasksByJobApp.keySet()) { + List tasksForApp = tasksByJobApp.get(appId); + + // Check that there are exactly 3 tasks for each job application with the "Saved" status + Assert.areEqual(6, tasksForApp.size(), 'There should be 6 tasks created for each job application after updating to "Accepted" status'); + + // Separate tasks into "Saved" and "Accepted" groups based on their task subjects + Integer savedTaskCount = 0; + Integer acceptedTaskCount = 0; + + for (Task task : tasksForApp) { + if (task.Subject.contains('aligns with your interests') || + task.Subject.contains('skills to see if the role is a good fit') || + task.Subject.contains('mark your excitement level')) { + // Count tasks from the "Saved" status + savedTaskCount++; + } else { + // Count tasks from the "Accepted" Status + acceptedTaskCount++; + } + } + + // Ensure that there are 3 tasks from 'Saved' status (but don't recheck their details) + Assert.areEqual(3, savedTaskCount, 'There should still be 3 tasks from the "Saved" status'); + + // Ensure that there are 3 tasks from 'Accepted' status and check their details + Assert.areEqual(3, acceptedTaskCount, 'There should be 3 new tasks from the "Accepted" status'); + + // Check the details of the "Accepted" tasks + Boolean check1 = false; + Boolean check2 = false; + Boolean check3 = false; + + for (Task task : tasksForApp) { + if (task.Type == 'Onboarding & Reflection' && task.Priority == 'Normal' && task.Subject == 'Plan your resignation if applicable') { + check1 = true; + } else if (task.Type == 'Onboarding & Reflection' && task.Priority == 'Low' && task.Subject == 'Take some time to relax and recharge') { + check2 = true; + } else if (task.Type == 'Onboarding & Reflection' && task.Priority == 'Normal' && task.Subject == 'Prepare for your first day of onboarding') { + check3 = true; + } + } + + // Assert that all three "Accepted" tasks were created correctly + Assert.isTrue(check1, 'Task 1 (Plan your resignation if applicable) should be created.'); + Assert.isTrue(check2, 'Task 2 (Take some time to relax and recharge) should be created.'); + Assert.isTrue(check3, 'Task 3 (Prepare for your first day of onboarding) should be created.'); + } + } + + @isTest + static void testProcessJobApplications_Closed() { + // Retrieve Job Applications from TestSetup + List oldAppList = [SELECT Id, Job_Application_Status__c FROM Job_Application__c]; + + //Prepare map of old job applications with original status + Map oldJobAppMap = new Map(oldAppList); + + // Use TestDataFactory to update job applications to 'Closed' status + TestDataFactory.updateJobApplicationStatus(oldAppList, 'Closed'); + + // Call the method to be tested + Test.startTest(); + JobApplicationHelper.processJobApplications(oldAppList, oldJobAppMap); + Test.stopTest(); + + // Retrieve the created tasks + List createdTasks = [SELECT Id, Type, ActivityDate, Priority, OwnerId, WhatId, Status, Subject FROM Task]; + + // Assertions + // Ensure that each job application has three related tasks + Map> tasksByJobApp = new Map>(); + for (Task task : createdTasks) { + if (!tasksByJobApp.containsKey(task.WhatId)) { + tasksByJobApp.put(task.WhatId, new List()); + } + tasksByJobApp.get(task.WhatId).add(task); + } + + for (Id appId : tasksByJobApp.keySet()) { + List tasksForApp = tasksByJobApp.get(appId); + + // Check that there are exactly 3 tasks for each job application with the "Saved" status + Assert.areEqual(5, tasksForApp.size(), 'There should be 5 tasks created for each job application after updating to "Closed" status'); + + // Separate tasks into "Saved" and "Closed" groups based on their task subjects + Integer savedTaskCount = 0; + Integer closedTaskCount = 0; + + for (Task task : tasksForApp) { + if (task.Subject.contains('aligns with your interests') || + task.Subject.contains('skills to see if the role is a good fit') || + task.Subject.contains('mark your excitement level')) { + // Count tasks from the "Saved" status + savedTaskCount++; + } else { + // Count tasks from the "Closed" Status + closedTaskCount++; + } + } + + // Ensure that there are 3 tasks from 'Saved' status (but don't recheck their details) + Assert.areEqual(3, savedTaskCount, 'There should still be 3 tasks from the "Saved" status'); + + // Ensure that there are 2 tasks from 'Closed' status and check their details + Assert.areEqual(2, closedTaskCount, 'There should be 2 new tasks from the "Closed" status'); + + // Check the details of the "Closed" tasks + Boolean check1 = false; + Boolean check2 = false; + + for (Task task : tasksForApp) { + if (task.Type == 'Email' && task.Priority == 'Normal' && task.Subject == 'Send a follow-up email thanking the interviewer and asking for feedback') { + check1 = true; + } else if (task.Type == 'Onboarding & Reflection' && task.Priority == 'Low' && task.Subject == 'Review your notes and reflect on areas of improvement') { + check2 = true; + } + } + + // Assert that all two "Closed" tasks were created correctly + Assert.isTrue(check1, 'Task 1 (Send a follow-up email thanking the interviewer and asking for feedback) should be created.'); + Assert.isTrue(check2, 'Task 2 (Review your notes and reflect on areas of improvement) should be created.'); + } + } + + + @isTest + static void testWhenElseCondition_ExceptionThrown() { + // Arrange: Create a list of new job applications with an unrecognized status. + List newJobAppList = new List(); + Job_Application__c jobApp = new Job_Application__c(Job_Application_Status__c = 'UnrecognizedStatus'); + newJobAppList.add(jobApp); + + // Empty map for old job applications. + Map oldJobAppMap = new Map(); + + // Act and Assert: Expect the exception to be thrown. + Test.startTest(); + try { + JobApplicationHelper.processJobApplications(newJobAppList, oldJobAppMap); + Assert.fail('Expected a CustomException to be thrown'); // Fail the test if no exception is thrown. + } catch (CustomException e) { + Assert.areEqual('Unrecognized job application status: UnrecognizedStatus', e.getMessage(), 'The exception message should match the expected message.'); + } + Test.stopTest(); + } + + @isTest + static void testSetPrimaryContact_PostitiveCase() { + List jobApps = TestDataFactory.createJobApplications(200, 'Saved', 2); + + for (Job_Application__c jobApp : jobApps) { + jobApp.Id = null; + } + + Test.startTest(); + insert jobApps; + Test.stopTest(); + + jobApps = [SELECT Id, Primary_Contact__c FROM Job_Application__c WHERE Id IN :jobApps]; + for (Job_Application__c jobApp : jobApps) { + Assert.areNotEqual(null, jobApp.Primary_Contact__c, 'Primary Contact should be set for all job applications.'); + } + } + + @isTest + static void testSetPrimaryContact_NegativeNoContacts() { + // Insert an account with no related contacts + Account acc = new Account(Name = 'No Contact Account'); + insert acc; + + // Insert a job application with that account + Job_Application__c jobApp = new Job_Application__c( + Company_Name__c = acc.Id, + OwnerId = UserInfo.getUserId(), + Job_Application_Status__c = 'Saved' + ); + + Test.startTest(); + insert jobApp; + Test.stopTest(); + + // Reload the job application after insert to check the Primary Contact field + jobApp = [SELECT Primary_Contact__c FROM Job_Application__c WHERE Id = :jobApp.Id]; + + // Assert that the primary contact is still null + Assert.areEqual(null, jobApp.Primary_Contact__c, 'Primary Contact should remain null for an account with no contacts.'); + } + + @isTest + static void testSetPrimaryContact_NegativeNoAccount() { + // Insert a job application with no related account + Job_Application__c jobApp = new Job_Application__c( + Company_Name__c = null, + OwnerId = UserInfo.getUserId(), + Job_Application_Status__c = 'Saved' + ); + + Test.startTest(); + insert jobApp; + Test.stopTest(); + + // Reload the job application after insert to check the Primary Contact field + jobApp = [SELECT Primary_Contact__c FROM Job_Application__c WHERE Id = :jobApp.Id]; + + // Assert that the primary contact is still null + Assert.areEqual(null, jobApp.Primary_Contact__c, 'Primary Contact should remain null for a job application without an account.'); + } +} \ No newline at end of file diff --git a/force-app/main/default/classes/JobApplicationHelperTest.cls-meta.xml b/force-app/main/default/classes/JobApplicationHelperTest.cls-meta.xml new file mode 100644 index 0000000..998805a --- /dev/null +++ b/force-app/main/default/classes/JobApplicationHelperTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 62.0 + Active + diff --git a/force-app/main/default/classes/JobApplicationTriggerHandler.cls b/force-app/main/default/classes/JobApplicationTriggerHandler.cls new file mode 100644 index 0000000..06fc2e0 --- /dev/null +++ b/force-app/main/default/classes/JobApplicationTriggerHandler.cls @@ -0,0 +1,27 @@ +public with sharing class JobApplicationTriggerHandler extends TriggerHandler { + private List newJobAppList; + private Map newJobAppMap; + private List oldJobAppList; + private Map oldJobAppMap; + + public JobApplicationTriggerHandler() { + this.newJobAppList = (List) Trigger.new; + this.newJobAppMap = (Map) Trigger.newMap; + this.oldJobAppList = (List) Trigger.old; + this.oldJobAppMap = (Map) Trigger.oldMap; + } + + public override void afterUpdate() { + JobApplicationHelper.processJobApplications(newJobAppList, oldJobAppMap); + } + + public override void afterInsert() { + List jobAppIds = new List(); + for(Job_Application__c jobApp : newJobAppList) { + jobAppIds.add(jobApp.Id); + } + + System.enqueueJob(new SetPrimaryContactQueueable(jobAppIds)); + JobApplicationHelper.processJobApplications(newJobAppList, oldJobAppMap); + } +} \ No newline at end of file diff --git a/force-app/main/default/classes/JobApplicationTriggerHandler.cls-meta.xml b/force-app/main/default/classes/JobApplicationTriggerHandler.cls-meta.xml new file mode 100644 index 0000000..7a51829 --- /dev/null +++ b/force-app/main/default/classes/JobApplicationTriggerHandler.cls-meta.xml @@ -0,0 +1,5 @@ + + + 58.0 + Active + diff --git a/force-app/main/default/classes/JoobleJobSearchController.cls b/force-app/main/default/classes/JoobleJobSearchController.cls new file mode 100644 index 0000000..3be93f2 --- /dev/null +++ b/force-app/main/default/classes/JoobleJobSearchController.cls @@ -0,0 +1,152 @@ +public with sharing class JoobleJobSearchController { + @AuraEnabled + public static Map searchJobs(String keyword, String location, Integer pageNumber, Integer pageSize) { + String apiKey = API_Config__c.getInstance().Jooble_API__c; + String endpoint = 'https://jooble.org/api/' + apiKey; + + HttpRequest req = new HttpRequest(); + req.setEndpoint(endpoint); + req.setMethod('POST'); + req.setHeader('Content-Type', 'application/json'); + req.setTimeout(20000); + + Map requestMap = new Map{ + 'keywords' => keyword, + 'location' => location, + 'page' => String.valueOf(pageNumber), + 'resultonpage' => pageSize + }; + + req.setBody(JSON.serialize(requestMap)); + + Http http = new Http(); + HttpResponse res = http.send(req); + + if (res.getStatusCode() == 200) { + Map jsonResults = (Map) JSON.deserializeUntyped(res.getBody()); + Integer totalCount = (Integer) jsonResults.get('totalCount'); + List jobsList = (List) jsonResults.get('jobs'); + + List jobs = new List(); + + for (Object obj : jobsList) { + Map jobJson = (Map) obj; + jobs.add(createJobFromJson(jobJson)); + } + + return new Map{ + 'jobs' => jobs, + 'totalCount' => totalCount + }; + } else { + throw new AuraHandledException('Failed to retrieve jobs: ' + res.getStatusCode()); + } + } + + // Helper method to map each job JSON to Job object + private static Job createJobFromJson(Map jobJson) { + Job job = new Job(); + job.id = String.valueOf(jobJson.get('id')); + job.title = (String) jobJson.get('title'); + job.location = (String) jobJson.get('location'); + job.snippet = (String) jobJson.get('snippet'); + job.salary = (String) jobJson.get('salary'); + job.type = (String) jobJson.get('type'); + job.link = (String) jobJson.get('link'); + job.company = (String) jobJson.get('company'); + + String rawDate = (String) jobJson.get('updated'); + if (String.isNotBlank(rawDate)) { + try { + DateTime dt = DateTime.valueOfGmt(rawDate.replace('T', ' ')); + job.updated = dt.format('MMMM d, yyyy'); + } catch (Exception e) { + job.updated = rawDate; // Fallback to original value if parsing fails + } + } else { + job.updated = 'Date not available'; + } + + return job; + } + + public class Job { + @AuraEnabled public String title { get; set; } + @AuraEnabled public String link { get; set; } + @AuraEnabled public String salary { get; set; } + @AuraEnabled public String snippet { get; set; } + @AuraEnabled public String company { get; set; } + @AuraEnabled public String type { get; set; } + @AuraEnabled public String location { get; set; } + @AuraEnabled public String updated { get; set; } + @AuraEnabled public String id { get; set; } + } + + @AuraEnabled + public static Id createJobApplication(Job jobApp) { + System.debug('Starting createJobApplication'); + Set existingJoobleExternalIds = new Set(); + + for (Job_Application__c job : [SELECT JoobleExternalId__c FROM Job_Application__c]) { + existingJoobleExternalIds.add(job.JoobleExternalId__c); + } + + System.debug('Existing JoobleExternalIds: ' + existingJoobleExternalIds); + + if (existingJoobleExternalIds.contains(jobApp.id)) { + System.debug('Duplicate job detected with ID: ' + jobApp.id); + throw new AuraHandledException('This job is already saved!'); + } + + try { + List existingAccounts = [SELECT Id FROM Account WHERE Name = :jobApp.company LIMIT 1]; + Id accountId; + + if (existingAccounts.isEmpty()) { + Account newAccount = new Account(Name = jobApp.company); + insert newAccount; + accountId = newAccount.Id; + System.debug('New account created with ID: ' + accountId); + } else { + accountId = existingAccounts[0].Id; + System.debug('Using existing account with ID: ' + accountId); + } + + Job_Application__c newJobApp = new Job_Application__c( + Job_Position__c = jobApp.title, + URL__c = jobApp.link, + Description__c = jobApp.snippet, + Location__c = jobApp.location, + Date_Posted__c = formatDateString(jobApp.updated), + Job_Application_Status__c = 'Saved', + Job_Source__c = 'Jooble Job Board', + JoobleExternalId__c = jobApp.id, + Company_Name__c = accountId + ); + + insert newJobApp; + System.debug('Job Application created with ID: ' + newJobApp.Id); + return newJobApp.Id; + + } catch(Exception e) { + System.debug('Exception caught in createJobApplication: ' + e.getMessage()); + throw new AuraHandledException('Error creating job application: ' + e.getMessage()); + } + } + + + private static String formatDateString(String dateStr) { + if (String.isBlank(dateStr)) { + return Date.today().format(); + } + + try { + DateTime dt = DateTime.valueOfGmt(dateStr); + return dt.format('yyyy-MM-dd'); + } catch (Exception e) { + return Date.today().format(); + } + } +} + + diff --git a/force-app/main/default/classes/JoobleJobSearchController.cls-meta.xml b/force-app/main/default/classes/JoobleJobSearchController.cls-meta.xml new file mode 100644 index 0000000..61af24c --- /dev/null +++ b/force-app/main/default/classes/JoobleJobSearchController.cls-meta.xml @@ -0,0 +1,5 @@ + + + 62.0 + Active + diff --git a/force-app/main/default/classes/JoobleJobSearchControllerTest.cls b/force-app/main/default/classes/JoobleJobSearchControllerTest.cls new file mode 100644 index 0000000..03d5129 --- /dev/null +++ b/force-app/main/default/classes/JoobleJobSearchControllerTest.cls @@ -0,0 +1,145 @@ +@IsTest +private class JoobleJobSearchControllerTest { + + @IsTest + static void testSearchJobs() { + // Mock success response + HttpResponse mockResponse = new HttpResponse(); + mockResponse.setStatusCode(200); + mockResponse.setBody('{"totalCount": 100, "jobs": [{"id": "123", "title": "Software Engineer", "location": "San Francisco", "snippet": "Great opportunity", "salary": "$100k-$150k", "type": "Full-time", "link": "http://example.com", "company": "Tech Co", "updated": "2023-05-01T12:00:00Z"}]}'); + + Test.setMock(HttpCalloutMock.class, new MockHttpResponseGenerator(mockResponse)); + + API_Config__c config = new API_Config__c(Jooble_API__c = 'test-api-key'); + insert config; + + Test.startTest(); + Map result = JoobleJobSearchController.searchJobs('Software Engineer', 'San Francisco', 1, 10); + Test.stopTest(); + + // Verify results + Assert.areEqual(100, result.get('totalCount'), 'Total count should match'); + List jobs = (List)result.get('jobs'); + Assert.areEqual(1, jobs.size(), 'Should have one job in the result'); + + JoobleJobSearchController.Job job = jobs[0]; + Assert.areEqual('Software Engineer', job.title, 'Job title should match'); + Assert.areEqual('San Francisco', job.location, 'Job location should match'); + Assert.areEqual('May 1, 2023', job.updated, 'Job updated date should be formatted correctly'); + } + + @IsTest +static void testSearchJobsError() { + // Mock error response + HttpResponse mockResponse = new HttpResponse(); + mockResponse.setStatusCode(400); + mockResponse.setBody('Error'); + + Test.setMock(HttpCalloutMock.class, new MockHttpResponseGenerator(mockResponse)); + + API_Config__c config = new API_Config__c(Jooble_API__c = 'test-api-key'); + insert config; + + Test.startTest(); + try { + JoobleJobSearchController.searchJobs('Software Engineer', 'San Francisco', 1, 10); + System.assert(false, 'Expected an exception but did not get one.'); + } catch (Exception e) { + //System.debug('Caught exception message: ' + e.getMessage()); + System.assert(true, 'Exception was correctly thrown due to error response.'); + } + Test.stopTest(); +} + + + @IsTest + static void testCreateJobApplication() { + JoobleJobSearchController.Job jobApp = createTestJob(); + + Test.startTest(); + Id jobAppId = JoobleJobSearchController.createJobApplication(jobApp); + Test.stopTest(); + + Job_Application__c createdJobApp = [SELECT Id, Job_Position__c, Company_Name__r.Name FROM Job_Application__c WHERE Id = :jobAppId]; + Assert.areEqual('Software Engineer', createdJobApp.Job_Position__c, 'Job position should match'); + Assert.areEqual('Tech Co', createdJobApp.Company_Name__r.Name, 'Company name should match'); + } + + @IsTest +static void testCreateDuplicateJobApplication() { + // Create a job application to simulate + JoobleJobSearchController.Job jobApp = createTestJob(); + + Job_Application__c existingJobApp = new Job_Application__c( + JoobleExternalId__c = '123', // Same ID as the test jobApp to trigger dupe + Job_Position__c = 'Software Engineer', + Job_Application_Status__c = 'Saved' + ); + insert existingJobApp; + + // Verify the duplicate job setup + Job_Application__c insertedJobApp = [SELECT Id, JoobleExternalId__c FROM Job_Application__c WHERE JoobleExternalId__c = :jobApp.id LIMIT 1]; + System.assertNotEquals(null, insertedJobApp, 'Setup failed: existing job application not found in database.'); + + Test.startTest(); + try { + JoobleJobSearchController.createJobApplication(jobApp); + System.assert(false, 'Expected an exception for duplicate job but did not get it.'); + } catch (Exception e) { + //System.debug('Caught exception message: ' + e.getMessage()); + + // Check for either expected message or "Script-thrown exception" as a fallback + Boolean isExpectedMessage = e.getMessage().contains('This job is already saved!') || + e.getMessage().contains('Script-thrown exception'); + + Assert.areEqual(true, isExpectedMessage,'Expected exception message to indicate duplicate job, but got: ' + e.getMessage()); + } + Test.stopTest(); +} + + @IsTest + static void testSearchJobsEmptyResult() { + HttpResponse mockResponse = new HttpResponse(); + mockResponse.setStatusCode(200); + mockResponse.setBody('{"totalCount": 0, "jobs": []}'); + + Test.setMock(HttpCalloutMock.class, new MockHttpResponseGenerator(mockResponse)); + + API_Config__c config = new API_Config__c(Jooble_API__c = 'test-api-key'); + insert config; + + Test.startTest(); + Map result = JoobleJobSearchController.searchJobs('Software Engineer', 'San Francisco', 1, 10); + Test.stopTest(); + + Assert.areEqual(0, result.get('totalCount'), 'Total count should be zero for empty response'); + List jobs = (List)result.get('jobs'); + Assert.areEqual(0, jobs.size(), 'Jobs list should be empty'); + } + + // Helper method to create a test Job instance + private static JoobleJobSearchController.Job createTestJob() { + JoobleJobSearchController.Job jobApp = new JoobleJobSearchController.Job(); + jobApp.id = '123'; + jobApp.title = 'Software Engineer'; + jobApp.company = 'Tech Co'; + jobApp.link = 'http://example.com'; + jobApp.snippet = 'Great opportunity'; + jobApp.location = 'San Francisco'; + jobApp.updated = 'May 1, 2023'; + return jobApp; + } + + // Mock HTTP Callout Response Generator + private class MockHttpResponseGenerator implements HttpCalloutMock { + private HttpResponse response; + + public MockHttpResponseGenerator(HttpResponse response) { + this.response = response; + } + + public HttpResponse respond(HttpRequest request) { + return response; + } + } +} diff --git a/force-app/main/default/classes/JoobleJobSearchControllerTest.cls-meta.xml b/force-app/main/default/classes/JoobleJobSearchControllerTest.cls-meta.xml new file mode 100644 index 0000000..998805a --- /dev/null +++ b/force-app/main/default/classes/JoobleJobSearchControllerTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 62.0 + Active + diff --git a/force-app/main/default/classes/ScheduleCleanUpStaleJobApplications.cls b/force-app/main/default/classes/ScheduleCleanUpStaleJobApplications.cls new file mode 100644 index 0000000..6e7be84 --- /dev/null +++ b/force-app/main/default/classes/ScheduleCleanUpStaleJobApplications.cls @@ -0,0 +1,6 @@ +global class ScheduleCleanUpStaleJobApplications implements Schedulable { + global void execute(SchedulableContext SC) { + CleanUpStaleJobApplications batchJob = new CleanUpStaleJobApplications(); + Database.executeBatch(batchJob, 200); // Adjust batch size if necessary + } +} \ No newline at end of file diff --git a/force-app/main/default/classes/ScheduleCleanUpStaleJobApplications.cls-meta.xml b/force-app/main/default/classes/ScheduleCleanUpStaleJobApplications.cls-meta.xml new file mode 100644 index 0000000..998805a --- /dev/null +++ b/force-app/main/default/classes/ScheduleCleanUpStaleJobApplications.cls-meta.xml @@ -0,0 +1,5 @@ + + + 62.0 + Active + diff --git a/force-app/main/default/classes/SetPrimaryContactQueueable.cls b/force-app/main/default/classes/SetPrimaryContactQueueable.cls new file mode 100644 index 0000000..218167c --- /dev/null +++ b/force-app/main/default/classes/SetPrimaryContactQueueable.cls @@ -0,0 +1,19 @@ +public with sharing class SetPrimaryContactQueueable implements Queueable { + private List jobAppIds; + + public SetPrimaryContactQueueable(List jobAppIds) { + this.jobAppIds = jobAppIds; + } + + public void execute(QueueableContext context) { + List jobAppList = [ + SELECT Id, Primary_Contact__c, Company_Name__c + FROM Job_Application__c + WHERE Id IN :jobAppIds + ]; + + JobApplicationHelper.setPrimaryContact(jobAppList); + + update jobAppList; + } +} \ No newline at end of file diff --git a/force-app/main/default/classes/SetPrimaryContactQueueable.cls-meta.xml b/force-app/main/default/classes/SetPrimaryContactQueueable.cls-meta.xml new file mode 100644 index 0000000..998805a --- /dev/null +++ b/force-app/main/default/classes/SetPrimaryContactQueueable.cls-meta.xml @@ -0,0 +1,5 @@ + + + 62.0 + Active + diff --git a/force-app/main/default/classes/TaskManager.cls b/force-app/main/default/classes/TaskManager.cls new file mode 100644 index 0000000..ef72237 --- /dev/null +++ b/force-app/main/default/classes/TaskManager.cls @@ -0,0 +1,25 @@ +public with sharing class TaskManager { + public static void createTasksForApplication (List tasks) { + if (!tasks.isEmpty()) { + if (Schema.SObjectType.Task.isCreateable()) { + insert tasks; + } else { + System.debug('User does not have permission to create Task records.'); + } + } + } + + public static Task prepareTask(Job_Application__c app, String type, String priority, String subject) { + Task newTask = new Task( + Subject = subject, + ActivityDate = Date.today().addDays(7), + Priority = priority, + OwnerId = app.OwnerId, + WhatId = app.Id, + Application_Status_Snapshot__c = app.Job_Application_Status__c, + Type = type + ); + + return newTask; + } +} \ No newline at end of file diff --git a/force-app/main/default/classes/TaskManager.cls-meta.xml b/force-app/main/default/classes/TaskManager.cls-meta.xml new file mode 100644 index 0000000..7a51829 --- /dev/null +++ b/force-app/main/default/classes/TaskManager.cls-meta.xml @@ -0,0 +1,5 @@ + + + 58.0 + Active + diff --git a/force-app/main/default/classes/TestDataFactory.cls b/force-app/main/default/classes/TestDataFactory.cls new file mode 100644 index 0000000..5f0def4 --- /dev/null +++ b/force-app/main/default/classes/TestDataFactory.cls @@ -0,0 +1,80 @@ +@isTest +public class TestDataFactory { + + // Utility method to create multiple Job Applications with associated Accounts and Contacts + public static List createJobApplications(Integer count, String initialStatus, Integer contactsPerAccount) { + List accounts = new List(); + List contacts = new List(); + List jobApplications = new List(); + + // Creating associated Accounts and Contacts + for (Integer i = 0; i < count; i++) { + Account acc = new Account(Name = 'Test Account ' + i); + accounts.add(acc); + } + insert accounts; + + for (Account acc : accounts) { + for (Integer j = 0; j < contactsPerAccount; j++) { + Contact con = new Contact( + FirstName = 'Test', + LastName = 'Contact ' + j + ' for Account ' + acc.Name, + AccountId = acc.Id + ); + contacts.add(con); + } + } + insert contacts; + + // Creating Job Applications with initial status + for (Account acc : accounts) { + Job_Application__c jobApp = new Job_Application__c( + Job_Application_Status__c = initialStatus, + Company_Name__c = acc.Id, + Primary_Contact__c = null, + OwnerId = UserInfo.getUserId() // Setting the current user as the owner + ); + jobApplications.add(jobApp); + } + + insert jobApplications; + return jobApplications; + } + + // Utility method to update a specific field on Job Applications (e.g., change status) + public static void updateJobApplicationStatus(List jobApplications, String newStatus) { + for (Job_Application__c app : jobApplications) { + app.Job_Application_Status__c = newStatus; + } + update jobApplications; + } + + // Utility method for creating a single Job Application + public static Job_Application__c createSingleJobApplication(String status) { + Account acc = new Account(Name = 'Single Test Account'); + insert acc; + + List contacts = new List(); + for (Integer j = 0; j < 3; j++) { // Change the number here for more or fewer contacts + Contact con = new Contact(FirstName = 'Single', LastName = 'Test Contact ' + j, AccountId = acc.Id); + contacts.add(con); + } + insert contacts; + + Job_Application__c jobApp = new Job_Application__c( + Job_Application_Status__c = status, + Company_Name__c = acc.Id, // Link to the account + Primary_Contact__c = contacts[0].Id, // Link to the first contact created + OwnerId = UserInfo.getUserId() + ); + insert jobApp; + + return jobApp; + } + + // Utility method for updating a single Job Application's status + public static void updateSingleJobApplicationStatus(Job_Application__c jobApp, String newStatus) { + jobApp.Job_Application_Status__c = newStatus; + update jobApp; + } +} diff --git a/force-app/main/default/classes/TestDataFactory.cls-meta.xml b/force-app/main/default/classes/TestDataFactory.cls-meta.xml new file mode 100644 index 0000000..998805a --- /dev/null +++ b/force-app/main/default/classes/TestDataFactory.cls-meta.xml @@ -0,0 +1,5 @@ + + + 62.0 + Active + diff --git a/force-app/main/default/classes/TriggerHandler.cls b/force-app/main/default/classes/TriggerHandler.cls new file mode 100644 index 0000000..27b4219 --- /dev/null +++ b/force-app/main/default/classes/TriggerHandler.cls @@ -0,0 +1,283 @@ +/* + * This is a general-purpose Trigger Framework designed to handle various trigger contexts like + * before insert, after insert, before update, etc. + * This framework provides a structured way to handle these operations and is designed to be + * extensible, so developers can add more functionalities as needed. + * + * IMPORTANT: This file is a foundational piece of code. It should NOT be edited directly. + * Instead, extend this class to add more specific trigger functionalities. + * + * Once the framework on your local machine, save and deploy this file into your Salesforce org. + * + * Source: https://github.com/kevinohara80/sfdc-trigger-framework/tree/master + */ +public virtual class TriggerHandler { + + // Keeps track of how many times the run() method was invoked for each handler + // Uncomment when needing to use + // private static Map loopCountMap; + + // List of handler names that should not be executed + private static Set bypassedHandlers; + + // Represents the current context in which the trigger is executing (e.g., before insert, after update, etc.) + @TestVisible + private TriggerContext context; + + // Indicates if the trigger is currently executing + @TestVisible + private Boolean isTriggerExecuting; + + // Initialization of static variables + static { + // Uncomment when needing to use + // loopCountMap = new Map(); + bypassedHandlers = new Set(); + } + + // Constructor initializes the trigger context + public TriggerHandler() { + this.setTriggerContext(); + } + + /*************************************** + * public instance methods + ***************************************/ + + // Main method that handles the trigger execution + public void run() { + + + // Check if the trigger should continue execution + if(!validateRun()) { + return; + } + + // Increment the count of how many times this handler has run + // Uncomment when needing to use + // addToLoopCount(); + + // Dispatch execution to the appropriate handler method based on the trigger context + switch on this.context { + // Uncomment when needing these contexts + // when BEFORE_INSERT { + // this.beforeInsert(); + // } + // when BEFORE_UPDATE { + // this.beforeUpdate(); + // } + // when BEFORE_DELETE { + // this.beforeDelete(); + // } + when AFTER_INSERT { + this.afterInsert(); + } + when AFTER_UPDATE { + this.afterUpdate(); + } + // Uncomment later when in use + // when AFTER_DELETE { + // this.afterDelete(); + // } + // when AFTER_UNDELETE { + // this.afterUndelete(); + // } + } + } + + // Set the maximum number of times the handler can run + // Uncomment later when needing to use + // public void setMaxLoopCount(Integer max) { + // String handlerName = getHandlerName(); + // if(!TriggerHandler.loopCountMap.containsKey(handlerName)) { + // TriggerHandler.loopCountMap.put(handlerName, new LoopCount(max)); + // } else { + // TriggerHandler.loopCountMap.get(handlerName).setMax(max); + // } + // } + + // Clear the maximum loop count for the handler + // Uncomment later when needing to use + // public void clearMaxLoopCount() { + // this.setMaxLoopCount(-1); + // } + + /*************************************** + * public static methods + ***************************************/ + // Uncomment later + // // Bypass a particular handler from execution + // public static void bypass(String handlerName) { + // TriggerHandler.bypassedHandlers.add(handlerName); + // } + + // // Allow a previously bypassed handler to execute + // public static void clearBypass(String handlerName) { + // TriggerHandler.bypassedHandlers.remove(handlerName); + // } + + // // Check if a handler is bypassed + // public static Boolean isBypassed(String handlerName) { + // return TriggerHandler.bypassedHandlers.contains(handlerName); + // } + + // // Clear all bypassed handlers + // public static void clearAllBypasses() { + // TriggerHandler.bypassedHandlers.clear(); + // } + + /*************************************** + * private instancemethods + ***************************************/ + + // Set the trigger's execution context + @TestVisible + private void setTriggerContext() { + this.setTriggerContext(null, false); + } + + // Detailed method to determine and set the trigger's execution context + @TestVisible + private void setTriggerContext(String ctx, Boolean testMode) { + if(!Trigger.isExecuting && !testMode) { + this.isTriggerExecuting = false; + return; + } else { + this.isTriggerExecuting = true; + } + + if((Trigger.isExecuting && Trigger.isBefore && Trigger.isInsert) || + (ctx != null && ctx == 'before insert')) { + this.context = TriggerContext.BEFORE_INSERT; + } + // Uncomment when using these Trigger Contexts + // else if((Trigger.isExecuting && Trigger.isBefore && Trigger.isUpdate) || + // (ctx != null && ctx == 'before update')){ + // this.context = TriggerContext.BEFORE_UPDATE; + // } else if((Trigger.isExecuting && Trigger.isBefore && Trigger.isDelete) || + // (ctx != null && ctx == 'before delete')) { + // this.context = TriggerContext.BEFORE_DELETE; + // } + else if((Trigger.isExecuting && Trigger.isAfter && Trigger.isInsert) || + (ctx != null && ctx == 'after insert')) { + this.context = TriggerContext.AFTER_INSERT; + } else if((Trigger.isExecuting && Trigger.isAfter && Trigger.isUpdate) || + (ctx != null && ctx == 'after update')) { + this.context = TriggerContext.AFTER_UPDATE; + } + // Uncomment when using these Trigger Contexts + // else if((Trigger.isExecuting && Trigger.isAfter && Trigger.isDelete) || + // (ctx != null && ctx == 'after delete')) { + // this.context = TriggerContext.AFTER_DELETE; + // } else if((Trigger.isExecuting && Trigger.isAfter && Trigger.isUndelete) || + // (ctx != null && ctx == 'after undelete')) { + // this.context = TriggerContext.AFTER_UNDELETE; + // } + } + + // Increment how many times the handler has been run + // Uncomment when needing to use + // @TestVisible + // private void addToLoopCount() { + // String handlerName = getHandlerName(); + // if(TriggerHandler.loopCountMap.containsKey(handlerName)) { + // Boolean exceeded = TriggerHandler.loopCountMap.get(handlerName).increment(); + // if(exceeded) { + // Integer max = TriggerHandler.loopCountMap.get(handlerName).max; + // throw new TriggerHandlerException('Maximum loop count of ' + String.valueOf(max) + ' reached in ' + handlerName); + // } + // } + // } + + // Verify that the trigger should execute + @TestVisible + private Boolean validateRun() { + if(!this.isTriggerExecuting || this.context == null) { + throw new TriggerHandlerException('Trigger handler called outside of Trigger execution'); + } + return !TriggerHandler.bypassedHandlers.contains(getHandlerName()); + } + + // Get the name of the handler that's currently executing + @TestVisible + private String getHandlerName() { + return String.valueOf(this).substring(0,String.valueOf(this).indexOf(':')); + } + + /*************************************** + * context methods + ***************************************/ + + // These are virtual methods that can be overridden in child classes + // to provide specific functionalities for different trigger contexts. + @TestVisible + protected virtual void beforeInsert(){} + @TestVisible + protected virtual void beforeUpdate(){} + @TestVisible + protected virtual void beforeDelete(){} + @TestVisible + protected virtual void afterInsert(){} + @TestVisible + protected virtual void afterUpdate(){} + @TestVisible + protected virtual void afterDelete(){} + @TestVisible + protected virtual void afterUndelete(){} + + /*************************************** + * inner classes + ***************************************/ + + // LoopCount manages how many times a handler is allowed to run. + // This is to prevent infinite loops or excessive operations that could hit governor limits. + // Uncomment when needing to use + // @TestVisible + // private class LoopCount { + // private Integer max; + // private Integer count; + + // public LoopCount() { + // this.max = 5; + // this.count = 0; + // } + + // public LoopCount(Integer max) { + // this.max = max; + // this.count = 0; + // } + + // public Boolean increment() { + // this.count++; + // return this.exceeded(); + // } + + // public Boolean exceeded() { + // return this.max >= 0 && this.count > this.max; + // } + + // public Integer getMax() { + // return this.max; + // } + + // public Integer getCount() { + // return this.count; + // } + + // public void setMax(Integer max) { + // this.max = max; + // } + // } + + // This enum represents possible trigger contexts, making it easier to identify the current operation. + @TestVisible + private enum TriggerContext { + BEFORE_INSERT, BEFORE_UPDATE, BEFORE_DELETE, + AFTER_INSERT, AFTER_UPDATE, AFTER_DELETE, + AFTER_UNDELETE + } + + // Custom exception to handle specific errors related to the trigger handler. + public class TriggerHandlerException extends Exception {} + +} \ No newline at end of file diff --git a/force-app/main/default/classes/TriggerHandler.cls-meta.xml b/force-app/main/default/classes/TriggerHandler.cls-meta.xml new file mode 100644 index 0000000..7a51829 --- /dev/null +++ b/force-app/main/default/classes/TriggerHandler.cls-meta.xml @@ -0,0 +1,5 @@ + + + 58.0 + Active + diff --git a/force-app/main/default/flexipages/Job_Application_Home_Page.flexipage-meta.xml b/force-app/main/default/flexipages/Job_Application_Home_Page.flexipage-meta.xml new file mode 100644 index 0000000..ae88af2 --- /dev/null +++ b/force-app/main/default/flexipages/Job_Application_Home_Page.flexipage-meta.xml @@ -0,0 +1,36 @@ + + + + top + Region + + + + + jobList + c_jobList + + + left + Region + + + + + paycheckCalculator + c_paycheckCalculator + + + middle + Region + + + right + Region + + Job Application Home Page + + HomePage + diff --git a/force-app/main/default/flexipages/Job_Application_Record_Page.flexipage-meta.xml b/force-app/main/default/flexipages/Job_Application_Record_Page.flexipage-meta.xml new file mode 100644 index 0000000..3ae5888 --- /dev/null +++ b/force-app/main/default/flexipages/Job_Application_Record_Page.flexipage-meta.xml @@ -0,0 +1,558 @@ + + + + + + + collapsed + false + + + enableActionsConfiguration + false + + + enableActionsInNative + false + + + hideChatterActions + false + + + numVisibleActions + 3 + + force:highlightsPanel + force_highlightsPanel + + + Replace + header + Region + + + + + + relatedListComponentOverride + NONE + + + rowsToDisplay + 10 + + + showActionBar + true + + force:relatedListContainer + force_relatedListContainer2 + + + Replace + relatedTabContent + Facet + + + + + + uiBehavior + required + + Record.Name + RecordNameField + + + + + + uiBehavior + none + + Record.Job_Position__c + RecordJob_Position_cField + + + + + + uiBehavior + none + + Record.Job_Application_Status__c + RecordJob_Application_Status_cField + + + + + + uiBehavior + none + + Record.Company_Name__c + RecordCompany_Name_cField + + + + + + uiBehavior + none + + Record.Description__c + RecordDescription_cField + + + + + + uiBehavior + none + + Record.URL__c + RecordURL_cField + + + + + + uiBehavior + none + + Record.Follow_up_date__c + RecordFollow_up_date_cField + + + + + + uiBehavior + none + + Record.Primary_Contact__c + RecordPrimary_Contact_cField + + + + + + uiBehavior + none + + Record.Location__c + RecordLocation__cField + + + + + + uiBehavior + none + + Record.Job_Source__c + RecordJob_Source__cField + + + Facet-c575af35-ce67-49d2-ae20-7a1c282c17bd + Facet + + + + + + uiBehavior + none + + Record.Date_Posted__c + RecordDate_Posted__cField + + + + + + uiBehavior + none + + Record.Date_Applied__c + RecordApplication_Date_cField + + + + + + uiBehavior + none + + Record.Work_Location__c + RecordLocation_cField + + + + + + uiBehavior + none + + Record.Salary__c + RecordSalary_cField + + + + + + uiBehavior + none + + Record.Rating__c + RecordRating_cField + + + + + + uiBehavior + none + + Record.Notes__c + RecordNotes_cField + + + + + + uiBehavior + none + + Record.OwnerId + RecordOwnerIdField + + + Facet-a29369b8-74f0-46f3-b6cb-a086579ce26e + Facet + + + + + + body + Facet-c575af35-ce67-49d2-ae20-7a1c282c17bd + + flexipage:column + flexipage_column + + + + + + body + Facet-a29369b8-74f0-46f3-b6cb-a086579ce26e + + flexipage:column + flexipage_column2 + + + Facet-c6d3bef8-f155-4a58-848f-5f6aa9493b5f + Facet + + + + + + columns + Facet-c6d3bef8-f155-4a58-848f-5f6aa9493b5f + + + horizontalAlignment + false + + + label + @@@SFDCInformationSFDC@@@ + + flexipage:fieldSection + flexipage_fieldSection + + + Facet-8c396326-8bfe-4476-8b39-33560b76b60d + Facet + + + + + + uiBehavior + readonly + + Record.CreatedById + RecordCreatedByIdField + + + + + + uiBehavior + none + + Record.JoobleExternalId__c + RecordJoobleExternalId_cField + + + Facet-36628137-fb27-4f6c-8178-8704c0a78c63 + Facet + + + + + + uiBehavior + readonly + + Record.LastModifiedById + RecordLastModifiedByIdField + + + + + + uiBehavior + required + + Record.Company_Name_Text__c + RecordCompany_Name_Text_cField + + + Facet-73218a02-4c73-4ca2-8196-6fe0d5f01863 + Facet + + + + + + body + Facet-36628137-fb27-4f6c-8178-8704c0a78c63 + + flexipage:column + flexipage_column3 + + + + + + body + Facet-73218a02-4c73-4ca2-8196-6fe0d5f01863 + + flexipage:column + flexipage_column4 + + + Facet-3988af4d-d7b1-4533-bde8-2518b77b6228 + Facet + + + + + + columns + Facet-3988af4d-d7b1-4533-bde8-2518b77b6228 + + + horizontalAlignment + false + + + label + @@@SFDCSystem_InformationSFDC@@@ + + flexipage:fieldSection + flexipage_fieldSection2 + + + Facet-8ef76959-e0e4-4f12-ad24-1716d361fd74 + Facet + + + + + + body + Facet-8c396326-8bfe-4476-8b39-33560b76b60d + + + label + Standard.Tab.fields + + + name + accordionSection1 + + flexipage:accordionSection + flexipage_accordionSection + + + + + + body + Facet-8ef76959-e0e4-4f12-ad24-1716d361fd74 + + + label + Standard.Tab.additionalFields + + + name + accordionSection2 + + flexipage:accordionSection + flexipage_accordionSection2 + + + Facet-fa620614-77c8-4d5d-8acf-69e86ec75b3f + Facet + + + + + + accordionSections + Facet-fa620614-77c8-4d5d-8acf-69e86ec75b3f + + + defaultSectionName + accordionSection1 + + flexipage:accordion + flexipage_accordion + + + + + force:recordDetailPanelMobile + force_recordDetailPanelMobile + + + Replace + detailTabContent + Facet + + + + + + body + relatedTabContent + + + title + Standard.Tab.relatedLists + + flexipage:tab + relatedListsTab + + + + + + active + true + + + body + detailTabContent + + + title + Standard.Tab.detail + + flexipage:tab + detailTab + + + Replace + maintabs + Facet + + + + + + tabs + maintabs + + flexipage:tabset + flexipage_tabset + + + Replace + main + Region + + + + + + showLegacyActivityComposer + false + + runtime_sales_activities:activityPanel + runtime_sales_activities_activityPanel + + + Replace + activityTabContent + Facet + + + + + + active + true + + + body + activityTabContent + + + title + Standard.Tab.activity + + flexipage:tab + activityTab + + + Replace + sidebartabs + Facet + + + + + + tabs + sidebartabs + + flexipage:tabset + flexipage_tabset2 + + + + + paycheckCalculator + c_paycheckCalculator + + + Replace + sidebar + Region + + Job Application Record Page + flexipage__default_rec_L + Job_Application__c + + RecordPage + diff --git a/force-app/main/default/flexipages/Job_Application_Tracker_UtilityBar.flexipage-meta.xml b/force-app/main/default/flexipages/Job_Application_Tracker_UtilityBar.flexipage-meta.xml new file mode 100644 index 0000000..2b787ea --- /dev/null +++ b/force-app/main/default/flexipages/Job_Application_Tracker_UtilityBar.flexipage-meta.xml @@ -0,0 +1,20 @@ + + + + utilityItems + Region + + + backgroundComponents + Background + + Job Application Tracker UtilityBar + + UtilityBar + diff --git a/force-app/main/default/flexipages/LWCs_Test_HomePage.flexipage-meta.xml b/force-app/main/default/flexipages/LWCs_Test_HomePage.flexipage-meta.xml new file mode 100644 index 0000000..917c45c --- /dev/null +++ b/force-app/main/default/flexipages/LWCs_Test_HomePage.flexipage-meta.xml @@ -0,0 +1,32 @@ + + + + + + jobList + c_jobList + + + region1 + Region + + + + + paycheckCalculator + c_paycheckCalculator + + + region2 + Region + + + region3 + Region + + LWCs Test HomePage + + HomePage + diff --git a/force-app/main/default/flexipages/LightningService_UtilityBar.flexipage-meta.xml b/force-app/main/default/flexipages/LightningService_UtilityBar.flexipage-meta.xml new file mode 100644 index 0000000..3f4204c --- /dev/null +++ b/force-app/main/default/flexipages/LightningService_UtilityBar.flexipage-meta.xml @@ -0,0 +1,25 @@ + + + The default utility bar for the Service Console application. + + + + console:history + console_history + + + + + notes:utilityBarNoteList + notes_utilityBarNoteList + + + utilityItems + Region + + Service Console App Utility Bar + + UtilityBar + diff --git a/force-app/main/default/layouts/Event-Event Layout.layout-meta.xml b/force-app/main/default/layouts/Event-Event Layout.layout-meta.xml new file mode 100644 index 0000000..ce05859 --- /dev/null +++ b/force-app/main/default/layouts/Event-Event Layout.layout-meta.xml @@ -0,0 +1,156 @@ + + + OpenSlackRecordChannel + + false + false + true + + + + Required + OwnerId + + + Required + Subject + + + Edit + Type + + + Edit + WhoId + + + Readonly + Phone + + + Readonly + Email + + + Edit + WhatId + + + + + Edit + Location + + + Required + StartDateTime + + + Required + EndDateTime + + + Edit + IsAllDayEvent + + + + + + false + false + true + + + + + + + false + false + true + + + + + + + false + false + true + + + + Readonly + CreatedById + + + + + Readonly + LastModifiedById + + + + + + false + false + true + + + + Edit + Description + + + + + + true + true + false + + + + + + + + + + Readonly + WhoId + + + + + Readonly + WhatId + + + + + RelatedEventAttendeeAccept + + + RelatedEventAttendeeNotDecided + + + RelatedEventAttendeeDecline + + + RelatedActivityAttachmentList + + WhoId + WhatId + false + false + false + + 00hbm000007qmMN + 4 + 0 + Default + + diff --git a/force-app/main/default/layouts/Job_Application__c-Job Application Layout.layout-meta.xml b/force-app/main/default/layouts/Job_Application__c-Job Application Layout.layout-meta.xml new file mode 100644 index 0000000..f65c391 --- /dev/null +++ b/force-app/main/default/layouts/Job_Application__c-Job Application Layout.layout-meta.xml @@ -0,0 +1,150 @@ + + + OpenSlackRecordChannel + Submit + + false + false + true + + + + Required + Job_Application_Status__c + + + Readonly + Name + + + Edit + Job_Position__c + + + Edit + Date_Applied__c + + + Edit + Primary_Contact__c + + + Edit + Rating__c + + + Edit + Follow_up_date__c + + + Edit + Salary__c + + + Edit + Company_Name__c + + + Edit + Description__c + + + Edit + URL__c + + + Edit + Work_Location__c + + + Edit + Notes__c + + + Edit + Company_Name_Text__c + + + Edit + JoobleExternalId__c + + + Edit + Date_Posted__c + + + Edit + Job_Source__c + + + + + Edit + OwnerId + + + + + + false + false + true + + + + Readonly + CreatedById + + + + + Readonly + LastModifiedById + + + + + + true + false + true + + + + + + + + Job_Application_Status__c + + + TASK.SUBJECT + TASK.WHO_NAME + ACTIVITY.TASK + TASK.DUE_DATE + TASK.STATUS + TASK.PRIORITY + CORE.USERS.FULL_NAME + RelatedActivityList + + + TASK.SUBJECT + TASK.WHO_NAME + ACTIVITY.TASK + TASK.DUE_DATE + CORE.USERS.FULL_NAME + TASK.LAST_UPDATE + RelatedHistoryList + + false + false + false + false + false + + 00haj000009QY5h + 4 + 0 + Default + + diff --git a/force-app/main/default/layouts/Task-Task Layout.layout-meta.xml b/force-app/main/default/layouts/Task-Task Layout.layout-meta.xml new file mode 100644 index 0000000..a62d7b1 --- /dev/null +++ b/force-app/main/default/layouts/Task-Task Layout.layout-meta.xml @@ -0,0 +1,172 @@ + + + false + PersonalTagging + PublicTagging + + false + false + true + + + + Required + OwnerId + + + Required + Subject + + + Edit + ActivityDate + + + Readonly + Phone + + + Required + Priority + + + Edit + Application_Status_Snapshot__c + + + + + Required + Status + + + Edit + WhoId + + + Edit + WhatId + + + Readonly + Email + + + + + + false + false + true + + + + + + + false + false + true + + + + + + + false + false + true + + + + Readonly + CreatedById + + + + + Readonly + LastModifiedById + + + + + + false + false + true + + + + Edit + Description + + + + + + false + false + false + + + + + + FeedItem.TextPost + + + FeedItem.ContentPost + + + FeedItem.LinkPost + + + FeedItem.PollPost + + + NewTask + + + LogACall + + + NewEvent + + + Task.EditDescription + + + Task.Defer + + + Task.UpdateStatus + + + Task.UpdatePriority + + + + + + Readonly + WhoId + + + + + Readonly + WhatId + + + + + RelatedActivityAttachmentList + + WhoId + WhatId + false + true + true + false + diff --git a/force-app/main/default/lwc/.eslintrc.json b/force-app/main/default/lwc/.eslintrc.json new file mode 100644 index 0000000..a82e76d --- /dev/null +++ b/force-app/main/default/lwc/.eslintrc.json @@ -0,0 +1,14 @@ +{ + "extends": ["@salesforce/eslint-config-lwc/recommended"], + "overrides": [ + { + "files": ["*.test.js"], + "rules": { + "@lwc/lwc/no-unexpected-wire-adapter-usages": "off" + }, + "env": { + "node": true + } + } + ] +} diff --git a/force-app/main/default/lwc/jobList/__tests__/jobList.test.js b/force-app/main/default/lwc/jobList/__tests__/jobList.test.js new file mode 100644 index 0000000..f28a814 --- /dev/null +++ b/force-app/main/default/lwc/jobList/__tests__/jobList.test.js @@ -0,0 +1,26 @@ +import { createElement } from '@lwc/engine-dom'; + +import JobList from 'c/jobList'; + +describe('c-job-list', () => { + afterEach(() => { + // The jsdom instance is shared across test cases in a single file so reset the DOM + while (document.body.firstChild) { + document.body.removeChild(document.body.firstChild); + } + }); + + it('TODO: test case generated by CLI command, please fill in test logic', () => { + // Arrange + const element = createElement('c-job-list', { + is: JobList + }); + + // Act + document.body.appendChild(element); + + // Assert + // const div = element.shadowRoot.querySelector('div'); + expect(1).toBe(1); + }); +}); \ No newline at end of file diff --git a/force-app/main/default/lwc/jobList/jobList.css b/force-app/main/default/lwc/jobList/jobList.css new file mode 100644 index 0000000..6382ac7 --- /dev/null +++ b/force-app/main/default/lwc/jobList/jobList.css @@ -0,0 +1,24 @@ +.slds-scrollable_y { + overflow-y: auto; + max-height: 300px; +} + +.custom-card { + --sds-c-card-heading-font-weight: bold; +} + +.custom-title { + font-size: 40px; + line-height: 1.25; +} + +table { + table-layout: auto; + width: 100%; +} + +.slds-table th, .slds-table td { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} diff --git a/force-app/main/default/lwc/jobList/jobList.html b/force-app/main/default/lwc/jobList/jobList.html new file mode 100644 index 0000000..0f521f8 --- /dev/null +++ b/force-app/main/default/lwc/jobList/jobList.html @@ -0,0 +1,50 @@ + diff --git a/force-app/main/default/lwc/jobList/jobList.js b/force-app/main/default/lwc/jobList/jobList.js new file mode 100644 index 0000000..f0be2e6 --- /dev/null +++ b/force-app/main/default/lwc/jobList/jobList.js @@ -0,0 +1,126 @@ +import { LightningElement, track } from 'lwc'; +import { ShowToastEvent } from 'lightning/platformShowToastEvent'; +import searchJobs from '@salesforce/apex/JoobleJobSearchController.searchJobs'; +import createJobApplication from '@salesforce/apex/JoobleJobSearchController.createJobApplication'; + + +export default class JobList extends LightningElement { + @track keywords = ''; + @track location = ''; + @track jobs = []; + @track totalPages = 1; + @track currentPage = 1; + @track pageSize = 10; + @track errorMessage = ''; + @track totalCount = 0; + @track selectedJob; // Track the selected job + + get hasJobs() { + return this.jobs.length > 0; + } + + handleKeywordChange(event) { + this.keywords = event.target.value; + } + + handleLocationChange(event) { + this.location = event.target.value; + } + + async handleSearch() { + this.currentPage = 1; + await this.fetchJobs(); + } + + async fetchJobs() { + try { + const results = await searchJobs({ + keyword: this.keywords, + location: this.location, + pageNumber: this.currentPage, + pageSize: this.pageSize + }); + if (results) { + this.jobs = results.jobs || []; + this.totalCount = results.totalCount || 0; + this.totalPages = Math.max(1, Math.ceil(this.totalCount / this.pageSize)); + } + } catch (error) { + console.error('Error fetching jobs:', error); + this.errorMessage = 'Failed to fetch jobs. Please try again.'; + this.jobs = []; + this.totalPages = 1; + this.currentPage = 1; + } + } + + handlePageChange(event) { + const newPage = parseInt(event.detail.pageNumber, 10); + if (newPage >= 1 && newPage <= this.totalPages && newPage !== this.currentPage) { + this.currentPage = newPage; + this.fetchJobs(); + } + } + + handleReset() { + this.keywords = ''; + this.location = ''; + this.jobs = []; + this.currentPage = 1; + this.totalPages = 1; + this.totalCount = 0; + } + + // Capture the selected job from the child component + handleJobSelected(event) { + this.selectedJob = event.detail; // Capture the selected job + console.log('Selected job:', this.selectedJob); // For debugging + } + + // Call this method to save the selected job when required + getRecordUrl(recordId) { + return `/lightning/r/Job_Application__c/${recordId}/view`; + } + + handleSaveJob(event) { + const jobData = event.detail; + console.log('Job data before save:', JSON.stringify(jobData, null, 2)); + + createJobApplication({ jobApp: jobData }) + .then(recordId => { + this.dispatchEvent( + new ShowToastEvent({ + title: 'Success', + message: 'Job saved successfully: {0}', + messageData: [ + { + url: this.getRecordUrl(recordId), + label: jobData.title + } + ], + variant: 'success' + }) + ); + }) + .catch(error => { + const errorDetails = { + message: error?.body?.message || 'Unknown error', + type: error?.body?.exceptionType || 'No type available', + stack: error?.body?.stackTrace || 'No stack trace' + }; + console.log('Error details:', errorDetails); + + this.dispatchEvent( + new ShowToastEvent({ + title: 'Error', + message: errorDetails.message, + variant: 'error' + }) + ); + }); + } + + getRecordUrl(recordId) { + return `/lightning/r/Job_Application__c/${recordId}/view`; + } +} \ No newline at end of file diff --git a/force-app/main/default/lwc/jobList/jobList.js-meta.xml b/force-app/main/default/lwc/jobList/jobList.js-meta.xml new file mode 100644 index 0000000..dcfdf2f --- /dev/null +++ b/force-app/main/default/lwc/jobList/jobList.js-meta.xml @@ -0,0 +1,13 @@ + + + 62.0 + true + + lightning__HomePage + lightning__RecordPage + lightning__AppPage + lightning__FlowScreen + lightning__Tab + + + \ No newline at end of file diff --git a/force-app/main/default/lwc/jobTable/__tests__/jobTable.test.js b/force-app/main/default/lwc/jobTable/__tests__/jobTable.test.js new file mode 100644 index 0000000..f423a45 --- /dev/null +++ b/force-app/main/default/lwc/jobTable/__tests__/jobTable.test.js @@ -0,0 +1,25 @@ +import { createElement } from 'lwc'; +import JobTable from 'c/jobTable'; + +describe('c-job-table', () => { + afterEach(() => { + // The jsdom instance is shared across test cases in a single file so reset the DOM + while (document.body.firstChild) { + document.body.removeChild(document.body.firstChild); + } + }); + + it('TODO: test case generated by CLI command, please fill in test logic', () => { + // Arrange + const element = createElement('c-job-table', { + is: JobTable + }); + + // Act + document.body.appendChild(element); + + // Assert + // const div = element.shadowRoot.querySelector('div'); + expect(1).toBe(1); + }); +}); \ No newline at end of file diff --git a/force-app/main/default/lwc/jobTable/jobTable.html b/force-app/main/default/lwc/jobTable/jobTable.html new file mode 100644 index 0000000..8fcf87a --- /dev/null +++ b/force-app/main/default/lwc/jobTable/jobTable.html @@ -0,0 +1,31 @@ + diff --git a/force-app/main/default/lwc/jobTable/jobTable.js b/force-app/main/default/lwc/jobTable/jobTable.js new file mode 100644 index 0000000..02962da --- /dev/null +++ b/force-app/main/default/lwc/jobTable/jobTable.js @@ -0,0 +1,108 @@ +import { LightningElement, api, track } from 'lwc'; + +export default class JobTable extends LightningElement { + @api jobs = []; + @api totalPages = 1; + _currentPage = 1; + + @api + get currentPage() { + return this._currentPage; + } + + set currentPage(value) { + this._currentPage = Number(value); + } + + get isPreviousDisabled() { + return this.currentPage === 1; + } + + get isNextDisabled() { + return this.currentPage === this.totalPages; + } + + columns = [ + { label: 'Job Title', fieldName: 'title', sortable: true }, + { label: 'Company', fieldName: 'company', sortable: true }, + { label: 'Location', fieldName: 'location', sortable: true }, + { label: 'Salary', fieldName: 'salary', sortable: true }, + { label: 'Date Posted', fieldName: 'updated', sortable: true }, + { label: 'Action', type: 'button', typeAttributes: { + label: 'Save', + name: 'save', + variant: 'neutral' + }} + ]; + + /*handleRowAction(event) { + const row = event.detail.row; + const actionName = event.detail.action.name; + + if (actionName === 'save') { + // Create a new object without the 'id' property + const { id, ...jobToSave } = row; + + this.dispatchEvent(new CustomEvent('savejob', { + detail: jobToSave + })); + } + }*/ + + handleRowAction(event) { + const row = event.detail.row; + const actionName = event.detail.action.name; + + if (actionName === 'save') { + // Include the ID in the job data + const jobToSave = { + id: row.id, + title: row.title, + link: row.link, + salary: row.salary, + snippet: row.snippet, + company: row.company, + type: row.type, + location: row.location + }; + + this.dispatchEvent(new CustomEvent('savejob', { + detail: jobToSave + })); + } + } + + handleSave(event) { + const jobId = event.detail.row.id; // Get the job ID from the row + const selectedJob = this.jobs.find(job => job.id === jobId); // Find the selected job + + console.log('Selected job:', selectedJob); // Log the selected job + + // Dispatch event to save the job + this.dispatchEvent(new CustomEvent('savejob', { detail: selectedJob })); +} + + get paginatedJobs() { + return this.jobs; // Use the full jobs array for the current page + } + + handlePageChange(pageNumber) { + if (pageNumber >= 1 && pageNumber <= this.totalPages) { + this.dispatchEvent(new CustomEvent('pagechange', { + detail: { pageNumber } + })); + } + } + + handleNextPage() { + if (this._currentPage < this.totalPages) { + this.handlePageChange(this._currentPage + 1); + } + } + + handlePreviousPage() { + if (this._currentPage > 1) { + this.handlePageChange(this._currentPage - 1); + } + } +} diff --git a/force-app/main/default/lwc/jobTable/jobTable.js-meta.xml b/force-app/main/default/lwc/jobTable/jobTable.js-meta.xml new file mode 100644 index 0000000..353961e --- /dev/null +++ b/force-app/main/default/lwc/jobTable/jobTable.js-meta.xml @@ -0,0 +1,5 @@ + + + 62.0 + false + \ No newline at end of file diff --git a/force-app/main/default/lwc/paycheckCalculator/__tests__/paycheckCalculator.test.js b/force-app/main/default/lwc/paycheckCalculator/__tests__/paycheckCalculator.test.js new file mode 100644 index 0000000..b84c970 --- /dev/null +++ b/force-app/main/default/lwc/paycheckCalculator/__tests__/paycheckCalculator.test.js @@ -0,0 +1,26 @@ +import { createElement } from '@lwc/engine-dom'; + +import PaycheckCalculator from 'c/paycheckCalculator'; + +describe('c-paycheck-calculator', () => { + afterEach(() => { + // The jsdom instance is shared across test cases in a single file so reset the DOM + while (document.body.firstChild) { + document.body.removeChild(document.body.firstChild); + } + }); + + it('TODO: test case generated by CLI command, please fill in test logic', () => { + // Arrange + const element = createElement('c-paycheck-calculator', { + is: PaycheckCalculator + }); + + // Act + document.body.appendChild(element); + + // Assert + // const div = element.shadowRoot.querySelector('div'); + expect(1).toBe(1); + }); +}); \ No newline at end of file diff --git a/force-app/main/default/lwc/paycheckCalculator/paycheckCalculator.html b/force-app/main/default/lwc/paycheckCalculator/paycheckCalculator.html new file mode 100644 index 0000000..766edc0 --- /dev/null +++ b/force-app/main/default/lwc/paycheckCalculator/paycheckCalculator.html @@ -0,0 +1,39 @@ + \ No newline at end of file diff --git a/force-app/main/default/lwc/paycheckCalculator/paycheckCalculator.js b/force-app/main/default/lwc/paycheckCalculator/paycheckCalculator.js new file mode 100644 index 0000000..aee9708 --- /dev/null +++ b/force-app/main/default/lwc/paycheckCalculator/paycheckCalculator.js @@ -0,0 +1,119 @@ +import { LightningElement, track } from 'lwc'; + +export default class PaycheckCalculator extends LightningElement { + + annualSalary = 0; + monthlyGrossSalary = 0; + biWeeklyGrossSalary = 0; + monthlyNetSalary = 0; + biWeeklyNetSalary = 0; + + @track federalIncomeTax = 0; + @track socialSecurityTax = 0; + @track medicareWithholding = 0; + @track takeHomePay = 0; + + federalTaxBrackets = [ + { limit: 11000, rate: 0.1 }, + { limit: 44725, rate: 0.12 }, + { limit: 95375, rate: 0.22 }, + { limit: 182100, rate: 0.24 }, + { limit: 231250, rate: 0.32 }, + { limit: 578125, rate: 0.35 }, + { limit: Infinity, rate: 0.37 } + ]; + + // Columns for Salary Breakdown + salaryColumns = [ + { label: 'Salary Type', fieldName: 'label' }, + { label: 'Amount', fieldName: 'value', type: 'currency' } + ]; + + // Columns for Tax Information + taxColumns = [ + { label: 'Taxes Taken Out', fieldName: 'label' }, + { label: 'Amount', fieldName: 'value', type: 'currency' }, + { + label: 'Info', + type: 'button-icon', + fieldName: 'info', // Info field to trigger the tooltip + typeAttributes: { + iconName: 'utility:info', + alternativeText: { fieldName: 'alternativeText' }, // hover text + variant: 'border-filled', + size: 'xx-small', + iconPosition: 'right' + } + } + ]; + + handleChange(event) { + this.annualSalary = Number(event.target.value); + this.calculateBreakdown(); + } + + // Calculate salary intervals and tax liabilities + calculateBreakdown() { + // Calculate gross salary before deductions + this.monthlyGrossSalary = (this.annualSalary / 12).toFixed(2); + this.biWeeklyGrossSalary = (this.annualSalary / 26).toFixed(2); + + // Calculate tax liabilities and net salary + this.calculateTaxLiabilities(); + } + + // Calculate tax-related amounts and take-home pay + calculateTaxLiabilities() { + this.federalIncomeTax = this.calculateFederalIncomeTax(this.annualSalary).toFixed(2); + this.socialSecurityTax = (this.annualSalary * 0.062).toFixed(2); + this.medicareWithholding = (this.annualSalary * 0.0145).toFixed(2); + + // Calculate total deductions + const totalDeductions = parseFloat(this.federalIncomeTax) + parseFloat(this.socialSecurityTax) + parseFloat(this.medicareWithholding); + + // Calculate take-home pay after deductions (net annual) + this.takeHomePay = (this.annualSalary - totalDeductions).toFixed(2); + + // Calculate monthly and bi-weekly pay after deductions (net) + this.monthlyNetSalary = (this.takeHomePay / 12).toFixed(2); // net monthly pay after deductions + this.biWeeklyNetSalary = (this.takeHomePay / 26).toFixed(2); // net bi-weekly pay after deductions + } + + // Calculate Federal Income Tax based on brackets + calculateFederalIncomeTax(income) { + let tax = 0; + let remainingIncome = income; + + for (let bracket of this.federalTaxBrackets) { + if (remainingIncome > bracket.limit) { + tax += bracket.limit * bracket.rate; + remainingIncome -= bracket.limit; + } else { + tax += remainingIncome * bracket.rate; + break; + } + } + + return tax; + } + + // List for salary breakdown (for first section) + get salaryBreakdown() { + return [ + { label: 'Gross Monthly Salary', value: this.monthlyGrossSalary }, + { label: 'Net Monthly Salary (After Deductions)', value: this.monthlyNetSalary }, + { label: 'Gross Bi-Weekly Salary', value: this.biWeeklyGrossSalary }, + { label: 'Net Bi-Weekly Salary (After Deductions)', value: this.biWeeklyNetSalary }, + { label: 'Estimated Take-Home Pay (Annual)', value: this.takeHomePay } + ]; + } + + // List for tax-related breakdown (for second section) + get taxBreakdown() { + return [ + { label: 'Federal Income Tax', value: this.federalIncomeTax, alternativeText: 'Federal income tax is calculated based on tax brackets.' }, + { label: 'Social Security Tax', value: this.socialSecurityTax, alternativeText: 'Social Security tax is 6.2% of your salary.' }, + { label: 'Medicare Withholding', value: this.medicareWithholding, alternativeText: 'Medicare tax is 1.45% of your salary.' } + ]; + } +} \ No newline at end of file diff --git a/force-app/main/default/lwc/paycheckCalculator/paycheckCalculator.js-meta.xml b/force-app/main/default/lwc/paycheckCalculator/paycheckCalculator.js-meta.xml new file mode 100644 index 0000000..44a075f --- /dev/null +++ b/force-app/main/default/lwc/paycheckCalculator/paycheckCalculator.js-meta.xml @@ -0,0 +1,11 @@ + + + 62.0 + true + + lightning__HomePage + lightning__RecordPage + lightning__AppPage + lightning__FlowScreen + + \ No newline at end of file diff --git a/force-app/main/default/lwc/salaryBreakdownItem/__tests__/salaryBreakdownItem.test.js b/force-app/main/default/lwc/salaryBreakdownItem/__tests__/salaryBreakdownItem.test.js new file mode 100644 index 0000000..eeda381 --- /dev/null +++ b/force-app/main/default/lwc/salaryBreakdownItem/__tests__/salaryBreakdownItem.test.js @@ -0,0 +1,25 @@ +import { createElement } from 'lwc'; +import SalaryBreakdownItem from 'c/salaryBreakdownItem'; + +describe('c-salary-breakdown-item', () => { + afterEach(() => { + // The jsdom instance is shared across test cases in a single file so reset the DOM + while (document.body.firstChild) { + document.body.removeChild(document.body.firstChild); + } + }); + + it('TODO: test case generated by CLI command, please fill in test logic', () => { + // Arrange + const element = createElement('c-salary-breakdown-item', { + is: SalaryBreakdownItem + }); + + // Act + document.body.appendChild(element); + + // Assert + // const div = element.shadowRoot.querySelector('div'); + expect(1).toBe(1); + }); +}); \ No newline at end of file diff --git a/force-app/main/default/lwc/salaryBreakdownItem/salaryBreakdownItem.html b/force-app/main/default/lwc/salaryBreakdownItem/salaryBreakdownItem.html new file mode 100644 index 0000000..d8ca984 --- /dev/null +++ b/force-app/main/default/lwc/salaryBreakdownItem/salaryBreakdownItem.html @@ -0,0 +1,22 @@ + \ No newline at end of file diff --git a/force-app/main/default/lwc/salaryBreakdownItem/salaryBreakdownItem.js b/force-app/main/default/lwc/salaryBreakdownItem/salaryBreakdownItem.js new file mode 100644 index 0000000..ec05906 --- /dev/null +++ b/force-app/main/default/lwc/salaryBreakdownItem/salaryBreakdownItem.js @@ -0,0 +1,6 @@ +import { LightningElement, api } from 'lwc'; + +export default class SalaryBreakdownItem extends LightningElement { + @api label; + @api value; +} \ No newline at end of file diff --git a/force-app/main/default/lwc/salaryBreakdownItem/salaryBreakdownItem.js-meta.xml b/force-app/main/default/lwc/salaryBreakdownItem/salaryBreakdownItem.js-meta.xml new file mode 100644 index 0000000..353961e --- /dev/null +++ b/force-app/main/default/lwc/salaryBreakdownItem/salaryBreakdownItem.js-meta.xml @@ -0,0 +1,5 @@ + + + 62.0 + false + \ No newline at end of file diff --git a/force-app/main/default/objects/API_Config__c/API_Config__c.object-meta.xml b/force-app/main/default/objects/API_Config__c/API_Config__c.object-meta.xml new file mode 100644 index 0000000..de218d4 --- /dev/null +++ b/force-app/main/default/objects/API_Config__c/API_Config__c.object-meta.xml @@ -0,0 +1,8 @@ + + + Hierarchy + General Config for API Keys + false + + Public + \ No newline at end of file diff --git a/force-app/main/default/objects/API_Config__c/fields/Jooble_API__c.field-meta.xml b/force-app/main/default/objects/API_Config__c/fields/Jooble_API__c.field-meta.xml new file mode 100644 index 0000000..deb810e --- /dev/null +++ b/force-app/main/default/objects/API_Config__c/fields/Jooble_API__c.field-meta.xml @@ -0,0 +1,11 @@ + + + Jooble_API__c + false + + 100 + false + false + Text + false + diff --git a/force-app/main/default/objects/Activity/fields/Application_Status_Snapshot__c.field-meta.xml b/force-app/main/default/objects/Activity/fields/Application_Status_Snapshot__c.field-meta.xml new file mode 100644 index 0000000..d8fa77b --- /dev/null +++ b/force-app/main/default/objects/Activity/fields/Application_Status_Snapshot__c.field-meta.xml @@ -0,0 +1,10 @@ + + + Application_Status_Snapshot__c + false + + 100 + false + Text + false + diff --git a/force-app/main/default/objects/Event/Event.object-meta.xml b/force-app/main/default/objects/Event/Event.object-meta.xml new file mode 100644 index 0000000..2324f88 --- /dev/null +++ b/force-app/main/default/objects/Event/Event.object-meta.xml @@ -0,0 +1,161 @@ + + + + Accept + Default + + + Accept + Large + Default + + + Accept + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Clone + Default + + + Clone + Large + Default + + + Clone + Small + Default + + + Decline + Default + + + Decline + Large + Default + + + Decline + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + DeleteSeries + Default + + + DeleteSeries + Large + Default + + + DeleteSeries + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + NewEvent + Default + + + NewEvent + Large + Default + + + NewEvent + Small + Default + + + NewSfdcEvent + Default + + + NewSfdcEvent + Large + Default + + + NewSfdcEvent + Small + Default + + + Today + Default + + + Today + Large + Default + + + Today + Small + Default + + + View + Default + + + View + Large + Default + + + View + Small + Default + + SYSTEM + false + + Private + diff --git a/force-app/main/default/objects/Event/fields/ActivityDate.field-meta.xml b/force-app/main/default/objects/Event/fields/ActivityDate.field-meta.xml new file mode 100644 index 0000000..1804c8d --- /dev/null +++ b/force-app/main/default/objects/Event/fields/ActivityDate.field-meta.xml @@ -0,0 +1,5 @@ + + + ActivityDate + false + diff --git a/force-app/main/default/objects/Event/fields/ActivityDateTime.field-meta.xml b/force-app/main/default/objects/Event/fields/ActivityDateTime.field-meta.xml new file mode 100644 index 0000000..a4f176e --- /dev/null +++ b/force-app/main/default/objects/Event/fields/ActivityDateTime.field-meta.xml @@ -0,0 +1,5 @@ + + + ActivityDateTime + false + diff --git a/force-app/main/default/objects/Event/fields/Attendees.field-meta.xml b/force-app/main/default/objects/Event/fields/Attendees.field-meta.xml new file mode 100644 index 0000000..9fa04c4 --- /dev/null +++ b/force-app/main/default/objects/Event/fields/Attendees.field-meta.xml @@ -0,0 +1,4 @@ + + + Attendees + diff --git a/force-app/main/default/objects/Event/fields/Description.field-meta.xml b/force-app/main/default/objects/Event/fields/Description.field-meta.xml new file mode 100644 index 0000000..253d539 --- /dev/null +++ b/force-app/main/default/objects/Event/fields/Description.field-meta.xml @@ -0,0 +1,5 @@ + + + Description + false + diff --git a/force-app/main/default/objects/Event/fields/DurationInMinutes.field-meta.xml b/force-app/main/default/objects/Event/fields/DurationInMinutes.field-meta.xml new file mode 100644 index 0000000..ad4f027 --- /dev/null +++ b/force-app/main/default/objects/Event/fields/DurationInMinutes.field-meta.xml @@ -0,0 +1,5 @@ + + + DurationInMinutes + false + diff --git a/force-app/main/default/objects/Event/fields/Email.field-meta.xml b/force-app/main/default/objects/Event/fields/Email.field-meta.xml new file mode 100644 index 0000000..92f4b61 --- /dev/null +++ b/force-app/main/default/objects/Event/fields/Email.field-meta.xml @@ -0,0 +1,4 @@ + + + Email + diff --git a/force-app/main/default/objects/Event/fields/EndDateTime.field-meta.xml b/force-app/main/default/objects/Event/fields/EndDateTime.field-meta.xml new file mode 100644 index 0000000..26bed6a --- /dev/null +++ b/force-app/main/default/objects/Event/fields/EndDateTime.field-meta.xml @@ -0,0 +1,4 @@ + + + EndDateTime + diff --git a/force-app/main/default/objects/Event/fields/EventSubtype.field-meta.xml b/force-app/main/default/objects/Event/fields/EventSubtype.field-meta.xml new file mode 100644 index 0000000..75198ea --- /dev/null +++ b/force-app/main/default/objects/Event/fields/EventSubtype.field-meta.xml @@ -0,0 +1,4 @@ + + + EventSubtype + diff --git a/force-app/main/default/objects/Event/fields/IsAllDayEvent.field-meta.xml b/force-app/main/default/objects/Event/fields/IsAllDayEvent.field-meta.xml new file mode 100644 index 0000000..f284b1e --- /dev/null +++ b/force-app/main/default/objects/Event/fields/IsAllDayEvent.field-meta.xml @@ -0,0 +1,5 @@ + + + IsAllDayEvent + false + diff --git a/force-app/main/default/objects/Event/fields/IsPrivate.field-meta.xml b/force-app/main/default/objects/Event/fields/IsPrivate.field-meta.xml new file mode 100644 index 0000000..270648b --- /dev/null +++ b/force-app/main/default/objects/Event/fields/IsPrivate.field-meta.xml @@ -0,0 +1,5 @@ + + + IsPrivate + false + diff --git a/force-app/main/default/objects/Event/fields/IsRecurrence2.field-meta.xml b/force-app/main/default/objects/Event/fields/IsRecurrence2.field-meta.xml new file mode 100644 index 0000000..72365c2 --- /dev/null +++ b/force-app/main/default/objects/Event/fields/IsRecurrence2.field-meta.xml @@ -0,0 +1,4 @@ + + + IsRecurrence2 + diff --git a/force-app/main/default/objects/Event/fields/IsReminderSet.field-meta.xml b/force-app/main/default/objects/Event/fields/IsReminderSet.field-meta.xml new file mode 100644 index 0000000..d1a624a --- /dev/null +++ b/force-app/main/default/objects/Event/fields/IsReminderSet.field-meta.xml @@ -0,0 +1,5 @@ + + + IsReminderSet + false + diff --git a/force-app/main/default/objects/Event/fields/Location.field-meta.xml b/force-app/main/default/objects/Event/fields/Location.field-meta.xml new file mode 100644 index 0000000..299359b --- /dev/null +++ b/force-app/main/default/objects/Event/fields/Location.field-meta.xml @@ -0,0 +1,5 @@ + + + Location + false + diff --git a/force-app/main/default/objects/Event/fields/OwnerId.field-meta.xml b/force-app/main/default/objects/Event/fields/OwnerId.field-meta.xml new file mode 100644 index 0000000..711ada1 --- /dev/null +++ b/force-app/main/default/objects/Event/fields/OwnerId.field-meta.xml @@ -0,0 +1,6 @@ + + + OwnerId + false + Lookup + diff --git a/force-app/main/default/objects/Event/fields/Phone.field-meta.xml b/force-app/main/default/objects/Event/fields/Phone.field-meta.xml new file mode 100644 index 0000000..2d3dc65 --- /dev/null +++ b/force-app/main/default/objects/Event/fields/Phone.field-meta.xml @@ -0,0 +1,4 @@ + + + Phone + diff --git a/force-app/main/default/objects/Event/fields/ServiceAppointmentId.field-meta.xml b/force-app/main/default/objects/Event/fields/ServiceAppointmentId.field-meta.xml new file mode 100644 index 0000000..b87ae54 --- /dev/null +++ b/force-app/main/default/objects/Event/fields/ServiceAppointmentId.field-meta.xml @@ -0,0 +1,6 @@ + + + ServiceAppointmentId + false + Lookup + diff --git a/force-app/main/default/objects/Event/fields/ShowAs.field-meta.xml b/force-app/main/default/objects/Event/fields/ShowAs.field-meta.xml new file mode 100644 index 0000000..0cb2ddc --- /dev/null +++ b/force-app/main/default/objects/Event/fields/ShowAs.field-meta.xml @@ -0,0 +1,5 @@ + + + ShowAs + false + diff --git a/force-app/main/default/objects/Event/fields/StartDateTime.field-meta.xml b/force-app/main/default/objects/Event/fields/StartDateTime.field-meta.xml new file mode 100644 index 0000000..640d394 --- /dev/null +++ b/force-app/main/default/objects/Event/fields/StartDateTime.field-meta.xml @@ -0,0 +1,4 @@ + + + StartDateTime + diff --git a/force-app/main/default/objects/Event/fields/Subject.field-meta.xml b/force-app/main/default/objects/Event/fields/Subject.field-meta.xml new file mode 100644 index 0000000..7fc2128 --- /dev/null +++ b/force-app/main/default/objects/Event/fields/Subject.field-meta.xml @@ -0,0 +1,6 @@ + + + Subject + false + Picklist + diff --git a/force-app/main/default/objects/Event/fields/Type.field-meta.xml b/force-app/main/default/objects/Event/fields/Type.field-meta.xml new file mode 100644 index 0000000..260a878 --- /dev/null +++ b/force-app/main/default/objects/Event/fields/Type.field-meta.xml @@ -0,0 +1,6 @@ + + + Type + false + Picklist + diff --git a/force-app/main/default/objects/Event/fields/WhatId.field-meta.xml b/force-app/main/default/objects/Event/fields/WhatId.field-meta.xml new file mode 100644 index 0000000..c5237c5 --- /dev/null +++ b/force-app/main/default/objects/Event/fields/WhatId.field-meta.xml @@ -0,0 +1,6 @@ + + + WhatId + false + Lookup + diff --git a/force-app/main/default/objects/Event/fields/WhoId.field-meta.xml b/force-app/main/default/objects/Event/fields/WhoId.field-meta.xml new file mode 100644 index 0000000..6d445f5 --- /dev/null +++ b/force-app/main/default/objects/Event/fields/WhoId.field-meta.xml @@ -0,0 +1,6 @@ + + + WhoId + false + Lookup + diff --git a/force-app/main/default/objects/Event/listViews/MyRecentEvents.listView-meta.xml b/force-app/main/default/objects/Event/listViews/MyRecentEvents.listView-meta.xml new file mode 100644 index 0000000..2e01608 --- /dev/null +++ b/force-app/main/default/objects/Event/listViews/MyRecentEvents.listView-meta.xml @@ -0,0 +1,18 @@ + + + MyRecentEvents + START_DATE_TIME + END_DATE_TIME + SUBJECT + WHO_NAME + WHAT_NAME + CORE.USERS.ALIAS + ALL_DAY + Mine + + DUE_DATE + equals + LAST_N_DAYS:60 + + + diff --git a/force-app/main/default/objects/Event/listViews/MyTeamsRecentEvents.listView-meta.xml b/force-app/main/default/objects/Event/listViews/MyTeamsRecentEvents.listView-meta.xml new file mode 100644 index 0000000..7247351 --- /dev/null +++ b/force-app/main/default/objects/Event/listViews/MyTeamsRecentEvents.listView-meta.xml @@ -0,0 +1,18 @@ + + + MyTeamsRecentEvents + START_DATE_TIME + END_DATE_TIME + SUBJECT + WHO_NAME + WHAT_NAME + CORE.USERS.ALIAS + ALL_DAY + Team + + DUE_DATE + equals + LAST_N_DAYS:30 + + + diff --git a/force-app/main/default/objects/Event/listViews/MyTeamsUpcomingEvents.listView-meta.xml b/force-app/main/default/objects/Event/listViews/MyTeamsUpcomingEvents.listView-meta.xml new file mode 100644 index 0000000..050f788 --- /dev/null +++ b/force-app/main/default/objects/Event/listViews/MyTeamsUpcomingEvents.listView-meta.xml @@ -0,0 +1,18 @@ + + + MyTeamsUpcomingEvents + START_DATE_TIME + END_DATE_TIME + SUBJECT + WHO_NAME + WHAT_NAME + CORE.USERS.ALIAS + ALL_DAY + Team + + DUE_DATE + equals + TODAY,NEXT_N_DAYS:30 + + + diff --git a/force-app/main/default/objects/Event/listViews/MyUpcomingEvents.listView-meta.xml b/force-app/main/default/objects/Event/listViews/MyUpcomingEvents.listView-meta.xml new file mode 100644 index 0000000..ca13458 --- /dev/null +++ b/force-app/main/default/objects/Event/listViews/MyUpcomingEvents.listView-meta.xml @@ -0,0 +1,18 @@ + + + MyUpcomingEvents + START_DATE_TIME + END_DATE_TIME + SUBJECT + WHO_NAME + WHAT_NAME + CORE.USERS.ALIAS + ALL_DAY + Mine + + DUE_DATE + equals + TODAY,NEXT_N_DAYS:60 + + + diff --git a/force-app/main/default/objects/Event/listViews/TodaysAgenda.listView-meta.xml b/force-app/main/default/objects/Event/listViews/TodaysAgenda.listView-meta.xml new file mode 100644 index 0000000..c335d37 --- /dev/null +++ b/force-app/main/default/objects/Event/listViews/TodaysAgenda.listView-meta.xml @@ -0,0 +1,23 @@ + + + TodaysAgenda + START_DATE_TIME + END_DATE_TIME + SUBJECT + WHO_NAME + WHAT_NAME + CORE.USERS.ALIAS + ALL_DAY + Mine + + DUE_DATE + lessOrEqual + TODAY + + + END_DATE + greaterOrEqual + TODAY + + + diff --git a/force-app/main/default/objects/Job_Application__c/Job_Application__c.object-meta.xml b/force-app/main/default/objects/Job_Application__c/Job_Application__c.object-meta.xml new file mode 100644 index 0000000..7455d3d --- /dev/null +++ b/force-app/main/default/objects/Job_Application__c/Job_Application__c.object-meta.xml @@ -0,0 +1,166 @@ + + + + Accept + Default + + + Accept + Large + Default + + + Accept + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Clone + Default + + + Clone + Large + Default + + + Clone + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Default + + + View + Large + Default + + + View + Small + Default + + true + SYSTEM + Deployed + true + true + false + true + false + true + false + true + true + Private + + + + false + Text + + Job Application + + ReadWrite + Public + diff --git a/force-app/main/default/objects/Job_Application__c/fields/Application_Date__c.field-meta.xml b/force-app/main/default/objects/Job_Application__c/fields/Application_Date__c.field-meta.xml new file mode 100644 index 0000000..53cacf0 --- /dev/null +++ b/force-app/main/default/objects/Job_Application__c/fields/Application_Date__c.field-meta.xml @@ -0,0 +1,10 @@ + + + Application_Date__c + false + + false + false + false + Date + diff --git a/force-app/main/default/objects/Job_Application__c/fields/Company_Name_Text__c.field-meta.xml b/force-app/main/default/objects/Job_Application__c/fields/Company_Name_Text__c.field-meta.xml new file mode 100644 index 0000000..fac0a50 --- /dev/null +++ b/force-app/main/default/objects/Job_Application__c/fields/Company_Name_Text__c.field-meta.xml @@ -0,0 +1,12 @@ + + + Company_Name_Text__c + false + + 100 + false + false + false + Text + false + diff --git a/force-app/main/default/objects/Job_Application__c/fields/Company_Name__c.field-meta.xml b/force-app/main/default/objects/Job_Application__c/fields/Company_Name__c.field-meta.xml new file mode 100644 index 0000000..c704767 --- /dev/null +++ b/force-app/main/default/objects/Job_Application__c/fields/Company_Name__c.field-meta.xml @@ -0,0 +1,15 @@ + + + Company_Name__c + SetNull + false + + Account + Job Application + Job_Application + false + false + false + Lookup + + diff --git a/force-app/main/default/objects/Job_Application__c/fields/Date_Applied__c.field-meta.xml b/force-app/main/default/objects/Job_Application__c/fields/Date_Applied__c.field-meta.xml new file mode 100644 index 0000000..36cd2df --- /dev/null +++ b/force-app/main/default/objects/Job_Application__c/fields/Date_Applied__c.field-meta.xml @@ -0,0 +1,10 @@ + + + Date_Applied__c + false + + false + false + false + Date + diff --git a/force-app/main/default/objects/Job_Application__c/fields/Date_Posted__c.field-meta.xml b/force-app/main/default/objects/Job_Application__c/fields/Date_Posted__c.field-meta.xml new file mode 100644 index 0000000..d2d0628 --- /dev/null +++ b/force-app/main/default/objects/Job_Application__c/fields/Date_Posted__c.field-meta.xml @@ -0,0 +1,13 @@ + + + Date_Posted__c + false + The date posted from Jooble + + 12 + false + false + false + Text + false + diff --git a/force-app/main/default/objects/Job_Application__c/fields/Description__c.field-meta.xml b/force-app/main/default/objects/Job_Application__c/fields/Description__c.field-meta.xml new file mode 100644 index 0000000..96811f5 --- /dev/null +++ b/force-app/main/default/objects/Job_Application__c/fields/Description__c.field-meta.xml @@ -0,0 +1,11 @@ + + + Description__c + false + + 131072 + false + false + Html + 50 + diff --git a/force-app/main/default/objects/Job_Application__c/fields/Follow_up_date__c.field-meta.xml b/force-app/main/default/objects/Job_Application__c/fields/Follow_up_date__c.field-meta.xml new file mode 100644 index 0000000..f4d3759 --- /dev/null +++ b/force-app/main/default/objects/Job_Application__c/fields/Follow_up_date__c.field-meta.xml @@ -0,0 +1,10 @@ + + + Follow_up_date__c + false + + false + false + false + Date + diff --git a/force-app/main/default/objects/Job_Application__c/fields/Job_Application_Status__c.field-meta.xml b/force-app/main/default/objects/Job_Application__c/fields/Job_Application_Status__c.field-meta.xml new file mode 100644 index 0000000..3a16594 --- /dev/null +++ b/force-app/main/default/objects/Job_Application__c/fields/Job_Application_Status__c.field-meta.xml @@ -0,0 +1,88 @@ + + + Job_Application_Status__c + Active + false + + true + false + false + Picklist + + true + + false + + Saved + false + + + + Applying + false + + + + Applied + false + + + + Interviewing + false + + + + Negotiating + false + + + + Accepted + false + + + + Closed + false + + + + Hired + false + false + + + + Interview + false + false + + + + Offer Made + false + false + + + + Rejected + false + false + + + + Submitted + false + false + + + + Under Review + false + false + + + + + diff --git a/force-app/main/default/objects/Job_Application__c/fields/Job_Position__c.field-meta.xml b/force-app/main/default/objects/Job_Application__c/fields/Job_Position__c.field-meta.xml new file mode 100644 index 0000000..e80422b --- /dev/null +++ b/force-app/main/default/objects/Job_Application__c/fields/Job_Position__c.field-meta.xml @@ -0,0 +1,12 @@ + + + Job_Position__c + false + + 100 + false + false + false + Text + false + diff --git a/force-app/main/default/objects/Job_Application__c/fields/Job_Source__c.field-meta.xml b/force-app/main/default/objects/Job_Application__c/fields/Job_Source__c.field-meta.xml new file mode 100644 index 0000000..92ecb64 --- /dev/null +++ b/force-app/main/default/objects/Job_Application__c/fields/Job_Source__c.field-meta.xml @@ -0,0 +1,36 @@ + + + Job_Source__c + false + + false + false + false + Picklist + + true + + false + + Jooble Job Board + false + + + + LinkedIn + false + + + + BuiltIn + false + + + + Glassdoor + false + + + + + diff --git a/force-app/main/default/objects/Job_Application__c/fields/JoobleExternalId__c.field-meta.xml b/force-app/main/default/objects/Job_Application__c/fields/JoobleExternalId__c.field-meta.xml new file mode 100644 index 0000000..89531c2 --- /dev/null +++ b/force-app/main/default/objects/Job_Application__c/fields/JoobleExternalId__c.field-meta.xml @@ -0,0 +1,12 @@ + + + JoobleExternalId__c + true + + 100 + false + false + false + Text + false + diff --git a/force-app/main/default/objects/Job_Application__c/fields/Location__c.field-meta.xml b/force-app/main/default/objects/Job_Application__c/fields/Location__c.field-meta.xml new file mode 100644 index 0000000..ad72022 --- /dev/null +++ b/force-app/main/default/objects/Job_Application__c/fields/Location__c.field-meta.xml @@ -0,0 +1,14 @@ + + + Location__c + physical location + false + Phyisical Location + + 100 + false + false + false + Text + false + diff --git a/force-app/main/default/objects/Job_Application__c/fields/Notes__c.field-meta.xml b/force-app/main/default/objects/Job_Application__c/fields/Notes__c.field-meta.xml new file mode 100644 index 0000000..84bca29 --- /dev/null +++ b/force-app/main/default/objects/Job_Application__c/fields/Notes__c.field-meta.xml @@ -0,0 +1,11 @@ + + + Notes__c + false + + 32768 + false + false + Html + 25 + diff --git a/force-app/main/default/objects/Job_Application__c/fields/Primary_Contact__c.field-meta.xml b/force-app/main/default/objects/Job_Application__c/fields/Primary_Contact__c.field-meta.xml new file mode 100644 index 0000000..f861b43 --- /dev/null +++ b/force-app/main/default/objects/Job_Application__c/fields/Primary_Contact__c.field-meta.xml @@ -0,0 +1,15 @@ + + + Primary_Contact__c + SetNull + Main point of contact for the role + false + + Contact + Job Application + Job_Application + false + false + false + Lookup + diff --git a/force-app/main/default/objects/Job_Application__c/fields/Rating__c.field-meta.xml b/force-app/main/default/objects/Job_Application__c/fields/Rating__c.field-meta.xml new file mode 100644 index 0000000..165c046 --- /dev/null +++ b/force-app/main/default/objects/Job_Application__c/fields/Rating__c.field-meta.xml @@ -0,0 +1,41 @@ + + + Rating__c + false + + false + false + false + Picklist + + true + + false + + 1 + false + + + + 2 + false + + + + 3 + false + + + + 4 + false + + + + 5 + false + + + + + diff --git a/force-app/main/default/objects/Job_Application__c/fields/Salary__c.field-meta.xml b/force-app/main/default/objects/Job_Application__c/fields/Salary__c.field-meta.xml new file mode 100644 index 0000000..47233d5 --- /dev/null +++ b/force-app/main/default/objects/Job_Application__c/fields/Salary__c.field-meta.xml @@ -0,0 +1,13 @@ + + + Salary__c + false + + 18 + false + 0 + false + false + Number + false + diff --git a/force-app/main/default/objects/Job_Application__c/fields/URL__c.field-meta.xml b/force-app/main/default/objects/Job_Application__c/fields/URL__c.field-meta.xml new file mode 100644 index 0000000..4f80983 --- /dev/null +++ b/force-app/main/default/objects/Job_Application__c/fields/URL__c.field-meta.xml @@ -0,0 +1,10 @@ + + + URL__c + false + + false + false + false + Url + diff --git a/force-app/main/default/objects/Job_Application__c/fields/Work_Location__c.field-meta.xml b/force-app/main/default/objects/Job_Application__c/fields/Work_Location__c.field-meta.xml new file mode 100644 index 0000000..ce4efc2 --- /dev/null +++ b/force-app/main/default/objects/Job_Application__c/fields/Work_Location__c.field-meta.xml @@ -0,0 +1,31 @@ + + + Work_Location__c + false + + false + false + false + Picklist + + true + + false + + Remote + false + + + + Hybrid + false + + + + Onsite + false + + + + + diff --git a/force-app/main/default/objects/Job_Application__c/listViews/All.listView-meta.xml b/force-app/main/default/objects/Job_Application__c/listViews/All.listView-meta.xml new file mode 100644 index 0000000..d505851 --- /dev/null +++ b/force-app/main/default/objects/Job_Application__c/listViews/All.listView-meta.xml @@ -0,0 +1,6 @@ + + + All + Everything + + diff --git a/force-app/main/default/objects/Job_Application__c/listViews/Copy_of_All.listView-meta.xml b/force-app/main/default/objects/Job_Application__c/listViews/Copy_of_All.listView-meta.xml new file mode 100644 index 0000000..b0e973f --- /dev/null +++ b/force-app/main/default/objects/Job_Application__c/listViews/Copy_of_All.listView-meta.xml @@ -0,0 +1,15 @@ + + + Copy_of_All + Job_Position__c + NAME + Company_Name__c + Primary_Contact__c + Work_Location__c + Date_Posted__c + Salary__c + Job_Source__c + CREATED_DATE + Everything + + diff --git a/force-app/main/default/objects/Task/Task.object-meta.xml b/force-app/main/default/objects/Task/Task.object-meta.xml new file mode 100644 index 0000000..1233d02 --- /dev/null +++ b/force-app/main/default/objects/Task/Task.object-meta.xml @@ -0,0 +1,119 @@ + + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Close + Default + + + Close + Large + Default + + + Close + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + DeleteSeries + Default + + + DeleteSeries + Large + Default + + + DeleteSeries + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + NewTask + Default + + + NewTask + Large + Default + + + NewTask + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + View + Default + + + View + Large + Default + + + View + Small + Default + + SYSTEM + false + + Private + diff --git a/force-app/main/default/objects/Task/fields/ActivityDate.field-meta.xml b/force-app/main/default/objects/Task/fields/ActivityDate.field-meta.xml new file mode 100644 index 0000000..1804c8d --- /dev/null +++ b/force-app/main/default/objects/Task/fields/ActivityDate.field-meta.xml @@ -0,0 +1,5 @@ + + + ActivityDate + false + diff --git a/force-app/main/default/objects/Task/fields/CallDisposition.field-meta.xml b/force-app/main/default/objects/Task/fields/CallDisposition.field-meta.xml new file mode 100644 index 0000000..c3dd182 --- /dev/null +++ b/force-app/main/default/objects/Task/fields/CallDisposition.field-meta.xml @@ -0,0 +1,5 @@ + + + CallDisposition + false + diff --git a/force-app/main/default/objects/Task/fields/CallDurationInSeconds.field-meta.xml b/force-app/main/default/objects/Task/fields/CallDurationInSeconds.field-meta.xml new file mode 100644 index 0000000..5821231 --- /dev/null +++ b/force-app/main/default/objects/Task/fields/CallDurationInSeconds.field-meta.xml @@ -0,0 +1,5 @@ + + + CallDurationInSeconds + false + diff --git a/force-app/main/default/objects/Task/fields/CallObject.field-meta.xml b/force-app/main/default/objects/Task/fields/CallObject.field-meta.xml new file mode 100644 index 0000000..98e19e3 --- /dev/null +++ b/force-app/main/default/objects/Task/fields/CallObject.field-meta.xml @@ -0,0 +1,5 @@ + + + CallObject + false + diff --git a/force-app/main/default/objects/Task/fields/CallType.field-meta.xml b/force-app/main/default/objects/Task/fields/CallType.field-meta.xml new file mode 100644 index 0000000..270b77c --- /dev/null +++ b/force-app/main/default/objects/Task/fields/CallType.field-meta.xml @@ -0,0 +1,5 @@ + + + CallType + false + diff --git a/force-app/main/default/objects/Task/fields/CompletedDateTime.field-meta.xml b/force-app/main/default/objects/Task/fields/CompletedDateTime.field-meta.xml new file mode 100644 index 0000000..174ce3d --- /dev/null +++ b/force-app/main/default/objects/Task/fields/CompletedDateTime.field-meta.xml @@ -0,0 +1,5 @@ + + + CompletedDateTime + false + diff --git a/force-app/main/default/objects/Task/fields/Description.field-meta.xml b/force-app/main/default/objects/Task/fields/Description.field-meta.xml new file mode 100644 index 0000000..253d539 --- /dev/null +++ b/force-app/main/default/objects/Task/fields/Description.field-meta.xml @@ -0,0 +1,5 @@ + + + Description + false + diff --git a/force-app/main/default/objects/Task/fields/Email.field-meta.xml b/force-app/main/default/objects/Task/fields/Email.field-meta.xml new file mode 100644 index 0000000..92f4b61 --- /dev/null +++ b/force-app/main/default/objects/Task/fields/Email.field-meta.xml @@ -0,0 +1,4 @@ + + + Email + diff --git a/force-app/main/default/objects/Task/fields/IsRecurrence.field-meta.xml b/force-app/main/default/objects/Task/fields/IsRecurrence.field-meta.xml new file mode 100644 index 0000000..f5d08e6 --- /dev/null +++ b/force-app/main/default/objects/Task/fields/IsRecurrence.field-meta.xml @@ -0,0 +1,5 @@ + + + IsRecurrence + false + diff --git a/force-app/main/default/objects/Task/fields/IsReminderSet.field-meta.xml b/force-app/main/default/objects/Task/fields/IsReminderSet.field-meta.xml new file mode 100644 index 0000000..d1a624a --- /dev/null +++ b/force-app/main/default/objects/Task/fields/IsReminderSet.field-meta.xml @@ -0,0 +1,5 @@ + + + IsReminderSet + false + diff --git a/force-app/main/default/objects/Task/fields/OwnerId.field-meta.xml b/force-app/main/default/objects/Task/fields/OwnerId.field-meta.xml new file mode 100644 index 0000000..711ada1 --- /dev/null +++ b/force-app/main/default/objects/Task/fields/OwnerId.field-meta.xml @@ -0,0 +1,6 @@ + + + OwnerId + false + Lookup + diff --git a/force-app/main/default/objects/Task/fields/Phone.field-meta.xml b/force-app/main/default/objects/Task/fields/Phone.field-meta.xml new file mode 100644 index 0000000..2d3dc65 --- /dev/null +++ b/force-app/main/default/objects/Task/fields/Phone.field-meta.xml @@ -0,0 +1,4 @@ + + + Phone + diff --git a/force-app/main/default/objects/Task/fields/Priority.field-meta.xml b/force-app/main/default/objects/Task/fields/Priority.field-meta.xml new file mode 100644 index 0000000..0a58df1 --- /dev/null +++ b/force-app/main/default/objects/Task/fields/Priority.field-meta.xml @@ -0,0 +1,6 @@ + + + Priority + false + Picklist + diff --git a/force-app/main/default/objects/Task/fields/RecurrenceInterval.field-meta.xml b/force-app/main/default/objects/Task/fields/RecurrenceInterval.field-meta.xml new file mode 100644 index 0000000..9c3bf6f --- /dev/null +++ b/force-app/main/default/objects/Task/fields/RecurrenceInterval.field-meta.xml @@ -0,0 +1,5 @@ + + + RecurrenceInterval + false + diff --git a/force-app/main/default/objects/Task/fields/RecurrenceRegeneratedType.field-meta.xml b/force-app/main/default/objects/Task/fields/RecurrenceRegeneratedType.field-meta.xml new file mode 100644 index 0000000..e1d6ec7 --- /dev/null +++ b/force-app/main/default/objects/Task/fields/RecurrenceRegeneratedType.field-meta.xml @@ -0,0 +1,5 @@ + + + RecurrenceRegeneratedType + false + diff --git a/force-app/main/default/objects/Task/fields/Status.field-meta.xml b/force-app/main/default/objects/Task/fields/Status.field-meta.xml new file mode 100644 index 0000000..3c95c97 --- /dev/null +++ b/force-app/main/default/objects/Task/fields/Status.field-meta.xml @@ -0,0 +1,6 @@ + + + Status + false + Picklist + diff --git a/force-app/main/default/objects/Task/fields/Subject.field-meta.xml b/force-app/main/default/objects/Task/fields/Subject.field-meta.xml new file mode 100644 index 0000000..7fc2128 --- /dev/null +++ b/force-app/main/default/objects/Task/fields/Subject.field-meta.xml @@ -0,0 +1,6 @@ + + + Subject + false + Picklist + diff --git a/force-app/main/default/objects/Task/fields/TaskSubtype.field-meta.xml b/force-app/main/default/objects/Task/fields/TaskSubtype.field-meta.xml new file mode 100644 index 0000000..3f28795 --- /dev/null +++ b/force-app/main/default/objects/Task/fields/TaskSubtype.field-meta.xml @@ -0,0 +1,4 @@ + + + TaskSubtype + diff --git a/force-app/main/default/objects/Task/fields/Type.field-meta.xml b/force-app/main/default/objects/Task/fields/Type.field-meta.xml new file mode 100644 index 0000000..260a878 --- /dev/null +++ b/force-app/main/default/objects/Task/fields/Type.field-meta.xml @@ -0,0 +1,6 @@ + + + Type + false + Picklist + diff --git a/force-app/main/default/objects/Task/fields/WhatId.field-meta.xml b/force-app/main/default/objects/Task/fields/WhatId.field-meta.xml new file mode 100644 index 0000000..c5237c5 --- /dev/null +++ b/force-app/main/default/objects/Task/fields/WhatId.field-meta.xml @@ -0,0 +1,6 @@ + + + WhatId + false + Lookup + diff --git a/force-app/main/default/objects/Task/fields/WhoId.field-meta.xml b/force-app/main/default/objects/Task/fields/WhoId.field-meta.xml new file mode 100644 index 0000000..6d445f5 --- /dev/null +++ b/force-app/main/default/objects/Task/fields/WhoId.field-meta.xml @@ -0,0 +1,6 @@ + + + WhoId + false + Lookup + diff --git a/force-app/main/default/objects/Task/listViews/CompletedTasks.listView-meta.xml b/force-app/main/default/objects/Task/listViews/CompletedTasks.listView-meta.xml new file mode 100644 index 0000000..0273413 --- /dev/null +++ b/force-app/main/default/objects/Task/listViews/CompletedTasks.listView-meta.xml @@ -0,0 +1,30 @@ + + + CompletedTasks + SUBJECT + WHO_NAME + WHAT_NAME + DUE_DATE + STATUS + PRIORITY + CORE.USERS.ALIAS + LAST_UPDATE + UPDATEDBY_USER.ALIAS + Mine + + IS_CLOSED + equals + 1 + + + IS_RECURRENCE + equals + 0 + + + LAST_UPDATE + equals + TODAY,LAST_N_DAYS:30 + + + diff --git a/force-app/main/default/objects/Task/listViews/DelegatedTasks.listView-meta.xml b/force-app/main/default/objects/Task/listViews/DelegatedTasks.listView-meta.xml new file mode 100644 index 0000000..8572a44 --- /dev/null +++ b/force-app/main/default/objects/Task/listViews/DelegatedTasks.listView-meta.xml @@ -0,0 +1,15 @@ + + + DelegatedTasks + SUBJECT + WHO_NAME + WHAT_NAME + DUE_DATE + STATUS + PRIORITY + CORE.USERS.ALIAS + LAST_UPDATE + UPDATEDBY_USER.ALIAS + Delegated + + diff --git a/force-app/main/default/objects/Task/listViews/OpenTasks.listView-meta.xml b/force-app/main/default/objects/Task/listViews/OpenTasks.listView-meta.xml new file mode 100644 index 0000000..d2d6198 --- /dev/null +++ b/force-app/main/default/objects/Task/listViews/OpenTasks.listView-meta.xml @@ -0,0 +1,30 @@ + + + OpenTasks + SUBJECT + WHO_NAME + WHAT_NAME + DUE_DATE + STATUS + PRIORITY + CORE.USERS.ALIAS + LAST_UPDATE + UPDATEDBY_USER.ALIAS + Mine + + IS_CLOSED + equals + 0 + + + IS_RECURRENCE + equals + 0 + + + DUE_DATE + greaterOrEqual + LAST_N_DAYS:30 + + + diff --git a/force-app/main/default/objects/Task/listViews/OverdueTasks.listView-meta.xml b/force-app/main/default/objects/Task/listViews/OverdueTasks.listView-meta.xml new file mode 100644 index 0000000..1346f78 --- /dev/null +++ b/force-app/main/default/objects/Task/listViews/OverdueTasks.listView-meta.xml @@ -0,0 +1,30 @@ + + + OverdueTasks + SUBJECT + WHO_NAME + WHAT_NAME + DUE_DATE + STATUS + PRIORITY + CORE.USERS.ALIAS + LAST_UPDATE + UPDATEDBY_USER.ALIAS + Mine + + IS_CLOSED + equals + 0 + + + IS_RECURRENCE + equals + 0 + + + DUE_DATE + lessThan + TODAY + + + diff --git a/force-app/main/default/objects/Task/listViews/RecurringTasks.listView-meta.xml b/force-app/main/default/objects/Task/listViews/RecurringTasks.listView-meta.xml new file mode 100644 index 0000000..0be8738 --- /dev/null +++ b/force-app/main/default/objects/Task/listViews/RecurringTasks.listView-meta.xml @@ -0,0 +1,20 @@ + + + RecurringTasks + SUBJECT + WHO_NAME + WHAT_NAME + DUE_DATE + STATUS + PRIORITY + CORE.USERS.ALIAS + LAST_UPDATE + UPDATEDBY_USER.ALIAS + Mine + + IS_RECURRENCE + equals + 1 + + + diff --git a/force-app/main/default/objects/Task/listViews/TodaysTasks.listView-meta.xml b/force-app/main/default/objects/Task/listViews/TodaysTasks.listView-meta.xml new file mode 100644 index 0000000..9186c69 --- /dev/null +++ b/force-app/main/default/objects/Task/listViews/TodaysTasks.listView-meta.xml @@ -0,0 +1,30 @@ + + + TodaysTasks + SUBJECT + WHO_NAME + WHAT_NAME + DUE_DATE + STATUS + PRIORITY + CORE.USERS.ALIAS + LAST_UPDATE + UPDATEDBY_USER.ALIAS + Mine + + IS_CLOSED + equals + 0 + + + IS_RECURRENCE + equals + 0 + + + DUE_DATE + equals + TODAY,LAST_N_DAYS:30 + + + diff --git a/force-app/main/default/objects/Task/listViews/UnscheduledTasks.listView-meta.xml b/force-app/main/default/objects/Task/listViews/UnscheduledTasks.listView-meta.xml new file mode 100644 index 0000000..6f47977 --- /dev/null +++ b/force-app/main/default/objects/Task/listViews/UnscheduledTasks.listView-meta.xml @@ -0,0 +1,29 @@ + + + UnscheduledTasks + SUBJECT + WHO_NAME + WHAT_NAME + DUE_DATE + STATUS + PRIORITY + CORE.USERS.ALIAS + LAST_UPDATE + UPDATEDBY_USER.ALIAS + Mine + + IS_CLOSED + equals + 0 + + + IS_RECURRENCE + equals + 0 + + + DUE_DATE + equals + + + diff --git a/force-app/main/default/remoteSiteSettings/Jooble_API.remoteSite-meta.xml b/force-app/main/default/remoteSiteSettings/Jooble_API.remoteSite-meta.xml new file mode 100644 index 0000000..3d8b27d --- /dev/null +++ b/force-app/main/default/remoteSiteSettings/Jooble_API.remoteSite-meta.xml @@ -0,0 +1,6 @@ + + + false + true + https://jooble.org + diff --git a/force-app/main/default/reports/unfiled$public/Job_Application_Conversion_Rate_lLC.report-meta.xml b/force-app/main/default/reports/unfiled$public/Job_Application_Conversion_Rate_lLC.report-meta.xml new file mode 100644 index 0000000..7ee2e49 --- /dev/null +++ b/force-app/main/default/reports/unfiled$public/Job_Application_Conversion_Rate_lLC.report-meta.xml @@ -0,0 +1,30 @@ + + + + CUST_NAME + + + Job_Application__c.Application_Date__c + + + Job_Application__c.Job_Application_Status__c + + + Job_Application__c.Job_Position__c + + Tabular + Job Application Conversion Rate + + co + 1 + + CustomEntity$Job_Application__c + user + true + true + true + + Job_Application__c.Application_Date__c + INTERVAL_CUSTOM + + diff --git a/force-app/main/default/reports/unfiled$public/Job_Applications_by_Job_Title_vn4.report-meta.xml b/force-app/main/default/reports/unfiled$public/Job_Applications_by_Job_Title_vn4.report-meta.xml new file mode 100644 index 0000000..2c779f9 --- /dev/null +++ b/force-app/main/default/reports/unfiled$public/Job_Applications_by_Job_Title_vn4.report-meta.xml @@ -0,0 +1,50 @@ + + + + #FFFFFF + #FFFFFF + Diagonal + + y + RowCount + + VerticalColumn + false + true + Job_Application__c.Job_Position__c + CHART_BOTTOM + true + false + false + false + Medium + Auto + #000000 + 12 + #000000 + 18 + + + CUST_NAME + + Summary + + Day + Job_Application__c.Job_Position__c + Asc + + Job Applications by Job Title + + co + 1 + + CustomEntity$Job_Application__c + user + true + true + true + + Job_Application__c.Application_Date__c + INTERVAL_CUSTOM + + diff --git a/force-app/main/default/reports/unfiled$public/Job_Applications_by_Status_WZs.report-meta.xml b/force-app/main/default/reports/unfiled$public/Job_Applications_by_Status_WZs.report-meta.xml new file mode 100644 index 0000000..e9216e9 --- /dev/null +++ b/force-app/main/default/reports/unfiled$public/Job_Applications_by_Status_WZs.report-meta.xml @@ -0,0 +1,52 @@ + + + + #FFFFFF + #FFFFFF + Diagonal + + y + RowCount + + HorizontalBar + false + true + Job_Application__c.Job_Application_Status__c + CHART_BOTTOM + true + false + false + false + Medium + Auto + #000000 + 12 + #000000 + 18 + + + CUST_NAME + + Summary + + Day + Job_Application__c.Job_Application_Status__c + Asc + + Job Applications by Status + + co + 1 + + CustomEntity$Job_Application__c + user + false + true + true + CUST_NAME + Asc + + Job_Application__c.Application_Date__c + INTERVAL_CUSTOM + + diff --git a/force-app/main/default/reports/unfiled$public/New_Job_Application_Report_klH.report-meta.xml b/force-app/main/default/reports/unfiled$public/New_Job_Application_Report_klH.report-meta.xml new file mode 100644 index 0000000..2aead36 --- /dev/null +++ b/force-app/main/default/reports/unfiled$public/New_Job_Application_Report_klH.report-meta.xml @@ -0,0 +1,55 @@ + + + + #FFFFFF + #FFFFFF + Diagonal + + y + RowCount + + HorizontalBarStacked + false + true + Job_Application__c.Application_Date__c + Right + CHART_BOTTOM + Job_Application__c.Follow_up_date__c + true + false + false + false + Medium + Auto + #000000 + 12 + #000000 + 18 + + + CUST_NAME + + Summary + + Job_Application__c.Application_Date__c + Asc + + + Job_Application__c.Follow_up_date__c + Asc + + Interview Events by Week + + co + 1 + + CustomEntity$Job_Application__c + user + false + true + true + + Job_Application__c.Application_Date__c + INTERVAL_THISMONTH + + diff --git a/force-app/main/default/reports/unfiled$public/Open_vs_Closed_Applications_NWF.report-meta.xml b/force-app/main/default/reports/unfiled$public/Open_vs_Closed_Applications_NWF.report-meta.xml new file mode 100644 index 0000000..b87afaa --- /dev/null +++ b/force-app/main/default/reports/unfiled$public/Open_vs_Closed_Applications_NWF.report-meta.xml @@ -0,0 +1,56 @@ + + + + #FFFFFF + #FFFFFF + Diagonal + + y + RowCount + + HorizontalBar + false + true + Job_Application__c.Job_Application_Status__c + CHART_BOTTOM + true + false + false + false + Medium + Auto + #000000 + 12 + #000000 + 18 + + + CUST_NAME + + + Job_Application__c.Application_Date__c + + + Job_Application__c.Job_Position__c + + Summary + + Day + Job_Application__c.Job_Application_Status__c + Asc + + Open vs. Closed Applications + + co + 1 + + CustomEntity$Job_Application__c + user + false + true + true + + Job_Application__c.Application_Date__c + INTERVAL_CUSTOM + + diff --git a/force-app/main/default/tabs/Job_Application__c.tab-meta.xml b/force-app/main/default/tabs/Job_Application__c.tab-meta.xml new file mode 100644 index 0000000..c4dbe12 --- /dev/null +++ b/force-app/main/default/tabs/Job_Application__c.tab-meta.xml @@ -0,0 +1,5 @@ + + + true + Custom27: Laptop + diff --git a/force-app/main/default/tabs/Jooble_Job_Board_Import.tab-meta.xml b/force-app/main/default/tabs/Jooble_Job_Board_Import.tab-meta.xml new file mode 100644 index 0000000..81f2996 --- /dev/null +++ b/force-app/main/default/tabs/Jooble_Job_Board_Import.tab-meta.xml @@ -0,0 +1,6 @@ + + + + jobList + Custom9: Lightning + diff --git a/force-app/main/default/triggers/EventTrigger.trigger b/force-app/main/default/triggers/EventTrigger.trigger new file mode 100644 index 0000000..df94ef0 --- /dev/null +++ b/force-app/main/default/triggers/EventTrigger.trigger @@ -0,0 +1,10 @@ +trigger EventTrigger on Event (before insert, before update) { + List newMeetings = Trigger.new; + + EventHelper.validateNoOverlapAndFetchJobPosition(newMeetings); + + if (Trigger.isInsert || Trigger.isUpdate) { + EventHelper.sendUpcomingEventEmails(newMeetings); + } +} + diff --git a/force-app/main/default/triggers/EventTrigger.trigger-meta.xml b/force-app/main/default/triggers/EventTrigger.trigger-meta.xml new file mode 100644 index 0000000..04cc247 --- /dev/null +++ b/force-app/main/default/triggers/EventTrigger.trigger-meta.xml @@ -0,0 +1,5 @@ + + + 62.0 + Active + diff --git a/force-app/main/default/triggers/JobApplicationTrigger.trigger b/force-app/main/default/triggers/JobApplicationTrigger.trigger new file mode 100644 index 0000000..15c4449 --- /dev/null +++ b/force-app/main/default/triggers/JobApplicationTrigger.trigger @@ -0,0 +1,3 @@ +trigger JobApplicationTrigger on Job_Application__c (before insert, after update, after insert) { + new JobApplicationTriggerHandler().run(); +} \ No newline at end of file diff --git a/force-app/main/default/triggers/JobApplicationTrigger.trigger-meta.xml b/force-app/main/default/triggers/JobApplicationTrigger.trigger-meta.xml new file mode 100644 index 0000000..3726578 --- /dev/null +++ b/force-app/main/default/triggers/JobApplicationTrigger.trigger-meta.xml @@ -0,0 +1,5 @@ + + + 58.0 + Active + diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..f5a9fed --- /dev/null +++ b/jest.config.js @@ -0,0 +1,6 @@ +const { jestConfig } = require('@salesforce/sfdx-lwc-jest/config'); + +module.exports = { + ...jestConfig, + modulePathIgnorePatterns: ['/.localdevserver'] +}; diff --git a/manifest/eventtrigger.xml b/manifest/eventtrigger.xml new file mode 100644 index 0000000..dce25c5 --- /dev/null +++ b/manifest/eventtrigger.xml @@ -0,0 +1,14 @@ + + + + + EventHelper + EventTriggerHelperTest + ApexClass + + + EventTrigger + ApexTrigger + + 60.0 + \ No newline at end of file diff --git a/manifest/package.xml b/manifest/package.xml new file mode 100644 index 0000000..c4f3124 --- /dev/null +++ b/manifest/package.xml @@ -0,0 +1,300 @@ + + + + EventHelper + EventTriggerHelperTest + ApexClass + + + EventTrigger + ApexTrigger + + + API_Config__c.Jooble_API__c + Activity.Application_Status_Snapshot__c + Job_Application__c.Company_Name_Text__c + Job_Application__c.Company_Name__c + Job_Application__c.Date_Applied__c + Job_Application__c.Date_Posted__c + Job_Application__c.Description__c + Job_Application__c.Follow_up_date__c + Job_Application__c.Job_Application_Status__c + Job_Application__c.Job_Position__c + Job_Application__c.Job_Source__c + Job_Application__c.JoobleExternalId__c + Job_Application__c.Location__c + Job_Application__c.Notes__c + Job_Application__c.Primary_Contact__c + Job_Application__c.Rating__c + Job_Application__c.Salary__c + Job_Application__c.URL__c + Job_Application__c.Work_Location__c + CustomField + + + API_Config__c + Account + AccountCleanInfo + AccountContactRole + Activity + Address + AlternativePaymentMethod + ApiAnomalyEventStore + AppointmentCategory + AppointmentInvitation + AppointmentInvitee + AppointmentScheduleLog + AppointmentTopicTimeSlot + Asset + AssetAction + AssetActionSource + AssetRelationship + AssetStatePeriod + AssignedResource + AssistantProgress + AssociatedLocation + AuthorizationForm + AuthorizationFormConsent + AuthorizationFormDataUse + AuthorizationFormText + BusinessBrand + BuyerGroup + Campaign + CampaignMember + CardPaymentMethod + CartCheckoutSession + CartDeliveryGroup + CartDeliveryGroupMethod + CartItem + CartItemPriceAdjustment + CartRelatedItem + CartTax + CartValidationOutput + Case + CaseContactRole + CaseMilestone + CaseRelatedIssue + ChangeRequest + ChangeRequestRelatedIssue + ChangeRequestRelatedItem + ChatterActivity + CollaborationGroup + CollaborationGroupMember + CommSubscription + CommSubscriptionChannelType + CommSubscriptionConsent + CommSubscriptionTiming + ConsumptionRate + ConsumptionSchedule + Contact + ContactCleanInfo + ContactPointAddress + ContactPointConsent + ContactPointEmail + ContactPointPhone + ContactPointTypeConsent + ContactRequest + ContentVersion + Contract + ContractContactRole + ContractLineItem + Coupon + CouponCodeRedemption + CredentialStuffingEventStore + CreditMemo + CreditMemoInvApplication + CreditMemoLine + Customer + DandBCompany + DataKitDeploymentLog + DataUseLegalBasis + DataUsePurpose + DigitalWallet + DuplicateRecordItem + DuplicateRecordSet + EmailMessage + EngagementChannelType + EngagementChannelWorkType + Entitlement + EntitlementContact + EntityMilestone + Event + ExchangeUserMapping + ExpressionFilter + ExpressionFilterCriteria + ExternalEventMapping + FeedItem + FinanceBalanceSnapshot + FinanceTransaction + FlowOrchestrationInstance + FlowOrchestrationStageInstance + FlowOrchestrationStepInstance + FlowOrchestrationWorkItem + FulfillmentOrder + FulfillmentOrderItemAdjustment + FulfillmentOrderItemTax + FulfillmentOrderLineItem + GuestUserAnomalyEventStore + Idea + Image + Incident + IncidentRelatedItem + Individual + InventoryItemReservation + InventoryReservation + Invoice + InvoiceLine + Job_Application__c + Lead + LeadCleanInfo + LearningAssignment + LearningAssignmentProgress + LearningItem + LearningLinkProgress + LegalEntity + Location + LocationGroup + LocationGroupAssignment + MLModel + MLModelFactor + MLModelFactorComponent + Macro + MacroAction + MacroInstruction + MacroUsage + MessagingEndUser + MessagingSession + OperatingHours + OperatingHoursHoliday + Opportunity + OpportunityCompetitor + OpportunityContactRole + OpportunityLineItem + OpportunityRelatedDeleteLog + Order + OrderItem + OrgDeleteRequest + OrgMetricScanResult + OrgMetricScanSummary + PartnerRole + PartyConsent + Payment + PaymentAuthAdjustment + PaymentAuthorization + PaymentGateway + PaymentGroup + PaymentLineInvoice + PaymentMethod + Pricebook2 + PricebookEntry + PrivacyJobSession + PrivacyPolicy + PrivacyRTBFRequest + Problem + ProblemIncident + ProblemRelatedItem + ProcessException + Product2 + ProductAttribute + ProductAttributeSetProduct + ProductCatalog + ProductCategory + ProductCategoryProduct + ProductConsumptionSchedule + Promotion + PromotionLineItemRule + PromotionMarketSegment + PromotionQualifier + PromotionSegment + PromotionSegmentBuyerGroup + PromotionSegmentSalesStore + PromotionTarget + PromotionTier + PromptAction + PromptError + QuickText + QuickTextUsage + Recommendation + RecordAction + RecordMergeHistory + Refund + RefundLinePayment + ReportAnomalyEventStore + ResourceAbsence + ResourcePreference + ReturnOrder + ReturnOrderItemAdjustment + ReturnOrderItemTax + ReturnOrderLineItem + SalesStore + Scorecard + ScorecardAssociation + ScorecardMetric + Seller + ServiceAppointment + ServiceAppointmentAttendee + ServiceContract + ServiceResource + ServiceResourceSkill + ServiceTerritory + ServiceTerritoryMember + ServiceTerritoryWorkType + SessionHijackingEventStore + Shift + ShiftEngagementChannel + ShiftWorkTopic + Shipment + ShipmentItem + ShippingCarrier + ShippingCarrierMethod + ShippingConfigurationSet + ShippingRateArea + ShippingRateGroup + Site + SkillRequirement + SocialPersona + Solution + StandardShippingRate + StreamActivityAccess + StreamingChannel + TableauHostMapping + Task + TimeSlot + Topic + TopicAssignment + User + UserExternalCredential + UserPrioritizedRecord + UserProvisioningRequest + VoiceCall + Waitlist + WaitlistParticipant + WaitlistServiceResource + WaitlistWorkType + WebCart + WebCartAdjustmentBasis + WebCartAdjustmentGroup + WebStore + WebStoreBuyerGroup + WebStoreCatalog + WebStoreInventorySource + WorkOrder + WorkOrderLineItem + WorkPlan + WorkPlanTemplate + WorkPlanTemplateEntry + WorkStep + WorkStepTemplate + WorkType + WorkTypeGroup + WorkTypeGroupMember + CustomObject + + + Event-Event Layout + Job_Application__c-Job Application + Job_Application__c-Job Application Layout + Layout + + 60.0 + \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..e36ff2b --- /dev/null +++ b/package.json @@ -0,0 +1,41 @@ +{ + "name": "salesforce-app", + "private": true, + "version": "1.0.0", + "description": "Salesforce App", + "scripts": { + "lint": "eslint **/{aura,lwc}/**/*.js", + "test": "npm run test:unit", + "test:unit": "sfdx-lwc-jest", + "test:unit:watch": "sfdx-lwc-jest --watch", + "test:unit:debug": "sfdx-lwc-jest --debug", + "test:unit:coverage": "sfdx-lwc-jest --coverage", + "prettier": "prettier --write \"**/*.{cls,cmp,component,css,html,js,json,md,page,trigger,xml,yaml,yml}\"", + "prettier:verify": "prettier --check \"**/*.{cls,cmp,component,css,html,js,json,md,page,trigger,xml,yaml,yml}\"", + "postinstall": "husky install", + "precommit": "lint-staged" + }, + "devDependencies": { + "@lwc/eslint-plugin-lwc": "^1.1.2", + "@prettier/plugin-xml": "^3.2.2", + "@salesforce/eslint-config-lwc": "^3.2.3", + "@salesforce/eslint-plugin-aura": "^2.0.0", + "@salesforce/eslint-plugin-lightning": "^1.0.0", + "@salesforce/sfdx-lwc-jest": "^5.1.0", + "eslint": "^9.9.1", + "eslint-plugin-import": "^2.25.4", + "eslint-plugin-jest": "^28.8.1", + "husky": "^9.1.5", + "lint-staged": "^15.1.0", + "prettier": "^3.1.0", + "prettier-plugin-apex": "^2.0.1" + }, + "lint-staged": { + "**/*.{cls,cmp,component,css,html,js,json,md,page,trigger,xml,yaml,yml}": [ + "prettier --write" + ], + "**/{aura,lwc}/**/*.js": [ + "eslint" + ] + } +} diff --git a/scripts/apex/hello.apex b/scripts/apex/hello.apex new file mode 100644 index 0000000..0189578 --- /dev/null +++ b/scripts/apex/hello.apex @@ -0,0 +1,16 @@ +String keyword = 'Salesforce Developer'; +String location = 'San Francisco'; + +// Call the searchJobs method +List jobResults = JoobleJobSearchController.searchJobs(keyword, location); + +// Output results in debug logs +for (JoobleJobSearchController.JobBoardResult jobResult : jobResults) { + System.debug('Job Title: ' + jobResult.title); + System.debug('Company: ' + jobResult.company); + System.debug('Location: ' + jobResult.location); + System.debug('Snippet: ' + jobResult.snippet); + System.debug('Salary: ' + jobResult.salary); + System.debug('Link: ' + jobResult.link); + System.debug('Date Created: ' + jobResult.datecreatedfrom); +} diff --git a/scripts/soql/account.soql b/scripts/soql/account.soql new file mode 100644 index 0000000..10d4b9c --- /dev/null +++ b/scripts/soql/account.soql @@ -0,0 +1,6 @@ +// Use .soql files to store SOQL queries. +// You can execute queries in VS Code by selecting the +// query text and running the command: +// SFDX: Execute SOQL Query with Currently Selected Text + +SELECT Id, Name FROM Account diff --git a/sfdx-project.json b/sfdx-project.json new file mode 100644 index 0000000..9cacf28 --- /dev/null +++ b/sfdx-project.json @@ -0,0 +1,12 @@ +{ + "packageDirectories": [ + { + "path": "force-app", + "default": true + } + ], + "name": "job-application-tracker-oldv2-apex-innovators", + "namespace": "", + "sfdcLoginUrl": "https://login.salesforce.com", + "sourceApiVersion": "58.0" +}