dsh-niuma 0.1.1 → 0.2.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.
- package/CHANGELOG.md +32 -0
- package/README.md +6 -0
- package/docs/preview-light-dark.png +0 -0
- package/lib/client.js +814 -217
- package/lib/client.js.map +2 -2
- package/lib/index.js +48 -118
- package/package.json +3 -8
- package/src/client/index.js +623 -164
- package/src/core.mjs +88 -0
- package/src/holidays.mjs +90 -10
package/lib/client.js
CHANGED
|
@@ -38,7 +38,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
38
38
|
var import_react = __toESM(require("react"), 1);
|
|
39
39
|
|
|
40
40
|
// src/holidays.mjs
|
|
41
|
-
var
|
|
41
|
+
var OFFICIAL_HOLIDAYS_2026 = Object.freeze([
|
|
42
42
|
// 元旦:1月1日(周四)至3日(周六)
|
|
43
43
|
"2026-01-01",
|
|
44
44
|
"2026-01-02",
|
|
@@ -80,7 +80,7 @@ var DEFAULT_HOLIDAYS = Object.freeze([
|
|
|
80
80
|
"2026-10-06",
|
|
81
81
|
"2026-10-07"
|
|
82
82
|
]);
|
|
83
|
-
var
|
|
83
|
+
var OFFICIAL_EXTRA_WORKDAYS_2026 = Object.freeze([
|
|
84
84
|
"2026-01-04",
|
|
85
85
|
// 元旦调休:周日上班
|
|
86
86
|
"2026-02-14",
|
|
@@ -94,11 +94,88 @@ var DEFAULT_EXTRA_WORKDAYS = Object.freeze([
|
|
|
94
94
|
"2026-10-10"
|
|
95
95
|
// 国庆调休:周六上班
|
|
96
96
|
]);
|
|
97
|
+
var HOLIDAY_BLOCKS = Object.freeze([
|
|
98
|
+
{ id: "newYear", year: 2026, from: "2026-01-01", to: "2026-01-03", days: 3, estimated: false },
|
|
99
|
+
{ id: "spring", year: 2026, from: "2026-02-15", to: "2026-02-23", days: 9, estimated: false },
|
|
100
|
+
{ id: "qingming", year: 2026, from: "2026-04-04", to: "2026-04-06", days: 3, estimated: false },
|
|
101
|
+
{ id: "labour", year: 2026, from: "2026-05-01", to: "2026-05-05", days: 5, estimated: false },
|
|
102
|
+
{ id: "dragon", year: 2026, from: "2026-06-19", to: "2026-06-21", days: 3, estimated: false },
|
|
103
|
+
{ id: "midAutumn", year: 2026, from: "2026-09-25", to: "2026-09-27", days: 3, estimated: false },
|
|
104
|
+
{ id: "national", year: 2026, from: "2026-10-01", to: "2026-10-07", days: 7, estimated: false }
|
|
105
|
+
]);
|
|
97
106
|
var HOLIDAY_SOURCE = Object.freeze({
|
|
98
107
|
year: 2026,
|
|
108
|
+
estimated: false,
|
|
99
109
|
title: "\u56FD\u52A1\u9662\u529E\u516C\u5385\u5173\u4E8E2026\u5E74\u90E8\u5206\u8282\u5047\u65E5\u5B89\u6392\u7684\u901A\u77E5\uFF08\u56FD\u529E\u53D1\u660E\u7535\u30142025\u30157\u53F7\uFF09",
|
|
100
110
|
url: "https://www.gov.cn/zhengce/content/202511/content_7047090.htm"
|
|
101
111
|
});
|
|
112
|
+
var ESTIMATED_HOLIDAYS_2027 = Object.freeze([
|
|
113
|
+
"2027-01-01",
|
|
114
|
+
"2027-01-02",
|
|
115
|
+
"2027-01-03",
|
|
116
|
+
"2027-02-05",
|
|
117
|
+
"2027-02-06",
|
|
118
|
+
"2027-02-07",
|
|
119
|
+
"2027-02-08",
|
|
120
|
+
"2027-02-09",
|
|
121
|
+
"2027-02-10",
|
|
122
|
+
"2027-02-11",
|
|
123
|
+
"2027-02-12",
|
|
124
|
+
"2027-02-13",
|
|
125
|
+
"2027-04-03",
|
|
126
|
+
"2027-04-04",
|
|
127
|
+
"2027-04-05",
|
|
128
|
+
"2027-05-01",
|
|
129
|
+
"2027-05-02",
|
|
130
|
+
"2027-05-03",
|
|
131
|
+
"2027-05-04",
|
|
132
|
+
"2027-05-05",
|
|
133
|
+
"2027-06-09",
|
|
134
|
+
"2027-09-15",
|
|
135
|
+
"2027-10-01",
|
|
136
|
+
"2027-10-02",
|
|
137
|
+
"2027-10-03",
|
|
138
|
+
"2027-10-04",
|
|
139
|
+
"2027-10-05",
|
|
140
|
+
"2027-10-06",
|
|
141
|
+
"2027-10-07"
|
|
142
|
+
]);
|
|
143
|
+
var ESTIMATED_EXTRA_WORKDAYS_2027 = Object.freeze([
|
|
144
|
+
"2027-01-31",
|
|
145
|
+
// 春节调休:周日上班(预估)
|
|
146
|
+
"2027-02-14",
|
|
147
|
+
// 春节调休:周日上班(预估)
|
|
148
|
+
"2027-05-08",
|
|
149
|
+
// 劳动节调休:周六上班(预估)
|
|
150
|
+
"2027-09-26",
|
|
151
|
+
// 国庆调休:周日上班(预估)
|
|
152
|
+
"2027-10-09"
|
|
153
|
+
// 国庆调休:周六上班(预估)
|
|
154
|
+
]);
|
|
155
|
+
var HOLIDAY_BLOCKS_2027_ESTIMATED = Object.freeze([
|
|
156
|
+
{ id: "newYear", year: 2027, from: "2027-01-01", to: "2027-01-03", days: 3, estimated: true },
|
|
157
|
+
{ id: "spring", year: 2027, from: "2027-02-05", to: "2027-02-13", days: 9, estimated: true },
|
|
158
|
+
{ id: "qingming", year: 2027, from: "2027-04-03", to: "2027-04-05", days: 3, estimated: true },
|
|
159
|
+
{ id: "labour", year: 2027, from: "2027-05-01", to: "2027-05-05", days: 5, estimated: true },
|
|
160
|
+
{ id: "dragon", year: 2027, from: "2027-06-09", to: "2027-06-09", days: 1, estimated: true },
|
|
161
|
+
{ id: "midAutumn", year: 2027, from: "2027-09-15", to: "2027-09-15", days: 1, estimated: true },
|
|
162
|
+
{ id: "national", year: 2027, from: "2027-10-01", to: "2027-10-07", days: 7, estimated: true }
|
|
163
|
+
]);
|
|
164
|
+
var HOLIDAY_ESTIMATE_2027 = Object.freeze({
|
|
165
|
+
year: 2027,
|
|
166
|
+
estimated: true,
|
|
167
|
+
announced: false,
|
|
168
|
+
title: "2027 \u5E74\u653E\u5047\u5B89\u6392\uFF08\u9884\u4F30\uFF0C\u5B98\u65B9\u5C1A\u672A\u516C\u5E03\uFF09",
|
|
169
|
+
note: "\u5B98\u65B9\u901A\u77E5\u901A\u5E38\u5728\u524D\u4E00\u5E74 11 \u6708\u521D\u53D1\u5E03\uFF1B\u4EE5\u4E0B\u4E3A\u6309 2024 \u5E74\u4FEE\u8BA2\u7684\u300A\u5168\u56FD\u5E74\u8282\u53CA\u7EAA\u5FF5\u65E5\u653E\u5047\u529E\u6CD5\u300B\u4E0E\u5F80\u5E74\u60EF\u4F8B\u63A8\u7B97\uFF0C\u4EC5\u4F9B\u53C2\u8003\uFF0C\u8BF7\u4EE5\u56FD\u52A1\u9662\u901A\u77E5\u4E3A\u51C6\u3002",
|
|
170
|
+
basis: [
|
|
171
|
+
"\u6625\u8282\u81EA\u9664\u5915\u8D77\u653E\u5047\u8C03\u4F11\uFF0C\u4E00\u822C\u8FDE\u4F11 8\u20139 \u5929",
|
|
172
|
+
"\u56FD\u5E86 7 \u5929\u3001\u52B3\u52A8\u8282 5 \u5929",
|
|
173
|
+
"\u5143\u65E6 / \u6E05\u660E / \u7AEF\u5348 / \u4E2D\u79CB\u5404 1 \u5929\u6CD5\u5B9A\u5047\uFF0C\u9047\u5468\u672B\u987A\u8FDE"
|
|
174
|
+
],
|
|
175
|
+
url: "https://www.gov.cn/zhengce/xxgk/"
|
|
176
|
+
});
|
|
177
|
+
var DEFAULT_HOLIDAYS = Object.freeze([...OFFICIAL_HOLIDAYS_2026, ...ESTIMATED_HOLIDAYS_2027]);
|
|
178
|
+
var DEFAULT_EXTRA_WORKDAYS = Object.freeze([...OFFICIAL_EXTRA_WORKDAYS_2026, ...ESTIMATED_EXTRA_WORKDAYS_2027]);
|
|
102
179
|
|
|
103
180
|
// src/core.mjs
|
|
104
181
|
var DEFAULTS = Object.freeze({
|
|
@@ -359,6 +436,68 @@ function formatDuration(seconds) {
|
|
|
359
436
|
if (minutes > 0) return `${minutes}\u5206 ${rest}\u79D2`;
|
|
360
437
|
return `${rest}\u79D2`;
|
|
361
438
|
}
|
|
439
|
+
function holidayDaysInMonth(year, monthIndex, config) {
|
|
440
|
+
const prefix = `${year}-${String(monthIndex + 1).padStart(2, "0")}-`;
|
|
441
|
+
return (config.holidays ?? DEFAULTS.holidays).filter((key) => key.startsWith(prefix)).length;
|
|
442
|
+
}
|
|
443
|
+
function monthSummary(config, year, monthIndex) {
|
|
444
|
+
const daysInMonth = new Date(year, monthIndex + 1, 0).getDate();
|
|
445
|
+
const workdays = workdaysInMonth(year, monthIndex, config);
|
|
446
|
+
const holidayDays = holidayDaysInMonth(year, monthIndex, config);
|
|
447
|
+
const monthlySalary = Number(config.monthlySalary) || 0;
|
|
448
|
+
const paidMinutes = paidMinutesPerDay(config);
|
|
449
|
+
const paidHours = paidMinutes / 60;
|
|
450
|
+
const dailyWage = workdays > 0 ? monthlySalary / workdays : 0;
|
|
451
|
+
const hourlyWage = paidHours > 0 ? dailyWage / paidHours : 0;
|
|
452
|
+
return {
|
|
453
|
+
year,
|
|
454
|
+
monthIndex,
|
|
455
|
+
month: monthIndex + 1,
|
|
456
|
+
daysInMonth,
|
|
457
|
+
workdays,
|
|
458
|
+
holidayDays,
|
|
459
|
+
restDays: daysInMonth - workdays,
|
|
460
|
+
monthlySalary,
|
|
461
|
+
paidMinutes,
|
|
462
|
+
paidHours,
|
|
463
|
+
dailyWage,
|
|
464
|
+
hourlyWage,
|
|
465
|
+
minuteWage: hourlyWage / 60,
|
|
466
|
+
secondWage: hourlyWage / 3600
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
function yearSummary(config, year) {
|
|
470
|
+
const months = Array.from({ length: 12 }, (_, monthIndex) => monthSummary(config, year, monthIndex));
|
|
471
|
+
const totalWorkdays = months.reduce((sum, item) => sum + item.workdays, 0);
|
|
472
|
+
const totalHolidayDays = months.reduce((sum, item) => sum + item.holidayDays, 0);
|
|
473
|
+
const monthlySalary = Number(config.monthlySalary) || 0;
|
|
474
|
+
const annualIncome = monthlySalary * 12;
|
|
475
|
+
const paidMinutes = paidMinutesPerDay(config);
|
|
476
|
+
const paidHours = paidMinutes / 60;
|
|
477
|
+
const averageDailyWage = totalWorkdays > 0 ? annualIncome / totalWorkdays : 0;
|
|
478
|
+
const averageHourlyWage = paidHours > 0 ? averageDailyWage / paidHours : 0;
|
|
479
|
+
const maxHolidayMonth = months.reduce((best, item) => {
|
|
480
|
+
if (item.holidayDays !== best.holidayDays) return item.holidayDays > best.holidayDays ? item : best;
|
|
481
|
+
if (item.workdays !== best.workdays) return item.workdays < best.workdays ? item : best;
|
|
482
|
+
return item.month < best.month ? item : best;
|
|
483
|
+
}, months[0]);
|
|
484
|
+
return {
|
|
485
|
+
year,
|
|
486
|
+
months,
|
|
487
|
+
totalWorkdays,
|
|
488
|
+
totalHolidayDays,
|
|
489
|
+
paidMinutes,
|
|
490
|
+
paidHours,
|
|
491
|
+
monthlySalary,
|
|
492
|
+
annualIncome,
|
|
493
|
+
averageMonthlyWage: monthlySalary,
|
|
494
|
+
averageDailyWage,
|
|
495
|
+
averageHourlyWage,
|
|
496
|
+
averageMinuteWage: averageHourlyWage / 60,
|
|
497
|
+
averageSecondWage: averageHourlyWage / 3600,
|
|
498
|
+
maxHolidayMonth
|
|
499
|
+
};
|
|
500
|
+
}
|
|
362
501
|
|
|
363
502
|
// src/client/index.js
|
|
364
503
|
var PKG_NAME = true ? "dsh-niuma" : "dsh-niuma";
|
|
@@ -388,6 +527,15 @@ var CARD = {
|
|
|
388
527
|
boxSizing: "border-box"
|
|
389
528
|
};
|
|
390
529
|
var NUM = { fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" };
|
|
530
|
+
var GOLD = {
|
|
531
|
+
color: "#D9A441",
|
|
532
|
+
backgroundImage: "linear-gradient(180deg, #FFE9A8 0%, #F2C14E 40%, #D79A2B 72%, #B87A16 100%)",
|
|
533
|
+
WebkitBackgroundClip: "text",
|
|
534
|
+
backgroundClip: "text",
|
|
535
|
+
WebkitTextFillColor: "transparent",
|
|
536
|
+
textShadow: "0 1px 0 rgba(0,0,0,.05)"
|
|
537
|
+
};
|
|
538
|
+
var GOLD_TINT = "#E7B84D";
|
|
391
539
|
var STATUS_META = {
|
|
392
540
|
rest: { key: "statusRest", color: C.label3 },
|
|
393
541
|
before: { key: "statusBefore", color: C.warn },
|
|
@@ -423,7 +571,6 @@ var zh = {
|
|
|
423
571
|
"paidHoursPerDay": "\u65E5\u6709\u6548\u5DE5\u65F6",
|
|
424
572
|
"todayTarget": "\u4ECA\u65E5\u76EE\u6807",
|
|
425
573
|
"monthEarned": "\u672C\u6708\u5DF2\u8D5A",
|
|
426
|
-
"holidaySource": "\u8282\u5047\u65E5\u6765\u6E90",
|
|
427
574
|
"holidaySourceLink": "\u56FD\u52A1\u9662\u529E\u516C\u5385 2026 \u5E74\u8282\u5047\u65E5\u5B89\u6392",
|
|
428
575
|
"configLoading": "\u6B63\u5728\u8BFB\u53D6\u8BBE\u7F6E\u2026",
|
|
429
576
|
"configFailed": "\u8BBE\u7F6E\u8BFB\u53D6\u5931\u8D25\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u503C",
|
|
@@ -437,14 +584,53 @@ var zh = {
|
|
|
437
584
|
"lunchStartField": "\u5348\u4F11\u5F00\u59CB",
|
|
438
585
|
"lunchEndField": "\u5348\u4F11\u7ED3\u675F",
|
|
439
586
|
"lunchPaidField": "\u5348\u4F11\u7B97\u5DE5\u65F6",
|
|
440
|
-
"
|
|
441
|
-
"
|
|
587
|
+
"holidaysCardTitle": "\u8282\u5047\u65E5\u5B89\u6392",
|
|
588
|
+
"holidays2026Label": "2026 \u5E74\uFF08\u5B98\u65B9\uFF09",
|
|
589
|
+
"holidays2027Label": "2027 \u5E74\uFF08\u9884\u4F30\uFF09",
|
|
590
|
+
"holidayEstimateTag": "\u9884\u4F30",
|
|
591
|
+
"holidayEstimateNote": "\u5B98\u65B9\u5C1A\u672A\u516C\u5E03\uFF0C\u4EE5\u4E0A\u4E3A\u6309\u89C4\u5219\u4E0E\u60EF\u4F8B\u63A8\u7B97\uFF0C\u4EC5\u4F9B\u53C2\u8003\uFF0C\u4EE5\u56FD\u52A1\u9662\u901A\u77E5\u4E3A\u51C6\u3002",
|
|
592
|
+
"holidayEstimateShort": "2027 \u4E3A\u9884\u4F30\u503C",
|
|
593
|
+
"settingsDataNote": "\u8282\u5047\u65E5\u4E0E\u8C03\u4F11\u6570\u636E\u89C1\u4E0A\u65B9\u300C\u8282\u5047\u65E5\u5B89\u6392\u300D\u5361\u7247\uFF082027 \u5E74\u4E3A\u9884\u4F30\u503C\uFF0C\u4E0D\u53EF\u5728\u672C\u9875\u7F16\u8F91\uFF09\u3002",
|
|
594
|
+
"holidaysCardSub": "\u6CD5\u5B9A\u5047\u65E5 {days} \u5929 \xB7 \u8C03\u4F11\u4E0A\u73ED {extra} \u5929",
|
|
595
|
+
"holidaysDaysShort": "{days} \u5929",
|
|
596
|
+
"holidaysExtraNote": "\u8C03\u4F11\u4E0A\u73ED\u65E5 {extra} \u5929\uFF08\u5468\u672B\u8865\u73ED\uFF0C\u6309\u5DE5\u4F5C\u65E5\u8BA1\uFF09",
|
|
597
|
+
"holidayNewYear": "\u5143\u65E6",
|
|
598
|
+
"holidaySpring": "\u6625\u8282",
|
|
599
|
+
"holidayQingming": "\u6E05\u660E",
|
|
600
|
+
"holidayLabour": "\u52B3\u52A8\u8282",
|
|
601
|
+
"holidayDragon": "\u7AEF\u5348",
|
|
602
|
+
"holidayMidAutumn": "\u4E2D\u79CB",
|
|
603
|
+
"holidayNational": "\u56FD\u5E86",
|
|
442
604
|
"save": "\u4FDD\u5B58",
|
|
443
605
|
"saving": "\u4FDD\u5B58\u4E2D\u2026",
|
|
444
606
|
"saved": "\u5DF2\u4FDD\u5B58",
|
|
445
607
|
"saveFailed": "\u4FDD\u5B58\u5931\u8D25\uFF1A",
|
|
446
608
|
"dayUnit": "\u5929",
|
|
447
|
-
"hourUnit": "\u5C0F\u65F6"
|
|
609
|
+
"hourUnit": "\u5C0F\u65F6",
|
|
610
|
+
"viewLabel": "\u67E5\u770B",
|
|
611
|
+
"viewCurrent": "\u672C\u6708",
|
|
612
|
+
"viewYear": "\u5168\u5E74\u5E73\u5747",
|
|
613
|
+
"monthUnit": "\u6708",
|
|
614
|
+
"holidayShort": "\u5047",
|
|
615
|
+
"currentHint": "\u6309\u6BCF\u6708 {days} \u4E2A\u5DE5\u4F5C\u65E5\u3001\u6BCF\u5929 {hours} \u5C0F\u65F6\u8BA1",
|
|
616
|
+
"monthHint": "{year} \u5E74 {month} \u6708 \xB7 \u5DE5\u4F5C\u65E5 {workdays} \u5929 \xB7 \u6CD5\u5B9A\u5047\u65E5 {holidays} \u5929",
|
|
617
|
+
"yearHint": "{year} \u5168\u5E74 \xB7 {workdays} \u4E2A\u5DE5\u4F5C\u65E5 \xB7 \u6309\u5E74\u6536\u5165\u6298\u7B97",
|
|
618
|
+
"viewOnlyHint": "\u4EC5\u6362\u7B97\uFF0C\u4E0D\u5F71\u54CD\u4ECA\u65E5\u8BA1\u6570\u5668",
|
|
619
|
+
"yearWorkdays": "\u5168\u5E74\u5DE5\u4F5C\u65E5",
|
|
620
|
+
"yearIncome": "\u5168\u5E74\u6536\u5165",
|
|
621
|
+
"averageDaily": "\u5E73\u5747\u65E5\u85AA",
|
|
622
|
+
"averageHourly": "\u5E73\u5747\u65F6\u85AA",
|
|
623
|
+
"averageMinute": "\u5E73\u5747\u5206\u85AA",
|
|
624
|
+
"averageSecond": "\u5E73\u5747\u79D2\u85AA",
|
|
625
|
+
"monthWorkdaysCard": "\u5DE5\u4F5C\u65E5",
|
|
626
|
+
"maxHolidayBadge": "\u5168\u5E74\u5047\u65E5\u6700\u591A",
|
|
627
|
+
"maxHolidayNote": "\u5168\u5E74\u5047\u65E5\u6700\u591A\uFF1A{month} \u6708\uFF08\u6CD5\u5B9A\u5047\u65E5 {days} \u5929\uFF09",
|
|
628
|
+
"maxHolidayThis": "\u5168\u5E74\u5047\u65E5\u6700\u591A\u7684\u6708\u4EFD\uFF08\u6CD5\u5B9A\u5047\u65E5 {days} \u5929\uFF09",
|
|
629
|
+
"sectionRates": "\u5DE5\u8D44\u6362\u7B97",
|
|
630
|
+
"sectionRatesHint": "\u6309\u6BCF\u6708 {days} \u4E2A\u5DE5\u4F5C\u65E5\u3001\u6BCF\u5929 {hours} \u5C0F\u65F6\u8BA1",
|
|
631
|
+
"sectionDetail": "\u4ECA\u65E5\u53E3\u5F84\u660E\u7EC6",
|
|
632
|
+
"restHint": "\u4ECA\u5929\u4F11\u606F\uFF0C\u597D\u597D\u8EBA\u5E73",
|
|
633
|
+
"liveNow": "\u5B9E\u65F6"
|
|
448
634
|
};
|
|
449
635
|
var en = {
|
|
450
636
|
"nav": "Niuma",
|
|
@@ -474,7 +660,6 @@ var en = {
|
|
|
474
660
|
"paidHoursPerDay": "Paid hours / day",
|
|
475
661
|
"todayTarget": "Today's target",
|
|
476
662
|
"monthEarned": "Earned this month",
|
|
477
|
-
"holidaySource": "Holiday data",
|
|
478
663
|
"holidaySourceLink": "State Council 2026 holiday schedule",
|
|
479
664
|
"configLoading": "Loading settings\u2026",
|
|
480
665
|
"configFailed": "Failed to load settings, using defaults",
|
|
@@ -488,24 +673,60 @@ var en = {
|
|
|
488
673
|
"lunchStartField": "Lunch start",
|
|
489
674
|
"lunchEndField": "Lunch end",
|
|
490
675
|
"lunchPaidField": "Lunch is paid",
|
|
491
|
-
"
|
|
492
|
-
"
|
|
676
|
+
"holidaysCardTitle": "Holiday calendar",
|
|
677
|
+
"holidays2026Label": "2026 (official)",
|
|
678
|
+
"holidays2027Label": "2027 (estimated)",
|
|
679
|
+
"holidayEstimateTag": "estimate",
|
|
680
|
+
"holidayEstimateNote": "Not announced officially yet \u2014 estimated from the rules and past practice, for reference only.",
|
|
681
|
+
"holidayEstimateShort": "2027 is estimated",
|
|
682
|
+
"settingsDataNote": "Holiday data lives in the calendar card above (2027 values are estimates and not editable here).",
|
|
683
|
+
"holidaysCardSub": "{days} statutory days \xB7 {extra} make-up workdays",
|
|
684
|
+
"holidaysDaysShort": "{days} d",
|
|
685
|
+
"holidaysExtraNote": "{extra} make-up workdays (weekend shifts, counted as workdays)",
|
|
686
|
+
"holidayNewYear": "New Year's Day",
|
|
687
|
+
"holidaySpring": "Spring Festival",
|
|
688
|
+
"holidayQingming": "Qingming",
|
|
689
|
+
"holidayLabour": "Labour Day",
|
|
690
|
+
"holidayDragon": "Dragon Boat",
|
|
691
|
+
"holidayMidAutumn": "Mid-Autumn",
|
|
692
|
+
"holidayNational": "National Day",
|
|
493
693
|
"save": "Save",
|
|
494
694
|
"saving": "Saving\u2026",
|
|
495
695
|
"saved": "Saved",
|
|
496
696
|
"saveFailed": "Save failed: ",
|
|
497
697
|
"dayUnit": "d",
|
|
498
|
-
"hourUnit": "h"
|
|
698
|
+
"hourUnit": "h",
|
|
699
|
+
"viewLabel": "View",
|
|
700
|
+
"viewCurrent": "This month",
|
|
701
|
+
"viewYear": "Year average",
|
|
702
|
+
"monthUnit": "M",
|
|
703
|
+
"holidayShort": "hol.",
|
|
704
|
+
"currentHint": "{days} workdays / month \xB7 {hours} h / day",
|
|
705
|
+
"monthHint": "{year}-{month} \xB7 {workdays} workdays \xB7 {holidays} statutory holidays",
|
|
706
|
+
"yearHint": "{year} \xB7 {workdays} workdays \xB7 averaged over the year",
|
|
707
|
+
"viewOnlyHint": "Conversion only \u2014 today's ticker is unaffected",
|
|
708
|
+
"yearWorkdays": "Workdays / year",
|
|
709
|
+
"yearIncome": "Annual income",
|
|
710
|
+
"averageDaily": "Avg per day",
|
|
711
|
+
"averageHourly": "Avg per hour",
|
|
712
|
+
"averageMinute": "Avg per minute",
|
|
713
|
+
"averageSecond": "Avg per second",
|
|
714
|
+
"monthWorkdaysCard": "Workdays",
|
|
715
|
+
"maxHolidayBadge": "Most holidays",
|
|
716
|
+
"maxHolidayNote": "Most holidays: month {month} ({days} statutory days)",
|
|
717
|
+
"maxHolidayThis": "The month with the most holidays ({days} statutory days)",
|
|
718
|
+
"sectionRates": "Pay rates",
|
|
719
|
+
"sectionRatesHint": "{days} workdays / month \xB7 {hours} h / day",
|
|
720
|
+
"sectionDetail": "Today's basis",
|
|
721
|
+
"restHint": "Day off \u2014 enjoy it",
|
|
722
|
+
"liveNow": "LIVE"
|
|
499
723
|
};
|
|
500
724
|
var fallbackTranslate = (key) => zh[key] != null ? zh[key] : key;
|
|
501
|
-
var money = (value, digits = 2) =>
|
|
725
|
+
var money = (value, digits = 2) => `${formatMoney(value, digits)} \u5143`;
|
|
502
726
|
function trimNumber(value, digits = 2) {
|
|
503
727
|
const fixed = Number(value || 0).toFixed(digits);
|
|
504
728
|
return fixed.replace(/\.?0+$/, "") || "0";
|
|
505
729
|
}
|
|
506
|
-
function splitDateLines(text) {
|
|
507
|
-
return String(text == null ? "" : text).split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
508
|
-
}
|
|
509
730
|
function formFromConfig(config) {
|
|
510
731
|
return {
|
|
511
732
|
monthlySalary: String(config.monthlySalary == null ? "" : config.monthlySalary),
|
|
@@ -514,9 +735,7 @@ function formFromConfig(config) {
|
|
|
514
735
|
lunchEnabled: config.lunchEnabled !== false,
|
|
515
736
|
lunchStart: String(config.lunchStart == null ? "" : config.lunchStart),
|
|
516
737
|
lunchEnd: String(config.lunchEnd == null ? "" : config.lunchEnd),
|
|
517
|
-
lunchPaid: config.lunchPaid === true
|
|
518
|
-
holidaysText: (config.holidays || []).join("\n"),
|
|
519
|
-
extraWorkdaysText: (config.extraWorkdays || []).join("\n")
|
|
738
|
+
lunchPaid: config.lunchPaid === true
|
|
520
739
|
};
|
|
521
740
|
}
|
|
522
741
|
function patchFromForm(form) {
|
|
@@ -528,9 +747,7 @@ function patchFromForm(form) {
|
|
|
528
747
|
lunchEnabled: Boolean(form.lunchEnabled),
|
|
529
748
|
lunchStart: String(form.lunchStart || "").trim(),
|
|
530
749
|
lunchEnd: String(form.lunchEnd || "").trim(),
|
|
531
|
-
lunchPaid: Boolean(form.lunchPaid)
|
|
532
|
-
holidays: splitDateLines(form.holidaysText),
|
|
533
|
-
extraWorkdays: splitDateLines(form.extraWorkdaysText)
|
|
750
|
+
lunchPaid: Boolean(form.lunchPaid)
|
|
534
751
|
};
|
|
535
752
|
}
|
|
536
753
|
function useRemoteConfig() {
|
|
@@ -600,49 +817,110 @@ function NiumaIcon(props) {
|
|
|
600
817
|
import_react.default.createElement("path", { key: "c", d: "M4 8h.01M12 8h.01" })
|
|
601
818
|
]);
|
|
602
819
|
}
|
|
820
|
+
var PANEL_CSS = `
|
|
821
|
+
@keyframes niumaPulse { 0%,100% { opacity: .95; transform: scale(1); } 50% { opacity: .3; transform: scale(.68); } }
|
|
822
|
+
@media (prefers-reduced-motion: reduce) { .niuma-live-dot { animation: none !important; } }
|
|
823
|
+
`;
|
|
824
|
+
function StyleTag() {
|
|
825
|
+
return import_react.default.createElement("style", null, PANEL_CSS);
|
|
826
|
+
}
|
|
603
827
|
function StatusBadge(props) {
|
|
604
828
|
const t = props.t;
|
|
605
829
|
const meta = STATUS_META[props.status] || STATUS_META.rest;
|
|
830
|
+
const live = props.status === "working";
|
|
606
831
|
return import_react.default.createElement("span", {
|
|
607
832
|
style: {
|
|
608
833
|
display: "inline-flex",
|
|
609
834
|
alignItems: "center",
|
|
610
835
|
gap: 6,
|
|
611
|
-
padding: "
|
|
836
|
+
padding: "4px 11px 4px 9px",
|
|
612
837
|
borderRadius: 999,
|
|
613
838
|
fontSize: 12,
|
|
614
839
|
fontWeight: 600,
|
|
615
840
|
lineHeight: 1.5,
|
|
616
841
|
color: meta.color,
|
|
617
|
-
border: `1px solid ${meta.color}`,
|
|
618
|
-
background:
|
|
842
|
+
border: `1px solid color-mix(in srgb, ${meta.color} 42%, transparent)`,
|
|
843
|
+
background: `color-mix(in srgb, ${meta.color} 12%, transparent)`,
|
|
619
844
|
whiteSpace: "nowrap"
|
|
620
845
|
}
|
|
621
846
|
}, [
|
|
622
847
|
import_react.default.createElement("i", {
|
|
623
848
|
key: "dot",
|
|
849
|
+
className: live ? "niuma-live-dot" : void 0,
|
|
624
850
|
style: { width: 6, height: 6, borderRadius: "50%", background: meta.color, display: "inline-block" }
|
|
625
851
|
}),
|
|
626
852
|
t(meta.key)
|
|
627
853
|
]);
|
|
628
854
|
}
|
|
855
|
+
function SectionTitle(props) {
|
|
856
|
+
return import_react.default.createElement("div", {
|
|
857
|
+
style: {
|
|
858
|
+
display: "flex",
|
|
859
|
+
alignItems: "baseline",
|
|
860
|
+
justifyContent: "space-between",
|
|
861
|
+
gap: 8,
|
|
862
|
+
flexWrap: "wrap",
|
|
863
|
+
margin: "2px 1px -1px"
|
|
864
|
+
}
|
|
865
|
+
}, [
|
|
866
|
+
import_react.default.createElement("span", {
|
|
867
|
+
key: "t",
|
|
868
|
+
style: { fontSize: 12.5, fontWeight: 700, color: C.label2, letterSpacing: ".2px" }
|
|
869
|
+
}, props.title),
|
|
870
|
+
props.hint ? import_react.default.createElement("span", { key: "h", style: { fontSize: 10.5, color: C.label3, ...NUM } }, props.hint) : null
|
|
871
|
+
]);
|
|
872
|
+
}
|
|
629
873
|
function HeroCard(props) {
|
|
630
874
|
const t = props.t;
|
|
631
875
|
const snap = props.snap;
|
|
632
|
-
|
|
876
|
+
const rates = snap.rates;
|
|
877
|
+
const progress = Math.max(0, Math.min(1, Number(snap.todayProgress) || 0));
|
|
878
|
+
const percentText = `${(progress * 100).toFixed(1)}%`;
|
|
879
|
+
const live = snap.status === "working";
|
|
880
|
+
const rest = snap.status === "rest";
|
|
881
|
+
const heroBackground = "linear-gradient(135deg, color-mix(in srgb, " + C.brand + " 17%, " + C.layer1 + ") 0%, " + C.layer1 + " 58%, " + C.layer1 + " 100%)";
|
|
882
|
+
return import_react.default.createElement("div", {
|
|
883
|
+
style: {
|
|
884
|
+
position: "relative",
|
|
885
|
+
overflow: "hidden",
|
|
886
|
+
borderRadius: 18,
|
|
887
|
+
padding: "16px 18px 14px",
|
|
888
|
+
border: `1px solid color-mix(in srgb, ${GOLD_TINT} 34%, ${C.border1})`,
|
|
889
|
+
background: heroBackground,
|
|
890
|
+
boxShadow: "0 12px 30px rgba(0,0,0,.10)",
|
|
891
|
+
boxSizing: "border-box",
|
|
892
|
+
minWidth: 0
|
|
893
|
+
}
|
|
894
|
+
}, [
|
|
633
895
|
import_react.default.createElement("div", {
|
|
634
|
-
key: "
|
|
896
|
+
key: "glow",
|
|
897
|
+
"aria-hidden": "true",
|
|
635
898
|
style: {
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
899
|
+
position: "absolute",
|
|
900
|
+
right: -52,
|
|
901
|
+
top: -72,
|
|
902
|
+
width: 188,
|
|
903
|
+
height: 188,
|
|
904
|
+
borderRadius: "50%",
|
|
905
|
+
background: `radial-gradient(circle, color-mix(in srgb, ${GOLD_TINT} 30%, transparent) 0%, transparent 68%)`,
|
|
906
|
+
pointerEvents: "none"
|
|
643
907
|
}
|
|
908
|
+
}),
|
|
909
|
+
import_react.default.createElement("div", {
|
|
910
|
+
key: "head",
|
|
911
|
+
style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8, flexWrap: "wrap" }
|
|
644
912
|
}, [
|
|
645
|
-
import_react.default.createElement("span", {
|
|
913
|
+
import_react.default.createElement("span", {
|
|
914
|
+
key: "a",
|
|
915
|
+
style: { display: "inline-flex", alignItems: "center", gap: 6, fontSize: 12.5, fontWeight: 600, color: C.label2 }
|
|
916
|
+
}, [
|
|
917
|
+
live ? import_react.default.createElement("i", {
|
|
918
|
+
key: "dot",
|
|
919
|
+
className: "niuma-live-dot",
|
|
920
|
+
style: { width: 7, height: 7, borderRadius: "50%", background: C.ok, display: "inline-block" }
|
|
921
|
+
}) : null,
|
|
922
|
+
t("earnedToday")
|
|
923
|
+
]),
|
|
646
924
|
import_react.default.createElement(
|
|
647
925
|
"span",
|
|
648
926
|
{ key: "b", style: { fontSize: 11, color: C.label3, ...NUM } },
|
|
@@ -651,65 +929,61 @@ function HeroCard(props) {
|
|
|
651
929
|
]),
|
|
652
930
|
import_react.default.createElement("div", {
|
|
653
931
|
key: "amt",
|
|
654
|
-
style: { display: "flex", alignItems: "baseline", gap:
|
|
932
|
+
style: { display: "flex", alignItems: "baseline", gap: 7, marginTop: 4, flexWrap: "wrap" }
|
|
655
933
|
}, [
|
|
656
|
-
import_react.default.createElement("span", {
|
|
657
|
-
key: "cur",
|
|
658
|
-
style: { fontSize: 22, fontWeight: 700, color: C.label2, ...NUM }
|
|
659
|
-
}, "\xA5"),
|
|
660
934
|
import_react.default.createElement("span", {
|
|
661
935
|
key: "val",
|
|
662
936
|
style: {
|
|
663
|
-
fontSize:
|
|
664
|
-
lineHeight: 1.
|
|
665
|
-
fontWeight:
|
|
666
|
-
letterSpacing: "
|
|
667
|
-
|
|
668
|
-
...NUM
|
|
937
|
+
fontSize: 46,
|
|
938
|
+
lineHeight: 1.12,
|
|
939
|
+
fontWeight: 800,
|
|
940
|
+
letterSpacing: "-1.2px",
|
|
941
|
+
...GOLD,
|
|
942
|
+
...NUM,
|
|
943
|
+
...rest ? { opacity: 0.62 } : {}
|
|
669
944
|
}
|
|
670
|
-
}, formatMoney(snap.todayEarned, 4))
|
|
945
|
+
}, formatMoney(snap.todayEarned, 4)),
|
|
946
|
+
import_react.default.createElement("span", {
|
|
947
|
+
key: "unit",
|
|
948
|
+
style: { fontSize: 18, fontWeight: 700, color: C.label2, ...NUM }
|
|
949
|
+
}, "\u5143")
|
|
671
950
|
]),
|
|
672
951
|
import_react.default.createElement("div", {
|
|
673
|
-
key: "
|
|
674
|
-
style: {
|
|
675
|
-
}, `${t("perSecond")} ${money(snap.rates.secondWage, 4)}`)
|
|
676
|
-
]);
|
|
677
|
-
}
|
|
678
|
-
function ProgressCard(props) {
|
|
679
|
-
const t = props.t;
|
|
680
|
-
const snap = props.snap;
|
|
681
|
-
const progress = Math.max(0, Math.min(1, Number(snap.todayProgress) || 0));
|
|
682
|
-
const percentText = `${(progress * 100).toFixed(1)}%`;
|
|
683
|
-
const countdownChip = (key, label, seconds) => import_react.default.createElement("div", {
|
|
684
|
-
key,
|
|
685
|
-
style: { ...CARD, flex: "1 1 130px", minWidth: 118, padding: "8px 10px" }
|
|
686
|
-
}, [
|
|
687
|
-
import_react.default.createElement("div", { key: "l", style: { fontSize: 11, color: C.label3 } }, label),
|
|
688
|
-
import_react.default.createElement("div", {
|
|
689
|
-
key: "v",
|
|
690
|
-
style: { fontSize: 14, fontWeight: 650, color: C.label, marginTop: 2, ...NUM }
|
|
691
|
-
}, Number(seconds) > 0 ? formatDuration(seconds) : "\u2014")
|
|
692
|
-
]);
|
|
693
|
-
return import_react.default.createElement("div", { style: CARD }, [
|
|
694
|
-
import_react.default.createElement("div", {
|
|
695
|
-
key: "top",
|
|
696
|
-
style: { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 8, flexWrap: "wrap" }
|
|
952
|
+
key: "chips",
|
|
953
|
+
style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8, marginTop: 5, flexWrap: "wrap" }
|
|
697
954
|
}, [
|
|
698
|
-
import_react.default.createElement("span", { key: "l", style: { fontSize: 12, color: C.label2 } }, t("todayProgress")),
|
|
699
955
|
import_react.default.createElement("span", {
|
|
700
|
-
key: "
|
|
701
|
-
style: {
|
|
702
|
-
|
|
956
|
+
key: "per",
|
|
957
|
+
style: {
|
|
958
|
+
display: "inline-flex",
|
|
959
|
+
alignItems: "center",
|
|
960
|
+
gap: 4,
|
|
961
|
+
fontSize: 11.5,
|
|
962
|
+
fontWeight: 600,
|
|
963
|
+
color: C.label2,
|
|
964
|
+
background: `color-mix(in srgb, ${C.brand} 11%, transparent)`,
|
|
965
|
+
border: `1px solid color-mix(in srgb, ${C.brand} 22%, transparent)`,
|
|
966
|
+
borderRadius: 999,
|
|
967
|
+
padding: "2px 9px",
|
|
968
|
+
...NUM
|
|
969
|
+
}
|
|
970
|
+
}, `${t("perSecond")} +${money(rates.secondWage, 4)}`),
|
|
971
|
+
import_react.default.createElement(
|
|
972
|
+
"span",
|
|
973
|
+
{ key: "target", style: { fontSize: 11, color: C.label3, ...NUM } },
|
|
974
|
+
rest ? t("restHint") : `${t("todayTarget")} ${money(snap.todayTarget, 2)}`
|
|
975
|
+
)
|
|
976
|
+
]),
|
|
977
|
+
import_react.default.createElement("div", {
|
|
978
|
+
key: "progTop",
|
|
979
|
+
style: { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 8, marginTop: 13, flexWrap: "wrap" }
|
|
980
|
+
}, [
|
|
981
|
+
import_react.default.createElement("span", { key: "l", style: { fontSize: 11.5, color: C.label3 } }, t("todayProgress")),
|
|
982
|
+
import_react.default.createElement("span", { key: "p", style: { fontSize: 12.5, fontWeight: 700, color: C.brand, ...NUM } }, percentText)
|
|
703
983
|
]),
|
|
704
984
|
import_react.default.createElement("div", {
|
|
705
985
|
key: "bar",
|
|
706
|
-
style: {
|
|
707
|
-
marginTop: 6,
|
|
708
|
-
height: 8,
|
|
709
|
-
borderRadius: 999,
|
|
710
|
-
background: C.layer2,
|
|
711
|
-
overflow: "hidden"
|
|
712
|
-
}
|
|
986
|
+
style: { marginTop: 5, height: 9, borderRadius: 999, background: `color-mix(in srgb, ${C.label} 9%, transparent)`, overflow: "hidden" }
|
|
713
987
|
}, [
|
|
714
988
|
import_react.default.createElement("div", {
|
|
715
989
|
key: "fill",
|
|
@@ -717,31 +991,60 @@ function ProgressCard(props) {
|
|
|
717
991
|
width: `${(progress * 100).toFixed(2)}%`,
|
|
718
992
|
height: "100%",
|
|
719
993
|
borderRadius: 999,
|
|
720
|
-
background: C.brand,
|
|
721
|
-
transition: "width .
|
|
994
|
+
background: `linear-gradient(90deg, ${C.brand} 0%, color-mix(in srgb, ${C.brand} 55%, ${C.ok}) 100%)`,
|
|
995
|
+
transition: "width .35s linear"
|
|
722
996
|
}
|
|
723
997
|
})
|
|
724
998
|
]),
|
|
725
999
|
import_react.default.createElement("div", {
|
|
726
|
-
key: "
|
|
727
|
-
style: { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 8, marginTop: 5, flexWrap: "wrap", fontSize:
|
|
1000
|
+
key: "progNum",
|
|
1001
|
+
style: { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 8, marginTop: 5, flexWrap: "wrap", fontSize: 10.5, color: C.label3 }
|
|
728
1002
|
}, [
|
|
729
1003
|
import_react.default.createElement("span", { key: "a" }, t("progressOfTarget")),
|
|
730
1004
|
import_react.default.createElement(
|
|
731
1005
|
"span",
|
|
732
|
-
{ key: "b", style: { ...NUM } },
|
|
1006
|
+
{ key: "b", style: { color: C.label2, fontWeight: 600, ...NUM } },
|
|
733
1007
|
`${money(snap.todayEarned, 2)} / ${money(snap.todayTarget, 2)}`
|
|
734
1008
|
)
|
|
735
|
-
]),
|
|
736
|
-
import_react.default.createElement("div", {
|
|
737
|
-
key: "cd",
|
|
738
|
-
style: { display: "flex", flexWrap: "wrap", gap: 8, marginTop: 8 }
|
|
739
|
-
}, [
|
|
740
|
-
countdownChip("start", t("toStart"), snap.secondsToStart),
|
|
741
|
-
countdownChip("end", t("toEnd"), snap.secondsToEnd)
|
|
742
1009
|
])
|
|
743
1010
|
]);
|
|
744
1011
|
}
|
|
1012
|
+
function CountdownRow(props) {
|
|
1013
|
+
const t = props.t;
|
|
1014
|
+
const snap = props.snap;
|
|
1015
|
+
const chip = (key, label, value, accent) => import_react.default.createElement("div", {
|
|
1016
|
+
key,
|
|
1017
|
+
style: {
|
|
1018
|
+
flex: "1 1 120px",
|
|
1019
|
+
minWidth: 116,
|
|
1020
|
+
display: "flex",
|
|
1021
|
+
alignItems: "baseline",
|
|
1022
|
+
justifyContent: "space-between",
|
|
1023
|
+
gap: 8,
|
|
1024
|
+
padding: "9px 12px",
|
|
1025
|
+
borderRadius: 12,
|
|
1026
|
+
background: accent ? `color-mix(in srgb, ${GOLD_TINT} 9%, ${C.layer1})` : C.layer1,
|
|
1027
|
+
border: `1px solid ${accent ? `color-mix(in srgb, ${GOLD_TINT} 38%, ${C.border1})` : C.border1}`,
|
|
1028
|
+
boxSizing: "border-box"
|
|
1029
|
+
}
|
|
1030
|
+
}, [
|
|
1031
|
+
import_react.default.createElement("span", {
|
|
1032
|
+
key: "l",
|
|
1033
|
+
style: { fontSize: 11, color: accent ? C.label2 : C.label3, whiteSpace: "nowrap" }
|
|
1034
|
+
}, label),
|
|
1035
|
+
import_react.default.createElement("span", {
|
|
1036
|
+
key: "v",
|
|
1037
|
+
style: { fontSize: 13.5, fontWeight: 700, color: C.label, ...NUM }
|
|
1038
|
+
}, value)
|
|
1039
|
+
]);
|
|
1040
|
+
return import_react.default.createElement("div", {
|
|
1041
|
+
style: { display: "flex", flexWrap: "wrap", gap: 9 }
|
|
1042
|
+
}, [
|
|
1043
|
+
chip("start", t("toStart"), Number(snap.secondsToStart) > 0 ? formatDuration(snap.secondsToStart) : "\u2014"),
|
|
1044
|
+
chip("end", t("toEnd"), Number(snap.secondsToEnd) > 0 ? formatDuration(snap.secondsToEnd) : "\u2014"),
|
|
1045
|
+
chip("month", t("monthEarned"), money(snap.monthEarned, 2), true)
|
|
1046
|
+
]);
|
|
1047
|
+
}
|
|
745
1048
|
var RATE_CARDS = [
|
|
746
1049
|
{ key: "secondWage", label: "secondWage", digits: 4 },
|
|
747
1050
|
{ key: "minuteWage", label: "minuteWage", digits: 4 },
|
|
@@ -751,27 +1054,78 @@ var RATE_CARDS = [
|
|
|
751
1054
|
{ key: "monthlyWage", label: "monthlyWage", digits: 2 }
|
|
752
1055
|
];
|
|
753
1056
|
function RatesGrid(props) {
|
|
754
|
-
const
|
|
755
|
-
const rates = props.rates;
|
|
1057
|
+
const cards = Array.isArray(props.cards) ? props.cards : [];
|
|
756
1058
|
return import_react.default.createElement("div", {
|
|
757
1059
|
style: { display: "grid", gridTemplateColumns: "repeat(3, minmax(0, 1fr))", gap: 8 }
|
|
758
|
-
},
|
|
759
|
-
|
|
760
|
-
"
|
|
761
|
-
|
|
1060
|
+
}, cards.map((card, index) => {
|
|
1061
|
+
const accent = card.accent === true;
|
|
1062
|
+
return import_react.default.createElement("div", {
|
|
1063
|
+
key: card.key,
|
|
1064
|
+
"data-stat": `stat-${index}`,
|
|
1065
|
+
style: {
|
|
1066
|
+
...CARD,
|
|
1067
|
+
padding: "10px 11px",
|
|
1068
|
+
borderRadius: 12,
|
|
1069
|
+
...accent ? {
|
|
1070
|
+
border: `1px solid color-mix(in srgb, ${GOLD_TINT} 46%, ${C.border1})`,
|
|
1071
|
+
background: `color-mix(in srgb, ${GOLD_TINT} 9%, ${C.layer1})`
|
|
1072
|
+
} : {}
|
|
1073
|
+
}
|
|
1074
|
+
}, [
|
|
1075
|
+
import_react.default.createElement("div", {
|
|
1076
|
+
key: "l",
|
|
1077
|
+
style: { fontSize: 11, color: accent ? C.label2 : C.label3, fontWeight: accent ? 600 : 400 }
|
|
1078
|
+
}, card.label),
|
|
1079
|
+
import_react.default.createElement("div", {
|
|
1080
|
+
key: "v",
|
|
1081
|
+
style: {
|
|
1082
|
+
fontSize: 15.5,
|
|
1083
|
+
fontWeight: 700,
|
|
1084
|
+
letterSpacing: "-.2px",
|
|
1085
|
+
color: accent ? C.label : C.label,
|
|
1086
|
+
marginTop: 2,
|
|
1087
|
+
...NUM
|
|
1088
|
+
}
|
|
1089
|
+
}, card.value)
|
|
1090
|
+
]);
|
|
1091
|
+
}));
|
|
1092
|
+
}
|
|
1093
|
+
function PeriodSelect(props) {
|
|
1094
|
+
const t = props.t;
|
|
1095
|
+
const year = props.year;
|
|
1096
|
+
const months = props.months;
|
|
1097
|
+
const style = {
|
|
1098
|
+
appearance: "none",
|
|
1099
|
+
WebkitAppearance: "none",
|
|
1100
|
+
border: `1px solid ${C.border}`,
|
|
1101
|
+
background: C.layer2,
|
|
1102
|
+
color: C.label,
|
|
1103
|
+
borderRadius: 8,
|
|
1104
|
+
padding: "4px 9px",
|
|
1105
|
+
fontSize: 12,
|
|
1106
|
+
fontWeight: 600,
|
|
1107
|
+
cursor: "pointer",
|
|
1108
|
+
outline: "none",
|
|
1109
|
+
maxWidth: 220
|
|
1110
|
+
};
|
|
1111
|
+
return import_react.default.createElement("select", {
|
|
1112
|
+
value: props.value,
|
|
1113
|
+
"aria-label": t("viewLabel"),
|
|
1114
|
+
onChange: (event) => props.onChange(event.target.value),
|
|
1115
|
+
style
|
|
762
1116
|
}, [
|
|
763
|
-
import_react.default.createElement("
|
|
764
|
-
import_react.default.createElement("
|
|
765
|
-
key:
|
|
766
|
-
|
|
767
|
-
},
|
|
768
|
-
|
|
1117
|
+
import_react.default.createElement("option", { key: "current", value: "current" }, t("viewCurrent")),
|
|
1118
|
+
...months.map((month) => import_react.default.createElement("option", {
|
|
1119
|
+
key: `m${month.month}`,
|
|
1120
|
+
value: `m${month.month}`
|
|
1121
|
+
}, `${month.month}${t("monthUnit")} \xB7 ${month.workdays}${t("dayUnit")} \xB7 ${t("holidayShort")}${month.holidayDays}`)),
|
|
1122
|
+
import_react.default.createElement("option", { key: "year", value: "year" }, `${year} ${t("viewYear")}`)
|
|
1123
|
+
]);
|
|
769
1124
|
}
|
|
770
1125
|
function DetailsFooter(props) {
|
|
771
1126
|
const t = props.t;
|
|
772
1127
|
const snap = props.snap;
|
|
773
1128
|
const rates = snap.rates;
|
|
774
|
-
const source = props.holidaySource || HOLIDAY_SOURCE;
|
|
775
1129
|
const items = [
|
|
776
1130
|
{ key: "salary", label: t("monthlySalaryLabel"), value: money(rates.monthlySalary, 2) },
|
|
777
1131
|
{ key: "workdays", label: t("workdaysThisMonth"), value: `${rates.monthlyWorkdays}${t("dayUnit")}` },
|
|
@@ -780,36 +1134,165 @@ function DetailsFooter(props) {
|
|
|
780
1134
|
{ key: "month", label: t("monthEarned"), value: money(snap.monthEarned, 2) }
|
|
781
1135
|
];
|
|
782
1136
|
return import_react.default.createElement("div", {
|
|
783
|
-
style: {
|
|
1137
|
+
style: {
|
|
1138
|
+
background: C.layer1,
|
|
1139
|
+
border: `1px solid ${C.border1}`,
|
|
1140
|
+
borderRadius: 12,
|
|
1141
|
+
padding: "10px 12px",
|
|
1142
|
+
fontSize: 11,
|
|
1143
|
+
color: C.label3,
|
|
1144
|
+
lineHeight: 1.6
|
|
1145
|
+
}
|
|
784
1146
|
}, [
|
|
785
1147
|
import_react.default.createElement("div", {
|
|
786
1148
|
key: "items",
|
|
787
|
-
style: { display: "
|
|
788
|
-
}, items.map((item) => import_react.default.createElement("
|
|
1149
|
+
style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(118px, 1fr))", gap: "7px 12px" }
|
|
1150
|
+
}, items.map((item) => import_react.default.createElement("div", {
|
|
789
1151
|
key: item.key,
|
|
790
|
-
style: {
|
|
1152
|
+
style: { minWidth: 0 }
|
|
791
1153
|
}, [
|
|
792
|
-
import_react.default.createElement("
|
|
793
|
-
import_react.default.createElement("
|
|
1154
|
+
import_react.default.createElement("div", { key: "l", style: { fontSize: 10.5, color: C.label3 } }, item.label),
|
|
1155
|
+
import_react.default.createElement("div", {
|
|
794
1156
|
key: "v",
|
|
795
|
-
style: {
|
|
1157
|
+
style: { fontSize: 13, fontWeight: 650, color: C.label2, ...NUM }
|
|
796
1158
|
}, item.value)
|
|
1159
|
+
])))
|
|
1160
|
+
]);
|
|
1161
|
+
}
|
|
1162
|
+
var HOLIDAY_LABEL_KEYS = {
|
|
1163
|
+
newYear: "holidayNewYear",
|
|
1164
|
+
spring: "holidaySpring",
|
|
1165
|
+
qingming: "holidayQingming",
|
|
1166
|
+
labour: "holidayLabour",
|
|
1167
|
+
dragon: "holidayDragon",
|
|
1168
|
+
midAutumn: "holidayMidAutumn",
|
|
1169
|
+
national: "holidayNational"
|
|
1170
|
+
};
|
|
1171
|
+
function HolidaysCard(props) {
|
|
1172
|
+
const t = props.t;
|
|
1173
|
+
const source = props.holidaySource || HOLIDAY_SOURCE;
|
|
1174
|
+
const extra = Array.isArray(props.extraWorkdays) ? props.extraWorkdays : [];
|
|
1175
|
+
const countYear = (year) => extra.filter((key) => String(key).startsWith(`${year}-`)).length;
|
|
1176
|
+
const shortDate = (key) => key.slice(5).replace("-", "/");
|
|
1177
|
+
const sections = [
|
|
1178
|
+
{
|
|
1179
|
+
key: "official",
|
|
1180
|
+
label: t("holidays2026Label"),
|
|
1181
|
+
blocks: HOLIDAY_BLOCKS,
|
|
1182
|
+
estimated: false,
|
|
1183
|
+
extraCount: countYear(2026),
|
|
1184
|
+
tag: null
|
|
1185
|
+
},
|
|
1186
|
+
{
|
|
1187
|
+
key: "estimate",
|
|
1188
|
+
label: t("holidays2027Label"),
|
|
1189
|
+
blocks: HOLIDAY_BLOCKS_2027_ESTIMATED,
|
|
1190
|
+
estimated: true,
|
|
1191
|
+
extraCount: countYear(2027),
|
|
1192
|
+
tag: t("holidayEstimateTag")
|
|
1193
|
+
}
|
|
1194
|
+
];
|
|
1195
|
+
const sumDays = (blocks) => blocks.reduce((sum, block) => sum + block.days, 0);
|
|
1196
|
+
const renderSection = (section) => import_react.default.createElement("div", { key: section.key, style: { marginTop: section.key === "official" ? 8 : 12 } }, [
|
|
1197
|
+
import_react.default.createElement("div", {
|
|
1198
|
+
key: "head",
|
|
1199
|
+
style: { display: "flex", alignItems: "baseline", flexWrap: "wrap", gap: 8 }
|
|
1200
|
+
}, [
|
|
1201
|
+
import_react.default.createElement("span", { key: "l", style: { fontSize: 11.5, fontWeight: 700, color: C.label2 } }, section.label),
|
|
1202
|
+
section.tag ? import_react.default.createElement("span", {
|
|
1203
|
+
key: "tag",
|
|
1204
|
+
style: {
|
|
1205
|
+
fontSize: 9.5,
|
|
1206
|
+
fontWeight: 700,
|
|
1207
|
+
color: C.label2,
|
|
1208
|
+
border: `1px dashed color-mix(in srgb, ${GOLD_TINT} 60%, transparent)`,
|
|
1209
|
+
borderRadius: 999,
|
|
1210
|
+
padding: "0 6px",
|
|
1211
|
+
lineHeight: 1.6
|
|
1212
|
+
}
|
|
1213
|
+
}, section.tag) : null,
|
|
1214
|
+
import_react.default.createElement(
|
|
1215
|
+
"span",
|
|
1216
|
+
{ key: "s", style: { marginLeft: "auto", fontSize: 10, color: C.label3, ...NUM } },
|
|
1217
|
+
t("holidaysCardSub").replace("{days}", String(sumDays(section.blocks))).replace("{extra}", String(section.extraCount))
|
|
1218
|
+
)
|
|
1219
|
+
]),
|
|
1220
|
+
import_react.default.createElement("div", {
|
|
1221
|
+
key: "grid",
|
|
1222
|
+
style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(118px, 1fr))", gap: 7, marginTop: 6 }
|
|
1223
|
+
}, section.blocks.map((block) => import_react.default.createElement("div", {
|
|
1224
|
+
key: block.id,
|
|
1225
|
+
style: {
|
|
1226
|
+
border: section.estimated ? `1px dashed color-mix(in srgb, ${GOLD_TINT} 40%, ${C.border1})` : `1px solid ${C.border1}`,
|
|
1227
|
+
borderRadius: 9,
|
|
1228
|
+
padding: "6px 8px",
|
|
1229
|
+
background: `color-mix(in srgb, ${GOLD_TINT} ${section.estimated ? 3 : 6}%, transparent)`
|
|
1230
|
+
}
|
|
1231
|
+
}, [
|
|
1232
|
+
import_react.default.createElement("div", {
|
|
1233
|
+
key: "row",
|
|
1234
|
+
style: { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 6 }
|
|
1235
|
+
}, [
|
|
1236
|
+
import_react.default.createElement(
|
|
1237
|
+
"span",
|
|
1238
|
+
{ key: "n", style: { fontSize: 11.5, fontWeight: 650, color: C.label } },
|
|
1239
|
+
t(HOLIDAY_LABEL_KEYS[block.id])
|
|
1240
|
+
),
|
|
1241
|
+
import_react.default.createElement("span", {
|
|
1242
|
+
key: "d",
|
|
1243
|
+
style: {
|
|
1244
|
+
fontSize: 10,
|
|
1245
|
+
fontWeight: 700,
|
|
1246
|
+
color: C.label2,
|
|
1247
|
+
background: `color-mix(in srgb, ${GOLD_TINT} 16%, transparent)`,
|
|
1248
|
+
borderRadius: 999,
|
|
1249
|
+
padding: "0 6px",
|
|
1250
|
+
...NUM
|
|
1251
|
+
}
|
|
1252
|
+
}, t("holidaysDaysShort").replace("{days}", String(block.days)))
|
|
1253
|
+
]),
|
|
1254
|
+
import_react.default.createElement(
|
|
1255
|
+
"div",
|
|
1256
|
+
{ key: "r", style: { fontSize: 10, color: C.label3, marginTop: 2, ...NUM } },
|
|
1257
|
+
`${shortDate(block.from)} \u2013 ${shortDate(block.to)}`
|
|
1258
|
+
)
|
|
797
1259
|
]))),
|
|
1260
|
+
section.estimated ? import_react.default.createElement("div", {
|
|
1261
|
+
key: "est-note",
|
|
1262
|
+
style: { marginTop: 6, fontSize: 10.5, color: C.label3, lineHeight: 1.5 }
|
|
1263
|
+
}, `\u26A0 ${t("holidayEstimateNote")}`) : null
|
|
1264
|
+
]);
|
|
1265
|
+
return import_react.default.createElement("div", {
|
|
1266
|
+
style: {
|
|
1267
|
+
background: C.layer1,
|
|
1268
|
+
border: `1px solid ${C.border1}`,
|
|
1269
|
+
borderRadius: 12,
|
|
1270
|
+
padding: "10px 12px"
|
|
1271
|
+
}
|
|
1272
|
+
}, [
|
|
798
1273
|
import_react.default.createElement("div", {
|
|
799
|
-
key: "
|
|
800
|
-
style: { display: "flex",
|
|
1274
|
+
key: "title",
|
|
1275
|
+
style: { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 8, flexWrap: "wrap" }
|
|
801
1276
|
}, [
|
|
802
|
-
import_react.default.createElement(
|
|
1277
|
+
import_react.default.createElement(
|
|
1278
|
+
"span",
|
|
1279
|
+
{ key: "t", style: { fontSize: 12.5, fontWeight: 700, color: C.label2, letterSpacing: ".2px" } },
|
|
1280
|
+
t("holidaysCardTitle")
|
|
1281
|
+
),
|
|
803
1282
|
import_react.default.createElement("a", {
|
|
804
1283
|
key: "a",
|
|
805
1284
|
href: source.url,
|
|
806
1285
|
target: "_blank",
|
|
807
1286
|
rel: "noreferrer",
|
|
808
1287
|
title: source.title,
|
|
809
|
-
style: { color: C.brand, textDecoration: "
|
|
810
|
-
}, `${source.year}
|
|
811
|
-
|
|
812
|
-
|
|
1288
|
+
style: { fontSize: 10.5, color: C.brand, textDecoration: "none", fontWeight: 600 }
|
|
1289
|
+
}, `${source.year} ${t("holidaySourceLink")} \u2197`)
|
|
1290
|
+
]),
|
|
1291
|
+
sections.map(renderSection),
|
|
1292
|
+
import_react.default.createElement("div", {
|
|
1293
|
+
key: "foot",
|
|
1294
|
+
style: { marginTop: 8, fontSize: 10.5, color: C.label3 }
|
|
1295
|
+
}, t("holidaysExtraNote").replace("{extra}", String(extra.length)))
|
|
813
1296
|
]);
|
|
814
1297
|
}
|
|
815
1298
|
function SettingsSection(props) {
|
|
@@ -822,7 +1305,7 @@ function SettingsSection(props) {
|
|
|
822
1305
|
const inputStyle = {
|
|
823
1306
|
width: "100%",
|
|
824
1307
|
boxSizing: "border-box",
|
|
825
|
-
padding: "
|
|
1308
|
+
padding: "6px 9px",
|
|
826
1309
|
borderRadius: 8,
|
|
827
1310
|
border: `1px solid ${C.border}`,
|
|
828
1311
|
background: C.layer2,
|
|
@@ -831,10 +1314,10 @@ function SettingsSection(props) {
|
|
|
831
1314
|
fontVariantNumeric: "tabular-nums",
|
|
832
1315
|
outline: "none"
|
|
833
1316
|
};
|
|
834
|
-
const labelStyle = { fontSize:
|
|
1317
|
+
const labelStyle = { fontSize: 10.5, color: C.label3, fontWeight: 500 };
|
|
835
1318
|
const textField = (field, labelKey, placeholder) => import_react.default.createElement("label", {
|
|
836
1319
|
key: field,
|
|
837
|
-
style: { display: "flex", flexDirection: "column", gap:
|
|
1320
|
+
style: { display: "flex", flexDirection: "column", gap: 4, flex: "1 1 130px", minWidth: 112 }
|
|
838
1321
|
}, [
|
|
839
1322
|
import_react.default.createElement("span", { key: "l", style: labelStyle }, t(labelKey)),
|
|
840
1323
|
import_react.default.createElement("input", {
|
|
@@ -867,37 +1350,24 @@ function SettingsSection(props) {
|
|
|
867
1350
|
}),
|
|
868
1351
|
import_react.default.createElement("span", { key: "l" }, t(labelKey))
|
|
869
1352
|
]);
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
style: { display: "flex", flexDirection: "column", gap: 3, flex: "1 1 220px", minWidth: 200 }
|
|
1353
|
+
return import_react.default.createElement("details", {
|
|
1354
|
+
style: { background: C.layer1, border: `1px solid ${C.border1}`, borderRadius: 12, padding: "11px 13px" }
|
|
873
1355
|
}, [
|
|
874
|
-
import_react.default.createElement("span", { key: "l", style: labelStyle }, t(labelKey)),
|
|
875
|
-
import_react.default.createElement("textarea", {
|
|
876
|
-
key: "i",
|
|
877
|
-
rows: 4,
|
|
878
|
-
value: form[field],
|
|
879
|
-
spellCheck: false,
|
|
880
|
-
onChange: (event) => setField(field, event.target.value),
|
|
881
|
-
style: {
|
|
882
|
-
...inputStyle,
|
|
883
|
-
resize: "vertical",
|
|
884
|
-
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
|
|
885
|
-
lineHeight: 1.5
|
|
886
|
-
}
|
|
887
|
-
})
|
|
888
|
-
]);
|
|
889
|
-
return import_react.default.createElement("details", { style: { ...CARD, padding: "10px 12px" } }, [
|
|
890
1356
|
import_react.default.createElement("summary", {
|
|
891
1357
|
key: "sum",
|
|
892
|
-
style: { cursor: "pointer", fontSize: 13, fontWeight:
|
|
1358
|
+
style: { cursor: "pointer", fontSize: 13, fontWeight: 650, color: C.label, userSelect: "none" }
|
|
893
1359
|
}, t("settings")),
|
|
894
1360
|
import_react.default.createElement("div", {
|
|
895
1361
|
key: "hint",
|
|
896
|
-
style: { fontSize:
|
|
1362
|
+
style: { fontSize: 10.5, color: C.label3, margin: "5px 0 10px" }
|
|
897
1363
|
}, t("settingsHint")),
|
|
1364
|
+
import_react.default.createElement("div", {
|
|
1365
|
+
key: "dataNote",
|
|
1366
|
+
style: { fontSize: 10.5, color: C.label3, margin: "-4px 0 10px", lineHeight: 1.55 }
|
|
1367
|
+
}, t("settingsDataNote")),
|
|
898
1368
|
import_react.default.createElement("div", {
|
|
899
1369
|
key: "fields",
|
|
900
|
-
style: { display: "flex", flexWrap: "wrap", gap:
|
|
1370
|
+
style: { display: "flex", flexWrap: "wrap", gap: 9 }
|
|
901
1371
|
}, [
|
|
902
1372
|
textField("monthlySalary", "salaryField", "10000"),
|
|
903
1373
|
textField("workStart", "workStartField", "09:00"),
|
|
@@ -907,16 +1377,9 @@ function SettingsSection(props) {
|
|
|
907
1377
|
checkField("lunchEnabled", "lunchEnabledField"),
|
|
908
1378
|
checkField("lunchPaid", "lunchPaidField")
|
|
909
1379
|
]),
|
|
910
|
-
import_react.default.createElement("div", {
|
|
911
|
-
key: "lists",
|
|
912
|
-
style: { display: "flex", flexWrap: "wrap", gap: 8, marginTop: 8 }
|
|
913
|
-
}, [
|
|
914
|
-
textareaField("holidaysText", "holidaysField"),
|
|
915
|
-
textareaField("extraWorkdaysText", "extraWorkdaysField")
|
|
916
|
-
]),
|
|
917
1380
|
import_react.default.createElement("div", {
|
|
918
1381
|
key: "actions",
|
|
919
|
-
style: { display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap", marginTop:
|
|
1382
|
+
style: { display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap", marginTop: 11 }
|
|
920
1383
|
}, [
|
|
921
1384
|
import_react.default.createElement("button", {
|
|
922
1385
|
key: "save",
|
|
@@ -924,14 +1387,19 @@ function SettingsSection(props) {
|
|
|
924
1387
|
onClick: onSave,
|
|
925
1388
|
disabled: save.status === "saving",
|
|
926
1389
|
style: {
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
1390
|
+
// 金色按钮:与主卡金额、节假日强调同一套金色系;深棕字保证任何主题下都有对比度
|
|
1391
|
+
border: `1px solid color-mix(in srgb, ${GOLD_TINT} 72%, rgba(0,0,0,.18))`,
|
|
1392
|
+
color: "#3A2A05",
|
|
1393
|
+
background: save.status === "saving" ? `color-mix(in srgb, ${GOLD_TINT} 52%, #fff)` : "linear-gradient(180deg, #F8DC86 0%, #EFC258 52%, #D99D2C 100%)",
|
|
930
1394
|
borderRadius: 8,
|
|
931
|
-
padding: "
|
|
1395
|
+
padding: "7px 20px",
|
|
932
1396
|
fontSize: 13,
|
|
933
|
-
fontWeight:
|
|
934
|
-
|
|
1397
|
+
fontWeight: 700,
|
|
1398
|
+
letterSpacing: ".3px",
|
|
1399
|
+
cursor: save.status === "saving" ? "default" : "pointer",
|
|
1400
|
+
opacity: save.status === "saving" ? 0.72 : 1,
|
|
1401
|
+
boxShadow: `0 2px 10px color-mix(in srgb, ${GOLD_TINT} 36%, transparent), inset 0 1px 0 rgba(255,255,255,.45)`,
|
|
1402
|
+
textShadow: "0 1px 0 rgba(255,255,255,.28)"
|
|
935
1403
|
}
|
|
936
1404
|
}, save.status === "saving" ? t("saving") : t("save")),
|
|
937
1405
|
save.status === "ok" ? import_react.default.createElement("span", { key: "ok", style: { fontSize: 12, color: C.ok } }, save.message || t("saved")) : null,
|
|
@@ -959,6 +1427,48 @@ function NiumaPanel(props) {
|
|
|
959
1427
|
const snap = import_react.default.useMemo(() => earnedToday(cfg, now), [cfg, now]);
|
|
960
1428
|
const rates = snap && snap.rates ? snap.rates : computeRates(cfg, snap && snap.dateKey);
|
|
961
1429
|
const baseForm = import_react.default.useMemo(() => formFromConfig(cfg), [cfg]);
|
|
1430
|
+
const viewYear = Number(String(snap && snap.dateKey ? snap.dateKey : "").slice(0, 4)) || (/* @__PURE__ */ new Date()).getFullYear();
|
|
1431
|
+
const yearEstimated = viewYear === HOLIDAY_ESTIMATE_2027.year;
|
|
1432
|
+
const yearData = import_react.default.useMemo(() => yearSummary(cfg, viewYear), [cfg, viewYear]);
|
|
1433
|
+
const [view, setView] = import_react.default.useState("current");
|
|
1434
|
+
const selectedMonth = view.startsWith("m") ? yearData.months[Number(view.slice(1)) - 1] : null;
|
|
1435
|
+
const maxHolidayMonth = yearData.maxHolidayMonth;
|
|
1436
|
+
const maxHolidayText = t("maxHolidayNote").replace("{month}", String(maxHolidayMonth.month)).replace("{days}", String(maxHolidayMonth.holidayDays));
|
|
1437
|
+
let viewCards;
|
|
1438
|
+
let viewHint;
|
|
1439
|
+
let viewNote;
|
|
1440
|
+
if (view === "year") {
|
|
1441
|
+
viewCards = [
|
|
1442
|
+
{ key: "avgSecond", label: t("averageSecond"), value: money(yearData.averageSecondWage, 4), accent: true },
|
|
1443
|
+
{ key: "avgMinute", label: t("averageMinute"), value: money(yearData.averageMinuteWage, 4) },
|
|
1444
|
+
{ key: "avgHourly", label: t("averageHourly"), value: money(yearData.averageHourlyWage, 2) },
|
|
1445
|
+
{ key: "avgDaily", label: t("averageDaily"), value: money(yearData.averageDailyWage, 2) },
|
|
1446
|
+
{ key: "yearWorkdays", label: t("yearWorkdays"), value: `${yearData.totalWorkdays}${t("dayUnit")}` },
|
|
1447
|
+
{ key: "yearIncome", label: t("yearIncome"), value: money(yearData.annualIncome, 2) }
|
|
1448
|
+
];
|
|
1449
|
+
viewHint = `${t("yearHint").replace("{year}", String(viewYear)).replace("{workdays}", String(yearData.totalWorkdays))} \xB7 ${t("viewOnlyHint")}` + (yearEstimated ? ` \xB7 ${t("holidayEstimateShort")}` : "");
|
|
1450
|
+
viewNote = yearEstimated ? t("holidayEstimateNote") : maxHolidayText;
|
|
1451
|
+
} else if (selectedMonth) {
|
|
1452
|
+
viewCards = [
|
|
1453
|
+
{ key: "secondWage", label: t("secondWage"), value: money(selectedMonth.secondWage, 4), accent: true },
|
|
1454
|
+
{ key: "minuteWage", label: t("minuteWage"), value: money(selectedMonth.minuteWage, 4) },
|
|
1455
|
+
{ key: "hourlyWage", label: t("hourlyWage"), value: money(selectedMonth.hourlyWage, 2) },
|
|
1456
|
+
{ key: "dailyWage", label: t("dailyWage"), value: money(selectedMonth.dailyWage, 2) },
|
|
1457
|
+
{ key: "monthlyWage", label: t("monthlyWage"), value: money(selectedMonth.monthlySalary, 2) },
|
|
1458
|
+
{ key: "monthWorkdays", label: t("monthWorkdaysCard"), value: `${selectedMonth.workdays}${t("dayUnit")}` }
|
|
1459
|
+
];
|
|
1460
|
+
viewHint = `${t("monthHint").replace("{year}", String(viewYear)).replace("{month}", String(selectedMonth.month)).replace("{workdays}", String(selectedMonth.workdays)).replace("{holidays}", String(selectedMonth.holidayDays))} \xB7 ${t("viewOnlyHint")}` + (yearEstimated ? ` \xB7 ${t("holidayEstimateShort")}` : "");
|
|
1461
|
+
viewNote = yearEstimated ? t("holidayEstimateNote") : selectedMonth.month === maxHolidayMonth.month ? `\u2605 ${t("maxHolidayThis").replace("{days}", String(maxHolidayMonth.holidayDays))}` : maxHolidayText;
|
|
1462
|
+
} else {
|
|
1463
|
+
viewCards = RATE_CARDS.map((spec) => ({
|
|
1464
|
+
key: spec.key,
|
|
1465
|
+
label: t(spec.label),
|
|
1466
|
+
value: money(rates[spec.key], spec.digits),
|
|
1467
|
+
accent: spec.key === "secondWage"
|
|
1468
|
+
}));
|
|
1469
|
+
viewHint = t("currentHint").replace("{days}", String(rates.monthlyWorkdays)).replace("{hours}", trimNumber(rates.paidHours, 2));
|
|
1470
|
+
viewNote = maxHolidayText;
|
|
1471
|
+
}
|
|
962
1472
|
const [draft, setDraft] = import_react.default.useState(null);
|
|
963
1473
|
const [save, setSave] = import_react.default.useState({ status: "idle", errors: [], message: "" });
|
|
964
1474
|
const form = draft || baseForm;
|
|
@@ -1011,79 +1521,166 @@ function NiumaPanel(props) {
|
|
|
1011
1521
|
} : remote.status === "loading" ? { color: C.label3, text: t("configLoading"), retry: false } : null;
|
|
1012
1522
|
return import_react.default.createElement("div", {
|
|
1013
1523
|
style: {
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1524
|
+
// DSH 的 main slot 给的是定高容器:插件必须自己当滚动容器,否则展开设置后底部会被裁掉。
|
|
1525
|
+
// 注意:这里用 block + 内层 flex 的写法 —— 如果根直接 display:flex,子项会在定高容器里
|
|
1526
|
+
// 被 flex-shrink 压扁(展开设置时整张主卡高度被压缩、内容被 overflow 裁掉)。
|
|
1527
|
+
height: "100%",
|
|
1528
|
+
width: "100%",
|
|
1529
|
+
overflowY: "auto",
|
|
1530
|
+
overflowX: "hidden",
|
|
1531
|
+
overscrollBehavior: "contain",
|
|
1532
|
+
scrollbarGutter: "stable",
|
|
1533
|
+
boxSizing: "border-box",
|
|
1019
1534
|
fontSize: 13,
|
|
1020
1535
|
lineHeight: 1.45,
|
|
1021
1536
|
color: C.label,
|
|
1022
1537
|
fontFamily: "inherit"
|
|
1023
1538
|
}
|
|
1024
1539
|
}, [
|
|
1025
|
-
|
|
1540
|
+
import_react.default.createElement(StyleTag, { key: "css" }),
|
|
1026
1541
|
import_react.default.createElement("div", {
|
|
1027
|
-
key: "
|
|
1028
|
-
style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }
|
|
1029
|
-
}, [
|
|
1030
|
-
import_react.default.createElement("div", { key: "tt", style: { marginRight: "auto", minWidth: 0 } }, [
|
|
1031
|
-
import_react.default.createElement("div", {
|
|
1032
|
-
key: "t",
|
|
1033
|
-
style: { fontSize: 16, fontWeight: 700, color: C.label }
|
|
1034
|
-
}, t("title")),
|
|
1035
|
-
import_react.default.createElement("div", {
|
|
1036
|
-
key: "s",
|
|
1037
|
-
style: { fontSize: 11, color: C.label3 }
|
|
1038
|
-
}, t("subtitle"))
|
|
1039
|
-
]),
|
|
1040
|
-
import_react.default.createElement(StatusBadge, { key: "badge", t, status: snap.status })
|
|
1041
|
-
]),
|
|
1042
|
-
// 配置读取状态
|
|
1043
|
-
banner ? import_react.default.createElement("div", {
|
|
1044
|
-
key: "banner",
|
|
1542
|
+
key: "inner",
|
|
1045
1543
|
style: {
|
|
1046
1544
|
display: "flex",
|
|
1047
|
-
|
|
1048
|
-
gap:
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
padding: "
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
background: C.layer1
|
|
1545
|
+
flexDirection: "column",
|
|
1546
|
+
gap: 11,
|
|
1547
|
+
width: "100%",
|
|
1548
|
+
maxWidth: 820,
|
|
1549
|
+
margin: "0 auto",
|
|
1550
|
+
padding: "16px 18px 40px",
|
|
1551
|
+
boxSizing: "border-box",
|
|
1552
|
+
minWidth: 0
|
|
1056
1553
|
}
|
|
1057
1554
|
}, [
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
key: "
|
|
1061
|
-
|
|
1062
|
-
|
|
1555
|
+
// 品牌头:图标块 + 标题/副标题 + 状态徽标
|
|
1556
|
+
import_react.default.createElement("div", {
|
|
1557
|
+
key: "head",
|
|
1558
|
+
style: { display: "flex", alignItems: "center", gap: 11, flexWrap: "wrap" }
|
|
1559
|
+
}, [
|
|
1560
|
+
import_react.default.createElement("div", {
|
|
1561
|
+
key: "icon",
|
|
1562
|
+
style: {
|
|
1563
|
+
width: 38,
|
|
1564
|
+
height: 38,
|
|
1565
|
+
borderRadius: 12,
|
|
1566
|
+
flex: "0 0 auto",
|
|
1567
|
+
display: "grid",
|
|
1568
|
+
placeItems: "center",
|
|
1569
|
+
color: C.brand,
|
|
1570
|
+
background: `color-mix(in srgb, ${C.brand} 13%, transparent)`,
|
|
1571
|
+
border: `1px solid color-mix(in srgb, ${C.brand} 26%, transparent)`
|
|
1572
|
+
}
|
|
1573
|
+
}, import_react.default.createElement(NiumaIcon, { size: 21 })),
|
|
1574
|
+
import_react.default.createElement("div", { key: "tt", style: { marginRight: "auto", minWidth: 0 } }, [
|
|
1575
|
+
import_react.default.createElement("div", {
|
|
1576
|
+
key: "t",
|
|
1577
|
+
style: { fontSize: 17.5, fontWeight: 800, letterSpacing: "-.3px", color: C.label }
|
|
1578
|
+
}, t("title")),
|
|
1579
|
+
import_react.default.createElement("div", {
|
|
1580
|
+
key: "s",
|
|
1581
|
+
style: { fontSize: 11, color: C.label3, marginTop: 1 }
|
|
1582
|
+
}, t("subtitle"))
|
|
1583
|
+
]),
|
|
1584
|
+
import_react.default.createElement(StatusBadge, { key: "badge", t, status: snap.status })
|
|
1585
|
+
]),
|
|
1586
|
+
// 配置读取状态
|
|
1587
|
+
banner ? import_react.default.createElement("div", {
|
|
1588
|
+
key: "banner",
|
|
1063
1589
|
style: {
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
padding: "2px 10px",
|
|
1590
|
+
display: "flex",
|
|
1591
|
+
alignItems: "center",
|
|
1592
|
+
gap: 8,
|
|
1593
|
+
flexWrap: "wrap",
|
|
1069
1594
|
fontSize: 12,
|
|
1070
|
-
|
|
1595
|
+
color: banner.color,
|
|
1596
|
+
padding: "6px 10px",
|
|
1597
|
+
borderRadius: 9,
|
|
1598
|
+
border: `1px solid color-mix(in srgb, ${banner.color} 45%, transparent)`,
|
|
1599
|
+
background: `color-mix(in srgb, ${banner.color} 10%, transparent)`
|
|
1071
1600
|
}
|
|
1072
|
-
},
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1601
|
+
}, [
|
|
1602
|
+
import_react.default.createElement("span", { key: "txt", style: { flex: "1 1 160px" } }, banner.text),
|
|
1603
|
+
banner.retry ? import_react.default.createElement("button", {
|
|
1604
|
+
key: "retry",
|
|
1605
|
+
type: "button",
|
|
1606
|
+
onClick: remote.refresh,
|
|
1607
|
+
style: {
|
|
1608
|
+
border: `1px solid ${banner.color}`,
|
|
1609
|
+
color: banner.color,
|
|
1610
|
+
background: "transparent",
|
|
1611
|
+
borderRadius: 6,
|
|
1612
|
+
padding: "2px 10px",
|
|
1613
|
+
fontSize: 12,
|
|
1614
|
+
cursor: "pointer"
|
|
1615
|
+
}
|
|
1616
|
+
}, t("retry")) : null
|
|
1617
|
+
]) : null,
|
|
1618
|
+
import_react.default.createElement(HeroCard, { key: "hero", t, snap }),
|
|
1619
|
+
import_react.default.createElement(CountdownRow, { key: "countdown", t, snap }),
|
|
1620
|
+
import_react.default.createElement("div", {
|
|
1621
|
+
key: "ratesHead",
|
|
1622
|
+
style: {
|
|
1623
|
+
display: "flex",
|
|
1624
|
+
alignItems: "center",
|
|
1625
|
+
justifyContent: "space-between",
|
|
1626
|
+
gap: 8,
|
|
1627
|
+
flexWrap: "wrap",
|
|
1628
|
+
margin: "2px 1px -1px"
|
|
1629
|
+
}
|
|
1630
|
+
}, [
|
|
1631
|
+
import_react.default.createElement("span", {
|
|
1632
|
+
key: "t",
|
|
1633
|
+
style: { fontSize: 12.5, fontWeight: 700, color: C.label2, letterSpacing: ".2px" }
|
|
1634
|
+
}, t("sectionRates")),
|
|
1635
|
+
import_react.default.createElement(PeriodSelect, {
|
|
1636
|
+
key: "period",
|
|
1637
|
+
t,
|
|
1638
|
+
value: view,
|
|
1639
|
+
onChange: setView,
|
|
1640
|
+
year: viewYear,
|
|
1641
|
+
months: yearData.months
|
|
1642
|
+
})
|
|
1643
|
+
]),
|
|
1644
|
+
import_react.default.createElement("div", {
|
|
1645
|
+
key: "ratesHint",
|
|
1646
|
+
style: { fontSize: 10.5, color: C.label3, ...NUM }
|
|
1647
|
+
}, viewHint),
|
|
1648
|
+
import_react.default.createElement(RatesGrid, { key: "rates", cards: viewCards }),
|
|
1649
|
+
viewNote ? import_react.default.createElement("div", {
|
|
1650
|
+
key: "ratesNote",
|
|
1651
|
+
style: { fontSize: 10.5, color: C.label3, display: "flex", alignItems: "center", gap: 6, flexWrap: "wrap" }
|
|
1652
|
+
}, [
|
|
1653
|
+
import_react.default.createElement("span", {
|
|
1654
|
+
key: "badge",
|
|
1655
|
+
style: {
|
|
1656
|
+
color: C.label2,
|
|
1657
|
+
fontWeight: 600,
|
|
1658
|
+
background: `color-mix(in srgb, ${GOLD_TINT} 14%, transparent)`,
|
|
1659
|
+
border: `1px solid color-mix(in srgb, ${GOLD_TINT} 32%, transparent)`,
|
|
1660
|
+
borderRadius: 999,
|
|
1661
|
+
padding: "1px 8px"
|
|
1662
|
+
}
|
|
1663
|
+
}, t("maxHolidayBadge")),
|
|
1664
|
+
import_react.default.createElement("span", { key: "txt" }, viewNote)
|
|
1665
|
+
]) : null,
|
|
1666
|
+
import_react.default.createElement(SectionTitle, { key: "detailTitle", title: t("sectionDetail") }),
|
|
1667
|
+
import_react.default.createElement(DetailsFooter, { key: "footer", t, snap, holidaySource }),
|
|
1668
|
+
import_react.default.createElement(HolidaysCard, {
|
|
1669
|
+
key: "holidays",
|
|
1670
|
+
t,
|
|
1671
|
+
holidaySource,
|
|
1672
|
+
extraWorkdays: cfg.extraWorkdays
|
|
1673
|
+
}),
|
|
1674
|
+
import_react.default.createElement(SettingsSection, {
|
|
1675
|
+
key: "settings",
|
|
1676
|
+
t,
|
|
1677
|
+
form,
|
|
1678
|
+
setField,
|
|
1679
|
+
setCheck,
|
|
1680
|
+
save,
|
|
1681
|
+
onSave
|
|
1682
|
+
})
|
|
1683
|
+
])
|
|
1087
1684
|
]);
|
|
1088
1685
|
}
|
|
1089
1686
|
var inject = ["slots", "locale"];
|