Source Code

;; Job Board Contract
;; Post jobs and apply on-chain
;; Halal - fair employment
;; Clarity 4 compatible

(define-constant CONTRACT-OWNER tx-sender)
(define-constant ERR-NOT-AUTHORIZED (err u401))
(define-constant ERR-NOT-FOUND (err u404))
(define-constant ERR-ALREADY-APPLIED (err u405))
(define-constant ERR-CLOSED (err u406))
(define-constant POSTING-FEE u500000) ;; 0.5 STX

(define-data-var job-count uint u0)
(define-data-var application-count uint u0)

(define-map jobs uint {
  employer: principal, title: (string-utf8 100), description: (string-utf8 200),
  salary-range: (string-utf8 50), job-type: (string-ascii 20),
  applications: uint, status: (string-ascii 20), posted: uint
})
(define-map applications uint { job-id: uint, applicant: principal, cover: (string-utf8 200), status: (string-ascii 20), applied: uint })
(define-map has-applied { job-id: uint, applicant: principal } uint)

(define-public (post-job (title (string-utf8 100)) (description (string-utf8 200)) (salary (string-utf8 50)) (job-type (string-ascii 20)))
  (let ((id (+ (var-get job-count) u1)))
    (try! (stx-transfer? POSTING-FEE tx-sender CONTRACT-OWNER))
    (map-set jobs id {
      employer: tx-sender, title: title, description: description,
      salary-range: salary, job-type: job-type, applications: u0, status: "open", posted: stacks-block-height
    })
    (var-set job-count id) (ok id)))

(define-public (apply-for-job (job-id uint) (cover (string-utf8 200)))
  (let (
    (job (unwrap! (map-get? jobs job-id) ERR-NOT-FOUND))
    (aid (+ (var-get application-count) u1))
  )
    (asserts! (is-eq (get status job) "open") ERR-CLOSED)
    (asserts! (is-none (map-get? has-applied { job-id: job-id, applicant: tx-sender })) ERR-ALREADY-APPLIED)
    (map-set applications aid { job-id: job-id, applicant: tx-sender, cover: cover, status: "pending", applied: stacks-block-height })
    (map-set has-applied { job-id: job-id, applicant: tx-sender } aid)
    (map-set jobs job-id (merge job { applications: (+ (get applications job) u1) }))
    (var-set application-count aid) (ok aid)))

(define-public (update-application-status (app-id uint) (new-status (string-ascii 20)))
  (let (
    (app (unwrap! (map-get? applications app-id) ERR-NOT-FOUND))
    (job (unwrap! (map-get? jobs (get job-id app)) ERR-NOT-FOUND))
  )
    (asserts! (is-eq tx-sender (get employer job)) ERR-NOT-AUTHORIZED)
    (map-set applications app-id (merge app { status: new-status })) (ok true)))

(define-public (close-job (job-id uint))
  (let ((job (unwrap! (map-get? jobs job-id) ERR-NOT-FOUND)))
    (asserts! (is-eq tx-sender (get employer job)) ERR-NOT-AUTHORIZED)
    (map-set jobs job-id (merge job { status: "closed" })) (ok true)))

(define-read-only (get-job (id uint)) (map-get? jobs id))
(define-read-only (get-application (id uint)) (map-get? applications id))
(define-read-only (get-job-count) (ok (var-get job-count)))
(define-read-only (get-application-count) (ok (var-get application-count)))

Functions (8)

FunctionAccessArgs
post-jobpublictitle: (string-utf8 100
apply-for-jobpublicjob-id: uint, cover: (string-utf8 200
update-application-statuspublicapp-id: uint, new-status: (string-ascii 20
close-jobpublicjob-id: uint
get-jobread-onlyid: uint
get-applicationread-onlyid: uint
get-job-countread-only
get-application-countread-only