kofi-stack-template-generator 2.1.51 → 2.1.52
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/.turbo/turbo-build.log +6 -6
- package/dist/index.js +356 -156
- package/package.json +2 -2
- package/src/templates.generated.ts +26 -5
- package/templates/marketing/nextjs/package.json.hbs +6 -1
- package/templates/marketing/nextjs/src/app/layout.tsx.hbs +19 -5
- package/templates/marketing/nextjs/src/app/page.tsx.hbs +160 -164
- package/templates/marketing/nextjs/src/components/Footer/index.tsx +188 -0
- package/templates/marketing/nextjs/src/components/Header/MegaMenu.tsx +117 -0
- package/templates/marketing/nextjs/src/components/Header/MobileMenu.tsx +178 -0
- package/templates/marketing/nextjs/src/components/Header/index.tsx +172 -0
- package/templates/marketing/nextjs/src/components/Logo.tsx +46 -0
- package/templates/marketing/nextjs/src/components/ThemeProvider.tsx +8 -0
- package/templates/marketing/nextjs/src/components/ThemeSelector.tsx +69 -0
- package/templates/marketing/nextjs/src/components/blocks/BentoFeatures.tsx +249 -0
- package/templates/marketing/nextjs/src/components/blocks/FeatureShowcase.tsx +110 -0
- package/templates/marketing/nextjs/src/components/blocks/FinalCTA.tsx +91 -0
- package/templates/marketing/nextjs/src/components/blocks/IndustryTabs.tsx +137 -0
- package/templates/marketing/nextjs/src/components/blocks/LogoBanner.tsx +80 -0
- package/templates/marketing/nextjs/src/components/blocks/PricingTable.tsx +210 -0
- package/templates/marketing/nextjs/src/components/blocks/ProofBanner.tsx +72 -0
- package/templates/marketing/nextjs/src/components/blocks/TestimonialsGrid.tsx +119 -0
- package/templates/marketing/nextjs/src/components/blocks/TrustColumns.tsx +110 -0
- package/templates/marketing/nextjs/src/components/blocks/index.ts +9 -0
- package/templates/marketing/nextjs/src/components/heros/AnimatedMockup.tsx +242 -0
- package/templates/marketing/nextjs/src/components/heros/ProductShowcaseHero.tsx +84 -0
- package/templates/marketing/nextjs/src/components/heros/index.ts +2 -0
- package/templates/marketing/nextjs/src/lib/utils.ts +6 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
BarChart3,
|
|
5
|
+
Globe,
|
|
6
|
+
Layers,
|
|
7
|
+
type LucideIcon,
|
|
8
|
+
Rocket,
|
|
9
|
+
Settings,
|
|
10
|
+
Shield,
|
|
11
|
+
Zap,
|
|
12
|
+
} from "lucide-react"
|
|
13
|
+
import { cn } from "@/lib/utils"
|
|
14
|
+
|
|
15
|
+
interface Feature {
|
|
16
|
+
size?: "small" | "tall"
|
|
17
|
+
style?: "default" | "primary" | "accent" | "gradient"
|
|
18
|
+
icon?: string
|
|
19
|
+
stat?: string
|
|
20
|
+
title: string
|
|
21
|
+
description: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface BentoFeaturesProps {
|
|
25
|
+
heading?: string
|
|
26
|
+
subheading?: string
|
|
27
|
+
features?: Feature[]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const iconMap: Record<string, LucideIcon> = {
|
|
31
|
+
rocket: Rocket,
|
|
32
|
+
zap: Zap,
|
|
33
|
+
layers: Layers,
|
|
34
|
+
shield: Shield,
|
|
35
|
+
globe: Globe,
|
|
36
|
+
settings: Settings,
|
|
37
|
+
barChart: BarChart3,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const gridPositions = [
|
|
41
|
+
"md:col-span-1 md:row-span-1",
|
|
42
|
+
"md:col-span-1 md:row-span-1",
|
|
43
|
+
"md:col-span-1 md:row-span-1",
|
|
44
|
+
"md:col-span-1 md:row-span-2",
|
|
45
|
+
"md:col-span-1 md:row-span-1",
|
|
46
|
+
"md:col-span-1 md:row-span-1",
|
|
47
|
+
"md:col-span-1 md:row-span-1",
|
|
48
|
+
"md:col-span-1 md:row-span-1",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
const styleClasses: Record<string, { bg: string; text: string; icon: string; statText: string }> = {
|
|
52
|
+
default: {
|
|
53
|
+
bg: "bg-card border border-border/50 hover:border-border",
|
|
54
|
+
text: "text-foreground",
|
|
55
|
+
icon: "bg-primary/10 text-primary",
|
|
56
|
+
statText: "text-primary",
|
|
57
|
+
},
|
|
58
|
+
primary: {
|
|
59
|
+
bg: "bg-primary hover:bg-primary/95",
|
|
60
|
+
text: "text-primary-foreground",
|
|
61
|
+
icon: "bg-white/20 text-white",
|
|
62
|
+
statText: "text-white",
|
|
63
|
+
},
|
|
64
|
+
accent: {
|
|
65
|
+
bg: "bg-gradient-to-br from-teal-500 to-teal-600 hover:from-teal-500/95 hover:to-teal-600/95",
|
|
66
|
+
text: "text-white",
|
|
67
|
+
icon: "bg-white/20 text-white",
|
|
68
|
+
statText: "text-white",
|
|
69
|
+
},
|
|
70
|
+
gradient: {
|
|
71
|
+
bg: "bg-gradient-to-br from-primary via-primary/90 to-secondary hover:from-primary/95",
|
|
72
|
+
text: "text-white",
|
|
73
|
+
icon: "bg-white/20 text-white",
|
|
74
|
+
statText: "text-white",
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const defaultFeatures: Feature[] = [
|
|
79
|
+
{
|
|
80
|
+
size: "small",
|
|
81
|
+
style: "gradient",
|
|
82
|
+
icon: "zap",
|
|
83
|
+
stat: "5x",
|
|
84
|
+
title: "Faster onboarding",
|
|
85
|
+
description: "Get your team up and running in hours, not weeks.",
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
size: "small",
|
|
89
|
+
style: "accent",
|
|
90
|
+
icon: "rocket",
|
|
91
|
+
title: "Quick setup",
|
|
92
|
+
description: "Configure your workspace, invite your team, and start collaborating.",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
size: "small",
|
|
96
|
+
style: "default",
|
|
97
|
+
icon: "layers",
|
|
98
|
+
title: "Powerful integrations",
|
|
99
|
+
description: "Connect with 100+ tools you already use.",
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
size: "tall",
|
|
103
|
+
style: "primary",
|
|
104
|
+
icon: "shield",
|
|
105
|
+
title: "Enterprise security",
|
|
106
|
+
description: "SOC 2 compliant with end-to-end encryption and complete audit trails for peace of mind.",
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
size: "small",
|
|
110
|
+
style: "default",
|
|
111
|
+
icon: "globe",
|
|
112
|
+
stat: "99.9%",
|
|
113
|
+
title: "Uptime",
|
|
114
|
+
description: "Reliable infrastructure you can count on.",
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
size: "small",
|
|
118
|
+
style: "default",
|
|
119
|
+
icon: "globe",
|
|
120
|
+
title: "Global scale",
|
|
121
|
+
description: "Multi-region with custom domains.",
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
size: "small",
|
|
125
|
+
style: "default",
|
|
126
|
+
icon: "settings",
|
|
127
|
+
title: "Smart automation",
|
|
128
|
+
description: "Automate repetitive tasks and workflows.",
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
size: "small",
|
|
132
|
+
style: "default",
|
|
133
|
+
icon: "layers",
|
|
134
|
+
title: "Flexible workflows",
|
|
135
|
+
description: "Build custom processes for any use case.",
|
|
136
|
+
},
|
|
137
|
+
]
|
|
138
|
+
|
|
139
|
+
export function BentoFeatures({
|
|
140
|
+
heading = "Discover what SaaSify can do",
|
|
141
|
+
subheading = "Everything you need to work smarter and scale faster",
|
|
142
|
+
features = defaultFeatures,
|
|
143
|
+
}: BentoFeaturesProps) {
|
|
144
|
+
const displayFeatures = features.length > 0 ? features : defaultFeatures
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<section className="py-16 md:py-24">
|
|
148
|
+
<div className="container mx-auto px-4">
|
|
149
|
+
{/* Header */}
|
|
150
|
+
{(heading || subheading) && (
|
|
151
|
+
<div className="text-center mb-12 md:mb-16 max-w-3xl mx-auto">
|
|
152
|
+
{heading && (
|
|
153
|
+
<h2 className="text-4xl md:text-5xl lg:text-6xl font-bold tracking-tight mb-6">
|
|
154
|
+
{heading}
|
|
155
|
+
</h2>
|
|
156
|
+
)}
|
|
157
|
+
{subheading && (
|
|
158
|
+
<p className="text-xl md:text-2xl text-muted-foreground">{subheading}</p>
|
|
159
|
+
)}
|
|
160
|
+
</div>
|
|
161
|
+
)}
|
|
162
|
+
|
|
163
|
+
{/* Bento Grid */}
|
|
164
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 md:gap-5 md:grid-rows-[repeat(3,minmax(140px,160px))]">
|
|
165
|
+
{displayFeatures.map((feature, index) => {
|
|
166
|
+
const style = feature.style || "default"
|
|
167
|
+
const iconKey = feature.icon
|
|
168
|
+
const Icon = iconKey && iconKey in iconMap ? iconMap[iconKey] : null
|
|
169
|
+
const styleConfig = styleClasses[style] || styleClasses.default
|
|
170
|
+
const gridPosition = gridPositions[index] || "md:col-span-1 md:row-span-1"
|
|
171
|
+
const isLarge =
|
|
172
|
+
gridPosition.includes("col-span-2") && gridPosition.includes("row-span-2")
|
|
173
|
+
|
|
174
|
+
return (
|
|
175
|
+
<div
|
|
176
|
+
key={feature.title}
|
|
177
|
+
className={cn(
|
|
178
|
+
"relative rounded-2xl overflow-hidden p-5 md:p-6 flex flex-col transition-all duration-300 group",
|
|
179
|
+
"hover:shadow-lg hover:-translate-y-0.5",
|
|
180
|
+
"col-span-1 row-span-1",
|
|
181
|
+
gridPosition,
|
|
182
|
+
styleConfig.bg
|
|
183
|
+
)}
|
|
184
|
+
>
|
|
185
|
+
{/* Decorative elements */}
|
|
186
|
+
{!isLarge && feature.size === "tall" && (
|
|
187
|
+
<div className="absolute -right-6 -bottom-6 w-24 h-24 md:w-32 md:h-32 rounded-full bg-white/10 blur-xl" />
|
|
188
|
+
)}
|
|
189
|
+
|
|
190
|
+
{/* Content */}
|
|
191
|
+
<div className="relative z-10 flex flex-col h-full">
|
|
192
|
+
{/* Icon */}
|
|
193
|
+
{Icon && (
|
|
194
|
+
<div
|
|
195
|
+
className={cn(
|
|
196
|
+
"w-10 h-10 md:w-11 md:h-11 rounded-xl flex items-center justify-center mb-auto",
|
|
197
|
+
styleConfig.icon
|
|
198
|
+
)}
|
|
199
|
+
>
|
|
200
|
+
<Icon className="w-5 h-5 md:w-5 md:h-5" />
|
|
201
|
+
</div>
|
|
202
|
+
)}
|
|
203
|
+
|
|
204
|
+
{/* Spacer */}
|
|
205
|
+
<div className="flex-1 min-h-2" />
|
|
206
|
+
|
|
207
|
+
{/* Stat */}
|
|
208
|
+
{feature.stat && (
|
|
209
|
+
<div
|
|
210
|
+
className={cn(
|
|
211
|
+
"font-bold mb-0.5 leading-none",
|
|
212
|
+
isLarge ? "text-5xl md:text-6xl" : "text-3xl md:text-4xl",
|
|
213
|
+
styleConfig.statText
|
|
214
|
+
)}
|
|
215
|
+
>
|
|
216
|
+
{feature.stat}
|
|
217
|
+
</div>
|
|
218
|
+
)}
|
|
219
|
+
|
|
220
|
+
{/* Title */}
|
|
221
|
+
<h3
|
|
222
|
+
className={cn(
|
|
223
|
+
"font-semibold mb-1",
|
|
224
|
+
isLarge ? "text-lg md:text-xl" : "text-base md:text-lg",
|
|
225
|
+
styleConfig.text
|
|
226
|
+
)}
|
|
227
|
+
>
|
|
228
|
+
{feature.title}
|
|
229
|
+
</h3>
|
|
230
|
+
|
|
231
|
+
{/* Description */}
|
|
232
|
+
<p
|
|
233
|
+
className={cn(
|
|
234
|
+
"leading-snug",
|
|
235
|
+
isLarge ? "text-sm md:text-base" : "text-xs md:text-sm",
|
|
236
|
+
style === "default" ? "text-muted-foreground" : "text-white/80"
|
|
237
|
+
)}
|
|
238
|
+
>
|
|
239
|
+
{feature.description}
|
|
240
|
+
</p>
|
|
241
|
+
</div>
|
|
242
|
+
</div>
|
|
243
|
+
)
|
|
244
|
+
})}
|
|
245
|
+
</div>
|
|
246
|
+
</div>
|
|
247
|
+
</section>
|
|
248
|
+
)
|
|
249
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import Link from "next/link"
|
|
4
|
+
import { Check } from "lucide-react"
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
interface FeatureShowcaseProps {
|
|
8
|
+
label?: string
|
|
9
|
+
headline: string
|
|
10
|
+
description: string
|
|
11
|
+
features?: { text: string }[]
|
|
12
|
+
link?: {
|
|
13
|
+
label: string
|
|
14
|
+
href: string
|
|
15
|
+
}
|
|
16
|
+
imagePosition?: "left" | "right"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function FeatureShowcase({
|
|
20
|
+
label,
|
|
21
|
+
headline,
|
|
22
|
+
description,
|
|
23
|
+
features = [],
|
|
24
|
+
link,
|
|
25
|
+
imagePosition = "right",
|
|
26
|
+
}: FeatureShowcaseProps) {
|
|
27
|
+
return (
|
|
28
|
+
<section className="py-16 md:py-24">
|
|
29
|
+
<div className="container mx-auto px-4">
|
|
30
|
+
<div
|
|
31
|
+
className={cn(
|
|
32
|
+
"grid md:grid-cols-2 gap-12 lg:gap-16 items-center",
|
|
33
|
+
imagePosition === "left" && "md:grid-flow-dense"
|
|
34
|
+
)}
|
|
35
|
+
>
|
|
36
|
+
{/* Content */}
|
|
37
|
+
<div className={cn(imagePosition === "left" && "md:col-start-2")}>
|
|
38
|
+
{label && (
|
|
39
|
+
<span className="inline-block text-sm font-medium text-primary mb-4">
|
|
40
|
+
{label}
|
|
41
|
+
</span>
|
|
42
|
+
)}
|
|
43
|
+
<h2 className="text-3xl md:text-4xl font-bold tracking-tight mb-4">
|
|
44
|
+
{headline}
|
|
45
|
+
</h2>
|
|
46
|
+
<p className="text-lg text-muted-foreground mb-6">
|
|
47
|
+
{description}
|
|
48
|
+
</p>
|
|
49
|
+
|
|
50
|
+
{features.length > 0 && (
|
|
51
|
+
<ul className="space-y-3 mb-8">
|
|
52
|
+
{features.map((feature) => (
|
|
53
|
+
<li key={feature.text} className="flex items-center gap-3">
|
|
54
|
+
<div className="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-primary/10">
|
|
55
|
+
<Check className="h-3 w-3 text-primary" />
|
|
56
|
+
</div>
|
|
57
|
+
<span className="text-sm text-muted-foreground">
|
|
58
|
+
{feature.text}
|
|
59
|
+
</span>
|
|
60
|
+
</li>
|
|
61
|
+
))}
|
|
62
|
+
</ul>
|
|
63
|
+
)}
|
|
64
|
+
|
|
65
|
+
{link && (
|
|
66
|
+
<Link
|
|
67
|
+
href={link.href}
|
|
68
|
+
className="inline-flex items-center gap-2 text-sm font-medium text-primary hover:underline"
|
|
69
|
+
>
|
|
70
|
+
{link.label}
|
|
71
|
+
<span aria-hidden="true">→</span>
|
|
72
|
+
</Link>
|
|
73
|
+
)}
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
{/* Image Placeholder */}
|
|
77
|
+
<div
|
|
78
|
+
className={cn(
|
|
79
|
+
"relative aspect-[4/3] rounded-2xl bg-muted overflow-hidden",
|
|
80
|
+
imagePosition === "left" && "md:col-start-1 md:row-start-1"
|
|
81
|
+
)}
|
|
82
|
+
>
|
|
83
|
+
<div className="absolute inset-0 flex items-center justify-center">
|
|
84
|
+
<div className="text-center">
|
|
85
|
+
<div className="w-16 h-16 mx-auto mb-4 rounded-xl bg-primary/10 flex items-center justify-center">
|
|
86
|
+
<svg
|
|
87
|
+
className="w-8 h-8 text-primary"
|
|
88
|
+
fill="none"
|
|
89
|
+
stroke="currentColor"
|
|
90
|
+
viewBox="0 0 24 24"
|
|
91
|
+
>
|
|
92
|
+
<path
|
|
93
|
+
strokeLinecap="round"
|
|
94
|
+
strokeLinejoin="round"
|
|
95
|
+
strokeWidth={1.5}
|
|
96
|
+
d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
|
|
97
|
+
/>
|
|
98
|
+
</svg>
|
|
99
|
+
</div>
|
|
100
|
+
<p className="text-sm text-muted-foreground">
|
|
101
|
+
Feature illustration
|
|
102
|
+
</p>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</section>
|
|
109
|
+
)
|
|
110
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import Link from "next/link"
|
|
4
|
+
import { cn } from "@/lib/utils"
|
|
5
|
+
|
|
6
|
+
interface FinalCTALink {
|
|
7
|
+
label: string
|
|
8
|
+
href: string
|
|
9
|
+
variant: "default" | "outline"
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface FinalCTAProps {
|
|
13
|
+
headline?: string
|
|
14
|
+
subheading?: string
|
|
15
|
+
links?: FinalCTALink[]
|
|
16
|
+
style?: "dark" | "light" | "gradient"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function FinalCTA({
|
|
20
|
+
headline = "Ready to transform how your team works?",
|
|
21
|
+
subheading = "Join thousands of teams who chose the smarter way to work. Start free, upgrade as you grow.",
|
|
22
|
+
links = [
|
|
23
|
+
{ label: "Start free trial", href: "/sign-up", variant: "outline" },
|
|
24
|
+
{ label: "Book a demo", href: "/contact", variant: "default" },
|
|
25
|
+
],
|
|
26
|
+
style = "dark",
|
|
27
|
+
}: FinalCTAProps) {
|
|
28
|
+
const bgClasses = {
|
|
29
|
+
dark: "bg-foreground text-background",
|
|
30
|
+
light: "bg-muted text-foreground",
|
|
31
|
+
gradient: "bg-gradient-to-br from-primary to-primary/80 text-primary-foreground",
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const buttonClasses = {
|
|
35
|
+
dark: {
|
|
36
|
+
default: "bg-background text-foreground hover:bg-background/90",
|
|
37
|
+
outline: "border border-background/30 text-background hover:bg-background/10",
|
|
38
|
+
},
|
|
39
|
+
light: {
|
|
40
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
41
|
+
outline: "border border-border hover:bg-background",
|
|
42
|
+
},
|
|
43
|
+
gradient: {
|
|
44
|
+
default: "bg-background text-foreground hover:bg-background/90",
|
|
45
|
+
outline: "border border-primary-foreground/30 text-primary-foreground hover:bg-primary-foreground/10",
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<section className={cn("py-16 md:py-24", bgClasses[style])}>
|
|
51
|
+
<div className="container mx-auto px-4">
|
|
52
|
+
<div className="max-w-3xl mx-auto text-center">
|
|
53
|
+
<h2 className="text-3xl md:text-4xl lg:text-5xl font-bold tracking-tight mb-6">
|
|
54
|
+
{headline}
|
|
55
|
+
</h2>
|
|
56
|
+
{subheading && (
|
|
57
|
+
<p
|
|
58
|
+
className={cn(
|
|
59
|
+
"text-lg md:text-xl mb-8",
|
|
60
|
+
style === "dark"
|
|
61
|
+
? "text-background/80"
|
|
62
|
+
: style === "gradient"
|
|
63
|
+
? "text-primary-foreground/80"
|
|
64
|
+
: "text-muted-foreground"
|
|
65
|
+
)}
|
|
66
|
+
>
|
|
67
|
+
{subheading}
|
|
68
|
+
</p>
|
|
69
|
+
)}
|
|
70
|
+
|
|
71
|
+
{links && links.length > 0 && (
|
|
72
|
+
<div className="flex flex-wrap gap-4 justify-center">
|
|
73
|
+
{links.map((link) => (
|
|
74
|
+
<Link
|
|
75
|
+
key={link.href}
|
|
76
|
+
href={link.href}
|
|
77
|
+
className={cn(
|
|
78
|
+
"inline-flex items-center justify-center px-6 py-3 text-sm font-medium rounded-md transition-colors",
|
|
79
|
+
buttonClasses[style][link.variant]
|
|
80
|
+
)}
|
|
81
|
+
>
|
|
82
|
+
{link.label}
|
|
83
|
+
</Link>
|
|
84
|
+
))}
|
|
85
|
+
</div>
|
|
86
|
+
)}
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
</section>
|
|
90
|
+
)
|
|
91
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useState } from "react"
|
|
4
|
+
import Link from "next/link"
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
interface Tab {
|
|
8
|
+
name: string
|
|
9
|
+
stat: string
|
|
10
|
+
statLabel: string
|
|
11
|
+
description: string
|
|
12
|
+
link?: {
|
|
13
|
+
label: string
|
|
14
|
+
href: string
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface IndustryTabsProps {
|
|
19
|
+
heading?: string
|
|
20
|
+
subheading?: string
|
|
21
|
+
tabs?: Tab[]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const defaultTabs: Tab[] = [
|
|
25
|
+
{
|
|
26
|
+
name: "Sales Teams",
|
|
27
|
+
stat: "40%",
|
|
28
|
+
statLabel: "Faster deal cycles with smart pipeline tools",
|
|
29
|
+
description:
|
|
30
|
+
"Close deals faster with intelligent pipeline management, automated follow-ups, and real-time insights that help your team hit quota every quarter.",
|
|
31
|
+
link: { label: "Solutions for sales", href: "/use-cases/sales" },
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: "Marketing Teams",
|
|
35
|
+
stat: "3x",
|
|
36
|
+
statLabel: "Campaign velocity with streamlined workflows",
|
|
37
|
+
description:
|
|
38
|
+
"Launch campaigns that convert with collaborative planning, asset management, and performance analytics all in one place.",
|
|
39
|
+
link: { label: "Solutions for marketing", href: "/use-cases/marketing" },
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: "Product Teams",
|
|
43
|
+
stat: "50%",
|
|
44
|
+
statLabel: "Faster shipping with better prioritization",
|
|
45
|
+
description:
|
|
46
|
+
"Ship features users love with roadmap planning, feedback collection, and release management that keeps everyone aligned.",
|
|
47
|
+
link: { label: "Solutions for product", href: "/use-cases/product" },
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "Operations",
|
|
51
|
+
stat: "60%",
|
|
52
|
+
statLabel: "Time saved with process automation",
|
|
53
|
+
description:
|
|
54
|
+
"Scale your operations without the chaos. Automate processes, manage resources, and get visibility across your entire organization.",
|
|
55
|
+
link: { label: "Solutions for ops", href: "/use-cases/operations" },
|
|
56
|
+
},
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
export function IndustryTabs({
|
|
60
|
+
heading = "Solutions that deliver real results",
|
|
61
|
+
subheading = "Whether you're in sales, marketing, or product, SaaSify adapts to how your team works.",
|
|
62
|
+
tabs = defaultTabs,
|
|
63
|
+
}: IndustryTabsProps) {
|
|
64
|
+
const [activeTab, setActiveTab] = useState(0)
|
|
65
|
+
const displayTabs = tabs.length > 0 ? tabs : defaultTabs
|
|
66
|
+
const activeContent = displayTabs[activeTab]
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<section className="py-16 md:py-24 bg-muted/30">
|
|
70
|
+
<div className="container mx-auto px-4">
|
|
71
|
+
{/* Header */}
|
|
72
|
+
{(heading || subheading) && (
|
|
73
|
+
<div className="text-center mb-12 md:mb-16 max-w-3xl mx-auto">
|
|
74
|
+
{heading && (
|
|
75
|
+
<h2 className="text-3xl md:text-4xl lg:text-5xl font-bold tracking-tight mb-4">
|
|
76
|
+
{heading}
|
|
77
|
+
</h2>
|
|
78
|
+
)}
|
|
79
|
+
{subheading && (
|
|
80
|
+
<p className="text-lg md:text-xl text-muted-foreground">
|
|
81
|
+
{subheading}
|
|
82
|
+
</p>
|
|
83
|
+
)}
|
|
84
|
+
</div>
|
|
85
|
+
)}
|
|
86
|
+
|
|
87
|
+
{/* Tabs */}
|
|
88
|
+
<div className="flex flex-wrap justify-center gap-2 mb-12">
|
|
89
|
+
{displayTabs.map((tab, index) => (
|
|
90
|
+
<button
|
|
91
|
+
key={tab.name}
|
|
92
|
+
type="button"
|
|
93
|
+
onClick={() => setActiveTab(index)}
|
|
94
|
+
className={cn(
|
|
95
|
+
"px-4 py-2 text-sm font-medium rounded-full transition-colors",
|
|
96
|
+
activeTab === index
|
|
97
|
+
? "bg-primary text-primary-foreground"
|
|
98
|
+
: "bg-background border border-border hover:bg-muted"
|
|
99
|
+
)}
|
|
100
|
+
>
|
|
101
|
+
{tab.name}
|
|
102
|
+
</button>
|
|
103
|
+
))}
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
{/* Content */}
|
|
107
|
+
<div className="grid md:grid-cols-2 gap-12 lg:gap-16 items-center max-w-5xl mx-auto">
|
|
108
|
+
{/* Stats side */}
|
|
109
|
+
<div className="text-center md:text-left">
|
|
110
|
+
<div className="text-7xl md:text-8xl lg:text-9xl font-bold text-primary mb-2">
|
|
111
|
+
{activeContent.stat}
|
|
112
|
+
</div>
|
|
113
|
+
<p className="text-xl md:text-2xl font-medium text-foreground">
|
|
114
|
+
{activeContent.statLabel}
|
|
115
|
+
</p>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
{/* Description side */}
|
|
119
|
+
<div>
|
|
120
|
+
<p className="text-lg text-muted-foreground mb-6">
|
|
121
|
+
{activeContent.description}
|
|
122
|
+
</p>
|
|
123
|
+
{activeContent.link && (
|
|
124
|
+
<Link
|
|
125
|
+
href={activeContent.link.href}
|
|
126
|
+
className="inline-flex items-center gap-2 px-6 py-3 text-sm font-medium rounded-md bg-primary text-primary-foreground hover:bg-primary/90 transition-colors"
|
|
127
|
+
>
|
|
128
|
+
{activeContent.link.label}
|
|
129
|
+
<span aria-hidden="true">→</span>
|
|
130
|
+
</Link>
|
|
131
|
+
)}
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
</section>
|
|
136
|
+
)
|
|
137
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
|
|
5
|
+
interface Logo {
|
|
6
|
+
name: string
|
|
7
|
+
initials?: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface LogoBannerProps {
|
|
11
|
+
heading?: string
|
|
12
|
+
logos?: Logo[]
|
|
13
|
+
style?: "scroll" | "grid"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const defaultLogos: Logo[] = [
|
|
17
|
+
{ name: "TechFlow Inc", initials: "TF" },
|
|
18
|
+
{ name: "Acme Corp", initials: "AC" },
|
|
19
|
+
{ name: "Evergreen HQ", initials: "EH" },
|
|
20
|
+
{ name: "Atlas Network", initials: "AN" },
|
|
21
|
+
{ name: "Beacon Digital", initials: "BD" },
|
|
22
|
+
{ name: "Cascade Systems", initials: "CS" },
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
function LogoItem({ logo }: { logo: Logo }) {
|
|
26
|
+
return (
|
|
27
|
+
<div
|
|
28
|
+
className={cn(
|
|
29
|
+
"logo-item flex items-center justify-center px-6 md:px-8",
|
|
30
|
+
"grayscale hover:grayscale-0 transition-all duration-300"
|
|
31
|
+
)}
|
|
32
|
+
>
|
|
33
|
+
<div className="flex items-center gap-2">
|
|
34
|
+
<span className="w-8 h-8 rounded bg-muted flex items-center justify-center text-xs font-bold text-foreground">
|
|
35
|
+
{logo.initials || logo.name.slice(0, 2).toUpperCase()}
|
|
36
|
+
</span>
|
|
37
|
+
<span className="text-sm font-medium text-muted-foreground hidden md:block">
|
|
38
|
+
{logo.name}
|
|
39
|
+
</span>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function LogoBanner({
|
|
46
|
+
heading = "Trusted by fast-growing companies everywhere",
|
|
47
|
+
logos = defaultLogos,
|
|
48
|
+
style = "scroll",
|
|
49
|
+
}: LogoBannerProps) {
|
|
50
|
+
const displayLogos = logos.length > 0 ? logos : defaultLogos
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<section className="py-12 md:py-16 border-y border-border/50">
|
|
54
|
+
<div className="container mx-auto px-4">
|
|
55
|
+
{heading && (
|
|
56
|
+
<p className="text-center text-sm text-muted-foreground mb-8 uppercase tracking-wider">
|
|
57
|
+
{heading}
|
|
58
|
+
</p>
|
|
59
|
+
)}
|
|
60
|
+
|
|
61
|
+
{style === "scroll" ? (
|
|
62
|
+
<div className="logo-scroll-container">
|
|
63
|
+
<div className="logo-scroll-track">
|
|
64
|
+
{/* Double the logos for seamless loop */}
|
|
65
|
+
{[...displayLogos, ...displayLogos].map((logo, index) => (
|
|
66
|
+
<LogoItem key={`${logo.name}-${index}`} logo={logo} />
|
|
67
|
+
))}
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
) : (
|
|
71
|
+
<div className="grid grid-cols-3 md:grid-cols-6 gap-8 items-center justify-items-center">
|
|
72
|
+
{displayLogos.map((logo) => (
|
|
73
|
+
<LogoItem key={logo.name} logo={logo} />
|
|
74
|
+
))}
|
|
75
|
+
</div>
|
|
76
|
+
)}
|
|
77
|
+
</div>
|
|
78
|
+
</section>
|
|
79
|
+
)
|
|
80
|
+
}
|