@edgedev/create-edge-app 1.1.23 → 1.1.26

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.
Files changed (116) hide show
  1. package/.env +1 -0
  2. package/.env.dev +1 -0
  3. package/README.md +55 -20
  4. package/{agent.md → agents.md} +2 -0
  5. package/bin/cli.js +6 -6
  6. package/edge/components/auth/login.vue +384 -0
  7. package/edge/components/auth/register.vue +396 -0
  8. package/edge/components/auth.vue +108 -0
  9. package/edge/components/autoFileUpload.vue +215 -0
  10. package/edge/components/billing.vue +8 -0
  11. package/edge/components/buttonDivider.vue +14 -0
  12. package/edge/components/chip.vue +34 -0
  13. package/edge/components/clipboardButton.vue +42 -0
  14. package/edge/components/cms/block.vue +529 -0
  15. package/edge/components/cms/blockApi.vue +212 -0
  16. package/edge/components/cms/blockEditor.vue +725 -0
  17. package/edge/components/cms/blockInput.vue +66 -0
  18. package/edge/components/cms/blockPicker.vue +486 -0
  19. package/edge/components/cms/blockRender.vue +78 -0
  20. package/edge/components/cms/blockSheetContent.vue +28 -0
  21. package/edge/components/cms/codeEditor.vue +466 -0
  22. package/edge/components/cms/fontUpload.vue +327 -0
  23. package/edge/components/cms/htmlContent.vue +807 -0
  24. package/edge/components/cms/init_blocks/api_with_subarrays.html +17 -0
  25. package/edge/components/cms/init_blocks/array_with_collection.html +7 -0
  26. package/edge/components/cms/init_blocks/array_with_objects.html +7 -0
  27. package/edge/components/cms/init_blocks/carousel.html +103 -0
  28. package/edge/components/cms/init_blocks/contact_us.html +69 -0
  29. package/edge/components/cms/init_blocks/content_with_left_image.html +27 -0
  30. package/edge/components/cms/init_blocks/footer.html +24 -0
  31. package/edge/components/cms/init_blocks/header_divider.html +7 -0
  32. package/edge/components/cms/init_blocks/hero.html +35 -0
  33. package/edge/components/cms/init_blocks/hero_carousel.html +52 -0
  34. package/edge/components/cms/init_blocks/newsletter.html +117 -0
  35. package/edge/components/cms/init_blocks/post_content.html +7 -0
  36. package/edge/components/cms/init_blocks/post_title_header.html +21 -0
  37. package/edge/components/cms/init_blocks/posts_list.html +20 -0
  38. package/edge/components/cms/init_blocks/properties_showcase.html +100 -0
  39. package/edge/components/cms/init_blocks/property_carousel.html +59 -0
  40. package/edge/components/cms/init_blocks/property_detail.html +112 -0
  41. package/edge/components/cms/init_blocks/property_detail_header.html +34 -0
  42. package/edge/components/cms/init_blocks/property_results.html +137 -0
  43. package/edge/components/cms/init_blocks/property_search.html +75 -0
  44. package/edge/components/cms/init_blocks/simple_array.html +7 -0
  45. package/edge/components/cms/mediaCard.vue +116 -0
  46. package/edge/components/cms/mediaManager.vue +386 -0
  47. package/edge/components/cms/menu.vue +1103 -0
  48. package/edge/components/cms/optionsSelect.vue +107 -0
  49. package/edge/components/cms/page.vue +1785 -0
  50. package/edge/components/cms/posts.vue +1083 -0
  51. package/edge/components/cms/site.vue +1298 -0
  52. package/edge/components/cms/themeDefaultMenu.vue +548 -0
  53. package/edge/components/cms/themeEditor.vue +426 -0
  54. package/edge/components/dashboard.vue +776 -0
  55. package/edge/components/editor.vue +671 -0
  56. package/edge/components/fileTree.vue +72 -0
  57. package/edge/components/files.vue +89 -0
  58. package/edge/components/formSubtypes/myOrgs.vue +214 -0
  59. package/edge/components/formSubtypes/users.vue +336 -0
  60. package/edge/components/functionChips.vue +57 -0
  61. package/edge/components/gError.vue +98 -0
  62. package/edge/components/gHelper.vue +67 -0
  63. package/edge/components/gInput.vue +1331 -0
  64. package/edge/components/loggingIn.vue +41 -0
  65. package/edge/components/menu.vue +137 -0
  66. package/edge/components/menuContent.vue +132 -0
  67. package/edge/components/myAccount.vue +317 -0
  68. package/edge/components/myOrganizations.vue +75 -0
  69. package/edge/components/myProfile.vue +122 -0
  70. package/edge/components/orgSwitcher.vue +25 -0
  71. package/edge/components/organizationMembers.vue +522 -0
  72. package/edge/components/organizationSettings.vue +271 -0
  73. package/edge/components/shad/breadcrumbs.vue +35 -0
  74. package/edge/components/shad/button.vue +43 -0
  75. package/edge/components/shad/checkbox.vue +73 -0
  76. package/edge/components/shad/combobox.vue +238 -0
  77. package/edge/components/shad/datepicker.vue +184 -0
  78. package/edge/components/shad/dialog.vue +32 -0
  79. package/edge/components/shad/dropdownMenu.vue +54 -0
  80. package/edge/components/shad/dropdownMenuItem.vue +21 -0
  81. package/edge/components/shad/form.vue +59 -0
  82. package/edge/components/shad/html.vue +877 -0
  83. package/edge/components/shad/input.vue +139 -0
  84. package/edge/components/shad/number.vue +109 -0
  85. package/edge/components/shad/select.vue +151 -0
  86. package/edge/components/shad/selectTags.vue +278 -0
  87. package/edge/components/shad/switch.vue +67 -0
  88. package/edge/components/shad/tags.vue +137 -0
  89. package/edge/components/shad/textarea.vue +102 -0
  90. package/edge/components/shad/typeMoney.vue +167 -0
  91. package/edge/components/sideBar.vue +288 -0
  92. package/edge/components/sideBarContent.vue +268 -0
  93. package/edge/components/sidebarProvider.vue +33 -0
  94. package/edge/components/tooltip.vue +16 -0
  95. package/edge/components/userMenu.vue +148 -0
  96. package/edge/components/v/alert.vue +59 -0
  97. package/edge/components/v/alertTitle.vue +18 -0
  98. package/edge/components/v/card.vue +53 -0
  99. package/edge/components/v/cardActions.vue +18 -0
  100. package/edge/components/v/cardText.vue +18 -0
  101. package/edge/components/v/cardTitle.vue +20 -0
  102. package/edge/components/v/col.vue +56 -0
  103. package/edge/components/v/list.vue +46 -0
  104. package/edge/components/v/listItem.vue +26 -0
  105. package/edge/components/v/listItemTitle.vue +18 -0
  106. package/edge/components/v/row.vue +42 -0
  107. package/edge/components/v/toolbar.vue +24 -0
  108. package/edge/composables/global.ts +519 -0
  109. package/edge-pull.sh +2 -0
  110. package/edge-push.sh +1 -0
  111. package/edge-status.sh +14 -0
  112. package/firebase.json +5 -2
  113. package/firebase_init.sh +21 -6
  114. package/package.json +1 -1
  115. package/plugins/firebase.client.ts +1 -0
  116. package/edge-components-install.sh +0 -1
@@ -0,0 +1,17 @@
1
+ <div class="w-full bg-transparent">
2
+ <div class="p-8 max-w-screen-lg mx-auto">
3
+ <ul class="list-inside">
4
+ {{{#array {"field":"list", "api": "https://api.clearwaterproperties.com/api/front/offices", "apiField": "data", "apiQuery": "", "limit": 4, "value": [] }}}}
5
+ <li class="list-disc">
6
+ {{item.name}} - {{item.geo.lat}} : {{item.geo.lng}}
7
+
8
+ <ul class="ml-[50px] list-disc">
9
+ {{{#subarray:agent {"field":"item.agents", "limit": 0}}}}
10
+ <li>{{agent.name}} (email: {{agent.email}})</li>
11
+ {{{/subarray}}}
12
+ </ul>
13
+ </li>
14
+ {{{/array}}}
15
+ </ul>
16
+ </div>
17
+ </div>
@@ -0,0 +1,7 @@
1
+ <div class="pa-8 bg-brand">
2
+ {{{#array {"field":"list","schema":[{"field":"name","value":"text"}],"collection":{"path":"users","query":[{"field":"name","operator":">","value":""}],"order":[{"field":"name","direction":"asc"}]},"queryOptions":[{"field":"office_id","title":"Office","optionsKey":"label","optionsValue":"value","options":[{"label":"Office 1","value":"7"},{"label":"Office 2","value":"39"},{"label":"Office 3","value":"32"}]},{"field":"userId","title":"Agent","optionsKey":"name","optionsValue":"userId","options":"users"}],"limit":100,"value":[]}}}}
3
+ <h1 class="text-4xl">
4
+ {{item.name}}
5
+ </h1>
6
+ {{{/array}}}
7
+ </div>
@@ -0,0 +1,7 @@
1
+ <div class="p-1 w-full bg-red-200">
2
+ <ul class="list-inside">
3
+ {{{#array {"field":"listthing2", "schema": [{"field": "header", "type": "text"}], "value": [{"header": "testing"}]}}}}
4
+ <li class="list-disc">{{item.header}}</li>
5
+ {{{/array}}}
6
+ </ul>
7
+ </div>
@@ -0,0 +1,103 @@
1
+ <div
2
+ data-carousel
3
+ class="relative overflow-hidden"
4
+ data-carousel-autoplay
5
+ data-carousel-interval="4000"
6
+ data-carousel-loop
7
+ data-carousel-slides-to-scroll="1"
8
+ data-carousel-slides-to-scroll-lg="3"
9
+ >
10
+ <div data-carousel-track class="flex">
11
+ {{{#array {"field":"List","schema":{"header":"text"},"value":[{"header":"One"},{"header":"Two"},{"header":"Three"},{"header":"Four"}]}}}}
12
+ <div class="shrink-0 min-w-0 flex-[0_0_100%] lg:flex-[0_0_33.333%] p-4">
13
+ <div class="bg-white shadow rounded p-6 h-40 flex items-center justify-center">
14
+ {{item.header}}
15
+ </div>
16
+ </div>
17
+ {{{/array}}}
18
+ </div>
19
+
20
+ <button type="button" data-carousel-prev
21
+ class="absolute left-2 top-1/2 -translate-y-1/2 p-2 bg-black/60 text-white rounded-full">‹</button>
22
+
23
+ <button type="button" data-carousel-next
24
+ class="absolute right-2 top-1/2 -translate-y-1/2 p-2 bg-black/60 text-white rounded-full">›</button>
25
+
26
+ <div data-carousel-dots class="mt-3 flex justify-center gap-2"></div>
27
+ </div>
28
+
29
+ <!--
30
+ SUPPORTED DATA ATTRIBUTES (on [data-carousel]):
31
+
32
+ - data-carousel-autoplay
33
+ Enable autoplay (off by default).
34
+
35
+ - data-carousel-interval="MS"
36
+ Milliseconds between slides for autoplay. Default: 5000.
37
+
38
+ - data-carousel-loop
39
+ Enable looping. In our init we disable "containScroll" when loop is on, so it can wrap.
40
+
41
+ - data-carousel-transition="fade"
42
+ Use Embla's fade plugin (best for single-slide view). Omit for multi-slide carousels.
43
+
44
+ - data-carousel-fade-duration="MS"
45
+ Fade transition duration in ms when transition="fade". Default: 400.
46
+
47
+ - data-carousel-no-pause
48
+ Prevent autoplay from pausing on hover/focus.
49
+
50
+ - data-carousel-slides-to-scroll="N"
51
+ Base slidesToScroll (mobile). Default: 1.
52
+
53
+ - data-carousel-slides-to-scroll-md="N"
54
+ slidesToScroll at ≥768px (Tailwind md breakpoint).
55
+
56
+ - data-carousel-slides-to-scroll-lg="N"
57
+ slidesToScroll at ≥1024px (Tailwind lg breakpoint).
58
+
59
+ - data-carousel-slides-to-scroll-xl="N"
60
+ slidesToScroll at ≥1280px (Tailwind xl breakpoint).
61
+
62
+ CHILD SELECTORS:
63
+
64
+ - [data-carousel-track]
65
+ The slide track container (display:flex). Keep overflow clipping on the ROOT, not here.
66
+
67
+ - [data-carousel-prev] / [data-carousel-next]
68
+ Optional buttons. In loop mode we keep them enabled; we also manually wrap when hitting edges.
69
+
70
+ - [data-carousel-dots]
71
+ Optional container; we build dots from Embla's "snap list" (respects slidesToScroll & breakpoints).
72
+
73
+ LAYOUT NOTES:
74
+
75
+ - For deterministic N-up widths, use flex-basis directly:
76
+ • 1-up mobile → class="flex-[0_0_100%]"
77
+ • 3-up ≥lg → class="lg:flex-[0_0_33.333%]"
78
+ (You can also do basis-full/lg:basis-1/3 + shrink-0, but flex-[0_0_*] is the most reliable with carousels.)
79
+
80
+ - Root/viewport should have overflow-hidden. Track should be flex without overflow-hidden.
81
+
82
+ - Multi-up + loop behavior:
83
+ • Looping feels best when you have more slides than visible. With 3-up and only 4 slides it still works,
84
+ but snaps are limited (often just 0 and 1). We added a control "wrap" so Next at the end jumps back to 0
85
+ and Prev at the start jumps to the last snap.
86
+ • Dots represent snaps, not slides. With slidesToScroll=1 at 3-up, expect more frequent dots; with 3 they
87
+ represent pages.
88
+
89
+ - If your layout adapts to container width (not viewport), use Tailwind container queries:
90
+ Wrap the carousel in a "@container" parent, and use classes like "@[1024px]:flex-[0_0_33.333%]".
91
+
92
+ TYPICAL SETUPS:
93
+
94
+ - Single-slide with fade:
95
+ data-carousel-transition="fade"
96
+ data-carousel-fade-duration="800"
97
+ slides use flex-[0_0_100%]
98
+
99
+ - Multi-slide desktop paging by 3:
100
+ data-carousel-slides-to-scroll="1"
101
+ data-carousel-slides-to-scroll-lg="3"
102
+ slides use flex-[0_0_100%] lg:flex-[0_0_33.333%]
103
+ -->
@@ -0,0 +1,69 @@
1
+ <section
2
+ class="relative cms-block cms-block-contact-form-placeholder rounded-2xl border border-dashed border-slate-300 bg-slate-50/70 px-4 py-6 sm:px-6 sm:py-8"
3
+ data-block-type="contact-form-placeholder"
4
+ >
5
+ <!-- OVERLAY BADGE -->
6
+ <div class="pointer-events-none absolute inset-x-0 top-0 z-20 flex justify-center pt-3">
7
+ <div
8
+ class="rounded-full border border-slate-300 bg-white/95 px-4 py-1 text-[11px] font-semibold uppercase tracking-wide text-slate-600 shadow-sm"
9
+ >
10
+ Contact Form Placeholder
11
+ </div>
12
+ </div>
13
+
14
+ <div class="mx-auto max-w-3xl pt-6">
15
+ <!-- Hidden config field: emailTo -->
16
+
17
+ <!-- Header -->
18
+ <div class="mb-6 space-y-2 text-center sm:text-left">
19
+ <h2 class="text-xl font-semibold text-slate-900">
20
+ {{{#text {"field":"formHeader","title":"Form Header","value":"Contact Us"}}}}
21
+ </h2>
22
+ <p class="text-sm text-slate-600">
23
+ {{{#text {"field":"formSubheader","title":"Form Subheader","value":"Subheader content"}}}}
24
+ </p>
25
+ </div>
26
+
27
+ <!-- Fields driven by CMS array (fieldName + fieldType as option) -->
28
+ <div class="space-y-4">
29
+ {{{#array {"field":"formFields","schema":[{"field":"fieldName","type":"text","title":"Field Label"},{"field":"fieldType","type":"option","option":{"optionsKey":"title","optionsValue":"value","options":[{"title":"Text","value":"text"},{"title":"Email","value":"email"},{"title":"Phone","value":"tel"},{"title":"Textarea","value":"textarea"}]},"value":"text"}],"value":[{"fieldName":"Name","fieldType":"text"},{"fieldName":"Email","fieldType":"email"},{"fieldName":"Message","fieldType":"textarea"}]}}}}
30
+ <div class="space-y-1">
31
+ <!-- Label preview -->
32
+ <p class="text-xs font-medium uppercase tracking-wide text-slate-600">
33
+ {{item.fieldName}}
34
+ {{{#if {"cond":"item.fieldType == 'textarea'"}}}}
35
+ <span class="ml-1 text-[10px] h-[300px] font-normal text-slate-400">
36
+ ({{item.fieldType}})
37
+ </span>
38
+ <div class="w-full rounded-lg bg-slate-100 h-20"></div>
39
+ {{{#else}}}
40
+ <span class="ml-1 text-[10px] font-normal text-slate-400">
41
+ ({{item.fieldType}})
42
+ </span>
43
+ <div class="w-full rounded-lg bg-slate-100 h-10"></div>
44
+ {{{/if}}}
45
+ </p>
46
+
47
+ <!-- Input preview skeleton (visual only) -->
48
+
49
+ </div>
50
+ {{{/array}}}
51
+ </div>
52
+
53
+ <!-- Submit button preview -->
54
+ <div class="mt-6">
55
+ <button
56
+ type="button"
57
+ class="inline-flex w-full items-center justify-center rounded-lg bg-slate-900 px-4 py-2.5 text-sm font-semibold text-white shadow-sm disabled:cursor-not-allowed disabled:opacity-60 sm:w-auto"
58
+ disabled
59
+ >
60
+ {{{#text {"field":"buttonText","title":"Button Text","value":"Send Message"}}}}
61
+ </button>
62
+ </div>
63
+
64
+ <!-- Tiny note -->
65
+ <p class="mt-3 text-xs text-slate-400">
66
+ The CMS uses <code>emailTo</code> <span class="font-bold underline">({{{#text {"field":"emailTo","title":"Email To","value":"test@testing.com"}}}})</span> and <code>formFields</code> (with fieldType options) to build the real form on the live site.
67
+ </p>
68
+ </div>
69
+ </section>
@@ -0,0 +1,27 @@
1
+ <section class="bg-gray-100 text-gray-900 py-16">
2
+ <div class="max-w-7xl mx-auto px-4">
3
+ <div class="flex flex-col md:flex-row items-center gap-12">
4
+
5
+ <!-- Image -->
6
+ <div class="w-full md:w-1/2">
7
+ <img
8
+ src="{{{#image {"field": "backgroundImage", "value": "https://www.clearwaterproperties.com/image/static/hp-recreational.jpg" }}}}"
9
+ alt="David Hatef"
10
+ loading="lazy"
11
+ class="w-full h-auto rounded shadow-lg"
12
+ />
13
+ </div>
14
+
15
+ <!-- Text -->
16
+ <div class="w-full md:w-1/2 space-y-6">
17
+ <h2 class="text-3xl md:text-4xl font-bold">
18
+ {{{#text {"field": "header", "value": "" }}}}
19
+ </h2>
20
+ <div class="text-lg text-gray-700 leading-relaxed space-y-4">
21
+ {{{#richtext {"field": "content", "value": "" }}}}
22
+ </div>
23
+ </div>
24
+
25
+ </div>
26
+ </div>
27
+ </section>
@@ -0,0 +1,24 @@
1
+ <footer id="global-footer" class="bg-neutral-900 text-white">
2
+ <div class="mx-auto max-w-7xl px-6 py-12"> <!-- Top: Brand / Map / Contact -->
3
+ <div class="grid gap-10 md:grid-cols-3"> <!-- Brand / Title -->
4
+ <div class="text-center md:text-left">
5
+ <h6 class="text-lg font-semibold tracking-wide">{{{#text {"field":"name","value":""}}}}</h6>
6
+ <p class="mt-2 whitespace-pre-line text-sm text-neutral-300"> {{{#text {"field":"title","value":""}}}} </p>
7
+ </div> <!-- Map -->
8
+ <div> </div> <!-- Contact -->
9
+ <div class="text-center md:text-left">
10
+ <h6 class="text-lg font-semibold tracking-wide">Contact Us</h6>
11
+ <div class="mt-2 space-y-2 text-sm">
12
+ <p> Phone: <a class="font-normal hover:opacity-90 underline underline-offset-4" href="tel:2025502730"
13
+ aria-label="Call (202) 550-2730"> {{{#text {"field":"phone","value":""}}}} </a> </p>
14
+ <p> Office: <a class="font-normal hover:opacity-90 underline underline-offset-4" href="tel:6154755616"
15
+ aria-label="Call (615) 475-5616"> {{{#text {"field":"officeNumber","value":""}}}} </a> </p>
16
+ <div>
17
+ <div class="text-neutral-300">{{{#text {"field":"streetAddress","value":""}}}}</div>
18
+ <div class="text-neutral-300">{{{#text {"field":"city","value":"","title":"City, State & Zip"}}}}</div>
19
+ </div>
20
+ </div>
21
+ </div>
22
+ </div>
23
+ </div>
24
+ </footer>
@@ -0,0 +1,7 @@
1
+ <div class="p-1 w-full bg-brand text-center text-black">
2
+ <img class="mx-auto" src={{{#image {"field": "image", "title": "The Image", "value": "" }}}} />
3
+ <h1 class="text-xl font-brand">{{{#text {"field": "mainHeader", "value": "" }}}}</h1>
4
+ <h3 class="text-sm">{{{#text {"field": "subHeader", "value": "" }}}}</h3>
5
+ <h3 class="text-sm">{{{#text {"field":"lastHeader","option":{"optionsKey":"title","optionsValue":"value","options":[{"title":"Test","value":"test"},{"title":"Test 2","value":"test2"},{"title":"Test 3","value":"test3"}]},"value":"test"}}}}</h3>
6
+ <h3 class="text-8xl">{{{#text {"field":"lastHeader2","option":{"optionsKey":"name","optionsValue":"name","options":"users"},"value":"test"}}}}</h3>
7
+ </div>
@@ -0,0 +1,35 @@
1
+ <section
2
+ class="relative h-[50dvh] min-h-[260px] w-full bg-cover bg-center"
3
+ style="background-image: url('{{{#image {"field": "backgroundImage", "value": "https://www.clearwaterproperties.com/image/static/hp-recreational.jpg" }}}}')"
4
+ >
5
+ <!-- Dark overlay -->
6
+ <div class="absolute inset-0 bg-black/40"></div>
7
+
8
+ <!-- Centered content -->
9
+ <div class="relative z-10 flex h-full items-center justify-center">
10
+ <div class="px-6 text-center text-white">
11
+ <p class="text-[0.65rem] md:text-sm tracking-[0.25em] uppercase opacity-80">
12
+ {{{#text {"field": "topHeader", "value": "" }}}}
13
+ </p>
14
+
15
+ <h1 class="mt-4 text-4xl md:text-6xl font-semibold tracking-[0.35em]">
16
+ {{{#text {"field": "middleHeader", "value": "" }}}}
17
+ </h1>
18
+
19
+ <p class="mt-4 text-sm md:text-base italic opacity-90">
20
+ {{{#text {"field": "bottomHeader", "value": "" }}}}
21
+ </p>
22
+
23
+ <a
24
+ href="{{{#text {"field": "buttonURL", title: "Button URL", "value": "" }}}}"
25
+ class="mt-8 inline-block border border-white/80 px-6 py-3 text-[0.7rem] md:text-xs uppercase tracking-[0.2em]
26
+ transition hover:bg-white hover:text-neutral-900 focus:outline-none focus:ring-2 focus:ring-white/80"
27
+ >
28
+ {{{#text {"field": "buttonText", "value": "Search All Homes" }}}}
29
+ </a>
30
+ </div>
31
+ </div>
32
+
33
+ <!-- Optional vignette -->
34
+ <div class="pointer-events-none absolute inset-0 bg-gradient-to-b from-black/20 via-transparent to-black/30"></div>
35
+ </section>
@@ -0,0 +1,52 @@
1
+ <section
2
+ class="relative h-[50dvh] min-h-[260px] w-full bg-cover bg-center"
3
+ style="background-image: url('{{{#image {"field":"backgroundImage","value":"https://www.clearwaterproperties.com/image/static/hp-recreational.jpg","tags":["Backgrounds"]}}}}')"
4
+ >
5
+ <!-- Dark overlay -->
6
+ <div class="absolute inset-0 bg-black/40"></div>
7
+
8
+ <!-- Centered content -->
9
+ <div class="relative z-10 flex h-full items-center justify-center">
10
+ <div class="px-6 text-center text-white">
11
+ <div
12
+ data-carousel
13
+ class="relative overflow-hidden"
14
+ data-carousel-autoplay
15
+ data-carousel-interval="6000"
16
+ data-carousel-loop
17
+ data-carousel-slides-to-scroll="1"
18
+ data-carousel-no-pause
19
+ data-carousel-transition="fade"
20
+ >
21
+ <div data-carousel-track class="flex">
22
+ {{{#array {"field":"Slides","schema":[{"field":"topHeader","value":"text"},{"field":"middleHeader","type":"text"}],"value":[{"topHeader":"Lorem ipsum dolor sit amet.","middleHeader":"Lorem ipsum dolor sit amet."},{"topHeader":"Sit amet lorem.","middleHeader":"Sit amet lorem."}]}}}}
23
+ <div class="shrink-0 min-w-0 flex-[0_0_100%]">
24
+ <div class="text-center text-white">
25
+ <p class="text-[0.65rem] md:text-sm tracking-[0.35em] uppercase opacity-80 -ml-3">
26
+ {{item.topHeader}}
27
+ </p>
28
+ <h1 class="mt-4 text-2xl md:text-5xl font-semibold font-brand tracking-[0.35em]">
29
+ {{item.middleHeader}}
30
+ </h1>
31
+ </div>
32
+ </div>
33
+ {{{/array}}}
34
+ </div>
35
+ </div>
36
+ <p class="mt-4 text-sm md:text-base italic opacity-90">
37
+ {{{#text {"field": "bottomHeader", "value": "" }}}}
38
+ </p>
39
+
40
+ <a
41
+ href="{{{#text {"field": "buttonURL", title: "Button URL", "value": "" }}}}"
42
+ class="mt-8 inline-block border border-white/80 px-6 py-3 text-[0.7rem] md:text-xs uppercase tracking-[0.2em]
43
+ transition hover:bg-white hover:text-neutral-900 focus:outline-none focus:ring-2 focus:ring-white/80"
44
+ >
45
+ {{{#text {"field": "buttonText", "value": "Search All Homes" }}}}
46
+ </a>
47
+ </div>
48
+ </div>
49
+
50
+ <!-- Optional vignette -->
51
+ <div class="pointer-events-none absolute inset-0 bg-gradient-to-b from-black/20 via-transparent to-black/30"></div>
52
+ </section>
@@ -0,0 +1,117 @@
1
+ <section
2
+ class="relative cms-block cms-block-newsletter-placeholder rounded-2xl border border-dashed border-slate-300 bg-slate-50/70 px-4 py-6 sm:px-6 sm:py-8"
3
+ data-block-type="newsletter-placeholder"
4
+ >
5
+ <!-- OVERLAY BADGE -->
6
+ <div
7
+ class="pointer-events-none absolute inset-x-0 top-0 z-20 flex justify-center pt-3"
8
+ >
9
+ <div
10
+ class="rounded-full border border-slate-300 bg-white/95 px-4 py-1 text-[11px] font-semibold uppercase tracking-wide text-slate-600 shadow-sm"
11
+ >
12
+ Newsletter Placeholder
13
+ </div>
14
+ </div>
15
+
16
+ <div class="mx-auto max-w-2xl pt-6">
17
+ <!-- Hidden config field: emailTo -->
18
+
19
+ <!-- Header -->
20
+ <div class="mb-6 space-y-2 text-center sm:text-left">
21
+ <h2 class="text-xl font-semibold text-slate-900">
22
+ {{{#text {"field":"newsletterHeader","title":"Header","value":"Stay in the Loop"}}}}
23
+ </h2>
24
+ <p class="text-sm text-slate-600">
25
+ {{{#text {"field":"newsletterSubheader","title":"Subheader","value":"Get our latest news and updates"}}}}
26
+ </p>
27
+ </div>
28
+
29
+ <!-- Fields driven by CMS array (fieldName + fieldType as option) -->
30
+ <div class="space-y-4">
31
+ {{{#array {
32
+ "field":"newsletterFields",
33
+ "title":"Form Fields",
34
+ "schema":[
35
+ {
36
+ "field":"fieldName",
37
+ "type":"text",
38
+ "title":"Field Label"
39
+ },
40
+ {
41
+ "field":"fieldType",
42
+ "type":"option",
43
+ "title":"Field Type",
44
+ "option":{
45
+ "optionsKey":"title",
46
+ "optionsValue":"value",
47
+ "options":[
48
+ {
49
+ "title":"Text",
50
+ "value":"text"
51
+ },
52
+ {
53
+ "title":"Email",
54
+ "value":"email"
55
+ },
56
+ {
57
+ "title":"Phone",
58
+ "value":"tel"
59
+ },
60
+ {
61
+ "title":"Textarea",
62
+ "value":"textarea"
63
+ }
64
+ ]
65
+ },
66
+ "value":"text"
67
+ }
68
+ ],
69
+ "value":[
70
+ {
71
+ "fieldName":"Name",
72
+ "fieldType":"text"
73
+ },
74
+ {
75
+ "fieldName":"Email",
76
+ "fieldType":"email"
77
+ }
78
+ ]
79
+ }}}}
80
+ <div class="space-y-1">
81
+ <!-- Label preview -->
82
+ <p class="text-xs font-medium uppercase tracking-wide text-slate-600">
83
+ {{item.fieldName}}
84
+ {{{#if {"cond":"item.fieldType == 'textarea'"}}}}
85
+ <span class="ml-1 text-[10px] font-normal text-slate-400">
86
+ ({{item.fieldType}})
87
+ </span>
88
+ <div class="w-full rounded-lg bg-slate-100 h-20"></div>
89
+ {{{#else}}}
90
+ <span class="ml-1 text-[10px] font-normal text-slate-400">
91
+ ({{item.fieldType}})
92
+ </span>
93
+ <div class="w-full rounded-lg bg-slate-100 h-10"></div>
94
+ {{{/if}}}
95
+ </p>
96
+ </div>
97
+ {{{/array}}}
98
+ </div>
99
+
100
+ <!-- Submit button preview -->
101
+ <div class="mt-6">
102
+ <button
103
+ type="button"
104
+ class="inline-flex w-full items-center justify-center rounded-lg bg-slate-900 px-4 py-2.5 text-sm font-semibold text-white shadow-sm disabled:cursor-not-allowed disabled:opacity-60 sm:w-auto"
105
+ disabled
106
+ >
107
+ {{{#text {"field":"buttonText","title":"Button Text","value":"Subscribe"}}}}
108
+ </button>
109
+ </div>
110
+
111
+ <!-- Tiny note -->
112
+ <p class="mt-3 text-xs text-slate-400">
113
+ The CMS uses <code>emailTo</code> and <code>newsletterFields</code> (array of fieldName + fieldType)
114
+ to build the real newsletter signup form on the live site.
115
+ </p>
116
+ </div>
117
+ </section>
@@ -0,0 +1,7 @@
1
+ <div class="pa-8 bg-white font-sans">
2
+ {{{#array {"field":"list","schema":[{"field":"name","value":"text"},{"field":"content","value":"richtext"}],"collection":{"path":"post","query":[{"field":"doc_created_at","operator":">","value":0}],"order":[{"field":"doc_created_at","direction":"desc"}]},"queryOptions":[],"limit":1,"value":[]}}}}
3
+ <h1 class="text-sm">
4
+ {{item.content}}
5
+ </h1>
6
+ {{{/array}}}
7
+ </div>
@@ -0,0 +1,21 @@
1
+ {{{#array {"field":"list","schema":[{"field":"name","value":"text"}],"collection":{"path":"post","query":[{"field":"doc_created_at","operator":">","value":0}],"order":[{"field":"doc_created_at","direction":"desc"}]},"queryOptions":[],"limit":1,"value":[]}}}}
2
+ <section
3
+ class="relative h-[50dvh] min-h-[260px] w-full bg-cover bg-center"
4
+ style="background-image: url('{{item.featuredImage}}')"
5
+ >
6
+ <!-- Dark overlay -->
7
+ <div class="absolute inset-0 bg-black/40"></div>
8
+
9
+ <!-- Centered content -->
10
+ <div class="relative z-10 flex h-full items-center justify-center">
11
+ <div class="px-6 text-center text-white">
12
+ <h1 class="mt-4 !text-3xl md:!text-4xl font-brand font-semibold tracking-[0.35em]">
13
+ {{item.title}}
14
+ </h1>
15
+ </div>
16
+ </div>
17
+
18
+ <!-- Optional vignette -->
19
+ <div class="pointer-events-none absolute inset-0 bg-gradient-to-b from-black/20 via-transparent to-black/30"></div>
20
+ </section>
21
+ {{{/array}}}
@@ -0,0 +1,20 @@
1
+ <div class="p-8 bg-gray-50 font-sans">
2
+ <div class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
3
+ {{{#array {"field":"list","schema":[{"field":"name","value":"text"},{"field":"content","value":"richtext"}],"collection":{"path":"posts","query":[{"field":"doc_created_at","operator":">","value":0}],"order":[{"field":"doc_created_at","direction":"desc"}]},"queryOptions":[{"field":"tags","operator":"array-contains-any","options":[]}],"limit":10,"value":[]}}}}
4
+
5
+ <div class="bg-white rounded-2xl shadow-sm hover:shadow-md transition-shadow duration-200 overflow-hidden flex flex-col">
6
+ <img src="{{item.featuredImage}}" alt="{{item.title}}" class="h-48 w-full object-cover" />
7
+ <div class="p-5 flex flex-col flex-1">
8
+ <h2 class="text-lg font-semibold text-gray-800 mb-2 line-clamp-2">{{ item.title }}</h2>
9
+ <div class="text-gray-600 text-sm flex-1 mb-4 line-clamp-3">
10
+ {{ item.blurb }}
11
+ </div>
12
+ <a href="/posts/{{item.name}}" class="inline-block mt-auto self-start text-sm font-medium text-blue-600 hover:text-blue-800 transition-colors duration-150">
13
+ Read More →
14
+ </a>
15
+ </div>
16
+ </div>
17
+
18
+ {{{/array}}}
19
+ </div>
20
+ </div>
@@ -0,0 +1,100 @@
1
+ <section class="bg-gray-100 text-gray-900 py-16">
2
+ <div class="max-w-7xl mx-auto px-4 py-4">
3
+ <div class="text-center space-y-2">
4
+ <h2 class="text-3xl md:text-5xl font-semibold tracking-wide">
5
+ {{{#text {"field": "mainHeader", "value": "" }}}}
6
+ </h2>
7
+ <p class="text-base text-gray-600">
8
+ {{{#text {"field": "subHeader", "value": "" }}}}
9
+ </p>
10
+ <p>
11
+ {{{#text {"field": "thirdHeader", "value": "Hello" }}}}
12
+ </p>
13
+ </div>
14
+ </div>
15
+
16
+ <div class="max-w-7xl mx-auto px-4 grid md:grid-cols-2 lg:grid-cols-3 gap-6">
17
+ {{{#array {"field":"showcaseproperties","schema":{"listing_price":"money","square_feet":"number","acres":"number"},"api":"https://api.clearwaterproperties.com/api/front/properties","apiField":"data","apiQuery":"","queryOptions":[{"field":"sort","optionsKey":"label","optionsValue":"value","options":[{"label":"Highest Price","value":"listing_price"},{"label":"Lowest Price","value":"-listing_price"},{"label":"Newest","value":"-list_date"}]},{"field":"filter_scope[agent][]","title":"Agent","optionsKey":"name","optionsValue":"mls.primary","options":"users"}],"limit":6,"value":[]}}}}
18
+
19
+ <!-- Loading state -->
20
+ <div class="{{loading}} min-w-0">
21
+ <div class="block w-full bg-white shadow-md rounded-lg overflow-hidden animate-pulse">
22
+ <!-- Image skeleton -->
23
+ <div class="relative w-full h-64 bg-gray-200">
24
+ <!-- Top left badge -->
25
+ <div class="absolute top-2 left-2 w-16 h-5 bg-gray-300 rounded"></div>
26
+ <!-- Bottom right badge -->
27
+ <div class="absolute bottom-2 right-2 w-20 h-5 bg-gray-300 rounded"></div>
28
+ </div>
29
+
30
+ <!-- Content skeleton -->
31
+ <div class="p-4 space-y-3">
32
+ <!-- Price -->
33
+ <div class="w-32 h-6 bg-gray-300 rounded"></div>
34
+ <!-- Address -->
35
+ <div class="w-48 h-4 bg-gray-200 rounded"></div>
36
+ <!-- Stats -->
37
+ <div class="flex gap-4 mt-2">
38
+ <div class="w-12 h-4 bg-gray-200 rounded"></div>
39
+ <div class="w-12 h-4 bg-gray-200 rounded"></div>
40
+ <div class="w-16 h-4 bg-gray-200 rounded"></div>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ </div>
45
+
46
+ <!-- Loaded state -->
47
+ <div class="{{loaded}} min-w-0">
48
+ <a class="block w-full bg-white shadow-md rounded-lg overflow-hidden">
49
+ <div class="relative w-full h-64 bg-gray-200 bg-[url('/images/placeholder.jpg')] bg-cover bg-center">
50
+ <img
51
+ src="{{item.thumbnail_url}}"
52
+ alt="For Sale"
53
+ loading="lazy"
54
+ class="block w-full h-full object-cover"
55
+ />
56
+
57
+ <div class="absolute top-2 left-2 bg-black/70 text-white px-2 py-1 rounded text-xs">
58
+ {{item.listing_status}}
59
+ </div>
60
+ <div class="absolute bottom-2 right-2 bg-white/80 text-gray-800 px-2 py-1 rounded text-xs">
61
+ MLS® {{item.display_mlsnumber}}
62
+ </div>
63
+ </div>
64
+
65
+ <div class="p-4">
66
+ <h4 class="text-xl font-bold text-gray-800">{{item.listing_price}}</h4>
67
+ <div class="text-gray-600 mt-1 truncate">
68
+ {{item.display_address}}
69
+ </div>
70
+ <div class="flex gap-4 mt-2 text-sm text-gray-700">
71
+ {{{#if {"cond": "item.total_bedrooms > 0"}}}}
72
+ <span>{{item.total_bedrooms}} Beds</span>
73
+ {{{/if}}}
74
+ {{{#if {"cond": "item.total_bathrooms > 0"}}}}
75
+ <span>{{item.total_bathrooms}} Baths</span>
76
+ {{{/if}}}
77
+ {{{#if {"cond": "item.property_type == 'ranch'"}}}}
78
+ <span>{{item.acres}} Acres</span>
79
+ {{{#else}}}
80
+ {{{#if {"cond": "item.square_feet > 0"}}}}
81
+ <span>{{item.square_feet}} Sq.Ft.</span>
82
+ {{{/if}}}
83
+ {{{/if}}}
84
+ </div>
85
+ </div>
86
+ </a>
87
+ </div>
88
+
89
+ {{{/array}}}
90
+ </div>
91
+
92
+ <div class="max-w-7xl mx-auto px-4 py-4 text-center">
93
+ <a
94
+ href="{{{#text {"field": "buttonURL", "value": "" }}}}"
95
+ class="inline-flex items-center justify-center rounded-lg px-5 py-3 text-sm font-semibold bg-white text-gray-900 ring-1 ring-gray-300 shadow hover:bg-gray-50 hover:shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-gray-900 transition"
96
+ >
97
+ {{{#text {"field": "buttonText", "value": "View More" }}}}
98
+ </a>
99
+ </div>
100
+ </section>