@notionhive/testimonials 0.1.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.
Files changed (139) hide show
  1. package/bin/testimonials.js +16 -0
  2. package/category.config.json +7 -0
  3. package/package.json +24 -0
  4. package/registry/index.json +174 -0
  5. package/registry/testimonial-01.json +10 -0
  6. package/registry/testimonial-02.json +10 -0
  7. package/registry/testimonial-03.json +10 -0
  8. package/registry/testimonial-04.json +10 -0
  9. package/registry/testimonial-05.json +9 -0
  10. package/registry/testimonial-06.json +10 -0
  11. package/registry/testimonial-07.json +10 -0
  12. package/registry/testimonial-08.json +10 -0
  13. package/registry/testimonial-09.json +10 -0
  14. package/registry/testimonial-10.json +10 -0
  15. package/registry/testimonial-11.json +10 -0
  16. package/registry/testimonial-12.json +10 -0
  17. package/registry/testimonial-13.json +10 -0
  18. package/registry/testimonial-14.json +9 -0
  19. package/registry/testimonial-15.json +10 -0
  20. package/registry/testimonial-16.json +10 -0
  21. package/registry/testimonial-17.json +10 -0
  22. package/templates/components/atoms/SafeImage/SafeImage.jsx +101 -0
  23. package/templates/components/atoms/SafeImage/index.js +1 -0
  24. package/templates/components/hooks/useCarousel.js +73 -0
  25. package/templates/components/organisms/Testimonial01/Testimonial01.jsx +171 -0
  26. package/templates/components/organisms/Testimonial01/Testimonial01.propTypes.js +82 -0
  27. package/templates/components/organisms/Testimonial01/index.js +1 -0
  28. package/templates/components/organisms/Testimonial02/Testimonial02.jsx +200 -0
  29. package/templates/components/organisms/Testimonial02/Testimonial02.propTypes.js +75 -0
  30. package/templates/components/organisms/Testimonial02/index.js +1 -0
  31. package/templates/components/organisms/Testimonial03/Testimonial03.jsx +208 -0
  32. package/templates/components/organisms/Testimonial03/Testimonial03.propTypes.js +90 -0
  33. package/templates/components/organisms/Testimonial03/index.js +1 -0
  34. package/templates/components/organisms/Testimonial04/Testimonial04.jsx +242 -0
  35. package/templates/components/organisms/Testimonial04/Testimonial04.propTypes.js +81 -0
  36. package/templates/components/organisms/Testimonial04/index.js +1 -0
  37. package/templates/components/organisms/Testimonial05/Testimonial05.jsx +88 -0
  38. package/templates/components/organisms/Testimonial05/Testimonial05.propTypes.js +29 -0
  39. package/templates/components/organisms/Testimonial05/index.js +1 -0
  40. package/templates/components/organisms/Testimonial06/Testimonial06.jsx +203 -0
  41. package/templates/components/organisms/Testimonial06/Testimonial06.propTypes.js +106 -0
  42. package/templates/components/organisms/Testimonial06/index.js +1 -0
  43. package/templates/components/organisms/Testimonial07/Testimonial07.jsx +245 -0
  44. package/templates/components/organisms/Testimonial07/Testimonial07.propTypes.js +80 -0
  45. package/templates/components/organisms/Testimonial07/index.js +1 -0
  46. package/templates/components/organisms/Testimonial08/Testimonial08.jsx +188 -0
  47. package/templates/components/organisms/Testimonial08/Testimonial08.propTypes.js +69 -0
  48. package/templates/components/organisms/Testimonial08/index.js +1 -0
  49. package/templates/components/organisms/Testimonial09/Testimonial09.jsx +214 -0
  50. package/templates/components/organisms/Testimonial09/Testimonial09.propTypes.js +105 -0
  51. package/templates/components/organisms/Testimonial09/index.js +1 -0
  52. package/templates/components/organisms/Testimonial10/Testimonial10.jsx +218 -0
  53. package/templates/components/organisms/Testimonial10/Testimonial10.propTypes.js +65 -0
  54. package/templates/components/organisms/Testimonial10/index.js +1 -0
  55. package/templates/components/organisms/Testimonial11/Testimonial11.jsx +242 -0
  56. package/templates/components/organisms/Testimonial11/Testimonial11.propTypes.js +73 -0
  57. package/templates/components/organisms/Testimonial11/index.js +1 -0
  58. package/templates/components/organisms/Testimonial12/Testimonial12.jsx +271 -0
  59. package/templates/components/organisms/Testimonial12/Testimonial12.propTypes.js +81 -0
  60. package/templates/components/organisms/Testimonial12/index.js +1 -0
  61. package/templates/components/organisms/Testimonial13/Testimonial13.jsx +206 -0
  62. package/templates/components/organisms/Testimonial13/Testimonial13.propTypes.js +87 -0
  63. package/templates/components/organisms/Testimonial13/index.js +1 -0
  64. package/templates/components/organisms/Testimonial14/Testimonial14.jsx +89 -0
  65. package/templates/components/organisms/Testimonial14/Testimonial14.propTypes.js +87 -0
  66. package/templates/components/organisms/Testimonial14/index.js +1 -0
  67. package/templates/components/organisms/Testimonial15/Testimonial15.jsx +201 -0
  68. package/templates/components/organisms/Testimonial15/Testimonial15.propTypes.js +89 -0
  69. package/templates/components/organisms/Testimonial15/index.js +1 -0
  70. package/templates/components/organisms/Testimonial16/Testimonial16.jsx +193 -0
  71. package/templates/components/organisms/Testimonial16/Testimonial16.propTypes.js +90 -0
  72. package/templates/components/organisms/Testimonial16/index.js +1 -0
  73. package/templates/components/organisms/Testimonial17/Testimonial17.jsx +236 -0
  74. package/templates/components/organisms/Testimonial17/Testimonial17.propTypes.js +98 -0
  75. package/templates/components/organisms/Testimonial17/index.js +1 -0
  76. package/templates/public/testimonials/testimonial01/avatar-marcus.jpg +0 -0
  77. package/templates/public/testimonials/testimonial01/avatar-wilma.jpg +0 -0
  78. package/templates/public/testimonials/testimonial01/company-logo-2.png +0 -0
  79. package/templates/public/testimonials/testimonial01/company-logo-icon-raw.png +0 -0
  80. package/templates/public/testimonials/testimonial01/company-logo-icon.png +0 -0
  81. package/templates/public/testimonials/testimonial01/desco-logo-export.png +0 -0
  82. package/templates/public/testimonials/testimonial01/desco-logo-raw1.png +0 -0
  83. package/templates/public/testimonials/testimonial01/desco-logo-raw2.png +0 -0
  84. package/templates/public/testimonials/testimonial01/desco-logo.png +0 -0
  85. package/templates/public/testimonials/testimonial02/avatar-julia.jpg +0 -0
  86. package/templates/public/testimonials/testimonial02/avatar-marie.jpg +0 -0
  87. package/templates/public/testimonials/testimonial02/avatar-mark.jpg +0 -0
  88. package/templates/public/testimonials/testimonial02/bg-gradient.png +0 -0
  89. package/templates/public/testimonials/testimonial03/avatar-david.jpg +0 -0
  90. package/templates/public/testimonials/testimonial03/globe-bg.png +0 -0
  91. package/templates/public/testimonials/testimonial04/avatar-carlos.jpg +0 -0
  92. package/templates/public/testimonials/testimonial04/avatar-john.jpg +0 -0
  93. package/templates/public/testimonials/testimonial04/avatar-sabbir.jpg +0 -0
  94. package/templates/public/testimonials/testimonial05/portrait.jpg +0 -0
  95. package/templates/public/testimonials/testimonial06/avatar-ahmed.jpg +0 -0
  96. package/templates/public/testimonials/testimonial06/avatar-danzel.jpg +0 -0
  97. package/templates/public/testimonials/testimonial06/avatar-lisa.jpg +0 -0
  98. package/templates/public/testimonials/testimonial06/avatar-maria.jpg +0 -0
  99. package/templates/public/testimonials/testimonial06/avatar-sarah.jpg +0 -0
  100. package/templates/public/testimonials/testimonial07/photo-2.jpg +0 -0
  101. package/templates/public/testimonials/testimonial07/photo-3.jpg +0 -0
  102. package/templates/public/testimonials/testimonial07/photo-alt.png +0 -0
  103. package/templates/public/testimonials/testimonial07/photo.jpg +0 -0
  104. package/templates/public/testimonials/testimonial07/slide-2.png +0 -0
  105. package/templates/public/testimonials/testimonial08/student-1.jpg +0 -0
  106. package/templates/public/testimonials/testimonial08/student-2.jpg +0 -0
  107. package/templates/public/testimonials/testimonial08/student-3.jpg +0 -0
  108. package/templates/public/testimonials/testimonial09/avatar-1.jpg +0 -0
  109. package/templates/public/testimonials/testimonial09/avatar-2.jpg +0 -0
  110. package/templates/public/testimonials/testimonial09/avatar-3.jpg +0 -0
  111. package/templates/public/testimonials/testimonial09/avatar-4.jpg +0 -0
  112. package/templates/public/testimonials/testimonial09/avatar-5.jpg +0 -0
  113. package/templates/public/testimonials/testimonial09/avatar-6.jpg +0 -0
  114. package/templates/public/testimonials/testimonial10/card-1.jpg +0 -0
  115. package/templates/public/testimonials/testimonial10/card-2.jpg +0 -0
  116. package/templates/public/testimonials/testimonial10/card-3.jpg +0 -0
  117. package/templates/public/testimonials/testimonial11/avatar-1.jpg +0 -0
  118. package/templates/public/testimonials/testimonial11/avatar-2.jpg +0 -0
  119. package/templates/public/testimonials/testimonial11/avatar-3.jpg +0 -0
  120. package/templates/public/testimonials/testimonial12/story-1.jpg +0 -0
  121. package/templates/public/testimonials/testimonial12/story-2.jpg +0 -0
  122. package/templates/public/testimonials/testimonial12/story-3.jpg +0 -0
  123. package/templates/public/testimonials/testimonial12/story-4.jpg +0 -0
  124. package/templates/public/testimonials/testimonial13/slide-1.jpg +0 -0
  125. package/templates/public/testimonials/testimonial13/slide-2.jpg +0 -0
  126. package/templates/public/testimonials/testimonial13/slide-3.jpg +0 -0
  127. package/templates/public/testimonials/testimonial13/slide-4.jpg +0 -0
  128. package/templates/public/testimonials/testimonial14/avatar-1.jpg +0 -0
  129. package/templates/public/testimonials/testimonial14/avatar-2.jpg +0 -0
  130. package/templates/public/testimonials/testimonial14/avatar-3.jpg +0 -0
  131. package/templates/public/testimonials/testimonial14/avatar-4.jpg +0 -0
  132. package/templates/public/testimonials/testimonial15/slide-1.jpg +0 -0
  133. package/templates/public/testimonials/testimonial15/slide-2.jpg +0 -0
  134. package/templates/public/testimonials/testimonial15/slide-3.jpg +0 -0
  135. package/templates/public/testimonials/testimonial16/avatar.jpg +0 -0
  136. package/templates/public/testimonials/testimonial16/featured.jpg +0 -0
  137. package/templates/public/testimonials/testimonial17/avatar-1.jpg +0 -0
  138. package/templates/public/testimonials/testimonial17/avatar-2.jpg +0 -0
  139. package/templates/public/testimonials/testimonial17/avatar-3.jpg +0 -0
@@ -0,0 +1,90 @@
1
+ import PropTypes from "prop-types";
2
+
3
+ const imageShape = PropTypes.shape({
4
+ src: PropTypes.string.isRequired,
5
+ alt: PropTypes.string,
6
+ });
7
+
8
+ const slideShape = PropTypes.shape({
9
+ id: PropTypes.string,
10
+ quote: PropTypes.string.isRequired,
11
+ authorName: PropTypes.string.isRequired,
12
+ authorTitle: PropTypes.string,
13
+ image: imageShape.isRequired,
14
+ avatar: imageShape,
15
+ });
16
+
17
+ export const testimonial16PropTypes = {
18
+ headline: PropTypes.string,
19
+ slides: PropTypes.arrayOf(slideShape),
20
+ activeSlide: PropTypes.number,
21
+ onSlideChange: PropTypes.func,
22
+ className: PropTypes.string,
23
+ };
24
+
25
+ export const testimonial16DefaultProps = {
26
+ headline: "Whats our client say about us",
27
+ slides: [
28
+ {
29
+ id: "laura",
30
+ quote:
31
+ "Ergo Ventures played a pivotal role in modernizing our legacy systems with a seamless Odoo ERP integration. Their team was responsive, professional, and highly skilled.",
32
+ authorName: "Laura Mitchell",
33
+ authorTitle: "COO, Pacific Growth Supplies, USA",
34
+ image: {
35
+ src: "/testimonials/testimonial16/featured.jpg",
36
+ alt: "Two business professionals shaking hands",
37
+ },
38
+ avatar: {
39
+ src: "/testimonials/testimonial16/avatar.jpg",
40
+ alt: "Laura Mitchell",
41
+ },
42
+ },
43
+ {
44
+ id: "james",
45
+ quote:
46
+ "Their strategic guidance helped us scale operations while maintaining quality across every department.",
47
+ authorName: "James Porter",
48
+ authorTitle: "CEO, Northline Logistics",
49
+ image: {
50
+ src: "/testimonials/testimonial16/featured.jpg",
51
+ alt: "Business partnership",
52
+ },
53
+ avatar: {
54
+ src: "/testimonials/testimonial16/avatar.jpg",
55
+ alt: "James Porter",
56
+ },
57
+ },
58
+ {
59
+ id: "sarah",
60
+ quote:
61
+ "From discovery to deployment, the team delivered on time and exceeded our integration goals.",
62
+ authorName: "Sarah Nguyen",
63
+ authorTitle: "CTO, Brightfield Retail",
64
+ image: {
65
+ src: "/testimonials/testimonial16/featured.jpg",
66
+ alt: "Team collaboration",
67
+ },
68
+ avatar: {
69
+ src: "/testimonials/testimonial16/avatar.jpg",
70
+ alt: "Sarah Nguyen",
71
+ },
72
+ },
73
+ {
74
+ id: "michael",
75
+ quote:
76
+ "We saw measurable efficiency gains within the first quarter of working together.",
77
+ authorName: "Michael Torres",
78
+ authorTitle: "Operations Director, Apex Manufacturing",
79
+ image: {
80
+ src: "/testimonials/testimonial16/featured.jpg",
81
+ alt: "Operations review",
82
+ },
83
+ avatar: {
84
+ src: "/testimonials/testimonial16/avatar.jpg",
85
+ alt: "Michael Torres",
86
+ },
87
+ },
88
+ ],
89
+ activeSlide: 0,
90
+ };
@@ -0,0 +1 @@
1
+ export { Testimonial16, default } from "./Testimonial16";
@@ -0,0 +1,236 @@
1
+ "use client";
2
+
3
+ import { useEffect, useState } from "react";
4
+
5
+ import SafeImage from "../../atoms/SafeImage";
6
+ import { useCarousel } from "../../hooks/useCarousel";
7
+ import {
8
+ testimonial17DefaultProps,
9
+ testimonial17PropTypes,
10
+ } from "./Testimonial17.propTypes";
11
+
12
+ function DiamondIcon({ className = "" }) {
13
+ return (
14
+ <svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" className={className}>
15
+ <path d="M8 0 16 8 8 16 0 8 8 0z" />
16
+ </svg>
17
+ );
18
+ }
19
+
20
+ function StarIcon({ className = "" }) {
21
+ return (
22
+ <svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" className={className}>
23
+ <path d="M10 1.5 12.47 7.03 18.5 7.64 14 11.97 15.18 18 10 15.03 4.82 18 6 11.97 1.5 7.64 7.53 7.03 10 1.5z" />
24
+ </svg>
25
+ );
26
+ }
27
+
28
+ function ChevronIcon({ className = "" }) {
29
+ return (
30
+ <svg
31
+ viewBox="0 0 20 20"
32
+ fill="none"
33
+ stroke="currentColor"
34
+ strokeWidth="2"
35
+ strokeLinecap="round"
36
+ strokeLinejoin="round"
37
+ aria-hidden="true"
38
+ className={className}
39
+ >
40
+ <path d="M12 4 6 10l6 6" />
41
+ </svg>
42
+ );
43
+ }
44
+
45
+ function GoogleIcon({ className = "" }) {
46
+ return (
47
+ <svg viewBox="0 0 24 24" aria-hidden="true" className={className}>
48
+ <path
49
+ fill="#4285F4"
50
+ d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z"
51
+ />
52
+ <path
53
+ fill="#34A853"
54
+ d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
55
+ />
56
+ <path
57
+ fill="#FBBC05"
58
+ d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18A10.96 10.96 0 0 0 1 12c0 1.77.42 3.45 1.18 4.93l3.66-2.84z"
59
+ />
60
+ <path
61
+ fill="#EA4335"
62
+ d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
63
+ />
64
+ </svg>
65
+ );
66
+ }
67
+
68
+ function getCardsPerView() {
69
+ if (typeof window === "undefined") return 3;
70
+ if (window.matchMedia("(min-width: 1024px)").matches) return 3;
71
+ if (window.matchMedia("(min-width: 640px)").matches) return 2;
72
+ return 1;
73
+ }
74
+
75
+ /**
76
+ * Testimonial17 — Google-style review cards with star ratings and carousel navigation.
77
+ *
78
+ * @param {object} props - See Testimonial17.propTypes.js.
79
+ */
80
+ export function Testimonial17({
81
+ eyebrow = testimonial17DefaultProps.eyebrow,
82
+ headline = testimonial17DefaultProps.headline,
83
+ reviews = testimonial17DefaultProps.reviews,
84
+ activeSlide: activeSlideProp = testimonial17DefaultProps.activeSlide,
85
+ onSlideChange,
86
+ className = "",
87
+ }) {
88
+ const [perView, setPerView] = useState(3);
89
+ const maxIndex = Math.max(0, reviews.length - perView);
90
+
91
+ const isControlled = typeof activeSlideProp === "number";
92
+ const { activeSlide, goTo } = useCarousel({
93
+ count: maxIndex + 1,
94
+ initialIndex: activeSlideProp,
95
+ onChange: onSlideChange,
96
+ });
97
+ const current = isControlled ? activeSlideProp : activeSlide;
98
+
99
+ const setSlide = (index) => {
100
+ const next = Math.min(Math.max(index, 0), maxIndex);
101
+ if (isControlled) {
102
+ onSlideChange?.(next);
103
+ } else {
104
+ goTo(next);
105
+ }
106
+ };
107
+
108
+ useEffect(() => {
109
+ const update = () => setPerView(getCardsPerView());
110
+ update();
111
+ window.addEventListener("resize", update);
112
+ return () => window.removeEventListener("resize", update);
113
+ }, []);
114
+
115
+ useEffect(() => {
116
+ if (!isControlled) {
117
+ goTo(Math.min(activeSlideProp, maxIndex));
118
+ }
119
+ }, [activeSlideProp, goTo, isControlled, maxIndex]);
120
+
121
+ useEffect(() => {
122
+ if (!isControlled && current > maxIndex) {
123
+ goTo(maxIndex);
124
+ }
125
+ }, [current, goTo, isControlled, maxIndex]);
126
+
127
+ const stepPercent = 100 / perView;
128
+
129
+ return (
130
+ <section
131
+ className={[
132
+ "relative w-full overflow-hidden bg-gradient-to-b from-white to-[#f0f0f0]",
133
+ "px-4 py-16 sm:px-6 sm:py-20 md:px-10 lg:px-20 lg:py-[100px] xl:px-[80px]",
134
+ className,
135
+ ]
136
+ .filter(Boolean)
137
+ .join(" ")}
138
+ data-testimonial="testimonial17"
139
+ >
140
+ <div className="mx-auto flex w-full max-w-7xl flex-col gap-10 sm:gap-14 lg:gap-16">
141
+ <div className="flex flex-col gap-8 lg:flex-row lg:items-end lg:justify-between">
142
+ <div className="flex max-w-2xl flex-col gap-6 sm:gap-8">
143
+ <div className="flex items-center gap-4">
144
+ <DiamondIcon className="size-4 text-[#059fbb]" />
145
+ <p className="text-sm font-semibold uppercase tracking-[-0.01em] text-[#059fbb] sm:text-base">
146
+ {eyebrow}
147
+ </p>
148
+ </div>
149
+ <h2 className="text-3xl font-semibold uppercase leading-tight tracking-[-0.06em] text-[#0b1016] sm:text-4xl lg:text-[48px]">
150
+ {headline}
151
+ </h2>
152
+ </div>
153
+
154
+ <div className="flex items-center gap-4">
155
+ <button
156
+ type="button"
157
+ aria-label="Previous reviews"
158
+ onClick={() => setSlide(current - 1)}
159
+ disabled={current === 0}
160
+ className="flex items-center rounded-full border-2 border-[#e1e1e1] p-2 text-[#0b1016]/40 transition-colors duration-200 ease-out hover:border-[#0b1016]/30 focus-visible:outline-2 focus-visible:outline-offset-2 disabled:cursor-not-allowed"
161
+ >
162
+ <ChevronIcon className="size-5" />
163
+ </button>
164
+ <button
165
+ type="button"
166
+ aria-label="Next reviews"
167
+ onClick={() => setSlide(current + 1)}
168
+ disabled={current >= maxIndex}
169
+ className="flex items-center rounded-full border-2 border-[#0b1016] p-2 text-[#0b1016] transition-colors duration-200 ease-out hover:bg-[#0b1016]/5 focus-visible:outline-2 focus-visible:outline-offset-2 disabled:cursor-not-allowed disabled:opacity-40"
170
+ >
171
+ <ChevronIcon className="size-5 rotate-180" />
172
+ </button>
173
+ </div>
174
+ </div>
175
+
176
+ <div className="overflow-hidden">
177
+ <div
178
+ className="flex gap-6 transition-transform duration-500 ease-out motion-reduce:transition-none"
179
+ style={{ transform: `translateX(-${current * stepPercent}%)` }}
180
+ >
181
+ {reviews.map((review) => (
182
+ <article
183
+ key={review.id ?? review.authorName}
184
+ className="flex w-full shrink-0 flex-col gap-6 rounded-3xl border border-white bg-white/80 p-8 sm:w-1/2 lg:w-1/3 lg:min-h-[368px]"
185
+ >
186
+ <div className="flex items-center gap-0.5 text-[#f47720]" aria-label={`${review.rating ?? 5} out of 5 stars`}>
187
+ {Array.from({ length: review.rating ?? 5 }).map((_, index) => (
188
+ <StarIcon key={index} className="size-5" />
189
+ ))}
190
+ </div>
191
+
192
+ <blockquote className="flex-1 text-xl leading-snug tracking-[-0.04em] text-[#0b1016] sm:text-2xl">
193
+ {review.quote}
194
+ </blockquote>
195
+
196
+ <div className="flex items-center justify-between gap-4">
197
+ <div className="flex min-w-0 items-center gap-3.5">
198
+ {review.avatar?.src ? (
199
+ <div className="relative size-12 shrink-0 overflow-hidden rounded-full">
200
+ <SafeImage
201
+ src={review.avatar.src}
202
+ alt={review.avatar.alt ?? review.authorName}
203
+ fill
204
+ className="object-cover object-center"
205
+ sizes="48px"
206
+ />
207
+ </div>
208
+ ) : null}
209
+ <div className="min-w-0">
210
+ <p className="text-lg font-semibold tracking-[-0.02em] text-[#0b1016]">
211
+ {review.authorName}
212
+ </p>
213
+ {review.timeAgo ? (
214
+ <p className="text-base text-[#4c4e51]">{review.timeAgo}</p>
215
+ ) : null}
216
+ </div>
217
+ </div>
218
+
219
+ {review.source === "google" ? (
220
+ <div className="flex size-10 shrink-0 items-center justify-center rounded-lg border border-[#e1e1e1] bg-white shadow-sm">
221
+ <GoogleIcon className="size-5" />
222
+ </div>
223
+ ) : null}
224
+ </div>
225
+ </article>
226
+ ))}
227
+ </div>
228
+ </div>
229
+ </div>
230
+ </section>
231
+ );
232
+ }
233
+
234
+ Testimonial17.propTypes = testimonial17PropTypes;
235
+
236
+ export default Testimonial17;
@@ -0,0 +1,98 @@
1
+ import PropTypes from "prop-types";
2
+
3
+ const imageShape = PropTypes.shape({
4
+ src: PropTypes.string.isRequired,
5
+ alt: PropTypes.string,
6
+ });
7
+
8
+ const reviewShape = PropTypes.shape({
9
+ id: PropTypes.string,
10
+ quote: PropTypes.string.isRequired,
11
+ authorName: PropTypes.string.isRequired,
12
+ timeAgo: PropTypes.string,
13
+ rating: PropTypes.number,
14
+ avatar: imageShape,
15
+ source: PropTypes.oneOf(["google", "none"]),
16
+ });
17
+
18
+ export const testimonial17PropTypes = {
19
+ eyebrow: PropTypes.string,
20
+ headline: PropTypes.string,
21
+ reviews: PropTypes.arrayOf(reviewShape),
22
+ activeSlide: PropTypes.number,
23
+ onSlideChange: PropTypes.func,
24
+ className: PropTypes.string,
25
+ };
26
+
27
+ export const testimonial17DefaultProps = {
28
+ eyebrow: "Common AC Problems",
29
+ headline: "AC Repair Customers Trust Breeze Brothers",
30
+ reviews: [
31
+ {
32
+ id: "james",
33
+ quote:
34
+ "Samantha arrived promptly and repaired the air conditioner without any hassle. She was friendly and left the area clean after the job.",
35
+ authorName: "James Allen",
36
+ timeAgo: "5 days ago",
37
+ rating: 5,
38
+ avatar: {
39
+ src: "/testimonials/testimonial17/avatar-1.jpg",
40
+ alt: "James Allen",
41
+ },
42
+ source: "google",
43
+ },
44
+ {
45
+ id: "carlos",
46
+ quote:
47
+ "Maria fixed my heating system quickly and professionally. She identified the problem right away and made sure everything was working before she left.",
48
+ authorName: "Carlos Mendez",
49
+ timeAgo: "3 days ago",
50
+ rating: 5,
51
+ avatar: {
52
+ src: "/testimonials/testimonial17/avatar-2.jpg",
53
+ alt: "Carlos Mendez",
54
+ },
55
+ source: "google",
56
+ },
57
+ {
58
+ id: "linda",
59
+ quote:
60
+ "Tom showed great patience explaining the options for my HVAC upgrade. His recommendations were sensible and fit my budget perfectly.",
61
+ authorName: "Linda Park",
62
+ timeAgo: "1 week ago",
63
+ rating: 5,
64
+ avatar: {
65
+ src: "/testimonials/testimonial17/avatar-3.jpg",
66
+ alt: "Linda Park",
67
+ },
68
+ source: "google",
69
+ },
70
+ {
71
+ id: "emma",
72
+ quote:
73
+ "Fast response and honest pricing. The technician explained everything clearly before starting the repair.",
74
+ authorName: "Emma Wright",
75
+ timeAgo: "2 weeks ago",
76
+ rating: 5,
77
+ avatar: {
78
+ src: "/testimonials/testimonial17/avatar-1.jpg",
79
+ alt: "Emma Wright",
80
+ },
81
+ source: "google",
82
+ },
83
+ {
84
+ id: "robert",
85
+ quote:
86
+ "Outstanding service from start to finish. Our AC has been running perfectly since the visit.",
87
+ authorName: "Robert Kim",
88
+ timeAgo: "3 weeks ago",
89
+ rating: 5,
90
+ avatar: {
91
+ src: "/testimonials/testimonial17/avatar-2.jpg",
92
+ alt: "Robert Kim",
93
+ },
94
+ source: "google",
95
+ },
96
+ ],
97
+ activeSlide: 0,
98
+ };
@@ -0,0 +1 @@
1
+ export { Testimonial17, default } from "./Testimonial17";