@intellias/menu 1.0.0

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.
Files changed (174) hide show
  1. package/Menu.ts +625 -0
  2. package/README.md +121 -0
  3. package/assets/icons/arrow-down.svg +13 -0
  4. package/assets/icons/arrow-left.svg +3 -0
  5. package/assets/icons/arrow-submenu.svg +8 -0
  6. package/assets/icons/close.svg +3 -0
  7. package/assets/icons/delete.svg +13 -0
  8. package/assets/icons/faq-new.svg +4 -0
  9. package/assets/icons/kudos.svg +14 -0
  10. package/assets/icons/loading-spinner.svg +40 -0
  11. package/assets/icons/notification.svg +11 -0
  12. package/assets/icons/notifications/assessment.svg +10 -0
  13. package/assets/icons/notifications/buddy_program.svg +8 -0
  14. package/assets/icons/notifications/compensation_packages.svg +10 -0
  15. package/assets/icons/notifications/dsat.svg +8 -0
  16. package/assets/icons/notifications/feedback.svg +7 -0
  17. package/assets/icons/notifications/kudos.svg +4 -0
  18. package/assets/icons/notifications/overtime_requests.svg +15 -0
  19. package/assets/icons/notifications/pdu_program.svg +4 -0
  20. package/assets/icons/notifications/profile_update.svg +6 -0
  21. package/assets/icons/notifications/recommendation_program.svg +7 -0
  22. package/assets/icons/notifications/reminders.svg +4 -0
  23. package/assets/icons/notifications/sick_leaves.svg +5 -0
  24. package/assets/icons/notifications/smarts.svg +4 -0
  25. package/assets/icons/notifications/survey.svg +8 -0
  26. package/assets/icons/notifications/unpaid_leave.svg +4 -0
  27. package/assets/icons/notifications/vacations.svg +4 -0
  28. package/assets/icons/pause.svg +13 -0
  29. package/assets/icons/play.svg +13 -0
  30. package/assets/icons/smart.svg +14 -0
  31. package/assets/icons/smarts-kudos.svg +11 -0
  32. package/assets/icons/spinner-solid.svg +1 -0
  33. package/assets/icons/vacation.svg +14 -0
  34. package/assets/icons/visibility.svg +1 -0
  35. package/assets/intems-logo.svg +3 -0
  36. package/babel.config.js +6 -0
  37. package/buses/eventBus.ts +19 -0
  38. package/buses/events/GiveKudosEvent.ts +7 -0
  39. package/components/buttons/action-button/ActionButton.scss +133 -0
  40. package/components/buttons/action-button/ActionButton.ts +57 -0
  41. package/components/buttons/action-button/ActionButton.vue +50 -0
  42. package/components/buttons/secondary-button/SecondaryButton.scss +48 -0
  43. package/components/buttons/secondary-button/SecondaryButton.ts +28 -0
  44. package/components/buttons/secondary-button/SecondaryButton.vue +27 -0
  45. package/components/confirm/Confirm.scss +44 -0
  46. package/components/confirm/Confirm.ts +82 -0
  47. package/components/confirm/Confirm.vue +64 -0
  48. package/components/give-kudos-form/GiveKudosForm.scss +114 -0
  49. package/components/give-kudos-form/GiveKudosForm.ts +159 -0
  50. package/components/give-kudos-form/GiveKudosForm.vue +131 -0
  51. package/components/give-kudos-form/mixins/UserSelectMixin.ts +57 -0
  52. package/components/give-kudos-form/models/KudosShareModel.ts +6 -0
  53. package/components/give-kudos-form/models/UserListModel.ts +5 -0
  54. package/components/give-kudos-form/validators/InEnglishValidatorRegex.ts +17 -0
  55. package/components/give-kudos-form/validators/KudosBalance.ts +10 -0
  56. package/components/modal/Modal.scss +53 -0
  57. package/components/modal/Modal.ts +70 -0
  58. package/components/modal/Modal.vue +28 -0
  59. package/components/notifications-sidebar/NotificationsSidebar.scss +665 -0
  60. package/components/notifications-sidebar/NotificationsSidebar.ts +203 -0
  61. package/components/notifications-sidebar/NotificationsSidebar.vue +171 -0
  62. package/components/notifications-sidebar/models/BroadcastNotificationPayload.ts +8 -0
  63. package/components/notifications-sidebar/models/TotalCounter.ts +50 -0
  64. package/components/notifications-sidebar/notification/Notification.ts +17 -0
  65. package/components/notifications-sidebar/notification/Notification.vue +87 -0
  66. package/components/play-pause/PlayPause.scss +33 -0
  67. package/components/play-pause/PlayPause.ts +156 -0
  68. package/components/play-pause/PlayPause.vue +36 -0
  69. package/components/play-pause/PlayPauseStatus.ts +4 -0
  70. package/components/play-pause/helpers/LatestTrackedTimeDurationHelper.ts +55 -0
  71. package/components/play-pause/helpers/PauseHelper.ts +55 -0
  72. package/components/play-pause/helpers/TrackedTimeHelper.ts +80 -0
  73. package/components/preloader/Preloader.vue +34 -0
  74. package/components/request-loader/RequestLoader.scss +20 -0
  75. package/components/request-loader/RequestLoader.ts +80 -0
  76. package/components/request-loader/RequestLoader.vue +15 -0
  77. package/components/sub-menu/SubMenu.scss +33 -0
  78. package/components/sub-menu/SubMenu.ts +38 -0
  79. package/components/sub-menu/SubMenu.vue +21 -0
  80. package/components/time-ago/TimeAgo.ts +34 -0
  81. package/components/time-ago/TimeAgo.vue +9 -0
  82. package/components/user-picture/UserPicture.scss +62 -0
  83. package/components/user-picture/UserPicture.ts +105 -0
  84. package/components/user-picture/UserPicture.vue +23 -0
  85. package/components/v-select-intems/VSelectIntems.ts +112 -0
  86. package/components/v-select-intems/VSelectIntems.vue +68 -0
  87. package/components/v-select-intems/open-indicator/OpenIndicator.vue +3 -0
  88. package/dist/css/1.css +85 -0
  89. package/dist/css/2.css +34 -0
  90. package/dist/css/3.css +34 -0
  91. package/dist/css/4.css +34 -0
  92. package/dist/css/5.css +34 -0
  93. package/dist/css/main.css +1564 -0
  94. package/dist/html/app.html +19 -0
  95. package/dist/html/styles.html +1 -0
  96. package/dist/images/intems-logo.svg +3 -0
  97. package/dist/js/0.js +315 -0
  98. package/dist/js/1.js +313 -0
  99. package/dist/js/2.js +217 -0
  100. package/dist/js/3.js +181 -0
  101. package/dist/js/4.js +181 -0
  102. package/dist/js/5.js +181 -0
  103. package/dist/js/6.js +47 -0
  104. package/dist/js/main.js +7465 -0
  105. package/dist/js/vue.js +15 -0
  106. package/helpers/GeneralHelper.ts +61 -0
  107. package/helpers/PublisherSubscriber.ts +34 -0
  108. package/helpers/QueryFilter.ts +204 -0
  109. package/helpers/TimeHelper.ts +54 -0
  110. package/helpers/Validations.ts +7 -0
  111. package/helpers/model/ModelHelper.ts +155 -0
  112. package/helpers/model/decorators/AsCollection.ts +26 -0
  113. package/helpers/model/decorators/AsModel.ts +25 -0
  114. package/helpers/model/decorators/DateTime.ts +26 -0
  115. package/helpers/model/decorators/TimeDuration.ts +33 -0
  116. package/helpers/moment/Duration.ts +64 -0
  117. package/helpers/moment/Moment.ts +17 -0
  118. package/helpers/moment/index.d.ts +20 -0
  119. package/helpers/response/AxiosProxy.ts +72 -0
  120. package/helpers/response/ErrorsToToastHelper.ts +42 -0
  121. package/helpers/response/ResponseCode.ts +16 -0
  122. package/helpers/response/ResponseHelper.ts +42 -0
  123. package/helpers/response/ResponseInterface.ts +34 -0
  124. package/helpers/response/ResponseState.ts +6 -0
  125. package/jsconfig.json +19 -0
  126. package/mixins/IntemsMath.ts +29 -0
  127. package/mixins/notifications/ActionProcessing.ts +134 -0
  128. package/mixins/notifications/confirms/AssetRejectionConfirm.vue +59 -0
  129. package/mixins/notifications/confirms/SickLeaveRejectionConfirm.vue +31 -0
  130. package/mixins/notifications/confirms/UnpaidLeaveRejectionConfirm.vue +31 -0
  131. package/mixins/notifications/confirms/VacationRejectionConfirm.vue +31 -0
  132. package/models/AssetModel.ts +80 -0
  133. package/models/AssetRequestModel.ts +25 -0
  134. package/models/ConfigModel.ts +20 -0
  135. package/models/EmployeeStartEndDateModel.ts +16 -0
  136. package/models/EmploymentTypeModel.ts +95 -0
  137. package/models/LogoutModel.ts +3 -0
  138. package/models/Model.ts +12 -0
  139. package/models/ModuleOptions.ts +24 -0
  140. package/models/PublicInfoModel.ts +38 -0
  141. package/models/PublicLocationModel.ts +36 -0
  142. package/models/StatusModel.ts +8 -0
  143. package/models/TrackedTimeModel.ts +32 -0
  144. package/models/location/PublicLocationModel.ts +36 -0
  145. package/models/timezones/LocalTimezoneHistoryModel.ts +8 -0
  146. package/models/timezones/TimezoneModel.ts +9 -0
  147. package/models/user/ExtendedInfoModel.ts +37 -0
  148. package/models/user/NotificationActionModel.ts +53 -0
  149. package/models/user/NotificationModel.ts +84 -0
  150. package/models/user/PublicInfoModel.ts +38 -0
  151. package/models/user/WelcomeProgram.ts +4 -0
  152. package/package.json +117 -0
  153. package/repositories/BaseRepository.ts +42 -0
  154. package/repositories/BaseUrl.ts +107 -0
  155. package/repositories/Repository.ts +291 -0
  156. package/repositories/ResourceRepository.ts +99 -0
  157. package/repositories/TrackedTimeRepository.ts +43 -0
  158. package/repositories/UserRepository.ts +75 -0
  159. package/repositories/subscribers/CreateSubscribers.ts +28 -0
  160. package/repositories/subscribers/DefaultSubscribers.ts +82 -0
  161. package/repositories/subscribers/DeleteSubscribers.ts +28 -0
  162. package/repositories/subscribers/DownloadSubscribers.ts +28 -0
  163. package/repositories/subscribers/Subscribers.ts +13 -0
  164. package/repositories/subscribers/UpdateSubscribers.ts +32 -0
  165. package/scss/components/_global.scss +66 -0
  166. package/scss/components/_mixins.scss +31 -0
  167. package/scss/components/_tooltip.scss +45 -0
  168. package/scss/components/_utils.scss +26 -0
  169. package/scss/components/_variables.scss +103 -0
  170. package/scss/main.scss +7 -0
  171. package/shims-vue.d.ts +9 -0
  172. package/tsconfig.json +31 -0
  173. package/webpack.config.js +126 -0
  174. package/wrapper.ts +51 -0
@@ -0,0 +1,61 @@
1
+ import axios from "axios";
2
+ import { Collection } from "collect.js";
3
+
4
+ export function getRandomIntegerBetween(min: number, max: number): number {
5
+ const randomBuffer = new Uint32Array(1);
6
+
7
+ window.crypto.getRandomValues(randomBuffer);
8
+
9
+ const randomNumber = randomBuffer[0] / (0xffffffff + 1);
10
+
11
+ return Math.floor(randomNumber * (max - min + 1)) + min;
12
+ }
13
+
14
+ export function sortObjectByKeysCaseInsensitive(
15
+ object: unknown
16
+ ): Record<string, unknown> {
17
+ return (object instanceof Collection ? object : Object.keys(object))
18
+ .sort((a, b) => a.localeCompare(b))
19
+ .reduce((sorted, key) => {
20
+ sorted[key] = object[key];
21
+ return sorted;
22
+ }, {});
23
+ }
24
+
25
+ export function downloadFile(filePath: string, fileName?: string): void {
26
+ axios({
27
+ url: filePath,
28
+ method: "GET",
29
+ responseType: "blob",
30
+ }).then((response) => {
31
+ const fileURL = window.URL.createObjectURL(new Blob([response.data]));
32
+ const fileLink = document.createElement("a");
33
+
34
+ fileLink.href = fileURL;
35
+ fileLink.setAttribute(
36
+ "download",
37
+ fileName || filePath.replace(/^.*[\\\/]/, "")
38
+ );
39
+ document.body.appendChild(fileLink);
40
+
41
+ fileLink.click();
42
+ });
43
+ }
44
+
45
+ export function postForm(url: string, data?: Record<string, string>): void {
46
+ const form = document.createElement("form");
47
+ form.method = "POST";
48
+ form.action = url;
49
+
50
+ Object.entries(data).forEach(([key, value]: [key: string, value: string]) => {
51
+ const input = document.createElement("input");
52
+ input.type = "hidden";
53
+ input.name = key;
54
+ input.value = value;
55
+ form.append(input);
56
+ });
57
+
58
+ document.body.appendChild(form);
59
+
60
+ form.submit();
61
+ }
@@ -0,0 +1,34 @@
1
+ // Publisher-Subscriber pattern interface
2
+ type Callback = (data: unknown) => void;
3
+ type Subscriber<Event> = Map<Event, Set<Callback>>;
4
+
5
+ export default abstract class PublisherSubscriber<Event> {
6
+ protected subscribers: Map<string, Subscriber<Event>> = new Map();
7
+
8
+ subscribe(event: Event, callback: Callback, context = ""): void {
9
+ if (!this.subscribers.has(context)) {
10
+ this.createSubscriber(context);
11
+ }
12
+ if (!this.subscribers.get(context).has(event)) {
13
+ this.createEvent(context, event);
14
+ }
15
+ this.subscribers.get(context).get(event).add(callback);
16
+ }
17
+
18
+ protected createSubscriber(context: string): void {
19
+ this.subscribers.set(context, new Map());
20
+ }
21
+
22
+ protected createEvent(context: string, event: Event): void {
23
+ this.subscribers.get(context).set(event, new Set());
24
+ }
25
+
26
+ protected publish(event: Event, data: unknown, context = ""): void {
27
+ const subscriber = this.subscribers.get(context);
28
+ if (subscriber && subscriber.has(event)) {
29
+ subscriber.get(event).forEach((callback) => {
30
+ callback(data);
31
+ });
32
+ }
33
+ }
34
+ }
@@ -0,0 +1,204 @@
1
+ import { Moment } from "moment";
2
+
3
+ enum SortDirection {
4
+ asc = "asc",
5
+ desc = "desc",
6
+ }
7
+ const MAX_ITEMS_ON_PAGE = 25;
8
+ export default class QueryFilter {
9
+ private query = {};
10
+ max_on_page = MAX_ITEMS_ON_PAGE as number;
11
+ total = undefined as number;
12
+ end_reached = false as boolean;
13
+ loading = false as boolean;
14
+
15
+ equal(key: string, value: string | number | Moment): this {
16
+ if (value) {
17
+ this.query[`${key}$eq`] = value;
18
+ }
19
+
20
+ return this;
21
+ }
22
+
23
+ custom(
24
+ key: string,
25
+ value: string | number | boolean | Moment | string[]
26
+ ): this {
27
+ if (value !== null) {
28
+ this.query[key] = value;
29
+ }
30
+
31
+ return this;
32
+ }
33
+
34
+ scope(key: string, value: string | number | Moment): this {
35
+ if (value) {
36
+ this.query[`${key}$scope`] = value;
37
+ }
38
+
39
+ return this;
40
+ }
41
+
42
+ jsonContains(key: string, value: string | number | Moment): this {
43
+ if (value) {
44
+ this.query[`${key}$jc`] = value;
45
+ }
46
+
47
+ return this;
48
+ }
49
+
50
+ notEqual(key: string, value: string | number | Moment): this {
51
+ if (value) {
52
+ this.query[`${key}$neq`] = value;
53
+ }
54
+
55
+ return this;
56
+ }
57
+
58
+ gte(key: string, value: string | number | Moment): this {
59
+ if (value) {
60
+ this.query[`${key}$gte`] = value;
61
+ }
62
+
63
+ return this;
64
+ }
65
+
66
+ lte(key: string, value: string | number | Moment): this {
67
+ if (value) {
68
+ this.query[`${key}$lte`] = value;
69
+ }
70
+
71
+ return this;
72
+ }
73
+
74
+ gt(key: string, value: string | number | Moment): this {
75
+ if (value) {
76
+ this.query[`${key}$gt`] = value;
77
+ }
78
+
79
+ return this;
80
+ }
81
+
82
+ lt(key: string, value: string | number | Moment): this {
83
+ if (value) {
84
+ this.query[`${key}$lt`] = value;
85
+ }
86
+
87
+ return this;
88
+ }
89
+
90
+ like(key: string, value: string | number | Moment): this {
91
+ if (value) {
92
+ this.query[`${key}$like`] = value;
93
+ }
94
+
95
+ return this;
96
+ }
97
+
98
+ offset(offset: number): this {
99
+ this.query[`@offset`] = offset;
100
+
101
+ return this;
102
+ }
103
+
104
+ take(take: number = this.max_on_page): this {
105
+ this.query[`@take`] = take;
106
+
107
+ return this;
108
+ }
109
+
110
+ page(page: number | null, max_on_page = this.max_on_page): this {
111
+ if (page) {
112
+ this.offset(page * max_on_page - max_on_page);
113
+ this.take(max_on_page);
114
+ this.custom("page", page);
115
+ }
116
+
117
+ return this;
118
+ }
119
+
120
+ firstPage(): this {
121
+ this.end_reached = false;
122
+ this.page(1);
123
+
124
+ return this;
125
+ }
126
+
127
+ unsetPagination(): this {
128
+ this.end_reached = false;
129
+ this.offset(undefined);
130
+
131
+ return this;
132
+ }
133
+
134
+ nextPage(): this {
135
+ if (this.query[`@offset`] !== undefined) {
136
+ this.offset(this.query[`@offset`] + this.max_on_page);
137
+ } else {
138
+ this.offset(0);
139
+ }
140
+ this.take(this.max_on_page);
141
+
142
+ return this;
143
+ }
144
+
145
+ unlimited(): this {
146
+ this.take(null);
147
+ this.offset(null);
148
+ this.page(null);
149
+
150
+ return this;
151
+ }
152
+
153
+ sort(key: number, direction: SortDirection = SortDirection.asc): this {
154
+ this.query[`@sort,${direction}`] = key;
155
+
156
+ return this;
157
+ }
158
+
159
+ in(key: string, values: Array<string | number | Moment>): this {
160
+ if (values) {
161
+ this.query[`${key}$in`] = values.join(",");
162
+ }
163
+
164
+ return this;
165
+ }
166
+
167
+ notIn(key: string, values: Array<string | number | Moment>): this {
168
+ if (values) {
169
+ this.query[`${key}$nin`] = values.join(",");
170
+ }
171
+
172
+ return this;
173
+ }
174
+
175
+ endList(): void {
176
+ this.end_reached = true;
177
+ }
178
+
179
+ startLoading(): void {
180
+ this.loading = true;
181
+ }
182
+
183
+ endLoading(): void {
184
+ this.loading = false;
185
+ }
186
+
187
+ maxOnPage(max_on_page: number): this {
188
+ this.max_on_page = max_on_page;
189
+
190
+ return this;
191
+ }
192
+
193
+ getKey(key: string): unknown {
194
+ return this.query[key];
195
+ }
196
+
197
+ clone(): QueryFilter {
198
+ return Object.assign(Object.create(Object.getPrototypeOf(this)), this);
199
+ }
200
+
201
+ toObject(): Record<string, unknown> {
202
+ return this.query;
203
+ }
204
+ }
@@ -0,0 +1,54 @@
1
+ import { Moment } from "moment";
2
+ import moment from "./moment/Moment";
3
+
4
+ const workingHours = 8;
5
+
6
+ const formatDuration = (duration: number) => {
7
+ const hours = Math.trunc(duration / 60);
8
+ const minutes = `0${duration % 60}`.slice(-2);
9
+ return `${hours}:${minutes}`;
10
+ };
11
+
12
+ const fiveMinutesRound = (minutes: number) => {
13
+ return Math.round(minutes / 5) * 5;
14
+ };
15
+
16
+ /**
17
+ * Returns difference between two dates rounded using 5 minutes grid
18
+ * @param from
19
+ * @param to
20
+ */
21
+ const diffMinutes = (from: Moment, to: Moment) => {
22
+ const minutes = to.diff(from, "minutes", true);
23
+ return fiveMinutesRound(minutes);
24
+ };
25
+
26
+ /**
27
+ * Example 1: start = 16:03:00, to: 16:04:30 (can be current time), return = 16:05:30
28
+ * Example 2: start = 16:03:00, to: 16:05:30, return = 16:10:30 (not 16:08:00 because now 5 minutes round is 5 minutes
29
+ * and next change (10 minutes) will appear in 16:10:30)
30
+ * Example 3: start = 16:03:00, to: 16:07:00, return = 16:10:30
31
+ * Example 4: start = 16:03:00, to: 16:10:00 (can be current time), return = 16:10:30
32
+ * Example 5: start = 16:10:35, to: 16:10:00 (can be current time), return = 16:15:30
33
+ *
34
+ * @todo write tests!!!
35
+ * @param {Moment} from
36
+ * @param {Moment} to
37
+ */
38
+ const nextTimeFiveMinutesWillRound = (from: Moment, to: Moment) => {
39
+ const minutesDiff = to.diff(from, "minutes", true);
40
+ const roundedTo5Minutes = fiveMinutesRound(minutesDiff);
41
+ const timeElapsed = minutesDiff - roundedTo5Minutes;
42
+ const leftToHalfOf5Minutes = 2.5 - timeElapsed;
43
+
44
+ return to.clone().add(leftToHalfOf5Minutes, "minutes");
45
+ };
46
+
47
+ export {
48
+ workingHours,
49
+ formatDuration,
50
+ diffMinutes,
51
+ fiveMinutesRound,
52
+ nextTimeFiveMinutesWillRound,
53
+ moment,
54
+ };
@@ -0,0 +1,7 @@
1
+ const onlyLatin = (value: string): boolean => !/[^\u0020-\u007e\u00a0-\u00ff]/g.test(value);
2
+
3
+ const onlyCyrillic = (value: string): boolean => /^[^A-Za-z]*$/g.test(value);
4
+
5
+ const monthReportingLimit = (value: number): boolean => value >= 1 && value <= 300;
6
+
7
+ export { onlyLatin, onlyCyrillic, monthReportingLimit };
@@ -0,0 +1,155 @@
1
+ import { collect, Collection as BaseCollection } from "collect.js";
2
+ import * as moment from "moment";
3
+
4
+ /**
5
+ * Map resource to model
6
+ * @param target model class
7
+ * @param source resource that we get from api
8
+ */
9
+ const map = (target, source) => {
10
+ if (Array.isArray(source)) {
11
+ return mapCollection(source, target);
12
+ }
13
+
14
+ if (!target) {
15
+ return;
16
+ }
17
+ const model = new target();
18
+ for (const key of Object.keys(model)) {
19
+ model[key] = getModelPropertyValue(source[key], model[key]);
20
+ }
21
+
22
+ return model;
23
+ };
24
+
25
+ const tree = (collection) => {
26
+ return collection
27
+ .filter((item) => !item.parent)
28
+ .map((parent) => {
29
+ parent.children = collection.where("parent.id", parent.id);
30
+
31
+ return parent;
32
+ });
33
+ };
34
+
35
+ const arrayTree = (collection) => {
36
+ return collection
37
+ .filter((item) => !item.parent)
38
+ .map((parent) => {
39
+ parent.children = [];
40
+ collection
41
+ .where("parent.id", parent.id)
42
+ .each((child) => parent.children.push(child));
43
+ return parent;
44
+ })
45
+ .all();
46
+ };
47
+
48
+ /**
49
+ * Get value for model object property using source property (api resource property)
50
+ * @param sourceProperty
51
+ * @param modelProperty
52
+ */
53
+ const getModelPropertyValue = (sourceProperty, modelProperty) => {
54
+ // Todo: Maybe all of this move to fabric with polymorph classes (move to decorators, then this method will not be needed
55
+ if (sourceProperty === undefined) {
56
+ return undefined;
57
+ }
58
+ if (sourceProperty === null) {
59
+ return null;
60
+ }
61
+
62
+ if (modelProperty instanceof TypeModel) {
63
+ return map(modelProperty.getModel(), sourceProperty);
64
+ }
65
+
66
+ if (modelProperty instanceof TypeCollection) {
67
+ return mapCollection(sourceProperty, modelProperty.getModel());
68
+ }
69
+
70
+ return sourceProperty;
71
+ };
72
+
73
+ /**
74
+ * Map array to Collection with models
75
+ * @param collection
76
+ * @param model
77
+ */
78
+ const mapCollection = <TModel>(
79
+ collection: Array<Partial<TModel>>,
80
+ model: TModel
81
+ ) => {
82
+ return collect(collection.map((source) => map(model, source)));
83
+ };
84
+
85
+ /**
86
+ * Special type that express that we expect get model from resource(api)
87
+ * @deprecated Use @AsModel decorator instead (example in ExtendedInfoModel)
88
+ * @param model
89
+ */
90
+ const asModel = <TModel>(model: TModel) => {
91
+ return new TypeModel(model);
92
+ };
93
+
94
+ /**
95
+ * Special type that express that we expect get collection(array) from resource(api)
96
+ * @deprecated Use @AsCollection decorator instead (example in ExtendedInfoModel)
97
+ * @param model
98
+ */
99
+
100
+ const asCollection = <TModel extends new (...args: any) => any>(
101
+ model: TModel
102
+ ): TypeCollection<TModel> | TypeCollection<InstanceType<TModel>> => {
103
+ return new TypeCollection(model);
104
+ };
105
+
106
+ // TODO: Move somewhere
107
+ const updateProperties = <T, U extends Partial<T>>(target: T, source: U): T => {
108
+ for (const key of Object.keys(source)) {
109
+ target[key] = cloneValue(source[key]);
110
+ }
111
+ return target;
112
+ };
113
+
114
+ const Moment = moment().constructor;
115
+ const cloneValue = (value) => {
116
+ const isMomentInstance = value instanceof Moment;
117
+ return isMomentInstance ? moment(value) : value;
118
+ };
119
+
120
+ abstract class Type<TModel> {
121
+ private readonly model: TModel;
122
+
123
+ public constructor(model: TModel) {
124
+ this.model = model;
125
+ }
126
+
127
+ getModel() {
128
+ return this.model;
129
+ }
130
+ }
131
+
132
+ /** This is helper class to specify type collection with some model for mapping */
133
+ class TypeCollection<TModel> extends Type<TModel> {}
134
+ /** This is helper class to specify type model for mapping */
135
+ class TypeModel<TModel> extends Type<TModel> {}
136
+
137
+ // Todo: maybe do same as we do with Moment class, just moment().constructor is class
138
+ // This code (class Collection) gives us possibility to check instanceof Collection (collect.js).
139
+ // Default Collection from collect.js causes an error: "TypeError: Right-hand side of 'instanceof' is not an object",
140
+ // because it is just a declaration
141
+ class Collection {}
142
+ Object.assign(Collection, collect().__proto__);
143
+
144
+ // type Collection<Item> = BaseCollection<Item>;
145
+
146
+ export {
147
+ asModel,
148
+ asCollection,
149
+ TypeCollection,
150
+ map,
151
+ Collection,
152
+ updateProperties,
153
+ tree,
154
+ arrayTree,
155
+ };
@@ -0,0 +1,26 @@
1
+ import { collect } from "collect.js";
2
+ import { map } from "../ModelHelper";
3
+
4
+ export default (property: string, instance: Object) => {
5
+ return function <T extends { new (...args: any[]): {} }>(constructor: T) {
6
+ return class extends constructor {
7
+ constructor(...args: any[]) {
8
+ super(...args);
9
+
10
+ let value;
11
+ Object.defineProperty(this, property, {
12
+ enumerable: true,
13
+ get: () => {
14
+ return value;
15
+ },
16
+ set: (newValue: Array<any> | undefined) => {
17
+ value =
18
+ newValue !== undefined
19
+ ? collect(newValue.map((source) => map(instance, source)))
20
+ : newValue;
21
+ },
22
+ });
23
+ }
24
+ };
25
+ };
26
+ };
@@ -0,0 +1,25 @@
1
+ import { map } from "../ModelHelper";
2
+
3
+ export default (property: string, instance: Object) => {
4
+ return function <T extends { new (...args: any[]): {} }>(constructor: T) {
5
+ return class extends constructor {
6
+ constructor(...args: any[]) {
7
+ super(...args);
8
+
9
+ let value;
10
+ Object.defineProperty(this, property, {
11
+ enumerable: true,
12
+ get: () => {
13
+ return value;
14
+ },
15
+ set: (newValue: Object | undefined) => {
16
+ value =
17
+ newValue !== undefined && newValue !== null
18
+ ? map(instance, newValue)
19
+ : newValue;
20
+ },
21
+ });
22
+ }
23
+ };
24
+ };
25
+ };
@@ -0,0 +1,26 @@
1
+ import { Moment } from "moment";
2
+ import { moment } from "../../TimeHelper";
3
+
4
+ export default (property) => {
5
+ return function <T extends { new (...args: any[]): {} }>(constructor: T) {
6
+ return class extends constructor {
7
+ constructor(...args: any[]) {
8
+ super(...args);
9
+
10
+ let value;
11
+ Object.defineProperty(this, property, {
12
+ enumerable: true,
13
+ get: () => {
14
+ return value;
15
+ },
16
+ set: (newValue: Moment | undefined | null) => {
17
+ value =
18
+ newValue !== undefined && newValue !== null
19
+ ? moment(newValue)
20
+ : newValue;
21
+ },
22
+ });
23
+ }
24
+ };
25
+ };
26
+ };
@@ -0,0 +1,33 @@
1
+ import { Duration as DurationType } from "moment";
2
+ import { moment } from "../../TimeHelper";
3
+
4
+ const Duration = moment.duration().constructor;
5
+
6
+ export default (property) => {
7
+ return function <T extends { new (...args: any[]): {} }>(constructor: T) {
8
+ return class extends constructor {
9
+ constructor(...args: any[]) {
10
+ super(...args);
11
+
12
+ let value = moment.duration(0);
13
+ Object.defineProperty(this, property, {
14
+ enumerable: true,
15
+ get: () => {
16
+ return value;
17
+ },
18
+ set: (newValue: DurationType | number) => {
19
+ if (!(newValue instanceof Duration)) {
20
+ let minutes = Number(newValue);
21
+ if (Number.isNaN(minutes)) {
22
+ minutes = 0;
23
+ }
24
+ newValue = moment.duration(minutes, "minutes");
25
+ }
26
+
27
+ value = moment.duration(newValue);
28
+ },
29
+ });
30
+ }
31
+ };
32
+ };
33
+ };
@@ -0,0 +1,64 @@
1
+ import { DurationInputArg1, DurationInputArg2 } from "moment";
2
+ import * as moment from "moment";
3
+ import { Duration } from "./index";
4
+ import { workingHours } from "../TimeHelper";
5
+
6
+ const duration = (
7
+ inp?: DurationInputArg1,
8
+ unit?: DurationInputArg2
9
+ ): Duration => {
10
+ const momentDurationPrototype = moment.duration(inp, unit);
11
+ const intemsDuration: Duration = Object.create(momentDurationPrototype);
12
+
13
+ // Is used when we do some primitive manipulations with class (eg. Duration + 5, will transform Duration into minutes and add 5)
14
+ intemsDuration.valueOf = () => {
15
+ return intemsDuration.asMinutes();
16
+ };
17
+
18
+ intemsDuration.clone = () => {
19
+ return duration(intemsDuration);
20
+ };
21
+
22
+ intemsDuration.minutesWithLeadingZeroes = () => {
23
+ return intemsDuration.minutes().toString().padStart(2, "0");
24
+ };
25
+
26
+ intemsDuration.hoursAsNumber = () => {
27
+ return Math.floor(intemsDuration.asHours());
28
+ };
29
+
30
+ intemsDuration.humanizedHours = () => {
31
+ return intemsDuration.hoursAsNumber() === 1 ? "hour" : "hours";
32
+ };
33
+
34
+ intemsDuration.humanizedMinutes = () => {
35
+ return intemsDuration.minutes() === 1 ? "minute" : "minutes";
36
+ };
37
+
38
+ intemsDuration.formatted = () => {
39
+ return `${intemsDuration.hoursAsNumber()}:${intemsDuration.minutesWithLeadingZeroes()}`;
40
+ };
41
+
42
+ intemsDuration.formattedInLongForm = () => {
43
+ const formattedHours = `${intemsDuration.hoursAsNumber()} ${intemsDuration.humanizedHours()}`;
44
+ const formattedMinutes = `${intemsDuration.minutes()} ${intemsDuration.humanizedMinutes()}`;
45
+
46
+ if (intemsDuration.hoursAsNumber() === 0) {
47
+ return formattedMinutes;
48
+ }
49
+
50
+ return intemsDuration.minutes() === 0
51
+ ? formattedHours
52
+ : `${formattedHours} ${formattedMinutes}`;
53
+ };
54
+
55
+ intemsDuration.formattedInWorkingDays = () => {
56
+ const workingDays = Math.floor(intemsDuration.asHours() / workingHours);
57
+ const humanizedDays = workingDays === 1 ? "day" : "days";
58
+ return `${workingDays} ${humanizedDays}`;
59
+ };
60
+
61
+ return intemsDuration;
62
+ };
63
+
64
+ export default duration;