1- import { faCheck , faPause , faPlay , faStop } from "@fortawesome/free-solid-svg-icons" ;
1+ import { faArrowUpRightFromSquare , faBan , faBookmark , faCheck , faCircleUser , faEdit , faPause , faPlay , faStop } from "@fortawesome/free-solid-svg-icons" ;
22import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" ;
33import clsx from "clsx" ;
44import { useEffect , useState } from "react" ;
55import { Tooltip } from "react-tooltip" ;
66import useSettings from "../../hooks/useSettings" ;
77import { TIssue } from "../../types/redmine" ;
8+ import ContextMenu from "../general/ContextMenu" ;
89import KBD from "../general/KBD" ;
910import CreateTimeEntryModal from "./CreateTimeEntryModal" ;
1011import EditTime from "./EditTime" ;
1112
13+ export type IssueData = {
14+ active : boolean ;
15+ start ?: number ;
16+ time : number ;
17+ remember : boolean ;
18+ } ;
19+
1220type PropTypes = {
1321 issue : TIssue ;
14- isActive : boolean ;
15- time : number ;
16- start ?: number ;
22+ data : IssueData ;
23+ assignedToMe : boolean ;
1724 onStart : ( ) => void ;
1825 onPause : ( time : number ) => void ;
1926 onStop : ( ) => void ;
20- //onDone: (time: number) => void;
2127 onOverrideTime : ( time : number ) => void ;
28+ onRemember : ( ) => void ;
29+ onForgot : ( ) => void ;
2230} ;
2331
24- const Issue = ( { issue, isActive , time, start, onStart, onPause, onStop, onOverrideTime } : PropTypes ) => {
32+ const Issue = ( { issue, data : { active , time, start, remember } , assignedToMe , onStart, onPause, onStop, onOverrideTime, onRemember , onForgot } : PropTypes ) => {
2533 const { settings } = useSettings ( ) ;
2634
2735 const [ timer , setTimer ] = useState ( calcTime ( time , start ) ) ;
2836
2937 useEffect ( ( ) => {
3038 setTimer ( calcTime ( time , start ) ) ;
31- if ( isActive && start ) {
39+ if ( active && start ) {
3240 const timerInterval = setInterval ( ( ) => {
3341 setTimer ( calcTime ( time , start ) ) ;
3442 } , 1000 ) ;
3543 return ( ) => clearInterval ( timerInterval ) ;
3644 }
37- } , [ isActive , time , start ] ) ;
45+ } , [ active , time , start ] ) ;
3846
3947 const [ editTime , setEditTime ] = useState < number | undefined > ( undefined ) ;
4048 const [ createTimeEntry , setCreateTimeEntry ] = useState < number | undefined > ( undefined ) ;
49+
50+ const [ contextMenu , setContextMenu ] = useState < { x : number ; y : number } | undefined > ( undefined ) ;
51+
4152 return (
4253 < >
4354 < div
@@ -55,7 +66,7 @@ const Issue = ({ issue, isActive, time, start, onStart, onPause, onStop, onOverr
5566 if ( editTime !== undefined ) {
5667 return ;
5768 }
58- if ( isActive ) {
69+ if ( active ) {
5970 onPause ( timer ) ;
6071 } else {
6172 onStart ( ) ;
@@ -64,8 +75,12 @@ const Issue = ({ issue, isActive, time, start, onStart, onPause, onStop, onOverr
6475 }
6576 } }
6677 data-tooltip-id = "tooltip-toggle-timer"
78+ onContextMenu = { ( e ) => {
79+ e . preventDefault ( ) ;
80+ setContextMenu ( { x : e . pageX , y : e . pageY } ) ;
81+ } }
6782 >
68- < h1 className = "mb-1 truncate" >
83+ < h1 className = "mb-1 truncate me-4 " >
6984 < a href = { `${ settings . redmineURL } /issues/${ issue . id } ` } target = "_blank" className = "text-blue-500 hover:underline" data-tooltip-id = { `tooltip-issue-${ issue . id } ` } tabIndex = { - 1 } >
7085 #{ issue . id }
7186 </ a > { " " }
@@ -87,10 +102,12 @@ const Issue = ({ issue, isActive, time, start, onStart, onPause, onStop, onOverr
87102 < th className = "pr-2 font-medium whitespace-nowrap" > Priority:</ th >
88103 < td > { issue . priority . name } </ td >
89104 </ tr >
90- < tr >
91- < th className = "pr-2 font-medium whitespace-nowrap" > Assignee:</ th >
92- < td > { issue . assigned_to . name } </ td >
93- </ tr >
105+ { issue . assigned_to && (
106+ < tr >
107+ < th className = "pr-2 font-medium whitespace-nowrap" > Assignee:</ th >
108+ < td > { issue . assigned_to . name } </ td >
109+ </ tr >
110+ ) }
94111 { issue . estimated_hours && (
95112 < tr >
96113 < th className = "pr-2 font-medium whitespace-nowrap" > Estimated time:</ th >
@@ -123,12 +140,12 @@ const Issue = ({ issue, isActive, time, start, onStart, onPause, onStop, onOverr
123140 onCancel = { ( ) => setEditTime ( undefined ) }
124141 />
125142 ) ) || (
126- < span className = { clsx ( "text-lg" , timer > 0 ? "text-yellow-500" : "text-gray-700 dark:text-gray-500" , isActive && "font-semibold" ) } onDoubleClick = { ( ) => setEditTime ( timer ) } data-tooltip-id = "tooltip-edit-timer" >
143+ < span className = { clsx ( "text-lg" , timer > 0 ? "text-yellow-500" : "text-gray-700 dark:text-gray-500" , active && "font-semibold" ) } onDoubleClick = { ( ) => setEditTime ( timer ) } data-tooltip-id = "tooltip-edit-timer" >
127144 { formatTime ( timer ) }
128145 </ span >
129146 ) }
130147 < Tooltip id = "tooltip-edit-timer" place = "top" delayShow = { 700 } content = "Double-click to edit" className = "italic" />
131- { ! isActive ? (
148+ { ! active ? (
132149 < FontAwesomeIcon icon = { faPlay } size = "2x" className = "text-green-500 cursor-pointer focus:outline-none" onClick = { onStart } data-tooltip-id = "tooltip-start-timer" tabIndex = { - 1 } />
133150 ) : (
134151 < FontAwesomeIcon icon = { faPause } size = "2x" className = "text-red-500 cursor-pointer focus:outline-none" onClick = { ( ) => onPause ( timer ) } data-tooltip-id = "tooltip-pause-timer" tabIndex = { - 1 } />
@@ -147,13 +164,19 @@ const Issue = ({ issue, isActive, time, start, onStart, onPause, onStop, onOverr
147164 < Tooltip id = "tooltip-stop-timer" place = "top" delayShow = { 700 } content = "Click to stop timer" className = "italic" />
148165 < Tooltip id = { `tooltip-done-timer-${ issue . id } ` } place = "bottom" delayShow = { 700 } className = "z-10 italic opacity-100" >
149166 Click to transfer{ " " }
150- < span className = { clsx ( "text-xs" , timer > 0 ? "text-yellow-500" : "text-gray-700 dark:text-gray-500" , isActive && "font-semibold" ) } >
167+ < span className = { clsx ( "text-xs" , timer > 0 ? "text-yellow-500" : "text-gray-700 dark:text-gray-500" , active && "font-semibold" ) } >
151168 { formatTime ( settings . options . roundTimeNearestQuarterHour ? roundTimeNearestQuarterHour ( timer ) : timer ) }
152169 </ span > { " " }
153170 to Redmine issue
154171 </ Tooltip >
155172 </ div >
156173 </ div >
174+ { ! assignedToMe && (
175+ < >
176+ < Tooltip id = "tooltip-not-assigned-to-me" place = "left" delayShow = { 700 } content = "Issue is not assigned to you" className = "italic" />
177+ < FontAwesomeIcon icon = { faCircleUser } className = "absolute top-2 right-2 text-gray-300 dark:text-gray-600" data-tooltip-id = "tooltip-not-assigned-to-me" />
178+ </ >
179+ ) }
157180 </ div >
158181 < Tooltip id = "tooltip-toggle-timer" place = "bottom" delayShow = { 4000 } className = "italic max-w-[275px]" >
159182 If selected, press < KBD text = "Ctrl" /> + < KBD text = "Spacebar" space = "xl" /> to toggle timer
@@ -169,6 +192,64 @@ const Issue = ({ issue, isActive, time, start, onStart, onPause, onStop, onOverr
169192 } }
170193 />
171194 ) }
195+ { contextMenu && (
196+ < ContextMenu
197+ x = { contextMenu . x }
198+ y = { contextMenu . y }
199+ onClose = { ( ) => setContextMenu ( undefined ) }
200+ menu = { [
201+ [
202+ {
203+ name : "Open in Redmine" ,
204+ icon : < FontAwesomeIcon icon = { faArrowUpRightFromSquare } /> ,
205+ onClick : ( ) => {
206+ window . open ( `${ settings . redmineURL } /issues/${ issue . id } ` , "_blank" ) ;
207+ } ,
208+ } ,
209+ ] ,
210+ [
211+ {
212+ name : "Start timer" ,
213+ icon : < FontAwesomeIcon icon = { faPlay } /> ,
214+ disabled : active ,
215+ onClick : onStart ,
216+ } ,
217+ {
218+ name : "Pause timer" ,
219+ icon : < FontAwesomeIcon icon = { faPause } /> ,
220+ disabled : ! active ,
221+ onClick : ( ) => onPause ( timer ) ,
222+ } ,
223+ {
224+ name : "Stop timer" ,
225+ icon : < FontAwesomeIcon icon = { faStop } /> ,
226+ disabled : timer === 0 ,
227+ onClick : onStop ,
228+ } ,
229+ {
230+ name : "Edit timer" ,
231+ icon : < FontAwesomeIcon icon = { faEdit } /> ,
232+ disabled : timer === 0 ,
233+ onClick : ( ) => setEditTime ( timer ) ,
234+ } ,
235+ ] ,
236+ [
237+ {
238+ name : "Remember issue" ,
239+ icon : < FontAwesomeIcon icon = { faBookmark } /> ,
240+ disabled : assignedToMe || remember ,
241+ onClick : onRemember ,
242+ } ,
243+ {
244+ name : "Forgot issue" ,
245+ icon : < FontAwesomeIcon icon = { faBan } /> ,
246+ disabled : assignedToMe || ! remember ,
247+ onClick : onForgot ,
248+ } ,
249+ ] ,
250+ ] }
251+ />
252+ ) }
172253 </ >
173254 ) ;
174255} ;
0 commit comments