@activecollab/components 2.0.365 → 2.0.367

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