@homepages/template-kit 0.9.0-dev-20260719205406 → 0.9.0-dev-20260719224319
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/CHANGELOG.md +32 -1
- package/dist/cli/check/stages/render.js +3 -2
- package/dist/cli/check/stages/validate/nav-contract.js +3 -2
- package/dist/cli/dev/server.js +3 -2
- package/dist/cli/new/scaffold/template/sections/hero/fixtures.ts +4 -4
- package/dist/fixtures/sample-property.d.ts +4 -7
- package/dist/fixtures/sample-property.js +41 -97
- package/dist/fixtures/scenario.d.ts +1 -1
- package/dist/fixtures/scenarios/for-rent-condo.js +24 -44
- package/dist/fixtures/scenarios/luxury-multi-unit.js +1 -1
- package/dist/fixtures/scenarios/sparse-single-family.js +14 -26
- package/dist/fixtures.d.ts +2 -2
- package/dist/fixtures.js +1 -2
- package/dist/manifest.json +940 -0
- package/dist/media/pack.d.ts +49 -0
- package/dist/media/pack.js +69 -0
- package/dist/media/resolve-fixture-media.js +73 -0
- package/dist/media/resolve-media.js +195 -0
- package/dist/package.js +1 -1
- package/dist/schema/property-facts.d.ts +2 -2
- package/dist/ssr.d.ts +13 -1
- package/dist/ssr.js +27 -12
- package/guide/recipes/image-slot-crop.md +6 -6
- package/guide/recipes/pick-a-scenario.md +13 -5
- package/guide/schema-system.md +36 -12
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @homepages/template-kit
|
|
2
2
|
|
|
3
|
-
## 0.9.0-dev-
|
|
3
|
+
## 0.9.0-dev-20260719224319
|
|
4
4
|
|
|
5
5
|
### Minor Changes
|
|
6
6
|
|
|
@@ -24,6 +24,37 @@
|
|
|
24
24
|
`resolveImportedFile`, `resetClientFileCache`) that the plugin's rules consume
|
|
25
25
|
over the peer edge.
|
|
26
26
|
|
|
27
|
+
- b7bfc96: Fixtures state media asset ids instead of URLs, and the kit resolves them.
|
|
28
|
+
|
|
29
|
+
A fixture now states what a saved page actually holds — `{ photo_id, alt }` — and
|
|
30
|
+
the kit resolves that id to a URL, an `alt`, and a responsive ladder when it loads
|
|
31
|
+
the fixture. Previously fixtures stated the post-resolution URL plus a synthesized
|
|
32
|
+
ladder, which exercised a code path real content never takes and left every preview
|
|
33
|
+
image broken.
|
|
34
|
+
|
|
35
|
+
Breaking changes to `@homepages/template-kit/fixtures`:
|
|
36
|
+
|
|
37
|
+
- `mkResponsive` is removed. Nothing needs to call it: the ladder is attached during
|
|
38
|
+
fixture load.
|
|
39
|
+
- `PhotoFixture.url` is removed; `PhotoFixture.id` now holds a media asset id.
|
|
40
|
+
- `ContactFixture.fields` replaces `headshot_url`, `logo_light`, and `logo_dark`
|
|
41
|
+
with `headshot_id`, `logo_light_id`, and `logo_dark_id`.
|
|
42
|
+
- `MediaFixture.video_url` becomes `video_id`. `tour_3d_url` is unchanged — a 3D
|
|
43
|
+
tour is a third-party embed, not a media asset.
|
|
44
|
+
- `FloorplanAssignment.url` and `.alt` are now optional: a fixture states the plan
|
|
45
|
+
ref, and url/alt/responsive appear only after resolution.
|
|
46
|
+
|
|
47
|
+
To migrate a fixture, drop `url` and `responsive` and keep the id:
|
|
48
|
+
|
|
49
|
+
```diff
|
|
50
|
+
hero_image: {
|
|
51
|
+
photo_id: photo.id,
|
|
52
|
+
- url: photo.url,
|
|
53
|
+
alt: photo.alt,
|
|
54
|
+
- responsive: mkResponsive(photo.url, 2048, 1259),
|
|
55
|
+
},
|
|
56
|
+
```
|
|
57
|
+
|
|
27
58
|
### Patch Changes
|
|
28
59
|
|
|
29
60
|
- 10f53a7: `template-kit dev`'s canvas hover/select ring no longer overflows the page's
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FixtureModuleShape, buildInvariantFixtures } from "../../../schema/fixture-schema.js";
|
|
2
2
|
import { loadSection } from "../loader.js";
|
|
3
|
+
import { resolveFixtureSetMedia } from "../../../media/resolve-fixture-media.js";
|
|
3
4
|
import { flattenLayout } from "../../../schema/rows.js";
|
|
4
5
|
import { renderSectionHtml } from "../../../ssr.js";
|
|
5
6
|
import { checkRenderInvariants } from "../render-invariants.js";
|
|
@@ -50,11 +51,11 @@ async function renderStage(workspaceRoot, template) {
|
|
|
50
51
|
let fixtures;
|
|
51
52
|
try {
|
|
52
53
|
loaded = await loadSection(workspaceRoot, section);
|
|
53
|
-
fixtures = buildInvariantFixtures({
|
|
54
|
+
fixtures = resolveFixtureSetMedia(buildInvariantFixtures({
|
|
54
55
|
schema: loaded.schema,
|
|
55
56
|
module: FixtureModuleShape.parse(loaded.fixtures),
|
|
56
57
|
selfAnchor: section.name
|
|
57
|
-
});
|
|
58
|
+
}));
|
|
58
59
|
} catch {
|
|
59
60
|
continue;
|
|
60
61
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { FixtureModuleShape, buildFixtureSet } from "../../../../schema/fixture-schema.js";
|
|
2
2
|
import { TemplateManifest } from "../../../../schema/manifest.js";
|
|
3
3
|
import { loadSection } from "../../loader.js";
|
|
4
|
+
import { resolveFixtureSetMedia } from "../../../../media/resolve-fixture-media.js";
|
|
4
5
|
import { renderSectionHtml } from "../../../../ssr.js";
|
|
5
6
|
import { resolveSectionRef } from "../../../../schema/resolve-section-ref.js";
|
|
6
7
|
import { materializeSection } from "./orchestrator.js";
|
|
@@ -68,11 +69,11 @@ async function assertNavContract(workspaceRoot, template) {
|
|
|
68
69
|
let html = "";
|
|
69
70
|
for (const section of composed) {
|
|
70
71
|
const { anchor, schema } = materializedByName.get(section.name);
|
|
71
|
-
const base = buildFixtureSet({
|
|
72
|
+
const base = resolveFixtureSetMedia(buildFixtureSet({
|
|
72
73
|
schema,
|
|
73
74
|
module: FixtureModuleShape.parse(fixturesByName.get(section.name)),
|
|
74
75
|
selfAnchor: anchor
|
|
75
|
-
}).find((f) => f.id === "typical");
|
|
76
|
+
})).find((f) => f.id === "typical");
|
|
76
77
|
if (!base) throw new Error(`Template "${template.key}": section "${section.name}" has no "typical" base fixture.`);
|
|
77
78
|
html += renderSectionHtml(rendererByName.get(section.name), {
|
|
78
79
|
slots: base.props.slots,
|
package/dist/cli/dev/server.js
CHANGED
|
@@ -69,11 +69,12 @@ async function startDevServer(opts) {
|
|
|
69
69
|
const sectionAssets = /* @__PURE__ */ new Map();
|
|
70
70
|
const islandBootstrapUrl = await writeIslandBootstrap(workspace.root);
|
|
71
71
|
const canvasIslandBootstrapUrl = await writeIslandCanvasBootstrap(workspace.root);
|
|
72
|
-
vite.watcher.on("change", () => {
|
|
72
|
+
vite.watcher.on("change", (path) => {
|
|
73
73
|
vite.moduleGraph.invalidateAll();
|
|
74
74
|
cssCache.clear();
|
|
75
75
|
sectionAssets.clear();
|
|
76
|
-
hmr.broadcast("
|
|
76
|
+
if (path.endsWith(".css")) hmr.broadcast("tailwind:changed", "{}");
|
|
77
|
+
else hmr.broadcast("section:changed", "{}");
|
|
77
78
|
});
|
|
78
79
|
const ctx = {
|
|
79
80
|
workspace,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FixtureModule } from "@homepages/template-kit";
|
|
2
|
-
import { luxuryMultiUnit
|
|
2
|
+
import { luxuryMultiUnit } from "@homepages/template-kit/fixtures";
|
|
3
3
|
|
|
4
4
|
// Base content the section is designed for. Nullability coverage (each optional
|
|
5
5
|
// slot rendered empty) is derived automatically from schema.ts — no fixture
|
|
@@ -12,12 +12,12 @@ const fixtures: FixtureModule = {
|
|
|
12
12
|
base: {
|
|
13
13
|
slots: {
|
|
14
14
|
headline: luxuryMultiUnit.facts.address.display,
|
|
15
|
+
// State the asset id, exactly as a saved page does — the preview server
|
|
16
|
+
// resolves it to a URL and a responsive ladder, the same way the API does
|
|
17
|
+
// when the page is published. A fixture never states a URL.
|
|
15
18
|
hero_image: {
|
|
16
19
|
photo_id: heroImage.id,
|
|
17
|
-
url: heroImage.url,
|
|
18
20
|
alt: heroImage.alt,
|
|
19
|
-
// Height set to a 16:9 ratio to match the slot's locked crop.
|
|
20
|
-
responsive: mkResponsive(heroImage.url, 2048, 1152),
|
|
21
21
|
},
|
|
22
22
|
blurb: "A 12-unit mansard Victorian in Jamaica Plain, moments from the Emerald Necklace.",
|
|
23
23
|
},
|
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
import { ResponsiveImage } from "../contracts/image-descriptor.js";
|
|
2
1
|
//#region src/fixtures/sample-property.d.ts
|
|
3
2
|
type PhotoFixture = {
|
|
4
3
|
id: string;
|
|
5
|
-
url: string;
|
|
6
4
|
alt: string;
|
|
7
5
|
tags: {
|
|
8
6
|
room?: string;
|
|
9
7
|
category?: string;
|
|
10
8
|
};
|
|
11
9
|
};
|
|
12
|
-
declare function mkResponsive(url: string, width: number, height: number): ResponsiveImage;
|
|
13
10
|
type PoiFixture = {
|
|
14
11
|
id: string;
|
|
15
12
|
category: "restaurant" | "park" | "transit" | "shopping" | "coffee" | "fitness";
|
|
@@ -31,14 +28,14 @@ type ContactFixture = {
|
|
|
31
28
|
display_name: string;
|
|
32
29
|
title: string;
|
|
33
30
|
bio: string;
|
|
34
|
-
|
|
31
|
+
headshot_id: string;
|
|
35
32
|
website_url: string;
|
|
36
33
|
email: string;
|
|
37
34
|
phone: string;
|
|
38
35
|
address: string;
|
|
39
36
|
org_name: string;
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
logo_light_id: string | null;
|
|
38
|
+
logo_dark_id: string | null;
|
|
42
39
|
socials: Array<{
|
|
43
40
|
platform: "instagram" | "linkedin" | "x" | "facebook";
|
|
44
41
|
url: string;
|
|
@@ -54,4 +51,4 @@ type AmenityItem = {
|
|
|
54
51
|
items: string[];
|
|
55
52
|
};
|
|
56
53
|
//#endregion
|
|
57
|
-
export { AmenityItem, ContactFixture, PhotoFixture, PoiFixture
|
|
54
|
+
export { AmenityItem, ContactFixture, PhotoFixture, PoiFixture };
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
//#region src/fixtures/sample-property.ts
|
|
2
|
-
const PHOTO_BASE = "https://example.invalid/photos/queensway";
|
|
3
2
|
const galleryPhotos = [
|
|
4
3
|
{
|
|
5
|
-
id: "
|
|
6
|
-
url: `${PHOTO_BASE}/01-lobby.jpg`,
|
|
4
|
+
id: "dev_upl_queensway_01_lobby",
|
|
7
5
|
alt: "Lobby with terrazzo flooring and chandeliers.",
|
|
8
6
|
tags: {
|
|
9
7
|
room: "lobby",
|
|
@@ -11,8 +9,7 @@ const galleryPhotos = [
|
|
|
11
9
|
}
|
|
12
10
|
},
|
|
13
11
|
{
|
|
14
|
-
id: "
|
|
15
|
-
url: `${PHOTO_BASE}/02-gym-a.jpg`,
|
|
12
|
+
id: "dev_upl_queensway_02_gym_a",
|
|
16
13
|
alt: "Gym with cardio equipment and floor-to-ceiling mirrors.",
|
|
17
14
|
tags: {
|
|
18
15
|
room: "gym",
|
|
@@ -20,8 +17,7 @@ const galleryPhotos = [
|
|
|
20
17
|
}
|
|
21
18
|
},
|
|
22
19
|
{
|
|
23
|
-
id: "
|
|
24
|
-
url: `${PHOTO_BASE}/03-gym-b.jpg`,
|
|
20
|
+
id: "dev_upl_queensway_03_gym_b",
|
|
25
21
|
alt: "Gym free-weights area.",
|
|
26
22
|
tags: {
|
|
27
23
|
room: "gym",
|
|
@@ -29,8 +25,7 @@ const galleryPhotos = [
|
|
|
29
25
|
}
|
|
30
26
|
},
|
|
31
27
|
{
|
|
32
|
-
id: "
|
|
33
|
-
url: `${PHOTO_BASE}/04-living-a.jpg`,
|
|
28
|
+
id: "dev_upl_queensway_04_living_a",
|
|
34
29
|
alt: "Living area with bay windows and original mouldings.",
|
|
35
30
|
tags: {
|
|
36
31
|
room: "living",
|
|
@@ -38,8 +33,7 @@ const galleryPhotos = [
|
|
|
38
33
|
}
|
|
39
34
|
},
|
|
40
35
|
{
|
|
41
|
-
id: "
|
|
42
|
-
url: `${PHOTO_BASE}/05-living-b.jpg`,
|
|
36
|
+
id: "dev_upl_queensway_05_living_b",
|
|
43
37
|
alt: "Living area facing the fireplace.",
|
|
44
38
|
tags: {
|
|
45
39
|
room: "living",
|
|
@@ -47,8 +41,7 @@ const galleryPhotos = [
|
|
|
47
41
|
}
|
|
48
42
|
},
|
|
49
43
|
{
|
|
50
|
-
id: "
|
|
51
|
-
url: `${PHOTO_BASE}/06-kitchen-a.jpg`,
|
|
44
|
+
id: "dev_upl_queensway_06_kitchen_a",
|
|
52
45
|
alt: "Kitchen with quartz waterfall island.",
|
|
53
46
|
tags: {
|
|
54
47
|
room: "kitchen",
|
|
@@ -56,8 +49,7 @@ const galleryPhotos = [
|
|
|
56
49
|
}
|
|
57
50
|
},
|
|
58
51
|
{
|
|
59
|
-
id: "
|
|
60
|
-
url: `${PHOTO_BASE}/07-kitchen-b.jpg`,
|
|
52
|
+
id: "dev_upl_queensway_07_kitchen_b",
|
|
61
53
|
alt: "Kitchen breakfast nook by the window.",
|
|
62
54
|
tags: {
|
|
63
55
|
room: "kitchen",
|
|
@@ -65,8 +57,7 @@ const galleryPhotos = [
|
|
|
65
57
|
}
|
|
66
58
|
},
|
|
67
59
|
{
|
|
68
|
-
id: "
|
|
69
|
-
url: `${PHOTO_BASE}/08-dining.jpg`,
|
|
60
|
+
id: "dev_upl_queensway_08_dining",
|
|
70
61
|
alt: "Dining area set for six.",
|
|
71
62
|
tags: {
|
|
72
63
|
room: "dining",
|
|
@@ -74,8 +65,7 @@ const galleryPhotos = [
|
|
|
74
65
|
}
|
|
75
66
|
},
|
|
76
67
|
{
|
|
77
|
-
id: "
|
|
78
|
-
url: `${PHOTO_BASE}/09-living-c.jpg`,
|
|
68
|
+
id: "dev_upl_queensway_09_living_c",
|
|
79
69
|
alt: "Living area with statement art wall.",
|
|
80
70
|
tags: {
|
|
81
71
|
room: "living",
|
|
@@ -83,8 +73,7 @@ const galleryPhotos = [
|
|
|
83
73
|
}
|
|
84
74
|
},
|
|
85
75
|
{
|
|
86
|
-
id: "
|
|
87
|
-
url: `${PHOTO_BASE}/10-living-d.jpg`,
|
|
76
|
+
id: "dev_upl_queensway_10_living_d",
|
|
88
77
|
alt: "Living area opening onto private terrace.",
|
|
89
78
|
tags: {
|
|
90
79
|
room: "living",
|
|
@@ -92,8 +81,7 @@ const galleryPhotos = [
|
|
|
92
81
|
}
|
|
93
82
|
},
|
|
94
83
|
{
|
|
95
|
-
id: "
|
|
96
|
-
url: `${PHOTO_BASE}/11-living-e.jpg`,
|
|
84
|
+
id: "dev_upl_queensway_11_living_e",
|
|
97
85
|
alt: "Sunken living area with built-in shelving.",
|
|
98
86
|
tags: {
|
|
99
87
|
room: "living",
|
|
@@ -101,8 +89,7 @@ const galleryPhotos = [
|
|
|
101
89
|
}
|
|
102
90
|
},
|
|
103
91
|
{
|
|
104
|
-
id: "
|
|
105
|
-
url: `${PHOTO_BASE}/12-living-f.jpg`,
|
|
92
|
+
id: "dev_upl_queensway_12_living_f",
|
|
106
93
|
alt: "Living area corner with reading chair.",
|
|
107
94
|
tags: {
|
|
108
95
|
room: "living",
|
|
@@ -110,8 +97,7 @@ const galleryPhotos = [
|
|
|
110
97
|
}
|
|
111
98
|
},
|
|
112
99
|
{
|
|
113
|
-
id: "
|
|
114
|
-
url: `${PHOTO_BASE}/13-master-bedroom.jpg`,
|
|
100
|
+
id: "dev_upl_queensway_13_master_bedroom",
|
|
115
101
|
alt: "Primary bedroom with king bed and skylight.",
|
|
116
102
|
tags: {
|
|
117
103
|
room: "bedroom",
|
|
@@ -119,8 +105,7 @@ const galleryPhotos = [
|
|
|
119
105
|
}
|
|
120
106
|
},
|
|
121
107
|
{
|
|
122
|
-
id: "
|
|
123
|
-
url: `${PHOTO_BASE}/14-master-bath-a.jpg`,
|
|
108
|
+
id: "dev_upl_queensway_14_master_bath_a",
|
|
124
109
|
alt: "Spa-style primary bath with freestanding tub.",
|
|
125
110
|
tags: {
|
|
126
111
|
room: "bath",
|
|
@@ -128,8 +113,7 @@ const galleryPhotos = [
|
|
|
128
113
|
}
|
|
129
114
|
},
|
|
130
115
|
{
|
|
131
|
-
id: "
|
|
132
|
-
url: `${PHOTO_BASE}/15-master-bath-b.jpg`,
|
|
116
|
+
id: "dev_upl_queensway_15_master_bath_b",
|
|
133
117
|
alt: "Primary bath double vanity and shower.",
|
|
134
118
|
tags: {
|
|
135
119
|
room: "bath",
|
|
@@ -137,8 +121,7 @@ const galleryPhotos = [
|
|
|
137
121
|
}
|
|
138
122
|
},
|
|
139
123
|
{
|
|
140
|
-
id: "
|
|
141
|
-
url: `${PHOTO_BASE}/16-common-bath.jpg`,
|
|
124
|
+
id: "dev_upl_queensway_16_common_bath",
|
|
142
125
|
alt: "Common bath with marble subway tile.",
|
|
143
126
|
tags: {
|
|
144
127
|
room: "bath",
|
|
@@ -146,8 +129,7 @@ const galleryPhotos = [
|
|
|
146
129
|
}
|
|
147
130
|
},
|
|
148
131
|
{
|
|
149
|
-
id: "
|
|
150
|
-
url: `${PHOTO_BASE}/17-entrance.jpg`,
|
|
132
|
+
id: "dev_upl_queensway_17_entrance",
|
|
151
133
|
alt: "Entrance foyer with original mosaic floor.",
|
|
152
134
|
tags: {
|
|
153
135
|
room: "entry",
|
|
@@ -155,8 +137,7 @@ const galleryPhotos = [
|
|
|
155
137
|
}
|
|
156
138
|
},
|
|
157
139
|
{
|
|
158
|
-
id: "
|
|
159
|
-
url: `${PHOTO_BASE}/18-patio.jpg`,
|
|
140
|
+
id: "dev_upl_queensway_18_patio",
|
|
160
141
|
alt: "Patio with built-in seating and planters.",
|
|
161
142
|
tags: {
|
|
162
143
|
room: "patio",
|
|
@@ -164,8 +145,7 @@ const galleryPhotos = [
|
|
|
164
145
|
}
|
|
165
146
|
},
|
|
166
147
|
{
|
|
167
|
-
id: "
|
|
168
|
-
url: `${PHOTO_BASE}/19-outdoor-deck.jpg`,
|
|
148
|
+
id: "dev_upl_queensway_19_outdoor_deck",
|
|
169
149
|
alt: "Outdoor deck with sunset view.",
|
|
170
150
|
tags: {
|
|
171
151
|
room: "deck",
|
|
@@ -173,8 +153,7 @@ const galleryPhotos = [
|
|
|
173
153
|
}
|
|
174
154
|
},
|
|
175
155
|
{
|
|
176
|
-
id: "
|
|
177
|
-
url: `${PHOTO_BASE}/20-exterior-hero.jpg`,
|
|
156
|
+
id: "dev_upl_queensway_20_exterior_hero",
|
|
178
157
|
alt: "Mansard victorian facade at golden hour.",
|
|
179
158
|
tags: {
|
|
180
159
|
room: "exterior-front",
|
|
@@ -183,56 +162,21 @@ const galleryPhotos = [
|
|
|
183
162
|
}
|
|
184
163
|
];
|
|
185
164
|
const allPhotos = galleryPhotos;
|
|
186
|
-
|
|
187
|
-
320,
|
|
188
|
-
640,
|
|
189
|
-
960,
|
|
190
|
-
1200,
|
|
191
|
-
1600,
|
|
192
|
-
2048
|
|
193
|
-
];
|
|
194
|
-
function mkResponsive(url, width, height) {
|
|
195
|
-
const widths = RUNG_WIDTHS.filter((w) => w <= width);
|
|
196
|
-
if (widths.length === 0) widths.push(width);
|
|
197
|
-
const ladder = (ext) => widths.map((w) => `${url}.${w}.${ext} ${w}w`).join(", ");
|
|
198
|
-
return {
|
|
199
|
-
sources: [
|
|
200
|
-
{
|
|
201
|
-
type: "image/avif",
|
|
202
|
-
srcset: ladder("avif")
|
|
203
|
-
},
|
|
204
|
-
{
|
|
205
|
-
type: "image/webp",
|
|
206
|
-
srcset: ladder("webp")
|
|
207
|
-
},
|
|
208
|
-
{
|
|
209
|
-
type: "image/jpeg",
|
|
210
|
-
srcset: ladder("jpg")
|
|
211
|
-
}
|
|
212
|
-
],
|
|
213
|
-
width,
|
|
214
|
-
height
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
const FP_BASE = "https://example.invalid/floorplans/queensway";
|
|
218
|
-
function fp(id, url, alt, floorLabel, position) {
|
|
165
|
+
function fp(id, floorLabel, position) {
|
|
219
166
|
return {
|
|
220
167
|
id,
|
|
221
|
-
url,
|
|
222
|
-
alt,
|
|
223
168
|
floor_label: floorLabel,
|
|
224
|
-
position
|
|
225
|
-
responsive: mkResponsive(url, 1024, 1024)
|
|
169
|
+
position
|
|
226
170
|
};
|
|
227
171
|
}
|
|
228
|
-
const FLOORPLAN_3BR = (suffix) => [fp(`
|
|
229
|
-
const FLOORPLAN_2BR = (suffix) => [fp(`
|
|
172
|
+
const FLOORPLAN_3BR = (suffix) => [fp(`dev_efp_queensway_3br_${suffix}_l1`, "Main Floor", 0), fp(`dev_efp_queensway_3br_${suffix}_l2`, "Upper Floor", 1)];
|
|
173
|
+
const FLOORPLAN_2BR = (suffix) => [fp(`dev_efp_queensway_2br_${suffix}_l1`, null, 0)];
|
|
230
174
|
const FLOORPLAN_4BR = (suffix) => [
|
|
231
|
-
fp(`
|
|
232
|
-
fp(`
|
|
233
|
-
fp(`
|
|
175
|
+
fp(`dev_efp_queensway_4br_${suffix}_l1`, "Main Floor", 0),
|
|
176
|
+
fp(`dev_efp_queensway_4br_${suffix}_l2`, "Upper Floor", 1),
|
|
177
|
+
fp(`dev_efp_queensway_4br_${suffix}_l3`, "Penthouse", 2)
|
|
234
178
|
];
|
|
235
|
-
const FLOORPLAN_STUDIO = (suffix) => [fp(`
|
|
179
|
+
const FLOORPLAN_STUDIO = (suffix) => [fp(`dev_efp_queensway_studio_${suffix}_l1`, null, 0)];
|
|
236
180
|
function unit(position, unitLabel, beds, baths, sqft, price, notes, description, features, floorPlans) {
|
|
237
181
|
return {
|
|
238
182
|
unit_id: `unit_${String(position + 1).padStart(2, "0")}`,
|
|
@@ -645,14 +589,14 @@ const sampleContacts = [
|
|
|
645
589
|
display_name: "James Donovan",
|
|
646
590
|
title: "Listing Agent",
|
|
647
591
|
bio: "James Donovan is a 15-year veteran of the Boston real estate market, specializing in historic multi-family conversions in Jamaica Plain and the South End. He has closed over $400M in residential transactions and is known for white-glove attention to multi-unit listings.",
|
|
648
|
-
|
|
592
|
+
headshot_id: "dev_lib_james_donovan",
|
|
649
593
|
website_url: "https://okane-marketing.example.invalid",
|
|
650
594
|
email: "info@homepages.io",
|
|
651
595
|
phone: "(617) 555-1068",
|
|
652
596
|
address: "677 Centre St, Suite 200, Jamaica Plain, MA 02130",
|
|
653
597
|
org_name: "Okane Marketing",
|
|
654
|
-
|
|
655
|
-
|
|
598
|
+
logo_light_id: "dev_lib_okane_logo_light",
|
|
599
|
+
logo_dark_id: "dev_lib_okane_logo_dark",
|
|
656
600
|
socials: [
|
|
657
601
|
{
|
|
658
602
|
platform: "instagram",
|
|
@@ -682,14 +626,14 @@ const sampleContacts = [
|
|
|
682
626
|
display_name: "Elena Marsh",
|
|
683
627
|
title: "Buyer's Agent",
|
|
684
628
|
bio: "Elena Marsh has guided over 120 buyer clients through the Boston multi-family market since joining the team in 2018, with a focus on Jamaica Plain, Roslindale, and West Roxbury first-time and investor purchases. She is a certified condo-conversion specialist and negotiates every offer personally.",
|
|
685
|
-
|
|
629
|
+
headshot_id: "dev_lib_elena_marsh",
|
|
686
630
|
website_url: "https://okane-marketing.example.invalid",
|
|
687
631
|
email: "elena@homepages.io",
|
|
688
632
|
phone: "(617) 555-2214",
|
|
689
633
|
address: "677 Centre St, Suite 200, Jamaica Plain, MA 02130",
|
|
690
634
|
org_name: "Okane Marketing",
|
|
691
|
-
|
|
692
|
-
|
|
635
|
+
logo_light_id: "dev_lib_okane_logo_light",
|
|
636
|
+
logo_dark_id: "dev_lib_okane_logo_dark",
|
|
693
637
|
socials: [{
|
|
694
638
|
platform: "instagram",
|
|
695
639
|
url: "https://instagram.com/elenamarsh.realty/"
|
|
@@ -708,14 +652,14 @@ const sampleContacts = [
|
|
|
708
652
|
display_name: "Marcus Webb",
|
|
709
653
|
title: "Director of Sales",
|
|
710
654
|
bio: "Marcus Webb leads the team's dispositions practice, working with investors and owner-occupants on multi-unit acquisitions and conversions across greater Boston. A former general contractor, he brings a builder's eye to every walkthrough and has overseen more than 60 multi-family closings.",
|
|
711
|
-
|
|
655
|
+
headshot_id: "dev_lib_marcus_webb",
|
|
712
656
|
website_url: "https://okane-marketing.example.invalid",
|
|
713
657
|
email: "marcus@homepages.io",
|
|
714
658
|
phone: "(617) 555-3357",
|
|
715
659
|
address: "677 Centre St, Suite 200, Jamaica Plain, MA 02130",
|
|
716
660
|
org_name: "Okane Marketing",
|
|
717
|
-
|
|
718
|
-
|
|
661
|
+
logo_light_id: "dev_lib_okane_logo_light",
|
|
662
|
+
logo_dark_id: "dev_lib_okane_logo_dark",
|
|
719
663
|
socials: [{
|
|
720
664
|
platform: "linkedin",
|
|
721
665
|
url: "https://linkedin.com/in/marcus-webb-realty/"
|
|
@@ -824,10 +768,10 @@ const sampleProperty = {
|
|
|
824
768
|
phone: c.fields.phone,
|
|
825
769
|
address: c.fields.address,
|
|
826
770
|
website_url: c.fields.website_url,
|
|
827
|
-
avatar_upload_id:
|
|
771
|
+
avatar_upload_id: c.fields.headshot_id,
|
|
828
772
|
org_name: c.fields.org_name,
|
|
829
|
-
logo_light_upload_id: c.fields.
|
|
830
|
-
logo_dark_upload_id: c.fields.
|
|
773
|
+
logo_light_upload_id: c.fields.logo_light_id,
|
|
774
|
+
logo_dark_upload_id: c.fields.logo_dark_id,
|
|
831
775
|
instagram_url: c.fields.socials.find((s) => s.platform === "instagram")?.url ?? null,
|
|
832
776
|
linkedin_url: c.fields.socials.find((s) => s.platform === "linkedin")?.url ?? null,
|
|
833
777
|
x_url: c.fields.socials.find((s) => s.platform === "x")?.url ?? null,
|
|
@@ -836,4 +780,4 @@ const sampleProperty = {
|
|
|
836
780
|
};
|
|
837
781
|
|
|
838
782
|
//#endregion
|
|
839
|
-
export { allPhotos,
|
|
783
|
+
export { allPhotos, sampleAmenities, sampleContacts, samplePois, sampleProperty };
|
|
@@ -3,7 +3,7 @@ import { AmenityItem, ContactFixture, PhotoFixture, PoiFixture } from "./sample-
|
|
|
3
3
|
//#region src/fixtures/scenario.d.ts
|
|
4
4
|
type ScenarioKey = "luxuryMultiUnit" | "sparseSingleFamily" | "forRentCondo";
|
|
5
5
|
type MediaFixture = {
|
|
6
|
-
|
|
6
|
+
video_id: string | null;
|
|
7
7
|
tour_3d_url: string | null;
|
|
8
8
|
};
|
|
9
9
|
type Scenario = {
|