@activecollab/components 2.0.365 → 2.0.366
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/presentation/listView/columns.js +967 -0
- package/dist/cjs/presentation/listView/columns.js.map +1 -0
- package/dist/esm/presentation/listView/columns.d.ts +53 -0
- package/dist/esm/presentation/listView/columns.d.ts.map +1 -0
- package/dist/esm/presentation/listView/columns.js +855 -0
- package/dist/esm/presentation/listView/columns.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,967 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isOverdue = exports.formatDateRange = exports.dayOffset = exports.TimeCell = exports.LabelsProvider = exports.LabelCell = exports.DateCell = void 0;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _momentTimezone = _interopRequireDefault(require("moment-timezone"));
|
|
9
|
+
var _styledComponents = _interopRequireWildcard(require("styled-components"));
|
|
10
|
+
var _Button = require("../../components/Button");
|
|
11
|
+
var _Checkbox = require("../../components/Checkbox");
|
|
12
|
+
var _Chip = require("../../components/Chip");
|
|
13
|
+
var _DatePicker = require("../../components/DatePicker");
|
|
14
|
+
var _Dot = require("../../components/Dot");
|
|
15
|
+
var _Icons = require("../../components/Icons");
|
|
16
|
+
var _Input = require("../../components/Input");
|
|
17
|
+
var _Label = require("../../components/Label");
|
|
18
|
+
var _Modal = require("../../components/Modal");
|
|
19
|
+
var _ProgressPie = require("../../components/ProgressPie");
|
|
20
|
+
var _Select = require("../../components/Select");
|
|
21
|
+
var _SelectTrigger = require("../../components/SelectTrigger");
|
|
22
|
+
var _Textarea = require("../../components/Textarea");
|
|
23
|
+
var _Transitions = require("../../components/Transitions");
|
|
24
|
+
var _Typography = require("../../components/Typography");
|
|
25
|
+
var _utils = require("../../utils");
|
|
26
|
+
var _excluded = ["open", "className", "children"],
|
|
27
|
+
_excluded2 = ["labels"],
|
|
28
|
+
_excluded3 = ["value", "tooltip", "overdue"];
|
|
29
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
30
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
|
|
31
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
32
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
33
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
34
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
35
|
+
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
36
|
+
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
37
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
38
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
39
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
40
|
+
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
41
|
+
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
42
|
+
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
43
|
+
function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
|
|
44
|
+
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
|
|
45
|
+
/**
|
|
46
|
+
* Interactive list-view column cells for the `Presentation/List view` story —
|
|
47
|
+
* the Label, Date and Time cells with their editors, mirroring
|
|
48
|
+
* `new-activecollab-front/src/components/ListView/columns/*`:
|
|
49
|
+
*
|
|
50
|
+
* - **Label** (`columns/Label.js` + `LabelTrigger`/`LabelItems`): chips for 1–2
|
|
51
|
+
* labels, dots from 3 up, `+N` past 8, and a DS `Select` picker (search,
|
|
52
|
+
* colored dot + label name in the label's own colour, checkbox, "+ Add new").
|
|
53
|
+
* - **Date** (`columns/Date.js`): a range trigger that formats like the app
|
|
54
|
+
* (`formatDateRange`), turns red when overdue, and opens the DS `DatePicker`
|
|
55
|
+
* in atomic mode so it gets the Save / ✕ / Clear footer.
|
|
56
|
+
* - **Time** (`columns/Time.js`): `ProgressPie` + `tracked/estimated`, coloured
|
|
57
|
+
* by how much of the estimate is used, opening the "Add Time Record" dialog.
|
|
58
|
+
*
|
|
59
|
+
* Everything is local state — there is no store, so edits live as long as the
|
|
60
|
+
* story is mounted.
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
var SANS = '-apple-system, BlinkMacSystemFont, "Inter", "Roboto", "Helvetica Neue", Arial, sans-serif';
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* CSS-only tooltip, mirroring the app's `.c-cooltip.c-cooltip--top` (see
|
|
67
|
+
* `styles/c_cooltip.less`). The list renders hundreds of these, and the DS
|
|
68
|
+
* `Tooltip` mounts a Portal + Popper per instance — far too heavy per cell — so
|
|
69
|
+
* the cells use `data-tooltip` exactly like the real list view does.
|
|
70
|
+
*/
|
|
71
|
+
var CooltipStyle = (0, _styledComponents.css)(["&[data-tooltip],[data-tooltip]{position:relative;}&[data-tooltip]::after,[data-tooltip]::after{pointer-events:none;content:attr(data-tooltip);position:absolute;bottom:calc(100% + 0.5rem);left:50%;transform:scale(0.85) translateX(-50%);transform-origin:0;opacity:0;max-width:12.5rem;padding:0.25rem 0.625rem;border-radius:0.5rem;background-color:rgba(0,0,0,0.9);color:#fff;font-family:", ";font-size:0.75rem;font-weight:500;line-height:1.3;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}@media (hover:hover){&[data-tooltip]:not(:focus):hover::after,[data-tooltip]:not(:focus):hover::after{transition:opacity 0.15s ease,transform 0.15s ease;transition-delay:0.6s;transform:scale(1) translateX(-50%);opacity:1;}}"], SANS);
|
|
72
|
+
|
|
73
|
+
/* ================================================================== */
|
|
74
|
+
/* Shared trigger button (mirrors ListView/ListButton.js) */
|
|
75
|
+
/* ================================================================== */
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* `Menu` clones its `target` and injects `open`, so — like the app's
|
|
79
|
+
* `ListButton` — the trigger has to swallow that prop and turn it into the DS
|
|
80
|
+
* `active` state plus an `.open` class the row CSS uses to keep the trigger
|
|
81
|
+
* visible while its menu is open.
|
|
82
|
+
*/
|
|
83
|
+
var ListButtonBase = /*#__PURE__*/(0, _react.forwardRef)(function (_ref, ref) {
|
|
84
|
+
var open = _ref.open,
|
|
85
|
+
className = _ref.className,
|
|
86
|
+
children = _ref.children,
|
|
87
|
+
rest = _objectWithoutProperties(_ref, _excluded);
|
|
88
|
+
return /*#__PURE__*/_react.default.createElement(_Button.Button, _extends({}, rest, {
|
|
89
|
+
ref: ref,
|
|
90
|
+
type: "button",
|
|
91
|
+
variant: "text gray",
|
|
92
|
+
active: open,
|
|
93
|
+
className: "list-button".concat(className ? " ".concat(className) : "").concat(open ? " open" : "")
|
|
94
|
+
}), children);
|
|
95
|
+
});
|
|
96
|
+
ListButtonBase.displayName = "ListButtonBase";
|
|
97
|
+
|
|
98
|
+
/*
|
|
99
|
+
* The DS Button treats a single element child as "icon only" — inline-flex,
|
|
100
|
+
* centered, a fixed 32px wide and `overflow: hidden`, which squashes a date or
|
|
101
|
+
* a chip. `&&` doubles the specificity so these rules win over that block; the
|
|
102
|
+
* button then hugs its content and the column's `justify-content: flex-end`
|
|
103
|
+
* parks it against the right edge.
|
|
104
|
+
*/
|
|
105
|
+
var StyledListButton = (0, _styledComponents.default)(ListButtonBase).withConfig({
|
|
106
|
+
displayName: "columns__StyledListButton",
|
|
107
|
+
componentId: "sc-9f4385-0"
|
|
108
|
+
})(["&&{display:inline-flex;align-items:center;justify-content:flex-end;width:auto;min-width:0;max-width:100%;padding:4px 8px;overflow:visible;text-align:right;}", ""], CooltipStyle);
|
|
109
|
+
|
|
110
|
+
/* ================================================================== */
|
|
111
|
+
/* Labels */
|
|
112
|
+
/* ================================================================== */
|
|
113
|
+
|
|
114
|
+
/* The project's label catalogue. Names are uppercase, like in the app. */
|
|
115
|
+
var INITIAL_LABELS = [{
|
|
116
|
+
id: "in-progress",
|
|
117
|
+
name: "IN PROGRESS",
|
|
118
|
+
color: "#CDE8F5",
|
|
119
|
+
textColor: "#3D7F9E"
|
|
120
|
+
}, {
|
|
121
|
+
id: "needs-review",
|
|
122
|
+
name: "NEEDS REVIEW",
|
|
123
|
+
color: "#F9E79F",
|
|
124
|
+
textColor: "#A98A17"
|
|
125
|
+
}, {
|
|
126
|
+
id: "approved",
|
|
127
|
+
name: "APPROVED",
|
|
128
|
+
color: "#A9DFBF",
|
|
129
|
+
textColor: "#3D9A6A"
|
|
130
|
+
}, {
|
|
131
|
+
id: "blocked",
|
|
132
|
+
name: "BLOCKED",
|
|
133
|
+
color: "#F1948A",
|
|
134
|
+
textColor: "#B5544C"
|
|
135
|
+
}, {
|
|
136
|
+
id: "on-hold",
|
|
137
|
+
name: "ON HOLD",
|
|
138
|
+
color: "#F5CBA7",
|
|
139
|
+
textColor: "#B4713A"
|
|
140
|
+
}, {
|
|
141
|
+
id: "idea",
|
|
142
|
+
name: "IDEA",
|
|
143
|
+
color: "#FAD7A0",
|
|
144
|
+
textColor: "#B98A2B"
|
|
145
|
+
}, {
|
|
146
|
+
id: "todo",
|
|
147
|
+
name: "TODO",
|
|
148
|
+
color: "#D6EAF8",
|
|
149
|
+
textColor: "#4A87B5"
|
|
150
|
+
}, {
|
|
151
|
+
id: "research",
|
|
152
|
+
name: "RESEARCH",
|
|
153
|
+
color: "#D7BDE2",
|
|
154
|
+
textColor: "#8155A0"
|
|
155
|
+
}, {
|
|
156
|
+
id: "accessibility",
|
|
157
|
+
name: "ACCESSIBILITY",
|
|
158
|
+
color: "#AED6F1",
|
|
159
|
+
textColor: "#3E7CA6"
|
|
160
|
+
}, {
|
|
161
|
+
id: "copy",
|
|
162
|
+
name: "COPY",
|
|
163
|
+
color: "#F5B7B1",
|
|
164
|
+
textColor: "#B5544C"
|
|
165
|
+
}, {
|
|
166
|
+
id: "design-debt",
|
|
167
|
+
name: "DESIGN DEBT",
|
|
168
|
+
color: "#E8DAEF",
|
|
169
|
+
textColor: "#7D5A99"
|
|
170
|
+
}, {
|
|
171
|
+
id: "qa",
|
|
172
|
+
name: "QA",
|
|
173
|
+
color: "#A3E4D7",
|
|
174
|
+
textColor: "#34907C"
|
|
175
|
+
}, {
|
|
176
|
+
id: "client-input",
|
|
177
|
+
name: "CLIENT INPUT",
|
|
178
|
+
color: "#FDEBD0",
|
|
179
|
+
textColor: "#B08640"
|
|
180
|
+
}, {
|
|
181
|
+
id: "mobile",
|
|
182
|
+
name: "MOBILE",
|
|
183
|
+
color: "#D1F2EB",
|
|
184
|
+
textColor: "#2E8B7A"
|
|
185
|
+
}, {
|
|
186
|
+
id: "performance",
|
|
187
|
+
name: "PERFORMANCE",
|
|
188
|
+
color: "#FCF3CF",
|
|
189
|
+
textColor: "#A38B1E"
|
|
190
|
+
}, {
|
|
191
|
+
id: "asset",
|
|
192
|
+
name: "ASSET",
|
|
193
|
+
color: "#D5D8DC",
|
|
194
|
+
textColor: "#6B7378"
|
|
195
|
+
}];
|
|
196
|
+
|
|
197
|
+
/* Colours handed out to labels created from the picker's "+ Add new" row. */
|
|
198
|
+
var NEW_LABEL_COLORS = [{
|
|
199
|
+
color: "#F9E0F2",
|
|
200
|
+
textColor: "#A4569B"
|
|
201
|
+
}, {
|
|
202
|
+
color: "#DDEBFB",
|
|
203
|
+
textColor: "#4E7BA8"
|
|
204
|
+
}, {
|
|
205
|
+
color: "#E4F6D8",
|
|
206
|
+
textColor: "#5F9440"
|
|
207
|
+
}, {
|
|
208
|
+
color: "#FDE2D5",
|
|
209
|
+
textColor: "#B36A45"
|
|
210
|
+
}];
|
|
211
|
+
var LabelsContext = /*#__PURE__*/(0, _react.createContext)({
|
|
212
|
+
labels: INITIAL_LABELS,
|
|
213
|
+
labelsById: {},
|
|
214
|
+
addLabel: function addLabel() {
|
|
215
|
+
return "";
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Holds the label catalogue for the whole list, so a label created in one
|
|
221
|
+
* task's picker shows up in every other picker too.
|
|
222
|
+
*/
|
|
223
|
+
var LabelsProvider = exports.LabelsProvider = function LabelsProvider(_ref2) {
|
|
224
|
+
var children = _ref2.children;
|
|
225
|
+
var _useState = (0, _react.useState)(INITIAL_LABELS),
|
|
226
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
227
|
+
labels = _useState2[0],
|
|
228
|
+
setLabels = _useState2[1];
|
|
229
|
+
var addLabel = (0, _react.useCallback)(function (name) {
|
|
230
|
+
var trimmed = name.trim().toUpperCase();
|
|
231
|
+
var id = "label-".concat(trimmed.toLowerCase().replace(/[^a-z0-9]+/g, "-"));
|
|
232
|
+
setLabels(function (prev) {
|
|
233
|
+
if (prev.some(function (label) {
|
|
234
|
+
return label.id === id;
|
|
235
|
+
})) {
|
|
236
|
+
return prev;
|
|
237
|
+
}
|
|
238
|
+
var palette = NEW_LABEL_COLORS[prev.length % NEW_LABEL_COLORS.length];
|
|
239
|
+
return [...prev, _objectSpread({
|
|
240
|
+
id,
|
|
241
|
+
name: trimmed
|
|
242
|
+
}, palette)];
|
|
243
|
+
});
|
|
244
|
+
return id;
|
|
245
|
+
}, []);
|
|
246
|
+
var value = (0, _react.useMemo)(function () {
|
|
247
|
+
return {
|
|
248
|
+
labels,
|
|
249
|
+
labelsById: labels.reduce(function (acc, label) {
|
|
250
|
+
return _objectSpread(_objectSpread({}, acc), {}, {
|
|
251
|
+
[label.id]: label
|
|
252
|
+
});
|
|
253
|
+
}, {}),
|
|
254
|
+
addLabel
|
|
255
|
+
};
|
|
256
|
+
}, [addLabel, labels]);
|
|
257
|
+
return /*#__PURE__*/_react.default.createElement(LabelsContext.Provider, {
|
|
258
|
+
value: value
|
|
259
|
+
}, children);
|
|
260
|
+
};
|
|
261
|
+
var useTaskLabels = function useTaskLabels() {
|
|
262
|
+
return (0, _react.useContext)(LabelsContext);
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
/* Empty state pill (mirrors EntityLabel + .entity-label in list-view.less) */
|
|
266
|
+
var StyledLabelPlaceholder = _styledComponents.default.span.withConfig({
|
|
267
|
+
displayName: "columns__StyledLabelPlaceholder",
|
|
268
|
+
componentId: "sc-9f4385-1"
|
|
269
|
+
})(["display:inline-flex;align-items:center;padding:1px 6px;border:1px solid var(--color-theme-700);border-radius:4px;.list-button.open &,.list-button:hover &{border-color:var(--color-primary-700,var(--color-primary));}"]);
|
|
270
|
+
var StyledLabelItems = _styledComponents.default.span.withConfig({
|
|
271
|
+
displayName: "columns__StyledLabelItems",
|
|
272
|
+
componentId: "sc-9f4385-2"
|
|
273
|
+
})(["display:inline-flex;align-items:center;min-width:0;> *:not(:last-child){margin-right:2px;}.rest-count{margin-left:2px;}", ""], CooltipStyle);
|
|
274
|
+
var MAX_SHOWN_LABELS = 8;
|
|
275
|
+
var LabelItems = function LabelItems(_ref3) {
|
|
276
|
+
var labels = _ref3.labels;
|
|
277
|
+
var showAsDot = labels.length > 2;
|
|
278
|
+
var shown = labels;
|
|
279
|
+
var restCount = 0;
|
|
280
|
+
if (labels.length > MAX_SHOWN_LABELS) {
|
|
281
|
+
restCount = Math.min(labels.length - MAX_SHOWN_LABELS, 99);
|
|
282
|
+
shown = labels.slice(0, MAX_SHOWN_LABELS - 1);
|
|
283
|
+
}
|
|
284
|
+
return /*#__PURE__*/_react.default.createElement(StyledLabelItems, null, shown.map(function (label) {
|
|
285
|
+
return showAsDot ? /*#__PURE__*/_react.default.createElement(_Dot.Dot, {
|
|
286
|
+
key: label.id,
|
|
287
|
+
color: label.color,
|
|
288
|
+
size: 20,
|
|
289
|
+
"data-tooltip": label.name
|
|
290
|
+
}) : /*#__PURE__*/_react.default.createElement(_Chip.Chip, {
|
|
291
|
+
key: label.id,
|
|
292
|
+
label: label.name,
|
|
293
|
+
backgroundColor: label.color,
|
|
294
|
+
color: "#303037",
|
|
295
|
+
style: {
|
|
296
|
+
maxWidth: "90px"
|
|
297
|
+
},
|
|
298
|
+
typographyProps: {
|
|
299
|
+
variant: "Caption 2",
|
|
300
|
+
weight: "bold"
|
|
301
|
+
},
|
|
302
|
+
"data-tooltip": label.name
|
|
303
|
+
});
|
|
304
|
+
}), restCount > 0 ? /*#__PURE__*/_react.default.createElement(_Typography.Body2, {
|
|
305
|
+
weight: "bold",
|
|
306
|
+
className: "rest-count"
|
|
307
|
+
}, "+", restCount) : null);
|
|
308
|
+
};
|
|
309
|
+
var LabelTrigger = /*#__PURE__*/(0, _react.forwardRef)(function (_ref4, ref) {
|
|
310
|
+
var labels = _ref4.labels,
|
|
311
|
+
rest = _objectWithoutProperties(_ref4, _excluded2);
|
|
312
|
+
if (labels.length === 0) {
|
|
313
|
+
return /*#__PURE__*/_react.default.createElement(StyledListButton, _extends({}, rest, {
|
|
314
|
+
ref: ref,
|
|
315
|
+
className: "show-on-hover",
|
|
316
|
+
"aria-label": "Add labels",
|
|
317
|
+
"data-tooltip": "Add Labels"
|
|
318
|
+
}), /*#__PURE__*/_react.default.createElement(StyledLabelPlaceholder, null, /*#__PURE__*/_react.default.createElement(_Typography.Caption2, {
|
|
319
|
+
weight: "bold",
|
|
320
|
+
color: "tertiary"
|
|
321
|
+
}, "LABEL")));
|
|
322
|
+
}
|
|
323
|
+
return /*#__PURE__*/_react.default.createElement(StyledListButton, _extends({}, rest, {
|
|
324
|
+
ref: ref,
|
|
325
|
+
"aria-label": "Change labels"
|
|
326
|
+
}), /*#__PURE__*/_react.default.createElement(LabelItems, {
|
|
327
|
+
labels: labels
|
|
328
|
+
}));
|
|
329
|
+
});
|
|
330
|
+
LabelTrigger.displayName = "LabelTrigger";
|
|
331
|
+
var LabelCell = exports.LabelCell = function LabelCell(_ref5) {
|
|
332
|
+
var labelIds = _ref5.labelIds,
|
|
333
|
+
_ref5$completed = _ref5.completed,
|
|
334
|
+
completed = _ref5$completed === void 0 ? false : _ref5$completed;
|
|
335
|
+
var _useTaskLabels = useTaskLabels(),
|
|
336
|
+
catalog = _useTaskLabels.labels,
|
|
337
|
+
labelsById = _useTaskLabels.labelsById,
|
|
338
|
+
addLabel = _useTaskLabels.addLabel;
|
|
339
|
+
var _useState3 = (0, _react.useState)(labelIds !== null && labelIds !== void 0 ? labelIds : []),
|
|
340
|
+
_useState4 = _slicedToArray(_useState3, 2),
|
|
341
|
+
selected = _useState4[0],
|
|
342
|
+
setSelected = _useState4[1];
|
|
343
|
+
|
|
344
|
+
/* alphabetical, like LabelTrigger sorts before rendering */
|
|
345
|
+
var selectedLabels = (0, _react.useMemo)(function () {
|
|
346
|
+
return selected.map(function (id) {
|
|
347
|
+
return labelsById[id];
|
|
348
|
+
}).filter(function (label) {
|
|
349
|
+
return Boolean(label);
|
|
350
|
+
}).sort(function (a, b) {
|
|
351
|
+
return a.name.localeCompare(b.name);
|
|
352
|
+
});
|
|
353
|
+
}, [labelsById, selected]);
|
|
354
|
+
var options = (0, _react.useMemo)(function () {
|
|
355
|
+
return catalog.map(function (label) {
|
|
356
|
+
return {
|
|
357
|
+
id: label.id,
|
|
358
|
+
name: label.name,
|
|
359
|
+
color: label.color,
|
|
360
|
+
textColor: label.textColor
|
|
361
|
+
};
|
|
362
|
+
});
|
|
363
|
+
}, [catalog]);
|
|
364
|
+
var handleChange = (0, _react.useCallback)(function (value) {
|
|
365
|
+
setSelected(Array.isArray(value) ? value.map(String) : [String(value)].filter(Boolean));
|
|
366
|
+
}, []);
|
|
367
|
+
var handleAddNew = (0, _react.useCallback)(function (name) {
|
|
368
|
+
if (!name || !name.trim()) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
var id = addLabel(name);
|
|
372
|
+
setSelected(function (prev) {
|
|
373
|
+
return prev.includes(id) ? prev : [...prev, id];
|
|
374
|
+
});
|
|
375
|
+
}, [addLabel]);
|
|
376
|
+
|
|
377
|
+
/* Completed tasks show their labels read-only (mirrors Label.js) */
|
|
378
|
+
if (completed) {
|
|
379
|
+
return /*#__PURE__*/_react.default.createElement(StyledReadOnlyCell, {
|
|
380
|
+
className: "col col-label"
|
|
381
|
+
}, selectedLabels.length > 0 ? /*#__PURE__*/_react.default.createElement(LabelItems, {
|
|
382
|
+
labels: selectedLabels
|
|
383
|
+
}) : null);
|
|
384
|
+
}
|
|
385
|
+
return /*#__PURE__*/_react.default.createElement("div", {
|
|
386
|
+
className: "col col-label"
|
|
387
|
+
}, /*#__PURE__*/_react.default.createElement(_Select.Select, {
|
|
388
|
+
type: "multiple",
|
|
389
|
+
options: options,
|
|
390
|
+
selected: selected,
|
|
391
|
+
onChange: handleChange,
|
|
392
|
+
emptyValue: "Add new",
|
|
393
|
+
emptyAction: handleAddNew,
|
|
394
|
+
autoHeightMax: 278,
|
|
395
|
+
position: "bottom-end",
|
|
396
|
+
target: /*#__PURE__*/_react.default.createElement(LabelTrigger, {
|
|
397
|
+
labels: selectedLabels
|
|
398
|
+
})
|
|
399
|
+
}));
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
/* ================================================================== */
|
|
403
|
+
/* Date */
|
|
404
|
+
/* ================================================================== */
|
|
405
|
+
|
|
406
|
+
/** The personalized date format the app defaults to (`MMM D. YYYY`). */
|
|
407
|
+
var DATE_FORMAT = "MMM D. YYYY";
|
|
408
|
+
|
|
409
|
+
/** Midnight UTC today — the reference point for the mock data and overdue. */
|
|
410
|
+
var todayUnix = function todayUnix() {
|
|
411
|
+
return _momentTimezone.default.utc().startOf("day").unix();
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
/** Build a mock timestamp N days from today (negative = in the past). */
|
|
415
|
+
var dayOffset = exports.dayOffset = function dayOffset(offset) {
|
|
416
|
+
return _momentTimezone.default.utc().startOf("day").add(offset, "days").unix();
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
/** mirrors utils/date.js → formatDateRange (month-first format) */
|
|
420
|
+
var formatDateRange = exports.formatDateRange = function formatDateRange(from, to) {
|
|
421
|
+
if (from === undefined || from === null) {
|
|
422
|
+
return undefined;
|
|
423
|
+
}
|
|
424
|
+
var start = _momentTimezone.default.unix(from).utc();
|
|
425
|
+
var due = _momentTimezone.default.unix(to !== null && to !== void 0 ? to : from).utc();
|
|
426
|
+
var withoutYear = DATE_FORMAT.replace(/YYYY|,$/gi, "").trim();
|
|
427
|
+
var withoutYearAndMonth = withoutYear.replace(/MMM|,$/gi, "").trim();
|
|
428
|
+
if (from === to || to === undefined) {
|
|
429
|
+
return start.format(withoutYear);
|
|
430
|
+
}
|
|
431
|
+
if (start.year() === due.year() && start.month() === due.month()) {
|
|
432
|
+
return "".concat(start.format(withoutYear), " - ").concat(due.format(withoutYearAndMonth));
|
|
433
|
+
}
|
|
434
|
+
return "".concat(start.format(withoutYear), " - ").concat(due.format(withoutYear));
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
/** mirrors utils/date.js → formatDateSmart(…, long) */
|
|
438
|
+
var formatDateLong = function formatDateLong(value) {
|
|
439
|
+
return value ? _momentTimezone.default.unix(value).utc().format(DATE_FORMAT) : "";
|
|
440
|
+
};
|
|
441
|
+
var isOverdue = exports.isOverdue = function isOverdue(dueOn, completed) {
|
|
442
|
+
return Boolean(dueOn && !completed && dueOn < todayUnix());
|
|
443
|
+
};
|
|
444
|
+
var StyledDateValue = _styledComponents.default.span.withConfig({
|
|
445
|
+
displayName: "columns__StyledDateValue",
|
|
446
|
+
componentId: "sc-9f4385-3"
|
|
447
|
+
})(["font-family:", ";white-space:nowrap;"], SANS);
|
|
448
|
+
|
|
449
|
+
/* completed rows render values without a trigger, so the cell itself hosts the
|
|
450
|
+
CSS tooltip */
|
|
451
|
+
var StyledReadOnlyCell = _styledComponents.default.div.withConfig({
|
|
452
|
+
displayName: "columns__StyledReadOnlyCell",
|
|
453
|
+
componentId: "sc-9f4385-4"
|
|
454
|
+
})(["padding-right:8px;", ""], CooltipStyle);
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* `DatePicker` wraps itself in a `width: 100%` inline-block, which would stretch
|
|
458
|
+
* across the column and left-align the trigger the moment the picker mounts —
|
|
459
|
+
* the value would visibly jump and the popper would anchor to the wrong spot.
|
|
460
|
+
* Keeping that wrapper right-aligned makes the open state geometrically
|
|
461
|
+
* identical to the closed one.
|
|
462
|
+
*/
|
|
463
|
+
var StyledPickerCell = _styledComponents.default.div.withConfig({
|
|
464
|
+
displayName: "columns__StyledPickerCell",
|
|
465
|
+
componentId: "sc-9f4385-5"
|
|
466
|
+
})(["> div{text-align:right;}"]);
|
|
467
|
+
var DateTrigger = /*#__PURE__*/(0, _react.forwardRef)(function (_ref6, ref) {
|
|
468
|
+
var value = _ref6.value,
|
|
469
|
+
tooltip = _ref6.tooltip,
|
|
470
|
+
overdue = _ref6.overdue,
|
|
471
|
+
rest = _objectWithoutProperties(_ref6, _excluded3);
|
|
472
|
+
return /*#__PURE__*/_react.default.createElement(StyledListButton, _extends({}, rest, {
|
|
473
|
+
ref: ref,
|
|
474
|
+
className: value ? "show-on-hover has-value" : "show-on-hover",
|
|
475
|
+
"aria-label": value ? "Change dates" : "Add date",
|
|
476
|
+
"data-tooltip": tooltip
|
|
477
|
+
}), value ? /*#__PURE__*/_react.default.createElement(StyledDateValue, null, /*#__PURE__*/_react.default.createElement(_Typography.Caption1, {
|
|
478
|
+
weight: "bold",
|
|
479
|
+
color: overdue ? "alert" : "secondary"
|
|
480
|
+
}, value)) : /*#__PURE__*/_react.default.createElement(_Icons.CalendarIcon, null));
|
|
481
|
+
});
|
|
482
|
+
DateTrigger.displayName = "DateTrigger";
|
|
483
|
+
var DateCell = exports.DateCell = function DateCell(_ref7) {
|
|
484
|
+
var _dates$from, _dates$to2;
|
|
485
|
+
var startOn = _ref7.startOn,
|
|
486
|
+
dueOn = _ref7.dueOn,
|
|
487
|
+
_ref7$completed = _ref7.completed,
|
|
488
|
+
completed = _ref7$completed === void 0 ? false : _ref7$completed;
|
|
489
|
+
var _useState5 = (0, _react.useState)({
|
|
490
|
+
from: startOn,
|
|
491
|
+
to: dueOn
|
|
492
|
+
}),
|
|
493
|
+
_useState6 = _slicedToArray(_useState5, 2),
|
|
494
|
+
dates = _useState6[0],
|
|
495
|
+
setDates = _useState6[1];
|
|
496
|
+
/**
|
|
497
|
+
* The DS `DatePicker` does real work on mount (timezone guess, month grids,
|
|
498
|
+
* key handlers) — with one per row that is the single most expensive thing on
|
|
499
|
+
* this page. So the closed state is just the trigger; the picker is mounted
|
|
500
|
+
* on first click, already open, and unmounted again when it closes.
|
|
501
|
+
*/
|
|
502
|
+
var _useState7 = (0, _react.useState)(false),
|
|
503
|
+
_useState8 = _slicedToArray(_useState7, 2),
|
|
504
|
+
pickerMounted = _useState8[0],
|
|
505
|
+
setPickerMounted = _useState8[1];
|
|
506
|
+
|
|
507
|
+
/* moment isn't cheap either; only reformat when the dates actually change */
|
|
508
|
+
var _useMemo = (0, _react.useMemo)(function () {
|
|
509
|
+
var _dates$to;
|
|
510
|
+
var formatted = formatDateRange(dates.from, dates.to);
|
|
511
|
+
var hint = "Add Date";
|
|
512
|
+
if (dates.from) {
|
|
513
|
+
hint = !dates.to || dates.to === dates.from ? formatDateLong(dates.from) : "".concat(formatDateLong(dates.from), " - ").concat(formatDateLong(dates.to));
|
|
514
|
+
}
|
|
515
|
+
return {
|
|
516
|
+
value: formatted,
|
|
517
|
+
overdue: isOverdue((_dates$to = dates.to) !== null && _dates$to !== void 0 ? _dates$to : dates.from, completed),
|
|
518
|
+
tooltip: hint
|
|
519
|
+
};
|
|
520
|
+
}, [completed, dates.from, dates.to]),
|
|
521
|
+
value = _useMemo.value,
|
|
522
|
+
overdue = _useMemo.overdue,
|
|
523
|
+
tooltip = _useMemo.tooltip;
|
|
524
|
+
var handleSave = (0, _react.useCallback)(function (range) {
|
|
525
|
+
if (!range || range.from === null || range.from === undefined) {
|
|
526
|
+
setDates({});
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
setDates({
|
|
530
|
+
from: Number(range.from),
|
|
531
|
+
to: range.to ? Number(range.to) : Number(range.from)
|
|
532
|
+
});
|
|
533
|
+
}, []);
|
|
534
|
+
var handleClear = (0, _react.useCallback)(function () {
|
|
535
|
+
return setDates({});
|
|
536
|
+
}, []);
|
|
537
|
+
var handleCalendarToggle = (0, _react.useCallback)(function (next) {
|
|
538
|
+
if (!next) {
|
|
539
|
+
setPickerMounted(false);
|
|
540
|
+
}
|
|
541
|
+
}, []);
|
|
542
|
+
|
|
543
|
+
/* Completed tasks show the date read-only (mirrors Date.js) */
|
|
544
|
+
if (completed) {
|
|
545
|
+
return /*#__PURE__*/_react.default.createElement(StyledReadOnlyCell, {
|
|
546
|
+
className: "col col-date"
|
|
547
|
+
}, value ? /*#__PURE__*/_react.default.createElement(StyledDateValue, {
|
|
548
|
+
"data-tooltip": tooltip
|
|
549
|
+
}, /*#__PURE__*/_react.default.createElement(_Typography.Caption1, {
|
|
550
|
+
weight: "bold",
|
|
551
|
+
color: overdue ? "alert" : "secondary"
|
|
552
|
+
}, value)) : null);
|
|
553
|
+
}
|
|
554
|
+
if (!pickerMounted) {
|
|
555
|
+
return /*#__PURE__*/_react.default.createElement("div", {
|
|
556
|
+
className: "col col-date"
|
|
557
|
+
}, /*#__PURE__*/_react.default.createElement(DateTrigger, {
|
|
558
|
+
value: value,
|
|
559
|
+
tooltip: tooltip,
|
|
560
|
+
overdue: overdue,
|
|
561
|
+
onClick: function onClick() {
|
|
562
|
+
return setPickerMounted(true);
|
|
563
|
+
}
|
|
564
|
+
}));
|
|
565
|
+
}
|
|
566
|
+
return /*#__PURE__*/_react.default.createElement(StyledPickerCell, {
|
|
567
|
+
className: "col col-date"
|
|
568
|
+
}, /*#__PURE__*/_react.default.createElement(_DatePicker.DatePicker, {
|
|
569
|
+
open: true,
|
|
570
|
+
mode: "custom",
|
|
571
|
+
instant: false,
|
|
572
|
+
maxRange: 366,
|
|
573
|
+
firstDayOfWeek: 1,
|
|
574
|
+
position: "bottom-end",
|
|
575
|
+
selected: {
|
|
576
|
+
from: (_dates$from = dates.from) !== null && _dates$from !== void 0 ? _dates$from : null,
|
|
577
|
+
to: (_dates$to2 = dates.to) !== null && _dates$to2 !== void 0 ? _dates$to2 : null
|
|
578
|
+
},
|
|
579
|
+
onSave: handleSave,
|
|
580
|
+
onClear: handleClear,
|
|
581
|
+
onCalendarToggle: handleCalendarToggle,
|
|
582
|
+
target: /*#__PURE__*/_react.default.createElement(DateTrigger, {
|
|
583
|
+
value: value,
|
|
584
|
+
tooltip: tooltip,
|
|
585
|
+
overdue: overdue
|
|
586
|
+
})
|
|
587
|
+
}));
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
/* ================================================================== */
|
|
591
|
+
/* Time */
|
|
592
|
+
/* ================================================================== */
|
|
593
|
+
|
|
594
|
+
/** mirrors utils/utils.jsx → calculateTimeToEstimatePercentage */
|
|
595
|
+
var timeToEstimatePercentage = function timeToEstimatePercentage(tracked, estimated) {
|
|
596
|
+
if (estimated > 0) {
|
|
597
|
+
return tracked > 0 ? Math.round(tracked / estimated * 100) : 0;
|
|
598
|
+
}
|
|
599
|
+
return null;
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
/** mirrors utils/utils.jsx → formatTrackedAndEstimatedTime */
|
|
603
|
+
var formatTrackedAndEstimated = function formatTrackedAndEstimated(tracked, estimated) {
|
|
604
|
+
return estimated > 0 ? "".concat((0, _utils.formatHours)(tracked, false, false, "short"), "/").concat((0, _utils.formatHours)(estimated, false, false, "short")) : (0, _utils.formatHours)(tracked, false, false, "short");
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
/* mirrors Time.js → progressColor() */
|
|
608
|
+
var progressColor = function progressColor(percent) {
|
|
609
|
+
if (percent >= 100) {
|
|
610
|
+
return "var(--red-alert)";
|
|
611
|
+
}
|
|
612
|
+
if (percent >= 75) {
|
|
613
|
+
return "var(--warning)";
|
|
614
|
+
}
|
|
615
|
+
return "var(--color-sucess-green)";
|
|
616
|
+
};
|
|
617
|
+
var StyledTimeValue = _styledComponents.default.span.withConfig({
|
|
618
|
+
displayName: "columns__StyledTimeValue",
|
|
619
|
+
componentId: "sc-9f4385-6"
|
|
620
|
+
})(["display:inline-flex;align-items:center;font-family:", ";white-space:nowrap;.c-progress-pie,svg{flex-shrink:0;}.time-pie{margin-right:8px;}"], SANS);
|
|
621
|
+
var TimeValue = function TimeValue(_ref8) {
|
|
622
|
+
var tracked = _ref8.tracked,
|
|
623
|
+
estimated = _ref8.estimated;
|
|
624
|
+
var percent = timeToEstimatePercentage(tracked, estimated);
|
|
625
|
+
return /*#__PURE__*/_react.default.createElement(StyledTimeValue, null, estimated > 0 ? /*#__PURE__*/_react.default.createElement(_ProgressPie.ProgressPie, {
|
|
626
|
+
className: "time-pie",
|
|
627
|
+
radius: 10,
|
|
628
|
+
progress: percent !== null && percent !== void 0 ? percent : 0,
|
|
629
|
+
progressColor: progressColor(percent !== null && percent !== void 0 ? percent : 0),
|
|
630
|
+
backgroundColor: "var(--color-theme-300)"
|
|
631
|
+
}) : null, /*#__PURE__*/_react.default.createElement(_Typography.Caption1, {
|
|
632
|
+
weight: "bold"
|
|
633
|
+
}, formatTrackedAndEstimated(tracked, estimated)));
|
|
634
|
+
};
|
|
635
|
+
var TimeTrigger = function TimeTrigger(_ref9) {
|
|
636
|
+
var tracked = _ref9.tracked,
|
|
637
|
+
estimated = _ref9.estimated,
|
|
638
|
+
onClick = _ref9.onClick;
|
|
639
|
+
var hasTime = tracked > 0 || estimated > 0;
|
|
640
|
+
return /*#__PURE__*/_react.default.createElement(StyledListButton, {
|
|
641
|
+
onClick: onClick,
|
|
642
|
+
className: hasTime ? "show-on-hover open" : "show-on-hover",
|
|
643
|
+
"aria-label": hasTime ? "Edit time" : "Add time",
|
|
644
|
+
"data-tooltip": hasTime ? "Edit Time" : "Add Time"
|
|
645
|
+
}, hasTime ? /*#__PURE__*/_react.default.createElement(TimeValue, {
|
|
646
|
+
tracked: tracked,
|
|
647
|
+
estimated: estimated
|
|
648
|
+
}) : /*#__PURE__*/_react.default.createElement(_Icons.ClockAddIcon, null));
|
|
649
|
+
};
|
|
650
|
+
var TimeCell = exports.TimeCell = function TimeCell(_ref0) {
|
|
651
|
+
var taskName = _ref0.taskName,
|
|
652
|
+
_ref0$tracked = _ref0.tracked,
|
|
653
|
+
tracked = _ref0$tracked === void 0 ? 0 : _ref0$tracked,
|
|
654
|
+
_ref0$estimated = _ref0.estimated,
|
|
655
|
+
estimated = _ref0$estimated === void 0 ? 0 : _ref0$estimated,
|
|
656
|
+
users = _ref0.users,
|
|
657
|
+
currentUserId = _ref0.currentUserId,
|
|
658
|
+
_ref0$completed = _ref0.completed,
|
|
659
|
+
completed = _ref0$completed === void 0 ? false : _ref0$completed;
|
|
660
|
+
var _useState9 = (0, _react.useState)(false),
|
|
661
|
+
_useState0 = _slicedToArray(_useState9, 2),
|
|
662
|
+
open = _useState0[0],
|
|
663
|
+
setOpen = _useState0[1];
|
|
664
|
+
var _useState1 = (0, _react.useState)(function () {
|
|
665
|
+
var _users$find$name, _users$find;
|
|
666
|
+
return tracked > 0 ? [{
|
|
667
|
+
id: 1,
|
|
668
|
+
date: dayOffset(-3),
|
|
669
|
+
userId: currentUserId,
|
|
670
|
+
userName: (_users$find$name = (_users$find = users.find(function (user) {
|
|
671
|
+
return user.id === currentUserId;
|
|
672
|
+
})) === null || _users$find === void 0 ? void 0 : _users$find.name) !== null && _users$find$name !== void 0 ? _users$find$name : "You",
|
|
673
|
+
jobType: "Design",
|
|
674
|
+
hours: tracked,
|
|
675
|
+
billable: true
|
|
676
|
+
}] : [];
|
|
677
|
+
}),
|
|
678
|
+
_useState10 = _slicedToArray(_useState1, 2),
|
|
679
|
+
records = _useState10[0],
|
|
680
|
+
setRecords = _useState10[1];
|
|
681
|
+
var total = (0, _react.useMemo)(function () {
|
|
682
|
+
return records.reduce(function (sum, record) {
|
|
683
|
+
return sum + record.hours;
|
|
684
|
+
}, 0);
|
|
685
|
+
}, [records]);
|
|
686
|
+
var handleAdd = (0, _react.useCallback)(function (record) {
|
|
687
|
+
setRecords(function (prev) {
|
|
688
|
+
return [_objectSpread(_objectSpread({}, record), {}, {
|
|
689
|
+
id: prev.length + 1
|
|
690
|
+
}), ...prev];
|
|
691
|
+
});
|
|
692
|
+
setOpen(false);
|
|
693
|
+
}, []);
|
|
694
|
+
|
|
695
|
+
/* Completed tasks show the value without the "add" affordance (mirrors
|
|
696
|
+
Time.js passing hideIcon for completed tasks) */
|
|
697
|
+
if (completed) {
|
|
698
|
+
return /*#__PURE__*/_react.default.createElement(StyledReadOnlyCell, {
|
|
699
|
+
className: "col col-time"
|
|
700
|
+
}, total > 0 || estimated > 0 ? /*#__PURE__*/_react.default.createElement(TimeValue, {
|
|
701
|
+
tracked: total,
|
|
702
|
+
estimated: estimated
|
|
703
|
+
}) : null);
|
|
704
|
+
}
|
|
705
|
+
return /*#__PURE__*/_react.default.createElement("div", {
|
|
706
|
+
className: "col col-time"
|
|
707
|
+
}, /*#__PURE__*/_react.default.createElement(TimeTrigger, {
|
|
708
|
+
tracked: total,
|
|
709
|
+
estimated: estimated,
|
|
710
|
+
onClick: function onClick() {
|
|
711
|
+
return setOpen(true);
|
|
712
|
+
}
|
|
713
|
+
}), open ? /*#__PURE__*/_react.default.createElement(TimeRecordDialog, {
|
|
714
|
+
open: open,
|
|
715
|
+
taskName: taskName,
|
|
716
|
+
users: users,
|
|
717
|
+
currentUserId: currentUserId,
|
|
718
|
+
records: records,
|
|
719
|
+
total: total,
|
|
720
|
+
onSubmit: handleAdd,
|
|
721
|
+
onClose: function onClose() {
|
|
722
|
+
return setOpen(false);
|
|
723
|
+
}
|
|
724
|
+
}) : null);
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
/* ------------------------------------------------------------------ */
|
|
728
|
+
/* Add Time Record dialog (mirrors TimeRecordForm/*) */
|
|
729
|
+
/* ------------------------------------------------------------------ */
|
|
730
|
+
|
|
731
|
+
var JOB_TYPES = [{
|
|
732
|
+
id: "design",
|
|
733
|
+
name: "Design"
|
|
734
|
+
}, {
|
|
735
|
+
id: "development",
|
|
736
|
+
name: "Development"
|
|
737
|
+
}, {
|
|
738
|
+
id: "devops",
|
|
739
|
+
name: "Devops"
|
|
740
|
+
}, {
|
|
741
|
+
id: "consulting",
|
|
742
|
+
name: "Consulting"
|
|
743
|
+
}, {
|
|
744
|
+
id: "qa",
|
|
745
|
+
name: "QA"
|
|
746
|
+
}];
|
|
747
|
+
|
|
748
|
+
/* The dialog paper — the app builds this one by hand rather than with DS
|
|
749
|
+
`Dialog` because it is 770px wide (`.time_record_form_modal_wrapper`). */
|
|
750
|
+
var StyledTimeRecordPaper = _styledComponents.default.div.withConfig({
|
|
751
|
+
displayName: "columns__StyledTimeRecordPaper",
|
|
752
|
+
componentId: "sc-9f4385-7"
|
|
753
|
+
})(["position:relative;width:770px;max-width:95vw;max-height:calc(100vh - 156px);margin:126px auto 30px auto;padding:20px 30px;overflow:auto;border-radius:8px;background-color:var(--page-paper-main);color:var(--color-theme-900);box-shadow:var(--shadow-primary);box-sizing:border-box;font-family:", ";*{box-sizing:border-box;}@media (max-width:830px){width:calc(100vw - 60px);}.trd-header{display:flex;align-items:center;justify-content:space-between;}.trd-title{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;margin-right:16px;font-size:1rem;font-weight:700;}.trd-date button{width:auto;}.trd-date > div{text-align:right;}.trd-divider{margin:20px 0 32px 0;border-bottom:1px solid var(--border-primary);height:1px;}.trd-body{display:flex;justify-content:space-between;position:relative;}.trd-form{width:100%;}.trd-fields{display:flex;align-items:flex-end;}.trd-hours{width:144px;margin-right:16px;flex-shrink:0;}.trd-hours .c-input-wrapper,.trd-hours input{width:100%;}.trd-field{margin-right:16px;}.trd-field:last-of-type{margin-right:20px;}.trd-field .c-label{display:block;margin-bottom:2px;}.trd-field button{width:144px;}.trd-billable{display:flex;align-items:center;margin-top:24px;}.trd-billable .c-label{margin-left:8px;cursor:pointer;}.trd-description-trigger{display:inline-flex;align-items:center;margin-top:16px;color:var(--color-secondary);font-size:14px;font-weight:500;background:none;border:none;padding:0;cursor:pointer;font-family:", ";}.trd-description-trigger svg{margin-right:4px;fill:var(--color-primary);transition:transform 200ms ease;}.trd-description-trigger.collapsed svg{transform:rotate(180deg);}.trd-textarea{width:360px;max-width:100%;margin-top:8px;font-size:12px;resize:none;}.trd-actions{display:flex;margin-top:24px;}.trd-actions .c-button:first-child{margin-right:12px;}.trd-total{position:absolute;right:0;bottom:0;display:inline-flex;align-items:center;align-self:flex-end;background:none;border:none;padding:0;cursor:pointer;}.trd-total svg{margin-right:4px;fill:var(--color-primary);transition:transform 200ms ease;}.trd-total.collapsed svg{transform:rotate(180deg);}.trd-records{margin-top:32px;border-top:1px solid var(--border-primary);}.trd-record{display:flex;align-items:center;justify-content:space-between;height:36px;border-bottom:1px solid var(--color-theme-300);}.trd-record-left{display:flex;align-items:center;gap:12px;min-width:0;}"], SANS, SANS);
|
|
754
|
+
var TimeRecordDialog = function TimeRecordDialog(_ref1) {
|
|
755
|
+
var open = _ref1.open,
|
|
756
|
+
taskName = _ref1.taskName,
|
|
757
|
+
users = _ref1.users,
|
|
758
|
+
currentUserId = _ref1.currentUserId,
|
|
759
|
+
records = _ref1.records,
|
|
760
|
+
total = _ref1.total,
|
|
761
|
+
onSubmit = _ref1.onSubmit,
|
|
762
|
+
onClose = _ref1.onClose;
|
|
763
|
+
var _useState11 = (0, _react.useState)(""),
|
|
764
|
+
_useState12 = _slicedToArray(_useState11, 2),
|
|
765
|
+
value = _useState12[0],
|
|
766
|
+
setValue = _useState12[1];
|
|
767
|
+
var _useState13 = (0, _react.useState)(todayUnix()),
|
|
768
|
+
_useState14 = _slicedToArray(_useState13, 2),
|
|
769
|
+
date = _useState14[0],
|
|
770
|
+
setDate = _useState14[1];
|
|
771
|
+
var _useState15 = (0, _react.useState)(currentUserId),
|
|
772
|
+
_useState16 = _slicedToArray(_useState15, 2),
|
|
773
|
+
userId = _useState16[0],
|
|
774
|
+
setUserId = _useState16[1];
|
|
775
|
+
var _useState17 = (0, _react.useState)("devops"),
|
|
776
|
+
_useState18 = _slicedToArray(_useState17, 2),
|
|
777
|
+
jobTypeId = _useState18[0],
|
|
778
|
+
setJobTypeId = _useState18[1];
|
|
779
|
+
var _useState19 = (0, _react.useState)(false),
|
|
780
|
+
_useState20 = _slicedToArray(_useState19, 2),
|
|
781
|
+
billable = _useState20[0],
|
|
782
|
+
setBillable = _useState20[1];
|
|
783
|
+
var _useState21 = (0, _react.useState)(true),
|
|
784
|
+
_useState22 = _slicedToArray(_useState21, 2),
|
|
785
|
+
showDescription = _useState22[0],
|
|
786
|
+
setShowDescription = _useState22[1];
|
|
787
|
+
var _useState23 = (0, _react.useState)(""),
|
|
788
|
+
_useState24 = _slicedToArray(_useState23, 2),
|
|
789
|
+
description = _useState24[0],
|
|
790
|
+
setDescription = _useState24[1];
|
|
791
|
+
var _useState25 = (0, _react.useState)(false),
|
|
792
|
+
_useState26 = _slicedToArray(_useState25, 2),
|
|
793
|
+
showRecords = _useState26[0],
|
|
794
|
+
setShowRecords = _useState26[1];
|
|
795
|
+
var user = users.find(function (item) {
|
|
796
|
+
return item.id === userId;
|
|
797
|
+
});
|
|
798
|
+
var jobType = JOB_TYPES.find(function (item) {
|
|
799
|
+
return item.id === jobTypeId;
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
/** "1:30" / "1.5" → decimal hours */
|
|
803
|
+
var hours = (0, _react.useMemo)(function () {
|
|
804
|
+
var trimmed = value.trim();
|
|
805
|
+
if (!trimmed) {
|
|
806
|
+
return 0;
|
|
807
|
+
}
|
|
808
|
+
if (trimmed.includes(":")) {
|
|
809
|
+
var _trimmed$split = trimmed.split(":"),
|
|
810
|
+
_trimmed$split2 = _slicedToArray(_trimmed$split, 2),
|
|
811
|
+
h = _trimmed$split2[0],
|
|
812
|
+
m = _trimmed$split2[1];
|
|
813
|
+
return Number(h || 0) + Number(m || 0) / 60;
|
|
814
|
+
}
|
|
815
|
+
return Number(trimmed) || 0;
|
|
816
|
+
}, [value]);
|
|
817
|
+
var handleSubmit = (0, _react.useCallback)(function () {
|
|
818
|
+
var _user$name, _jobType$name;
|
|
819
|
+
onSubmit({
|
|
820
|
+
date,
|
|
821
|
+
userId,
|
|
822
|
+
userName: (_user$name = user === null || user === void 0 ? void 0 : user.name) !== null && _user$name !== void 0 ? _user$name : "",
|
|
823
|
+
jobType: (_jobType$name = jobType === null || jobType === void 0 ? void 0 : jobType.name) !== null && _jobType$name !== void 0 ? _jobType$name : "",
|
|
824
|
+
hours,
|
|
825
|
+
billable,
|
|
826
|
+
description
|
|
827
|
+
});
|
|
828
|
+
}, [billable, date, description, hours, jobType, onSubmit, user, userId]);
|
|
829
|
+
return /*#__PURE__*/_react.default.createElement(_Modal.Modal, {
|
|
830
|
+
open: open,
|
|
831
|
+
onClose: onClose
|
|
832
|
+
}, /*#__PURE__*/_react.default.createElement(_Transitions.SlideFromTop, {
|
|
833
|
+
in: open
|
|
834
|
+
}, /*#__PURE__*/_react.default.createElement(StyledTimeRecordPaper, {
|
|
835
|
+
role: "dialog",
|
|
836
|
+
"aria-label": "Add time record"
|
|
837
|
+
}, /*#__PURE__*/_react.default.createElement("div", {
|
|
838
|
+
className: "trd-header"
|
|
839
|
+
}, /*#__PURE__*/_react.default.createElement("span", {
|
|
840
|
+
className: "trd-title"
|
|
841
|
+
}, taskName), /*#__PURE__*/_react.default.createElement("span", {
|
|
842
|
+
className: "trd-date"
|
|
843
|
+
}, /*#__PURE__*/_react.default.createElement(_DatePicker.DatePicker, {
|
|
844
|
+
mode: "daily",
|
|
845
|
+
required: true,
|
|
846
|
+
firstDayOfWeek: 1,
|
|
847
|
+
position: "bottom-end",
|
|
848
|
+
selected: {
|
|
849
|
+
from: date,
|
|
850
|
+
to: date
|
|
851
|
+
},
|
|
852
|
+
onSave: function onSave(range) {
|
|
853
|
+
return range !== null && range !== void 0 && range.from ? setDate(Number(range.from)) : undefined;
|
|
854
|
+
},
|
|
855
|
+
target: /*#__PURE__*/_react.default.createElement(_SelectTrigger.SelectTrigger, null, formatDateRange(date, date))
|
|
856
|
+
}))), /*#__PURE__*/_react.default.createElement("div", {
|
|
857
|
+
className: "trd-divider"
|
|
858
|
+
}), /*#__PURE__*/_react.default.createElement("div", {
|
|
859
|
+
className: "trd-body"
|
|
860
|
+
}, /*#__PURE__*/_react.default.createElement("div", {
|
|
861
|
+
className: "trd-form"
|
|
862
|
+
}, /*#__PURE__*/_react.default.createElement("div", {
|
|
863
|
+
className: "trd-fields"
|
|
864
|
+
}, /*#__PURE__*/_react.default.createElement("span", {
|
|
865
|
+
className: "trd-hours"
|
|
866
|
+
}, /*#__PURE__*/_react.default.createElement(_Input.InputHours, {
|
|
867
|
+
autoFocus: true,
|
|
868
|
+
allowEmptyValue: true,
|
|
869
|
+
size: "biggest",
|
|
870
|
+
placeholder: "00:00",
|
|
871
|
+
value: value,
|
|
872
|
+
onChange: setValue
|
|
873
|
+
})), /*#__PURE__*/_react.default.createElement("span", {
|
|
874
|
+
className: "trd-field"
|
|
875
|
+
}, /*#__PURE__*/_react.default.createElement(_Label.Label, {
|
|
876
|
+
size: "small"
|
|
877
|
+
}, "User"), /*#__PURE__*/_react.default.createElement(_Select.Select, {
|
|
878
|
+
options: users,
|
|
879
|
+
selected: userId,
|
|
880
|
+
forceCloseMenu: true,
|
|
881
|
+
onChange: function onChange(next) {
|
|
882
|
+
return setUserId(String(next));
|
|
883
|
+
},
|
|
884
|
+
target: /*#__PURE__*/_react.default.createElement(_SelectTrigger.SelectTrigger, {
|
|
885
|
+
id: "trd-user"
|
|
886
|
+
}, user === null || user === void 0 ? void 0 : user.name)
|
|
887
|
+
})), /*#__PURE__*/_react.default.createElement("span", {
|
|
888
|
+
className: "trd-field"
|
|
889
|
+
}, /*#__PURE__*/_react.default.createElement(_Label.Label, {
|
|
890
|
+
size: "small"
|
|
891
|
+
}, "Job Type"), /*#__PURE__*/_react.default.createElement(_Select.Select, {
|
|
892
|
+
options: JOB_TYPES,
|
|
893
|
+
selected: jobTypeId,
|
|
894
|
+
forceCloseMenu: true,
|
|
895
|
+
onChange: function onChange(next) {
|
|
896
|
+
return setJobTypeId(String(next));
|
|
897
|
+
},
|
|
898
|
+
target: /*#__PURE__*/_react.default.createElement(_SelectTrigger.SelectTrigger, {
|
|
899
|
+
id: "trd-job-type"
|
|
900
|
+
}, jobType === null || jobType === void 0 ? void 0 : jobType.name)
|
|
901
|
+
})), /*#__PURE__*/_react.default.createElement("span", {
|
|
902
|
+
className: "trd-billable"
|
|
903
|
+
}, /*#__PURE__*/_react.default.createElement(_Checkbox.Checkbox, {
|
|
904
|
+
id: "trd-billable",
|
|
905
|
+
checked: billable,
|
|
906
|
+
onChange: function onChange() {
|
|
907
|
+
return setBillable(function (prev) {
|
|
908
|
+
return !prev;
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
}), /*#__PURE__*/_react.default.createElement(_Label.Label, {
|
|
912
|
+
htmlFor: "trd-billable",
|
|
913
|
+
weight: "regular"
|
|
914
|
+
}, "Billable"))), /*#__PURE__*/_react.default.createElement("button", {
|
|
915
|
+
type: "button",
|
|
916
|
+
className: showDescription ? "trd-description-trigger" : "trd-description-trigger collapsed",
|
|
917
|
+
onClick: function onClick() {
|
|
918
|
+
return setShowDescription(function (prev) {
|
|
919
|
+
return !prev;
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
}, /*#__PURE__*/_react.default.createElement(_Icons.CollapseExpandSingleIcon, null), /*#__PURE__*/_react.default.createElement("span", null, "Description")), showDescription ? /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(_Textarea.Textarea, {
|
|
923
|
+
className: "trd-textarea",
|
|
924
|
+
value: description,
|
|
925
|
+
onChange: function onChange(event) {
|
|
926
|
+
return setDescription(event.target.value);
|
|
927
|
+
}
|
|
928
|
+
})) : null, /*#__PURE__*/_react.default.createElement("div", {
|
|
929
|
+
className: "trd-actions"
|
|
930
|
+
}, /*#__PURE__*/_react.default.createElement(_Button.Button, {
|
|
931
|
+
variant: "primary",
|
|
932
|
+
disabled: hours <= 0,
|
|
933
|
+
onClick: handleSubmit
|
|
934
|
+
}, "Add Time Record"), /*#__PURE__*/_react.default.createElement(_Button.Button, {
|
|
935
|
+
variant: "secondary",
|
|
936
|
+
onClick: onClose
|
|
937
|
+
}, "Cancel"))), total > 0 ? /*#__PURE__*/_react.default.createElement("button", {
|
|
938
|
+
type: "button",
|
|
939
|
+
className: showRecords ? "trd-total" : "trd-total collapsed",
|
|
940
|
+
onClick: function onClick() {
|
|
941
|
+
return setShowRecords(function (prev) {
|
|
942
|
+
return !prev;
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
}, /*#__PURE__*/_react.default.createElement(_Icons.CollapseExpandSingleIcon, null), /*#__PURE__*/_react.default.createElement(_Typography.Header3, {
|
|
946
|
+
color: "theme-secondary",
|
|
947
|
+
tabularNums: true
|
|
948
|
+
}, "Total: ", (0, _utils.formatHours)(total, false, true), "h")) : null), showRecords && records.length > 0 ? /*#__PURE__*/_react.default.createElement("div", {
|
|
949
|
+
className: "trd-records"
|
|
950
|
+
}, records.map(function (record) {
|
|
951
|
+
return /*#__PURE__*/_react.default.createElement("div", {
|
|
952
|
+
key: record.id,
|
|
953
|
+
className: "trd-record"
|
|
954
|
+
}, /*#__PURE__*/_react.default.createElement("span", {
|
|
955
|
+
className: "trd-record-left"
|
|
956
|
+
}, /*#__PURE__*/_react.default.createElement(_Typography.Caption1, {
|
|
957
|
+
weight: "bold",
|
|
958
|
+
color: "secondary"
|
|
959
|
+
}, formatDateRange(record.date, record.date)), /*#__PURE__*/_react.default.createElement(_Typography.Body2, null, record.userName), /*#__PURE__*/_react.default.createElement(_Typography.Caption1, {
|
|
960
|
+
color: "secondary"
|
|
961
|
+
}, record.jobType)), /*#__PURE__*/_react.default.createElement(_Typography.Body2, {
|
|
962
|
+
weight: "bold",
|
|
963
|
+
tabularNums: true
|
|
964
|
+
}, (0, _utils.formatHours)(record.hours), "h"));
|
|
965
|
+
})) : null)));
|
|
966
|
+
};
|
|
967
|
+
//# sourceMappingURL=columns.js.map
|