לדלג לתוכן

7.10 פרויקטים פרויקט

פרויקט - לוח ניהול משימות

בפרויקט הזה תבנו אפליקציית ניהול משימות (Task Management Dashboard) מלאה שמשלבת את כל הנושאים שלמדנו בסקציה הזו: Zustand לניהול state, React Query לשליפת נתונים, React Router לניתוב, React Hook Form ו-Zod לטפסים, ודפוסי תכנון מתקדמים.


תיאור הפרויקט

לוח בקרה לניהול משימות צוותי, עם אותנטיקציה, פעולות CRUD, סינון ומיון, ועיצוב רספונסיבי.


דרישות טכניות

טכנולוגיות

  • React + TypeScript
  • React Router (ניתוב)
  • Zustand (ניהול UI state + אותנטיקציה)
  • React Query / TanStack Query (שליפת נתונים מ-API)
  • React Hook Form + Zod (טפסים וולידציה)
  • CSS Modules או Tailwind CSS (עיצוב)

מבנה הפרויקט

src/
  components/
    ui/           - קומפוננטות UI בסיסיות (Button, Input, Modal, Toast)
    layout/       - Header, Sidebar, Layout
    tasks/        - TaskCard, TaskList, TaskForm, TaskFilters
    auth/         - LoginForm, RegisterForm
  hooks/          - הוקים מותאמים אישית
  stores/         - Zustand stores
  api/            - פונקציות API (mock או אמיתי)
  schemas/        - סכמות Zod
  pages/          - דפי האפליקציה
  types/          - טיפוסי TypeScript

תכונות נדרשות

1. אותנטיקציה - Authentication Flow

  • דף התחברות עם טופס (אימייל + סיסמה)
  • דף הרשמה עם ולידציה (שם, אימייל, סיסמה, אישור סיסמה)
  • שמירת טוקן ב-localStorage עם Zustand persist
  • נתיבים מוגנים (Protected Routes) - הפניה לדף התחברות
  • כפתור התנתקות
  • Zustand store לניהול מצב האותנטיקציה
// דוגמת מבנה Auth Store
interface AuthStore {
  user: User | null;
  token: string | null;
  isAuthenticated: boolean;
  login: (email: string, password: string) => Promise<void>;
  register: (data: RegisterData) => Promise<void>;
  logout: () => void;
}

2. ניהול משימות - CRUD Operations

  • יצירת משימה חדשה (טופס עם React Hook Form + Zod)
  • צפייה בפרטי משימה
  • עריכת משימה
  • מחיקת משימה (עם אישור במודל)
  • שינוי סטטוס משימה

כל משימה כוללת:

interface Task {
  id: string;
  title: string;
  description: string;
  status: "todo" | "in-progress" | "review" | "done";
  priority: "low" | "medium" | "high" | "urgent";
  assignee: string;
  dueDate: string;
  tags: string[];
  createdAt: string;
  updatedAt: string;
}

סכמת Zod לולידציית טופס:

const taskSchema = z.object({
  title: z.string().min(3, "כותרת חייבת להכיל לפחות 3 תווים").max(100),
  description: z.string().max(500).optional(),
  status: z.enum(["todo", "in-progress", "review", "done"]),
  priority: z.enum(["low", "medium", "high", "urgent"]),
  assignee: z.string().min(1, "יש לבחור אחראי"),
  dueDate: z.string().refine(
    (date) => new Date(date) > new Date(),
    "תאריך יעד חייב להיות בעתיד"
  ),
  tags: z.array(z.string()).max(5, "מקסימום 5 תגיות"),
});

3. שליפת נתונים - React Query

  • שליפת רשימת משימות עם useQuery
  • יצירה, עדכון ומחיקה עם useMutation
  • Optimistic updates על שינוי סטטוס
  • Invalidation אוטומטי אחרי mutations
  • מטמון עם staleTime מתאים
// דוגמת hooks
function useTasks(filters: TaskFilters) {
  return useQuery({
    queryKey: ["tasks", filters],
    queryFn: () => fetchTasks(filters),
    staleTime: 2 * 60 * 1000,
  });
}

function useCreateTask() {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: createTask,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["tasks"] });
    },
  });
}

4. סינון ומיון - Filtering and Sorting

  • סינון לפי סטטוס (multi-select)
  • סינון לפי עדיפות
  • סינון לפי אחראי
  • חיפוש חופשי בכותרת ותיאור
  • מיון לפי: תאריך יצירה, תאריך יעד, עדיפות, כותרת
  • שמירת פילטרים ב-URL עם useSearchParams

5. ניתוב - React Router

מבנה נתיבים:

/login                    - דף התחברות
/register                 - דף הרשמה
/                         - לוח בקרה ראשי (מוגן)
/tasks                    - רשימת משימות (מוגן)
/tasks/:taskId            - פרטי משימה (מוגן)
/tasks/new                - יצירת משימה (מוגן)
/tasks/:taskId/edit       - עריכת משימה (מוגן)
/board                    - תצוגת Kanban (מוגן)
*                         - דף 404

6. עיצוב רספונסיבי - Responsive Layout

  • Layout עם Header (ניווט + פרופיל) ו-Sidebar (תפריט)
  • ב-mobile: Sidebar נסגר ונפתח בלחיצה
  • תצוגת משימות: רשימה במובייל, Grid בדסקטופ
  • מודלים עם Portals
  • מערכת Toast להודעות (Portal)
  • תמיכה ב-dark mode עם Zustand persist

תכונות בונוס (אופציונלי)

  • תצוגת Kanban עם drag & drop (ספריית dnd-kit)
  • דפדוף ברשימת משימות
  • גלילה אינסופית
  • ייצוא משימות ל-CSV
  • גרפים וסטטיסטיקות (משימות לפי סטטוס, עדיפות, אחראי)
  • Undo על פעולת מחיקה
  • Keyboard shortcuts (Ctrl+N ליצירת משימה, / לחיפוש)

API מדומה

אפשר להשתמש ב-API מדומה עם setTimeout, או ב-json-server:

npm install -g json-server

קובץ db.json:

{
  "users": [
    { "id": "1", "name": "דני כהן", "email": "dani@example.com", "password": "123456" }
  ],
  "tasks": [
    {
      "id": "1",
      "title": "עיצוב דף הבית",
      "description": "לעצב את דף הבית של האפליקציה",
      "status": "in-progress",
      "priority": "high",
      "assignee": "דני כהן",
      "dueDate": "2024-04-15",
      "tags": ["עיצוב", "UI"],
      "createdAt": "2024-03-01",
      "updatedAt": "2024-03-10"
    }
  ]
}

הרצה:

json-server --watch db.json --port 3001

שלבי עבודה מומלצים

שלב 1 - בסיס (יום 1-2)

  • הקמת פרויקט עם Vite + TypeScript
  • התקנת כל הספריות
  • הגדרת מבנה תיקיות
  • הגדרת React Router עם Layout בסיסי
  • הגדרת QueryClient ו-Zustand store

שלב 2 - אותנטיקציה (יום 3)

  • Auth store ב-Zustand עם persist
  • טפסי התחברות והרשמה עם React Hook Form + Zod
  • Protected Routes
  • ניווט מותנה (מחובר/לא מחובר)

שלב 3 - CRUD משימות (יום 4-5)

  • API functions (mock)
  • React Query hooks (useTasks, useCreateTask, useUpdateTask, useDeleteTask)
  • טופס יצירת/עריכת משימה
  • רשימת משימות עם TaskCard
  • דף פרטי משימה
  • מודל אישור מחיקה

שלב 4 - סינון ומיון (יום 6)

  • קומפוננטת TaskFilters
  • שמירת פילטרים ב-URL
  • חיפוש חופשי עם debounce
  • מיון

שלב 5 - עיצוב ושיפורים (יום 7-8)

  • עיצוב רספונסיבי
  • Dark mode
  • Toast notifications
  • אנימציות
  • Optimistic updates
  • Error Boundaries

שלב 6 - בונוסים (יום 9-10)

  • תצוגת Kanban
  • סטטיסטיקות
  • Keyboard shortcuts
  • ליטוש ותיקון באגים

קריטריונים להצלחה

  • האפליקציה עובדת ללא שגיאות
  • שימוש נכון בכל הספריות שנלמדו
  • קוד נקי ומאורגן עם TypeScript
  • ולידציה מלאה בטפסים
  • ניתוב עם נתיבים מוגנים
  • עיצוב רספונסיבי בסיסי
  • טיפול במצבי טעינה ושגיאה
  • שמירת state ב-persist (אותנטיקציה, הגדרות)

דוגמת מבנה קובץ ראשי

// App.tsx
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { Suspense, lazy } from "react";
import { ErrorBoundary } from "react-error-boundary";

const queryClient = new QueryClient({
  defaultOptions: {
    queries: { staleTime: 2 * 60 * 1000, retry: 2 },
  },
});

const LoginPage = lazy(() => import("./pages/LoginPage"));
const RegisterPage = lazy(() => import("./pages/RegisterPage"));
const DashboardPage = lazy(() => import("./pages/DashboardPage"));
const TasksPage = lazy(() => import("./pages/TasksPage"));
const TaskDetailPage = lazy(() => import("./pages/TaskDetailPage"));
const BoardPage = lazy(() => import("./pages/BoardPage"));

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <BrowserRouter>
        <ErrorBoundary FallbackComponent={ErrorFallback}>
          <ToastProvider>
            <Suspense fallback={<LoadingPage />}>
              <Routes>
                <Route path="/login" element={<LoginPage />} />
                <Route path="/register" element={<RegisterPage />} />

                <Route element={<ProtectedRoute />}>
                  <Route element={<AppLayout />}>
                    <Route path="/" element={<DashboardPage />} />
                    <Route path="/tasks" element={<TasksPage />} />
                    <Route path="/tasks/:taskId" element={<TaskDetailPage />} />
                    <Route path="/board" element={<BoardPage />} />
                  </Route>
                </Route>

                <Route path="*" element={<NotFoundPage />} />
              </Routes>
            </Suspense>
          </ToastProvider>
        </ErrorBoundary>
      </BrowserRouter>
      <ReactQueryDevtools />
    </QueryClientProvider>
  );
}

בהצלחה!