Source Code

;; Task Manager Contract
;; On-chain project/task management
;; Halal - productivity and organization
;; 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-DONE (err u405))

(define-data-var task-count uint u0)
(define-data-var completed-count uint u0)

(define-map tasks uint {
  creator: principal, assignee: principal, title: (string-utf8 100),
  priority: uint, status: (string-ascii 20), created: uint, completed-at: uint
})
(define-map user-task-count principal uint)
(define-map user-completed principal uint)

(define-public (create-task (title (string-utf8 100)) (assignee principal) (priority uint))
  (let ((id (+ (var-get task-count) u1)))
    (map-set tasks id {
      creator: tx-sender, assignee: assignee, title: title,
      priority: priority, status: "todo", created: stacks-block-height, completed-at: u0
    })
    (map-set user-task-count assignee (+ (default-to u0 (map-get? user-task-count assignee)) u1))
    (var-set task-count id) (ok id)))

(define-public (update-status (task-id uint) (new-status (string-ascii 20)))
  (let ((task (unwrap! (map-get? tasks task-id) ERR-NOT-FOUND)))
    (asserts! (or (is-eq tx-sender (get assignee task)) (is-eq tx-sender (get creator task))) ERR-NOT-AUTHORIZED)
    (map-set tasks task-id (merge task { status: new-status })) (ok true)))

(define-public (complete-task (task-id uint))
  (let ((task (unwrap! (map-get? tasks task-id) ERR-NOT-FOUND)))
    (asserts! (or (is-eq tx-sender (get assignee task)) (is-eq tx-sender (get creator task))) ERR-NOT-AUTHORIZED)
    (asserts! (not (is-eq (get status task) "done")) ERR-ALREADY-DONE)
    (map-set tasks task-id (merge task { status: "done", completed-at: stacks-block-height }))
    (map-set user-completed (get assignee task) (+ (default-to u0 (map-get? user-completed (get assignee task))) u1))
    (var-set completed-count (+ (var-get completed-count) u1)) (ok true)))

(define-public (reassign-task (task-id uint) (new-assignee principal))
  (let ((task (unwrap! (map-get? tasks task-id) ERR-NOT-FOUND)))
    (asserts! (is-eq tx-sender (get creator task)) ERR-NOT-AUTHORIZED)
    (map-set tasks task-id (merge task { assignee: new-assignee })) (ok true)))

(define-read-only (get-task (id uint)) (map-get? tasks id))
(define-read-only (get-task-count) (ok (var-get task-count)))
(define-read-only (get-completed-count) (ok (var-get completed-count)))
(define-read-only (get-user-tasks (who principal)) (ok (default-to u0 (map-get? user-task-count who))))
(define-read-only (get-user-completed (who principal)) (ok (default-to u0 (map-get? user-completed who))))

Functions (9)

FunctionAccessArgs
create-taskpublictitle: (string-utf8 100
update-statuspublictask-id: uint, new-status: (string-ascii 20
complete-taskpublictask-id: uint
reassign-taskpublictask-id: uint, new-assignee: principal
get-taskread-onlyid: uint
get-task-countread-only
get-completed-countread-only
get-user-tasksread-onlywho: principal
get-user-completedread-onlywho: principal