@@ -10,31 +10,17 @@ import { connect } from 'react-redux';
1010import hasPermission from '~/utils/permissions' ;
1111import { boxStyle } from '~/styles' ;
1212import { toast } from 'react-toastify' ;
13- import { modifyProject , clearError } from '../../../actions/projects' ;
14- import ModalTemplate from './../../common/Modal' ;
15- import { CONFIRM_ARCHIVE , CONFIRM_UNARCHIVE } from './../../../languages/en/messages' ;
13+ import { modifyProject } from '../../../actions/projects' ;
14+ import { OverlayTrigger , Tooltip } from 'react-bootstrap' ;
1615
1716const Project = props => {
1817 const { darkMode, index } = props ;
1918 const [ projectData , setProjectData ] = useState ( props . projectData ) ;
20- const { projectName, isActive, isArchived = false , _id : projectId } = projectData ;
21- // const { projectName, isActive, isArchived, _id: projectId } = projectData;
19+ const { projectName = '' , isActive = false , _id : projectId } = projectData || { } ;
2220 const [ displayName , setDisplayName ] = useState ( projectName ) ;
23- const initialModalData = {
24- showModal : false ,
25- modalMessage : "" ,
26- modalTitle : "" ,
27- hasConfirmBtn : false ,
28- hasInactiveBtn : false ,
29- } ;
30- const [ category , setCategory ] = useState ( props . category || 'Unspecified' ) ; // Initialize with props or default
31-
32- const [ modalData , setModalData ] = useState ( initialModalData ) ;
33-
34- const onCloseModal = ( ) => {
35- setModalData ( initialModalData ) ;
36- if ( props . clearError ) props . clearError ( ) ;
37- } ;
21+ const [ category , setCategory ] = useState (
22+ props . projectData ?. category || props . category || 'Unspecified' ,
23+ ) ;
3824
3925 const canPutProject = props . hasPermission ( 'putProject' ) ;
4026 const canDeleteProject = props . hasPermission ( 'deleteProject' ) ;
@@ -100,74 +86,20 @@ const Project = props => {
10086 } ;
10187
10288 const onArchiveProject = ( ) => {
103- if ( isArchived ) {
104- setModalData ( {
105- showModal : true ,
106- modalMessage : `<p>Do you want to unarchive this ${ projectData . projectName } ?</p>` ,
107- modalTitle : CONFIRM_UNARCHIVE ,
108- hasConfirmBtn : true ,
109- hasInactiveBtn : isActive ,
110- } ) ;
111- } else {
112- setModalData ( {
113- showModal : true ,
114- modalMessage : `<p>Do you want to archive ${ projectData . projectName } ?</p>` ,
115- modalTitle : CONFIRM_ARCHIVE ,
116- hasConfirmBtn : true ,
117- hasInactiveBtn : isActive ,
118- } ) ;
119- }
120- }
121-
122- const setProjectInactive = ( ) => {
123- updateProject ( 'isActive' , ! isActive ) ;
124- onCloseModal ( ) ;
125- }
126- // const confirmArchive = () => {
127- // updateProject('isArchived', !isArchived);
128- // props.onProjectArchived(projectData);
129- // onCloseModal();
130- // };
131- const confirmArchive = async ( ) => {
132- // build the new project object we want on the server
133- const updatedProject = { ...projectData , isArchived : ! projectData . isArchived } ;
134-
135- // If parent provided an onProjectArchived handler, call it (parent will modify + refresh)
136- if ( typeof props . onProjectArchived === 'function' ) {
137- try {
138- await props . onProjectArchived ( updatedProject ) ;
139- } catch ( err ) {
140- // eslint-disable-next-line no-console
141- console . error ( 'Error archiving/unarchiving project:' , err ) ;
142- }
143- } else if ( typeof props . modifyProject === 'function' ) {
144- // fallback: if parent didn't pass handler, call modifyProject directly
145- try {
146- await props . modifyProject ( updatedProject ) ;
147- } catch ( err ) {
148- // eslint-disable-next-line no-console
149- console . error ( 'Fallback modifyProject error:' , err ) ;
150- }
151- }
152-
153- onCloseModal ( ) ;
89+ props . onClickArchiveBtn ( projectData ) ;
15490 } ;
15591
15692 useEffect ( ( ) => {
15793 setProjectData ( props . projectData ) ;
15894 setDisplayName ( props . projectData ?. projectName || '' ) ;
15995 setCategory ( props . projectData ?. category || props . category || 'Unspecified' ) ;
160- if ( props . projectData . category ) {
161- setCategory ( props . projectData . category ) ;
162- }
16396 } , [ props . projectData , props . category ] ) ;
16497
16598 return (
166- < >
167- < tr
168- className = { styles [ 'projects__tr' ] }
169- id = { `tr_${ props . projectId } ` }
170- >
99+ < tr
100+ className = { styles [ 'projects__tr' ] }
101+ id = { `tr_${ props . projectId } ` }
102+ >
171103 < th className = { styles [ 'projects__order--input' ] } scope = "row" >
172104 < div className = { darkMode ? 'text-light' : '' } > { index + 1 } </ div >
173105 </ th >
@@ -245,15 +177,25 @@ const Project = props => {
245177 </ td >
246178
247179 < td >
248- < NavItem tag = { Link } to = { `/project/members/${ projectId } ` } >
249- < button
250- type = "button"
251- className = "btn btn-outline-info"
252- style = { darkMode ? { } : boxStyle }
180+ < div style = { { position : 'relative' , display : 'inline-block' } } >
181+ < NavItem tag = { Link } to = { `/project/members/${ projectId } ` } className = "d-flex align-items-center" >
182+ < button
183+ type = "button"
184+ className = "btn btn-outline-info d-flex align-items-center project-member-btn"
185+ style = { darkMode ? { } : boxStyle }
186+ >
187+ < i className = "fa fa-users" aria-hidden = "true" />
188+ </ button >
189+ </ NavItem >
190+ < OverlayTrigger
191+ placement = "top"
192+ overlay = { < Tooltip > Active members</ Tooltip > }
253193 >
254- < i className = "fa fa-users" aria-hidden = "true" />
255- </ button >
256- </ NavItem >
194+ < span className = { styles [ 'project-member-badge' ] } >
195+ { props . activeMemberCounts }
196+ </ span >
197+ </ OverlayTrigger >
198+ </ div >
257199 </ td >
258200
259201 < td >
@@ -268,46 +210,20 @@ const Project = props => {
268210 </ NavItem >
269211 </ td >
270212
271-
272- { ( canDeleteProject ) ? (
273-
274- // <td>
275- // <button
276- // data-testid="delete-button"
277- // type="button"
278- // className="btn btn-outline-danger"
279- // onClick={onArchiveProject}
280- // style={darkMode ? {} : boxStyle}
281- // disabled={isArchived}
282- // >
283- // {ARCHIVE}
284- // </button>
285- // </td>
286- // ) : null}
287- // </tr>
288- // </>
289- < td >
290- < button
291- data-testid = "delete-button"
292- type = "button"
293- className = "btn btn-outline-danger"
294- style = { darkMode ? { borderColor : '#D2042D' } : boxStyle }
295- onClick = { onArchiveProject } >
296- { /* {ARCHIVE} */ }
297- { isArchived ? "UNARCHIVE" :"Archive" }
298- </ button >
299- </ td >
300- ) : null }
301- </ tr >
302- < ModalTemplate
303- isOpen = { modalData . showModal }
304- closeModal = { onCloseModal }
305- confirmModal = { modalData . hasConfirmBtn ? confirmArchive : null }
306- setInactiveModal = { modalData . hasInactiveBtn ? setProjectInactive : null }
307- modalMessage = { modalData . modalMessage }
308- modalTitle = { modalData . modalTitle }
309- />
310- </ >
213+ { canDeleteProject ? (
214+ < td >
215+ < button
216+ data-testid = "delete-button"
217+ type = "button"
218+ className = "btn btn-outline-danger"
219+ style = { darkMode ? { borderColor : '#D2042D' } : boxStyle }
220+ onClick = { onArchiveProject }
221+ >
222+ { ARCHIVE }
223+ </ button >
224+ </ td >
225+ ) : null }
226+ </ tr >
311227 ) ;
312228} ;
313229
@@ -329,6 +245,7 @@ Project.propTypes = {
329245 onClickProjectStatusBtn : PropTypes . func ,
330246 onClickArchiveBtn : PropTypes . func ,
331247 projectId : PropTypes . string ,
248+ activeMemberCounts : PropTypes . oneOfType ( [ PropTypes . number , PropTypes . string ] ) ,
332249} ;
333250
334251// Default props
@@ -341,7 +258,8 @@ Project.defaultProps = {
341258 onClickProjectStatusBtn : ( ) => { } ,
342259 onClickArchiveBtn : ( ) => { } ,
343260 projectId : '' ,
261+ activeMemberCounts : '' ,
344262} ;
345263
346264const mapStateToProps = state => state ;
347- export default connect ( mapStateToProps , { hasPermission, modifyProject, clearError } ) ( Project ) ;
265+ export default connect ( mapStateToProps , { hasPermission, modifyProject } ) ( Project ) ;
0 commit comments