@wix/himalaya-cli 0.1036.0 → 0.1037.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/portfolio/src/index.d.ts +46 -9
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-portfolio.mjs +14 -2
- package/dist/cli.mjs +476 -109
- package/dist/preview-assets.generated.bin +0 -0
- package/fixtures/wix-canned.json +51 -2
- package/guides/sdk-reference.json +16 -10
- package/guides/wix-business-context.md +13 -1
- package/guides/wix-site-to-himi-app.md +10 -1
- package/package.json +2 -2
- package/templates/beauty-salon/tier5-src/himi-booking-bookings/index.ts +14 -8
- package/templates/beauty-salon/tier5-src/himi-branded-lite-home/index.ts +12 -1
- package/templates/beauty-salon-owner/tier5-src/_shared/owner-module-data.ts +2 -2
- package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-appointments/index.ts +4 -1
- package/templates/branded-lite/tier5-src/himi-booking-bookings/index.ts +14 -8
- package/templates/branded-lite/tier5-src/himi-branded-lite-home/index.ts +12 -1
- package/templates/branded-lite/tier5-src/himi-branded-lite-portfolio/index.ts +2 -2
- package/templates/branded-lite/tier5-src/himi-branded-lite-portfolio-project/index.ts +3 -3
- package/templates/community-club/tier5-src/himi-branded-lite-home/index.ts +12 -1
- package/templates/community-club-owner/tier5-src/_shared/owner-module-data.ts +2 -2
- package/templates/events-meetups/tier5-src/himi-branded-lite-home/index.ts +12 -1
- package/templates/events-meetups-owner/tier5-src/_shared/owner-module-data.ts +2 -2
- package/templates/gym-fitness/tier5-src/himi-booking-bookings/index.ts +14 -8
- package/templates/gym-fitness/tier5-src/himi-branded-lite-home/index.ts +12 -1
- package/templates/gym-fitness-owner/tier5-src/_shared/owner-module-data.ts +2 -2
- package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-appointments/index.ts +4 -1
- package/templates/nonprofit-cause/tier5-src/himi-branded-lite-home/index.ts +12 -1
- package/templates/nonprofit-cause-owner/tier5-src/_shared/owner-module-data.ts +2 -2
- package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-appointments/index.ts +4 -1
- package/templates/owner-lite/tier5-src/_shared/owner-module-data.ts +2 -2
- package/templates/owner-lite/tier5-src/himi-owner-lite-appointments/index.ts +4 -1
- package/templates/podcast-creator/tier5-src/himi-branded-lite-home/index.ts +12 -1
- package/templates/podcast-creator-owner/tier5-src/_shared/owner-module-data.ts +2 -2
- package/templates/portfolio-creative/tier5-src/himi-branded-lite-home/index.ts +12 -1
- package/templates/portfolio-creative/tier5-src/himi-branded-lite-portfolio/index.ts +2 -2
- package/templates/portfolio-creative/tier5-src/himi-branded-lite-portfolio-project/index.ts +3 -3
- package/templates/portfolio-creative-owner/tier5-src/_shared/owner-module-data.ts +2 -2
- package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-appointments/index.ts +4 -1
- package/templates/restaurant/tier5-src/himi-branded-lite-home/index.ts +12 -1
- package/templates/restaurant-owner/tier5-src/_shared/owner-module-data.ts +2 -2
- package/templates/restaurant-owner/tier5-src/himi-owner-lite-appointments/index.ts +4 -1
- package/templates/retail-store/tier5-src/himi-branded-lite-home/index.ts +12 -1
- package/templates/retail-store-owner/tier5-src/_shared/owner-module-data.ts +2 -2
- package/templates/yoga-studio/tier5-src/himi-booking-bookings/index.ts +14 -8
- package/templates/yoga-studio/tier5-src/himi-branded-lite-home/index.ts +12 -1
- package/templates/yoga-studio-owner/tier5-src/_shared/owner-module-data.ts +2 -2
- package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-appointments/index.ts +4 -1
|
@@ -388,6 +388,10 @@ function resolveEffectiveLayout(arg: unknown, productCount: number): LayoutId {
|
|
|
388
388
|
});
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
+
/** The time-of-day greeting. Read at REFRESH time only, never at boot: the release build hashes
|
|
392
|
+
* the initial state, so a greeting read off the build machine's clock made every build of an
|
|
393
|
+
* unchanged template a different release (#3268, the `nondeterministic-initial-state` lint).
|
|
394
|
+
* The initial state carries `neutralWelcome()` instead, and the first refresh swaps this in. */
|
|
391
395
|
function welcome(): string {
|
|
392
396
|
const hour = new Date().getHours();
|
|
393
397
|
if (hour < 12) return copyParam("home.greetingMorning", "Good morning");
|
|
@@ -395,6 +399,11 @@ function welcome(): string {
|
|
|
395
399
|
return copyParam("home.greetingEvening", "Good evening");
|
|
396
400
|
}
|
|
397
401
|
|
|
402
|
+
/** The boot-time greeting: clock-free, so the baked initial state is the same on every build. */
|
|
403
|
+
function neutralWelcome(): string {
|
|
404
|
+
return copyParam("home.greetingNeutral", "Welcome");
|
|
405
|
+
}
|
|
406
|
+
|
|
398
407
|
function feature(p: StoreProduct): ProductFeature {
|
|
399
408
|
const name = cleanTitle(p.name) || copyParam("home.productFallbackName", "Featured item");
|
|
400
409
|
const image = productImage(p);
|
|
@@ -871,7 +880,9 @@ function baseState(): State {
|
|
|
871
880
|
hideHeroInitial: true,
|
|
872
881
|
hideHeroFallback: true,
|
|
873
882
|
hideFeaturedSkeleton: false,
|
|
874
|
-
|
|
883
|
+
// Not welcome(): that reads the clock, and this state is baked into the release. A neutral
|
|
884
|
+
// greeting keeps the hero pill painted (same size, no reflow) until the first refresh.
|
|
885
|
+
welcome: neutralWelcome(),
|
|
875
886
|
refreshedAt: "",
|
|
876
887
|
effectiveLayout: resolveEffectiveLayout(undefined, 0),
|
|
877
888
|
accentColor: resolveAccent(undefined),
|
|
@@ -8,7 +8,7 @@ import { queryPrograms, getProgram } from '@himalaya/wix-online-programs';
|
|
|
8
8
|
import { queryReviews, getReview } from '@himalaya/wix-reviews';
|
|
9
9
|
import { queryReservations, getReservation } from '@himalaya/wix-table-reservations';
|
|
10
10
|
import { queryForms, getForm } from '@himalaya/wix-forms';
|
|
11
|
-
import { listProjects, listProjectItems } from '@himalaya/wix-portfolio';
|
|
11
|
+
import { listProjects, listProjectItems, portfolioCoverRef, type WixMediaImage } from '@himalaya/wix-portfolio';
|
|
12
12
|
import { queryGiftCards, getGiftCard } from '@himalaya/wix-gift-cards';
|
|
13
13
|
import { queryDonationCampaigns, getDonationCampaign } from '@himalaya/wix-donations';
|
|
14
14
|
import { query as queryBadges, get as getBadge } from '@himalaya/wix-badges';
|
|
@@ -208,7 +208,7 @@ export function ownerRecord(module: OwnerModuleId, raw: unknown): OwnerRecord {
|
|
|
208
208
|
details = [['Namespace', subtitle]];
|
|
209
209
|
break;
|
|
210
210
|
case 'portfolio':
|
|
211
|
-
title = field(raw, 'title'); subtitle = field(raw, 'description'); image =
|
|
211
|
+
title = field(raw, 'title'); subtitle = field(raw, 'description'); image = portfolioCoverRef(at(raw, 'coverImage') as WixMediaImage | string | undefined);
|
|
212
212
|
statusLabel = typeof at(raw, 'hidden') === 'boolean' ? at(raw, 'hidden') ? 'Hidden' : 'Visible' : '';
|
|
213
213
|
break;
|
|
214
214
|
case 'donationCampaigns':
|
|
@@ -311,7 +311,10 @@ function shape(s: Partial<AppointmentsState>): AppointmentsState {
|
|
|
311
311
|
// Until the owner picks a day, follow the soonest booked one, so a freshly
|
|
312
312
|
// loaded month never opens on an empty day list.
|
|
313
313
|
const soonest = all.find((r) => r._day)?._day ?? "";
|
|
314
|
-
|
|
314
|
+
// Today only once the list is READY (the month is hidden until then): the initial state is baked
|
|
315
|
+
// into the release, and a day read off the build machine's clock made every build of unchanged
|
|
316
|
+
// source a different release (#3268, the `nondeterministic-initial-state` lint).
|
|
317
|
+
const focus = (s._picked ? s.selectedDate : "") || soonest || (status === "ready" ? dayKey(new Date()) : "");
|
|
315
318
|
const dayAppointments = all.filter((r) => r._day === focus);
|
|
316
319
|
const dayEvents = eventsOf(dayAppointments);
|
|
317
320
|
const ready = status === "ready";
|
|
@@ -388,6 +388,10 @@ function resolveEffectiveLayout(arg: unknown, productCount: number): LayoutId {
|
|
|
388
388
|
});
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
+
/** The time-of-day greeting. Read at REFRESH time only, never at boot: the release build hashes
|
|
392
|
+
* the initial state, so a greeting read off the build machine's clock made every build of an
|
|
393
|
+
* unchanged template a different release (#3268, the `nondeterministic-initial-state` lint).
|
|
394
|
+
* The initial state carries `neutralWelcome()` instead, and the first refresh swaps this in. */
|
|
391
395
|
function welcome(): string {
|
|
392
396
|
const hour = new Date().getHours();
|
|
393
397
|
if (hour < 12) return copyParam("home.greetingMorning", "Good morning");
|
|
@@ -395,6 +399,11 @@ function welcome(): string {
|
|
|
395
399
|
return copyParam("home.greetingEvening", "Good evening");
|
|
396
400
|
}
|
|
397
401
|
|
|
402
|
+
/** The boot-time greeting: clock-free, so the baked initial state is the same on every build. */
|
|
403
|
+
function neutralWelcome(): string {
|
|
404
|
+
return copyParam("home.greetingNeutral", "Welcome");
|
|
405
|
+
}
|
|
406
|
+
|
|
398
407
|
function feature(p: StoreProduct): ProductFeature {
|
|
399
408
|
const name = cleanTitle(p.name) || copyParam("home.productFallbackName", "Featured item");
|
|
400
409
|
const image = productImage(p);
|
|
@@ -871,7 +880,9 @@ function baseState(): State {
|
|
|
871
880
|
hideHeroInitial: true,
|
|
872
881
|
hideHeroFallback: true,
|
|
873
882
|
hideFeaturedSkeleton: false,
|
|
874
|
-
|
|
883
|
+
// Not welcome(): that reads the clock, and this state is baked into the release. A neutral
|
|
884
|
+
// greeting keeps the hero pill painted (same size, no reflow) until the first refresh.
|
|
885
|
+
welcome: neutralWelcome(),
|
|
875
886
|
refreshedAt: "",
|
|
876
887
|
effectiveLayout: resolveEffectiveLayout(undefined, 0),
|
|
877
888
|
accentColor: resolveAccent(undefined),
|
|
@@ -8,7 +8,7 @@ import { queryPrograms, getProgram } from '@himalaya/wix-online-programs';
|
|
|
8
8
|
import { queryReviews, getReview } from '@himalaya/wix-reviews';
|
|
9
9
|
import { queryReservations, getReservation } from '@himalaya/wix-table-reservations';
|
|
10
10
|
import { queryForms, getForm } from '@himalaya/wix-forms';
|
|
11
|
-
import { listProjects, listProjectItems } from '@himalaya/wix-portfolio';
|
|
11
|
+
import { listProjects, listProjectItems, portfolioCoverRef, type WixMediaImage } from '@himalaya/wix-portfolio';
|
|
12
12
|
import { queryGiftCards, getGiftCard } from '@himalaya/wix-gift-cards';
|
|
13
13
|
import { queryDonationCampaigns, getDonationCampaign } from '@himalaya/wix-donations';
|
|
14
14
|
import { query as queryBadges, get as getBadge } from '@himalaya/wix-badges';
|
|
@@ -208,7 +208,7 @@ export function ownerRecord(module: OwnerModuleId, raw: unknown): OwnerRecord {
|
|
|
208
208
|
details = [['Namespace', subtitle]];
|
|
209
209
|
break;
|
|
210
210
|
case 'portfolio':
|
|
211
|
-
title = field(raw, 'title'); subtitle = field(raw, 'description'); image =
|
|
211
|
+
title = field(raw, 'title'); subtitle = field(raw, 'description'); image = portfolioCoverRef(at(raw, 'coverImage') as WixMediaImage | string | undefined);
|
|
212
212
|
statusLabel = typeof at(raw, 'hidden') === 'boolean' ? at(raw, 'hidden') ? 'Hidden' : 'Visible' : '';
|
|
213
213
|
break;
|
|
214
214
|
case 'donationCampaigns':
|
|
@@ -311,7 +311,10 @@ function shape(s: Partial<AppointmentsState>): AppointmentsState {
|
|
|
311
311
|
// Until the owner picks a day, follow the soonest booked one, so a freshly
|
|
312
312
|
// loaded month never opens on an empty day list.
|
|
313
313
|
const soonest = all.find((r) => r._day)?._day ?? "";
|
|
314
|
-
|
|
314
|
+
// Today only once the list is READY (the month is hidden until then): the initial state is baked
|
|
315
|
+
// into the release, and a day read off the build machine's clock made every build of unchanged
|
|
316
|
+
// source a different release (#3268, the `nondeterministic-initial-state` lint).
|
|
317
|
+
const focus = (s._picked ? s.selectedDate : "") || soonest || (status === "ready" ? dayKey(new Date()) : "");
|
|
315
318
|
const dayAppointments = all.filter((r) => r._day === focus);
|
|
316
319
|
const dayEvents = eventsOf(dayAppointments);
|
|
317
320
|
const ready = status === "ready";
|
|
@@ -8,7 +8,7 @@ import { queryPrograms, getProgram } from '@himalaya/wix-online-programs';
|
|
|
8
8
|
import { queryReviews, getReview } from '@himalaya/wix-reviews';
|
|
9
9
|
import { queryReservations, getReservation } from '@himalaya/wix-table-reservations';
|
|
10
10
|
import { queryForms, getForm } from '@himalaya/wix-forms';
|
|
11
|
-
import { listProjects, listProjectItems } from '@himalaya/wix-portfolio';
|
|
11
|
+
import { listProjects, listProjectItems, portfolioCoverRef, type WixMediaImage } from '@himalaya/wix-portfolio';
|
|
12
12
|
import { queryGiftCards, getGiftCard } from '@himalaya/wix-gift-cards';
|
|
13
13
|
import { queryDonationCampaigns, getDonationCampaign } from '@himalaya/wix-donations';
|
|
14
14
|
import { query as queryBadges, get as getBadge } from '@himalaya/wix-badges';
|
|
@@ -208,7 +208,7 @@ export function ownerRecord(module: OwnerModuleId, raw: unknown): OwnerRecord {
|
|
|
208
208
|
details = [['Namespace', subtitle]];
|
|
209
209
|
break;
|
|
210
210
|
case 'portfolio':
|
|
211
|
-
title = field(raw, 'title'); subtitle = field(raw, 'description'); image =
|
|
211
|
+
title = field(raw, 'title'); subtitle = field(raw, 'description'); image = portfolioCoverRef(at(raw, 'coverImage') as WixMediaImage | string | undefined);
|
|
212
212
|
statusLabel = typeof at(raw, 'hidden') === 'boolean' ? at(raw, 'hidden') ? 'Hidden' : 'Visible' : '';
|
|
213
213
|
break;
|
|
214
214
|
case 'donationCampaigns':
|
|
@@ -311,7 +311,10 @@ function shape(s: Partial<AppointmentsState>): AppointmentsState {
|
|
|
311
311
|
// Until the owner picks a day, follow the soonest booked one, so a freshly
|
|
312
312
|
// loaded month never opens on an empty day list.
|
|
313
313
|
const soonest = all.find((r) => r._day)?._day ?? "";
|
|
314
|
-
|
|
314
|
+
// Today only once the list is READY (the month is hidden until then): the initial state is baked
|
|
315
|
+
// into the release, and a day read off the build machine's clock made every build of unchanged
|
|
316
|
+
// source a different release (#3268, the `nondeterministic-initial-state` lint).
|
|
317
|
+
const focus = (s._picked ? s.selectedDate : "") || soonest || (status === "ready" ? dayKey(new Date()) : "");
|
|
315
318
|
const dayAppointments = all.filter((r) => r._day === focus);
|
|
316
319
|
const dayEvents = eventsOf(dayAppointments);
|
|
317
320
|
const ready = status === "ready";
|
|
@@ -388,6 +388,10 @@ function resolveEffectiveLayout(arg: unknown, productCount: number): LayoutId {
|
|
|
388
388
|
});
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
+
/** The time-of-day greeting. Read at REFRESH time only, never at boot: the release build hashes
|
|
392
|
+
* the initial state, so a greeting read off the build machine's clock made every build of an
|
|
393
|
+
* unchanged template a different release (#3268, the `nondeterministic-initial-state` lint).
|
|
394
|
+
* The initial state carries `neutralWelcome()` instead, and the first refresh swaps this in. */
|
|
391
395
|
function welcome(): string {
|
|
392
396
|
const hour = new Date().getHours();
|
|
393
397
|
if (hour < 12) return copyParam("home.greetingMorning", "Good morning");
|
|
@@ -395,6 +399,11 @@ function welcome(): string {
|
|
|
395
399
|
return copyParam("home.greetingEvening", "Good evening");
|
|
396
400
|
}
|
|
397
401
|
|
|
402
|
+
/** The boot-time greeting: clock-free, so the baked initial state is the same on every build. */
|
|
403
|
+
function neutralWelcome(): string {
|
|
404
|
+
return copyParam("home.greetingNeutral", "Welcome");
|
|
405
|
+
}
|
|
406
|
+
|
|
398
407
|
function feature(p: StoreProduct): ProductFeature {
|
|
399
408
|
const name = cleanTitle(p.name) || copyParam("home.productFallbackName", "Featured item");
|
|
400
409
|
const image = productImage(p);
|
|
@@ -871,7 +880,9 @@ function baseState(): State {
|
|
|
871
880
|
hideHeroInitial: true,
|
|
872
881
|
hideHeroFallback: true,
|
|
873
882
|
hideFeaturedSkeleton: false,
|
|
874
|
-
|
|
883
|
+
// Not welcome(): that reads the clock, and this state is baked into the release. A neutral
|
|
884
|
+
// greeting keeps the hero pill painted (same size, no reflow) until the first refresh.
|
|
885
|
+
welcome: neutralWelcome(),
|
|
875
886
|
refreshedAt: "",
|
|
876
887
|
effectiveLayout: resolveEffectiveLayout(undefined, 0),
|
|
877
888
|
accentColor: resolveAccent(undefined),
|
|
@@ -8,7 +8,7 @@ import { queryPrograms, getProgram } from '@himalaya/wix-online-programs';
|
|
|
8
8
|
import { queryReviews, getReview } from '@himalaya/wix-reviews';
|
|
9
9
|
import { queryReservations, getReservation } from '@himalaya/wix-table-reservations';
|
|
10
10
|
import { queryForms, getForm } from '@himalaya/wix-forms';
|
|
11
|
-
import { listProjects, listProjectItems } from '@himalaya/wix-portfolio';
|
|
11
|
+
import { listProjects, listProjectItems, portfolioCoverRef, type WixMediaImage } from '@himalaya/wix-portfolio';
|
|
12
12
|
import { queryGiftCards, getGiftCard } from '@himalaya/wix-gift-cards';
|
|
13
13
|
import { queryDonationCampaigns, getDonationCampaign } from '@himalaya/wix-donations';
|
|
14
14
|
import { query as queryBadges, get as getBadge } from '@himalaya/wix-badges';
|
|
@@ -208,7 +208,7 @@ export function ownerRecord(module: OwnerModuleId, raw: unknown): OwnerRecord {
|
|
|
208
208
|
details = [['Namespace', subtitle]];
|
|
209
209
|
break;
|
|
210
210
|
case 'portfolio':
|
|
211
|
-
title = field(raw, 'title'); subtitle = field(raw, 'description'); image =
|
|
211
|
+
title = field(raw, 'title'); subtitle = field(raw, 'description'); image = portfolioCoverRef(at(raw, 'coverImage') as WixMediaImage | string | undefined);
|
|
212
212
|
statusLabel = typeof at(raw, 'hidden') === 'boolean' ? at(raw, 'hidden') ? 'Hidden' : 'Visible' : '';
|
|
213
213
|
break;
|
|
214
214
|
case 'donationCampaigns':
|
|
@@ -388,6 +388,10 @@ function resolveEffectiveLayout(arg: unknown, productCount: number): LayoutId {
|
|
|
388
388
|
});
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
+
/** The time-of-day greeting. Read at REFRESH time only, never at boot: the release build hashes
|
|
392
|
+
* the initial state, so a greeting read off the build machine's clock made every build of an
|
|
393
|
+
* unchanged template a different release (#3268, the `nondeterministic-initial-state` lint).
|
|
394
|
+
* The initial state carries `neutralWelcome()` instead, and the first refresh swaps this in. */
|
|
391
395
|
function welcome(): string {
|
|
392
396
|
const hour = new Date().getHours();
|
|
393
397
|
if (hour < 12) return copyParam("home.greetingMorning", "Good morning");
|
|
@@ -395,6 +399,11 @@ function welcome(): string {
|
|
|
395
399
|
return copyParam("home.greetingEvening", "Good evening");
|
|
396
400
|
}
|
|
397
401
|
|
|
402
|
+
/** The boot-time greeting: clock-free, so the baked initial state is the same on every build. */
|
|
403
|
+
function neutralWelcome(): string {
|
|
404
|
+
return copyParam("home.greetingNeutral", "Welcome");
|
|
405
|
+
}
|
|
406
|
+
|
|
398
407
|
function feature(p: StoreProduct): ProductFeature {
|
|
399
408
|
const name = cleanTitle(p.name) || copyParam("home.productFallbackName", "Featured item");
|
|
400
409
|
const image = productImage(p);
|
|
@@ -871,7 +880,9 @@ function baseState(): State {
|
|
|
871
880
|
hideHeroInitial: true,
|
|
872
881
|
hideHeroFallback: true,
|
|
873
882
|
hideFeaturedSkeleton: false,
|
|
874
|
-
|
|
883
|
+
// Not welcome(): that reads the clock, and this state is baked into the release. A neutral
|
|
884
|
+
// greeting keeps the hero pill painted (same size, no reflow) until the first refresh.
|
|
885
|
+
welcome: neutralWelcome(),
|
|
875
886
|
refreshedAt: "",
|
|
876
887
|
effectiveLayout: resolveEffectiveLayout(undefined, 0),
|
|
877
888
|
accentColor: resolveAccent(undefined),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// himi-branded-lite-portfolio — real, public Wix Portfolio projects.
|
|
2
2
|
// Empty and error states stay explicit; media IDs are normalized for every host.
|
|
3
3
|
import { defineService } from "../_shared/collection-service.js";
|
|
4
|
-
import { listProjects, type WixPortfolioProject } from "@himalaya/wix-portfolio";
|
|
4
|
+
import { listProjects, portfolioCoverRef, type WixPortfolioProject } from "@himalaya/wix-portfolio";
|
|
5
5
|
import { wixMediaUrl } from "@himalaya/wix-media";
|
|
6
6
|
|
|
7
7
|
interface Row {
|
|
@@ -24,7 +24,7 @@ interface S {
|
|
|
24
24
|
|
|
25
25
|
function norm(project: WixPortfolioProject): Row {
|
|
26
26
|
const title = (project.title || "Project").trim() || "Project";
|
|
27
|
-
const image = wixMediaUrl(project.coverImage
|
|
27
|
+
const image = wixMediaUrl(portfolioCoverRef(project.coverImage));
|
|
28
28
|
return {
|
|
29
29
|
id: project.id || project.slug || title,
|
|
30
30
|
title,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// The public project row is resolved again from Wix, then its ordered project
|
|
3
3
|
// items supply the actual image/video/text story. No sample project is invented.
|
|
4
4
|
import { defineService } from "@himalaya/state";
|
|
5
|
-
import { listProjectItems, listProjects, type WixPortfolioProjectItem } from "@himalaya/wix-portfolio";
|
|
5
|
+
import { listProjectItems, listProjects, portfolioCoverRef, type WixPortfolioProjectItem } from "@himalaya/wix-portfolio";
|
|
6
6
|
import { wixMediaUrl, WIX_DETAIL_IMAGE } from "@himalaya/wix-media";
|
|
7
7
|
import { copyParam, stashCopy } from "../_shared/himi-copy.js";
|
|
8
8
|
import { presence, type PresenceFlags } from "../_shared/detail-presence.js";
|
|
@@ -48,7 +48,7 @@ function inputId(): string {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
function imageOf(item: WixPortfolioProjectItem): string {
|
|
51
|
-
return wixMediaUrl(item.image
|
|
51
|
+
return wixMediaUrl(portfolioCoverRef(item.image), WIX_DETAIL_IMAGE);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
function row(item: WixPortfolioProjectItem, index: number): MediaRow {
|
|
@@ -133,7 +133,7 @@ const svc = defineService<State, { load: (state: State, arg?: unknown) => Promis
|
|
|
133
133
|
title: (project.title || copyParam("portfolioProject.projectFallbackName", "Project")).trim(),
|
|
134
134
|
initial: ((project.title || "P").trim()[0] || "P").toUpperCase(),
|
|
135
135
|
description: (project.description ?? "").trim(),
|
|
136
|
-
coverImage: wixMediaUrl(project.coverImage
|
|
136
|
+
coverImage: wixMediaUrl(portfolioCoverRef(project.coverImage), WIX_DETAIL_IMAGE),
|
|
137
137
|
items,
|
|
138
138
|
emptyText: items.length ? "" : copyParam("portfolioProject.noMediaText", "This project has no published media yet."),
|
|
139
139
|
...presence("found"),
|
|
@@ -8,7 +8,7 @@ import { queryPrograms, getProgram } from '@himalaya/wix-online-programs';
|
|
|
8
8
|
import { queryReviews, getReview } from '@himalaya/wix-reviews';
|
|
9
9
|
import { queryReservations, getReservation } from '@himalaya/wix-table-reservations';
|
|
10
10
|
import { queryForms, getForm } from '@himalaya/wix-forms';
|
|
11
|
-
import { listProjects, listProjectItems } from '@himalaya/wix-portfolio';
|
|
11
|
+
import { listProjects, listProjectItems, portfolioCoverRef, type WixMediaImage } from '@himalaya/wix-portfolio';
|
|
12
12
|
import { queryGiftCards, getGiftCard } from '@himalaya/wix-gift-cards';
|
|
13
13
|
import { queryDonationCampaigns, getDonationCampaign } from '@himalaya/wix-donations';
|
|
14
14
|
import { query as queryBadges, get as getBadge } from '@himalaya/wix-badges';
|
|
@@ -208,7 +208,7 @@ export function ownerRecord(module: OwnerModuleId, raw: unknown): OwnerRecord {
|
|
|
208
208
|
details = [['Namespace', subtitle]];
|
|
209
209
|
break;
|
|
210
210
|
case 'portfolio':
|
|
211
|
-
title = field(raw, 'title'); subtitle = field(raw, 'description'); image =
|
|
211
|
+
title = field(raw, 'title'); subtitle = field(raw, 'description'); image = portfolioCoverRef(at(raw, 'coverImage') as WixMediaImage | string | undefined);
|
|
212
212
|
statusLabel = typeof at(raw, 'hidden') === 'boolean' ? at(raw, 'hidden') ? 'Hidden' : 'Visible' : '';
|
|
213
213
|
break;
|
|
214
214
|
case 'donationCampaigns':
|
|
@@ -311,7 +311,10 @@ function shape(s: Partial<AppointmentsState>): AppointmentsState {
|
|
|
311
311
|
// Until the owner picks a day, follow the soonest booked one, so a freshly
|
|
312
312
|
// loaded month never opens on an empty day list.
|
|
313
313
|
const soonest = all.find((r) => r._day)?._day ?? "";
|
|
314
|
-
|
|
314
|
+
// Today only once the list is READY (the month is hidden until then): the initial state is baked
|
|
315
|
+
// into the release, and a day read off the build machine's clock made every build of unchanged
|
|
316
|
+
// source a different release (#3268, the `nondeterministic-initial-state` lint).
|
|
317
|
+
const focus = (s._picked ? s.selectedDate : "") || soonest || (status === "ready" ? dayKey(new Date()) : "");
|
|
315
318
|
const dayAppointments = all.filter((r) => r._day === focus);
|
|
316
319
|
const dayEvents = eventsOf(dayAppointments);
|
|
317
320
|
const ready = status === "ready";
|
|
@@ -388,6 +388,10 @@ function resolveEffectiveLayout(arg: unknown, productCount: number): LayoutId {
|
|
|
388
388
|
});
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
+
/** The time-of-day greeting. Read at REFRESH time only, never at boot: the release build hashes
|
|
392
|
+
* the initial state, so a greeting read off the build machine's clock made every build of an
|
|
393
|
+
* unchanged template a different release (#3268, the `nondeterministic-initial-state` lint).
|
|
394
|
+
* The initial state carries `neutralWelcome()` instead, and the first refresh swaps this in. */
|
|
391
395
|
function welcome(): string {
|
|
392
396
|
const hour = new Date().getHours();
|
|
393
397
|
if (hour < 12) return copyParam("home.greetingMorning", "Good morning");
|
|
@@ -395,6 +399,11 @@ function welcome(): string {
|
|
|
395
399
|
return copyParam("home.greetingEvening", "Good evening");
|
|
396
400
|
}
|
|
397
401
|
|
|
402
|
+
/** The boot-time greeting: clock-free, so the baked initial state is the same on every build. */
|
|
403
|
+
function neutralWelcome(): string {
|
|
404
|
+
return copyParam("home.greetingNeutral", "Welcome");
|
|
405
|
+
}
|
|
406
|
+
|
|
398
407
|
function feature(p: StoreProduct): ProductFeature {
|
|
399
408
|
const name = cleanTitle(p.name) || copyParam("home.productFallbackName", "Featured item");
|
|
400
409
|
const image = productImage(p);
|
|
@@ -871,7 +880,9 @@ function baseState(): State {
|
|
|
871
880
|
hideHeroInitial: true,
|
|
872
881
|
hideHeroFallback: true,
|
|
873
882
|
hideFeaturedSkeleton: false,
|
|
874
|
-
|
|
883
|
+
// Not welcome(): that reads the clock, and this state is baked into the release. A neutral
|
|
884
|
+
// greeting keeps the hero pill painted (same size, no reflow) until the first refresh.
|
|
885
|
+
welcome: neutralWelcome(),
|
|
875
886
|
refreshedAt: "",
|
|
876
887
|
effectiveLayout: resolveEffectiveLayout(undefined, 0),
|
|
877
888
|
accentColor: resolveAccent(undefined),
|
|
@@ -8,7 +8,7 @@ import { queryPrograms, getProgram } from '@himalaya/wix-online-programs';
|
|
|
8
8
|
import { queryReviews, getReview } from '@himalaya/wix-reviews';
|
|
9
9
|
import { queryReservations, getReservation } from '@himalaya/wix-table-reservations';
|
|
10
10
|
import { queryForms, getForm } from '@himalaya/wix-forms';
|
|
11
|
-
import { listProjects, listProjectItems } from '@himalaya/wix-portfolio';
|
|
11
|
+
import { listProjects, listProjectItems, portfolioCoverRef, type WixMediaImage } from '@himalaya/wix-portfolio';
|
|
12
12
|
import { queryGiftCards, getGiftCard } from '@himalaya/wix-gift-cards';
|
|
13
13
|
import { queryDonationCampaigns, getDonationCampaign } from '@himalaya/wix-donations';
|
|
14
14
|
import { query as queryBadges, get as getBadge } from '@himalaya/wix-badges';
|
|
@@ -208,7 +208,7 @@ export function ownerRecord(module: OwnerModuleId, raw: unknown): OwnerRecord {
|
|
|
208
208
|
details = [['Namespace', subtitle]];
|
|
209
209
|
break;
|
|
210
210
|
case 'portfolio':
|
|
211
|
-
title = field(raw, 'title'); subtitle = field(raw, 'description'); image =
|
|
211
|
+
title = field(raw, 'title'); subtitle = field(raw, 'description'); image = portfolioCoverRef(at(raw, 'coverImage') as WixMediaImage | string | undefined);
|
|
212
212
|
statusLabel = typeof at(raw, 'hidden') === 'boolean' ? at(raw, 'hidden') ? 'Hidden' : 'Visible' : '';
|
|
213
213
|
break;
|
|
214
214
|
case 'donationCampaigns':
|
|
@@ -311,7 +311,10 @@ function shape(s: Partial<AppointmentsState>): AppointmentsState {
|
|
|
311
311
|
// Until the owner picks a day, follow the soonest booked one, so a freshly
|
|
312
312
|
// loaded month never opens on an empty day list.
|
|
313
313
|
const soonest = all.find((r) => r._day)?._day ?? "";
|
|
314
|
-
|
|
314
|
+
// Today only once the list is READY (the month is hidden until then): the initial state is baked
|
|
315
|
+
// into the release, and a day read off the build machine's clock made every build of unchanged
|
|
316
|
+
// source a different release (#3268, the `nondeterministic-initial-state` lint).
|
|
317
|
+
const focus = (s._picked ? s.selectedDate : "") || soonest || (status === "ready" ? dayKey(new Date()) : "");
|
|
315
318
|
const dayAppointments = all.filter((r) => r._day === focus);
|
|
316
319
|
const dayEvents = eventsOf(dayAppointments);
|
|
317
320
|
const ready = status === "ready";
|
|
@@ -388,6 +388,10 @@ function resolveEffectiveLayout(arg: unknown, productCount: number): LayoutId {
|
|
|
388
388
|
});
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
+
/** The time-of-day greeting. Read at REFRESH time only, never at boot: the release build hashes
|
|
392
|
+
* the initial state, so a greeting read off the build machine's clock made every build of an
|
|
393
|
+
* unchanged template a different release (#3268, the `nondeterministic-initial-state` lint).
|
|
394
|
+
* The initial state carries `neutralWelcome()` instead, and the first refresh swaps this in. */
|
|
391
395
|
function welcome(): string {
|
|
392
396
|
const hour = new Date().getHours();
|
|
393
397
|
if (hour < 12) return copyParam("home.greetingMorning", "Good morning");
|
|
@@ -395,6 +399,11 @@ function welcome(): string {
|
|
|
395
399
|
return copyParam("home.greetingEvening", "Good evening");
|
|
396
400
|
}
|
|
397
401
|
|
|
402
|
+
/** The boot-time greeting: clock-free, so the baked initial state is the same on every build. */
|
|
403
|
+
function neutralWelcome(): string {
|
|
404
|
+
return copyParam("home.greetingNeutral", "Welcome");
|
|
405
|
+
}
|
|
406
|
+
|
|
398
407
|
function feature(p: StoreProduct): ProductFeature {
|
|
399
408
|
const name = cleanTitle(p.name) || copyParam("home.productFallbackName", "Featured item");
|
|
400
409
|
const image = productImage(p);
|
|
@@ -871,7 +880,9 @@ function baseState(): State {
|
|
|
871
880
|
hideHeroInitial: true,
|
|
872
881
|
hideHeroFallback: true,
|
|
873
882
|
hideFeaturedSkeleton: false,
|
|
874
|
-
|
|
883
|
+
// Not welcome(): that reads the clock, and this state is baked into the release. A neutral
|
|
884
|
+
// greeting keeps the hero pill painted (same size, no reflow) until the first refresh.
|
|
885
|
+
welcome: neutralWelcome(),
|
|
875
886
|
refreshedAt: "",
|
|
876
887
|
effectiveLayout: resolveEffectiveLayout(undefined, 0),
|
|
877
888
|
accentColor: resolveAccent(undefined),
|
|
@@ -8,7 +8,7 @@ import { queryPrograms, getProgram } from '@himalaya/wix-online-programs';
|
|
|
8
8
|
import { queryReviews, getReview } from '@himalaya/wix-reviews';
|
|
9
9
|
import { queryReservations, getReservation } from '@himalaya/wix-table-reservations';
|
|
10
10
|
import { queryForms, getForm } from '@himalaya/wix-forms';
|
|
11
|
-
import { listProjects, listProjectItems } from '@himalaya/wix-portfolio';
|
|
11
|
+
import { listProjects, listProjectItems, portfolioCoverRef, type WixMediaImage } from '@himalaya/wix-portfolio';
|
|
12
12
|
import { queryGiftCards, getGiftCard } from '@himalaya/wix-gift-cards';
|
|
13
13
|
import { queryDonationCampaigns, getDonationCampaign } from '@himalaya/wix-donations';
|
|
14
14
|
import { query as queryBadges, get as getBadge } from '@himalaya/wix-badges';
|
|
@@ -208,7 +208,7 @@ export function ownerRecord(module: OwnerModuleId, raw: unknown): OwnerRecord {
|
|
|
208
208
|
details = [['Namespace', subtitle]];
|
|
209
209
|
break;
|
|
210
210
|
case 'portfolio':
|
|
211
|
-
title = field(raw, 'title'); subtitle = field(raw, 'description'); image =
|
|
211
|
+
title = field(raw, 'title'); subtitle = field(raw, 'description'); image = portfolioCoverRef(at(raw, 'coverImage') as WixMediaImage | string | undefined);
|
|
212
212
|
statusLabel = typeof at(raw, 'hidden') === 'boolean' ? at(raw, 'hidden') ? 'Hidden' : 'Visible' : '';
|
|
213
213
|
break;
|
|
214
214
|
case 'donationCampaigns':
|
|
@@ -11,9 +11,14 @@ const input = (globalThis as { himalaya?: { input?: Record<string, unknown> } })
|
|
|
11
11
|
const validDate = (value: unknown): value is string => typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value) && dayKey(new Date(`${value}T12:00:00`)) === value;
|
|
12
12
|
const views = localPreferences("booking.schedule", value => ["agenda", "day", "month"].includes(String(value)) ? String(value) : "agenda");
|
|
13
13
|
let mode = validDate(input?.date) ? "day" : views.read();
|
|
14
|
-
|
|
14
|
+
// "" until the schedule is first shown, NOT today's date: the release build hashes this worker's
|
|
15
|
+
// initial state, so a date read off the build machine's clock made every build of unchanged
|
|
16
|
+
// source a different release (#3268, the `nondeterministic-initial-state` lint). `derive()` fills
|
|
17
|
+
// in today the first time the schedule is visible, which is never at build time (no member).
|
|
18
|
+
let selectedDate = validDate(input?.date) ? input.date : "";
|
|
19
|
+
const today = () => dayKey(new Date());
|
|
15
20
|
let pageMember: MemberAccess | undefined;
|
|
16
|
-
let queryNow =
|
|
21
|
+
let queryNow = ""; // set by the first-page fetch, never at boot (see selectedDate)
|
|
17
22
|
let cancelTarget: { row: MemberBookingRow; member: MemberAccess } | undefined;
|
|
18
23
|
let cancelling = false;
|
|
19
24
|
interface S {
|
|
@@ -26,11 +31,12 @@ interface S {
|
|
|
26
31
|
nextCursor?: string; complete?: boolean; loadingMore?: boolean; pageError?: boolean; total?: number;
|
|
27
32
|
}
|
|
28
33
|
function derive(s: S): S {
|
|
29
|
-
const
|
|
34
|
+
const hideSchedule = !s.isMember || s.isLoading;
|
|
35
|
+
if (!selectedDate && !hideSchedule) selectedDate = today();
|
|
36
|
+
const dayRows = selectedDate ? s.rows.filter(row => row.day === selectedDate) : [];
|
|
30
37
|
const counts = new Map<string, number>();
|
|
31
38
|
for (const row of s.rows) if (row.day) counts.set(row.day, (counts.get(row.day) ?? 0) + 1);
|
|
32
|
-
|
|
33
|
-
return { ...s, scheduleMode: mode, selectedDate, dayRows, dayHeading: dayLabel(selectedDate), hideSchedule,
|
|
39
|
+
return { ...s, scheduleMode: mode, selectedDate, dayRows, dayHeading: selectedDate ? dayLabel(selectedDate) : "", hideSchedule,
|
|
34
40
|
hideAgenda: hideSchedule || mode !== "agenda", hideCalendar: hideSchedule || mode === "agenda", hideMonth: mode !== "month",
|
|
35
41
|
decorations: [...counts].map(([date, n]) => ({ date, dotCount: Math.min(n, 3), dotColorToken: "accentPrimary" })),
|
|
36
42
|
dayEmptyText: dayRows.length ? "" : s.complete ? copyParam("bookings.emptyDay", "No bookings on this day.") : copyParam("bookings.emptyLoadedDay", "No bookings on this day in loaded results. Load more to check later pages."),
|
|
@@ -48,7 +54,7 @@ const svc = defineBackendCollection<S, MemberBookingRow>("himiBookingBookings",
|
|
|
48
54
|
async fetch(criteria, cursor) {
|
|
49
55
|
const member = await memberAccess();
|
|
50
56
|
if (cursor && (!pageMember || pageMember.email !== member.email || pageMember.scope !== member.scope)) throw memberAccessChanged();
|
|
51
|
-
if (!cursor) queryNow = new Date().toISOString();
|
|
57
|
+
if (!cursor || !queryNow) queryNow = new Date().toISOString();
|
|
52
58
|
return fetchMemberBookings(criteria, cursor, queryNow, member);
|
|
53
59
|
},
|
|
54
60
|
},
|
|
@@ -65,8 +71,8 @@ const svc = defineBackendCollection<S, MemberBookingRow>("himiBookingBookings",
|
|
|
65
71
|
actions: {
|
|
66
72
|
scheduleView(s, arg) { const value = (arg as { selectedId?: string })?.selectedId; if (["agenda", "day", "month"].includes(value || "")) { mode = value!; views.write(mode); } return derive(s); },
|
|
67
73
|
selectDate(s, arg) { const date = (arg as { date?: unknown })?.date; if (validDate(date)) selectedDate = date; return derive(s); },
|
|
68
|
-
previousDay(s) { const date = new Date(`${selectedDate}T12:00:00`); date.setDate(date.getDate() - 1); selectedDate = dayKey(date); return derive(s); },
|
|
69
|
-
nextDay(s) { const date = new Date(`${selectedDate}T12:00:00`); date.setDate(date.getDate() + 1); selectedDate = dayKey(date); return derive(s); },
|
|
74
|
+
previousDay(s) { const date = new Date(`${selectedDate || today()}T12:00:00`); date.setDate(date.getDate() - 1); selectedDate = dayKey(date); return derive(s); },
|
|
75
|
+
nextDay(s) { const date = new Date(`${selectedDate || today()}T12:00:00`); date.setDate(date.getDate() + 1); selectedDate = dayKey(date); return derive(s); },
|
|
70
76
|
async rebook(s, arg) {
|
|
71
77
|
const member = pageMember, row = s.rows.find(row => row.id === (arg as { id?: string })?.id);
|
|
72
78
|
if (!member || !row?.serviceId) return s;
|
|
@@ -388,6 +388,10 @@ function resolveEffectiveLayout(arg: unknown, productCount: number): LayoutId {
|
|
|
388
388
|
});
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
+
/** The time-of-day greeting. Read at REFRESH time only, never at boot: the release build hashes
|
|
392
|
+
* the initial state, so a greeting read off the build machine's clock made every build of an
|
|
393
|
+
* unchanged template a different release (#3268, the `nondeterministic-initial-state` lint).
|
|
394
|
+
* The initial state carries `neutralWelcome()` instead, and the first refresh swaps this in. */
|
|
391
395
|
function welcome(): string {
|
|
392
396
|
const hour = new Date().getHours();
|
|
393
397
|
if (hour < 12) return copyParam("home.greetingMorning", "Good morning");
|
|
@@ -395,6 +399,11 @@ function welcome(): string {
|
|
|
395
399
|
return copyParam("home.greetingEvening", "Good evening");
|
|
396
400
|
}
|
|
397
401
|
|
|
402
|
+
/** The boot-time greeting: clock-free, so the baked initial state is the same on every build. */
|
|
403
|
+
function neutralWelcome(): string {
|
|
404
|
+
return copyParam("home.greetingNeutral", "Welcome");
|
|
405
|
+
}
|
|
406
|
+
|
|
398
407
|
function feature(p: StoreProduct): ProductFeature {
|
|
399
408
|
const name = cleanTitle(p.name) || copyParam("home.productFallbackName", "Featured item");
|
|
400
409
|
const image = productImage(p);
|
|
@@ -871,7 +880,9 @@ function baseState(): State {
|
|
|
871
880
|
hideHeroInitial: true,
|
|
872
881
|
hideHeroFallback: true,
|
|
873
882
|
hideFeaturedSkeleton: false,
|
|
874
|
-
|
|
883
|
+
// Not welcome(): that reads the clock, and this state is baked into the release. A neutral
|
|
884
|
+
// greeting keeps the hero pill painted (same size, no reflow) until the first refresh.
|
|
885
|
+
welcome: neutralWelcome(),
|
|
875
886
|
refreshedAt: "",
|
|
876
887
|
effectiveLayout: resolveEffectiveLayout(undefined, 0),
|
|
877
888
|
accentColor: resolveAccent(undefined),
|
|
@@ -8,7 +8,7 @@ import { queryPrograms, getProgram } from '@himalaya/wix-online-programs';
|
|
|
8
8
|
import { queryReviews, getReview } from '@himalaya/wix-reviews';
|
|
9
9
|
import { queryReservations, getReservation } from '@himalaya/wix-table-reservations';
|
|
10
10
|
import { queryForms, getForm } from '@himalaya/wix-forms';
|
|
11
|
-
import { listProjects, listProjectItems } from '@himalaya/wix-portfolio';
|
|
11
|
+
import { listProjects, listProjectItems, portfolioCoverRef, type WixMediaImage } from '@himalaya/wix-portfolio';
|
|
12
12
|
import { queryGiftCards, getGiftCard } from '@himalaya/wix-gift-cards';
|
|
13
13
|
import { queryDonationCampaigns, getDonationCampaign } from '@himalaya/wix-donations';
|
|
14
14
|
import { query as queryBadges, get as getBadge } from '@himalaya/wix-badges';
|
|
@@ -208,7 +208,7 @@ export function ownerRecord(module: OwnerModuleId, raw: unknown): OwnerRecord {
|
|
|
208
208
|
details = [['Namespace', subtitle]];
|
|
209
209
|
break;
|
|
210
210
|
case 'portfolio':
|
|
211
|
-
title = field(raw, 'title'); subtitle = field(raw, 'description'); image =
|
|
211
|
+
title = field(raw, 'title'); subtitle = field(raw, 'description'); image = portfolioCoverRef(at(raw, 'coverImage') as WixMediaImage | string | undefined);
|
|
212
212
|
statusLabel = typeof at(raw, 'hidden') === 'boolean' ? at(raw, 'hidden') ? 'Hidden' : 'Visible' : '';
|
|
213
213
|
break;
|
|
214
214
|
case 'donationCampaigns':
|
|
@@ -311,7 +311,10 @@ function shape(s: Partial<AppointmentsState>): AppointmentsState {
|
|
|
311
311
|
// Until the owner picks a day, follow the soonest booked one, so a freshly
|
|
312
312
|
// loaded month never opens on an empty day list.
|
|
313
313
|
const soonest = all.find((r) => r._day)?._day ?? "";
|
|
314
|
-
|
|
314
|
+
// Today only once the list is READY (the month is hidden until then): the initial state is baked
|
|
315
|
+
// into the release, and a day read off the build machine's clock made every build of unchanged
|
|
316
|
+
// source a different release (#3268, the `nondeterministic-initial-state` lint).
|
|
317
|
+
const focus = (s._picked ? s.selectedDate : "") || soonest || (status === "ready" ? dayKey(new Date()) : "");
|
|
315
318
|
const dayAppointments = all.filter((r) => r._day === focus);
|
|
316
319
|
const dayEvents = eventsOf(dayAppointments);
|
|
317
320
|
const ready = status === "ready";
|