Skip to content

Commit 0252145

Browse files
Merge pull request #11 from devinschumacher/main
add apps/extensions
2 parents 41f7425 + f0a19ee commit 0252145

File tree

18 files changed

+1369
-21
lines changed

18 files changed

+1369
-21
lines changed
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
"use client";
2+
3+
import { useState, useEffect } from "react";
4+
import { Button } from "@serp-tools/ui/components/button";
5+
import { Badge } from "@serp-tools/ui/components/badge";
6+
import { ExtensionCard } from "@/components/ExtensionCard";
7+
import { ExtensionsSearchBar } from "@/components/ExtensionsSearchBar";
8+
import {
9+
Sparkles,
10+
ArrowRight,
11+
Shield,
12+
Lock,
13+
Eye,
14+
ShoppingCart,
15+
Coins,
16+
Moon,
17+
CheckSquare,
18+
Bookmark,
19+
Palette,
20+
Code,
21+
Video,
22+
Clipboard,
23+
Gauge,
24+
Globe,
25+
Puzzle
26+
} from "lucide-react";
27+
import extensionsData from '@serp-tools/app-core/data/extensions.json';
28+
29+
// Icon mapping for extensions
30+
const iconMap: { [key: string]: any } = {
31+
'ublock-origin': Shield,
32+
'lastpass': Lock,
33+
'grammarly': CheckSquare,
34+
'honey': ShoppingCart,
35+
'metamask': Coins,
36+
'dark-reader': Moon,
37+
'todoist': CheckSquare,
38+
'pocket': Bookmark,
39+
'colorpick-eyedropper': Palette,
40+
'wappalyzer': Code,
41+
'json-formatter': Code,
42+
'ghostery': Shield,
43+
'loom': Video,
44+
'notion-web-clipper': Clipboard,
45+
'momentum': Gauge,
46+
};
47+
48+
// Process extensions from JSON data
49+
const processedExtensions = extensionsData
50+
.filter((extension: any) => extension.isActive)
51+
.map((extension: any) => ({
52+
id: extension.id,
53+
name: extension.name,
54+
description: extension.description,
55+
category: extension.category || 'other',
56+
icon: iconMap[extension.id] || Puzzle,
57+
href: extension.chromeStoreUrl || extension.firefoxAddonUrl || extension.url,
58+
tags: extension.tags || [],
59+
isNew: extension.isNew || false,
60+
isPopular: extension.isPopular || false,
61+
rating: extension.rating,
62+
users: extension.users,
63+
}));
64+
65+
66+
export default function HomePage() {
67+
const [selectedCategory, setSelectedCategory] = useState("all");
68+
const [searchQuery, setSearchQuery] = useState("");
69+
const [extensions, setExtensions] = useState(processedExtensions);
70+
const [categories, setCategories] = useState<any[]>([]);
71+
72+
useEffect(() => {
73+
// Create categories from extensions
74+
const categoryMap = new Map();
75+
categoryMap.set('all', { id: 'all', name: 'All Extensions', count: extensions.length });
76+
77+
extensions.forEach(extension => {
78+
if (!categoryMap.has(extension.category)) {
79+
// Use proper category names
80+
let catName = extension.category.charAt(0).toUpperCase() + extension.category.slice(1);
81+
if (extension.category === 'privacy') catName = 'Privacy & Security';
82+
else if (extension.category === 'productivity') catName = 'Productivity';
83+
else if (extension.category === 'developer') catName = 'Developer Tools';
84+
else if (extension.category === 'shopping') catName = 'Shopping';
85+
else if (extension.category === 'crypto') catName = 'Crypto & Web3';
86+
else if (extension.category === 'accessibility') catName = 'Accessibility';
87+
else if (extension.category === 'other') catName = 'Other';
88+
89+
categoryMap.set(extension.category, {
90+
id: extension.category,
91+
name: catName,
92+
count: 0
93+
});
94+
}
95+
categoryMap.get(extension.category).count++;
96+
});
97+
98+
setCategories(Array.from(categoryMap.values()));
99+
}, [extensions]);
100+
101+
// Filter extensions based on category and search
102+
const filteredExtensions = extensions.filter(extension => {
103+
const matchesCategory = selectedCategory === "all" || extension.category === selectedCategory;
104+
const matchesSearch = extension.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
105+
extension.description.toLowerCase().includes(searchQuery.toLowerCase()) ||
106+
extension.tags.some((tag: string) => tag?.toLowerCase().includes(searchQuery.toLowerCase()));
107+
return matchesCategory && matchesSearch;
108+
});
109+
110+
return (
111+
<main className="min-h-screen">
112+
{/* Hero Section */}
113+
<section className="relative overflow-hidden border-b">
114+
<div className="absolute inset-0 bg-grid-black/[0.02] dark:bg-grid-white/[0.02]" />
115+
<div className="container relative py-16 md:py-24">
116+
<div className="mx-auto max-w-2xl text-center">
117+
<Badge className="mb-4" variant="secondary">
118+
<Sparkles className="mr-1 h-3 w-3" />
119+
Discover...
120+
</Badge>
121+
<h1 className="mb-4 text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl">
122+
Browser extensions that supercharge your productivity
123+
</h1>
124+
</div>
125+
</div>
126+
</section>
127+
128+
{/* Main Content */}
129+
<section className="container py-12">
130+
{/* Search and Filter Bar */}
131+
<ExtensionsSearchBar
132+
searchQuery={searchQuery}
133+
setSearchQuery={setSearchQuery}
134+
categories={categories}
135+
selectedCategory={selectedCategory}
136+
setSelectedCategory={setSelectedCategory}
137+
/>
138+
139+
{/* Extensions Grid */}
140+
<div className="grid gap-5 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
141+
{filteredExtensions.map((extension) => (
142+
<ExtensionCard key={extension.id} extension={extension} />
143+
))}
144+
</div>
145+
146+
{filteredExtensions.length === 0 && (
147+
<div className="py-12 text-center">
148+
<p className="text-lg text-muted-foreground">
149+
No extensions found matching your criteria.
150+
</p>
151+
</div>
152+
)}
153+
</section>
154+
155+
{/* CTA Section */}
156+
<section className="border-t bg-muted/30">
157+
<div className="container py-16">
158+
<div className="mx-auto max-w-2xl text-center">
159+
<h2 className="mb-4 text-3xl font-bold">
160+
Missing an extension?
161+
</h2>
162+
<p className="mb-8 text-lg text-muted-foreground">
163+
We&apos;re constantly adding new extensions to our directory. Let us know what you&apos;re looking for!
164+
</p>
165+
<Button size="lg" className="group">
166+
Suggest an Extension
167+
<ArrowRight className="ml-2 h-4 w-4 transition-transform group-hover:translate-x-1" />
168+
</Button>
169+
</div>
170+
</div>
171+
</section>
172+
</main>
173+
);
174+
}

0 commit comments

Comments
 (0)