functionality build out

This commit is contained in:
Philip Peterson 2026-06-01 01:47:26 -07:00
parent 8c5ce93d79
commit 1d9b1c1625
7 changed files with 192 additions and 10 deletions

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,85 @@
import { h, render } from "https://esm.sh/preact@10";
import { useState } from "https://esm.sh/preact@10/hooks";
import { html } from "https://esm.sh/htm@3/preact";
const FAQS = [
{
q: "What should I expect at my first appointment?",
a: "Your first visit is a comprehensive diagnostic assessment lasting about an hour. We evaluate your movement, strength, pain points, and medical history to build a personalized treatment plan before any hands-on therapy begins.",
},
{
q: "Do I need a referral from my doctor?",
a: "In most cases, no. Washington state allows direct access to physical therapy, so you can book an appointment without a physician referral. However, some insurance plans require one -- check with your provider to be sure.",
},
{
q: "How long does a typical course of treatment last?",
a: "It depends on your condition and goals. Some patients see resolution in four to six weeks; others with surgical rehab or neurological conditions may work with us for several months. We set clear milestones at the start so you always know where you stand.",
},
{
q: "What insurances do you accept?",
a: "We accept most major insurance plans including Premera, Regence, Aetna, Cigna, and Medicare. We also offer self-pay rates. Contact us before your first appointment and we'll verify your coverage.",
},
{
q: "Can I book an appointment online?",
a: "Yes. Use the booking tool on our Schedule page to pick a service type, choose an available slot, and confirm your appointment. You'll receive a confirmation email immediately.",
},
{
q: "What should I wear or bring to my session?",
a: "Wear comfortable, loose-fitting clothing that allows easy access to the area being treated. Bring a photo ID, your insurance card, and any relevant imaging (X-rays, MRI reports) on your first visit.",
},
];
function FaqItem({ item, open, onToggle }) {
return html`
<div class="border-b border-gray-200">
<button
onClick=${onToggle}
class="w-full flex items-center justify-between gap-6 py-5 text-left cursor-pointer bg-transparent"
aria-expanded=${open}
>
<span class="text-[1.0625rem] text-gray-900">${item.q}</span>
<span class="shrink-0 text-xl text-gray-400 leading-none" style=${{ display: "inline-block", transform: open ? "rotate(45deg)" : "none", transition: "transform 0.2s ease" }}>+</span>
</button>
<div style=${{ display: "grid", gridTemplateRows: open ? "1fr" : "0fr", transition: "grid-template-rows 0.25s ease" }}>
<div style="overflow:hidden">
<p class="text-[15px] text-gray-500 leading-relaxed pb-5">${item.a}</p>
</div>
</div>
</div>
`;
}
function Faq() {
const [openIndex, setOpenIndex] = useState(null);
const toggle = function(i) {
setOpenIndex(function(prev) { return prev === i ? null : i; });
};
return html`
<div class="py-20 px-6 bg-white">
<h2 class="text-[clamp(3.5rem,8vw,6rem)] font-serif font-normal text-gray-900 text-center mb-16 leading-none">FAQ</h2>
<div class="max-w-[860px] mx-auto border-t border-gray-200">
${FAQS.map((item, i) => html`
<${FaqItem}
key=${i}
item=${item}
open=${openIndex === i}
onToggle=${function() { toggle(i); }}
/>
`)}
</div>
</div>
`;
}
class RptFaq extends HTMLElement {
connectedCallback() {
render(html`<${Faq} />`, this);
}
disconnectedCallback() {
render(null, this);
}
}
customElements.define("rpt-faq", RptFaq);

View file

@ -0,0 +1,97 @@
import { h, render } from "https://esm.sh/preact@10";
import { useState } from "https://esm.sh/preact@10/hooks";
import { html } from "https://esm.sh/htm@3/preact";
const TESTIMONIALS = [
{
name: "Sarah M.", category: "Sports Rehab Patient", initials: "SM", color: "#8ab4be",
quote: "After my ACL tear I was terrified I'd never run again. The team here built a plan that had me back on the field in four months. Every session felt purposeful.",
},
{
name: "Leon N.", category: "Neurology Patient", initials: "LN", color: "#a3bfc8",
quote: "Every new patient begins with a comprehensive diagnostic assessment. From there, they create a fully personalized treatment plan tailored to your goals -- whether that means returning to sport, recovering from surgery, or restoring function.",
},
{
name: "Diana K.", category: "Surgery Rehab Patient", initials: "DK", color: "#7aa3af",
quote: "Six weeks post-hip replacement and I was walking without a cane -- weeks ahead of what my surgeon expected. The therapists here are genuinely invested in your outcome, not just checking boxes.",
},
{
name: "Marcus T.", category: "Sports Rehab Patient", initials: "MT", color: "#6b9dab",
quote: "I came in with chronic shoulder pain that three other clinics couldn't resolve. Two months in, I'm lifting overhead for the first time in years. The diagnostic process here is legitimately different.",
},
{
name: "Rachel O.", category: "Surgery Rehab Patient", initials: "RO", color: "#93b8c3",
quote: "The booking process is seamless and the staff remembers you. I never felt like just another patient. My recovery from rotator cuff surgery exceeded every milestone.",
},
{
name: "James P.", category: "Neurology Patient", initials: "JP", color: "#80aab5",
quote: "After my stroke the neurological therapy program here gave me my independence back. The team combined manual therapy with targeted exercise in a way that made real, measurable progress every single week.",
},
];
const CARD_W = 270;
const GAP = 20;
function Testimonials() {
const [index, setIndex] = useState(0);
const prev = () => setIndex(function(i) { return Math.max(0, i - 1); });
const next = () => setIndex(function(i) { return Math.min(TESTIMONIALS.length - 1, i + 1); });
const leftEdge = "max(1.5rem, calc((100vw - 1248px) / 2 + 1.5rem))";
return html`
<div style="overflow:hidden">
<div class="py-16" style=${{ paddingLeft: leftEdge }}>
<div class="mb-10 pr-6">
<p class="text-xs tracking-widest uppercase text-[#306f8e] font-semibold mb-4">Testimonials</p>
<div class="flex items-end justify-between gap-6">
<h2 class="text-[clamp(1.75rem,3vw,2.5rem)] font-serif font-normal text-gray-900 leading-tight max-w-[520px]">
Don&rsquo;t take our word for it.<br />Hear it from our patients!
</h2>
<div class="flex gap-3 pb-1 shrink-0">
<button
onClick=${prev}
disabled=${index === 0}
aria-label="Previous testimonials"
class="w-10 h-10 rounded-full border border-gray-300 flex items-center justify-center text-gray-500 hover:bg-gray-100 transition-colors disabled:opacity-30"
>&#8592;</button>
<button
onClick=${next}
disabled=${index === TESTIMONIALS.length - 1}
aria-label="Next testimonials"
class="w-10 h-10 rounded-full border border-gray-300 flex items-center justify-center text-gray-500 hover:bg-gray-100 transition-colors disabled:opacity-30"
>&#8594;</button>
</div>
</div>
</div>
<div style=${{ display: "flex", gap: GAP + "px", transform: "translateX(-" + (index * (CARD_W + GAP)) + "px)", transition: "transform 0.5s ease", paddingBottom: "2px" }}>
${TESTIMONIALS.map((t, i) => html`
<div key=${i} style=${{ width: CARD_W + "px", flexShrink: 0 }} class="border border-gray-200 rounded-lg p-6 flex flex-col gap-5 bg-white">
<div
class="w-14 h-14 rounded-full flex items-center justify-center text-white font-semibold text-base shrink-0"
style=${{ backgroundColor: t.color }}
>${t.initials}</div>
<p class="text-[15px] text-gray-700 leading-relaxed flex-1">${t.quote}</p>
<div>
<p class="text-xl font-serif text-gray-900 mb-0.5">${t.name}</p>
<p class="text-xs tracking-widest uppercase text-[#306f8e] font-semibold">${t.category}</p>
</div>
</div>
`)}
</div>
</div>
</div>
`;
}
class RptTestimonials extends HTMLElement {
connectedCallback() {
render(html`<${Testimonials} />`, this);
}
disconnectedCallback() {
render(null, this);
}
}
customElements.define("rpt-testimonials", RptTestimonials);

View file

@ -191,7 +191,7 @@ function _riverside_pt_rebuild(): void {
} }
\Drupal::configFactory()->getEditable('system.site') \Drupal::configFactory()->getEditable('system.site')
->set('name', 'Riverside Physical Therapy') ->set('name', 'Riverside Therapeutics')
->set('page.front', '/home') ->set('page.front', '/home')
->save(); ->save();
} }

View file

@ -6,6 +6,8 @@ app:
js/nav.js: {} js/nav.js: {}
js/components/rpt-toggle.js: { attributes: { type: module } } js/components/rpt-toggle.js: { attributes: { type: module } }
js/components/rpt-carousel.js: { attributes: { type: module } } js/components/rpt-carousel.js: { attributes: { type: module } }
js/components/rpt-testimonials.js: { attributes: { type: module } }
js/components/rpt-faq.js: { attributes: { type: module } }
schedule: schedule:
css: css:
theme: theme:

View file

@ -2,7 +2,7 @@ riverside_pt.home:
path: '/home' path: '/home'
defaults: defaults:
_controller: '\Drupal\riverside_pt\Controller\HomeController::page' _controller: '\Drupal\riverside_pt\Controller\HomeController::page'
_title: 'Riverside Physical Therapy' _title: 'Welcome'
requirements: requirements:
_permission: 'access content' _permission: 'access content'

View file

@ -60,7 +60,7 @@
</div> </div>
</div> </div>
<div class="flex flex-col border border-[#b8d4dc] bg-white overflow-hidden"> <div class="flex flex-col border border-[#b8d4dc] bg-white overflow-hidden">
<img src="/modules/custom/riverside_pt/images/panels/2.jpg" alt="Sports rehabilitation" class="w-full h-48 object-cover" /> <img src="/modules/custom/riverside_pt/images/panels/2.jpg?v=2" alt="Sports rehabilitation" class="w-full h-48 object-cover" />
<div class="flex flex-col gap-4 p-6 flex-1"> <div class="flex flex-col gap-4 p-6 flex-1">
<h3 class="text-2xl font-normal text-gray-900">Sports Rehabilitation</h3> <h3 class="text-2xl font-normal text-gray-900">Sports Rehabilitation</h3>
<p class="text-[15px] text-gray-600 leading-relaxed flex-1">We help athletes recover from injury and return to peak performance with targeted, sport-specific programs built around your body and your goals.</p> <p class="text-[15px] text-gray-600 leading-relaxed flex-1">We help athletes recover from injury and return to peak performance with targeted, sport-specific programs built around your body and your goals.</p>
@ -111,12 +111,8 @@
</div> </div>
</section> </section>
<section class="py-16 bg-white"> <section class="bg-white">
<div class="max-w-[1040px] mx-auto px-6 mb-8"> <rpt-testimonials class="block"></rpt-testimonials>
<h2 class="text-3xl font-bold text-blue-900 mb-1.5">Our Facility</h2>
<p class="text-[17px] text-gray-500">A look inside our clinic</p>
</div>
<rpt-carousel class="block"></rpt-carousel>
</section> </section>
<section class="py-24 px-6 bg-white"> <section class="py-24 px-6 bg-white">
@ -134,3 +130,5 @@
</fieldset> </fieldset>
</div> </div>
</section> </section>
<rpt-faq class="block"></rpt-faq>