Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
35dd3e0
feat: complete material utilization chart feature
SudheeshTD Nov 15, 2025
22e7ce6
Merge branch 'development' into Sudheesh-material-utilization-ratio-c…
SudheeshTD Nov 15, 2025
fc7188c
Added sorting functionality for Bought, Used, Available, and Wasted c…
Sriamshreddy000 Nov 26, 2025
841aa2b
Completed the error pointed out by a developer
SudheeshTD Jan 8, 2026
33c0b4a
fix:Merging with updated files after debugging
SudheeshTD Jan 8, 2026
4aea423
initial commit
KH07 Jan 11, 2026
437de8c
initial commit for jsx
KH07 Jan 12, 2026
aa9451c
add dark mode support for weekly grid
KH07 Jan 15, 2026
c5ec64e
dark mode fixes
uhakruthi1 Feb 1, 2026
9973b17
feat: Implement client-side field validation, add a submission succes…
csk731 Feb 6, 2026
4b0c806
fix: Allow fractional quantities greater than zero in purchase reques…
csk731 Feb 6, 2026
7f78bc3
css changes
uhakruthi1 Feb 7, 2026
6c32980
Merge branch 'development' into fix-darkmode-lessons-input
uhakruthi1 Feb 7, 2026
0f004b4
css changes
uhakruthi1 Feb 7, 2026
ca70ba4
Add issue export and improve export UI
uhakruthi1 Feb 7, 2026
d27fc13
css
uhakruthi1 Feb 7, 2026
9ad81af
css
uhakruthi1 Feb 8, 2026
543abc3
css
uhakruthi1 Feb 8, 2026
0a25020
checks
uhakruthi1 Feb 8, 2026
140af8f
style: Add dark mode styles for select elements in purchase request form
csk731 Feb 8, 2026
a46afab
chore(deps): bump axios from 1.11.0 to 1.13.5
dependabot[bot] Feb 11, 2026
59af406
Add user search functionality
Akshay-Jayaram Feb 12, 2026
eedd075
Merge pull request #4717 from OneCommunityGlobal/kristin-add-drop-dow…
one-community Feb 12, 2026
65417af
Merge pull request #4460 from OneCommunityGlobal/sriamsh_materiallist…
one-community Feb 13, 2026
7a302a0
Merge branch 'development' into Sudheesh-material-utilization-ratio-c…
SudheeshTD Feb 13, 2026
f61ca6a
Merge pull request #4808 from OneCommunityGlobal/fix-darkmode-lessons…
one-community Feb 13, 2026
ccfd8b2
Merge pull request #4809 from OneCommunityGlobal/bmd-issue-export
one-community Feb 13, 2026
3bbaf38
Merge pull request #4811 from OneCommunityGlobal/fix-issue-chart
one-community Feb 13, 2026
d1a8d3d
Merge pull request #4829 from OneCommunityGlobal/Akshay-Add-search-to…
one-community Feb 13, 2026
4f0b71a
Add tool detail panel interaction to Returned Late chart
Sriamshreddy000 Dec 19, 2025
7973280
Frontend: Convert PR Insights to module.css, convert floating point v…
Neeraj-Kondaveeti Nov 7, 2025
80ef486
Fix: Dark mode team dropdown visibility
Neeraj-Kondaveeti Nov 20, 2025
ed822d6
Venkataramanan fix: Formatted report bar color
Feb 13, 2026
684a00a
Merge pull request #4840 from OneCommunityGlobal/venkataramanan_fix_f…
one-community Feb 14, 2026
3be0101
Merge pull request #4607 from OneCommunityGlobal/sriamsh_returned_lat…
one-community Feb 14, 2026
cfe7d3f
Merge pull request #4401 from OneCommunityGlobal/Sudheesh-material-ut…
one-community Feb 14, 2026
95b7516
Merge pull request #4814 from OneCommunityGlobal/chaitanya-purchase-r…
one-community Feb 14, 2026
4483482
Merge pull request #4353 from OneCommunityGlobal/Neeraj_Fix_PR_Insights
one-community Feb 14, 2026
7fde245
Merge pull request #4823 from OneCommunityGlobal/dependabot/npm_and_y…
EvianTan Feb 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"ant-design": "^1.0.0",
"antd": "^5.27.6",
"assert": "^2.1.0",
"axios": "^1.11.0",
"axios": "^1.13.5",
"axios-mock-adapter": "^1.22.0",
"bootstrap": "^4.5.3",
"braces": "^3.0.3",
Expand Down
199 changes: 171 additions & 28 deletions src/components/BMDashboard/Issues/IssueDashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,18 @@ import {
FiTrash2,
FiCopy,
FiEdit,
FiDownload,
} from 'react-icons/fi';
import styles from './IssueDashboard.module.css';
import { Col, Row, Table } from 'reactstrap';
import {
Col,
Row,
Table,
UncontrolledDropdown,
DropdownToggle,
DropdownMenu,
DropdownItem,
} from 'reactstrap';
import { useDispatch, useSelector } from 'react-redux';
import {
copyIssue,
Expand All @@ -17,6 +26,8 @@ import {
renameIssue,
} from '~/actions/bmdashboard/issueActions';
import IssueHeader from './IssueHeader';
import { toast } from 'react-toastify';
import { jsPDF } from 'jspdf';

export default function IssueDashboard() {
const dispatch = useDispatch();
Expand All @@ -26,14 +37,18 @@ export default function IssueDashboard() {
const [currentPage, setCurrentPage] = useState(1);
const [menuOpen, setMenuOpen] = useState(null);
const itemsPerPage = 5;
const totalPages = Math.ceil(issues.length / itemsPerPage);
const displayIssues = issues;
const totalPages = Math.ceil(displayIssues.length / itemsPerPage);
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [showRenameModal, setShowRenameModal] = useState(false);
const [showCopyModal, setShowCopyModal] = useState(false);
const [selectedIssue, setSelectedIssue] = useState(null);
const [renameValue, setRenameValue] = useState('');

const currentItems = issues.slice((currentPage - 1) * itemsPerPage, currentPage * itemsPerPage);
const currentItems = displayIssues.slice(
(currentPage - 1) * itemsPerPage,
currentPage * itemsPerPage,
);

const toggleMenu = id => setMenuOpen(open => (open === id ? null : id));

Expand Down Expand Up @@ -77,6 +92,137 @@ export default function IssueDashboard() {
dispatch(fetchAllIssues());
}, [dispatch]);

const buildExportRows = sourceIssues => {
return (sourceIssues || []).map(issue => {
const assignedUser = issue.assignedTo
? `${issue.assignedTo.firstName || ''} ${issue.assignedTo.lastName || ''}`.trim()
: issue.assignedToName || issue.assignee || 'Unassigned';

const formatDate = value => {
if (!value) return '-';
const date = new Date(value);
if (Number.isNaN(date.getTime())) return `${value}`;
return date.toLocaleDateString();
};

return {
issueName: issue.name || issue.issueName || '-',
status: issue.status || issue.state || issue.issueStatus || '-',
priority: issue.priority || issue.severity || issue.issuePriority || '-',
assignedUser: assignedUser || '-',
createdDate: formatDate(
issue.createdDate || issue.createdAt || issue.openDate || issue.dateCreated,
),
lastUpdated: formatDate(issue.updatedDate || issue.updatedAt || issue.lastUpdated),
};
});
};

const exportHeaders = [
'Issue Name',
'Status',
'Priority',
'Assigned User',
'Created Date',
'Last Updated',
];

const downloadBlob = (blob, filename) => {
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = filename;
document.body.appendChild(link);
link.click();
link.remove();
window.URL.revokeObjectURL(url);
};

const handleExportCsv = () => {
const exportRows = buildExportRows(displayIssues);
if (exportRows.length === 0) {
toast.info('No issues available to export.');
return;
}
const escapeCsv = value => `"${String(value ?? '').replace(/"/g, '""')}"`;
const rows = [
exportHeaders,
...exportRows.map(row => [
row.issueName || '-',
row.status || '-',
row.priority || '-',
row.assignedUser || '-',
row.createdDate || '-',
row.lastUpdated || '-',
]),
];

const csvContent = rows.map(row => row.map(escapeCsv).join(',')).join('\n');
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
downloadBlob(blob, `issues-export-${new Date().toISOString().slice(0, 10)}.csv`);
toast.success('Issue export generated (CSV).');
};

const handleExportPdf = () => {
const exportRows = buildExportRows(displayIssues);
if (exportRows.length === 0) {
toast.info('No issues available to export.');
return;
}
const doc = new jsPDF({ orientation: 'portrait', unit: 'pt', format: 'a4' });
const pageWidth = doc.internal.pageSize.getWidth();
const startX = 40;
const startY = 50;
const rowHeight = 18;
const colWidths = [160, 70, 70, 110, 80, 80];
const truncate = (text, maxWidth) => {
if (doc.getTextWidth(text) <= maxWidth) return text;
let truncated = text;
while (truncated.length > 0 && doc.getTextWidth(`${truncated}…`) > maxWidth) {
truncated = truncated.slice(0, -1);
}
return `${truncated}…`;
};

doc.setFontSize(12);
doc.text('Issue Export', startX, startY - 20);
doc.setFontSize(9);

let x = startX;
exportHeaders.forEach((header, index) => {
const width = colWidths[index];
doc.text(truncate(header, width - 4), x, startY);
x += width;
});

let y = startY + rowHeight;
exportRows.forEach(row => {
if (y > doc.internal.pageSize.getHeight() - 40) {
doc.addPage();
y = 50;
}
const values = [
row.issueName || '-',
row.status || '-',
row.priority || '-',
row.assignedUser || '-',
row.createdDate || '-',
row.lastUpdated || '-',
];
let colX = startX;
values.forEach((value, index) => {
const width = colWidths[index];
doc.text(truncate(String(value ?? ''), width - 4), colX, y);
colX += width;
});
y += rowHeight;
});

const filename = `issues-export-${new Date().toISOString().slice(0, 10)}.pdf`;
doc.save(filename);
toast.success('Issue export generated (PDF).');
};

function getTimeSince(dateStr) {
const date = new Date(dateStr);
const now = new Date();
Expand Down Expand Up @@ -108,6 +254,28 @@ export default function IssueDashboard() {
<Col>
<h4 className={`fw-semibold ${darkMode ? 'text-light' : ''}`}>Issue Dashboard</h4>
</Col>
<Col className="d-flex justify-content-end">
<UncontrolledDropdown>
<DropdownToggle tag="button" className="btn btn-sm btn-primary" type="button">
<FiDownload className="me-2" />
Export
</DropdownToggle>
<DropdownMenu end className={`${darkMode ? styles.exportDropdownMenuDark : ''}`}>
<DropdownItem
onClick={handleExportCsv}
className={`${darkMode ? styles.exportDropdownItemDark : ''}`}
>
Export as CSV
</DropdownItem>
<DropdownItem
onClick={handleExportPdf}
className={`${darkMode ? styles.exportDropdownItemDark : ''}`}
>
Export as PDF
</DropdownItem>
</DropdownMenu>
</UncontrolledDropdown>
</Col>
</Row>

<div className={`${styles.issuesTableResponsive}`}>
Expand Down Expand Up @@ -239,31 +407,6 @@ export default function IssueDashboard() {
</button>
</li>

{Array.from({ length: totalPages }, (_, i) => {
const isActive = currentPage === i + 1;
let buttonClass = 'page-link';

if (darkMode) {
buttonClass += ' bg-dark text-light border-secondary';
}

if (isActive) {
buttonClass += darkMode ? ' bg-secondary' : ' bg-primary';
}

return (
<li key={i + 1} className={`page-item ${isActive ? 'active' : ''}`}>
<button
type="button"
className={buttonClass}
onClick={() => setCurrentPage(i + 1)}
>
{i + 1}
</button>
</li>
);
})}

<li className={`page-item ${currentPage === totalPages ? 'disabled' : ''}`}>
<button
type="button"
Expand Down
15 changes: 15 additions & 0 deletions src/components/BMDashboard/Issues/IssueDashboard.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,21 @@
background-color: #2f4157;
}

.exportDropdownMenuDark {
background-color: #1c2541;
border: 1px solid #3a506b;
}

.exportDropdownItemDark {
color: #ffffff !important;
}

.exportDropdownItemDark:hover,
.exportDropdownItemDark:focus {
background-color: #2d3b66 !important;
color: #ffffff !important;
}

.issueDashboardDropdownItemDark.textDanger {
color: #ff6b6b;
}
Expand Down
53 changes: 40 additions & 13 deletions src/components/BMDashboard/Issues/issueChart.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,51 @@
}

.issueChartSelect {
padding: 2px 10px;
font-size: 16px;
border-radius: 4px;
border: 1px solid #ccc;
background-color: #fff;
color: #333;
cursor: pointer;
transition: border-color 0.3s ease;
outline: none;
padding: 0;
border: none;
background: transparent;
margin-bottom: 20px;
}

.issueChartSelectDark {
border-color: #3d444d;
background-color: #22272e;
.filterMenuActions {
display: flex;
justify-content: space-between;
gap: 8px;
padding: 6px 10px 8px;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
position: static;
background: inherit;
}

.filterMenuButton {
background: transparent;
border: 1px solid #4caf50;
color: #4caf50;
padding: 4px 8px;
border-radius: 6px;
font-size: 12px;
cursor: pointer;
}

.activeFilterSummary {
width: 100%;
text-align: center;
font-size: 13px;
color: #5b6470;
margin-bottom: 6px;
font-weight: 600;
}

.issueChartEventContainerDark .activeFilterSummary {
color: #cfd7e3;
}

.issueChartSelectDark {
border: none;
background: transparent;
color: inherit;
}

/* Spacing between year groups within each issue type */
.issueChartYearGroup {
margin-bottom: 24px;
Expand Down Expand Up @@ -248,4 +275,4 @@
border-style: dashed;
opacity: 0.7;
}
}
}
Loading