@pradip1995/segment-store-faqs 0.2.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/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@pradip1995/segment-store-faqs",
3
+ "version": "0.2.0",
4
+ "license": "MIT",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "sideEffects": [
9
+ "src/segment.css"
10
+ ],
11
+ "files": [
12
+ "src"
13
+ ],
14
+ "exports": {
15
+ ".": "./src/index.ts",
16
+ "./manifest": "./src/manifest.ts"
17
+ },
18
+ "peerDependencies": {
19
+ "@pradip1995/plugin-sdk": "^0.2.0",
20
+ "react": ">=19",
21
+ "react-dom": ">=19",
22
+ "next": ">=15"
23
+ },
24
+ "dependencies": {
25
+ "@pradip1995/segment-primitives": "0.3.0",
26
+ "@pradip1995/segment-tokens": "0.3.2"
27
+ },
28
+ "devDependencies": {
29
+ "@pradip1995/plugin-sdk": "^0.2.0",
30
+ "@types/react": "^19",
31
+ "react": "19.0.3",
32
+ "typescript": "^5.7.2"
33
+ },
34
+ "scripts": {
35
+ "typecheck": "tsc --noEmit",
36
+ "lint": "tsc --noEmit"
37
+ }
38
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { default } from "./segment"
2
+ export { default as manifest } from "./manifest"
@@ -0,0 +1,11 @@
1
+ import type { SegmentManifest } from "@pradip1995/plugin-sdk"
2
+
3
+ const manifest: SegmentManifest = {
4
+ id: "store-faqs",
5
+ type: "segment",
6
+ version: "0.1.0",
7
+ compatibleFramework: ["^1.0.0"],
8
+ dataKey: "storeFaqs",
9
+ }
10
+
11
+ export default manifest
@@ -0,0 +1,82 @@
1
+ .store-faqs {
2
+ width: 100%;
3
+ padding: 2rem 0;
4
+ }
5
+
6
+ .store-faqs__inner {
7
+ max-width: var(--container-max);
8
+ margin: 0 auto;
9
+ padding: 2rem 1.5rem;
10
+ background: var(--color-brand-accent-muted);
11
+ border-radius: 1.25rem;
12
+ }
13
+
14
+ .store-faqs__title {
15
+ font-size: 1.5rem;
16
+ font-weight: 600;
17
+ text-transform: uppercase;
18
+ margin-bottom: 2rem;
19
+ color: var(--color-text-heading);
20
+ }
21
+
22
+ .store-faqs__category + .store-faqs__category {
23
+ margin-top: 2rem;
24
+ }
25
+
26
+ .store-faqs__category-title {
27
+ font-size: 1.125rem;
28
+ font-weight: 600;
29
+ margin-bottom: 1rem;
30
+ padding-bottom: 0.5rem;
31
+ border-bottom: 1px solid var(--color-brand-accent);
32
+ }
33
+
34
+ .store-faqs__question {
35
+ width: 100%;
36
+ display: flex;
37
+ justify-content: space-between;
38
+ gap: 1rem;
39
+ text-align: left;
40
+ background: none;
41
+ border: none;
42
+ padding: 0.75rem 0;
43
+ color: var(--color-text-heading);
44
+ }
45
+
46
+ .store-faqs__number {
47
+ color: var(--color-brand-accent);
48
+ font-weight: 600;
49
+ }
50
+
51
+ .store-faqs__toggle {
52
+ color: var(--color-brand-accent);
53
+ font-size: 1.5rem;
54
+ line-height: 1;
55
+ }
56
+
57
+ .store-faqs__answer {
58
+ margin: 0 0 0.75rem;
59
+ color: var(--color-text-body);
60
+ line-height: 1.6;
61
+ white-space: pre-line;
62
+ }
63
+
64
+ .store-faqs__divider {
65
+ height: 1px;
66
+ background: var(--color-brand-accent);
67
+ }
68
+
69
+ .store-faqs__more-wrap {
70
+ display: flex;
71
+ justify-content: center;
72
+ padding-top: 1.5rem;
73
+ }
74
+
75
+ .store-faqs__more-btn {
76
+ border-radius: 9999px;
77
+ padding: 0.75rem 2rem;
78
+ background: var(--color-brand-accent);
79
+ color: var(--color-text-inverse);
80
+ font-weight: 600;
81
+ text-transform: uppercase;
82
+ }
@@ -0,0 +1,147 @@
1
+ "use client"
2
+
3
+ import "./segment.css"
4
+ import { useMemo, useState } from "react"
5
+
6
+ type FaqItem = {
7
+ question: string
8
+ answer: string
9
+ }
10
+
11
+ type FaqCategory = {
12
+ title: string
13
+ faqs: FaqItem[]
14
+ }
15
+
16
+ const DEFAULT_CATEGORIES: FaqCategory[] = [
17
+ {
18
+ title: "Orders & delivery",
19
+ faqs: [
20
+ {
21
+ question: "When will I receive my order?",
22
+ answer: "Orders are typically processed within 1–2 business days. Delivery takes 3–5 business days depending on your location.",
23
+ },
24
+ {
25
+ question: "How can I track my order?",
26
+ answer: "Once your order ships, you will receive a tracking link via email. You can also track it from your account under My Orders.",
27
+ },
28
+ ],
29
+ },
30
+ {
31
+ title: "Returns & exchanges",
32
+ faqs: [
33
+ {
34
+ question: "What is your return policy?",
35
+ answer: "We offer a 7-day return policy for unused items in original packaging with tags attached.",
36
+ },
37
+ {
38
+ question: "How do I initiate a return?",
39
+ answer: "Go to your account, open the order, and select Return Items to generate a pickup request.",
40
+ },
41
+ ],
42
+ },
43
+ ]
44
+
45
+ export default function StoreFaqs({
46
+ categories,
47
+ title = "FAQs",
48
+ initialVisibleCount = 6,
49
+ }: {
50
+ categories?: FaqCategory[] | null
51
+ title?: string
52
+ initialVisibleCount?: number
53
+ }) {
54
+ const faqCategories = categories && categories.length > 0 ? categories : DEFAULT_CATEGORIES
55
+ const [openIndex, setOpenIndex] = useState<{ category: number; faq: number } | null>(null)
56
+ const [visibleCount, setVisibleCount] = useState(initialVisibleCount)
57
+
58
+ const totalFaqs = useMemo(
59
+ () => faqCategories.reduce((sum, category) => sum + category.faqs.length, 0),
60
+ [faqCategories]
61
+ )
62
+
63
+ const hasMore = totalFaqs > visibleCount
64
+ const showLessButton = visibleCount > initialVisibleCount
65
+
66
+ let globalIndex = 0
67
+
68
+ return (
69
+ <section className="store-faqs" aria-label={title}>
70
+ <div className="store-faqs__inner">
71
+ <h2 className="store-faqs__title">{title}</h2>
72
+
73
+ <div className="store-faqs__categories">
74
+ {faqCategories.map((category, catIndex) => {
75
+ const firstFaqIndex = globalIndex
76
+ if (firstFaqIndex >= visibleCount) return null
77
+
78
+ const visibleFaqs = category.faqs.filter((_, idx) => firstFaqIndex + idx < visibleCount)
79
+ if (visibleFaqs.length === 0) return null
80
+
81
+ return (
82
+ <div key={`${category.title}-${catIndex}`} className="store-faqs__category">
83
+ {category.title ? <h3 className="store-faqs__category-title">{category.title}</h3> : null}
84
+ <div className="store-faqs__list">
85
+ {visibleFaqs.map((faq, faqIndex) => {
86
+ const isExpanded = openIndex?.category === catIndex && openIndex?.faq === faqIndex
87
+ globalIndex += 1
88
+ const displayIndex = globalIndex
89
+
90
+ return (
91
+ <div key={`${faq.question}-${faqIndex}`} className="store-faqs__item">
92
+ <button
93
+ type="button"
94
+ onClick={() =>
95
+ setOpenIndex(isExpanded ? null : { category: catIndex, faq: faqIndex })
96
+ }
97
+ className="store-faqs__question"
98
+ aria-expanded={isExpanded}
99
+ >
100
+ <span>
101
+ <span className="store-faqs__number">{displayIndex}.</span> {faq.question}
102
+ </span>
103
+ <span className="store-faqs__toggle" aria-hidden>
104
+ {isExpanded ? "−" : "+"}
105
+ </span>
106
+ </button>
107
+ {isExpanded ? (
108
+ <p className="store-faqs__answer">{faq.answer}</p>
109
+ ) : null}
110
+ <div className="store-faqs__divider" aria-hidden />
111
+ </div>
112
+ )
113
+ })}
114
+ </div>
115
+ </div>
116
+ )
117
+ })}
118
+ </div>
119
+
120
+ {(hasMore || showLessButton) && (
121
+ <div className="store-faqs__more-wrap">
122
+ {hasMore ? (
123
+ <button
124
+ type="button"
125
+ onClick={() => setVisibleCount((prev) => Math.min(prev + initialVisibleCount, totalFaqs))}
126
+ className="store-faqs__more-btn"
127
+ >
128
+ More
129
+ </button>
130
+ ) : (
131
+ <button
132
+ type="button"
133
+ onClick={() => {
134
+ setVisibleCount(initialVisibleCount)
135
+ setOpenIndex(null)
136
+ }}
137
+ className="store-faqs__more-btn"
138
+ >
139
+ Less
140
+ </button>
141
+ )}
142
+ </div>
143
+ )}
144
+ </div>
145
+ </section>
146
+ )
147
+ }