Skip to content

Commit

Permalink
Job form fields (#440)
Browse files Browse the repository at this point in the history
* ui-fixes

* reset gmaps input

* job-form-width-fix

* job-form-fixes

* loading-spinner-skills-dropwdown

* extract out skills combobox

* cron-jobs-fix

* default values for email host and port + fix cron job schedule time to 45 mins
  • Loading branch information
aakash2330 authored Oct 4, 2024
1 parent d9e8b62 commit 0156bc3
Show file tree
Hide file tree
Showing 13 changed files with 743 additions and 18 deletions.
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,9 @@ EMAIL_PORT=587
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=

# go to https://lightcast.io/open-skills and signup to recieve your credentials
LIGHTCAST_CLIENT_ID=
LIGHTCAST_CLIENT_SECRET=

# To run the application in production environment / check the envs
# SKIP_ENV_CHECK=true npm run [replace with your script name]
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"@radix-ui/react-accordion": "^1.2.0",
"@radix-ui/react-avatar": "^1.1.0",
"@radix-ui/react-checkbox": "^1.1.1",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.1",
Expand All @@ -56,6 +56,7 @@
"bcryptjs": "^2.4.3",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"cmdk": "^1.0.0",
"dayjs": "^1.11.13",
"framer-motion": "^11.9.0",
"jiti": "^1.21.6",
Expand All @@ -66,6 +67,7 @@
"next-auth": "^4.24.7",
"next-themes": "^0.3.0",
"nextjs-toploader": "^1.6.12",
"node-cron": "^3.0.3",
"nodemailer": "^6.9.15",
"react": "^18",
"react-dom": "^18",
Expand Down
8 changes: 8 additions & 0 deletions src/actions/job.action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const createJob = withServerActionAsyncCatcher<
const result = JobPostSchema.parse(data);
const {
companyName,
skills,
companyBio,
companyEmail,
type,
Expand All @@ -51,14 +52,21 @@ export const createJob = withServerActionAsyncCatcher<
workMode,
description,
hasSalaryRange,
hasExperiencerange,
maxSalary,
minExperience,
maxExperience,
minSalary,
} = result;
await prisma.job.create({
data: {
userId: auth.user.id,
title,
description,
hasExperiencerange,
minExperience,
maxExperience,
skills,
companyName,
companyBio,
companyEmail,
Expand Down
51 changes: 51 additions & 0 deletions src/actions/skills.cron.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
'use server';
/* eslint-disable no-console */
import { NextResponse } from 'next/server';
import { bearerToken } from '@/config/skillapi.auth.token';

var cron = require('node-cron');

const url = 'https://auth.emsicloud.com/connect/token';

async function startAuthTokenCronJob() {
// don't run the cron job if its already running
await fetchAuthTokenCronJob();
// Schedule the cron to run every 45 minutes , the token resets after one hour
cron.schedule('*/45 * * * *', async () => {
await fetchAuthTokenCronJob();
});
return NextResponse.json({ data: 'Success', status: 200 });
}

const options = {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
client_id: process.env.LIGHTCAST_CLIENT_ID ?? '',
client_secret: process.env.LIGHTCAST_CLIENT_SECRET ?? '',
grant_type: 'client_credentials',
scope: 'emsi_open',
}),
};

async function fetchAuthTokenCronJob() {
console.log('Fetching updated Lightcast token at ' + new Date());
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
bearerToken.value = data.access_token;
} catch (error) {
console.error('Error:', error);
}
}

export async function getBearerToken() {
return bearerToken.value;
}

startAuthTokenCronJob();
109 changes: 109 additions & 0 deletions src/components/comboBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import * as React from 'react';
import { Check, ChevronsUpDown } from 'lucide-react';

import { cn } from '@/lib/utils';
import { Button } from '@/components/ui/button';
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from '@/components/ui/command';
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/components/ui/popover';
import { LoadingSpinner } from './loading-spinner';

export type TcomboBoxValue = { value: string; label: string };

export function Combobox({
dropdownValues,
setComboBoxInputValue,
isLoading,
setComboBoxSelectedValues,
comboBoxSelectedValues,
}: {
comboBoxSelectedValues: string[];
isLoading: boolean;
setComboBoxInputValue: React.Dispatch<React.SetStateAction<string>>;
dropdownValues: TcomboBoxValue[];
setComboBoxSelectedValues: React.Dispatch<React.SetStateAction<string[]>>;
}) {
const [open, setOpen] = React.useState(false);
const [value, setValue] = React.useState('');

return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={open}
className="w-full justify-between bg-gray-800 border-none text-white"
>
Search skillset ...
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="min-w-full p-0">
<Command>
<CommandInput
className="w-full"
onValueChange={(value) => {
setComboBoxInputValue(value);
}}
placeholder="Search skillset ..."
/>
<CommandList>
{isLoading ? (
<CommandEmpty>
<LoadingSpinner></LoadingSpinner>
</CommandEmpty>
) : (
<>
{!dropdownValues.length && (
<CommandEmpty>No framework found.</CommandEmpty>
)}

<CommandGroup>
{dropdownValues.map((item) => (
<CommandItem
key={item.value}
value={item.value}
onSelect={(currentValue) => {
setValue(currentValue === value ? '' : currentValue);
setOpen(false);
setComboBoxSelectedValues((prev) => {
const foundSelectedValueIndex = prev.findIndex(
(val) => val === item.value
);
if (foundSelectedValueIndex < 0) {
return [...prev, currentValue];
} else return prev;
});
}}
>
<Check
className={cn(
'mr-2 h-4 w-4',
comboBoxSelectedValues.includes(item.value)
? 'opacity-100'
: 'opacity-0'
)}
/>
{item.label}
</CommandItem>
))}
</CommandGroup>
</>
)}
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
}
Loading

0 comments on commit 0156bc3

Please sign in to comment.