@wix/himalaya-cli 0.1040.0 → 0.1042.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/cli.mjs +33 -17
- package/dist/preview-assets.generated.bin +0 -0
- package/package.json +2 -2
- package/templates/beauty-salon/dev/index.ts +42 -42
- package/templates/beauty-salon/tier5-src/_shared/collection-contract.ts +2 -0
- package/templates/beauty-salon/tier5-src/_shared/collection-service.ts +6 -1
- package/templates/beauty-salon-owner/config.ts +2 -2
- package/templates/beauty-salon-owner/dev/index.ts +287 -260
- package/templates/beauty-salon-owner/tier5-src/_shared/collection-contract.ts +2 -0
- package/templates/beauty-salon-owner/tier5-src/_shared/collection-service.ts +6 -1
- package/templates/beauty-salon-owner/tier5-src/_shared/owner-account.ts +45 -0
- package/templates/beauty-salon-owner/tier5-src/_shared/owner-preview-demo.ts +21 -0
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-account/index.ts +68 -0
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-ask/index.ts +4 -3
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -4
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-product-form/index.ts +29 -13
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-service-form/index.ts +9 -3
- package/templates/branded-lite/dev/index.ts +175 -175
- package/templates/branded-lite/tier5-src/_shared/collection-contract.ts +2 -0
- package/templates/branded-lite/tier5-src/_shared/collection-service.ts +6 -1
- package/templates/community-club/dev/index.ts +28 -28
- package/templates/community-club/tier5-src/_shared/collection-contract.ts +2 -0
- package/templates/community-club/tier5-src/_shared/collection-service.ts +6 -1
- package/templates/community-club-owner/config.ts +2 -2
- package/templates/community-club-owner/dev/index.ts +287 -260
- package/templates/community-club-owner/tier5-src/_shared/collection-contract.ts +2 -0
- package/templates/community-club-owner/tier5-src/_shared/collection-service.ts +6 -1
- package/templates/community-club-owner/tier5-src/_shared/owner-account.ts +45 -0
- package/templates/community-club-owner/tier5-src/_shared/owner-preview-demo.ts +21 -0
- package/templates/community-club-owner/tier5-src/himi-owner-lite-account/index.ts +68 -0
- package/templates/community-club-owner/tier5-src/himi-owner-lite-ask/index.ts +4 -3
- package/templates/community-club-owner/tier5-src/himi-owner-lite-event-form/index.ts +9 -3
- package/templates/community-club-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -4
- package/templates/community-club-owner/tier5-src/himi-owner-lite-product-form/index.ts +29 -13
- package/templates/events-meetups/dev/index.ts +21 -21
- package/templates/events-meetups/tier5-src/_shared/collection-contract.ts +2 -0
- package/templates/events-meetups/tier5-src/_shared/collection-service.ts +6 -1
- package/templates/events-meetups-owner/config.ts +2 -2
- package/templates/events-meetups-owner/dev/index.ts +259 -208
- package/templates/events-meetups-owner/tier5-src/_shared/collection-contract.ts +2 -0
- package/templates/events-meetups-owner/tier5-src/_shared/collection-service.ts +6 -1
- package/templates/events-meetups-owner/tier5-src/_shared/owner-account.ts +45 -0
- package/templates/events-meetups-owner/tier5-src/_shared/owner-preview-demo.ts +21 -0
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-account/index.ts +68 -0
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-ask/index.ts +4 -3
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-event-form/index.ts +9 -3
- package/templates/events-meetups-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -4
- package/templates/gym-fitness/dev/index.ts +49 -49
- package/templates/gym-fitness/tier5-src/_shared/collection-contract.ts +2 -0
- package/templates/gym-fitness/tier5-src/_shared/collection-service.ts +6 -1
- package/templates/gym-fitness-owner/config.ts +2 -2
- package/templates/gym-fitness-owner/dev/index.ts +294 -285
- package/templates/gym-fitness-owner/tier5-src/_shared/collection-contract.ts +2 -0
- package/templates/gym-fitness-owner/tier5-src/_shared/collection-service.ts +6 -1
- package/templates/gym-fitness-owner/tier5-src/_shared/owner-account.ts +45 -0
- package/templates/gym-fitness-owner/tier5-src/_shared/owner-preview-demo.ts +21 -0
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-account/index.ts +68 -0
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-ask/index.ts +4 -3
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-event-form/index.ts +9 -3
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -4
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-product-form/index.ts +29 -13
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-service-form/index.ts +9 -3
- package/templates/nonprofit-cause/dev/index.ts +28 -28
- package/templates/nonprofit-cause/tier5-src/_shared/collection-contract.ts +2 -0
- package/templates/nonprofit-cause/tier5-src/_shared/collection-service.ts +6 -1
- package/templates/nonprofit-cause-owner/config.ts +2 -2
- package/templates/nonprofit-cause-owner/dev/index.ts +266 -233
- package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-contract.ts +2 -0
- package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-service.ts +6 -1
- package/templates/nonprofit-cause-owner/tier5-src/_shared/owner-account.ts +45 -0
- package/templates/nonprofit-cause-owner/tier5-src/_shared/owner-preview-demo.ts +21 -0
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-account/index.ts +68 -0
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-ask/index.ts +4 -3
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-event-form/index.ts +9 -3
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -4
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-service-form/index.ts +9 -3
- package/templates/owner-lite/config.ts +2 -2
- package/templates/owner-lite/dev/index.ts +295 -287
- package/templates/owner-lite/tier5-src/_shared/collection-contract.ts +2 -0
- package/templates/owner-lite/tier5-src/_shared/collection-service.ts +6 -1
- package/templates/owner-lite/tier5-src/_shared/owner-account.ts +45 -0
- package/templates/owner-lite/tier5-src/_shared/owner-preview-demo.ts +21 -0
- package/templates/owner-lite/tier5-src/himi-owner-lite-account/index.ts +68 -0
- package/templates/owner-lite/tier5-src/himi-owner-lite-ask/index.ts +4 -3
- package/templates/owner-lite/tier5-src/himi-owner-lite-event-form/index.ts +9 -3
- package/templates/owner-lite/tier5-src/himi-owner-lite-post-form/index.ts +10 -4
- package/templates/owner-lite/tier5-src/himi-owner-lite-product-form/index.ts +29 -13
- package/templates/owner-lite/tier5-src/himi-owner-lite-service-form/index.ts +9 -3
- package/templates/podcast-creator/dev/index.ts +28 -28
- package/templates/podcast-creator/tier5-src/_shared/collection-contract.ts +2 -0
- package/templates/podcast-creator/tier5-src/_shared/collection-service.ts +6 -1
- package/templates/podcast-creator-owner/config.ts +2 -2
- package/templates/podcast-creator-owner/dev/index.ts +287 -260
- package/templates/podcast-creator-owner/tier5-src/_shared/collection-contract.ts +2 -0
- package/templates/podcast-creator-owner/tier5-src/_shared/collection-service.ts +6 -1
- package/templates/podcast-creator-owner/tier5-src/_shared/owner-account.ts +45 -0
- package/templates/podcast-creator-owner/tier5-src/_shared/owner-preview-demo.ts +21 -0
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-account/index.ts +68 -0
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-ask/index.ts +4 -3
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-event-form/index.ts +9 -3
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -4
- package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-product-form/index.ts +29 -13
- package/templates/portfolio-creative/dev/index.ts +35 -35
- package/templates/portfolio-creative/tier5-src/_shared/collection-contract.ts +2 -0
- package/templates/portfolio-creative/tier5-src/_shared/collection-service.ts +6 -1
- package/templates/portfolio-creative-owner/config.ts +2 -2
- package/templates/portfolio-creative-owner/dev/index.ts +287 -260
- package/templates/portfolio-creative-owner/tier5-src/_shared/collection-contract.ts +2 -0
- package/templates/portfolio-creative-owner/tier5-src/_shared/collection-service.ts +6 -1
- package/templates/portfolio-creative-owner/tier5-src/_shared/owner-account.ts +45 -0
- package/templates/portfolio-creative-owner/tier5-src/_shared/owner-preview-demo.ts +21 -0
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-account/index.ts +68 -0
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-ask/index.ts +4 -3
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -4
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-product-form/index.ts +29 -13
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-service-form/index.ts +9 -3
- package/templates/restaurant/dev/index.ts +21 -21
- package/templates/restaurant/tier5-src/_shared/collection-contract.ts +2 -0
- package/templates/restaurant/tier5-src/_shared/collection-service.ts +6 -1
- package/templates/restaurant-owner/config.ts +2 -2
- package/templates/restaurant-owner/dev/index.ts +294 -285
- package/templates/restaurant-owner/tier5-src/_shared/collection-contract.ts +2 -0
- package/templates/restaurant-owner/tier5-src/_shared/collection-service.ts +6 -1
- package/templates/restaurant-owner/tier5-src/_shared/owner-account.ts +45 -0
- package/templates/restaurant-owner/tier5-src/_shared/owner-preview-demo.ts +21 -0
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-account/index.ts +68 -0
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-ask/index.ts +4 -3
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-event-form/index.ts +9 -3
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -4
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-product-form/index.ts +29 -13
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-service-form/index.ts +9 -3
- package/templates/retail-store/dev/index.ts +21 -21
- package/templates/retail-store/tier5-src/_shared/collection-contract.ts +2 -0
- package/templates/retail-store/tier5-src/_shared/collection-service.ts +6 -1
- package/templates/retail-store-owner/config.ts +2 -2
- package/templates/retail-store-owner/dev/index.ts +280 -235
- package/templates/retail-store-owner/tier5-src/_shared/collection-contract.ts +2 -0
- package/templates/retail-store-owner/tier5-src/_shared/collection-service.ts +6 -1
- package/templates/retail-store-owner/tier5-src/_shared/owner-account.ts +45 -0
- package/templates/retail-store-owner/tier5-src/_shared/owner-preview-demo.ts +21 -0
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-account/index.ts +68 -0
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-ask/index.ts +4 -3
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -4
- package/templates/retail-store-owner/tier5-src/himi-owner-lite-product-form/index.ts +29 -13
- package/templates/yoga-studio/dev/index.ts +42 -42
- package/templates/yoga-studio/tier5-src/_shared/collection-contract.ts +2 -0
- package/templates/yoga-studio/tier5-src/_shared/collection-service.ts +6 -1
- package/templates/yoga-studio-owner/config.ts +2 -2
- package/templates/yoga-studio-owner/dev/index.ts +266 -233
- package/templates/yoga-studio-owner/tier5-src/_shared/collection-contract.ts +2 -0
- package/templates/yoga-studio-owner/tier5-src/_shared/collection-service.ts +6 -1
- package/templates/yoga-studio-owner/tier5-src/_shared/owner-account.ts +45 -0
- package/templates/yoga-studio-owner/tier5-src/_shared/owner-preview-demo.ts +21 -0
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-account/index.ts +68 -0
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-ask/index.ts +4 -3
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-event-form/index.ts +9 -3
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -4
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-service-form/index.ts +9 -3
|
@@ -168,6 +168,8 @@ export function projectCollection(source: CollectionRow[], config: CollectionCon
|
|
|
168
168
|
items: visible.map(row => ({ ...row, collectionId: collectionId(row), collectionTitle: collectionTitle(row, config),
|
|
169
169
|
collectionMeta: [...new Set((config.metadata ?? ['total', 'statusLabel', 'price', 'priceLine', 'dateLine', 'meta']).filter(key => key !== config.subtitle).map(key => scalar(row[key])).filter(Boolean))].join(' · '),
|
|
170
170
|
collectionSelectLabel: prefs.selectedIds.includes(collectionId(row)) ? 'Selected' : 'Select',
|
|
171
|
+
// The save toggle names the action it WILL take (the service localizes it).
|
|
172
|
+
collectionSaveLabel: prefs.savedIds.includes(collectionId(row)) ? 'Remove from saved' : 'Save',
|
|
171
173
|
collectionSubtitle: scalar(row[config.subtitle ?? '']) || scalar(row.subtitle) || scalar(row.description) || scalar(row.meta),
|
|
172
174
|
collectionImage: scalar(row[config.image ?? '']), collectionNoImage: !scalar(row[config.image ?? '']), collectionHasImage: !!scalar(row[config.image ?? '']),
|
|
173
175
|
collectionSaved: prefs.savedIds.includes(collectionId(row)), collectionNotSaved: !prefs.savedIds.includes(collectionId(row)), collectionSelected: prefs.selectedIds.includes(collectionId(row)),
|
|
@@ -123,7 +123,12 @@ export function decorateCollection(name: string, def: { state: State; actions?:
|
|
|
123
123
|
pendingFilter = '';
|
|
124
124
|
}
|
|
125
125
|
const projected = projectCollection(rows, config, prefs, !!backend && config.backendQuery === true, known);
|
|
126
|
-
projected.items.forEach(row => {
|
|
126
|
+
projected.items.forEach(row => {
|
|
127
|
+
row.collectionSelectLabel = row.collectionSelected ? copyParam('collection.selected', 'Selected') : copyParam('collection.select', 'Select');
|
|
128
|
+
// the save toggle names the action it WILL take for this row. The
|
|
129
|
+
// old static "Save / unsave" made the owner work out the row's state.
|
|
130
|
+
row.collectionSaveLabel = row.collectionSaved ? copyParam('collection.unsave', 'Remove from saved') : copyParam('collection.save', 'Save');
|
|
131
|
+
});
|
|
127
132
|
if (previewId && !rows.some(row => collectionId(row) === previewId)) previewId = '';
|
|
128
133
|
const filterChoices = [{ id: 'all', label: copyParam('collection.all', 'All') }, { id: 'saved', label: copyParam('collection.saved', 'Saved') },
|
|
129
134
|
...projected.filters.map(label => ({ id: `value:${label}`, label: copyParam(`collection.filter.${label}`, label) }))];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Pure seams of the owner-lite Account worker — no @himalaya/* imports, so a
|
|
2
|
+
// plain node test can load them without the bundle build's module aliases.
|
|
3
|
+
|
|
4
|
+
export interface AccountState {
|
|
5
|
+
status: "loading" | "ready";
|
|
6
|
+
/** "Name · email", the email alone, or "" (the line collapses). */
|
|
7
|
+
ownerLine: string;
|
|
8
|
+
/** "Version 1.2.0 (34)", or "" when the host cannot say. */
|
|
9
|
+
versionLine: string;
|
|
10
|
+
/** Why the owner line is empty, when the identity read failed; "" otherwise. */
|
|
11
|
+
error: string;
|
|
12
|
+
supportUrl: string;
|
|
13
|
+
privacyUrl: string;
|
|
14
|
+
termsUrl: string;
|
|
15
|
+
/** hiddenPath flags — TRUE hides, so each is phrased negatively. */
|
|
16
|
+
noSupport: boolean;
|
|
17
|
+
noPrivacy: boolean;
|
|
18
|
+
noTerms: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Only absolute http(s) URLs are openable; anything else is treated as absent. */
|
|
22
|
+
export function safeUrl(v: unknown): string {
|
|
23
|
+
if (typeof v !== "string") return "";
|
|
24
|
+
const t = v.trim();
|
|
25
|
+
return /^https?:\/\/[^\s]+$/i.test(t) ? t : "";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** "Name · email" when both are known, whichever one is when only one is, else "". */
|
|
29
|
+
export function ownerLineOf(name: string, email: string): string {
|
|
30
|
+
const n = name.trim(), e = email.trim();
|
|
31
|
+
if (n && e && n !== e) return `${n} · ${e}`;
|
|
32
|
+
return n || e;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function shapeAccount(s: Partial<AccountState>): AccountState {
|
|
36
|
+
const supportUrl = safeUrl(s.supportUrl), privacyUrl = safeUrl(s.privacyUrl), termsUrl = safeUrl(s.termsUrl);
|
|
37
|
+
return {
|
|
38
|
+
status: s.status ?? "loading",
|
|
39
|
+
ownerLine: s.ownerLine ?? "",
|
|
40
|
+
versionLine: s.versionLine ?? "",
|
|
41
|
+
error: s.error ?? "",
|
|
42
|
+
supportUrl, privacyUrl, termsUrl,
|
|
43
|
+
noSupport: !supportUrl, noPrivacy: !privacyUrl, noTerms: !termsUrl,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -18,6 +18,27 @@ export const DEMO_NOTE =
|
|
|
18
18
|
|
|
19
19
|
export const DEMO_SAVE_NOTE = "Demo mode — changes aren’t saved in this preview.";
|
|
20
20
|
|
|
21
|
+
/** The state patch for a write refused because this is the demo preview. A demo
|
|
22
|
+
* refusal is a MODE, not an error: no red error line — the neutral info banner
|
|
23
|
+
* (already on screen before the owner typed anything) switches to the save
|
|
24
|
+
* note, so the tap still visibly answers. `saveNote` is the (copy-pack) text. */
|
|
25
|
+
export function demoRefusalPatch(saveNote: string = DEMO_SAVE_NOTE): { status: "idle"; error: ""; savedMessage: ""; demoNote: string } {
|
|
26
|
+
return { status: "idle", error: "", savedMessage: "", demoNote: saveNote || DEMO_SAVE_NOTE };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** "€1,234.50" / "12,50" / " 10 " → "1234.50" / "12.50" / "10": digits and one
|
|
30
|
+
* decimal point — what an editable price field accepts and a price write needs.
|
|
31
|
+
* A separator followed by exactly 1–2 digits is the decimal; any other groups. */
|
|
32
|
+
export function editablePrice(raw: string): string {
|
|
33
|
+
const t = raw.replace(/[^\d.,]/g, "");
|
|
34
|
+
if (!t) return "";
|
|
35
|
+
const lastSep = Math.max(t.lastIndexOf("."), t.lastIndexOf(","));
|
|
36
|
+
if (lastSep >= 0 && /^\d{1,2}$/.test(t.slice(lastSep + 1))) {
|
|
37
|
+
return `${t.slice(0, lastSep).replace(/[.,]/g, "")}.${t.slice(lastSep + 1)}`;
|
|
38
|
+
}
|
|
39
|
+
return t.replace(/[.,]/g, "");
|
|
40
|
+
}
|
|
41
|
+
|
|
21
42
|
export const DEMO_IDS = ["demo-1", "demo-2", "demo-3"] as const;
|
|
22
43
|
|
|
23
44
|
export interface DemoProduct {
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Tier-5 bundle: __HIMI_APP_NAME__ — Account.
|
|
2
|
+
//
|
|
3
|
+
// The Account screen used to be a static SDUI descriptor whose subtitle was the
|
|
4
|
+
// literal "Wix owner (user)" — a role string, not a person. A descriptor cannot
|
|
5
|
+
// know who is signed in; a worker can. This one reads:
|
|
6
|
+
// • the signed-in Wix user (`getMyData`, the same users.wix.com read
|
|
7
|
+
// wix-owner-profile uses) → "Name · email" under the title;
|
|
8
|
+
// • the app's real version through @himalaya/app-info (never a baked literal,
|
|
9
|
+
// which goes stale the moment the binary is bumped);
|
|
10
|
+
// • the help / privacy / terms links, which arrive as onAppear params baked
|
|
11
|
+
// from provisioning.json `identity` — so a branded template ships ITS links,
|
|
12
|
+
// and `openLink` can only ever open one of those three URLs.
|
|
13
|
+
//
|
|
14
|
+
// Every read is best-effort: an identity or version the host cannot answer
|
|
15
|
+
// collapses its line rather than rendering an error on a settings screen.
|
|
16
|
+
|
|
17
|
+
import { defineService } from "@himalaya/state";
|
|
18
|
+
import { getMyData } from "@himalaya/wix-users";
|
|
19
|
+
import { displayVersion } from "@himalaya/app-info";
|
|
20
|
+
import { open } from "@himalaya/open-url";
|
|
21
|
+
import { stashCopy, copyTemplate } from "../_shared/himi-copy.js";
|
|
22
|
+
import { ownerLineOf, shapeAccount, type AccountState } from "../_shared/owner-account.js";
|
|
23
|
+
|
|
24
|
+
type LinkKind = "support" | "privacy" | "terms";
|
|
25
|
+
|
|
26
|
+
const obj = (v: unknown): Record<string, unknown> => (v && typeof v === "object" ? v as Record<string, unknown> : {});
|
|
27
|
+
|
|
28
|
+
const svc = defineService<AccountState, {
|
|
29
|
+
load: (s: AccountState, arg?: unknown) => Promise<AccountState>;
|
|
30
|
+
openLink: (s: AccountState, arg?: unknown) => Promise<AccountState>;
|
|
31
|
+
}>("himiOwnerLiteAccount", {
|
|
32
|
+
state: shapeAccount({}),
|
|
33
|
+
actions: {
|
|
34
|
+
load: async (s, arg) => {
|
|
35
|
+
stashCopy(arg);
|
|
36
|
+
const p = obj(arg);
|
|
37
|
+
const links = { supportUrl: String(p.supportUrl ?? s.supportUrl), privacyUrl: String(p.privacyUrl ?? s.privacyUrl), termsUrl: String(p.termsUrl ?? s.termsUrl) };
|
|
38
|
+
let ownerLine = "";
|
|
39
|
+
// Why the identity line is missing, when it is. Kept in state (the failure is
|
|
40
|
+
// modelled, not swallowed) but not painted: a settings screen that cannot name
|
|
41
|
+
// its owner still works, so the line collapses rather than showing an error.
|
|
42
|
+
let error: string = "";
|
|
43
|
+
try {
|
|
44
|
+
const d = await getMyData();
|
|
45
|
+
// The PROFILE name only — never the userName handle, an account nickname
|
|
46
|
+
// ("apptestercoowner"): exactly the kind of placeholder this line replaces.
|
|
47
|
+
const name = [d.profile?.firstName, d.profile?.lastName].filter(Boolean).join(" ");
|
|
48
|
+
ownerLine = ownerLineOf(name, String(d.email || d.contactEmail || ""));
|
|
49
|
+
} catch (e) {
|
|
50
|
+
error = String((e as Error)?.message ?? e);
|
|
51
|
+
}
|
|
52
|
+
let versionLine = "";
|
|
53
|
+
try {
|
|
54
|
+
const v = await displayVersion();
|
|
55
|
+
versionLine = v ? copyTemplate("ownerAccount.version", "Version {version}", { version: v }) : "";
|
|
56
|
+
} catch { /* host without app_info — the row collapses */ }
|
|
57
|
+
return shapeAccount({ ...s, ...links, status: "ready", ownerLine, versionLine, error });
|
|
58
|
+
},
|
|
59
|
+
openLink: async (s, arg) => {
|
|
60
|
+
const kind = String(obj(arg).kind ?? arg ?? "") as LinkKind;
|
|
61
|
+
const url = kind === "support" ? s.supportUrl : kind === "privacy" ? s.privacyUrl : kind === "terms" ? s.termsUrl : "";
|
|
62
|
+
if (url) await open(url);
|
|
63
|
+
return s;
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
export { svc };
|
|
@@ -111,7 +111,8 @@ const STARTER: Node = {
|
|
|
111
111
|
props: {
|
|
112
112
|
title: "Ask about your business",
|
|
113
113
|
subtitle: "Try “which products are out of stock”, “unpaid orders”, or “what's coming up”.",
|
|
114
|
-
|
|
114
|
+
// Not `search`: Ask is not search, and a magnifier says it is.
|
|
115
|
+
icon: "ai",
|
|
115
116
|
},
|
|
116
117
|
},
|
|
117
118
|
},
|
|
@@ -129,7 +130,7 @@ const enc = (n: Node): string => encodeSubtree(n as unknown as Parameters<typeof
|
|
|
129
130
|
const initialState: AskState = {
|
|
130
131
|
status: "idle",
|
|
131
132
|
intent: "",
|
|
132
|
-
headline: "
|
|
133
|
+
headline: "",
|
|
133
134
|
note: "",
|
|
134
135
|
customerNotice: "",
|
|
135
136
|
repairs: "",
|
|
@@ -267,7 +268,7 @@ function failed(state: AskState, headline: string, note: string): AskState {
|
|
|
267
268
|
body: {
|
|
268
269
|
package: "himalaya",
|
|
269
270
|
type: "EmptyState",
|
|
270
|
-
props: { title: headline, subtitle: note, icon: "
|
|
271
|
+
props: { title: headline, subtitle: note, icon: "ai" },
|
|
271
272
|
},
|
|
272
273
|
}],
|
|
273
274
|
},
|
|
@@ -9,7 +9,7 @@ import { wixMediaUrl, WIX_DETAIL_IMAGE } from "../_shared/himi-store.js";
|
|
|
9
9
|
import { defineService } from "@himalaya/state";
|
|
10
10
|
import { getDraftPost, createDraftPost, updateDraftPost, type WixDraftPostWrite, type WixRichContent } from "@himalaya/wix-blog";
|
|
11
11
|
import { ownerLoadError, isBridgeUnavailable, isOwnerPreviewDemo, shouldQueueOwnerWrite } from "../_shared/owner-load-error.js";
|
|
12
|
-
import { DEMO_POSTS, DEMO_NOTE, DEMO_SAVE_NOTE } from "../_shared/owner-preview-demo.js";
|
|
12
|
+
import { DEMO_POSTS, DEMO_NOTE, DEMO_SAVE_NOTE, demoRefusalPatch } from "../_shared/owner-preview-demo.js";
|
|
13
13
|
import { stashCopy, copyParam } from "../_shared/himi-copy.js";
|
|
14
14
|
import { get as dbGet, upsert as dbUpsert, remove as dbRemove, enqueue as dbEnqueue, type DbEntity } from "../_shared/owner-local-db.js";
|
|
15
15
|
|
|
@@ -127,6 +127,12 @@ function shape(s: Partial<PostFormState>): PostFormState {
|
|
|
127
127
|
|
|
128
128
|
const startMode: Mode = initialInput.mode === "edit" || initialInput.id ? "edit" : "create";
|
|
129
129
|
|
|
130
|
+
|
|
131
|
+
/** A demo-preview write refusal: see demoRefusalPatch (a mode, not an error). */
|
|
132
|
+
function demoRefusal(s: Parameters<typeof shape>[0]): ReturnType<typeof shape> {
|
|
133
|
+
return shape({ ...s, ...demoRefusalPatch(copyParam("ownerCommon.demoSaveNote", DEMO_SAVE_NOTE)) });
|
|
134
|
+
}
|
|
135
|
+
|
|
130
136
|
const svc = defineService<PostFormState, {
|
|
131
137
|
load: (s: PostFormState, arg?: unknown) => Promise<PostFormState>;
|
|
132
138
|
setCover: (s: PostFormState, arg?: unknown) => PostFormState;
|
|
@@ -140,7 +146,7 @@ const svc = defineService<PostFormState, {
|
|
|
140
146
|
// bakes no id/mode); the onAppear `id` param is the #570 suffixed-build
|
|
141
147
|
// fallback. An id ⇒ edit; no id anywhere ⇒ the create shell.
|
|
142
148
|
const id = navInputId() || idFrom(arg, s.id);
|
|
143
|
-
if (!id) return shape({ ...s, mode: "create", id: "", status: "idle", error: "" });
|
|
149
|
+
if (!id) return shape({ ...s, mode: "create", id: "", status: "idle", error: "", demoNote: isOwnerPreviewDemo() ? copyParam("ownerCommon.demoSaveNote", DEMO_SAVE_NOTE) : "" });
|
|
144
150
|
if (isOwnerPreviewDemo()) {
|
|
145
151
|
const d = DEMO_POSTS.find((x) => x.id === id);
|
|
146
152
|
if (d) {
|
|
@@ -203,7 +209,7 @@ const svc = defineService<PostFormState, {
|
|
|
203
209
|
return shape({ ...s, coverUrl, error: "" });
|
|
204
210
|
},
|
|
205
211
|
submit: async (s, values) => {
|
|
206
|
-
if (isOwnerPreviewDemo() || s.demoNote) return
|
|
212
|
+
if (isOwnerPreviewDemo() || s.demoNote) return demoRefusal(s);
|
|
207
213
|
const v = values ?? {};
|
|
208
214
|
const title = String(v.title ?? s.title).trim();
|
|
209
215
|
const excerpt = String(v.excerpt ?? s.excerpt).trim();
|
|
@@ -272,7 +278,7 @@ const svc = defineService<PostFormState, {
|
|
|
272
278
|
// Demo / web-preview (no owner bridge; the record came from demo sample
|
|
273
279
|
// data, marked by demoNote): a queued write could never replay, so answer
|
|
274
280
|
// with the demo save note instead of the real-offline write-behind.
|
|
275
|
-
if (s.demoNote) return
|
|
281
|
+
if (s.demoNote) return demoRefusal(s);
|
|
276
282
|
// Real device offline: cache and queue the edit for reconnect.
|
|
277
283
|
await cachePut(localRow(localId));
|
|
278
284
|
await queueWrite("tier5.himiOwnerLitePostForm.submit", { id: targetId, title, excerpt, body, coverUrl, coverChanged: String(coverChanged), preserveBody: String(preserveBody), isFeatured: String(isFeatured), mode: editing ? "edit" : "create" });
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
import { get as dbGet, upsert as dbUpsert, remove as dbRemove, enqueue as dbEnqueue, type DbEntity } from "../_shared/owner-local-db.js";
|
|
19
19
|
import { plainText } from "../_shared/himi-store.js";
|
|
20
20
|
import { ownerLoadError, isBridgeUnavailable, isOwnerPreviewDemo, shouldQueueOwnerWrite } from "../_shared/owner-load-error.js";
|
|
21
|
-
import { DEMO_PRODUCTS, DEMO_NOTE, DEMO_SAVE_NOTE } from "../_shared/owner-preview-demo.js";
|
|
21
|
+
import { DEMO_PRODUCTS, DEMO_NOTE, DEMO_SAVE_NOTE, demoRefusalPatch, editablePrice } from "../_shared/owner-preview-demo.js";
|
|
22
22
|
import { stashCopy, copyParam } from "../_shared/himi-copy.js";
|
|
23
23
|
|
|
24
24
|
type Mode = "create" | "edit";
|
|
@@ -95,9 +95,14 @@ function boolString(value: unknown, fallback: string): string {
|
|
|
95
95
|
// fall back to the raw numeric if the formatted string is missing.
|
|
96
96
|
function priceOf(p: WixProduct): string {
|
|
97
97
|
const pr = (p as any).price;
|
|
98
|
-
|
|
98
|
+
// The field is EDITABLE and renderer-validated as an amount, so prefill the
|
|
99
|
+
// raw number — never the display string ("€10.00"), which would fail the rule
|
|
100
|
+
// and disable Save before the owner typed anything.
|
|
101
|
+
if (typeof pr?.price === "number" && Number.isFinite(pr.price)) return pr.price.toFixed(2);
|
|
102
|
+
return editablePrice(String(pr?.formatted?.price ?? pr?.price ?? ""));
|
|
99
103
|
}
|
|
100
104
|
|
|
105
|
+
|
|
101
106
|
function shape(s: Partial<ProductFormState>): ProductFormState {
|
|
102
107
|
const mode: Mode = s.mode ?? "create";
|
|
103
108
|
const id = typeof s.id === "string" ? s.id : "";
|
|
@@ -128,6 +133,12 @@ function shape(s: Partial<ProductFormState>): ProductFormState {
|
|
|
128
133
|
|
|
129
134
|
const startMode: Mode = initialInput.mode === "edit" || initialInput.id ? "edit" : "create";
|
|
130
135
|
|
|
136
|
+
|
|
137
|
+
/** A demo-preview write refusal: see demoRefusalPatch (a mode, not an error). */
|
|
138
|
+
function demoRefusal(s: Parameters<typeof shape>[0]): ReturnType<typeof shape> {
|
|
139
|
+
return shape({ ...s, ...demoRefusalPatch(copyParam("ownerCommon.demoSaveNote", DEMO_SAVE_NOTE)) });
|
|
140
|
+
}
|
|
141
|
+
|
|
131
142
|
const svc = defineService<ProductFormState, {
|
|
132
143
|
load: (s: ProductFormState, arg?: unknown) => Promise<ProductFormState>;
|
|
133
144
|
submit: (s: ProductFormState, values?: Record<string, unknown>) => Promise<ProductFormState>;
|
|
@@ -141,13 +152,13 @@ const svc = defineService<ProductFormState, {
|
|
|
141
152
|
// bakes no id/mode); the onAppear `id` param is the #570 suffixed-build
|
|
142
153
|
// fallback. An id ⇒ edit; no id anywhere ⇒ the create shell.
|
|
143
154
|
const id = navInputId() || idFrom(arg, s.id);
|
|
144
|
-
if (!id) return shape({ ...s, mode: "create", id: "", status: "idle", error: "" });
|
|
155
|
+
if (!id) return shape({ ...s, mode: "create", id: "", status: "idle", error: "", demoNote: isOwnerPreviewDemo() ? copyParam("ownerCommon.demoSaveNote", DEMO_SAVE_NOTE) : "" });
|
|
145
156
|
if (isOwnerPreviewDemo()) {
|
|
146
157
|
const d = DEMO_PRODUCTS.find((p) => p.id === id);
|
|
147
158
|
if (d) {
|
|
148
159
|
return shape({
|
|
149
160
|
...s, mode: "edit", id, heading: copyParam("ownerProductForm.headingEdit", "Edit product"),
|
|
150
|
-
name: d.name, price: d.price
|
|
161
|
+
name: d.name, price: editablePrice(d.price), sku: d.sku, description: d.description,
|
|
151
162
|
visible: d.visible ? "true" : "false",
|
|
152
163
|
status: "idle", error: "", savedMessage: "", demoNote: copyParam("ownerCommon.demoNote", DEMO_NOTE),
|
|
153
164
|
});
|
|
@@ -178,7 +189,7 @@ const svc = defineService<ProductFormState, {
|
|
|
178
189
|
if (cached) {
|
|
179
190
|
return shape({
|
|
180
191
|
...s, mode: "edit", id, heading: "Edit product",
|
|
181
|
-
name: String(cached.name ?? ""), price: String(cached.price ?? ""), sku: String(cached.sku ?? ""),
|
|
192
|
+
name: String(cached.name ?? ""), price: editablePrice(String(cached.price ?? "")), sku: String(cached.sku ?? ""),
|
|
182
193
|
visible: cached.visible === "false" ? "false" : "true",
|
|
183
194
|
status: "idle", error: "", savedMessage: "",
|
|
184
195
|
});
|
|
@@ -188,7 +199,7 @@ const svc = defineService<ProductFormState, {
|
|
|
188
199
|
if (d) {
|
|
189
200
|
return shape({
|
|
190
201
|
...s, mode: "edit", id, heading: copyParam("ownerProductForm.headingEdit", "Edit product"),
|
|
191
|
-
name: d.name, price: d.price
|
|
202
|
+
name: d.name, price: editablePrice(d.price), sku: d.sku, description: d.description,
|
|
192
203
|
visible: d.visible ? "true" : "false",
|
|
193
204
|
status: "idle", error: "", savedMessage: "", demoNote: copyParam("ownerCommon.demoNote", DEMO_NOTE),
|
|
194
205
|
});
|
|
@@ -198,17 +209,22 @@ const svc = defineService<ProductFormState, {
|
|
|
198
209
|
}
|
|
199
210
|
},
|
|
200
211
|
submit: async (s, values) => {
|
|
201
|
-
if (isOwnerPreviewDemo() || s.demoNote) return
|
|
212
|
+
if (isOwnerPreviewDemo() || s.demoNote) return demoRefusal(s);
|
|
202
213
|
const v = values ?? {};
|
|
203
214
|
const name = String(v.name ?? s.name).trim();
|
|
204
|
-
const
|
|
215
|
+
const rawPrice = String(v.price ?? s.price).trim();
|
|
216
|
+
// "12,50" (comma-decimal keypad) is accepted and written as 12.50.
|
|
217
|
+
const price = editablePrice(rawPrice);
|
|
205
218
|
const sku = String(v.sku ?? s.sku).trim();
|
|
206
219
|
const description = String(v.description ?? s.description).trim();
|
|
207
220
|
const visible = boolString(v.visible, s.visible) === "true";
|
|
208
221
|
if (!name) return shape({ ...s, status: "error", error: copyParam("ownerProductForm.nameRequired", "Name is required"), savedMessage: "" });
|
|
209
222
|
const priceNum = Number(price);
|
|
210
|
-
|
|
211
|
-
|
|
223
|
+
// The renderer already blocks these (declarative `validation`); this is the
|
|
224
|
+
// worker's own guard for a host or queue replay that skips it. Checked on the
|
|
225
|
+
// RAW value, so "-5" is refused rather than normalized into "5".
|
|
226
|
+
if (!/^\d+([.,]\d{1,2})?$/.test(rawPrice) || !Number.isFinite(priceNum)) {
|
|
227
|
+
return shape({ ...s, name, price, sku, description, visible: visible ? "true" : "false", status: "error", error: copyParam("ownerProductForm.priceInvalid", "Enter a price of 0 or more."), savedMessage: "" });
|
|
212
228
|
}
|
|
213
229
|
|
|
214
230
|
const write: WixProductWrite = {
|
|
@@ -254,7 +270,7 @@ const svc = defineService<ProductFormState, {
|
|
|
254
270
|
// Demo / web-preview (no owner bridge; the record came from DEMO_PRODUCTS,
|
|
255
271
|
// marked by demoNote): a queued write could never replay, so answer with
|
|
256
272
|
// the demo save note instead of the real-offline write-behind.
|
|
257
|
-
if (s.demoNote) return
|
|
273
|
+
if (s.demoNote) return demoRefusal(s);
|
|
258
274
|
// Real device offline: the optimistic cache row already shows; queue the
|
|
259
275
|
// write to replay (WriteQueue, idempotent) when connectivity returns.
|
|
260
276
|
await queueWrite("tier5.himiOwnerLiteProductForm.submit", { id: s.id, name, price, sku, visible: String(visible), mode: s.mode });
|
|
@@ -269,7 +285,7 @@ const svc = defineService<ProductFormState, {
|
|
|
269
285
|
}
|
|
270
286
|
},
|
|
271
287
|
remove: async (s) => {
|
|
272
|
-
if (isOwnerPreviewDemo() || s.demoNote) return
|
|
288
|
+
if (isOwnerPreviewDemo() || s.demoNote) return demoRefusal(s);
|
|
273
289
|
if (!s.id) return shape({ ...s, status: "error", error: copyParam("ownerProductForm.errorNoDelete", "No product to delete."), savedMessage: "" });
|
|
274
290
|
// Optimistic local delete — the list drops the row immediately.
|
|
275
291
|
await cacheDrop(s.id);
|
|
@@ -278,7 +294,7 @@ const svc = defineService<ProductFormState, {
|
|
|
278
294
|
return shape({ ...s, id: "", status: "saved", error: "", savedMessage: copyParam("ownerProductForm.deletedMessage", "Product deleted."), savedId: "" });
|
|
279
295
|
} catch (e) {
|
|
280
296
|
if (shouldQueueOwnerWrite(e)) {
|
|
281
|
-
if (s.demoNote) return
|
|
297
|
+
if (s.demoNote) return demoRefusal(s);
|
|
282
298
|
await queueWrite("tier5.himiOwnerLiteProductForm.remove", { id: s.id });
|
|
283
299
|
return shape({ ...s, id: "", status: "saved", error: "", savedMessage: copyParam("ownerProductForm.deletedOffline", "Deleted offline — will sync when you reconnect."), savedId: "" });
|
|
284
300
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { defineService } from "@himalaya/state";
|
|
8
8
|
import { getService, updateService, type WixService } from "@himalaya/wix-bookings";
|
|
9
9
|
import { ownerLoadError, isBridgeUnavailable, isOwnerPreviewDemo, shouldQueueOwnerWrite } from "../_shared/owner-load-error.js";
|
|
10
|
-
import { DEMO_SERVICES, DEMO_NOTE, DEMO_SAVE_NOTE } from "../_shared/owner-preview-demo.js";
|
|
10
|
+
import { DEMO_SERVICES, DEMO_NOTE, DEMO_SAVE_NOTE, demoRefusalPatch } from "../_shared/owner-preview-demo.js";
|
|
11
11
|
import { stashCopy, copyParam } from "../_shared/himi-copy.js";
|
|
12
12
|
import { get as dbGet, upsert as dbUpsert, enqueue as dbEnqueue, type DbEntity } from "../_shared/owner-local-db.js";
|
|
13
13
|
|
|
@@ -103,6 +103,12 @@ function shape(s: Partial<ServiceFormState>): ServiceFormState {
|
|
|
103
103
|
};
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
|
|
107
|
+
/** A demo-preview write refusal: see demoRefusalPatch (a mode, not an error). */
|
|
108
|
+
function demoRefusal(s: Parameters<typeof shape>[0]): ReturnType<typeof shape> {
|
|
109
|
+
return shape({ ...s, ...demoRefusalPatch(copyParam("ownerCommon.demoSaveNote", DEMO_SAVE_NOTE)) });
|
|
110
|
+
}
|
|
111
|
+
|
|
106
112
|
const svc = defineService<ServiceFormState, {
|
|
107
113
|
load: (s: ServiceFormState, arg?: unknown) => Promise<ServiceFormState>;
|
|
108
114
|
submit: (s: ServiceFormState, values?: Record<string, unknown>) => Promise<ServiceFormState>;
|
|
@@ -164,7 +170,7 @@ const svc = defineService<ServiceFormState, {
|
|
|
164
170
|
}
|
|
165
171
|
},
|
|
166
172
|
submit: async (s, values) => {
|
|
167
|
-
if (isOwnerPreviewDemo() || s.demoNote) return
|
|
173
|
+
if (isOwnerPreviewDemo() || s.demoNote) return demoRefusal(s);
|
|
168
174
|
const v = values ?? {};
|
|
169
175
|
const name = String(v.name ?? s.name).trim();
|
|
170
176
|
const description = String(v.description ?? s.description).trim();
|
|
@@ -193,7 +199,7 @@ const svc = defineService<ServiceFormState, {
|
|
|
193
199
|
// Demo / web-preview (no owner bridge; the record came from demo sample
|
|
194
200
|
// data, marked by demoNote): a queued write could never replay, so answer
|
|
195
201
|
// with the demo save note instead of the real-offline write-behind.
|
|
196
|
-
if (s.demoNote) return
|
|
202
|
+
if (s.demoNote) return demoRefusal(s);
|
|
197
203
|
// Real device offline: the optimistic cache row already shows; queue the
|
|
198
204
|
// write to replay (WriteQueue, idempotent) when connectivity returns.
|
|
199
205
|
await queueWrite("tier5.himiOwnerLiteServiceForm.submit", { id: s.id, name, description });
|
|
@@ -5684,7 +5684,7 @@ const SCREENS = runtimeSetting({
|
|
|
5684
5684
|
},
|
|
5685
5685
|
{
|
|
5686
5686
|
"id": "save",
|
|
5687
|
-
"label": "
|
|
5687
|
+
"label": "$item.collectionSaveLabel",
|
|
5688
5688
|
"icon": "bookmark",
|
|
5689
5689
|
"action": {
|
|
5690
5690
|
"actionId": "tier5.himiBrandedMenus.collectionSave",
|
|
@@ -5737,7 +5737,7 @@ const SCREENS = runtimeSetting({
|
|
|
5737
5737
|
"rowSwipeActions": {
|
|
5738
5738
|
"trailing": [
|
|
5739
5739
|
{
|
|
5740
|
-
"label": "
|
|
5740
|
+
"label": "$item.collectionSaveLabel",
|
|
5741
5741
|
"icon": "bookmark",
|
|
5742
5742
|
"action": {
|
|
5743
5743
|
"actionId": "tier5.himiBrandedMenus.collectionSave",
|
|
@@ -6002,7 +6002,7 @@ const SCREENS = runtimeSetting({
|
|
|
6002
6002
|
},
|
|
6003
6003
|
{
|
|
6004
6004
|
"id": "save",
|
|
6005
|
-
"label": "
|
|
6005
|
+
"label": "$item.collectionSaveLabel",
|
|
6006
6006
|
"icon": "bookmark",
|
|
6007
6007
|
"action": {
|
|
6008
6008
|
"actionId": "tier5.himiBrandedMenus.collectionSave",
|
|
@@ -6257,7 +6257,7 @@ const SCREENS = runtimeSetting({
|
|
|
6257
6257
|
},
|
|
6258
6258
|
{
|
|
6259
6259
|
"id": "save",
|
|
6260
|
-
"label": "
|
|
6260
|
+
"label": "$item.collectionSaveLabel",
|
|
6261
6261
|
"icon": "bookmark",
|
|
6262
6262
|
"action": {
|
|
6263
6263
|
"actionId": "tier5.himiBrandedMenus.collectionSave",
|
|
@@ -6310,7 +6310,7 @@ const SCREENS = runtimeSetting({
|
|
|
6310
6310
|
"rowSwipeActions": {
|
|
6311
6311
|
"trailing": [
|
|
6312
6312
|
{
|
|
6313
|
-
"label": "
|
|
6313
|
+
"label": "$item.collectionSaveLabel",
|
|
6314
6314
|
"icon": "bookmark",
|
|
6315
6315
|
"action": {
|
|
6316
6316
|
"actionId": "tier5.himiBrandedMenus.collectionSave",
|
|
@@ -6575,7 +6575,7 @@ const SCREENS = runtimeSetting({
|
|
|
6575
6575
|
},
|
|
6576
6576
|
{
|
|
6577
6577
|
"id": "save",
|
|
6578
|
-
"label": "
|
|
6578
|
+
"label": "$item.collectionSaveLabel",
|
|
6579
6579
|
"icon": "bookmark",
|
|
6580
6580
|
"action": {
|
|
6581
6581
|
"actionId": "tier5.himiBrandedMenus.collectionSave",
|
|
@@ -7180,7 +7180,7 @@ const SCREENS = runtimeSetting({
|
|
|
7180
7180
|
},
|
|
7181
7181
|
{
|
|
7182
7182
|
"id": "save",
|
|
7183
|
-
"label": "
|
|
7183
|
+
"label": "$item.collectionSaveLabel",
|
|
7184
7184
|
"icon": "bookmark",
|
|
7185
7185
|
"action": {
|
|
7186
7186
|
"actionId": "tier5.himiBrandedMenus.collectionSave",
|
|
@@ -9040,7 +9040,7 @@ const SCREENS = runtimeSetting({
|
|
|
9040
9040
|
},
|
|
9041
9041
|
{
|
|
9042
9042
|
"id": "save",
|
|
9043
|
-
"label": "
|
|
9043
|
+
"label": "$item.collectionSaveLabel",
|
|
9044
9044
|
"icon": "bookmark",
|
|
9045
9045
|
"action": {
|
|
9046
9046
|
"actionId": "tier5.himiEvents.collectionSave",
|
|
@@ -9112,7 +9112,7 @@ const SCREENS = runtimeSetting({
|
|
|
9112
9112
|
"rowSwipeActions": {
|
|
9113
9113
|
"trailing": [
|
|
9114
9114
|
{
|
|
9115
|
-
"label": "
|
|
9115
|
+
"label": "$item.collectionSaveLabel",
|
|
9116
9116
|
"icon": "bookmark",
|
|
9117
9117
|
"action": {
|
|
9118
9118
|
"actionId": "tier5.himiEvents.collectionSave",
|
|
@@ -9396,7 +9396,7 @@ const SCREENS = runtimeSetting({
|
|
|
9396
9396
|
},
|
|
9397
9397
|
{
|
|
9398
9398
|
"id": "save",
|
|
9399
|
-
"label": "
|
|
9399
|
+
"label": "$item.collectionSaveLabel",
|
|
9400
9400
|
"icon": "bookmark",
|
|
9401
9401
|
"action": {
|
|
9402
9402
|
"actionId": "tier5.himiEvents.collectionSave",
|
|
@@ -9715,7 +9715,7 @@ const SCREENS = runtimeSetting({
|
|
|
9715
9715
|
},
|
|
9716
9716
|
{
|
|
9717
9717
|
"id": "save",
|
|
9718
|
-
"label": "
|
|
9718
|
+
"label": "$item.collectionSaveLabel",
|
|
9719
9719
|
"icon": "bookmark",
|
|
9720
9720
|
"action": {
|
|
9721
9721
|
"actionId": "tier5.himiEvents.collectionSave",
|
|
@@ -9787,7 +9787,7 @@ const SCREENS = runtimeSetting({
|
|
|
9787
9787
|
"rowSwipeActions": {
|
|
9788
9788
|
"trailing": [
|
|
9789
9789
|
{
|
|
9790
|
-
"label": "
|
|
9790
|
+
"label": "$item.collectionSaveLabel",
|
|
9791
9791
|
"icon": "bookmark",
|
|
9792
9792
|
"action": {
|
|
9793
9793
|
"actionId": "tier5.himiEvents.collectionSave",
|
|
@@ -10071,7 +10071,7 @@ const SCREENS = runtimeSetting({
|
|
|
10071
10071
|
},
|
|
10072
10072
|
{
|
|
10073
10073
|
"id": "save",
|
|
10074
|
-
"label": "
|
|
10074
|
+
"label": "$item.collectionSaveLabel",
|
|
10075
10075
|
"icon": "bookmark",
|
|
10076
10076
|
"action": {
|
|
10077
10077
|
"actionId": "tier5.himiEvents.collectionSave",
|
|
@@ -10762,7 +10762,7 @@ const SCREENS = runtimeSetting({
|
|
|
10762
10762
|
},
|
|
10763
10763
|
{
|
|
10764
10764
|
"id": "save",
|
|
10765
|
-
"label": "
|
|
10765
|
+
"label": "$item.collectionSaveLabel",
|
|
10766
10766
|
"icon": "bookmark",
|
|
10767
10767
|
"action": {
|
|
10768
10768
|
"actionId": "tier5.himiEvents.collectionSave",
|
|
@@ -12308,7 +12308,7 @@ const SCREENS = runtimeSetting({
|
|
|
12308
12308
|
},
|
|
12309
12309
|
{
|
|
12310
12310
|
"id": "save",
|
|
12311
|
-
"label": "
|
|
12311
|
+
"label": "$item.collectionSaveLabel",
|
|
12312
12312
|
"icon": "bookmark",
|
|
12313
12313
|
"action": {
|
|
12314
12314
|
"actionId": "tier5.himiBlogFeed.collectionSave",
|
|
@@ -12390,7 +12390,7 @@ const SCREENS = runtimeSetting({
|
|
|
12390
12390
|
"rowSwipeActions": {
|
|
12391
12391
|
"trailing": [
|
|
12392
12392
|
{
|
|
12393
|
-
"label": "
|
|
12393
|
+
"label": "$item.collectionSaveLabel",
|
|
12394
12394
|
"icon": "bookmark",
|
|
12395
12395
|
"action": {
|
|
12396
12396
|
"actionId": "tier5.himiBlogFeed.collectionSave",
|
|
@@ -12655,7 +12655,7 @@ const SCREENS = runtimeSetting({
|
|
|
12655
12655
|
},
|
|
12656
12656
|
{
|
|
12657
12657
|
"id": "save",
|
|
12658
|
-
"label": "
|
|
12658
|
+
"label": "$item.collectionSaveLabel",
|
|
12659
12659
|
"icon": "bookmark",
|
|
12660
12660
|
"action": {
|
|
12661
12661
|
"actionId": "tier5.himiBlogFeed.collectionSave",
|
|
@@ -12945,7 +12945,7 @@ const SCREENS = runtimeSetting({
|
|
|
12945
12945
|
},
|
|
12946
12946
|
{
|
|
12947
12947
|
"id": "save",
|
|
12948
|
-
"label": "
|
|
12948
|
+
"label": "$item.collectionSaveLabel",
|
|
12949
12949
|
"icon": "bookmark",
|
|
12950
12950
|
"action": {
|
|
12951
12951
|
"actionId": "tier5.himiBlogFeed.collectionSave",
|
|
@@ -13027,7 +13027,7 @@ const SCREENS = runtimeSetting({
|
|
|
13027
13027
|
"rowSwipeActions": {
|
|
13028
13028
|
"trailing": [
|
|
13029
13029
|
{
|
|
13030
|
-
"label": "
|
|
13030
|
+
"label": "$item.collectionSaveLabel",
|
|
13031
13031
|
"icon": "bookmark",
|
|
13032
13032
|
"action": {
|
|
13033
13033
|
"actionId": "tier5.himiBlogFeed.collectionSave",
|
|
@@ -13292,7 +13292,7 @@ const SCREENS = runtimeSetting({
|
|
|
13292
13292
|
},
|
|
13293
13293
|
{
|
|
13294
13294
|
"id": "save",
|
|
13295
|
-
"label": "
|
|
13295
|
+
"label": "$item.collectionSaveLabel",
|
|
13296
13296
|
"icon": "bookmark",
|
|
13297
13297
|
"action": {
|
|
13298
13298
|
"actionId": "tier5.himiBlogFeed.collectionSave",
|
|
@@ -13933,7 +13933,7 @@ const SCREENS = runtimeSetting({
|
|
|
13933
13933
|
},
|
|
13934
13934
|
{
|
|
13935
13935
|
"id": "save",
|
|
13936
|
-
"label": "
|
|
13936
|
+
"label": "$item.collectionSaveLabel",
|
|
13937
13937
|
"icon": "bookmark",
|
|
13938
13938
|
"action": {
|
|
13939
13939
|
"actionId": "tier5.himiBlogFeed.collectionSave",
|
|
@@ -168,6 +168,8 @@ export function projectCollection(source: CollectionRow[], config: CollectionCon
|
|
|
168
168
|
items: visible.map(row => ({ ...row, collectionId: collectionId(row), collectionTitle: collectionTitle(row, config),
|
|
169
169
|
collectionMeta: [...new Set((config.metadata ?? ['total', 'statusLabel', 'price', 'priceLine', 'dateLine', 'meta']).filter(key => key !== config.subtitle).map(key => scalar(row[key])).filter(Boolean))].join(' · '),
|
|
170
170
|
collectionSelectLabel: prefs.selectedIds.includes(collectionId(row)) ? 'Selected' : 'Select',
|
|
171
|
+
// The save toggle names the action it WILL take (the service localizes it).
|
|
172
|
+
collectionSaveLabel: prefs.savedIds.includes(collectionId(row)) ? 'Remove from saved' : 'Save',
|
|
171
173
|
collectionSubtitle: scalar(row[config.subtitle ?? '']) || scalar(row.subtitle) || scalar(row.description) || scalar(row.meta),
|
|
172
174
|
collectionImage: scalar(row[config.image ?? '']), collectionNoImage: !scalar(row[config.image ?? '']), collectionHasImage: !!scalar(row[config.image ?? '']),
|
|
173
175
|
collectionSaved: prefs.savedIds.includes(collectionId(row)), collectionNotSaved: !prefs.savedIds.includes(collectionId(row)), collectionSelected: prefs.selectedIds.includes(collectionId(row)),
|