Job board functions are sensible for employers to seek out potential candidates for roles inside their group, and for the freelancer or job seeker they could be a nice supply for locating work.
On this tutorial we will likely be constructing a Job Board Utility, which is an Upwork, Certainly or Fiverr clone, and within the course of we’ll discover ways to combine the Webiny CMS right into a Vue.js software utilizing GraphQL.
This job board software will can help you create and handle job vacancies utilizing Webiny CMS on the backend. We will likely be making our personal stylesheet versus utilizing some other CSS expertise, as we’ll primarily be focusing of the code slightly than the styling.
Step 1: Create a Webiny Mission
As beforehand talked about with a view to begin utilizing the Webiny CMS you could be create a brand new app in your native machine, then deploy it to AWS. The documentation discovered on the Webiny web site is properly detailed and can information you thru the set up course of in case you have not already accomplished so.
After you have all of the stipulations for Webiny CMS we will begin to create your new undertaking. Open your terminal and navigate to the folder you wish to retailer your undertaking. Subsequent utilizing the command beneath create your undertaking.
npx create-webiny-project my-webiny-project
As soon as the deployment is finished, you’ll be introduced with a URL the place you may entry your Admin Dashboard as proven beneath:
Step 2: Create Content material Fashions
Now that you’ve got the Webiny CMS put in we’ll begin to create our Content material Fashions that may maintain the information we want to see in our Vue.js Utility.
Utilizing the menu icon navigate to Headless CMS > Fashions, the place we’ll create a brand new Content material Mannequin known as Job. That is at it’s core a knowledge container that may maintain the template for the parameters of what qualifies as a job.
Choose Fashions from the Content material Fashions navigation merchandise as proven beneath:
Create your Mannequin as proven within the picture beneath:
Fill within the description that can assist you bear in mind what the job mannequin will comprise.
Subsequent, you may drag and drop completely different area varieties into the Edit space to create your structured content material:
We’ll first drag and drop a TEXT
area from the left to create our first content material.
We’ll give it a label Job Function and save the sphere. Discover that the Subject IDs will likely be used for accessing through GraphQL later.
Create these fields – you may rename them in the event you like. Right here is an instance of the all of the fields:
- Job Function –
Textual content
- Job Contact –
Textual content
(Sample – E-mail) - Job Location –
Textual content
- Job Reference URL –
Textual content
(Sample-URL) - Job Begin Date –
Date/Time
(Date Solely)
For sure fields I’ve additionally added setting within the validators part. This can guarantee the right values are entered into the backend. We will even examine the validation within the frontend too.
NOTE: In case you have any errors when pushing content material to the backend it could possibly be that you’ve got outlined validations that aren’t being adhered to on the front-end.
After you have saved your mannequin, return to Headless CMS and you will note the brand new mannequin underneath Ungrouped.
We’ll create a New Entry and can fill out all of the fields with dummy knowledge as we can take away it from the CMS later. As soon as you’re completely satisfied together with your entry, save and publish it.
Step 3: Creating the Vue.js App
In a separate folder in your native machine, we’ll create the Vue undertaking utilizing Vite.js. Vite is tremendous quick and top-of-the-line instruments for prototyping an software. You should use frameworks like React and Svelte with Vite as properly, nonetheless on this article we will likely be specializing in Vue.js.
npm create vite@newest [PUT YOUR OWN PROJECT NAME HERE]
After you have chosen your framework and the installer has accomplished, go into the listing and set up the dependencies:
cd [project name]
npm set up
Open the undertaking in your code editor. We’ll first take away all of the boilerplate code within the App.vue
file, then change with this code.
<script setup>
import { reactive } from "@vue/reactivity";
/* Array to comprise our job date */
const jobs = reactive([]);
</script>
<template>
{ /* container for the whole web page /* }
<div class="container">
{ /* a div to format all the roles in our database /* }
<div class="jobs">
<h1>JOBS</h1>
<div class="job" v-for="job in jobs" :key="job.jobUrl">
<div class="header">
<h2>{{ job.title }}</h2>
<span class="material-icons"> delete </span>
</div>
<h4>{{ job.jobUrl }}</h4>
<p>
{{ job.desc }}
</p>
<h3>{{ job.location }}</h3>
<h3>{{ job.contact }}</h3>
<h3>{{ job.expires }}</h3>
<span>posted on {{ job.createdOn}}</span>
</div>
</div>
{ /* a div to format our type sidebar /* }
<div class="facet">
</div>
</div>
</template>
{ /* A lot of the kinds are in kinds.css. Listed here are some only for this web page. /* }
<type scoped>
/* These two courses are to format the title and the delete button on a job */
.header {
show: flex;
justify-content: space-between;
}
.header span {
padding: 15px;
font-size: 2em;
cursor: pointer;
}
</type>
Styling the Utility
Within the type.css
file in your supply folder, add this CSS on the finish.
.container {
width:100%;
show: grid;
grid-template-columns: 66% 33%;
hole: 10px;
padding: 0px;
colour:#000;
box-sizing: border-box;
text-align: left;
}
.jobs{
padding: 5px;
}
.facet{
min-height: 100vh;
padding-left: 10px;
background-color:#FFE8A3;
}
.job,.jobForm {
show: inline-block;
margin: 5px;
}
textarea{
background-color: #fff;
border-radius: 10px;
border: none;
margin: 5px;
show: block;
colour: #000;
width: 100%;
}
enter {
background-color: #fff;
border-radius: 10px;
border: none;
margin: 5px;
colour: #000;
show: block;
width:300px;
padding: 20px;
peak: 1em;
}
button{
width: 100%;
background-color: orange;
peak: 4rem;
}
.job {
width: 40%;
border-radius: 20px;
colour: #000;
padding-left:10px;
background-color: #fff;
}
/**When the applying is seen on a cellular **/
@media solely display and (max-width: 640px) {
.container {
show: inline-block;
}
.job{
width: 90%;
}
}
Including Google Icons
I’ve additionally added the stylesheet to allow us to have Materials Icons by Google in our software. Within the index.html file add the hyperlink to the Materials Icons stylesheet as proven beneath:
<head>
<meta charset="UTF-8" />
<hyperlink href="https://fonts.googleapis.com/icon?household=Materials+Icons" rel="stylesheet">
<hyperlink rel="icon" kind="picture/svg+xml" href="/vite.svg" />
<meta title="viewport" content material="width=device-width, initial-scale=1.0" />
<title>Job Board Utility</title>
</head>
That is the naked construction of our software, presently we shouldn’t have any knowledge to visualise our software. Let’s repair that however integrating Webiny CMS in order that we will view the entries we created in our Webiny CMS undertaking.
Step 4: Integrating Our Headless CMS
Earlier than we will entry the Headless CMS from Vue we’d like a token and the URL for the API. Right here’s how you can get the URL:
https://www.webiny.com/docs/headless-cms/fundamentals/graphql-api
Alternatively you may open your Webiny undertaking and run this command
# Returns info for the "dev" surroundings.
yarn webiny data --env dev
Subsequent we’ll create a brand new API Key and provides the Headless CMS all of the permissions as we’d like to have the ability to push content material to the backend and never simply learn it.
Choose API Keys from the Settings menu:
The Token will likely be displayed right here when you save your permissions:
NOTE: Ensure you save your API token in a protected place
We have to add entry to our Headless CMS in order that we will entry it utilizing GraphQL, change the entry stage to the settings beneath:
Create an .env
file within the root of your Vue undertaking and add your knowledge beneath:
VITE_WEBINY_API=YOUR WEBINY CMS URL HERE
VITE_TOKEN=YOUR WEBINY CMS TOKEN API HERE
IMPORTANT NOTE: This .env
file with the uncovered API Key and token is okay when testing regionally in your machine. If you find yourself internet hosting your software someplace it may be accessed on the net, it might be finest to make use of a server-side perform for knowledge fetching as this instance code could be unsafe to make use of.
Including GraphQL Utilizing URQL
We will likely be utilizing URQL to assist us hyperlink the Webiny CMS to our Vue software. Let’s add URQL to our Vue JS software.
The total documentation for URQL may be discovered right here: https://formidable.com/open-source/urql/docs/fundamentals/vue/
In your undertaking folder, open the terminal and run this command:
npm set up --save @urql/vue graphql
Replace your foremost.js
file
import { createApp } from 'vue'
import './type.css'
import App from './App.vue'
import urql from '@urql/vue';
const app = createApp(App);
app.use(urql, {
url: import.meta.env.VITE_WEBINY_API,
fetchOptions: () => {
const token = import.meta.env.VITE_TOKEN;
return {
headers: { authorization: token ? `Bearer ${token}` : '' },
};
},
})
app.mount('#app')
GraphQL – View All Jobs
Now we have now come to the part the place we’ll begin utilizing GraphQL. The perfect practices is to check queries on the API itself earlier than placing them into our software.
Webiny gives a superb API Playground the place can check the backend.
Open your browser navigate to your Webiny Admin Dashboard and open the API Playground
Navigate to the Webiny Handle API and enter the values on the left beneath. Run the code to see your outcome.
Right here is the code for the above question:
question {
listJobs {
knowledge {
jobRole
jobContact
jobDescription
jobLocation
jobUrl
createdOn
}
}
Now that we all know the question returns the information we require, let’s add it to our Vue.js software.
As we will likely be utilizing Dates in our software we’ll set up second.js for date styling. Open the terminal and run the command beneath:
npm set up second --save
Open your App.vue
file and enter the code beneath which incorporates our second.js import and our GraphQL question utilizing useQuery
from URQL:
import { reactive } from "@vue/reactivity";
import second from "second";
import { useQuery } from "@urql/vue";
/* Array to comprise our jobs */
const jobs = reactive([]);
const currentDate = second(Date.now()).format("Do MMMM Y");
/* * This perform will entry the Webiny CMS utilizing GraphQL to return all the roles posted.
*/
const outcome = useQuery({
question: `
{
listJobs {
knowledge {
id
jobRole
jobContact
jobDescription
jobLocation
jobUrl
startDate
createdOn
}
}
}
`,
}).then((outcome) => {
// get the listing of outcome from the CMS
let jobResult = outcome.knowledge.worth.listJobs.knowledge;
// if any knowledge is returned push every outcome into the roles array
jobResult.forEach((factor) => {
jobs.push(factor);
});
});
</script>
<template>
{ /* container for the whole web page /* }
<div class="container">
{ /* a div to format all the roles in our database /* }
<div class="jobs">
<date class="currentDate">{{ currentDate }}</date>
<h1>JOBS</h1>
{ /* for every job in our database show /* }
<div class="job" v-for="job in jobs" :key="job.id">
<div class="header">
<h2>{{ job.jobRole }}</h2>
</div>
<h4>{{ job.jobUrl }}</h4>
<p>
{{ job.jobDescription }}
</p>
<h3><span class="material-icons"> map </span>{{ job.jobLocation }}</h3>
<h3><span class="material-icons"> mail </span>{{ job.jobContact }}</h3>
<h3>Expires: {{ second(job.startDate).format("Do MMMM Y") }}</h3>
<span>posted on {{ second(job.createdOn).format("Do MMMM Y") }}</span>
</div>
</div>
{ /* a div to format our type sidebar /* }
<div class="facet">
</div>
</div>
</template>
As soon as you’ve got added the code, open the terminal and run
npm run dev
In case you have set every little thing us appropriately, you must now be capable of see an inventory of Job entries that you simply created utilizing the Headless CMS within the Admin Dashboard.
Step 5: Managing Jobs
Now that we will see all the roles on our backend, let’s take a look at deleting them. In GraphQL, every time we need to manipulate current knowledge we have to use a mutation.
Within the template part add the brand new button to the div with a category of header. We will even add an onclick
occasion for the perform we’ll write for eradicating a single job by taking in it’s ID
as a parameter.
<div class="header">
<span>posted on {{ second(job.createdOn).format("Do MMMM Y") }}</span>
<button class="icon" @click on="removeJob(job.id)">
<span class="material-icons"> delete </span>
</button>
</div>
Delete a Job
As with the earlier question we check our mutation first on the backend. Record all the roles and this time we’ll return the ID together with some other knowledge. Copy one of many entries ID’s from one of many outcomes.
Subsequent we’ll write our GraphQL mutation including the ID we copied earlier then we’ll execute the code to see the outcome.
mutation($ID: ID!){
deleteJob(revision:$ID) {
knowledge
}
}
You must be capable of see the outcomes just like the screenshot beneath:
The check on the Webiny API Playground was successfull so now we will write our mutation in our Vue.js app. Within the script part of the App.vue
file enter the code beneath:
import { useQuery, useMutation } from "@urql/vue"; //
/** DELETING A JOB * */
const deleteJobResult = useMutation(`
mutation($ID: ID!){
deleteJob(revision:$ID)
{
knowledge
}
}
`);
/* Operate we'll name from the span onclick occasion */
/* Has a parameter of ID to get the related job within the backend */
const removeJob = (id) => {
const variables = {
ID: id,
};
deleteJobResult.executeMutation(variables).then((outcome) => {
if (outcome.error) {
console.error("There was an error:", outcome.error);
} else {
location.reload(); // refresh the web page
}
});
};
Congratulations! Now we have efficiently eliminated a job, utilizing it’s id, from Webiny CMS utilizing GraphQL mutation.
Including New Jobs
Now we have seen how we’re in a position to view all of the content material on the backend and even handle a particular factor in our CMS. Nonetheless, it might be good to not must all the time create our knowledge on the backend however as an alternative create a type that permits us to push knowledge into our CMS from our Vue software.
Let’s do that by making a brand new element known as Create.vue
within the elements
folder. In that file, enter the code beneath:
<script setup>
import { reactive } from "@vue/reactivity";
const newJob = reactive({
title: "",
desc: "",
jobUrl: "",
location: "",
contact: "",
expires: ""
})
/* Reset all of the dynamic fields after submitting the shape */
perform resetFields(newJob) {
newJob.title = "";
newJob.jobUrl = "";
newJob.desc = "";
newJob.location = "";
newJob.contact = "";
newJob.expires = "";
}
</script>
<template>
<div>
<h1>CREATE JOB</h1>
{ /* Type for making a job. This can push entered values into the CMS /* }
<type class="jobForm" @submit.stop="checkFields(newJob)">
<label for="title">Title</label>
<enter kind="textual content" title="title" v-model="newJob.title" />
<label for="desc">Job Description</label>
<textarea title="desc" class="jobDesc" rows="5" v-model="newJob.desc"> </textarea>
<label for="URL">Job Reference URL</label>
<enter kind="url" title="URL" placeholder="https://" sample="https://.*" v-model="newJob.jobUrl" />
<label for="contact">Job Contact E-mail</label>
<enter kind="e mail" title="contact" v-model="newJob.contact" />
<label for="location">Job Location</label>
<enter kind="textual content" title="location" v-model="newJob.location" />
<label for="expiry">Job Begin Date</label>
<enter kind="date" v-model="newJob.expires" title="expiry" />
<button kind="submit" worth="Submit">ADD JOB</button>
</type>
</div>
</template>
NOTE: In the event you study the HTML within the template part you’ll discover that the EMAIL and URL inputs have patterns hooked up to them which corresponds to the validator fields on the Webiny CMS.
Including Jobs to Webiny CMS
Earlier than we create the mutation we have to add a brand new job we’ll as soon as once more do a check on Webiny CMS.
Let’s check the backend by inserting some dummy knowledge and seeing what’s returned. Open API Playground in your Webiny CMS and underneath the Handle API enter some knowledge just like the one beneath:
mutation {
createJob(
knowledge: {
jobRole:"Internet Developer"
jobContact:"joe@joebloggs.com"
jobDescription:"Develop web sites with Webiny CMS"
jobLocation:"Distant"
jobUrl:"http://www.google.com"
startDate:"2022-09-01"
}
)
{
knowledge {
jobRole
jobDescription
jobContact
jobLocation
jobUrl
startDate
createdOn
}
}
}
When you enter that code within the API Playground, you must see the outcome as beneath:
Now that we now have efficiently created a brand new entry on our CMS we’ll use this code to create our GraphQL mutation.
Let’s convert this utilizing the useMutation
perform from URQL in our Vue.js software. We will add the code beneath to the <script>
part of our Create.vue
file.
import { reactive} from "@vue/reactivity"; // import ref from vue
import { useMutation } from "@urql/vue";
/* Create area for job type - corresponds to the fields we now have in our Webiny CMS */
const newJob = reactive({
title: "",
desc: "",
jobUrl: "",
location: "",
contact: "",
expires: ""
})
/**We'll use variables to gather from the shape fields*/
const createJobResult = useMutation(`
mutation(
$title:String!,
$contact:String!,
$desc:String!,
$location:String!,
$url:String!,$expiry:Date!) {
createJob(
knowledge:{
jobRole:$title
jobContact:$contact
jobDescription:$desc
jobLocation:$location
jobUrl:$url
startDate:$expiry
})
{
knowledge {
jobRole
jobDescription
jobContact
jobLocation
jobUrl
startDate
}
}
}
`);
/* This perform will take all of the enter from the shape and push it into an object.
/* Then it'll push the thing to the roles array and reset all of the enter fields.
const addJob = () => {
const variables = {
title: newJob.title,
contact: newJob.contact,
desc: newJob.desc,
location: newJob.location,
url: newJob.jobUrl,
expiry: new Date(newJob.expires).toISOString().break up("T")[0], // WEBINY CMS accepts a selected format for sending Dates
}
};
createJobResult.executeMutation(variables).then((outcome) => {
if (outcome.error) {
alert(outcome.error.title);
} else {
window.location.reload();
}
});
};
Now that we now have setup our Create.vue
let’s import it into App.vue
and add it to the sidebar within the template part.
import Create from "./elements/Create.vue";
{ /*} a div to format our type sidebar /* }
<div class="facet">
<Create></Create>
</div>
This could outcome within the type being rendered within the sidebar like so:
You must now see the element on the facet of the display when your run the next command within the terminal:
npm run dev
Fill out all of the fields to confirm that we now have related the applying efficiently. The web page will reload and the brand new entry will seem on the left with all the knowledge you entered.
Conclusion
On this article we now have learnt how you can use the Webiny Headless CMS, how you can arrange up Content material Fashions, how you can create Tokens, Handle Permissions and how you can check GraphQL question and mutations utilizing the API Playground.
Now we have additionally learnt how you can use GraphQL with URQL together with Vue.js to attach our Webiny CMS to our working Job Board software.
Thanks for studying!
Full supply code: https://github.com/webiny/write-with-webiny/tree/foremost/tutorials/job-board-vue-graphql
This text was written by a contributor to the Write with Webiny program. Would you want to jot down a technical article like this and receives a commission to take action? Try the Write with Webiny GitHub repo.