@wix/himalaya-cli 0.941.0 → 0.943.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/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/bookings-v2/src/index.d.ts +10 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/wix-data/src/index.d.ts +50 -3
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-data.mjs +16 -2
- package/dist/cli.mjs +6 -1
- package/guides/sdk-reference.json +45 -21
- package/package.json +2 -2
- package/templates/beauty-salon/tier5-src/wix-spaces-collections/index.ts +60 -12
- package/templates/branded-lite/dev/index.ts +224 -14
- package/templates/branded-lite/tier5-src/himi-branded-lite-badges/index.ts +11 -8
- package/templates/branded-lite/tier5-src/himi-branded-lite-coupons/index.ts +17 -6
- package/templates/branded-lite/tier5-src/himi-branded-lite-donations/index.ts +11 -8
- package/templates/branded-lite/tier5-src/himi-branded-lite-forms/index.ts +11 -10
- package/templates/branded-lite/tier5-src/himi-branded-lite-galleries/index.ts +12 -9
- package/templates/branded-lite/tier5-src/himi-branded-lite-giftcards/index.ts +11 -8
- package/templates/branded-lite/tier5-src/himi-branded-lite-inbox/index.ts +11 -8
- package/templates/branded-lite/tier5-src/himi-branded-lite-invoices/index.ts +11 -8
- package/templates/branded-lite/tier5-src/himi-branded-lite-loyalty/index.ts +11 -8
- package/templates/branded-lite/tier5-src/himi-branded-lite-notifications/index.ts +13 -8
- package/templates/branded-lite/tier5-src/himi-branded-lite-notifsettings/index.ts +11 -8
- package/templates/branded-lite/tier5-src/himi-branded-lite-reservations/index.ts +11 -8
- package/templates/branded-lite/tier5-src/himi-branded-lite-reviews/index.ts +11 -8
- package/templates/branded-lite/tier5-src/himi-branded-lite-videos/index.ts +12 -9
- package/templates/branded-lite/tier5-src/wix-spaces-collections/index.ts +60 -12
- package/templates/gym-fitness/tier5-src/wix-spaces-collections/index.ts +60 -12
- package/templates/portfolio-creative/tier5-src/wix-spaces-collections/index.ts +60 -12
- package/templates/retail-store/tier5-src/wix-spaces-collections/index.ts +60 -12
|
@@ -13,7 +13,11 @@ interface Row { id: string; title: string; subtitle: string }
|
|
|
13
13
|
interface S {
|
|
14
14
|
status: "loading" | "ready";
|
|
15
15
|
conversations: Row[]; emptyText: string;
|
|
16
|
-
isMock: boolean; notMock: boolean;
|
|
16
|
+
isMock: boolean; notMock: boolean; /** #2849 — hiddenWhen() hides on TRUE, so these are inverted: the empty-state banner
|
|
17
|
+
* shows only when the site really has none, and the unavailable one only when the
|
|
18
|
+
* query failed. A failed query must never render the sentence that claims the
|
|
19
|
+
* customer has no content. */
|
|
20
|
+
notMockEmpty: boolean; notMockUnavailable: boolean;
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
// businessDisplayData / participantDisplayData are open maps; pull a human name from
|
|
@@ -33,28 +37,27 @@ function norm(c: WixConversation, i: number): Row {
|
|
|
33
37
|
return { id: c.id || `conv-${i}`, title, subtitle: preview };
|
|
34
38
|
}
|
|
35
39
|
|
|
36
|
-
const MOCK_BANNER = "Sample content — this site has no messages yet.";
|
|
37
40
|
const MOCK: Row[] = [
|
|
38
41
|
{ id: "sample-orders", title: "Orders", subtitle: "Your order is on its way — track it here." },
|
|
39
42
|
{ id: "sample-support", title: "Support", subtitle: "Hi! Let us know how we can help." },
|
|
40
43
|
{ id: "sample-team", title: "The Team", subtitle: "Welcome 👋 Thanks for joining us!" },
|
|
41
44
|
];
|
|
42
45
|
|
|
43
|
-
function mockState(s: S): S {
|
|
44
|
-
return { ...s, status: "ready", conversations: MOCK.map((r) => ({ ...r })), emptyText: "", isMock: true, notMock: false,
|
|
46
|
+
function mockState(s: S, reason: "empty" | "unavailable"): S {
|
|
47
|
+
return { ...s, status: "ready", conversations: MOCK.map((r) => ({ ...r })), emptyText: "", isMock: true, notMock: false, notMockEmpty: reason !== "empty", notMockUnavailable: reason !== "unavailable"};
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
const svc = defineService<S, { load: (s: S) => Promise<S> }>("himiBrandedInbox", {
|
|
48
|
-
state: { status: "loading", conversations: [], emptyText: "", isMock: false, notMock: true,
|
|
51
|
+
state: { status: "loading", conversations: [], emptyText: "", isMock: false, notMock: true, notMockEmpty: true, notMockUnavailable: true},
|
|
49
52
|
actions: {
|
|
50
53
|
load: async (s) => {
|
|
51
54
|
try {
|
|
52
55
|
const r = await listConversations({ paging: { limit: 50 } });
|
|
53
56
|
const conversations = (r.conversations ?? []).map(norm);
|
|
54
|
-
if (conversations.length) return { ...s, status: "ready", conversations, emptyText: "", isMock: false, notMock: true,
|
|
55
|
-
return mockState(s);
|
|
57
|
+
if (conversations.length) return { ...s, status: "ready", conversations, emptyText: "", isMock: false, notMock: true, notMockEmpty: true, notMockUnavailable: true};
|
|
58
|
+
return mockState(s, "empty");
|
|
56
59
|
} catch {
|
|
57
|
-
return mockState(s);
|
|
60
|
+
return mockState(s, "unavailable");
|
|
58
61
|
}
|
|
59
62
|
},
|
|
60
63
|
},
|
|
@@ -11,7 +11,11 @@ interface Row { id: string; title: string; subtitle: string }
|
|
|
11
11
|
interface S {
|
|
12
12
|
status: "loading" | "ready";
|
|
13
13
|
invoices: Row[]; emptyText: string;
|
|
14
|
-
isMock: boolean; notMock: boolean;
|
|
14
|
+
isMock: boolean; notMock: boolean; /** #2849 — hiddenWhen() hides on TRUE, so these are inverted: the empty-state banner
|
|
15
|
+
* shows only when the site really has none, and the unavailable one only when the
|
|
16
|
+
* query failed. A failed query must never render the sentence that claims the
|
|
17
|
+
* customer has no content. */
|
|
18
|
+
notMockEmpty: boolean; notMockUnavailable: boolean;
|
|
15
19
|
}
|
|
16
20
|
|
|
17
21
|
const STATUS_LABEL: Record<string, string> = {
|
|
@@ -35,28 +39,27 @@ function norm(v: WixInvoice): Row {
|
|
|
35
39
|
return { id: v.id || "invoice", title, subtitle };
|
|
36
40
|
}
|
|
37
41
|
|
|
38
|
-
const MOCK_BANNER = "Sample content — you have no invoices yet.";
|
|
39
42
|
const MOCK: Row[] = [
|
|
40
43
|
{ id: "sample-1024", title: "Invoice #1024", subtitle: "Paid · $120.00" },
|
|
41
44
|
{ id: "sample-1031", title: "Invoice #1031", subtitle: "Sent · $80.00" },
|
|
42
45
|
{ id: "sample-1042", title: "Invoice #1042", subtitle: "Overdue · $45.00" },
|
|
43
46
|
];
|
|
44
47
|
|
|
45
|
-
function mockState(s: S): S {
|
|
46
|
-
return { ...s, status: "ready", invoices: MOCK.map((r) => ({ ...r })), emptyText: "", isMock: true, notMock: false,
|
|
48
|
+
function mockState(s: S, reason: "empty" | "unavailable"): S {
|
|
49
|
+
return { ...s, status: "ready", invoices: MOCK.map((r) => ({ ...r })), emptyText: "", isMock: true, notMock: false, notMockEmpty: reason !== "empty", notMockUnavailable: reason !== "unavailable"};
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
const svc = defineService<S, { load: (s: S) => Promise<S> }>("himiBrandedInvoices", {
|
|
50
|
-
state: { status: "loading", invoices: [], emptyText: "", isMock: false, notMock: true,
|
|
53
|
+
state: { status: "loading", invoices: [], emptyText: "", isMock: false, notMock: true, notMockEmpty: true, notMockUnavailable: true},
|
|
51
54
|
actions: {
|
|
52
55
|
load: async (s) => {
|
|
53
56
|
try {
|
|
54
57
|
const r = await queryInvoices({ query: { paging: { limit: 20 } } });
|
|
55
58
|
const invoices = (r.invoices ?? []).filter((v) => v.archived !== true).map(norm);
|
|
56
|
-
if (invoices.length) return { ...s, status: "ready", invoices, emptyText: "", isMock: false, notMock: true,
|
|
57
|
-
return mockState(s);
|
|
59
|
+
if (invoices.length) return { ...s, status: "ready", invoices, emptyText: "", isMock: false, notMock: true, notMockEmpty: true, notMockUnavailable: true};
|
|
60
|
+
return mockState(s, "empty");
|
|
58
61
|
} catch {
|
|
59
|
-
return mockState(s);
|
|
62
|
+
return mockState(s, "unavailable");
|
|
60
63
|
}
|
|
61
64
|
},
|
|
62
65
|
},
|
|
@@ -13,7 +13,11 @@ interface S {
|
|
|
13
13
|
status: "loading" | "ready";
|
|
14
14
|
programName: string; unit: string;
|
|
15
15
|
rewards: Row[]; emptyText: string;
|
|
16
|
-
isMock: boolean; notMock: boolean;
|
|
16
|
+
isMock: boolean; notMock: boolean; /** #2849 — hiddenWhen() hides on TRUE, so these are inverted: the empty-state banner
|
|
17
|
+
* shows only when the site really has none, and the unavailable one only when the
|
|
18
|
+
* query failed. A failed query must never render the sentence that claims the
|
|
19
|
+
* customer has no content. */
|
|
20
|
+
notMockEmpty: boolean; notMockUnavailable: boolean;
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
/** "500 points" / "Free" from a reward's requiredPoints. */
|
|
@@ -28,7 +32,6 @@ function norm(r: WixLoyaltyReward, unit: string): Row {
|
|
|
28
32
|
return { id: r.id || r.name || "reward", name: r.name || "Reward", subtitle: pointsLabel(r, unit) };
|
|
29
33
|
}
|
|
30
34
|
|
|
31
|
-
const MOCK_BANNER = "Sample content — this business hasn't set up a rewards program yet.";
|
|
32
35
|
const MOCK: Row[] = [
|
|
33
36
|
{ id: "sample-5off", name: "$5 off your next order", subtitle: "500 points" },
|
|
34
37
|
{ id: "sample-ship", name: "Free shipping", subtitle: "300 points" },
|
|
@@ -36,12 +39,12 @@ const MOCK: Row[] = [
|
|
|
36
39
|
{ id: "sample-gift", name: "Free welcome gift", subtitle: "1,000 points" },
|
|
37
40
|
];
|
|
38
41
|
|
|
39
|
-
function mockState(s: S): S {
|
|
40
|
-
return { ...s, status: "ready", programName: "Rewards", unit: "points", rewards: MOCK.map((r) => ({ ...r })), emptyText: "", isMock: true, notMock: false,
|
|
42
|
+
function mockState(s: S, reason: "empty" | "unavailable"): S {
|
|
43
|
+
return { ...s, status: "ready", programName: "Rewards", unit: "points", rewards: MOCK.map((r) => ({ ...r })), emptyText: "", isMock: true, notMock: false, notMockEmpty: reason !== "empty", notMockUnavailable: reason !== "unavailable"};
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
const svc = defineService<S, { load: (s: S) => Promise<S> }>("himiBrandedLoyalty", {
|
|
44
|
-
state: { status: "loading", programName: "Rewards", unit: "points", rewards: [], emptyText: "", isMock: false, notMock: true,
|
|
47
|
+
state: { status: "loading", programName: "Rewards", unit: "points", rewards: [], emptyText: "", isMock: false, notMock: true, notMockEmpty: true, notMockUnavailable: true},
|
|
45
48
|
actions: {
|
|
46
49
|
load: async (s) => {
|
|
47
50
|
try {
|
|
@@ -51,10 +54,10 @@ const svc = defineService<S, { load: (s: S) => Promise<S> }>("himiBrandedLoyalty
|
|
|
51
54
|
const programName = (p?.name || "Rewards").trim() || "Rewards";
|
|
52
55
|
const r = await listLoyaltyRewards();
|
|
53
56
|
const rewards = (r.rewards ?? []).filter((x) => x.active !== false).map((x) => norm(x, unit));
|
|
54
|
-
if (rewards.length) return { ...s, status: "ready", programName, unit, rewards, emptyText: "", isMock: false, notMock: true,
|
|
55
|
-
return mockState(s);
|
|
57
|
+
if (rewards.length) return { ...s, status: "ready", programName, unit, rewards, emptyText: "", isMock: false, notMock: true, notMockEmpty: true, notMockUnavailable: true};
|
|
58
|
+
return mockState(s, "empty");
|
|
56
59
|
} catch {
|
|
57
|
-
return mockState(s);
|
|
60
|
+
return mockState(s, "unavailable");
|
|
58
61
|
}
|
|
59
62
|
},
|
|
60
63
|
},
|
|
@@ -25,7 +25,12 @@ interface Row {
|
|
|
25
25
|
interface S {
|
|
26
26
|
status: "loading" | "ready";
|
|
27
27
|
items: Row[]; emptyText: string;
|
|
28
|
-
isMock: boolean; notMock: boolean;
|
|
28
|
+
isMock: boolean; notMock: boolean;
|
|
29
|
+
/** #2849 — hiddenWhen() hides on TRUE, so these are inverted: the empty-state banner
|
|
30
|
+
* shows only when the site really has none, and the unavailable one only when the
|
|
31
|
+
* query failed. A failed query must never render the sentence that claims the
|
|
32
|
+
* customer has no content. */
|
|
33
|
+
notMockEmpty: boolean; notMockUnavailable: boolean;
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
/** Human "2h ago" / "Yesterday" from an ISO date. */
|
|
@@ -79,7 +84,6 @@ function norm(n: MfvNotification): Row {
|
|
|
79
84
|
};
|
|
80
85
|
}
|
|
81
86
|
|
|
82
|
-
const MOCK_BANNER = "Sample content — you have no notifications yet.";
|
|
83
87
|
const MOCK: Row[] = [
|
|
84
88
|
{ id: "sample-like", name: "Maya Levin liked your post", subtitle: "“Weekend pop-up” · 2h ago", unread: true, unreadDot: "●", photo: "", initial: "M", dest: "" },
|
|
85
89
|
{ id: "sample-comment", name: "Daniel Cohen commented on your post", subtitle: "“Love this — count me in!” · 5h ago", unread: true, unreadDot: "●", photo: "", initial: "D", dest: "" },
|
|
@@ -88,8 +92,9 @@ const MOCK: Row[] = [
|
|
|
88
92
|
{ id: "sample-offer", name: "New offer just for you", subtitle: "15% off your next order · 3d ago", unread: false, unreadDot: "", photo: "", initial: "%", dest: "" },
|
|
89
93
|
];
|
|
90
94
|
|
|
91
|
-
function mockState(s: S): S {
|
|
92
|
-
return { ...s, status: "ready", items: MOCK.map((r) => ({ ...r })), emptyText: "", isMock: true, notMock: false,
|
|
95
|
+
function mockState(s: S, reason: "empty" | "unavailable"): S {
|
|
96
|
+
return { ...s, status: "ready", items: MOCK.map((r) => ({ ...r })), emptyText: "", isMock: true, notMock: false,
|
|
97
|
+
notMockEmpty: reason !== "empty", notMockUnavailable: reason !== "unavailable" };
|
|
93
98
|
}
|
|
94
99
|
|
|
95
100
|
const svc = defineService<S, {
|
|
@@ -97,15 +102,15 @@ const svc = defineService<S, {
|
|
|
97
102
|
markOneRead: (s: S, id: string) => Promise<S>;
|
|
98
103
|
markAll: (s: S) => Promise<S>;
|
|
99
104
|
}>("himiBrandedNotifications", {
|
|
100
|
-
state: { status: "loading", items: [], emptyText: "", isMock: false, notMock: true,
|
|
105
|
+
state: { status: "loading", items: [], emptyText: "", isMock: false, notMock: true, notMockEmpty: true, notMockUnavailable: true },
|
|
101
106
|
actions: {
|
|
102
107
|
load: async (s) => {
|
|
103
108
|
try {
|
|
104
109
|
const items = (await queryNotifications(25)).map(norm);
|
|
105
|
-
if (items.length) return { ...s, status: "ready", items, emptyText: "", isMock: false, notMock: true,
|
|
106
|
-
return mockState(s);
|
|
110
|
+
if (items.length) return { ...s, status: "ready", items, emptyText: "", isMock: false, notMock: true, notMockEmpty: true, notMockUnavailable: true };
|
|
111
|
+
return mockState(s, "empty");
|
|
107
112
|
} catch {
|
|
108
|
-
return mockState(s);
|
|
113
|
+
return mockState(s, "unavailable");
|
|
109
114
|
}
|
|
110
115
|
},
|
|
111
116
|
// Mark one read; optimistically clear its dot. Real write is best-effort and
|
|
@@ -21,7 +21,11 @@ interface Row { id: string; name: string; subtitle: string; on: boolean }
|
|
|
21
21
|
interface S {
|
|
22
22
|
status: "loading" | "ready";
|
|
23
23
|
topics: Row[]; emptyText: string;
|
|
24
|
-
isMock: boolean; notMock: boolean;
|
|
24
|
+
isMock: boolean; notMock: boolean; /** #2849 — hiddenWhen() hides on TRUE, so these are inverted: the empty-state banner
|
|
25
|
+
* shows only when the site really has none, and the unavailable one only when the
|
|
26
|
+
* query failed. A failed query must never render the sentence that claims the
|
|
27
|
+
* customer has no content. */
|
|
28
|
+
notMockEmpty: boolean; notMockUnavailable: boolean;
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
/** A topic delivers unless an explicit MUTED preference silences it. The status
|
|
@@ -45,7 +49,6 @@ function norm(t: NotificationTopic, prefs: PreferenceInfo[]): Row {
|
|
|
45
49
|
return row(t.id || name, name, isOn(t.id, prefs));
|
|
46
50
|
}
|
|
47
51
|
|
|
48
|
-
const MOCK_BANNER = "Sample content — notification settings will appear here once configured.";
|
|
49
52
|
const MOCK: Row[] = [
|
|
50
53
|
row("sample-orders", "Order updates", true),
|
|
51
54
|
row("sample-bookings", "Bookings & reminders", true),
|
|
@@ -53,15 +56,15 @@ const MOCK: Row[] = [
|
|
|
53
56
|
row("sample-community", "Community activity", true),
|
|
54
57
|
];
|
|
55
58
|
|
|
56
|
-
function mockState(s: S): S {
|
|
57
|
-
return { ...s, status: "ready", topics: MOCK.map((r) => ({ ...r })), emptyText: "", isMock: true, notMock: false,
|
|
59
|
+
function mockState(s: S, reason: "empty" | "unavailable"): S {
|
|
60
|
+
return { ...s, status: "ready", topics: MOCK.map((r) => ({ ...r })), emptyText: "", isMock: true, notMock: false, notMockEmpty: reason !== "empty", notMockUnavailable: reason !== "unavailable"};
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
const svc = defineService<S, {
|
|
61
64
|
load: (s: S) => Promise<S>;
|
|
62
65
|
toggle: (s: S, p?: { id?: string }) => Promise<S>;
|
|
63
66
|
}>("himiBrandedNotifSettings", {
|
|
64
|
-
state: { status: "loading", topics: [], emptyText: "", isMock: false, notMock: true,
|
|
67
|
+
state: { status: "loading", topics: [], emptyText: "", isMock: false, notMock: true, notMockEmpty: true, notMockUnavailable: true},
|
|
65
68
|
actions: {
|
|
66
69
|
load: async (s) => {
|
|
67
70
|
try {
|
|
@@ -71,10 +74,10 @@ const svc = defineService<S, {
|
|
|
71
74
|
]);
|
|
72
75
|
const prefs = prefsRes.preferences?.preferencesInfo ?? [];
|
|
73
76
|
const topics = (topicsRes.topics ?? []).map((t) => norm(t, prefs));
|
|
74
|
-
if (topics.length) return { ...s, status: "ready", topics, emptyText: "", isMock: false, notMock: true,
|
|
75
|
-
return mockState(s);
|
|
77
|
+
if (topics.length) return { ...s, status: "ready", topics, emptyText: "", isMock: false, notMock: true, notMockEmpty: true, notMockUnavailable: true};
|
|
78
|
+
return mockState(s, "empty");
|
|
76
79
|
} catch {
|
|
77
|
-
return mockState(s);
|
|
80
|
+
return mockState(s, "unavailable");
|
|
78
81
|
}
|
|
79
82
|
},
|
|
80
83
|
// Flip one topic's On/Off. Optimistic (updates the row immediately); the real
|
|
@@ -24,7 +24,11 @@ interface S {
|
|
|
24
24
|
/** The Agenda wire shape, derived from `reservations` on every transition. */
|
|
25
25
|
events: CalendarEvent[];
|
|
26
26
|
emptyText: string;
|
|
27
|
-
isMock: boolean; notMock: boolean;
|
|
27
|
+
isMock: boolean; notMock: boolean; /** #2849 — hiddenWhen() hides on TRUE, so these are inverted: the empty-state banner
|
|
28
|
+
* shows only when the site really has none, and the unavailable one only when the
|
|
29
|
+
* query failed. A failed query must never render the sentence that claims the
|
|
30
|
+
* customer has no content. */
|
|
31
|
+
notMockEmpty: boolean; notMockUnavailable: boolean;
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
const STATUS_LABEL: Record<string, string> = {
|
|
@@ -93,7 +97,6 @@ function withEvents(s: S, reservations: Row[]): S {
|
|
|
93
97
|
return { ...s, reservations, events: eventsOf(reservations) };
|
|
94
98
|
}
|
|
95
99
|
|
|
96
|
-
const MOCK_BANNER = "Sample content — you have no reservations yet.";
|
|
97
100
|
/** Sample rows sit on days RELATIVE to now, not on frozen "Aug 8" strings: an
|
|
98
101
|
* Agenda groups by real calendar day, and hardcoded past dates would pile the
|
|
99
102
|
* whole sample under one stale heading. */
|
|
@@ -119,8 +122,8 @@ function mockRows(): Row[] {
|
|
|
119
122
|
];
|
|
120
123
|
}
|
|
121
124
|
|
|
122
|
-
function mockState(s: S): S {
|
|
123
|
-
return withEvents({ ...s, status: "ready", emptyText: "", isMock: true, notMock: false,
|
|
125
|
+
function mockState(s: S, reason: "empty" | "unavailable"): S {
|
|
126
|
+
return withEvents({ ...s, status: "ready", emptyText: "", isMock: true, notMock: false, notMockEmpty: reason !== "empty", notMockUnavailable: reason !== "unavailable"}, mockRows());
|
|
124
127
|
}
|
|
125
128
|
|
|
126
129
|
/** The reservation id out of a tap. `Agenda.onSelectEvent` dispatches
|
|
@@ -138,7 +141,7 @@ const svc = defineService<S, {
|
|
|
138
141
|
load: (s: S) => Promise<S>;
|
|
139
142
|
cancel: (s: S, p?: unknown) => Promise<S>;
|
|
140
143
|
}>("himiBrandedReservations", {
|
|
141
|
-
state: { status: "loading", reservations: [], events: [], emptyText: "", isMock: false, notMock: true,
|
|
144
|
+
state: { status: "loading", reservations: [], events: [], emptyText: "", isMock: false, notMock: true, notMockEmpty: true, notMockUnavailable: true},
|
|
142
145
|
actions: {
|
|
143
146
|
load: async (s) => {
|
|
144
147
|
try {
|
|
@@ -147,11 +150,11 @@ const svc = defineService<S, {
|
|
|
147
150
|
const r = await queryReservations({ cursorPaging: { limit: 20 }, sort: [{ fieldName: "details.startDate", order: "ASC" }] });
|
|
148
151
|
const reservations = (r.reservations ?? []).map(norm);
|
|
149
152
|
if (reservations.length) {
|
|
150
|
-
return withEvents({ ...s, status: "ready", emptyText: "", isMock: false, notMock: true,
|
|
153
|
+
return withEvents({ ...s, status: "ready", emptyText: "", isMock: false, notMock: true, notMockEmpty: true, notMockUnavailable: true}, reservations);
|
|
151
154
|
}
|
|
152
|
-
return mockState(s);
|
|
155
|
+
return mockState(s, "empty");
|
|
153
156
|
} catch {
|
|
154
|
-
return mockState(s);
|
|
157
|
+
return mockState(s, "unavailable");
|
|
155
158
|
}
|
|
156
159
|
},
|
|
157
160
|
// Cancel one upcoming reservation. Optimistic (marks it Canceled immediately);
|
|
@@ -12,7 +12,11 @@ interface Row { id: string; author: string; stars: string; title: string; body:
|
|
|
12
12
|
interface S {
|
|
13
13
|
status: "loading" | "ready";
|
|
14
14
|
reviews: Row[]; emptyText: string;
|
|
15
|
-
isMock: boolean; notMock: boolean;
|
|
15
|
+
isMock: boolean; notMock: boolean; /** #2849 — hiddenWhen() hides on TRUE, so these are inverted: the empty-state banner
|
|
16
|
+
* shows only when the site really has none, and the unavailable one only when the
|
|
17
|
+
* query failed. A failed query must never render the sentence that claims the
|
|
18
|
+
* customer has no content. */
|
|
19
|
+
notMockEmpty: boolean; notMockUnavailable: boolean;
|
|
16
20
|
}
|
|
17
21
|
|
|
18
22
|
/** "★★★★☆" from a 0–5 rating (clamped). */
|
|
@@ -32,7 +36,6 @@ function norm(v: WixReview): Row {
|
|
|
32
36
|
};
|
|
33
37
|
}
|
|
34
38
|
|
|
35
|
-
const MOCK_BANNER = "Sample content — this business has no reviews yet.";
|
|
36
39
|
const MOCK: Row[] = [
|
|
37
40
|
{ id: "sample-1", author: "Maya L.", stars: "★★★★★", title: "Fantastic experience", body: "Best in town — friendly staff and top quality. Highly recommend!" },
|
|
38
41
|
{ id: "sample-2", author: "Daniel C.", stars: "★★★★☆", title: "Great value", body: "Really enjoyed it and will definitely be coming back." },
|
|
@@ -40,22 +43,22 @@ const MOCK: Row[] = [
|
|
|
40
43
|
{ id: "sample-4", author: "Amit L.", stars: "★★★★☆", title: "Solid", body: "Good quality and quick service. Would order again." },
|
|
41
44
|
];
|
|
42
45
|
|
|
43
|
-
function mockState(s: S): S {
|
|
44
|
-
return { ...s, status: "ready", reviews: MOCK.map((r) => ({ ...r })), emptyText: "", isMock: true, notMock: false,
|
|
46
|
+
function mockState(s: S, reason: "empty" | "unavailable"): S {
|
|
47
|
+
return { ...s, status: "ready", reviews: MOCK.map((r) => ({ ...r })), emptyText: "", isMock: true, notMock: false, notMockEmpty: reason !== "empty", notMockUnavailable: reason !== "unavailable"};
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
const svc = defineService<S, { load: (s: S) => Promise<S> }>("himiBrandedReviews", {
|
|
48
|
-
state: { status: "loading", reviews: [], emptyText: "", isMock: false, notMock: true,
|
|
51
|
+
state: { status: "loading", reviews: [], emptyText: "", isMock: false, notMock: true, notMockEmpty: true, notMockUnavailable: true},
|
|
49
52
|
actions: {
|
|
50
53
|
load: async (s) => {
|
|
51
54
|
try {
|
|
52
55
|
const r = await queryReviews({ cursorPaging: { limit: 20 } });
|
|
53
56
|
const raw = (r.reviews ?? r.results ?? []) as WixReview[];
|
|
54
57
|
const reviews = raw.map(norm).filter((x) => x.title || x.body);
|
|
55
|
-
if (reviews.length) return { ...s, status: "ready", reviews, emptyText: "", isMock: false, notMock: true,
|
|
56
|
-
return mockState(s);
|
|
58
|
+
if (reviews.length) return { ...s, status: "ready", reviews, emptyText: "", isMock: false, notMock: true, notMockEmpty: true, notMockUnavailable: true};
|
|
59
|
+
return mockState(s, "empty");
|
|
57
60
|
} catch {
|
|
58
|
-
return mockState(s);
|
|
61
|
+
return mockState(s, "unavailable");
|
|
59
62
|
}
|
|
60
63
|
},
|
|
61
64
|
},
|
|
@@ -37,7 +37,11 @@ interface Row { id: string; title: string; subtitle: string }
|
|
|
37
37
|
interface S {
|
|
38
38
|
status: "loading" | "ready";
|
|
39
39
|
channels: Row[]; emptyText: string;
|
|
40
|
-
isMock: boolean; notMock: boolean;
|
|
40
|
+
isMock: boolean; notMock: boolean; /** #2849 — hiddenWhen() hides on TRUE, so these are inverted: the empty-state banner
|
|
41
|
+
* shows only when the site really has none, and the unavailable one only when the
|
|
42
|
+
* query failed. A failed query must never render the sentence that claims the
|
|
43
|
+
* customer has no content. */
|
|
44
|
+
notMockEmpty: boolean; notMockUnavailable: boolean;
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
function norm(c: VodChannel): Row {
|
|
@@ -47,24 +51,23 @@ function norm(c: VodChannel): Row {
|
|
|
47
51
|
return { id: c.id || "channel", title, subtitle: parts.join(" · ") };
|
|
48
52
|
}
|
|
49
53
|
|
|
50
|
-
const MOCK_BANNER = "Sample content — this site has no Wix Video channels yet.";
|
|
51
54
|
const MOCK: Row[] = [
|
|
52
55
|
{ id: "sample-tutorials", title: "Tutorials", subtitle: "12 videos" },
|
|
53
56
|
{ id: "sample-behind", title: "Behind the Scenes", subtitle: "6 videos" },
|
|
54
57
|
{ id: "sample-masterclass", title: "Masterclass", subtitle: "8 videos · Premium" },
|
|
55
58
|
];
|
|
56
59
|
|
|
57
|
-
function mockState(s: S): S {
|
|
58
|
-
return { ...s, status: "ready", channels: MOCK.map((r) => ({ ...r })), emptyText: "", isMock: true, notMock: false,
|
|
60
|
+
function mockState(s: S, reason: "empty" | "unavailable"): S {
|
|
61
|
+
return { ...s, status: "ready", channels: MOCK.map((r) => ({ ...r })), emptyText: "", isMock: true, notMock: false, notMockEmpty: reason !== "empty", notMockUnavailable: reason !== "unavailable"};
|
|
59
62
|
}
|
|
60
63
|
|
|
61
64
|
const svc = defineService<S, { load: (s: S) => Promise<S> }>("himiBrandedVideos", {
|
|
62
|
-
state: { status: "loading", channels: [], emptyText: "", isMock: false, notMock: true,
|
|
65
|
+
state: { status: "loading", channels: [], emptyText: "", isMock: false, notMock: true, notMockEmpty: true, notMockUnavailable: true},
|
|
63
66
|
actions: {
|
|
64
67
|
load: async (s) => {
|
|
65
68
|
try {
|
|
66
69
|
const api = wixApi();
|
|
67
|
-
if (!api) return mockState(s);
|
|
70
|
+
if (!api) return mockState(s, "unavailable");
|
|
68
71
|
const res = (await api.call("POST", VOD_PUBLIC_GRAPHQL, { query: CHANNEL_LIST_QUERY, variables: {} })) as
|
|
69
72
|
{ data?: { channels?: { entities?: VodChannel[] } } } | { channels?: { entities?: VodChannel[] } };
|
|
70
73
|
// The gateway may return the GraphQL envelope ({data:{...}}) or the bridge may
|
|
@@ -73,10 +76,10 @@ const svc = defineService<S, { load: (s: S) => Promise<S> }>("himiBrandedVideos"
|
|
|
73
76
|
(res as { data?: { channels?: { entities?: VodChannel[] } } })?.data?.channels?.entities ??
|
|
74
77
|
(res as { channels?: { entities?: VodChannel[] } })?.channels?.entities ?? [];
|
|
75
78
|
const channels = entities.map(norm);
|
|
76
|
-
if (channels.length) return { ...s, status: "ready", channels, emptyText: "", isMock: false, notMock: true,
|
|
77
|
-
return mockState(s);
|
|
79
|
+
if (channels.length) return { ...s, status: "ready", channels, emptyText: "", isMock: false, notMock: true, notMockEmpty: true, notMockUnavailable: true};
|
|
80
|
+
return mockState(s, "empty");
|
|
78
81
|
} catch {
|
|
79
|
-
return mockState(s);
|
|
82
|
+
return mockState(s, "unavailable");
|
|
80
83
|
}
|
|
81
84
|
},
|
|
82
85
|
},
|
|
@@ -11,7 +11,12 @@ import {
|
|
|
11
11
|
} from "@himalaya/wix-stores-collections";
|
|
12
12
|
|
|
13
13
|
interface Row { id: string; name: string; subtitle: string }
|
|
14
|
-
interface S {
|
|
14
|
+
interface S {
|
|
15
|
+
status: "loading" | "ready" | "error";
|
|
16
|
+
collections: Row[];
|
|
17
|
+
emptyText: string;
|
|
18
|
+
error: string;
|
|
19
|
+
}
|
|
15
20
|
|
|
16
21
|
function fromCategory(c: StoreCategory): Row {
|
|
17
22
|
return { id: c.id || "", name: c.name || "Category", subtitle: categoryCount(c) };
|
|
@@ -23,26 +28,69 @@ function fromCollection(c: WixCollection): Row {
|
|
|
23
28
|
return { id: cc.id || cc.name || "collection", name: cc.name || "Collection", subtitle: n != null ? `${n} ${n === 1 ? "product" : "products"}` : "" };
|
|
24
29
|
}
|
|
25
30
|
|
|
26
|
-
|
|
31
|
+
type Loaded = { ok: true; rows: Row[] } | { ok: false; error: string };
|
|
32
|
+
|
|
33
|
+
async function loadCollections(): Promise<Loaded> {
|
|
27
34
|
try {
|
|
28
35
|
const r = await queryStoreCategories(50);
|
|
29
|
-
|
|
30
|
-
if (rows.length) return rows
|
|
31
|
-
|
|
36
|
+
// A V3 SUCCESS with zero rows is an empty catalogue, not a reason to try V1, and this is the
|
|
37
|
+
// line that matters. Written as `if (rows.length) return rows`, a modern (stores-v3) site
|
|
38
|
+
// that simply has no categories fell through to the V1 call — which `himi sdk
|
|
39
|
+
// wix-stores-collections` documents as answering HTTP 428 on exactly those sites — and the
|
|
40
|
+
// catch below turned that 428 into an empty list. The screen then told the visitor "No
|
|
41
|
+
// categories yet" over a dead endpoint.
|
|
42
|
+
//
|
|
43
|
+
// Only a V3 THROW earns the fallback, because a legacy V1-catalogue site is the single case
|
|
44
|
+
// the fallback exists for.
|
|
45
|
+
//
|
|
46
|
+
// WHAT IS ESTABLISHED vs WHAT IS ASSUMED, because the two are not the same here and a
|
|
47
|
+
// reviewer was right to ask. Established: V1-on-V3 answers HTTP 428 — `himi sdk
|
|
48
|
+
// wix-stores-collections` documents it and names the message. ASSUMED, and not yet
|
|
49
|
+
// verified against a live legacy site: that the reverse — the V3 Categories API, a separate
|
|
50
|
+
// app-namespaced service, called on a Catalog-V1 site — THROWS rather than answering
|
|
51
|
+
// `200 {}`. If it answers 200-empty, a legacy site stops reaching the fallback and shows
|
|
52
|
+
// "No categories yet" instead of its collections: the same wrong-empty-state this change
|
|
53
|
+
// fixes, moved to the other site type.
|
|
54
|
+
//
|
|
55
|
+
// It is left as a throw-only fallback deliberately, because the alternative is worse:
|
|
56
|
+
// falling back on a V3 empty result sends every modern site with a genuinely empty
|
|
57
|
+
// catalogue into the 428 path, and the catch below now surfaces that as "Couldn't load
|
|
58
|
+
// categories" over a catalogue that is simply empty. Confirm against a Catalog-V1 site
|
|
59
|
+
// before changing this; five templates carry this file.
|
|
60
|
+
return { ok: true, rows: (r.categories ?? []).map(fromCategory).filter((row) => row.id) };
|
|
61
|
+
} catch { /* V3 unavailable — a legacy V1 site is what the fallback is for */ }
|
|
32
62
|
try {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
63
|
+
// No `as never` here: `WixQueryCollectionsRequest.query` is `WixCollectionsQuery`, which
|
|
64
|
+
// declares `paging`, so this argument type-checks as written. Reach for a cast only when the
|
|
65
|
+
// types really cannot express the call — silencing them by habit hides the next real
|
|
66
|
+
// mismatch.
|
|
67
|
+
const r = await queryCollections({ query: { paging: { limit: 50 } } });
|
|
68
|
+
return { ok: true, rows: (((r as unknown as { collections?: WixCollection[] }).collections) ?? []).map(fromCollection) };
|
|
69
|
+
} catch (e) {
|
|
70
|
+
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
37
71
|
}
|
|
38
72
|
}
|
|
39
73
|
|
|
40
74
|
const svc = defineService<S, { load: (s: S) => Promise<S> }>("spacesCollections", {
|
|
41
|
-
state: { status: "loading", collections: [], emptyText: "" },
|
|
75
|
+
state: { status: "loading", collections: [], emptyText: "", error: "" },
|
|
42
76
|
actions: {
|
|
43
77
|
load: async () => {
|
|
44
|
-
const
|
|
45
|
-
|
|
78
|
+
const res = await loadCollections();
|
|
79
|
+
if (!res.ok) {
|
|
80
|
+
// `emptyText` carries the honest line as well as `error`, deliberately: this screen's
|
|
81
|
+
// descriptor binds only `emptyText`, so writing the failure there is what stops the
|
|
82
|
+
// visitor being told the catalogue is empty when the call actually failed. `status` and
|
|
83
|
+
// `error` are what an error-state binding and `himi test` read — bind them when you add
|
|
84
|
+
// a dedicated error view.
|
|
85
|
+
const line = "Couldn't load categories. Pull to refresh.";
|
|
86
|
+
return { status: "error", collections: [], emptyText: line, error: res.error };
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
status: "ready",
|
|
90
|
+
collections: res.rows,
|
|
91
|
+
emptyText: res.rows.length ? "" : "No categories yet",
|
|
92
|
+
error: "",
|
|
93
|
+
};
|
|
46
94
|
},
|
|
47
95
|
},
|
|
48
96
|
});
|
|
@@ -11,7 +11,12 @@ import {
|
|
|
11
11
|
} from "@himalaya/wix-stores-collections";
|
|
12
12
|
|
|
13
13
|
interface Row { id: string; name: string; subtitle: string }
|
|
14
|
-
interface S {
|
|
14
|
+
interface S {
|
|
15
|
+
status: "loading" | "ready" | "error";
|
|
16
|
+
collections: Row[];
|
|
17
|
+
emptyText: string;
|
|
18
|
+
error: string;
|
|
19
|
+
}
|
|
15
20
|
|
|
16
21
|
function fromCategory(c: StoreCategory): Row {
|
|
17
22
|
return { id: c.id || "", name: c.name || "Category", subtitle: categoryCount(c) };
|
|
@@ -23,26 +28,69 @@ function fromCollection(c: WixCollection): Row {
|
|
|
23
28
|
return { id: cc.id || cc.name || "collection", name: cc.name || "Collection", subtitle: n != null ? `${n} ${n === 1 ? "product" : "products"}` : "" };
|
|
24
29
|
}
|
|
25
30
|
|
|
26
|
-
|
|
31
|
+
type Loaded = { ok: true; rows: Row[] } | { ok: false; error: string };
|
|
32
|
+
|
|
33
|
+
async function loadCollections(): Promise<Loaded> {
|
|
27
34
|
try {
|
|
28
35
|
const r = await queryStoreCategories(50);
|
|
29
|
-
|
|
30
|
-
if (rows.length) return rows
|
|
31
|
-
|
|
36
|
+
// A V3 SUCCESS with zero rows is an empty catalogue, not a reason to try V1, and this is the
|
|
37
|
+
// line that matters. Written as `if (rows.length) return rows`, a modern (stores-v3) site
|
|
38
|
+
// that simply has no categories fell through to the V1 call — which `himi sdk
|
|
39
|
+
// wix-stores-collections` documents as answering HTTP 428 on exactly those sites — and the
|
|
40
|
+
// catch below turned that 428 into an empty list. The screen then told the visitor "No
|
|
41
|
+
// categories yet" over a dead endpoint.
|
|
42
|
+
//
|
|
43
|
+
// Only a V3 THROW earns the fallback, because a legacy V1-catalogue site is the single case
|
|
44
|
+
// the fallback exists for.
|
|
45
|
+
//
|
|
46
|
+
// WHAT IS ESTABLISHED vs WHAT IS ASSUMED, because the two are not the same here and a
|
|
47
|
+
// reviewer was right to ask. Established: V1-on-V3 answers HTTP 428 — `himi sdk
|
|
48
|
+
// wix-stores-collections` documents it and names the message. ASSUMED, and not yet
|
|
49
|
+
// verified against a live legacy site: that the reverse — the V3 Categories API, a separate
|
|
50
|
+
// app-namespaced service, called on a Catalog-V1 site — THROWS rather than answering
|
|
51
|
+
// `200 {}`. If it answers 200-empty, a legacy site stops reaching the fallback and shows
|
|
52
|
+
// "No categories yet" instead of its collections: the same wrong-empty-state this change
|
|
53
|
+
// fixes, moved to the other site type.
|
|
54
|
+
//
|
|
55
|
+
// It is left as a throw-only fallback deliberately, because the alternative is worse:
|
|
56
|
+
// falling back on a V3 empty result sends every modern site with a genuinely empty
|
|
57
|
+
// catalogue into the 428 path, and the catch below now surfaces that as "Couldn't load
|
|
58
|
+
// categories" over a catalogue that is simply empty. Confirm against a Catalog-V1 site
|
|
59
|
+
// before changing this; five templates carry this file.
|
|
60
|
+
return { ok: true, rows: (r.categories ?? []).map(fromCategory).filter((row) => row.id) };
|
|
61
|
+
} catch { /* V3 unavailable — a legacy V1 site is what the fallback is for */ }
|
|
32
62
|
try {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
63
|
+
// No `as never` here: `WixQueryCollectionsRequest.query` is `WixCollectionsQuery`, which
|
|
64
|
+
// declares `paging`, so this argument type-checks as written. Reach for a cast only when the
|
|
65
|
+
// types really cannot express the call — silencing them by habit hides the next real
|
|
66
|
+
// mismatch.
|
|
67
|
+
const r = await queryCollections({ query: { paging: { limit: 50 } } });
|
|
68
|
+
return { ok: true, rows: (((r as unknown as { collections?: WixCollection[] }).collections) ?? []).map(fromCollection) };
|
|
69
|
+
} catch (e) {
|
|
70
|
+
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
37
71
|
}
|
|
38
72
|
}
|
|
39
73
|
|
|
40
74
|
const svc = defineService<S, { load: (s: S) => Promise<S> }>("spacesCollections", {
|
|
41
|
-
state: { status: "loading", collections: [], emptyText: "" },
|
|
75
|
+
state: { status: "loading", collections: [], emptyText: "", error: "" },
|
|
42
76
|
actions: {
|
|
43
77
|
load: async () => {
|
|
44
|
-
const
|
|
45
|
-
|
|
78
|
+
const res = await loadCollections();
|
|
79
|
+
if (!res.ok) {
|
|
80
|
+
// `emptyText` carries the honest line as well as `error`, deliberately: this screen's
|
|
81
|
+
// descriptor binds only `emptyText`, so writing the failure there is what stops the
|
|
82
|
+
// visitor being told the catalogue is empty when the call actually failed. `status` and
|
|
83
|
+
// `error` are what an error-state binding and `himi test` read — bind them when you add
|
|
84
|
+
// a dedicated error view.
|
|
85
|
+
const line = "Couldn't load categories. Pull to refresh.";
|
|
86
|
+
return { status: "error", collections: [], emptyText: line, error: res.error };
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
status: "ready",
|
|
90
|
+
collections: res.rows,
|
|
91
|
+
emptyText: res.rows.length ? "" : "No categories yet",
|
|
92
|
+
error: "",
|
|
93
|
+
};
|
|
46
94
|
},
|
|
47
95
|
},
|
|
48
96
|
});
|