Skip to content

Commit fcaa55e

Browse files
Merge pull request #3415 from OneCommunityGlobal/guirong_add_title_sort_functions_wbspage
Guirong taking over for Mohammad- Project Title, Sorting task button, and Spaces between back button and return title
2 parents d516366 + e7f81c1 commit fcaa55e

6 files changed

Lines changed: 139 additions & 66 deletions

File tree

src/components/Projects/WBS/AddWBS/AddWBS.jsx

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,30 @@ import { addNewWBS } from './../../../../actions/wbs';
99
import hasPermission from 'utils/permissions';
1010

1111
const AddWBS = props => {
12-
const [showAddButton, setShowAddButton] = useState(false);
12+
const darkMode = props.state.theme.darkMode;
1313
const [newName, setNewName] = useState('');
14+
const [showAddButton, setShowAddButton] = useState(false);
1415
const canPostWBS = props.hasPermission('postWbs');
15-
const { darkMode } = props.state.theme;
1616

17-
const changeNewName = newName => {
18-
if (newName.length !== 0) {
19-
setShowAddButton(true);
20-
} else {
17+
const changeNewName = value => {
18+
setNewName(value);
19+
setShowAddButton(value.length >= 3);
20+
};
21+
22+
const handleAddWBS = () => {
23+
if (newName.length >= 3) {
24+
props.addNewWBS(props.projectId, newName);
25+
setNewName('');
2126
setShowAddButton(false);
2227
}
23-
setNewName(newName);
2428
};
2529

2630
return (
2731
<>
2832
{canPostWBS ? (
2933
<div className="input-group" id="new_project">
3034
<div className="input-group-prepend">
31-
<span className={`input-group-text ${darkMode ? 'bg-yinmn-blue border-0 text-light' : ''}`}>Add new WBS</span>
35+
<span className={`input-group-text ${darkMode ? 'bg-yinmn-blue border-0 text-light' : ''}`}>Add new WBS</span>
3236
</div>
3337

3438
<input
@@ -44,11 +48,18 @@ const AddWBS = props => {
4448
<button
4549
className="btn btn-outline-primary"
4650
type="button"
47-
onClick={e => props.addNewWBS(newName, props.projectId)}
51+
onClick={handleAddWBS}
52+
data-testid="add-wbs-button"
4853
>
4954
<i className="fa fa-plus" aria-hidden="true"></i>
5055
</button>
5156
) : null}
57+
<button className="btn btn-primary" type="button" onClick={props.onSortAscending}>
58+
A ↓
59+
</button>
60+
<button className="btn btn-primary" type="button" onClick={props.onSortDescending}>
61+
D ↑
62+
</button>
5263
</div>
5364
</div>
5465
) : null}

src/components/Projects/WBS/AddWBS/__tests__/AddWBS.test.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ describe("AddWBS component structure", () => {
6666
});
6767

6868
test("button should not be in the document when the input field is empty", () => {
69-
expect(screen.queryByRole('button')).toBeNull();
69+
expect(screen.queryByTestId('add-wbs-button')).toBeNull();
7070
});
7171

7272
test("user should be able to type in the input field", () => {
@@ -76,7 +76,7 @@ describe("AddWBS component structure", () => {
7676

7777
test("button should appear when user types in the input field", () => {
7878
typeIntoInput({ input: '123' });
79-
expect(screen.queryByRole('button')).not.toBeNull();
79+
expect(screen.queryByTestId('add-wbs-button')).not.toBeNull();
8080
});
8181
});
8282

src/components/Projects/WBS/WBSItem/WBSItem.jsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,16 @@ const WBSItem = ({ darkMode, index, name, wbsId, projectId, getPopupById, delete
3434
return (
3535
<React.Fragment>
3636
<tr>
37-
<th scope="row">
38-
<div>{index}</div>
37+
<th scope="row" style={{ width: '150px', textAlign: 'center' }}>
38+
{index}
3939
</th>
40-
<td className="members__name taskName">
40+
<td style={{ textAlign: 'left' }}>
4141
<NavItem tag={Link} to={`/wbs/tasks/${wbsId}/${projectId}/${name}`} className={darkMode ? 'text-azure' : ''}>
4242
{name}
4343
</NavItem>
4444
</td>
45-
{canDeleteWBS ? (
46-
<td className="members__assign">
45+
<td style={{ width: '50px', textAlign: 'center' }}>
46+
{canDeleteWBS ? (
4747
<button
4848
className="btn btn-outline-danger btn-sm"
4949
type="button"
@@ -52,8 +52,8 @@ const WBSItem = ({ darkMode, index, name, wbsId, projectId, getPopupById, delete
5252
>
5353
<i className="fa fa-minus" aria-hidden="true"></i>
5454
</button>
55-
</td>
56-
) : null}
55+
) : null}
56+
</td>
5757
</tr>
5858

5959
<ModalDelete

src/components/Projects/WBS/WBSItem/__tests__/WBSItem.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,12 @@ describe('WBSItem component', () => {
7878
it('check if deleteWBS html elements get displayed in virtual DOM when the permission is not present', () => {
7979
store.getState().auth.user.permissions.frontPermissions = [];
8080
const { container } = renderComponent(index, key, wbsId, projectId, name);
81-
expect(container.querySelector('.members__assign')).not.toBeInTheDocument();
81+
expect(screen.queryByRole('button', { class: 'btn btn-outline-danger btn-sm' })).not.toBeInTheDocument();
8282
});
8383

8484
it('check if deleteWBS html elements get displayed in virtual DOM when the permission is present', () => {
8585
const { container } = renderComponent(index, key, wbsId, projectId, name);
86-
expect(container.querySelector('.members__assign')).toBeInTheDocument();
86+
expect(screen.queryByRole('button', { class: 'btn btn-outline-danger btn-sm' })).toBeInTheDocument();
8787
});
8888
it('check if modal opens when button is clicked', async () => {
8989
axios.get.mockResolvedValue({

src/components/Projects/WBS/wbs.jsx

Lines changed: 72 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -26,72 +26,98 @@ const WBS = props => {
2626
}, [projectId]);
2727

2828
useEffect(() => {
29+
if (!props.state.wbs.WBSItems) return;
30+
2931
const sortedItems = [...props.state.wbs.WBSItems];
3032
if (sortOrder === 'asc') {
31-
sortedItems.sort((a, b) => a.wbsName.localeCompare(b.wbsName));
33+
sortedItems.sort((a, b) => a.wbsName.toLowerCase().localeCompare(b.wbsName.toLowerCase()));
3234
} else if (sortOrder === 'desc') {
33-
sortedItems.sort((a, b) => b.wbsName.localeCompare(a.wbsName));
35+
sortedItems.sort((a, b) => b.wbsName.toLowerCase().localeCompare(a.wbsName.toLowerCase()));
3436
} else {
3537
sortedItems.sort((a, b) => new Date(b.modifiedDatetime) - new Date(a.modifiedDatetime));
3638
}
3739
setSortedWBSItems(sortedItems);
3840
}, [props.state.wbs.WBSItems, sortOrder]);
3941

4042
const handleSortChange = (newOrder) => {
41-
setSortOrder(newOrder);
43+
setSortOrder(prevOrder => prevOrder === newOrder ? 'recent' : newOrder);
4244
};
4345

4446
return (
4547
<React.Fragment>
4648
<div className={darkMode ? 'bg-oxford-blue text-light' : ''} style={{minHeight: "100%"}}>
4749
<div className={`container pt-2 ${darkMode ? 'bg-yinmn-blue-light text-light' : ''}`}>
4850
<nav aria-label="breadcrumb">
49-
<ol className={`breadcrumb ${darkMode ? 'bg-space-cadet' : ''}`} style={darkMode ? boxStyleDark : boxStyle}>
50-
<NavItem tag={Link} to={`/projects/`}>
51-
<button type="button" className="btn btn-secondary mr-2" style={darkMode ? boxStyleDark : boxStyle}>
52-
<i className="fa fa-chevron-circle-left" aria-hidden="true"></i>
53-
</button>
54-
<span style={{ marginLeft: '8px', marginRight:'8px' }}>Return to Project List</span>
55-
</NavItem>
56-
<div id="member_project__name" style={{ flex: '1', textAlign: 'center', fontWeight: 'bold', display: 'flex',
57-
alignItems: 'center', justifyContent: 'center', }}>Project Name: {projectName}</div>
58-
</ol>
51+
<div className={`d-flex align-items-center breadcrumb ${darkMode ? 'bg-space-cadet' : ''}`}
52+
style={{
53+
...darkMode ? boxStyleDark : boxStyle,
54+
backgroundColor: darkMode ? '' : '#E9ECEF',
55+
margin: '0 0 16px',
56+
padding: '12px 16px',
57+
position: 'relative'
58+
}}>
59+
<div style={{ position: 'absolute', left: '1rem' }}>
60+
<NavItem tag={Link} to={`/projects/`}>
61+
<button type="button" className="btn btn-secondary" style={darkMode ? boxStyleDark : boxStyle}>
62+
<i className="fa fa-chevron-circle-left" aria-hidden="true"></i>
63+
<span style={{ marginLeft: '8px' }}>Return to Project List</span>
64+
</button>
65+
</NavItem>
66+
</div>
67+
<div style={{
68+
width: '100%',
69+
textAlign: 'center',
70+
fontWeight: 'bold',
71+
fontSize: '1.5rem'
72+
}}>{projectName}</div>
73+
</div>
5974
</nav>
6075

61-
<AddWBS projectId={projectId} />
76+
<AddWBS
77+
projectId={projectId}
78+
onSortAscending={() => handleSortChange('asc')}
79+
onSortDescending={() => handleSortChange('desc')}
80+
/>
6281

63-
<table className={`table table-bordered table-responsive-sm ${darkMode ? 'bg-yinmn-blue text-light dark-mode' : '' }`}>
64-
<thead>
65-
<tr className={darkMode ? 'bg-space-cadet' : ''}>
66-
<th scope="col" id="members__order">
67-
#
68-
</th>
69-
<th scope="col" id="members__name">
70-
Name
71-
<span style={{ marginLeft: '8px', cursor: 'pointer' }}>
72-
<i
73-
className={`fa ${sortOrder === 'recent' ? 'fa-sort' : sortOrder === 'asc' ? 'fa-sort-up' : 'fa-sort-down'}`}
74-
onClick={() => handleSortChange(sortOrder === 'asc' ? 'desc' : sortOrder === 'desc' ? 'recent' : 'asc')}
75-
></i>
76-
</span>
77-
</th>
78-
<th scope="col" id="members__name"></th>
79-
</tr>
80-
</thead>
81-
<tbody>
82-
{sortedWBSItems.map((item, i) =>
83-
item ? (
84-
<WBSItem
85-
index={i + 1}
86-
key={item._id}
87-
wbsId={item._id}
88-
projectId={projectId}
89-
name={item.wbsName}
90-
/>
91-
) : null,
92-
)}
93-
</tbody>
94-
</table>
82+
{!props.state.wbs.WBSItems ? (
83+
<div className="d-flex justify-content-center align-items-center pt-4">
84+
<div className="spinner-border text-primary" role="status">
85+
<span className="sr-only">Loading...</span>
86+
</div>
87+
</div>
88+
) : (
89+
<table className={`table table-bordered table-responsive-sm ${darkMode ? 'bg-yinmn-blue text-light dark-mode' : '' }`}>
90+
<thead>
91+
<tr className={darkMode ? 'bg-space-cadet' : ''}>
92+
<th scope="col" style={{ width: '150px' }}>#</th>
93+
<th scope="col" style={{ textAlign: 'left' }}>
94+
Name
95+
<span style={{ marginLeft: '8px', cursor: 'pointer' }}>
96+
<i
97+
className={`fa ${sortOrder === 'recent' ? 'fa-sort' : sortOrder === 'asc' ? 'fa-sort-up' : 'fa-sort-down'}`}
98+
onClick={() => handleSortChange(sortOrder === 'asc' ? 'desc' : sortOrder === 'desc' ? 'recent' : 'asc')}
99+
></i>
100+
</span>
101+
</th>
102+
<th scope="col" style={{ width: '50px' }}></th>
103+
</tr>
104+
</thead>
105+
<tbody>
106+
{sortedWBSItems.map((item, i) =>
107+
item ? (
108+
<WBSItem
109+
index={i + 1}
110+
key={item._id}
111+
wbsId={item._id}
112+
projectId={projectId}
113+
name={item.wbsName}
114+
darkMode={darkMode}
115+
/>
116+
) : null,
117+
)}
118+
</tbody>
119+
</table>
120+
)}
95121
</div>
96122
</div>
97123
</React.Fragment>

src/styles/print.css

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
@media print {
2+
/* 确保导航栏在打印时保持正常大小 */
3+
.navbar,
4+
.navbar * {
5+
transform: none !important;
6+
-webkit-transform: none !important;
7+
-moz-transform: none !important;
8+
-ms-transform: none !important;
9+
scale: 1 !important;
10+
zoom: 1 !important;
11+
}
12+
13+
/* 设置导航栏打印时的固定尺寸 */
14+
.navbar {
15+
width: 100% !important;
16+
height: auto !important;
17+
min-height: 60px !important;
18+
position: relative !important;
19+
display: flex !important;
20+
align-items: center !important;
21+
}
22+
23+
/* 确保导航栏内容正确显示 */
24+
.navbar-content {
25+
display: flex !important;
26+
align-items: center !important;
27+
justify-content: space-between !important;
28+
width: 100% !important;
29+
}
30+
31+
/* 防止文本溢出 */
32+
.navbar * {
33+
white-space: normal !important;
34+
overflow: visible !important;
35+
}
36+
}

0 commit comments

Comments
 (0)