@7365admin1/layer-common 4.97.1-staging.588 → 4.97.1-staging.589

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.
@@ -100,6 +100,7 @@ import {
100
100
  reportFileName,
101
101
  type CsvSection,
102
102
  } from "../../utils/report-export";
103
+ import { reportYear } from "../../utils/nfc-patrol-report";
103
104
 
104
105
  import PatrolReportTab from "./PatrolReport/PatrolReportTab.vue";
105
106
  import DailyReportTab from "./PatrolReport/DailyReportTab.vue";
@@ -137,6 +138,7 @@ function updateFilters(value: NFCPatrolReportFilters) {
137
138
  filters.route = value.route;
138
139
  filters.timeRange = value.timeRange;
139
140
  filters.date = value.date;
141
+ filters.year = value.year;
140
142
  }
141
143
 
142
144
  const { exportReportPdf } = useReportPdf();
@@ -147,10 +149,10 @@ function reportMeta(report: HTMLElement, extension: "pdf" | "csv"): ReportPdfMet
147
149
  const route = options.value.routes.find(({ value }) => value === filters.route)?.title;
148
150
  const text = (selector: string) => report.querySelector(selector)?.textContent?.trim() || undefined;
149
151
 
150
- // The yearly table always covers the current year (useNFCPatrolMonthlyReport).
152
+ // The yearly table covers the year picked on the Monthly Report.
151
153
  const period =
152
154
  activeTab.value === "monthly"
153
- ? { label: "Year", value: String(new Date().getFullYear()) }
155
+ ? { label: "Year", value: reportYear(filters.year) }
154
156
  : { label: "Date", value: formatReportDate(filters.date) };
155
157
  const details = [{ label: "Route", value: route ?? "-" }, period];
156
158
  if (activeTab.value === "patrol" && filters.timeRange) {
@@ -111,7 +111,7 @@
111
111
  {{
112
112
  form.timed
113
113
  ? "Patrols start at the route's start times, within the tolerance."
114
- : "No time window: this route can be patrolled at any time."
114
+ : "No time window: patrol at any time of day, on the selected Days only. Use Select all for every day."
115
115
  }}
116
116
  </div>
117
117
  </v-col>
@@ -5,6 +5,7 @@
5
5
  :options="options"
6
6
  :show-time-filter="false"
7
7
  :show-date-filter="false"
8
+ show-year-filter
8
9
  @update:model-value="$emit('update:filters', $event)"
9
10
  @export="(type) => $emit('export', type, csvSections())"
10
11
  />
@@ -53,7 +54,7 @@ import type {
53
54
  } from "../../../types/nfc-patrol-report";
54
55
 
55
56
  import { useNFCPatrolMonthlyReport } from "../../../composables/useNFCPatrolMonthlyReport";
56
- import { monthlyReportCsv } from "../../../utils/nfc-patrol-report";
57
+ import { monthlyReportCsv, reportYear } from "../../../utils/nfc-patrol-report";
57
58
  import PatrolMonthDialog from "./PatrolMonthDialog.vue";
58
59
  import type { CsvSection } from "../../../utils/report-export";
59
60
 
@@ -84,8 +85,7 @@ const monthTitle = ref("");
84
85
  const monthRows = shallowRef<NFCPatrolReportSummaryRow[]>([]);
85
86
 
86
87
  function openMonth(monthIndex: number) {
87
- // The yearly report always covers the current year (useNFCPatrolMonthlyReport).
88
- monthTitle.value = `${report.value?.rows[monthIndex]?.month ?? ""} ${new Date().getFullYear()}`;
88
+ monthTitle.value = `${report.value?.rows[monthIndex]?.month ?? ""} ${reportYear(props.filters.year)}`;
89
89
  monthRows.value = monthRuns(monthIndex);
90
90
  monthOpen.value = true;
91
91
  }
@@ -95,7 +95,7 @@ function csvSections() {
95
95
  }
96
96
 
97
97
  watch(
98
- () => [props.filters.route, props.active],
98
+ () => [props.filters.route, props.filters.year, props.active],
99
99
  () => {
100
100
  if (props.active) {
101
101
  void fetchReport();
@@ -63,6 +63,25 @@
63
63
  />
64
64
  </v-col>
65
65
 
66
+ <!-- Monthly Report: which year. Each month's row then opens that month. -->
67
+ <v-col
68
+ v-if="showYearFilter"
69
+ cols="12"
70
+ md="2"
71
+ class="px-md-2 mb-2 mb-md-0"
72
+ >
73
+ <v-select
74
+ :model-value="reportYear(modelValue.year)"
75
+ :items="reportYears()"
76
+ label="Year"
77
+ class="filter-field"
78
+ variant="outlined"
79
+ density="comfortable"
80
+ hide-details
81
+ @update:model-value="updateFilter('year', $event)"
82
+ />
83
+ </v-col>
84
+
66
85
  <v-spacer />
67
86
 
68
87
  <v-col cols="12" md="2" class="pl-md-2">
@@ -99,6 +118,7 @@ import type {
99
118
  NFCPatrolReportFilters,
100
119
  } from "../../../types/nfc-patrol-report";
101
120
  import ReportDatePicker from "./ReportDatePicker.vue";
121
+ import { reportYear, reportYears } from "../../../utils/nfc-patrol-report";
102
122
 
103
123
  const props = withDefaults(
104
124
  defineProps<{
@@ -106,12 +126,14 @@ const props = withDefaults(
106
126
  options: NFCPatrolReportFilterOptions;
107
127
  showTimeFilter?: boolean;
108
128
  showDateFilter?: boolean;
129
+ showYearFilter?: boolean;
109
130
  }>(),
110
131
  {
111
132
  showTimeFilter: true,
112
133
  showDateFilter: true,
134
+ showYearFilter: false,
113
135
  },
114
- );;
136
+ );
115
137
 
116
138
  const emit = defineEmits<{
117
139
  "update:modelValue": [value: NFCPatrolReportFilters];
@@ -11,6 +11,7 @@ import {
11
11
  mapLogsToMonthlyRows,
12
12
  mapLogsToMonthRuns,
13
13
  monthIndexOf,
14
+ reportYear,
14
15
  } from "../utils/nfc-patrol-report";
15
16
 
16
17
  interface NFCPatrolLogListResponse {
@@ -53,7 +54,7 @@ export function useNFCPatrolMonthlyReport(
53
54
  */
54
55
  const failed = ref(false);
55
56
 
56
- // The year's patrol logs, kept so a month's row can open its list without
57
+ // The picked year's patrol logs, kept so a month's row can open its list without
57
58
  // asking the server again. Not deep-reactive: nothing edits them.
58
59
  const logs = shallowRef<Record<string, any>[]>([]);
59
60
  let siteTimezone: string | undefined;
@@ -70,7 +71,7 @@ export function useNFCPatrolMonthlyReport(
70
71
  loading.value = true;
71
72
  notFound.value = false;
72
73
  failed.value = false;
73
- const currentYear = new Date().getFullYear().toString();
74
+ const year = reportYear(filters.year);
74
75
  try {
75
76
  // The whole year, not page one of it. `collectAllPages` reads the page
76
77
  // count out of the first reply and walks the rest.
@@ -81,7 +82,8 @@ export function useNFCPatrolMonthlyReport(
81
82
  limit: 100,
82
83
  site,
83
84
  routeId: filters.route,
84
- date: currentYear,
85
+ // "YYYY" + type "month": the server returns that whole year.
86
+ date: year,
85
87
  type: "month",
86
88
  }),
87
89
  ),
@@ -21,6 +21,7 @@ export function useNFCPatrolReportFilters(site: string) {
21
21
  route: "",
22
22
  timeRange: "",
23
23
  date: getTodayDate(),
24
+ year: String(new Date().getFullYear()),
24
25
  });
25
26
 
26
27
  const options = ref<NFCPatrolReportFilterOptions>({
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@7365admin1/layer-common",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "4.97.1-staging.588",
5
+ "version": "4.97.1-staging.589",
6
6
  "author": "7365admin1",
7
7
  "main": "./nuxt.config.ts",
8
8
  "//files": "What a consumer extending this layer actually loads. Without this npm ships the whole working tree - the changesets, the CI workflows, the render harness in tools/ and any scratch directory that happened to exist at publish time. Nuxt resolves a layer by directory, so every runtime directory below has to stay listed; adding a new top-level runtime directory means adding it here too.",
@@ -6,6 +6,8 @@ export interface NFCPatrolReportFilters {
6
6
  route: string;
7
7
  timeRange: string;
8
8
  date: string;
9
+ /** The Monthly Report's year, "YYYY". Absent = the current year. */
10
+ year?: string;
9
11
  }
10
12
 
11
13
  export interface NFCPatrolReportExportRequest {
@@ -102,6 +102,21 @@ export function isMissedCheckpoint(
102
102
  return Number.isFinite(end) && now.getTime() > end;
103
103
  }
104
104
 
105
+ /** The Monthly Report's year: the one picked, or the current year. */
106
+ export function reportYear(year?: string, now = new Date()) {
107
+ return /^\d{4}$/.test(year ?? "") ? (year as string) : String(now.getFullYear());
108
+ }
109
+
110
+ /**
111
+ * The years the Monthly Report offers, newest first: this year and the five
112
+ * before it.
113
+ */
114
+ // ponytail: fixed 6-year window; derive from the oldest log if sites need more.
115
+ export function reportYears(now = new Date(), count = 6) {
116
+ const current = now.getFullYear();
117
+ return Array.from({ length: count }, (_, i) => String(current - i));
118
+ }
119
+
105
120
  /** The month (0-11) a patrol log counts under; NaN for a date that is not one. */
106
121
  export function monthIndexOf(log: Record<string, any>) {
107
122
  return new Date(log?.date).getMonth();