From 1fb01513600ddde0b870090bdd27a646b5a5b2d8 Mon Sep 17 00:00:00 2001 From: oscar Date: Thu, 25 Sep 2025 16:49:03 +0200 Subject: [PATCH 1/3] inicio de la tarea --- client/src/components/TaskDetailModal.tsx | 125 +++++++++++++++++++++- client/src/lib/colorUtils.ts | 11 ++ client/src/pages/boards/service.ts | 37 ++++++- client/src/pages/boards/types.ts | 8 ++ client/src/store/boards/actions.ts | 56 +++++++++- client/src/store/boards/hooks.ts | 15 +++ client/src/store/boards/reducer.ts | 45 ++++++++ client/src/utils/endpoints.ts | 9 ++ server/src/controllers/boardController.ts | 32 +++++- server/src/controllers/cardController.ts | 37 +++++++ server/src/models/BoardModel.ts | 12 +++ server/src/models/CardModel.ts | 12 +++ server/src/routes/boards.routes.ts | 7 +- server/src/routes/card.routes.ts | 11 ++ server/src/services/BoardService.ts | 27 ++++- server/src/services/CardService.ts | 20 ++++ server/src/sockets/boardSockets.ts | 6 +- 17 files changed, 460 insertions(+), 10 deletions(-) create mode 100644 client/src/lib/colorUtils.ts diff --git a/client/src/components/TaskDetailModal.tsx b/client/src/components/TaskDetailModal.tsx index 2d5d148..b67f6e3 100644 --- a/client/src/components/TaskDetailModal.tsx +++ b/client/src/components/TaskDetailModal.tsx @@ -1,16 +1,19 @@ import React, { useState, useEffect, useRef } from "react"; -import type { Task } from "../pages/boards/types"; +import type { Label, Task } from "../pages/boards/types"; import type { User } from "../pages/login/types"; -import { getBoardUsers } from "../pages/boards/service"; +import { createLabel, getBoardUsers } from "../pages/boards/service"; import { Avatar } from "./ui/Avatar"; import { useAddAssigneeAction, + useAddLabelAction, useRemoveAssigneeAction, + useRemoveLabelAction, } from "../store/boards/hooks"; import { Editor } from "@tinymce/tinymce-react"; import { Icon } from "@iconify/react"; import { Button } from "./ui/Button"; import { useTranslation } from "react-i18next"; +import { getContrastColor } from "../lib/colorUtils"; interface TaskDetailModalProps { task: Task; @@ -57,6 +60,45 @@ const TaskDetailModal: React.FC = ({ const addMenuRef = useRef(null); const editorRef = useRef(null); const { t } = useTranslation(); + const [showLabels, setShowLabels] = useState(false); + const [labels, setLabels] = useState([]); + const addLabelAction = useAddLabelAction(); + const removeLabelAction = useRemoveLabelAction(); + + const NewLabelForm: React.FC<{ + boardId: string; + onCreated: (label: Label) => void; + }> = ({ boardId, onCreated }) => { + const [name, setName] = useState(""); + const [color, setColor] = useState("#cccccc"); + + const handleCreate = async () => { + const newLabel = await createLabel(boardId, { name, color }); + onCreated(newLabel); + setName(""); + }; + + return ( +
+ setColor(e.target.value)} + className="h-10 w-10 rounded border p-0" + /> + setName(e.target.value)} + className="flex-grow rounded border px-2" + /> + +
+ ); + }; useEffect(() => { if (contentInputRef.current) contentInputRef.current.focus(); @@ -329,7 +371,10 @@ const TaskDetailModal: React.FC = ({ /> - @@ -392,6 +437,80 @@ const TaskDetailModal: React.FC = ({ })} )} + + {showLabels && ( +
+
+

+ {t("board.labels", "Etiquetas")} +

+ +
+ {labels.map((label) => { + const isAssigned = task.labels?.some( + (l) => l.id === label.id, + ); + return ( + + ); + })} +
+ + setLabels((prev) => [...prev, l])} + /> + + +
+
+ )} {assignedUsers.length > 0 && ( diff --git a/client/src/lib/colorUtils.ts b/client/src/lib/colorUtils.ts new file mode 100644 index 0000000..ad03db3 --- /dev/null +++ b/client/src/lib/colorUtils.ts @@ -0,0 +1,11 @@ +export function getContrastColor(hex: string): "black" | "white" { + const cleanHex = hex.replace("#", ""); + + const r = parseInt(cleanHex.substring(0, 2), 16); + const g = parseInt(cleanHex.substring(2, 4), 16); + const b = parseInt(cleanHex.substring(4, 6), 16); + + const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255; + + return luminance > 0.6 ? "black" : "white"; +} diff --git a/client/src/pages/boards/service.ts b/client/src/pages/boards/service.ts index d07014a..dad4374 100644 --- a/client/src/pages/boards/service.ts +++ b/client/src/pages/boards/service.ts @@ -2,10 +2,11 @@ import { apiClient } from "../../api/client"; import { BOARD_ENDPOINTS, CARD_ENDPOINT, + LABEL_ENDPOINTS, LIST_ENDPOINT, } from "../../utils/endpoints"; import type { User } from "../login/types"; -import type { Board, Column, Task } from "./types"; +import type { Board, Column, Label, Task } from "./types"; export const getBoards = async ( limit: number, @@ -146,3 +147,37 @@ export const removeAssignee = async ( `${CARD_ENDPOINT.CARDS}/removeAssignee/${cardId}/${assigneeId}`, ); }; + +export const getBoardLabels = async ( + boardId: string | number, +): Promise => { + const { data } = await apiClient.get( + LABEL_ENDPOINTS.BY_BOARD(boardId), + ); + return data; +}; + +export const createLabel = async ( + boardId: string | number, + label: Pick, +): Promise