feat: MySQL integration, Docker setup, drag-and-drop kanban

This commit is contained in:
tusuii
2026-02-16 10:20:27 +05:30
parent 5d8af1f173
commit 892a2ceba1
24 changed files with 919 additions and 196 deletions

View File

@@ -1,22 +1,21 @@
import React, { useState } from 'react';
import type { Task, User } from './data';
import { USERS, PRIORITY_COLORS } from './data';
import { PRIORITY_COLORS } from './data';
import { Avatar, StatusBadge } from './Shared';
export function TeamTasksPage({ tasks }: { tasks: Task[]; currentUser: User }) {
export function TeamTasksPage({ tasks, users }: { tasks: Task[]; currentUser: User; users: User[] }) {
const [expanded, setExpanded] = useState<Record<string, boolean>>({});
const members = USERS;
return (
<div className="team-tasks">
<h2 style={{ fontSize: 18, fontWeight: 700, marginBottom: 16 }}>Team Tasks</h2>
{members.map(m => {
{users.map(m => {
const mTasks = tasks.filter(t => t.assignee === m.id);
const isOpen = expanded[m.id] !== false;
return (
<div key={m.id} className="team-group">
<div className="team-group-header" onClick={() => setExpanded(e => ({ ...e, [m.id]: !isOpen }))}>
<Avatar userId={m.id} size={28} />
<Avatar userId={m.id} size={28} users={users} />
<span className="team-group-name">{m.name}</span>
<span className="team-group-count">({mTasks.length} tasks)</span>
<span style={{ color: '#64748b' }}>{isOpen ? '▼' : '▶'}</span>
@@ -43,7 +42,7 @@ export function TeamTasksPage({ tasks }: { tasks: Task[]; currentUser: User }) {
);
}
export function MembersPage({ tasks }: { tasks: Task[] }) {
export function MembersPage({ tasks, users }: { tasks: Task[]; users: User[] }) {
const [expanded, setExpanded] = useState<string | null>(null);
const [showInvite, setShowInvite] = useState(false);
@@ -56,7 +55,7 @@ export function MembersPage({ tasks }: { tasks: Task[] }) {
<table className="members-table">
<thead><tr><th>Avatar</th><th>Full Name</th><th>Role</th><th>Dept</th><th>Assigned</th><th>Done</th><th>Active</th></tr></thead>
<tbody>
{USERS.map(u => {
{users.map(u => {
const ut = tasks.filter(t => t.assignee === u.id);
const done = ut.filter(t => t.status === 'done').length;
const active = ut.filter(t => t.status !== 'done').length;
@@ -64,7 +63,7 @@ export function MembersPage({ tasks }: { tasks: Task[] }) {
return (
<React.Fragment key={u.id}>
<tr onClick={() => setExpanded(expanded === u.id ? null : u.id)}>
<td><Avatar userId={u.id} size={28} /></td>
<td><Avatar userId={u.id} size={28} users={users} /></td>
<td>{u.name}</td>
<td><span style={{ background: `${roleColors[u.role]}22`, color: roleColors[u.role], padding: '2px 8px', borderRadius: 10, fontSize: 10, fontWeight: 600 }}>{u.role.toUpperCase()}</span></td>
<td>{u.dept}</td>
@@ -110,4 +109,3 @@ export function MembersPage({ tasks }: { tasks: Task[] }) {
</div>
);
}