Code Monkey home page Code Monkey logo

hire-win's Introduction

Documentation

Overview

To get started, you’ll need the following:

  • Postgres database
  • Blitzjs
  • Stripe CLI
  • Stripe API keys
  • Object storage with S3 styled credentials (minio for development)

Create an .env.local file with all your key credentials.

POSTMARK_TOKEN=
SESSION_SECRET_KEY= [generate using openssl rand -hex 16]
S3_BUCKET=hire-win
S3_ENDPOINT=http://localhost:9000
S3_SECRET_KEY=minioadmin
STRIPE_SECRET=
NEXT_PUBLIC_STRIPE_PUBLIC=
PRO_PLAN=
STRIPE_WEBHOOK=
NEXT_PUBLIC_APP_URL=http://localhost:3000
S3_ACCESS_KEY=minioadmin

Before starting the server, ensure your database is connected and up to date by running the command:

blitz prisma migrate dev

Then to start the server run the following commands in separate terminal windows:

yarn run stripe:dev

yarn dev

hire-win should now be accessible from http://localhost:3000

Roles & Permissions

There are 2 types of roles. A global role for a user and local role set on the membership associated with the workspace.

The global role by default can either be “USER” or “ADMIN”, giving you the flexibility to set global permissions based on the user’s role.

A local role is set when creating a membership on a workspace. When a user creates a workspace, the createWorkspace mutation will create a membership with the “OWNER” role.

Permissions are set in a central file with Blitz-Guard. Permissions for the updateWorkspace mutation and getWorkspace query are included.

The updateWorkspace mutations guard checks to see if the current user membership with the workspace has the “OWNER” or "ADMIN" role.

Only the owner of a workspace has access to the billing page of a workspace. You can set which member is an admin in your workspace settings page. There is included guards to check if a member is an admin or owner. For example:

const { can: isOwner } = await Guard.can(
  "isOwner",
  "workspace",
  { session },
  { where: { slug: context?.params?.slug as string } }
)

and

const { can: isAdmin } = await Guard.can(
  "isOwner",
  "workspace",
  { session },
  { where: { slug: context?.params?.slug as string } }
)

These guards only work for workspaces. You must supply the workspace slug to the Guard function.

The getWorkspace query guard just checks if the current user has a membership with the workspace.

You can create your own guard logic inside the ability.ts file. Check the example section on how to create a guard that checks what plan the workspace is subscribed too.

Payments

Processing is handled by Stripe Checkout. Plans are created on stripe where you copy the price id set in your .env file to the app > core > utils > plans.ts file.

Plans are billed per workspace. When your user completes the stripe checkout process a ping hits /api/stripe where the subscriptionId will be set on the workspace.

File Uploads

There are 2 api routes for uploading and removing files from your s3 style object storage:

/api/files/uploadFile.ts

/api/files/removeFile.ts

Upload returns the following object { Location: string, Key: string }

An upload component is also included at app > core > components > SingleFileUpload.tsx. It's made with react-dropzone and meant to work with react-hook-forms. The component automatically works with the supplied API routes.

API Key Management

You can generate API keys per workspace. There is a PUBLIC_KEY & SECRET_KEY. The difference is the public key stores the non-hashed version for abbreviation purposes and always stays the same, like a "username". The secret key can only be seen once when creating a new key. You can create as many secret keys that you want.

There is an included function app > core > utils > checkToken.ts that requires you to pass your request, response & a whether or not the route is a public route. A public route means only a PUBLIC_KEY is required for the check to pass.

const token = await checkToken(req, res, { PUBLIC: false })

The checkToken function will return the workspace id that is associated with the API keys if all checks are valid.

Admin Panel

There is a preconfigured admin panel, along with a blitz recipe to generate admin pages, queries and mutations from your prisma schema.

To run the generator, issue the following command:

yarn generate:admin

The generator will traverse your prisma schema and only generate the associated files that haven't been generated before. So you don't have to worry about duplicates.

The preconfigured admin panel comes with a dashboard that showcases how many paying customers you have, and how many has churned.

Another special feature included with the admin panel will allow admins to set a workspace plan. It's important to note, that when setting a workspace plan as an admin that the owner of the workspace will be given a 7 day free trial before having to enter their payment info. This also gives the owner of the workspace a chance to opt out, incase an admin subscribes the workspace to an expensive plan the workspace owner never wanted.

You can change how long the free trial is inside app > admin > mutations > updateWorkspace.ts. The specific code is found on line 58:

const subscription = await stripe.subscriptions.create({
  customer: workspace.stripeCustomerId ? workspace.stripeCustomerId : customer.id,
  items: [
    {
      price: data.stripePriceId as string,
      quantity: workspace?.memberships.length,
    },
  ],
  trial_period_days: 14,
})

Tests

Cypress E2E tests are included. The following is tested:

  • User signup
  • Only workspace owners can invite users
  • Only users invited to a workspace can view that workspace
  • Inviting a user successfully adds them to the workspace

To run the tests, make sure you're connecting to an alternate test database set in a .env.test.local file.

Then run in the following order:

yarn seed:db

yarn test:e2e

Examples

Customizing a guard that checks the workspace plan

Of course you're going to want different plans that allow for different features. This is how you do it:

Head to the app > guard > ability.ts file. You're going to import the app > workspaces > utils > checkPlan.ts utility function and check to see which plan the workspace is subscribed to. Then if they're subscribed to a plan, we'll allow the user to run the getWorkspace query. Allowing them to view their workspace page.

Inside the can("read", "workspace"), we'll add the following code to check if the workspace being viewed is subscribed to the pro plan:

can("read", "workspace", async (args) => {
  const workspace = await db.workspace.findFirst({
    where: args.where,
    include: {
      memberships: true,
    },
  })

  const plan = checkPlan(workspace)

  return (
    workspace?.memberships.some((p) => p.userId === ctx.session.userId) === true && plan === "pro"
  )
})

hire-win's People

Contributors

vivek7405 avatar

Stargazers

 avatar

Watchers

 avatar

hire-win's Issues

Candidate Pools

  • Allow user to create Candidate Pools and add a candidate to that pool

Schedule & Calendar configuration for all stages

  • Instead of allowing schedule & calendar configuration for just the stages assigned to the user, allow configuring schedule and calendar for all the stages

  • Show a new section below "Stages assigned to you" named "Other stages" and allow calendar and schedule configuration for them as well

  • This will help when the user is assigned as an interviewer for a particular candidate stage even when the entire job stage is not assigned to him

  • DB Structure changes may be needed which can be challenging

  • Hint: Separate tables, one for duration and interviewer, second for schedule and calendar

  • jobId, workflowStageId, duration, interviewerId -> @@unique(jobId, workflowStageId)

  • jobId, workflowStageId, scheduleId, calendarId, memberId -> @@unique(jobId, workflowStageId, memberId)

Company Workspaces

  • User can have multiple company workspaces and can invite members to company workspace and then add them to a job
  • Jobs, Categories, Workflows, Forms, Score Cards, Email Templates, Candidate Pools will be available for all company members
  • User will have to subscribe to each workspace to access the PRO features

Deploy the application to railway.app and setup cloudflare CDN

Deploy the application to railway.app and create the below mentioned environment variables. Add the domains hire.win and www.hire.win in railway.

Setup cloudflare CDN for hire.win domain. Also, add the following forwarding URL page rule in cloudflare so that the domain is always naked and www gets redirected to non-www:

If the URL matches:
www.hire.win/*
Then the settings are:
Forwarding URL
302-Temporary Redirect
Destination URL: https://hire.win/$1

Environment Variables to be added to railway.app:

DATABASE_URL=postgresql://vivekpatel@localhost:5432/hire-win
NEXT_PUBLIC_APP_URL=http://localhost:3000
SESSION_SECRET_KEY=asdfghjkl

STRIPE_SECRET=asdfghjkl
PRO_PLAN=asdfghjkl
PRO2_PLAN=asdfghjkl
NEXT_PUBLIC_STRIPE_PUBLIC=asdfghjkl
STRIPE_WEBHOOK=asdfghjkl

S3_ACCESS_KEY=miniominio
S3_SECRET_KEY=miniominio
S3_BUCKET=hire.win
S3_BUCKET_REGION=us-east-2

S3_ENDPOINT=http://localhost:9000

POSTMARK_TOKEN= asdfghjkl

Job Application Form

A job application form is used to source candidates. Every job can be assigned an application form so that the candidates can apply using that form.

The user first adds all the required questions and then creates forms where the user can select the questions for that particular form. Once added, the question type can't be edited and the question can only be archived after that.

The user can create multiple forms and assign one to a job.

Based on the selected form, the job application form will be generated through which the candidates can apply for the job.

The user can select the "Default" form while creating a job which shall generate an application form with basic required questions to apply for a job.

The responses from the form shall be the candidates sourced.

Restrict Company & Candidate creation on Free plan

  • Allow creating only 25 candidates for a job on the Free plan. Once the limit has reached, the job will automatically disappear from the careers page and the job description and job application pages shall also not be accessible unless the company is upgraded to the PRO plan

  • Allow creating a new company only if all the existing companies are subscribed the PRO plan

Assign candidate Interviewer

  • The Interviewers assigned in the Job Members page should act as the default assignment which can be changed per candidate
  • Provide a dropdown to change the default interviewer on the candidate details page
  • Only allow the admin/owner to change the assigned interviewer from the candidate details page

Probable solution:

  • Determine the existing interviewer for a candidate by checking if a record for that candidate and workflow stage exists in CandidateWorkflowStageInterviewer table or else show the default interviewer configured from the job members settings page

  • When the interviewer is changed from the candidate details page, create/update the entry in the CandidateWorkflowStageInterviewer table for that particular candidate & Workflow Stage

  • While Scheduling an interview, check for the candidate interviewer and use their default Schedule and default Calendar if they are not the default interviewer for that stage. For default interviewers, use the scheduling settings from job settings as is being done currently

Job description

Provide a rich text editor while creating/editing a job so that the user can write a job description which shall be visible in the "Apply to job" page where the candidates shall apply for the job.

Email Templates

  • Let users create email templates using LabeledRichTextField. Users can insert dynamic values like {{candidate-name}}, {{job-title}}, {{company-name}}, {{user-name}}, {{interviewer-name}}, {{organiser-name}}, {{other-attendees}} using a drop-down or else buttons.
  • Use the saved template while sending emails and replace the dynamic values with actual ones.
  • When clicking Send Email, user can either select a saved template or else draft a new email from scratch.

Cancel Interview & provide meeting link

  • Provide an option to cancel or reschedule an interview
  • Provide meeting link

Probable solution:

  • Get the event object and store eventId and htmlLink from event.data.id and event.data.htmlLink
  • Find out a way to cancel the event using eventId

Interview Workflow

A workflow consists of multiple stages through which a candidate has to pass for getting selected for a job.

A workflow will always have the fist stage as "Sourced" and the last stage as "Selected" by default.

The user first adds all the required stages and then creates workflows where the user can select the stages for that particular workflow.

The user can create multiple workflows and assign one to a job.

The workflow for a job can be changed only if there are no candidates for that job or else all the candidates are either in the first stage (Sourced) or else in the last stage (Selected).

Applicant Tracking with Kanban Board

  • Programatically add default & mandatory stages while creating a new workflow similar to Forms & Questions
  • Let an applicant be assigned to a stage from the workflow
  • Provide Kanban board to move candidate across the stages

Move to next stage from candidate details

  • There's a button "Move to Next Stage" in the candidate details page
  • The down arrow in the button should popup all the available stages to which the candidate can be moved
  • Before moving the candidate to the Next stage or the selected stage from dropdown, show a confirm dialog and move the candidate only if confirmed

Default Form

Add default Form (programatically) and make sure it is applied by default while creating a job

Theme color selection fix

  • Right now, we need to change the color twice and then when we save, the first selection is set as the actual theme color
  • Fix the behavior and also make color selection simpler by providing a dropdown of the available colors

Category setup

Categories are used to identify job types such as Marketing, Engineering, Support, etc.

  • While creating a new job, the user shall be asked to select a category.
  • If the required category is not already created, the user can add a new category there and then and apply it to the job or else leave the category field blank in which case a "Default" category shall be applied to the job.
  • A "Default" category shall automatically be created on creation of a user account.

Interview meeting Organizer

  • Interview meeting organizer is not being assigned correctly.
  • Use the organizer calendar to book the meeting to fix the bug

Detailed desc:

  • Schedule interview from Organiser's calendar instead of interviewer's calendar if organiser is not the interviewer so that he is the organiser of the meeting

  • Consider the schedule of Interviewer

  • If organiser is not the interviewer, push the organisers's calendars to the taken time slots along with other attendees

  • Create the event using organiser's calendar

The below two steps may be used to get the meeting link and cancel the meeting which will be taken care in a separate issue (#67):

  • Get the event object and store eventId and htmlLink from event.data.id and event.data.htmlLink

  • Find out a way to cancel the event using eventId

Checkbox for displaying salary on Job Board

Add a checkbox below Salary fields in Add/Edit Job page with the label "Display Salary in Job Board" and implement the logic for displaying Salary in the listed jobs on the Job Board if the checkbox is checked.

Job listing page

Provide a public Job Listing page so that the user can embed the page to their website for advertising their open jobs.

Provide a category wise listing of all the open jobs. There should be an apply button which shall navigate the user to the "Apply to job" page for that particular job.

Confirm email before signup

  • Confirm email before letting the user to signup
  • Hide email from signup screen since the user would have verified the email already
  • Similarly for Invite to Company as well, hide Email since the user would already have verified it

Apply to job page

Apply to job page shall be generated using the dynamic form the user has configured for a particular job.

The job description should appear at the top and then the dynamic form shall appear.

Apply to job page shall be public and anyone should be able to access it without logging in.

Controlled Kanban Board

  • Provide a toggle to enable drag for the kanban board which should be off by default
  • Also provide a right arrow for each candidate to move them to the next stage
  • Before moving the candidate to next stage while drag is disabled, show a confirm dialog message and move the candidate only if confirmed

Comments

  • Allow users to add stage-wise comments for a candidate

Emails

  • Let the user send emails to candidate either by using one of the saved email-templates or else by drafting one from scratch
  • The sent emails in a particular stage should appear under the Emails section in candidate stage view
  • Track which template was used for sending email if at all used and update the "0 Emails" count in email-templates page for that particular template which is hardcoded as of now.

Interview Scheduling

  • Create a default schedule while signing up.

  • Calendars UI - Allow users to connect calendars and list all the connected calendars. Also provide an option to set a default connected calendar. Note that the default baikal calendar shall already be connected when the user signs up. Though, the user can connect more calendars and set any one as default.

  • Let the Job Owner assign interviewers to stages and select meeting time interval for each stage from the Job Settings Members page.

  • In Job Settings Members page, allow users to select schedules for the stages assigned to the user and allow selecting calendar for each stage assigned to the user.

  • On Candidate page, below Score Cards div, list all the meetings stage wise similar to Score Cards and allow scheduling a New Meeting for the selected stage.

  • At present, the scorecard is enabled for the current stage and any user (interviewer) can change the score for the current stage. Modify the logic so that the score for any stage owned by the interviewer can be changed by them. The score for the rest of the stages that are not owned by an interviewer should be read-only. Note that an interviewer can own multiple stages.

  • Apply the same logic to Meetings, with the only change being that the job owner can Schedule and cancel meetings for all the stages irrespective of the assigned interviewer.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.