File size: 3,438 Bytes
1c8e50c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import React, { useCallback, useEffect, useState } from 'react';
import { useAuth } from '../../context/AuthContext';
import { isAdmin as checkIsAdmin } from '../../utils/roleUtils';
import { taskAPI } from '../../services/api';
import Loader from '../common/Loader';
import EmptyState from '../common/EmptyState';
import { formatDateTime, safeArray } from '../../utils/formatters';

const TaskList = ({ refreshKey = 0 }) => {
  const { user } = useAuth();
  const [tasks, setTasks] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState('');
  const [updating, setUpdating] = useState(null);

  const loadTasks = useCallback(async () => {
    try {
      setLoading(true);
      setError('');
      const response = checkIsAdmin(user) ? await taskAPI.getAllTasks() : await taskAPI.getUserTasks();
      setTasks(safeArray(response?.data));
    } catch (err) {
      const message = err?.response?.data?.message ?? 'We were unable to load your tasks.';
      setError(message);
      setTasks([]);
    } finally {
      setLoading(false);
    }
  }, [user?.role]);

  useEffect(() => {
    if (user) {
      loadTasks();
    }
  }, [loadTasks, user, refreshKey]);

  const handleCompleteTask = async (taskId) => {
    setUpdating(taskId);
    setError('');
    try {
      await taskAPI.completeTask(taskId);
      await loadTasks();
    } catch (err) {
      const message = err?.response?.data?.message ?? 'We could not update the task.';
      setError(message);
    } finally {
      setUpdating(null);
    }
  };

  if (loading) {
    return <Loader label="Loading tasks" />;
  }

  if (error && !tasks.length) {
    return <EmptyState title="We could not load tasks" description={error} />;
  }

  if (!tasks.length) {
    return <EmptyState title="No tasks yet" description="Tasks assigned to you will appear here." />;
  }

  return (
    <div className="task-grid" role="list" aria-label="Task list">
      {error && (
        <div className="alert alert--error" role="alert">
          {error}
        </div>
      )}
      {tasks.map((task) => {
        const taskId = task?.id ?? task?.taskId;
        const isComplete = Boolean(task?.completed);
        return (
          <article key={taskId} className="card task-card" role="listitem">
            <header>
              <h3 className="task-card__title">{task?.title}</h3>
              <p style={{ margin: '0 0 1rem', color: 'var(--color-muted)' }}>{task?.description}</p>
              {task?.dueDate && (
                <div className="task-card__meta">
                  <span>Due {formatDateTime(task?.dueDate)}</span>
                </div>
              )}
            </header>
            <footer className="task-card__meta">
              <span className={`status-badge ${isComplete ? 'status-badge--success' : 'status-badge--warning'}`}>
                {isComplete ? 'Completed' : 'In progress'}
              </span>
              {!isComplete && (
                <button
                  type="button"
                  className="button button--primary"
                  onClick={() => handleCompleteTask(taskId)}
                  disabled={updating === taskId}
                >
                  {updating === taskId ? 'Updating…' : 'Mark complete'}
                </button>
              )}
            </footer>
          </article>
        );
      })}
    </div>
  );
};

export default TaskList;