@spacesync/client 0.1.11 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +105 -13
  2. package/dist/analog-clock-picker.js +1 -1
  3. package/dist/booking-schedule-calendars.js +78 -9
  4. package/dist/booking-schedule-calendars.js.map +1 -1
  5. package/dist/booking-schedule.js +1 -1
  6. package/dist/booking-space-layout-picker.d.ts +1 -1
  7. package/dist/booking-space-layout-picker.js +2 -2
  8. package/dist/{chunk-OTSA5ROL.js → chunk-5CUJZNUC.js} +16 -5
  9. package/dist/chunk-5CUJZNUC.js.map +1 -0
  10. package/dist/{chunk-5NOKULX2.js → chunk-AHFRK6KP.js} +22 -5
  11. package/dist/chunk-AHFRK6KP.js.map +1 -0
  12. package/dist/{chunk-OV5ODANJ.js → chunk-EQLKSX4E.js} +22 -5
  13. package/dist/chunk-EQLKSX4E.js.map +1 -0
  14. package/dist/{chunk-RC5SWZPB.js → chunk-KXTYPZDK.js} +89 -18
  15. package/dist/chunk-KXTYPZDK.js.map +1 -0
  16. package/dist/chunk-ZVWUPYSV.js +170 -0
  17. package/dist/chunk-ZVWUPYSV.js.map +1 -0
  18. package/dist/{layout-canvas-view-DOjyUNJH.d.ts → layout-canvas-view-C3u96xu4.d.ts} +5 -1
  19. package/dist/layout-canvas-view.d.ts +1 -1
  20. package/dist/layout-canvas-view.js +2 -2
  21. package/dist/lib/dateDisplay.d.ts +16 -1
  22. package/dist/lib/dateDisplay.js +1 -1
  23. package/dist/notification-sidebar-preview.js +1 -1
  24. package/dist/page-list-chrome.d.ts +3 -1
  25. package/dist/page-list-chrome.js +1 -1
  26. package/dist/page-list-loading.js +1 -1
  27. package/dist/support-presence-status.js +1 -1
  28. package/dist/ui.d.ts +11 -1
  29. package/dist/ui.js +59 -15
  30. package/dist/ui.js.map +1 -1
  31. package/package.json +11 -5
  32. package/dist/chunk-5NOKULX2.js.map +0 -1
  33. package/dist/chunk-OTSA5ROL.js.map +0 -1
  34. package/dist/chunk-OV5ODANJ.js.map +0 -1
  35. package/dist/chunk-PBNBLCC2.js +0 -84
  36. package/dist/chunk-PBNBLCC2.js.map +0 -1
  37. package/dist/chunk-RC5SWZPB.js.map +0 -1
package/README.md CHANGED
@@ -2,6 +2,108 @@
2
2
 
3
3
  Reusable React UI, application shell, theme, and API-client primitives for SpaceSync apps (operator, member).
4
4
 
5
+ ## Develop
6
+
7
+ This is a standalone package (not a workspace). From this directory:
8
+
9
+ ```bash
10
+ pnpm install
11
+ pnpm typecheck
12
+ pnpm build
13
+ ```
14
+
15
+ ## Local testing (member / operator)
16
+
17
+ Consumer apps install the **published** package from npm. They do **not** read this repo’s `src/` directly — only the built `dist/` folder.
18
+
19
+ When the three repos sit side-by-side (see layout below), **member** and **operator** automatically alias `@spacesync/client` to `../spacesync-client/dist` and Tailwind scans that folder too. You do **not** need to copy into `node_modules` for local work — just build here.
20
+
21
+ ### One-time setup
22
+
23
+ Keep these repos as siblings on disk:
24
+
25
+ ```text
26
+ Documents/
27
+ spacesync-client/
28
+ spacesync-member/
29
+ spacesync-operator/
30
+ ```
31
+
32
+ Install dependencies in each app once:
33
+
34
+ ```bash
35
+ cd ../spacesync-member && npm install
36
+ cd ../spacesync-operator && npm install
37
+ ```
38
+
39
+ ### Daily workflow (recommended)
40
+
41
+ **Terminal 1 — client (watch + rebuild on save):**
42
+
43
+ ```bash
44
+ cd spacesync-client
45
+ pnpm dev
46
+ ```
47
+
48
+ **Terminal 2 — the app you’re testing:**
49
+
50
+ ```bash
51
+ cd ../spacesync-member && npm run dev # port 5174
52
+ # or
53
+ cd ../spacesync-operator && npm run dev # port 5173
54
+ ```
55
+
56
+ After the first `pnpm dev` in the client, save a file in `spacesync-client/src/` and the app dev server should full-reload within a second or two.
57
+
58
+ ### One-off build (no watch)
59
+
60
+ ```bash
61
+ pnpm build # dist/ only
62
+ pnpm sync:apps # dist/ + copy into both apps' node_modules (CI / no sibling alias)
63
+ pnpm sync:member
64
+ pnpm sync:operator
65
+ ```
66
+
67
+ ### If you still see the old UI
68
+
69
+ Do these in order:
70
+
71
+ **1. Restart the app dev server** (required after changing `vite.config.ts` or `tailwind.css`):
72
+
73
+ ```bash
74
+ # Ctrl+C in the member/operator terminal, then:
75
+ npm run dev
76
+ ```
77
+
78
+ **2. Clear Vite’s cache** (common after Tailwind class changes):
79
+
80
+ ```bash
81
+ rm -rf node_modules/.vite && npm run dev
82
+ ```
83
+
84
+ Run that inside `spacesync-member` or `spacesync-operator`.
85
+
86
+ **3. Hard refresh the browser:** `Cmd+Shift+R`
87
+
88
+ **4. Confirm the client actually built:**
89
+
90
+ ```bash
91
+ cd ../spacesync-client
92
+ pnpm build
93
+ rg "ring-gray-200" dist/ui.js # should print nothing (old styling)
94
+ rg "getComputedStyle" dist/ui.js # should match (new pill code)
95
+ ```
96
+
97
+ ### Why sync alone often wasn’t enough
98
+
99
+ | Layer | What was going wrong |
100
+ |-------|----------------------|
101
+ | JS | Copied `dist/` into `node_modules`, but Vite could still serve cached modules |
102
+ | CSS | Tailwind only rescans class names when its `@source` files change; stale `node_modules/.vite` kept old CSS |
103
+ | Fix | Apps now read `../spacesync-client/dist` directly via Vite alias + Tailwind `@source` |
104
+
105
+ `pnpm sync:apps` is still useful when repos are **not** siblings or before publishing to npm.
106
+
5
107
  ## Publish (this repo)
6
108
 
7
109
  Bump the version in `package.json`, then:
@@ -15,9 +117,9 @@ pnpm publish --access public
15
117
  Version bump shortcuts:
16
118
 
17
119
  ```bash
18
- npm version patch # 0.1.6 → 0.1.7
19
- npm version minor # 0.1.6 → 0.2.0
20
- npm version major # 0.1.6 → 1.0.0
120
+ npm version patch # 0.1.12 → 0.1.13
121
+ npm version minor # 0.1.12 → 0.2.0
122
+ npm version major # 0.1.12 → 1.0.0
21
123
  ```
22
124
 
23
125
  Git pushes do not publish. Only `pnpm publish` (or `npm publish --access public`) does.
@@ -38,13 +140,3 @@ import { AppWebShell } from "@spacesync/client/app-web-shell";
38
140
  ```css
39
141
  @import "@spacesync/client/styles/theme.css";
40
142
  ```
41
-
42
- ## Develop
43
-
44
- This is a standalone package (not a workspace). From this directory:
45
-
46
- ```bash
47
- pnpm install
48
- pnpm typecheck
49
- pnpm build
50
- ```
@@ -1,4 +1,4 @@
1
- import { parseHm, toTimeStr, formatTimeForDisplay } from './chunk-OV5ODANJ.js';
1
+ import { parseHm, toTimeStr, formatTimeForDisplay } from './chunk-EQLKSX4E.js';
2
2
  import './chunk-CXXOKCLJ.js';
3
3
  import './chunk-VIJ2HWPZ.js';
4
4
  import { cn } from './chunk-DMPSQ22Q.js';
@@ -1,6 +1,6 @@
1
- import { currentWeekStart, PRICING_TODAY, weekCountBetween, weekRangeLabel, currentMonthYm, monthCount, monthLabel, addMonthsYm, todayLocalYmd, addDays, diffDays, MIN_WEEK_START, localDateYmd } from './chunk-OTSA5ROL.js';
1
+ import { currentWeekStart, PRICING_TODAY, weekCountBetween, weekRangeLabel, currentMonthYm, monthCount, monthLabel, addMonthsYm, todayLocalYmd, addDays, diffDays, MIN_WEEK_START, localDateYmd } from './chunk-5CUJZNUC.js';
2
2
  import { CheckboxField } from './chunk-2IZODKTR.js';
3
- import { hourlyEndTimes, addMinutesToHm, formatTimeForDisplay, snapScheduleEndTime, TimeField } from './chunk-OV5ODANJ.js';
3
+ import { hourlyEndTimes, addMinutesToHm, formatTimeForDisplay, snapScheduleEndTime, TimeField, currentTimeHm } from './chunk-EQLKSX4E.js';
4
4
  import './chunk-CXXOKCLJ.js';
5
5
  import './chunk-VIJ2HWPZ.js';
6
6
  import './chunk-IBGGQXTH.js';
@@ -65,29 +65,75 @@ function ScheduleInfoBanner({ children }) {
65
65
  /* @__PURE__ */ jsx("span", { className: "text-xs leading-relaxed text-blue-700 dark:text-blue-400", children })
66
66
  ] });
67
67
  }
68
+ function clampNaturalNumber(value, min, max) {
69
+ if (!Number.isFinite(value)) return min;
70
+ return Math.min(max, Math.max(min, Math.floor(value)));
71
+ }
68
72
  function ScheduleStepper({
69
73
  value,
70
74
  min = 1,
71
75
  max = 100,
72
76
  onChange
73
77
  }) {
78
+ const [draft, setDraft] = useState(String(value));
79
+ const [focused, setFocused] = useState(false);
80
+ useEffect(() => {
81
+ if (!focused) {
82
+ setDraft(String(value));
83
+ }
84
+ }, [value, focused]);
85
+ const commitDraft = (raw) => {
86
+ const trimmed = raw.trim();
87
+ if (trimmed === "") {
88
+ onChange(min);
89
+ setDraft(String(min));
90
+ return;
91
+ }
92
+ const parsed = Number.parseInt(trimmed, 10);
93
+ const next = clampNaturalNumber(parsed, min, max);
94
+ onChange(next);
95
+ setDraft(String(next));
96
+ };
74
97
  return /* @__PURE__ */ jsxs("div", { className: "flex w-32 items-center overflow-hidden rounded-xl border border-border bg-card", children: [
75
98
  /* @__PURE__ */ jsx(
76
99
  "button",
77
100
  {
78
101
  type: "button",
79
- onClick: () => onChange(Math.max(min, value - 1)),
102
+ onClick: () => onChange(clampNaturalNumber(value - 1, min, max)),
80
103
  className: "flex h-9 w-9 shrink-0 items-center justify-center border-r border-border text-sm text-muted-foreground hover:bg-accent",
104
+ "aria-label": "Decrease",
81
105
  children: "\u2212"
82
106
  }
83
107
  ),
84
- /* @__PURE__ */ jsx("span", { className: "flex-1 text-center text-sm font-medium text-foreground", children: value }),
108
+ /* @__PURE__ */ jsx(
109
+ "input",
110
+ {
111
+ type: "text",
112
+ inputMode: "numeric",
113
+ pattern: "[0-9]*",
114
+ "aria-label": "Quantity",
115
+ value: draft,
116
+ onChange: (event) => setDraft(event.target.value.replace(/\D/g, "")),
117
+ onFocus: () => setFocused(true),
118
+ onBlur: () => {
119
+ setFocused(false);
120
+ commitDraft(draft);
121
+ },
122
+ onKeyDown: (event) => {
123
+ if (event.key === "Enter") {
124
+ event.currentTarget.blur();
125
+ }
126
+ },
127
+ className: "min-w-0 flex-1 border-0 bg-transparent px-1 text-center text-sm font-medium text-foreground outline-none focus:ring-0"
128
+ }
129
+ ),
85
130
  /* @__PURE__ */ jsx(
86
131
  "button",
87
132
  {
88
133
  type: "button",
89
- onClick: () => onChange(Math.min(max, value + 1)),
134
+ onClick: () => onChange(clampNaturalNumber(value + 1, min, max)),
90
135
  className: "flex h-9 w-9 shrink-0 items-center justify-center border-l border-border text-sm text-muted-foreground hover:bg-accent",
136
+ "aria-label": "Increase",
91
137
  children: "+"
92
138
  }
93
139
  )
@@ -1129,6 +1175,14 @@ function HourlySchedulePicker({
1129
1175
  ] })
1130
1176
  ] });
1131
1177
  }
1178
+ function defaultNeededTime(scheduleDate, preparationMinutes, todayYmd, now = /* @__PURE__ */ new Date()) {
1179
+ let time = currentTimeHm(now);
1180
+ if (preparationMinutes > 0 && scheduleDate === todayYmd) {
1181
+ const floor = minNeededTimeHm(preparationMinutes, now);
1182
+ if (time < floor) time = floor;
1183
+ }
1184
+ return time;
1185
+ }
1132
1186
  function ServiceNeededAtPicker({
1133
1187
  scheduleDate,
1134
1188
  scheduleTime,
@@ -1139,9 +1193,10 @@ function ServiceNeededAtPicker({
1139
1193
  minDate = todayLocalYmd(),
1140
1194
  preparationMinutes = 0
1141
1195
  }) {
1142
- const neededTime = scheduleTime || "09:00";
1143
1196
  const todayYmd = todayLocalYmd();
1144
- const minTimeToday = preparationMinutes > 0 && scheduleDate === todayYmd ? minNeededTimeHm(preparationMinutes) : void 0;
1197
+ const effectiveDate = scheduleDate || todayYmd;
1198
+ const neededTime = scheduleTime || defaultNeededTime(effectiveDate, preparationMinutes, todayYmd);
1199
+ const minTimeToday = preparationMinutes > 0 && effectiveDate === todayYmd ? minNeededTimeHm(preparationMinutes) : void 0;
1145
1200
  const syncNeeded = (date, time) => {
1146
1201
  let nextTime = time;
1147
1202
  if (preparationMinutes > 0 && date === todayYmd) {
@@ -1158,9 +1213,23 @@ function ServiceNeededAtPicker({
1158
1213
  };
1159
1214
  const handleDateChange = (v) => syncNeeded(v, neededTime);
1160
1215
  const handleTimeChange = (v) => {
1161
- if (!scheduleDate) return;
1162
- syncNeeded(scheduleDate, v);
1216
+ if (!effectiveDate) return;
1217
+ syncNeeded(effectiveDate, v);
1163
1218
  };
1219
+ useEffect(() => {
1220
+ const defaultTime = defaultNeededTime(effectiveDate, preparationMinutes, todayYmd);
1221
+ const floor = preparationMinutes > 0 && effectiveDate === todayYmd ? minNeededTimeHm(preparationMinutes) : void 0;
1222
+ let nextTime = scheduleTime || defaultTime;
1223
+ if (floor && nextTime < floor) nextTime = floor;
1224
+ if (scheduleTime === nextTime && scheduleDate === effectiveDate) return;
1225
+ onChange({
1226
+ scheduleDate: effectiveDate,
1227
+ scheduleTime: nextTime,
1228
+ scheduleEndTime: nextTime,
1229
+ startDate: effectiveDate,
1230
+ endDate: effectiveDate
1231
+ });
1232
+ }, [effectiveDate, scheduleTime, preparationMinutes, todayYmd, onChange]);
1164
1233
  const summary = scheduleDate && neededTime ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between rounded-xl border border-border bg-muted px-3 py-2.5", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
1165
1234
  /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", children: "Needed at" }),
1166
1235
  /* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-foreground", children: [