@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.
- package/README.md +105 -13
- package/dist/analog-clock-picker.js +1 -1
- package/dist/booking-schedule-calendars.js +78 -9
- package/dist/booking-schedule-calendars.js.map +1 -1
- package/dist/booking-schedule.js +1 -1
- package/dist/booking-space-layout-picker.d.ts +1 -1
- package/dist/booking-space-layout-picker.js +2 -2
- package/dist/{chunk-OTSA5ROL.js → chunk-5CUJZNUC.js} +16 -5
- package/dist/chunk-5CUJZNUC.js.map +1 -0
- package/dist/{chunk-5NOKULX2.js → chunk-AHFRK6KP.js} +22 -5
- package/dist/chunk-AHFRK6KP.js.map +1 -0
- package/dist/{chunk-OV5ODANJ.js → chunk-EQLKSX4E.js} +22 -5
- package/dist/chunk-EQLKSX4E.js.map +1 -0
- package/dist/{chunk-RC5SWZPB.js → chunk-KXTYPZDK.js} +89 -18
- package/dist/chunk-KXTYPZDK.js.map +1 -0
- package/dist/chunk-ZVWUPYSV.js +170 -0
- package/dist/chunk-ZVWUPYSV.js.map +1 -0
- package/dist/{layout-canvas-view-DOjyUNJH.d.ts → layout-canvas-view-C3u96xu4.d.ts} +5 -1
- package/dist/layout-canvas-view.d.ts +1 -1
- package/dist/layout-canvas-view.js +2 -2
- package/dist/lib/dateDisplay.d.ts +16 -1
- package/dist/lib/dateDisplay.js +1 -1
- package/dist/notification-sidebar-preview.js +1 -1
- package/dist/page-list-chrome.d.ts +3 -1
- package/dist/page-list-chrome.js +1 -1
- package/dist/page-list-loading.js +1 -1
- package/dist/support-presence-status.js +1 -1
- package/dist/ui.d.ts +11 -1
- package/dist/ui.js +59 -15
- package/dist/ui.js.map +1 -1
- package/package.json +11 -5
- package/dist/chunk-5NOKULX2.js.map +0 -1
- package/dist/chunk-OTSA5ROL.js.map +0 -1
- package/dist/chunk-OV5ODANJ.js.map +0 -1
- package/dist/chunk-PBNBLCC2.js +0 -84
- package/dist/chunk-PBNBLCC2.js.map +0 -1
- 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.
|
|
19
|
-
npm version minor # 0.1.
|
|
20
|
-
npm version major # 0.1.
|
|
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,6 +1,6 @@
|
|
|
1
|
-
import { currentWeekStart, PRICING_TODAY, weekCountBetween, weekRangeLabel, currentMonthYm, monthCount, monthLabel, addMonthsYm, todayLocalYmd, addDays, diffDays, MIN_WEEK_START, localDateYmd } from './chunk-
|
|
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-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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 (!
|
|
1162
|
-
syncNeeded(
|
|
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: [
|