@mpsinc/task-component
Version:
A flexible React component for displaying task cards with time tracking
173 lines (161 loc) • 7.91 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import ReactMarkdown from 'react-markdown';
function styleInject(css, ref) {
if ( ref === void 0 ) ref = {};
var insertAt = ref.insertAt;
if (!css || typeof document === 'undefined') { return; }
var head = document.head || document.getElementsByTagName('head')[0];
var style = document.createElement('style');
style.type = 'text/css';
if (insertAt === 'top') {
if (head.firstChild) {
head.insertBefore(style, head.firstChild);
} else {
head.appendChild(style);
}
} else {
head.appendChild(style);
}
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
}
var css_248z = "/* Main task card container */\n.task-card {\n border-radius: 8px;\n padding: 16px;\n margin: 12px 0;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n transition: all 0.2s ease-in-out;\n}\n\n.task-card:hover {\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);\n transform: translateY(-2px);\n}\n\n/* Task main section */\n.task-main {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 12px;\n}\n\n.task-main h1 {\n font-size: 1.25rem;\n margin: 0;\n font-weight: 600;\n flex: 1;\n}\n\n/* Task controls section */\n.task-controls {\n display: flex;\n gap: 8px;\n align-items: center;\n}\n\n.time-estimate {\n font-size: 0.9rem;\n opacity: 0.8;\n}\n\n.start-button,\n.complete-button {\n padding: 6px 12px;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 0.9rem;\n transition: background-color 0.2s;\n}\n\n.start-button {\n background-color: #4CAF50;\n color: white;\n}\n\n.complete-button {\n background-color: #2196F3;\n color: white;\n}\n\n.start-button:hover,\n.complete-button:hover {\n opacity: 0.9;\n}\n\n/* Task metadata section */\n.task-metadata {\n display: flex;\n gap: 16px;\n font-size: 0.9rem;\n margin-bottom: 12px;\n flex-wrap: wrap;\n}\n\n.task-id {\n opacity: 0.7;\n}\n\n.task-time {\n display: inline-block;\n}\n\n.task-remaining {\n display: inline-block;\n}\n\n.task-remaining.negative {\n color: #f44336;\n}\n\n/* Task description section */\n.task-description {\n font-size: 0.95rem;\n line-height: 1.5;\n margin-top: 12px;\n padding-top: 12px;\n border-top: 1px solid rgba(0, 0, 0, 0.1);\n}\n\n/* Markdown content styling */\n.task-description p {\n margin: 0 0 8px 0;\n}\n\n.task-description p:last-child {\n margin-bottom: 0;\n}\n\n.task-title {\n font-size: 1.5rem;\n line-height: 2.5;\n text-align: left;\n}\n\n/* Responsive design */\n@media (max-width: 600px) {\n .task-main {\n flex-direction: column;\n gap: 12px;\n }\n\n .task-controls {\n width: 100%;\n justify-content: flex-start;\n }\n\n .task-metadata {\n flex-direction: column;\n gap: 8px;\n }\n} ";
styleInject(css_248z);
const TaskComponent = _ref => {
let {
task,
taskColors,
backgroundColor
} = _ref;
// Define background and font colors for different task statuses
const default_taskColors = taskColors !== null && taskColors !== void 0 ? taskColors : {
backlog: {
background: '#f8f9fa',
// Light gray for backlog tasks
font: '#000000' // Black text for good contrast
},
planned: {
background: '#e8fffe',
// Light blue for planned tasks
font: '#000000' // Black text for good contrast
},
closed: {
background: '#bbbbbb',
// Different gray for completed tasks
font: '#000000' // Black text for good contrast
}
};
const formatTime = time => {
if (!time) return '--:--';
return time;
};
// Get the appropriate background color based on task status
const getdefault_backgroundColor = status => {
const colorScheme = default_taskColors[status] || default_taskColors.backlog;
return colorScheme.background;
};
// Get text color based on background
const getTextColor = status => {
const colorScheme = default_taskColors[status] || default_taskColors.backlog;
return colorScheme.font;
};
const getRemainingTimeClass = remainingTime => {
return remainingTime < 0 ? 'task-remaining negative' : 'task-remaining';
};
const formatRemainingTime = remainingTime => {
const absoluteTime = Math.abs(Math.round(remainingTime));
if (absoluteTime < 60) {
return "".concat(remainingTime < 0 ? '-' : '').concat(absoluteTime, " min");
}
const hours = Math.floor(absoluteTime / 60);
const minutes = absoluteTime % 60;
let formattedTime = '';
if (hours > 0) {
formattedTime += "".concat(hours, "h");
}
if (minutes > 0) {
formattedTime += " ".concat(minutes, "m");
}
return "".concat(remainingTime < 0 ? '-' : '').concat(formattedTime);
};
const renderActionButtons = status => {
if (status === 'closed') {
return null; // Don't show any buttons for completed tasks
}
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("button", {
className: "start-button"
}, "\u25B6 Start"), /*#__PURE__*/React.createElement("button", {
className: "complete-button"
}, "\u2713 Complete"));
};
const renderAnticipatedTime = (status, startTime, endTime) => {
if (status === 'closed') {
return null;
}
return /*#__PURE__*/React.createElement("span", {
className: "task-time"
}, "Anticipated: ", formatTime(startTime), " - ", formatTime(endTime));
};
const renderRemainingTime = (status, remainingTime) => {
if (status === 'closed') {
return null;
}
return /*#__PURE__*/React.createElement("span", {
className: getRemainingTimeClass(remainingTime)
}, "Remaining: ", formatRemainingTime(remainingTime));
};
return /*#__PURE__*/React.createElement("div", {
className: "task-card",
style: {
default_backgroundColor: getdefault_backgroundColor(task.status),
color: getTextColor(task.status)
}
}, /*#__PURE__*/React.createElement("div", {
className: "task-main"
}, /*#__PURE__*/React.createElement("h1", {
className: "task-title"
}, task.title), /*#__PURE__*/React.createElement("div", {
className: "task-controls"
}, /*#__PURE__*/React.createElement("span", {
className: "time-estimate"
}, task.estimated_time, " min estimated"), renderActionButtons(task.status))), /*#__PURE__*/React.createElement("div", {
className: "task-metadata"
}, /*#__PURE__*/React.createElement("span", {
className: "task-id"
}, "ID: ", task.id), renderAnticipatedTime(task.status, task.anticipated_start_time, task.anticipated_end_time), renderRemainingTime(task.status, task.remaining_time)), /*#__PURE__*/React.createElement("div", {
className: "task-description"
}, /*#__PURE__*/React.createElement(ReactMarkdown, null, task.description)));
};
TaskComponent.propTypes = {
task: PropTypes.shape({
id: PropTypes.number.isRequired,
title: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
estimated_time: PropTypes.number.isRequired,
anticipated_start_time: PropTypes.string,
anticipated_end_time: PropTypes.string,
remaining_time: PropTypes.number.isRequired,
status: PropTypes.string.isRequired
}).isRequired,
backgroundColor: PropTypes.string,
taskColors: PropTypes.shape({
backlog: PropTypes.shape({
background: PropTypes.string,
font: PropTypes.string
}),
planned: PropTypes.shape({
background: PropTypes.string,
font: PropTypes.string
}),
closed: PropTypes.shape({
background: PropTypes.string,
font: PropTypes.string
})
})
};
export { TaskComponent };
//# sourceMappingURL=index.esm.js.map