diff --git a/app/_components/FeatureComponents/Kanban/KanbanCard.tsx b/app/_components/FeatureComponents/Kanban/KanbanCard.tsx index f0f11b7cb..c0f6cc8fb 100644 --- a/app/_components/FeatureComponents/Kanban/KanbanCard.tsx +++ b/app/_components/FeatureComponents/Kanban/KanbanCard.tsx @@ -78,6 +78,7 @@ const KanbanCardComponent = ({ const [showTimeEntriesModal, setShowTimeEntriesModal] = useState(false); const [showStatusSheet, setShowStatusSheet] = useState(false); const hideMobileStatusDropdown = user?.hideMobileStatusDropdown === "enable"; + const hideTimeTrackingOnCards = user?.hideTimeTrackingOnCards === "enable"; const kanbanItemHook = useKanbanItem({ checklist, @@ -157,7 +158,7 @@ const KanbanCardComponent = ({ isOpen={showTimeEntriesModal} onClose={() => setShowTimeEntriesModal(false)} timeEntries={item.timeEntries} - checklistId={checklist.uuid || checklistId} + checklistId={checklistId} itemId={item.id} category={category} onUpdate={onUpdate} @@ -255,29 +256,33 @@ const KanbanCardComponent = ({ )} - - - {item.timeEntries && item.timeEntries.length > 0 && ( -
e.stopPropagation()}> - + setShowTimeEntriesModal(true)} + onTimerToggle={kanbanItemHook.handleTimerToggle} + onAddManualTime={kanbanItemHook.handleAddManualTime} /> -
+ + {item.timeEntries && item.timeEntries.length > 0 && ( +
e.stopPropagation()}> + setShowTimeEntriesModal(true)} + /> +
+ )} + )} {item.recurrence && ( diff --git a/app/_components/FeatureComponents/Kanban/KanbanCardDetail.tsx b/app/_components/FeatureComponents/Kanban/KanbanCardDetail.tsx index 5fa0c41cb..1a6c26e36 100644 --- a/app/_components/FeatureComponents/Kanban/KanbanCardDetail.tsx +++ b/app/_components/FeatureComponents/Kanban/KanbanCardDetail.tsx @@ -22,6 +22,12 @@ import { useTranslations } from "next-intl"; import { KanbanPriorityLevel } from "@/app/_types/enums"; import { KanbanCardDetailProperties } from "./KanbanCardDetailProperties"; import { KanbanCardDetailSubtasks } from "./KanbanCardDetailSubtasks"; +import { KanbanItemTimer } from "./KanbanItemTimer"; +import { TimeEntriesAccordion } from "./TimeEntriesAccordion"; +import { TimeEntriesModal } from "./TimeEntriesModal"; +import { useKanbanItem } from "@/app/_hooks/kanban/useKanbanItem"; +import { useAppMode } from "@/app/_providers/AppModeProvider"; +import { formatTimerTime } from "@/app/_utils/kanban/index"; import { DEFAULT_KANBAN_STATUSES } from "@/app/_consts/kanban"; interface KanbanCardDetailProps { @@ -78,7 +84,9 @@ export const KanbanCardDetail = ({ }: KanbanCardDetailProps) => { const t = useTranslations(); const { permissions } = usePermissions(); - const { formatDateTimeString } = usePreferredDateTime(); + const { usersPublicData } = useAppMode(); + const { formatDateTimeString, formatDateString, formatTimeString } = + usePreferredDateTime(); const statuses = checklist.statuses || DEFAULT_KANBAN_STATUSES; const defaultStatusId = [...statuses].sort((a, b) => a.order - b.order)[0]?.id || ""; @@ -98,6 +106,15 @@ export const KanbanCardDetail = ({ const [estimatedTimeInput, setEstimatedTimeInput] = useState(initialItem.estimatedTime?.toString() || ""); const [availableUsers, setAvailableUsers] = useState<{ username: string; avatarUrl?: string }[]>([]); const [boardIsShared, setBoardIsShared] = useState(false); + const [showTimeEntriesModal, setShowTimeEntriesModal] = useState(false); + + const kanbanItemHook = useKanbanItem({ + checklist, + item, + checklistId, + category, + onUpdate, + }); useEffect(() => { setItem(initialItem); @@ -379,16 +396,53 @@ export const KanbanCardDetail = ({ await _saveField({ estimatedTime: hours.toString() }); }; + const timeTrackingContent = ( +
+ + {item.timeEntries && item.timeEntries.length > 0 && ( + setShowTimeEntriesModal(true)} + /> + )} +
+ ); + return ( - + <> + {showTimeEntriesModal && item.timeEntries && ( + setShowTimeEntriesModal(false)} + timeEntries={item.timeEntries} + checklistId={checklistId} + itemId={item.id} + category={category} + onUpdate={onUpdate} + usersPublicData={usersPublicData} + /> + )} +
{isEditing ? ( @@ -545,9 +599,11 @@ export const KanbanCardDetail = ({ onEstimatedTimeSave={handleEstimatedTimeSave} formatDateTimeString={formatDateTimeString} onStatusChange={handleStatusChange} + timeTracking={timeTrackingContent} />
-
+
+ ); }; diff --git a/app/_components/FeatureComponents/Kanban/KanbanCardDetailProperties.tsx b/app/_components/FeatureComponents/Kanban/KanbanCardDetailProperties.tsx index e236d74a6..a590087b9 100644 --- a/app/_components/FeatureComponents/Kanban/KanbanCardDetailProperties.tsx +++ b/app/_components/FeatureComponents/Kanban/KanbanCardDetailProperties.tsx @@ -42,6 +42,7 @@ interface KanbanCardDetailPropertiesProps { onEstimatedTimeChange: (v: string) => void; onEstimatedTimeSave: () => void; formatDateTimeString: (v: string) => string; + timeTracking?: ReactNode; } interface PropertySectionProps { @@ -104,6 +105,7 @@ export const KanbanCardDetailProperties = ({ onEstimatedTimeChange, onEstimatedTimeSave, formatDateTimeString, + timeTracking, }: KanbanCardDetailPropertiesProps) => { const t = useTranslations(); @@ -288,6 +290,12 @@ export const KanbanCardDetailProperties = ({ )} + {timeTracking && ( + + {timeTracking} + + )} + {metadata.length > 0 && ( hideConnectionIndicator: user?.hideConnectionIndicator || "disable", hideStatusOnCards: user?.hideStatusOnCards || "disable", hideMobileStatusDropdown: user?.hideMobileStatusDropdown || "disable", + hideTimeTrackingOnCards: user?.hideTimeTrackingOnCards || "disable", codeBlockStyle: user?.codeBlockStyle || "default", }); @@ -171,6 +173,7 @@ export const UserPreferencesTab = ({ noteCategories, localeOptions }: SettingsTa const hasKanbanChanges = hasChanges([ "hideStatusOnCards", "hideMobileStatusDropdown", + "hideTimeTrackingOnCards", ]); const validateAndSave = async >( @@ -952,7 +955,11 @@ export const UserPreferencesTab = ({ noteCategories, localeOptions }: SettingsTa