trithuc-mvc-react 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.
@@ -0,0 +1,498 @@
1
+ /* eslint-disable react/prop-types */
2
+ "use client";
3
+ import { useEffect, useState } from "react";
4
+ import * as dateFns from "date-fns";
5
+ import { Box, Paper, Stack } from "@mui/material";
6
+ // import "tailwindcss/tailwind.css";
7
+
8
+ const isBetween = (date, from, to, inclusivity) => {
9
+ if (!["()", "[]", "(]", "[)"].includes(inclusivity)) {
10
+ throw new Error("Inclusivity parameter must be one of (), [], (], [)");
11
+ }
12
+
13
+ const isBeforeEqual = inclusivity[0] === "[",
14
+ isAfterEqual = inclusivity[1] === "]";
15
+
16
+ return (
17
+ (isBeforeEqual ? dateFns.isEqual(from, date) || dateFns.isBefore(from, date) : dateFns.isBefore(from, date)) &&
18
+ (isAfterEqual ? dateFns.isEqual(to, date) || dateFns.isAfter(to, date) : dateFns.isAfter(to, date))
19
+ );
20
+ };
21
+ const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
22
+
23
+ const StaticDateRangePicker = ({ value, onUpdate }) => {
24
+ const [currCalendar, setCurrCalendar] = useState([]);
25
+ const [nextCalendar, setNextCalendar] = useState([]);
26
+ const [currMonth, setCurrMonth] = useState(new Date().getMonth());
27
+ const [currYear, setCurrYear] = useState(new Date().getFullYear());
28
+
29
+ const [dateRange, setDateRange] = useState({
30
+ startDate: value?.startDate,
31
+ endDate: value?.endDate
32
+ });
33
+ // Thanks to https://github.com/date-fns/date-fns/issues/366#issuecomment-270408980
34
+
35
+ const generateCalendar = ({ month, year }) => {
36
+ const startOfMonth = dateFns.startOfMonth(new Date(year, month));
37
+ const endOfMonth = dateFns.endOfMonth(new Date(year, month));
38
+ const startDay = startOfMonth.getDay();
39
+ const daysInMonth = dateFns.getDaysInMonth(startOfMonth);
40
+ const days = [...Array(daysInMonth).keys()].map((v) => v + 1);
41
+ const calendar = [...Array(42).keys()].map((v) => {
42
+ if (v < startDay) {
43
+ return null;
44
+ }
45
+ if (v > daysInMonth + startDay - 1) {
46
+ return null;
47
+ }
48
+ return days[v - startDay];
49
+ });
50
+ return calendar;
51
+ };
52
+
53
+ useEffect(() => {
54
+ const _currCalendar = generateCalendar({
55
+ month: new Date(currYear, currMonth).getMonth(),
56
+ year: new Date(currYear, currMonth).getFullYear()
57
+ });
58
+ setCurrCalendar(_currCalendar);
59
+
60
+ const _nextCalendar = generateCalendar({
61
+ month: dateFns.add(new Date(currYear, currMonth), { months: 1 }).getMonth(),
62
+ year: dateFns.add(new Date(currYear, currMonth), { months: 1 }).getFullYear()
63
+ });
64
+ setNextCalendar(_nextCalendar);
65
+ }, [currMonth, currYear]);
66
+
67
+ useEffect(() => {
68
+ onUpdate?.(dateRange);
69
+ }, [dateRange]);
70
+
71
+ useEffect(() => {
72
+ if (!value) return;
73
+
74
+ if (value.startDate && !dateFns.isSameDay(value.startDate, dateRange.startDate)) {
75
+ if (dateFns.isBefore(value.startDate, dateRange.endDate)) {
76
+ setDateRange((old) => ({ ...old, startDate: value.startDate }));
77
+ } else {
78
+ setDateRange((old) => ({ startDate: value.startDate, endDate: null }));
79
+ }
80
+ let month = value.startDate.getMonth();
81
+ let year = value.startDate.getFullYear();
82
+ if (isNaN(month) || isNaN(year)) return;
83
+ setCurrMonth(month);
84
+ setCurrYear(year);
85
+ } else if (value.endDate && !dateFns.isSameDay(value.endDate, dateRange.endDate)) {
86
+ setDateRange((old) => ({ ...old, endDate: value.endDate }));
87
+ let month = value.endDate.getMonth();
88
+ let year = value.endDate.getFullYear();
89
+ if (isNaN(month) || isNaN(year)) return;
90
+ setCurrMonth(month - 1);
91
+ setCurrYear(year);
92
+ }
93
+ }, [value]);
94
+
95
+ const handleUpdateDateRangeOnLeftCalendar = (day) => {
96
+ let thisDate = new Date(currYear, currMonth, day);
97
+
98
+ if (!dateRange.startDate) {
99
+ setDateRange((d) => ({
100
+ ...d,
101
+ startDate: thisDate
102
+ // moment({
103
+ // day: v,
104
+ // month: currMonth,
105
+ // year: currYear,
106
+ // }),
107
+ }));
108
+ } else if (dateRange.startDate && !dateRange.endDate) {
109
+ if (dateFns.isBefore(thisDate, dateRange.startDate)) {
110
+ setDateRange({
111
+ startDate: thisDate,
112
+ // moment({
113
+ // day: v,
114
+ // month: currMonth,
115
+ // year: currYear,
116
+ // }),
117
+ endDate: null
118
+ });
119
+ } else {
120
+ setDateRange((d) => ({
121
+ ...d,
122
+ endDate: thisDate
123
+ // moment({
124
+ // day: v,
125
+ // month: currMonth,
126
+ // year: currYear,
127
+ // }),
128
+ }));
129
+ }
130
+ } else if (dateRange.startDate && dateRange.endDate) {
131
+ setDateRange({
132
+ startDate: thisDate,
133
+ // moment({
134
+ // day: v,
135
+ // month: currMonth,
136
+ // year: currYear,
137
+ // }),
138
+ endDate: null
139
+ });
140
+ }
141
+ };
142
+ const handleUpdateDateRangeOnRightCalendar = (day) => {
143
+ let thisDate = dateFns.add(new Date(currYear, currMonth, day), { months: 1 });
144
+
145
+ if (!dateRange.startDate) {
146
+ setDateRange((d) => ({
147
+ ...d,
148
+ startDate: thisDate
149
+ }));
150
+
151
+ // startDate: moment({
152
+ // day: v,
153
+ // month: moment({
154
+ // day: v,
155
+ // month: currMonth,
156
+ // year: currYear,
157
+ // })
158
+ // .add(1, "month")
159
+ // .month(),
160
+ // year: moment({
161
+ // day: v,
162
+ // month: currMonth,
163
+ // year: currYear,
164
+ // })
165
+ // .add(1, "month")
166
+ // .year(),
167
+ // }),
168
+ // }))
169
+ } else if (dateRange.startDate && !dateRange.endDate) {
170
+ if (dateFns.isBefore(thisDate, dateRange.startDate)) {
171
+ setDateRange({
172
+ startDate: thisDate,
173
+ // moment({
174
+ // day: v,
175
+ // month: currMonth,
176
+ // year: currYear,
177
+ // }),
178
+ endDate: null
179
+ });
180
+ } else {
181
+ setDateRange((d) => ({
182
+ ...d,
183
+ endDate: thisDate
184
+ // moment({
185
+ // day: v,
186
+ // month: currMonth,
187
+ // year: currYear,
188
+ // }),
189
+ }));
190
+ }
191
+ } else if (dateRange.startDate && dateRange.endDate) {
192
+ setDateRange({
193
+ startDate: thisDate,
194
+ // moment({
195
+ // day: v,
196
+ // month: moment({
197
+ // day: v,
198
+ // month: currMonth,
199
+ // year: currYear,
200
+ // })
201
+ // .add(1, "month")
202
+ // .month(),
203
+ // year: moment({
204
+ // day: v,
205
+ // month: currMonth,
206
+ // year: currYear,
207
+ // })
208
+ // .add(1, "month")
209
+ // .year(),
210
+ // }),
211
+ endDate: null
212
+ });
213
+ }
214
+ };
215
+
216
+ const currTitle = currYear && currMonth ? dateFns.format(new Date(currYear, currMonth), "MMMM") + new Date(currYear, currMonth).getFullYear() : "";
217
+ return (
218
+ <Paper>
219
+ <Box sx={{ display: "flex" }} className="date-range-piker-wrapper">
220
+ <div className="drop-shadow-sm shadow-sm min-w-[10rem] p-3 rounded-lg border border-r-0 flex flex-col">
221
+ <div className="flex justify-between items-center">
222
+ <h3 className="text-lg">
223
+ {" "}
224
+ {dateFns.format(new Date(currYear, currMonth), "MMMM")} {new Date(currYear, currMonth).getFullYear()}
225
+ </h3>
226
+
227
+ <Stack direction="row" spacing={2} alignItems={"center"}>
228
+ <button
229
+ type="button"
230
+ className="p-2 rounded-lg hover:bg-gray-300 border drop-shadow-sm"
231
+ onClick={() => {
232
+ const d = dateFns.sub(new Date(currYear, currMonth), {
233
+ months: 1
234
+ });
235
+ setCurrMonth(d.getMonth());
236
+ setCurrYear(d.getFullYear());
237
+ }}
238
+ >
239
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-4 h-4">
240
+ <path strokeLinecap="round" strokeLinejoin="round" d="M15.75 19.5L8.25 12l7.5-7.5" />
241
+ </svg>
242
+ </button>
243
+ <button
244
+ type="button"
245
+ className="p-2 rounded-lg hover:bg-gray-300 border drop-shadow-sm"
246
+ onClick={() => {
247
+ const d = dateFns.add(new Date(currYear, currMonth), {
248
+ months: 1
249
+ });
250
+ setCurrMonth(d.getMonth());
251
+ setCurrYear(d.getFullYear());
252
+ }}
253
+ >
254
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-4 h-4">
255
+ <path strokeLinecap="round" strokeLinejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
256
+ </svg>
257
+ </button>
258
+ </Stack>
259
+ </div>
260
+ <div className="grid grid-cols-7 p-3 gap-2 mt-3">
261
+ {days.map((v, i) => (
262
+ <div key={i} className="text-center w-12">
263
+ {v}
264
+ </div>
265
+ ))}
266
+ </div>
267
+
268
+ <div className="grid grid-cols-7 p-3 gap-2">
269
+ {currCalendar.map((v, i) =>
270
+ v ? (
271
+ <button
272
+ type="button"
273
+ key={i}
274
+ className={`rounded-lg border flex w-12 justify-center p-2 hover:bg-blue-600 hover:text-white ${
275
+ dateFns.isSameDay(new Date(currYear, currMonth, v), dateRange.startDate)
276
+ ? // dateRange.startDate?.isSame(
277
+ // moment({ day: v, month: currMonth, year: currYear }),
278
+ // "day"
279
+ // )
280
+ "bg-blue-600 text-white"
281
+ : ""
282
+ } ${
283
+ dateFns.isSameDay(new Date(currYear, currMonth, v), new Date())
284
+ ? // moment({ day: v, month: currMonth, year: currYear }).isSame(
285
+ // moment(),
286
+ // "day"
287
+ // )
288
+ "ring-blue-400 ring-2"
289
+ : ""
290
+ } ${
291
+ dateFns.isSameDay(new Date(currYear, currMonth, v), dateRange.endDate)
292
+ ? // dateRange.endDate?.isSame(
293
+ // moment({ day: v, month: currMonth, year: currYear }),
294
+ // "day"
295
+ // )
296
+ "bg-blue-600 text-white"
297
+ : ""
298
+ } ${
299
+ isBetween(new Date(currYear, currMonth, v), dateRange.startDate, dateRange.endDate, "[]")
300
+ ? // moment({
301
+ // day: v,
302
+ // month: currMonth,
303
+ // year: currYear,
304
+ // }).isBetween(
305
+ // dateRange.startDate,
306
+ // dateRange.endDate,
307
+ // "day",
308
+ // "()"
309
+ // )
310
+ "bg-blue-200"
311
+ : ""
312
+ }`}
313
+ onClick={() => {
314
+ handleUpdateDateRangeOnLeftCalendar(v);
315
+ }}
316
+ >
317
+ {v}
318
+ </button>
319
+ ) : (
320
+ <span key={i}></span>
321
+ )
322
+ )}
323
+ </div>
324
+ </div>
325
+ <div className="drop-shadow-sm shadow-sm min-w-[10rem] p-3 rounded-lg border flex flex-col">
326
+ <div className="flex justify-between items-center">
327
+ <h3 className="text-lg">
328
+ {dateFns.format(dateFns.add(new Date(currYear, currMonth), { months: 1 }), "MMMM")}{" "}
329
+ {
330
+ dateFns.add(new Date(currYear, currMonth), { months: 1 }).getFullYear()
331
+ // moment({ month: currMonth }).add(1, "month").year()
332
+ }
333
+ </h3>
334
+ <div className="flex gap-x-2 items-center">
335
+ <button
336
+ type="button"
337
+ className="p-2 rounded-lg hover:bg-gray-300 border drop-shadow-sm"
338
+ onClick={() => {
339
+ const d = dateFns.sub(new Date(currYear, currMonth), {
340
+ months: 1
341
+ });
342
+ // moment({ month: currMonth, year: currYear }).subtract(
343
+ // 1,
344
+ // "month"
345
+ // )
346
+ setCurrMonth(d.getMonth());
347
+ setCurrYear(d.getFullYear());
348
+ }}
349
+ >
350
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-4 h-4">
351
+ <path strokeLinecap="round" strokeLinejoin="round" d="M15.75 19.5L8.25 12l7.5-7.5" />
352
+ </svg>
353
+ </button>
354
+ <button
355
+ type="button"
356
+ className="p-2 rounded-lg hover:bg-gray-300 border drop-shadow-sm"
357
+ onClick={() => {
358
+ const d = dateFns.add(new Date(currYear, currMonth), {
359
+ months: 1
360
+ });
361
+ // moment({ month: currMonth }).add(1, "month")
362
+ setCurrMonth(d.getMonth());
363
+ setCurrYear(d.getFullYear());
364
+ }}
365
+ >
366
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-4 h-4">
367
+ <path strokeLinecap="round" strokeLinejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
368
+ </svg>
369
+ </button>
370
+ </div>
371
+ </div>
372
+ <div className="grid grid-cols-7 p-3 gap-2 mt-3">
373
+ {days.map((v, i) => (
374
+ <div key={i} className="text-center w-12">
375
+ {v}
376
+ </div>
377
+ ))}
378
+ </div>
379
+
380
+ <div className="grid grid-cols-7 p-3 gap-2">
381
+ {nextCalendar.map((day, i) =>
382
+ day ? (
383
+ <button
384
+ type="button"
385
+ key={i}
386
+ className={`rounded-lg border flex w-12 justify-center p-2 hover:bg-blue-600 hover:text-white ${
387
+ dateFns.isSameDay(
388
+ dateFns.add(new Date(currYear, currMonth, day), {
389
+ months: 1
390
+ }),
391
+ dateRange.startDate
392
+ )
393
+ ? // dateRange.startDate?.isSame(
394
+ // moment({
395
+ // day: v,
396
+ // month: moment({
397
+ // day: v,
398
+ // month: currMonth,
399
+ // year: currYear,
400
+ // })
401
+ // .add(1, "month")
402
+ // .month(),
403
+ // year: moment({ day: v, month: currMonth, year: currYear })
404
+ // .add(1, "month")
405
+ // .year(),
406
+ // }),
407
+ // "day"
408
+ // )
409
+ "bg-blue-600 text-white"
410
+ : ""
411
+ } ${
412
+ dateFns.isSameDay(
413
+ dateFns.add(new Date(currYear, currMonth, day), {
414
+ months: 1
415
+ }),
416
+ new Date()
417
+ )
418
+ ? // moment({
419
+ // day: v,
420
+ // month: moment({ day: v, month: currMonth, year: currYear })
421
+ // .add(1, "month")
422
+ // .month(),
423
+ // year: moment({ day: v, month: currMonth, year: currYear })
424
+ // .add(1, "month")
425
+ // .year(),
426
+ // }).isSame(moment(), "day")
427
+ "ring-blue-400 ring-2"
428
+ : ""
429
+ } ${
430
+ dateFns.isSameDay(
431
+ dateFns.add(new Date(currYear, currMonth, day), {
432
+ months: 1
433
+ }),
434
+ dateRange.endDate
435
+ )
436
+ ? // dateRange.endDate?.isSame(
437
+ // moment({
438
+ // day: v,
439
+ // month: moment({
440
+ // day: v,
441
+ // month: currMonth,
442
+ // year: currYear,
443
+ // })
444
+ // .add(1, "month")
445
+ // .month(),
446
+ // year: moment({ day: v, month: currMonth, year: currYear })
447
+ // .add(1, "month")
448
+ // .year(),
449
+ // }),
450
+ // "day"
451
+ // )
452
+ "bg-blue-600 text-white"
453
+ : ""
454
+ } ${
455
+ // isBetween(new Date(currYear, currMonth, v), {}))
456
+ isBetween(
457
+ dateFns.add(new Date(currYear, currMonth, day), {
458
+ months: 1
459
+ }),
460
+ dateRange.startDate,
461
+ dateRange.endDate,
462
+ "[]"
463
+ )
464
+ ? // moment({
465
+ // day: v,
466
+ // month: moment({ day: v, month: currMonth, year: currYear })
467
+ // .add(1, "month")
468
+ // .month(),
469
+ // year: moment({ day: v, month: currMonth, year: currYear })
470
+ // .add(1, "month")
471
+ // .year(),
472
+ // }).isBetween(
473
+ // dateRange.startDate,
474
+ // dateRange.endDate,
475
+ // "day",
476
+ // "()"
477
+ // )
478
+ "bg-blue-200"
479
+ : ""
480
+ }`}
481
+ onClick={() => {
482
+ handleUpdateDateRangeOnRightCalendar(day);
483
+ }}
484
+ >
485
+ {day}
486
+ </button>
487
+ ) : (
488
+ <span key={i}></span>
489
+ )
490
+ )}
491
+ </div>
492
+ </div>
493
+ </Box>
494
+ </Paper>
495
+ );
496
+ };
497
+
498
+ export default StaticDateRangePicker;
@@ -0,0 +1 @@
1
+ export { default as DateRangePicker } from "./DateRangePicker";
@@ -0,0 +1,4 @@
1
+ export { default as DataManagement } from "./DataManagement";
2
+
3
+ export * from "./date";
4
+ export * from "./table";
@@ -0,0 +1,34 @@
1
+ import { TablePagination, useMediaQuery, useTheme } from "@mui/material";
2
+ import PropTypes from "prop-types";
3
+
4
+ TablePaginationCustom.propsType = {
5
+ count: PropTypes.number,
6
+ rowsPerPage: PropTypes.number,
7
+ page: PropTypes.number,
8
+ onPageChange: PropTypes.func,
9
+ onRowsPerPageChange: PropTypes.func
10
+ };
11
+
12
+ function TablePaginationCustom({ count, rowsPerPage, page, onPageChange, onRowsPerPageChange, ...rest }) {
13
+ const theme = useTheme();
14
+ const matchsDownMD = useMediaQuery(theme.breakpoints.down("md"));
15
+ return (
16
+ <TablePagination
17
+ {...rest}
18
+ rowsPerPageOptions={[5, 10, 25, 100]}
19
+ component="div"
20
+ count={count}
21
+ rowsPerPage={rowsPerPage}
22
+ page={page}
23
+ onPageChange={onPageChange}
24
+ onRowsPerPageChange={onRowsPerPageChange}
25
+ showFirstButton={true}
26
+ showLastButton={true}
27
+ labelRowsPerPage={matchsDownMD ? "Số hàng" : "Số bản ghi trên trang:"}
28
+ labelDisplayedRows={function defaultLabelDisplayedRows({ from, to, count }) {
29
+ return `${from}–${to} của ${count !== -1 ? count : `more than ${to}`}`;
30
+ }}
31
+ />
32
+ );
33
+ }
34
+ export default TablePaginationCustom;
@@ -0,0 +1,15 @@
1
+ import { Skeleton, TableCell, TableRow } from "@mui/material";
2
+
3
+ const TableRowsLoader = ({ rowsNum, colsNum }) => {
4
+ return [...Array(rowsNum)].map((row, x) => (
5
+ <TableRow key={x}>
6
+ {[...Array(colsNum)].map((col, y) => (
7
+ <TableCell key={y} component="th" scope="row">
8
+ <Skeleton animation="wave" variant="text" />
9
+ </TableCell>
10
+ ))}
11
+ </TableRow>
12
+ ));
13
+ };
14
+
15
+ export default TableRowsLoader;
@@ -0,0 +1,2 @@
1
+ export { default as TablePagination } from "./TablePagination";
2
+ export { default as TableRowsLoader } from "./TableRowsLoader";
@@ -0,0 +1 @@
1
+ export const DEFAULT_DATE_FORMAT = "DD/MM/YYYY";
@@ -0,0 +1,23 @@
1
+ import moment from "moment";
2
+ import { DEFAULT_DATE_FORMAT } from "../constants";
3
+
4
+ export const currencyFormatter = new Intl.NumberFormat('vi-VN', {
5
+ style: 'currency',
6
+ currency: 'VND',
7
+ });
8
+ export const numberFormatter = new Intl.NumberFormat('vi-VN');
9
+
10
+ export const vndFormat = {
11
+ type: 'number',
12
+ valueFormat: (value) => currencyFormatter.format(value),
13
+ };
14
+
15
+ export const numberFormat = {
16
+ type: 'number',
17
+ valueFormat: (value) => numberFormatter.format(value),
18
+ }
19
+ export const dateFormat = {
20
+ valueFormat: (value) => {
21
+ return value && moment(value).format(DEFAULT_DATE_FORMAT);
22
+ }
23
+ }
@@ -0,0 +1 @@
1
+ export * from './data-table'
package/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./api";
2
+ export * from "./components";
3
+ export * from "./helpers";
package/package.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "trithuc-mvc-react",
3
+ "version": "1.0.0",
4
+ "main": "index.js",
5
+ "scripts": {
6
+ "test": "echo \"Error: no test specified\" && exit 1"
7
+ },
8
+ "author": "",
9
+ "license": "ISC",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git://git-remote-url"
13
+ },
14
+ "description": ""
15
+ }