@beknurakhmed/webforge-cli 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.
- package/README.md +212 -0
- package/dist/index.js +386 -0
- package/package.json +63 -0
- package/templates/angular/angular.json +36 -0
- package/templates/angular/package.json +29 -0
- package/templates/angular/src/app/app.component.ts +27 -0
- package/templates/angular/src/index.html +11 -0
- package/templates/angular/src/main.ts +4 -0
- package/templates/angular/src/styles.css +24 -0
- package/templates/angular/tsconfig.json +27 -0
- package/templates/base/README.md +16 -0
- package/templates/base/_gitignore +11 -0
- package/templates/extras/eslint/config/eslint.config.js +18 -0
- package/templates/extras/eslint/deps.json +7 -0
- package/templates/extras/prettier/config/.prettierignore +6 -0
- package/templates/extras/prettier/config/.prettierrc +7 -0
- package/templates/extras/prettier/deps.json +5 -0
- package/templates/nextjs/next.config.ts +5 -0
- package/templates/nextjs/package.json +16 -0
- package/templates/nextjs/src/app/globals.css +36 -0
- package/templates/nextjs/src/app/layout.tsx +19 -0
- package/templates/nextjs/src/app/page.tsx +8 -0
- package/templates/nextjs/tsconfig.json +21 -0
- package/templates/nuxt/app.vue +3 -0
- package/templates/nuxt/assets/css/main.css +24 -0
- package/templates/nuxt/nuxt.config.ts +5 -0
- package/templates/nuxt/package.json +17 -0
- package/templates/nuxt/pages/index.vue +20 -0
- package/templates/nuxt/tsconfig.json +3 -0
- package/templates/overlays/blog/react/src/App.css +41 -0
- package/templates/overlays/blog/react/src/App.tsx +39 -0
- package/templates/overlays/blog/react/src/components/BlogFooter.tsx +7 -0
- package/templates/overlays/blog/react/src/components/BlogHeader.tsx +15 -0
- package/templates/overlays/blog/react/src/components/BlogSidebar.tsx +26 -0
- package/templates/overlays/blog/react/src/components/PostList.tsx +27 -0
- package/templates/overlays/crm/react/src/App.css +48 -0
- package/templates/overlays/crm/react/src/App.tsx +49 -0
- package/templates/overlays/crm/react/src/components/ContactsTable.tsx +40 -0
- package/templates/overlays/crm/react/src/components/Filters.tsx +22 -0
- package/templates/overlays/crm/react/src/components/Sidebar.tsx +25 -0
- package/templates/overlays/crm/react/src/components/StatsCards.tsx +26 -0
- package/templates/overlays/dashboard/react/src/App.css +64 -0
- package/templates/overlays/dashboard/react/src/App.tsx +30 -0
- package/templates/overlays/dashboard/react/src/components/ChartPlaceholder.tsx +18 -0
- package/templates/overlays/dashboard/react/src/components/DataTable.tsx +44 -0
- package/templates/overlays/dashboard/react/src/components/KPICards.tsx +22 -0
- package/templates/overlays/dashboard/react/src/components/Sidebar.tsx +24 -0
- package/templates/overlays/ecommerce/react/src/App.css +82 -0
- package/templates/overlays/ecommerce/react/src/App.tsx +68 -0
- package/templates/overlays/ecommerce/react/src/components/Cart.tsx +47 -0
- package/templates/overlays/ecommerce/react/src/components/Footer.tsx +29 -0
- package/templates/overlays/ecommerce/react/src/components/Header.tsx +26 -0
- package/templates/overlays/ecommerce/react/src/components/ProductGrid.tsx +32 -0
- package/templates/overlays/ecommerce/vue/src/App.vue +44 -0
- package/templates/overlays/ecommerce/vue/src/components/CartPanel.vue +46 -0
- package/templates/overlays/ecommerce/vue/src/components/ProductGrid.vue +40 -0
- package/templates/overlays/ecommerce/vue/src/components/StoreFooter.vue +17 -0
- package/templates/overlays/ecommerce/vue/src/components/StoreHeader.vue +33 -0
- package/templates/overlays/landing/angular/src/app/app.component.ts +21 -0
- package/templates/overlays/landing/angular/src/app/components/cta.component.ts +24 -0
- package/templates/overlays/landing/angular/src/app/components/features.component.ts +42 -0
- package/templates/overlays/landing/angular/src/app/components/footer.component.ts +45 -0
- package/templates/overlays/landing/angular/src/app/components/hero.component.ts +41 -0
- package/templates/overlays/landing/nextjs/src/app/components/CTA.tsx +12 -0
- package/templates/overlays/landing/nextjs/src/app/components/Features.tsx +26 -0
- package/templates/overlays/landing/nextjs/src/app/components/Footer.tsx +27 -0
- package/templates/overlays/landing/nextjs/src/app/components/Hero.tsx +22 -0
- package/templates/overlays/landing/nextjs/src/app/globals.css +49 -0
- package/templates/overlays/landing/nextjs/src/app/page.tsx +15 -0
- package/templates/overlays/landing/nuxt/components/LandingCta.vue +18 -0
- package/templates/overlays/landing/nuxt/components/LandingFeatures.vue +36 -0
- package/templates/overlays/landing/nuxt/components/LandingFooter.vue +29 -0
- package/templates/overlays/landing/nuxt/components/LandingHero.vue +35 -0
- package/templates/overlays/landing/nuxt/pages/index.vue +15 -0
- package/templates/overlays/landing/react/src/App.css +70 -0
- package/templates/overlays/landing/react/src/App.tsx +18 -0
- package/templates/overlays/landing/react/src/components/CTA.tsx +14 -0
- package/templates/overlays/landing/react/src/components/Features.tsx +50 -0
- package/templates/overlays/landing/react/src/components/Footer.tsx +34 -0
- package/templates/overlays/landing/react/src/components/Hero.tsx +22 -0
- package/templates/overlays/landing/vanilla/src/main.ts +68 -0
- package/templates/overlays/landing/vanilla/src/style.css +43 -0
- package/templates/overlays/landing/vue/src/App.vue +19 -0
- package/templates/overlays/landing/vue/src/components/AppFooter.vue +44 -0
- package/templates/overlays/landing/vue/src/components/CTA.vue +21 -0
- package/templates/overlays/landing/vue/src/components/Features.vue +36 -0
- package/templates/overlays/landing/vue/src/components/Hero.vue +35 -0
- package/templates/overlays/portfolio/react/src/App.css +81 -0
- package/templates/overlays/portfolio/react/src/App.tsx +20 -0
- package/templates/overlays/portfolio/react/src/components/ContactForm.tsx +29 -0
- package/templates/overlays/portfolio/react/src/components/HeroSection.tsx +24 -0
- package/templates/overlays/portfolio/react/src/components/PortfolioFooter.tsx +14 -0
- package/templates/overlays/portfolio/react/src/components/Projects.tsx +33 -0
- package/templates/overlays/portfolio/react/src/components/Skills.tsx +27 -0
- package/templates/react/index.html +13 -0
- package/templates/react/package.json +19 -0
- package/templates/react/public/vite.svg +1 -0
- package/templates/react/src/App.css +11 -0
- package/templates/react/src/App.tsx +12 -0
- package/templates/react/src/index.css +42 -0
- package/templates/react/src/main.tsx +10 -0
- package/templates/react/vite.config.ts +6 -0
- package/templates/state/mobx/deps.json +6 -0
- package/templates/state/mobx/react/src/store/AppStore.ts +15 -0
- package/templates/state/ngrx/angular/src/app/store/app.state.ts +22 -0
- package/templates/state/ngrx/deps.json +7 -0
- package/templates/state/pinia/deps.json +5 -0
- package/templates/state/pinia/vue/src/store/useAppStore.ts +15 -0
- package/templates/state/redux/deps.json +6 -0
- package/templates/state/redux/react/src/store/counterSlice.ts +20 -0
- package/templates/state/redux/react/src/store/index.ts +11 -0
- package/templates/state/rxjs/angular/src/app/services/app-state.service.ts +18 -0
- package/templates/state/rxjs/deps.json +3 -0
- package/templates/state/zustand/deps.json +5 -0
- package/templates/state/zustand/react/src/store/useStore.ts +15 -0
- package/templates/styling/angular-material/deps.json +6 -0
- package/templates/styling/ant-design/deps.json +5 -0
- package/templates/styling/chakra-ui/deps.json +5 -0
- package/templates/styling/css-modules/deps.json +3 -0
- package/templates/styling/material-ui/deps.json +7 -0
- package/templates/styling/scss/deps.json +5 -0
- package/templates/styling/styled-components/deps.json +5 -0
- package/templates/styling/tailwind/config/postcss.config.js +5 -0
- package/templates/styling/tailwind/config/tailwind.config.js +11 -0
- package/templates/styling/tailwind/deps.json +6 -0
- package/templates/vanilla/index.html +13 -0
- package/templates/vanilla/package.json +14 -0
- package/templates/vanilla/src/main.ts +9 -0
- package/templates/vanilla/src/style.css +36 -0
- package/templates/vanilla/vite.config.ts +3 -0
- package/templates/vue/index.html +13 -0
- package/templates/vue/package.json +18 -0
- package/templates/vue/src/App.vue +23 -0
- package/templates/vue/src/main.ts +5 -0
- package/templates/vue/src/style.css +39 -0
- package/templates/vue/vite.config.ts +6 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { Sidebar } from './components/Sidebar';
|
|
3
|
+
import { StatsCards } from './components/StatsCards';
|
|
4
|
+
import { ContactsTable } from './components/ContactsTable';
|
|
5
|
+
import { Filters } from './components/Filters';
|
|
6
|
+
import './App.css';
|
|
7
|
+
|
|
8
|
+
export interface Contact {
|
|
9
|
+
id: number;
|
|
10
|
+
name: string;
|
|
11
|
+
email: string;
|
|
12
|
+
company: string;
|
|
13
|
+
status: 'Lead' | 'Prospect' | 'Customer' | 'Churned';
|
|
14
|
+
value: string;
|
|
15
|
+
lastContact: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const initialContacts: Contact[] = [
|
|
19
|
+
{ id: 1, name: 'Sarah Connor', email: 'sarah@acme.com', company: 'Acme Corp', status: 'Customer', value: '$12,400', lastContact: '2 hours ago' },
|
|
20
|
+
{ id: 2, name: 'John Smith', email: 'john@globex.com', company: 'Globex Inc', status: 'Lead', value: '$8,200', lastContact: '1 day ago' },
|
|
21
|
+
{ id: 3, name: 'Emma Wilson', email: 'emma@wayne.com', company: 'Wayne Enterprises', status: 'Prospect', value: '$34,000', lastContact: '3 days ago' },
|
|
22
|
+
{ id: 4, name: 'Mike Chen', email: 'mike@stark.com', company: 'Stark Industries', status: 'Customer', value: '$22,500', lastContact: '5 hours ago' },
|
|
23
|
+
{ id: 5, name: 'Lisa Park', email: 'lisa@initech.com', company: 'Initech', status: 'Churned', value: '$5,100', lastContact: '2 weeks ago' },
|
|
24
|
+
{ id: 6, name: 'David Lee', email: 'david@umbrella.com', company: 'Umbrella Corp', status: 'Lead', value: '$15,800', lastContact: '4 hours ago' },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
function App() {
|
|
28
|
+
const [contacts] = useState<Contact[]>(initialContacts);
|
|
29
|
+
const [filter, setFilter] = useState<string>('all');
|
|
30
|
+
|
|
31
|
+
const filtered = filter === 'all' ? contacts : contacts.filter((c) => c.status.toLowerCase() === filter);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div className="crm">
|
|
35
|
+
<Sidebar />
|
|
36
|
+
<main className="crm-main">
|
|
37
|
+
<header className="crm-header">
|
|
38
|
+
<h1>Contacts</h1>
|
|
39
|
+
<button className="btn-primary">+ Add Contact</button>
|
|
40
|
+
</header>
|
|
41
|
+
<StatsCards contacts={contacts} />
|
|
42
|
+
<Filters activeFilter={filter} onFilterChange={setFilter} />
|
|
43
|
+
<ContactsTable contacts={filtered} />
|
|
44
|
+
</main>
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export default App;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Contact } from '../App';
|
|
2
|
+
|
|
3
|
+
interface ContactsTableProps {
|
|
4
|
+
contacts: Contact[];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function ContactsTable({ contacts }: ContactsTableProps) {
|
|
8
|
+
return (
|
|
9
|
+
<div className="table-card">
|
|
10
|
+
<table className="data-table">
|
|
11
|
+
<thead>
|
|
12
|
+
<tr>
|
|
13
|
+
<th>Name</th>
|
|
14
|
+
<th>Company</th>
|
|
15
|
+
<th>Email</th>
|
|
16
|
+
<th>Status</th>
|
|
17
|
+
<th>Deal Value</th>
|
|
18
|
+
<th>Last Contact</th>
|
|
19
|
+
</tr>
|
|
20
|
+
</thead>
|
|
21
|
+
<tbody>
|
|
22
|
+
{contacts.map((c) => (
|
|
23
|
+
<tr key={c.id}>
|
|
24
|
+
<td className="contact-name">{c.name}</td>
|
|
25
|
+
<td>{c.company}</td>
|
|
26
|
+
<td className="contact-email">{c.email}</td>
|
|
27
|
+
<td>
|
|
28
|
+
<span className={`status-badge status-${c.status.toLowerCase()}`}>
|
|
29
|
+
{c.status}
|
|
30
|
+
</span>
|
|
31
|
+
</td>
|
|
32
|
+
<td className="contact-value">{c.value}</td>
|
|
33
|
+
<td className="contact-time">{c.lastContact}</td>
|
|
34
|
+
</tr>
|
|
35
|
+
))}
|
|
36
|
+
</tbody>
|
|
37
|
+
</table>
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
interface FiltersProps {
|
|
2
|
+
activeFilter: string;
|
|
3
|
+
onFilterChange: (filter: string) => void;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const filters = ['all', 'lead', 'prospect', 'customer', 'churned'];
|
|
7
|
+
|
|
8
|
+
export function Filters({ activeFilter, onFilterChange }: FiltersProps) {
|
|
9
|
+
return (
|
|
10
|
+
<div className="filters">
|
|
11
|
+
{filters.map((f) => (
|
|
12
|
+
<button
|
|
13
|
+
key={f}
|
|
14
|
+
className={`filter-btn ${activeFilter === f ? 'active' : ''}`}
|
|
15
|
+
onClick={() => onFilterChange(f)}
|
|
16
|
+
>
|
|
17
|
+
{f.charAt(0).toUpperCase() + f.slice(1)}
|
|
18
|
+
</button>
|
|
19
|
+
))}
|
|
20
|
+
</div>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const menuItems = [
|
|
2
|
+
{ icon: '👥', label: 'Contacts', active: true },
|
|
3
|
+
{ icon: '🏢', label: 'Companies', active: false },
|
|
4
|
+
{ icon: '💰', label: 'Deals', active: false },
|
|
5
|
+
{ icon: '📧', label: 'Emails', active: false },
|
|
6
|
+
{ icon: '📅', label: 'Calendar', active: false },
|
|
7
|
+
{ icon: '📊', label: 'Reports', active: false },
|
|
8
|
+
{ icon: '⚙️', label: 'Settings', active: false },
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
export function Sidebar() {
|
|
12
|
+
return (
|
|
13
|
+
<aside className="sidebar">
|
|
14
|
+
<div className="sidebar-logo">{{projectName}}</div>
|
|
15
|
+
<nav className="sidebar-nav">
|
|
16
|
+
{menuItems.map((item, i) => (
|
|
17
|
+
<a key={i} href="#" className={`sidebar-item ${item.active ? 'active' : ''}`}>
|
|
18
|
+
<span>{item.icon}</span>
|
|
19
|
+
{item.label}
|
|
20
|
+
</a>
|
|
21
|
+
))}
|
|
22
|
+
</nav>
|
|
23
|
+
</aside>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Contact } from '../App';
|
|
2
|
+
|
|
3
|
+
interface StatsCardsProps {
|
|
4
|
+
contacts: Contact[];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function StatsCards({ contacts }: StatsCardsProps) {
|
|
8
|
+
const stats = [
|
|
9
|
+
{ label: 'Total Contacts', value: contacts.length, color: '#4f46e5' },
|
|
10
|
+
{ label: 'Leads', value: contacts.filter((c) => c.status === 'Lead').length, color: '#f59e0b' },
|
|
11
|
+
{ label: 'Customers', value: contacts.filter((c) => c.status === 'Customer').length, color: '#10b981' },
|
|
12
|
+
{ label: 'Churned', value: contacts.filter((c) => c.status === 'Churned').length, color: '#ef4444' },
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<div className="stats-grid">
|
|
17
|
+
{stats.map((stat, i) => (
|
|
18
|
+
<div key={i} className="stat-card">
|
|
19
|
+
<div className="stat-indicator" style={{ background: stat.color }} />
|
|
20
|
+
<span className="stat-label">{stat.label}</span>
|
|
21
|
+
<div className="stat-value">{stat.value}</div>
|
|
22
|
+
</div>
|
|
23
|
+
))}
|
|
24
|
+
</div>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
.dashboard { display: flex; min-height: 100vh; }
|
|
2
|
+
|
|
3
|
+
/* Sidebar */
|
|
4
|
+
.sidebar { width: 250px; background: #111827; color: white; padding: 1.5rem; flex-shrink: 0; }
|
|
5
|
+
.sidebar-logo { font-size: 1.25rem; font-weight: 700; padding: 0.5rem 0.75rem; margin-bottom: 2rem; color: #4f46e5; }
|
|
6
|
+
.sidebar-nav { display: flex; flex-direction: column; gap: 0.25rem; }
|
|
7
|
+
.sidebar-item { display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem; border-radius: 8px; color: #9ca3af; text-decoration: none; transition: all 0.2s; }
|
|
8
|
+
.sidebar-item:hover { background: #1f2937; color: white; }
|
|
9
|
+
.sidebar-item.active { background: #4f46e5; color: white; }
|
|
10
|
+
.sidebar-icon { font-size: 1.1rem; }
|
|
11
|
+
|
|
12
|
+
/* Main */
|
|
13
|
+
.dashboard-main { flex: 1; background: #f9fafb; padding: 2rem; overflow-y: auto; }
|
|
14
|
+
.dashboard-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 2rem; }
|
|
15
|
+
.dashboard-header h1 { font-size: 1.75rem; font-weight: 700; color: #111827; margin: 0; }
|
|
16
|
+
.header-actions { display: flex; align-items: center; gap: 1rem; }
|
|
17
|
+
.search-input { padding: 0.5rem 1rem; border: 1px solid #d1d5db; border-radius: 8px; font-size: 0.9rem; }
|
|
18
|
+
.user-avatar { width: 36px; height: 36px; background: #4f46e5; color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 600; }
|
|
19
|
+
|
|
20
|
+
/* KPI Cards */
|
|
21
|
+
.kpi-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 1.5rem; margin-bottom: 2rem; }
|
|
22
|
+
.kpi-card { background: white; border-radius: 12px; padding: 1.5rem; border: 1px solid #e5e7eb; }
|
|
23
|
+
.kpi-label { font-size: 0.85rem; color: #6b7280; font-weight: 500; }
|
|
24
|
+
.kpi-value { font-size: 2rem; font-weight: 700; margin: 0.25rem 0; color: #111827; }
|
|
25
|
+
.kpi-change { font-size: 0.8rem; }
|
|
26
|
+
.kpi-change.positive { color: #10b981; }
|
|
27
|
+
.kpi-change.negative { color: #ef4444; }
|
|
28
|
+
|
|
29
|
+
/* Charts */
|
|
30
|
+
.dashboard-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); gap: 1.5rem; margin-bottom: 2rem; }
|
|
31
|
+
.chart-card { background: white; border-radius: 12px; padding: 1.5rem; border: 1px solid #e5e7eb; }
|
|
32
|
+
.chart-card h3 { font-size: 1.1rem; font-weight: 600; margin-bottom: 1rem; color: #111827; }
|
|
33
|
+
.chart-placeholder { height: 200px; display: flex; align-items: flex-end; }
|
|
34
|
+
.chart-bars { display: flex; gap: 8px; align-items: flex-end; height: 100%; width: 100%; }
|
|
35
|
+
.chart-bar { flex: 1; background: linear-gradient(to top, #4f46e5, #818cf8); border-radius: 4px 4px 0 0; transition: height 0.3s; min-width: 12px; }
|
|
36
|
+
|
|
37
|
+
/* Table */
|
|
38
|
+
.table-card { background: white; border-radius: 12px; border: 1px solid #e5e7eb; overflow: hidden; }
|
|
39
|
+
.table-header { display: flex; justify-content: space-between; align-items: center; padding: 1.25rem 1.5rem; }
|
|
40
|
+
.table-header h3 { margin: 0; font-size: 1.1rem; color: #111827; }
|
|
41
|
+
.table-btn { background: none; border: 1px solid #d1d5db; padding: 0.4rem 1rem; border-radius: 6px; cursor: pointer; font-size: 0.85rem; color: #374151; }
|
|
42
|
+
.data-table { width: 100%; border-collapse: collapse; }
|
|
43
|
+
.data-table th { text-align: left; padding: 0.75rem 1.5rem; font-size: 0.8rem; color: #6b7280; text-transform: uppercase; letter-spacing: 0.05em; border-top: 1px solid #e5e7eb; background: #f9fafb; }
|
|
44
|
+
.data-table td { padding: 0.75rem 1.5rem; border-top: 1px solid #f3f4f6; font-size: 0.9rem; color: #374151; }
|
|
45
|
+
.status-badge { padding: 0.25rem 0.75rem; border-radius: 9999px; font-size: 0.75rem; font-weight: 500; }
|
|
46
|
+
.status-completed { background: #d1fae5; color: #065f46; }
|
|
47
|
+
.status-processing { background: #dbeafe; color: #1e40af; }
|
|
48
|
+
.status-cancelled { background: #fee2e2; color: #991b1b; }
|
|
49
|
+
|
|
50
|
+
@media (max-width: 768px) {
|
|
51
|
+
.dashboard { flex-direction: column; }
|
|
52
|
+
.sidebar { width: 100%; }
|
|
53
|
+
.dashboard-grid { grid-template-columns: 1fr; }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@media (prefers-color-scheme: dark) {
|
|
57
|
+
.dashboard-main { background: #0f172a; }
|
|
58
|
+
.dashboard-header h1 { color: white; }
|
|
59
|
+
.kpi-card, .chart-card, .table-card { background: #1e293b; border-color: #334155; }
|
|
60
|
+
.kpi-value, .chart-card h3, .table-header h3 { color: white; }
|
|
61
|
+
.data-table th { background: #1e293b; border-color: #334155; color: #94a3b8; }
|
|
62
|
+
.data-table td { border-color: #334155; color: #cbd5e1; }
|
|
63
|
+
.search-input { background: #1e293b; border-color: #334155; color: white; }
|
|
64
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Sidebar } from './components/Sidebar';
|
|
2
|
+
import { KPICards } from './components/KPICards';
|
|
3
|
+
import { DataTable } from './components/DataTable';
|
|
4
|
+
import { ChartPlaceholder } from './components/ChartPlaceholder';
|
|
5
|
+
import './App.css';
|
|
6
|
+
|
|
7
|
+
function App() {
|
|
8
|
+
return (
|
|
9
|
+
<div className="dashboard">
|
|
10
|
+
<Sidebar />
|
|
11
|
+
<main className="dashboard-main">
|
|
12
|
+
<header className="dashboard-header">
|
|
13
|
+
<h1>Dashboard</h1>
|
|
14
|
+
<div className="header-actions">
|
|
15
|
+
<input type="text" placeholder="Search..." className="search-input" />
|
|
16
|
+
<div className="user-avatar">B</div>
|
|
17
|
+
</div>
|
|
18
|
+
</header>
|
|
19
|
+
<KPICards />
|
|
20
|
+
<div className="dashboard-grid">
|
|
21
|
+
<ChartPlaceholder title="Revenue Overview" />
|
|
22
|
+
<ChartPlaceholder title="User Growth" />
|
|
23
|
+
</div>
|
|
24
|
+
<DataTable />
|
|
25
|
+
</main>
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default App;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface ChartPlaceholderProps {
|
|
2
|
+
title: string;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function ChartPlaceholder({ title }: ChartPlaceholderProps) {
|
|
6
|
+
return (
|
|
7
|
+
<div className="chart-card">
|
|
8
|
+
<h3>{title}</h3>
|
|
9
|
+
<div className="chart-placeholder">
|
|
10
|
+
<div className="chart-bars">
|
|
11
|
+
{[40, 65, 45, 80, 55, 70, 90, 60, 75, 50, 85, 95].map((h, i) => (
|
|
12
|
+
<div key={i} className="chart-bar" style={{ height: `${h}%` }} />
|
|
13
|
+
))}
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const rows = [
|
|
2
|
+
{ id: '#3210', customer: 'Olivia Martin', email: 'olivia@email.com', amount: '$42.00', status: 'Completed' },
|
|
3
|
+
{ id: '#3209', customer: 'Ava Johnson', email: 'ava@email.com', amount: '$74.99', status: 'Processing' },
|
|
4
|
+
{ id: '#3208', customer: 'Michael Brown', email: 'michael@email.com', amount: '$64.00', status: 'Completed' },
|
|
5
|
+
{ id: '#3207', customer: 'Lisa Anderson', email: 'lisa@email.com', amount: '$49.99', status: 'Cancelled' },
|
|
6
|
+
{ id: '#3206', customer: 'Thomas Wilson', email: 'thomas@email.com', amount: '$120.00', status: 'Completed' },
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
export function DataTable() {
|
|
10
|
+
return (
|
|
11
|
+
<div className="table-card">
|
|
12
|
+
<div className="table-header">
|
|
13
|
+
<h3>Recent Orders</h3>
|
|
14
|
+
<button className="table-btn">View All</button>
|
|
15
|
+
</div>
|
|
16
|
+
<table className="data-table">
|
|
17
|
+
<thead>
|
|
18
|
+
<tr>
|
|
19
|
+
<th>Order</th>
|
|
20
|
+
<th>Customer</th>
|
|
21
|
+
<th>Email</th>
|
|
22
|
+
<th>Amount</th>
|
|
23
|
+
<th>Status</th>
|
|
24
|
+
</tr>
|
|
25
|
+
</thead>
|
|
26
|
+
<tbody>
|
|
27
|
+
{rows.map((row, i) => (
|
|
28
|
+
<tr key={i}>
|
|
29
|
+
<td>{row.id}</td>
|
|
30
|
+
<td>{row.customer}</td>
|
|
31
|
+
<td>{row.email}</td>
|
|
32
|
+
<td>{row.amount}</td>
|
|
33
|
+
<td>
|
|
34
|
+
<span className={`status-badge status-${row.status.toLowerCase()}`}>
|
|
35
|
+
{row.status}
|
|
36
|
+
</span>
|
|
37
|
+
</td>
|
|
38
|
+
</tr>
|
|
39
|
+
))}
|
|
40
|
+
</tbody>
|
|
41
|
+
</table>
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const kpis = [
|
|
2
|
+
{ label: 'Total Revenue', value: '$45,231', change: '+20.1%', positive: true },
|
|
3
|
+
{ label: 'Active Users', value: '2,350', change: '+15.3%', positive: true },
|
|
4
|
+
{ label: 'New Orders', value: '1,247', change: '-3.2%', positive: false },
|
|
5
|
+
{ label: 'Conversion Rate', value: '3.24%', change: '+2.1%', positive: true },
|
|
6
|
+
];
|
|
7
|
+
|
|
8
|
+
export function KPICards() {
|
|
9
|
+
return (
|
|
10
|
+
<div className="kpi-grid">
|
|
11
|
+
{kpis.map((kpi, i) => (
|
|
12
|
+
<div key={i} className="kpi-card">
|
|
13
|
+
<span className="kpi-label">{kpi.label}</span>
|
|
14
|
+
<div className="kpi-value">{kpi.value}</div>
|
|
15
|
+
<span className={`kpi-change ${kpi.positive ? 'positive' : 'negative'}`}>
|
|
16
|
+
{kpi.change} from last month
|
|
17
|
+
</span>
|
|
18
|
+
</div>
|
|
19
|
+
))}
|
|
20
|
+
</div>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const menuItems = [
|
|
2
|
+
{ icon: '📊', label: 'Dashboard', active: true },
|
|
3
|
+
{ icon: '👥', label: 'Users', active: false },
|
|
4
|
+
{ icon: '📦', label: 'Products', active: false },
|
|
5
|
+
{ icon: '📋', label: 'Orders', active: false },
|
|
6
|
+
{ icon: '📈', label: 'Analytics', active: false },
|
|
7
|
+
{ icon: '⚙️', label: 'Settings', active: false },
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
export function Sidebar() {
|
|
11
|
+
return (
|
|
12
|
+
<aside className="sidebar">
|
|
13
|
+
<div className="sidebar-logo">{{projectName}}</div>
|
|
14
|
+
<nav className="sidebar-nav">
|
|
15
|
+
{menuItems.map((item, i) => (
|
|
16
|
+
<a key={i} href="#" className={`sidebar-item ${item.active ? 'active' : ''}`}>
|
|
17
|
+
<span className="sidebar-icon">{item.icon}</span>
|
|
18
|
+
{item.label}
|
|
19
|
+
</a>
|
|
20
|
+
))}
|
|
21
|
+
</nav>
|
|
22
|
+
</aside>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
.ecommerce { min-height: 100vh; display: flex; flex-direction: column; }
|
|
2
|
+
|
|
3
|
+
/* Header */
|
|
4
|
+
.store-header { background: white; border-bottom: 1px solid #e5e7eb; position: sticky; top: 0; z-index: 10; }
|
|
5
|
+
.header-content { max-width: 1200px; margin: 0 auto; padding: 1rem 2rem; display: flex; align-items: center; gap: 2rem; flex-wrap: wrap; }
|
|
6
|
+
.store-logo { font-size: 1.5rem; color: #4f46e5; margin: 0; }
|
|
7
|
+
.store-nav { display: flex; gap: 1.5rem; }
|
|
8
|
+
.store-nav a { text-decoration: none; color: #6b7280; font-weight: 500; transition: color 0.2s; }
|
|
9
|
+
.store-nav a:hover, .store-nav a.active { color: #4f46e5; }
|
|
10
|
+
.header-actions { display: flex; gap: 1rem; margin-left: auto; align-items: center; }
|
|
11
|
+
.search-input { padding: 0.5rem 1rem; border: 1px solid #d1d5db; border-radius: 8px; font-size: 0.9rem; width: 200px; }
|
|
12
|
+
.cart-btn { background: #4f46e5; color: white; border: none; padding: 0.5rem 1.25rem; border-radius: 8px; cursor: pointer; font-weight: 600; }
|
|
13
|
+
|
|
14
|
+
/* Main */
|
|
15
|
+
.main { flex: 1; max-width: 1200px; margin: 0 auto; padding: 2rem; width: 100%; }
|
|
16
|
+
|
|
17
|
+
/* Product Section */
|
|
18
|
+
.product-section h2 { font-size: 1.75rem; font-weight: 700; margin-bottom: 1.5rem; }
|
|
19
|
+
.product-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 1.5rem; }
|
|
20
|
+
.product-card { border: 1px solid #e5e7eb; border-radius: 12px; overflow: hidden; transition: all 0.2s; background: white; }
|
|
21
|
+
.product-card:hover { box-shadow: 0 8px 25px rgba(0,0,0,0.08); transform: translateY(-2px); }
|
|
22
|
+
.product-image { font-size: 4rem; text-align: center; padding: 2rem; background: #f9fafb; }
|
|
23
|
+
.product-info { padding: 1.25rem; }
|
|
24
|
+
.product-category { font-size: 0.75rem; color: #4f46e5; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; }
|
|
25
|
+
.product-info h3 { font-size: 1.1rem; margin: 0.25rem 0; }
|
|
26
|
+
.product-info p { color: #6b7280; font-size: 0.9rem; margin-bottom: 1rem; }
|
|
27
|
+
.product-footer { display: flex; justify-content: space-between; align-items: center; }
|
|
28
|
+
.product-price { font-size: 1.25rem; font-weight: 700; color: #111827; }
|
|
29
|
+
.add-to-cart { background: #4f46e5; color: white; border: none; padding: 0.5rem 1rem; border-radius: 6px; cursor: pointer; font-weight: 500; font-size: 0.85rem; }
|
|
30
|
+
.add-to-cart:hover { background: #4338ca; }
|
|
31
|
+
|
|
32
|
+
/* Cart */
|
|
33
|
+
.cart-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.4); z-index: 50; }
|
|
34
|
+
.cart-panel { position: fixed; top: 0; right: 0; bottom: 0; width: 400px; max-width: 100%; background: white; box-shadow: -4px 0 25px rgba(0,0,0,0.1); display: flex; flex-direction: column; }
|
|
35
|
+
.cart-header { display: flex; justify-content: space-between; align-items: center; padding: 1.5rem; border-bottom: 1px solid #e5e7eb; }
|
|
36
|
+
.cart-header h2 { margin: 0; font-size: 1.25rem; }
|
|
37
|
+
.cart-close { background: none; border: none; font-size: 1.25rem; cursor: pointer; color: #6b7280; }
|
|
38
|
+
.cart-empty { text-align: center; padding: 3rem; color: #9ca3af; }
|
|
39
|
+
.cart-items { flex: 1; overflow-y: auto; padding: 1rem; }
|
|
40
|
+
.cart-item { display: flex; align-items: center; gap: 1rem; padding: 1rem 0; border-bottom: 1px solid #f3f4f6; }
|
|
41
|
+
.cart-item-image { font-size: 2rem; }
|
|
42
|
+
.cart-item-info h4 { margin: 0; font-size: 0.95rem; }
|
|
43
|
+
.cart-item-info p { color: #6b7280; font-size: 0.85rem; margin: 0; }
|
|
44
|
+
.cart-item-remove { background: none; border: none; color: #ef4444; cursor: pointer; font-size: 0.8rem; margin-left: auto; }
|
|
45
|
+
.cart-footer { padding: 1.5rem; border-top: 1px solid #e5e7eb; }
|
|
46
|
+
.cart-total { display: flex; justify-content: space-between; margin-bottom: 1rem; font-size: 1.1rem; }
|
|
47
|
+
.checkout-btn { width: 100%; background: #4f46e5; color: white; border: none; padding: 0.75rem; border-radius: 8px; font-size: 1rem; font-weight: 600; cursor: pointer; }
|
|
48
|
+
.checkout-btn:hover { background: #4338ca; }
|
|
49
|
+
|
|
50
|
+
/* Footer */
|
|
51
|
+
.store-footer { background: #111827; color: #d1d5db; padding: 3rem 2rem 1.5rem; margin-top: auto; }
|
|
52
|
+
.store-footer .footer-content { display: flex; justify-content: space-between; max-width: 1200px; margin: 0 auto; gap: 3rem; flex-wrap: wrap; }
|
|
53
|
+
.store-footer .footer-brand h3 { color: white; font-size: 1.25rem; margin-bottom: 0.5rem; }
|
|
54
|
+
.store-footer .footer-links { display: flex; gap: 3rem; }
|
|
55
|
+
.store-footer .footer-col { display: flex; flex-direction: column; gap: 0.5rem; }
|
|
56
|
+
.store-footer .footer-col h4 { color: white; font-size: 0.8rem; text-transform: uppercase; margin-bottom: 0.25rem; }
|
|
57
|
+
.store-footer .footer-col a { color: #9ca3af; text-decoration: none; font-size: 0.9rem; }
|
|
58
|
+
.store-footer .footer-col a:hover { color: white; }
|
|
59
|
+
.store-footer .footer-bottom { border-top: 1px solid #374151; margin-top: 2rem; padding-top: 1rem; text-align: center; max-width: 1200px; margin-left: auto; margin-right: auto; }
|
|
60
|
+
.store-footer .footer-bottom p { font-size: 0.8rem; color: #6b7280; }
|
|
61
|
+
|
|
62
|
+
@media (max-width: 768px) {
|
|
63
|
+
.header-content { flex-direction: column; align-items: flex-start; }
|
|
64
|
+
.header-actions { margin-left: 0; width: 100%; }
|
|
65
|
+
.search-input { flex: 1; }
|
|
66
|
+
.cart-panel { width: 100%; }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@media (prefers-color-scheme: dark) {
|
|
70
|
+
.store-header { background: #1f2937; border-color: #374151; }
|
|
71
|
+
.product-card { background: #1f2937; border-color: #374151; }
|
|
72
|
+
.product-image { background: #111827; }
|
|
73
|
+
.product-info h3 { color: white; }
|
|
74
|
+
.product-price { color: white; }
|
|
75
|
+
.product-section h2 { color: white; }
|
|
76
|
+
.search-input { background: #374151; border-color: #4b5563; color: white; }
|
|
77
|
+
.cart-panel { background: #1f2937; }
|
|
78
|
+
.cart-header { border-color: #374151; }
|
|
79
|
+
.cart-item { border-color: #374151; }
|
|
80
|
+
.cart-footer { border-color: #374151; }
|
|
81
|
+
.cart-item-info h4 { color: white; }
|
|
82
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { Header } from './components/Header';
|
|
3
|
+
import { ProductGrid } from './components/ProductGrid';
|
|
4
|
+
import { Cart } from './components/Cart';
|
|
5
|
+
import { Footer } from './components/Footer';
|
|
6
|
+
import './App.css';
|
|
7
|
+
|
|
8
|
+
export interface Product {
|
|
9
|
+
id: number;
|
|
10
|
+
name: string;
|
|
11
|
+
price: number;
|
|
12
|
+
image: string;
|
|
13
|
+
category: string;
|
|
14
|
+
description: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface CartItem extends Product {
|
|
18
|
+
quantity: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const products: Product[] = [
|
|
22
|
+
{ id: 1, name: 'Wireless Headphones', price: 79.99, image: '🎧', category: 'Electronics', description: 'Premium sound quality with noise cancellation.' },
|
|
23
|
+
{ id: 2, name: 'Smart Watch', price: 199.99, image: '⌚', category: 'Electronics', description: 'Track your fitness and stay connected.' },
|
|
24
|
+
{ id: 3, name: 'Laptop Stand', price: 49.99, image: '💻', category: 'Accessories', description: 'Ergonomic aluminum stand for better posture.' },
|
|
25
|
+
{ id: 4, name: 'Mechanical Keyboard', price: 129.99, image: '⌨️', category: 'Electronics', description: 'Tactile switches with RGB backlighting.' },
|
|
26
|
+
{ id: 5, name: 'USB-C Hub', price: 39.99, image: '🔌', category: 'Accessories', description: '7-in-1 hub with 4K HDMI output.' },
|
|
27
|
+
{ id: 6, name: 'Webcam HD', price: 69.99, image: '📷', category: 'Electronics', description: '1080p webcam with built-in microphone.' },
|
|
28
|
+
{ id: 7, name: 'Desk Lamp', price: 34.99, image: '💡', category: 'Home', description: 'LED desk lamp with adjustable brightness.' },
|
|
29
|
+
{ id: 8, name: 'Mouse Pad XL', price: 19.99, image: '🖱️', category: 'Accessories', description: 'Extended mouse pad with stitched edges.' },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
function App() {
|
|
33
|
+
const [cartItems, setCartItems] = useState<CartItem[]>([]);
|
|
34
|
+
const [isCartOpen, setIsCartOpen] = useState(false);
|
|
35
|
+
|
|
36
|
+
const addToCart = (product: Product) => {
|
|
37
|
+
setCartItems((prev) => {
|
|
38
|
+
const existing = prev.find((item) => item.id === product.id);
|
|
39
|
+
if (existing) {
|
|
40
|
+
return prev.map((item) =>
|
|
41
|
+
item.id === product.id ? { ...item, quantity: item.quantity + 1 } : item
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
return [...prev, { ...product, quantity: 1 }];
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const removeFromCart = (id: number) => {
|
|
49
|
+
setCartItems((prev) => prev.filter((item) => item.id !== id));
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const cartCount = cartItems.reduce((sum, item) => sum + item.quantity, 0);
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<div className="ecommerce">
|
|
56
|
+
<Header cartCount={cartCount} onCartClick={() => setIsCartOpen(!isCartOpen)} />
|
|
57
|
+
<main className="main">
|
|
58
|
+
<ProductGrid products={products} onAddToCart={addToCart} />
|
|
59
|
+
</main>
|
|
60
|
+
{isCartOpen && (
|
|
61
|
+
<Cart items={cartItems} onRemove={removeFromCart} onClose={() => setIsCartOpen(false)} />
|
|
62
|
+
)}
|
|
63
|
+
<Footer />
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export default App;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { CartItem } from '../App';
|
|
2
|
+
|
|
3
|
+
interface CartProps {
|
|
4
|
+
items: CartItem[];
|
|
5
|
+
onRemove: (id: number) => void;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function Cart({ items, onRemove, onClose }: CartProps) {
|
|
10
|
+
const total = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<div className="cart-overlay" onClick={onClose}>
|
|
14
|
+
<div className="cart-panel" onClick={(e) => e.stopPropagation()}>
|
|
15
|
+
<div className="cart-header">
|
|
16
|
+
<h2>Shopping Cart</h2>
|
|
17
|
+
<button className="cart-close" onClick={onClose}>X</button>
|
|
18
|
+
</div>
|
|
19
|
+
{items.length === 0 ? (
|
|
20
|
+
<p className="cart-empty">Your cart is empty</p>
|
|
21
|
+
) : (
|
|
22
|
+
<>
|
|
23
|
+
<div className="cart-items">
|
|
24
|
+
{items.map((item) => (
|
|
25
|
+
<div key={item.id} className="cart-item">
|
|
26
|
+
<span className="cart-item-image">{item.image}</span>
|
|
27
|
+
<div className="cart-item-info">
|
|
28
|
+
<h4>{item.name}</h4>
|
|
29
|
+
<p>Qty: {item.quantity} x ${item.price.toFixed(2)}</p>
|
|
30
|
+
</div>
|
|
31
|
+
<button className="cart-item-remove" onClick={() => onRemove(item.id)}>Remove</button>
|
|
32
|
+
</div>
|
|
33
|
+
))}
|
|
34
|
+
</div>
|
|
35
|
+
<div className="cart-footer">
|
|
36
|
+
<div className="cart-total">
|
|
37
|
+
<span>Total:</span>
|
|
38
|
+
<strong>${total.toFixed(2)}</strong>
|
|
39
|
+
</div>
|
|
40
|
+
<button className="checkout-btn">Checkout</button>
|
|
41
|
+
</div>
|
|
42
|
+
</>
|
|
43
|
+
)}
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export function Footer() {
|
|
2
|
+
return (
|
|
3
|
+
<footer className="store-footer">
|
|
4
|
+
<div className="footer-content">
|
|
5
|
+
<div className="footer-brand">
|
|
6
|
+
<h3>{{projectName}}</h3>
|
|
7
|
+
<p>Your one-stop shop for quality products.</p>
|
|
8
|
+
</div>
|
|
9
|
+
<div className="footer-links">
|
|
10
|
+
<div className="footer-col">
|
|
11
|
+
<h4>Shop</h4>
|
|
12
|
+
<a href="#">All Products</a>
|
|
13
|
+
<a href="#">New Arrivals</a>
|
|
14
|
+
<a href="#">Sale</a>
|
|
15
|
+
</div>
|
|
16
|
+
<div className="footer-col">
|
|
17
|
+
<h4>Support</h4>
|
|
18
|
+
<a href="#">FAQ</a>
|
|
19
|
+
<a href="#">Shipping</a>
|
|
20
|
+
<a href="#">Returns</a>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
<div className="footer-bottom">
|
|
25
|
+
<p>© 2025 {{projectName}}. All rights reserved.</p>
|
|
26
|
+
</div>
|
|
27
|
+
</footer>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
interface HeaderProps {
|
|
2
|
+
cartCount: number;
|
|
3
|
+
onCartClick: () => void;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function Header({ cartCount, onCartClick }: HeaderProps) {
|
|
7
|
+
return (
|
|
8
|
+
<header className="store-header">
|
|
9
|
+
<div className="header-content">
|
|
10
|
+
<h1 className="store-logo">{{projectName}}</h1>
|
|
11
|
+
<nav className="store-nav">
|
|
12
|
+
<a href="#" className="active">All Products</a>
|
|
13
|
+
<a href="#">Electronics</a>
|
|
14
|
+
<a href="#">Accessories</a>
|
|
15
|
+
<a href="#">Home</a>
|
|
16
|
+
</nav>
|
|
17
|
+
<div className="header-actions">
|
|
18
|
+
<input type="text" className="search-input" placeholder="Search products..." />
|
|
19
|
+
<button className="cart-btn" onClick={onCartClick}>
|
|
20
|
+
Cart ({cartCount})
|
|
21
|
+
</button>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</header>
|
|
25
|
+
);
|
|
26
|
+
}
|