dsh-niuma 0.1.0 → 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/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 DEFAULT_HOLIDAYS = Object.freeze([
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 DEFAULT_EXTRA_WORKDAYS = Object.freeze([
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
- "holidaysField": "\u8282\u5047\u65E5\uFF08\u4E00\u884C\u4E00\u4E2A YYYY-MM-DD\uFF09",
441
- "extraWorkdaysField": "\u8C03\u4F11\u4E0A\u73ED\u65E5\uFF08\u4E00\u884C\u4E00\u4E2A YYYY-MM-DD\uFF09",
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
- "holidaysField": "Holidays (one YYYY-MM-DD per line)",
492
- "extraWorkdaysField": "Make-up workdays (one YYYY-MM-DD per line)",
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) => `\xA5${formatMoney(value, digits)}`;
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: "3px 10px",
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: C.layer1,
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
- return import_react.default.createElement("div", { style: { ...CARD, padding: "13px 15px 12px" } }, [
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: "head",
896
+ key: "glow",
897
+ "aria-hidden": "true",
635
898
  style: {
636
- display: "flex",
637
- alignItems: "center",
638
- justifyContent: "space-between",
639
- gap: 8,
640
- flexWrap: "wrap",
641
- fontSize: 12,
642
- color: C.label2
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", { key: "a" }, t("earnedToday")),
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: 6, marginTop: 1, flexWrap: "wrap" }
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: 40,
664
- lineHeight: 1.15,
665
- fontWeight: 760,
666
- letterSpacing: "-.5px",
667
- color: C.ok,
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: "sub",
674
- style: { fontSize: 11, color: C.label3, marginTop: 2, ...NUM }
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: "p",
701
- style: { fontSize: 13, fontWeight: 650, color: C.brand, ...NUM }
702
- }, percentText)
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 .4s linear"
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: "nums",
727
- style: { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 8, marginTop: 5, flexWrap: "wrap", fontSize: 11, color: C.label3 }
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 t = props.t;
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
- }, RATE_CARDS.map((spec, index) => import_react.default.createElement("div", {
759
- key: spec.key,
760
- "data-stat": `stat-${index}`,
761
- style: { ...CARD, padding: "9px 10px" }
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("div", { key: "l", style: { fontSize: 11, color: C.label3 } }, t(spec.label)),
764
- import_react.default.createElement("div", {
765
- key: "v",
766
- style: { fontSize: 15, fontWeight: 650, color: C.label, marginTop: 2, ...NUM }
767
- }, money(rates[spec.key], spec.digits))
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: { ...CARD, padding: "9px 12px", fontSize: 11, color: C.label3, lineHeight: 1.7 }
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: "flex", flexWrap: "wrap", gap: "2px 14px" }
788
- }, items.map((item) => import_react.default.createElement("span", {
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: { display: "inline-flex", alignItems: "baseline", gap: 4, whiteSpace: "nowrap" }
1152
+ style: { minWidth: 0 }
791
1153
  }, [
792
- import_react.default.createElement("span", { key: "l" }, `${item.label}`),
793
- import_react.default.createElement("span", {
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: { color: C.label2, fontWeight: 600, ...NUM }
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: "src",
800
- style: { display: "flex", flexWrap: "wrap", alignItems: "baseline", gap: 4, marginTop: 2 }
1274
+ key: "title",
1275
+ style: { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 8, flexWrap: "wrap" }
801
1276
  }, [
802
- import_react.default.createElement("span", { key: "l" }, `${t("holidaySource")}\uFF1A`),
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: "underline" }
810
- }, `${source.year} \xB7 ${t("holidaySourceLink")} \u2197`),
811
- import_react.default.createElement("span", { key: "n", style: { color: C.label3 } }, `\uFF08${source.title}\uFF09`)
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: "5px 8px",
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: 11, color: C.label3 };
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: 3, flex: "1 1 130px", minWidth: 112 }
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
- const textareaField = (field, labelKey) => import_react.default.createElement("label", {
871
- key: field,
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: 600, color: C.label, userSelect: "none" }
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: 11, color: C.label3, margin: "4px 0 9px" }
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: 8 }
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: 10 }
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
- border: `1px solid ${C.brand}`,
928
- color: C.brand,
929
- background: "transparent",
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: "6px 16px",
1395
+ padding: "7px 20px",
932
1396
  fontSize: 13,
933
- fontWeight: 600,
934
- cursor: save.status === "saving" ? "default" : "pointer"
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
- display: "flex",
1015
- flexDirection: "column",
1016
- gap: 9,
1017
- minWidth: 0,
1018
- padding: "2px 1px 18px",
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: "head",
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
- alignItems: "center",
1048
- gap: 8,
1049
- flexWrap: "wrap",
1050
- fontSize: 12,
1051
- color: banner.color,
1052
- padding: "5px 9px",
1053
- borderRadius: 8,
1054
- border: `1px solid ${banner.color}`,
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
- import_react.default.createElement("span", { key: "txt", style: { flex: "1 1 160px" } }, banner.text),
1059
- banner.retry ? import_react.default.createElement("button", {
1060
- key: "retry",
1061
- type: "button",
1062
- onClick: remote.refresh,
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
- border: `1px solid ${banner.color}`,
1065
- color: banner.color,
1066
- background: "transparent",
1067
- borderRadius: 6,
1068
- padding: "2px 10px",
1590
+ display: "flex",
1591
+ alignItems: "center",
1592
+ gap: 8,
1593
+ flexWrap: "wrap",
1069
1594
  fontSize: 12,
1070
- cursor: "pointer"
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
- }, t("retry")) : null
1073
- ]) : null,
1074
- import_react.default.createElement(HeroCard, { key: "hero", t, snap }),
1075
- import_react.default.createElement(ProgressCard, { key: "progress", t, snap }),
1076
- import_react.default.createElement(RatesGrid, { key: "rates", t, rates }),
1077
- import_react.default.createElement(DetailsFooter, { key: "footer", t, snap, holidaySource }),
1078
- import_react.default.createElement(SettingsSection, {
1079
- key: "settings",
1080
- t,
1081
- form,
1082
- setField,
1083
- setCheck,
1084
- save,
1085
- onSave
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"];