embedingHF commited on
Commit
a667b81
·
verified ·
1 Parent(s): 5dfa6dc

Upload folder using huggingface_hub

Browse files
.env.example ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ # GEMINI_API_KEY: Required for Gemini AI API calls.
2
+ # AI Studio automatically injects this at runtime from user secrets.
3
+ # Users configure this via the Secrets panel in the AI Studio UI.
4
+ GEMINI_API_KEY="MY_GEMINI_API_KEY"
5
+
6
+ # APP_URL: The URL where this applet is hosted.
7
+ # AI Studio automatically injects this at runtime with the Cloud Run service URL.
8
+ # Used for self-referential links, OAuth callbacks, and API endpoints.
9
+ APP_URL="MY_APP_URL"
.gitattributes CHANGED
@@ -33,3 +33,7 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ src/assets/images/comfort_tire_1781509452553.jpg filter=lfs diff=lfs merge=lfs -text
37
+ src/assets/images/sport_tire_1781509490802.jpg filter=lfs diff=lfs merge=lfs -text
38
+ src/assets/images/suv_tire_1781509472580.jpg filter=lfs diff=lfs merge=lfs -text
39
+ src/assets/images/winter_tire_1781509513584.jpg filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ node_modules/
2
+ build/
3
+ dist/
4
+ coverage/
5
+ .DS_Store
6
+ *.log
7
+ .env*
8
+ !.env.example
README.md ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div align="center">
2
+ <img width="1200" height="475" alt="GHBanner" src="https://ai.google.dev/static/site-assets/images/share-ais-513315318.png" />
3
+ </div>
4
+
5
+ # Run and deploy your AI Studio app
6
+
7
+ This contains everything you need to run your app locally.
8
+
9
+ View your app in AI Studio: https://ai.studio/apps/b18648e1-f7e5-49af-8c1a-a41fa7ba6ce2
10
+
11
+ ## Run Locally
12
+
13
+ **Prerequisites:** Node.js
14
+
15
+
16
+ 1. Install dependencies:
17
+ `npm install`
18
+ 2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
19
+ 3. Run the app:
20
+ `npm run dev`
assets/.aistudio/.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ *
firebase-applet-config.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "projectId": "gen-lang-client-0073278154",
3
+ "appId": "1:439857597926:web:83652c75dc57d7059e9a27",
4
+ "apiKey": "AIzaSyBpSkNWbpSUtiOwhbo30Xhx_K8OU9ClT2U",
5
+ "authDomain": "gen-lang-client-0073278154.firebaseapp.com",
6
+ "firestoreDatabaseId": "ai-studio-b18648e1-f7e5-49af-8c1a-a41fa7ba6ce2",
7
+ "storageBucket": "gen-lang-client-0073278154.firebasestorage.app",
8
+ "messagingSenderId": "439857597926",
9
+ "measurementId": ""
10
+ }
firebase-blueprint.json ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "entities": {
3
+ "Product": {
4
+ "title": "Product",
5
+ "description": "Premium tire configuration parameters and live branch inventory metrics",
6
+ "type": "object",
7
+ "properties": {
8
+ "id": {
9
+ "type": "string",
10
+ "description": "Auto-generated document identifier"
11
+ },
12
+ "name": {
13
+ "type": "string",
14
+ "description": "Full tire model name"
15
+ },
16
+ "brand": {
17
+ "type": "string",
18
+ "description": "Tyre manufacturer / brand name"
19
+ },
20
+ "size": {
21
+ "type": "string",
22
+ "description": "Tyre size specification code"
23
+ },
24
+ "price": {
25
+ "type": "number",
26
+ "description": "Dealer wholesale price in PKR"
27
+ },
28
+ "category": {
29
+ "type": "string",
30
+ "enum": ["hot-selling", "new-brands", "famous"],
31
+ "description": "Segment classification category"
32
+ },
33
+ "stock": {
34
+ "type": "number",
35
+ "description": "Live stock quantity level currently available at Faisal Town Lahore showroom"
36
+ },
37
+ "feature": {
38
+ "type": "string",
39
+ "description": "Primary highlight tire characteristic"
40
+ },
41
+ "imageUrl": {
42
+ "type": "string",
43
+ "description": "Absolute URL path to tire product showcase image file"
44
+ },
45
+ "createdAt": {
46
+ "type": "string",
47
+ "format": "date-time",
48
+ "description": "Timestamp indicating document creation time"
49
+ }
50
+ },
51
+ "required": ["name", "brand", "size", "price", "category", "stock"]
52
+ },
53
+ "VisitorLog": {
54
+ "title": "VisitorLog",
55
+ "description": "Log of user visitor analytics for tracking store traffic and engagement",
56
+ "type": "object",
57
+ "properties": {
58
+ "id": {
59
+ "type": "string",
60
+ "description": "Auto-generated document identifier"
61
+ },
62
+ "sessionId": {
63
+ "type": "string",
64
+ "description": "Unique session identifier for the visitor"
65
+ },
66
+ "userAgent": {
67
+ "type": "string",
68
+ "description": "Visitor browser metadata"
69
+ },
70
+ "platform": {
71
+ "type": "string",
72
+ "description": "OS platform of the visitor"
73
+ },
74
+ "language": {
75
+ "type": "string",
76
+ "description": "Browser default language"
77
+ },
78
+ "pagePath": {
79
+ "type": "string",
80
+ "description": "Specific route or section visited"
81
+ },
82
+ "timestamp": {
83
+ "type": "string",
84
+ "format": "date-time",
85
+ "description": "Creation timestamp"
86
+ }
87
+ },
88
+ "required": ["sessionId", "timestamp"]
89
+ },
90
+ "NewsletterSubscriber": {
91
+ "title": "NewsletterSubscriber",
92
+ "description": "Newsletter subscriber email record for sending seasonal maintenance tips",
93
+ "type": "object",
94
+ "properties": {
95
+ "id": {
96
+ "type": "string",
97
+ "description": "Auto-generated document identifier"
98
+ },
99
+ "email": {
100
+ "type": "string",
101
+ "description": "Email address of the newsletter subscriber"
102
+ },
103
+ "createdAt": {
104
+ "type": "string",
105
+ "format": "date-time",
106
+ "description": "Subscription timestamp"
107
+ }
108
+ },
109
+ "required": ["email", "createdAt"]
110
+ }
111
+ },
112
+ "firestore": {
113
+ "products/{productId}": {
114
+ "schema": "Product",
115
+ "description": "Unified main retail/wholesale tire product collection"
116
+ },
117
+ "visitors/{visitorId}": {
118
+ "schema": "VisitorLog",
119
+ "description": "Collection storing website visitation events"
120
+ },
121
+ "subscribers/{subscriberId}": {
122
+ "schema": "NewsletterSubscriber",
123
+ "description": "Collection of customer newsletter recipient subscriber emails"
124
+ }
125
+ }
126
+ }
firestore.rules ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ rules_version = '2';
2
+ service cloud.firestore {
3
+ match /databases/{database}/documents {
4
+
5
+ // Global Safety net default-deny catch-all
6
+ match /{document=**} {
7
+ allow read, write: if false;
8
+ }
9
+
10
+ // Hardened helper functions for static verification (denial of wallet guard)
11
+ function isValidId(id) {
12
+ return id is string && id.size() <= 128 && id.matches('^[a-zA-Z0-9_-]+$');
13
+ }
14
+
15
+ function incoming() {
16
+ return request.resource.data;
17
+ }
18
+
19
+ function existing() {
20
+ return resource.data;
21
+ }
22
+
23
+ // High fidelity schema assertion helper for Tire Products
24
+ function isValidTireProduct(data) {
25
+ return data.keys().hasAll(['id', 'name', 'brand', 'size', 'feature', 'price', 'badge', 'segment', 'image', 'description'])
26
+ && data.keys().size() == 10
27
+ && data.id is string && data.id.size() <= 128
28
+ && data.name is string && data.name.size() <= 128
29
+ && data.brand is string && data.brand.size() <= 128
30
+ && data.size is string && data.size.size() <= 64
31
+ && data.feature is string && data.feature.size() <= 256
32
+ && (data.price is int || data.price is float) && data.price >= 0
33
+ && data.badge is string && data.badge.size() <= 128
34
+ && data.segment is string && (data.segment == 'hot' || data.segment == 'new' || data.segment == 'famous')
35
+ && data.image is string && data.image.size() <= 1024
36
+ && data.description is string && data.description.size() <= 4096;
37
+ }
38
+
39
+ // Modern High fidelity schema helper for the requested "products" collection
40
+ function isValidProduct(data) {
41
+ return data.name is string && data.name.size() <= 128
42
+ && data.brand is string && data.brand.size() <= 128
43
+ && data.size is string && data.size.size() <= 64
44
+ && (data.price is int || data.price is float) && data.price >= 0
45
+ && data.category is string && (data.category == 'hot-selling' || data.category == 'new-brands' || data.category == 'famous')
46
+ && (data.stock is int || data.stock is float) && data.stock >= 0
47
+ && data.feature is string && data.feature.size() <= 256
48
+ && data.imageUrl is string && data.imageUrl.size() <= 1024;
49
+ }
50
+
51
+ // High fidelity schema assertion helper for User Testimonials
52
+ function isValidUserReview(data) {
53
+ return data.keys().hasAll(['name', 'rating', 'text', 'date', 'initials'])
54
+ && data.keys().size() == 5
55
+ && data.name is string && data.name.size() <= 128
56
+ && data.rating is int && data.rating >= 1 && data.rating <= 5
57
+ && data.text is string && data.text.size() <= 4096
58
+ && data.date is string && data.date.size() <= 128
59
+ && data.initials is string && data.initials.size() <= 16;
60
+ }
61
+
62
+ // High fidelity schema assertion helper for Visitor Analytics
63
+ function isValidVisitorLog(data) {
64
+ return data.keys().hasAll(['sessionId', 'userAgent', 'platform', 'language', 'pagePath', 'timestamp'])
65
+ && data.keys().size() == 6
66
+ && data.sessionId is string && data.sessionId.size() <= 256
67
+ && data.userAgent is string && data.userAgent.size() <= 1024
68
+ && data.platform is string && data.platform.size() <= 128
69
+ && data.language is string && data.language.size() <= 64
70
+ && data.pagePath is string && data.pagePath.size() <= 256
71
+ && data.timestamp is timestamp && data.timestamp == request.time;
72
+ }
73
+
74
+ // High fidelity schema assertion helper for Newsletter Subscribers
75
+ function isValidSubscriber(data) {
76
+ return data.keys().hasAll(['email', 'createdAt'])
77
+ && data.keys().size() == 2
78
+ && data.email is string && data.email.size() <= 256 && data.email.matches('^[^@]+@[^@]+\\.[^@]+$')
79
+ && data.createdAt is timestamp && data.createdAt == request.time;
80
+ }
81
+
82
+ // Hardened constraints for the /products/ collection (Public read, authenticated write or seed prefix)
83
+ match /products/{productId} {
84
+ allow read: if true;
85
+ allow create: if (request.auth != null || productId.startsWith('seed-')) && isValidId(productId) && isValidProduct(incoming());
86
+ allow update: if request.auth != null && isValidId(productId) && isValidProduct(incoming());
87
+ allow delete: if request.auth != null && isValidId(productId);
88
+ }
89
+
90
+ // Allow public read on the system connectivity check path
91
+ match /test/connection {
92
+ allow read: if true;
93
+ }
94
+
95
+ // Hardened constraints for the /tyres/ collection
96
+ match /tyres/{tyreId} {
97
+ allow read: if true;
98
+ allow create, update: if isValidId(tyreId) && isValidTireProduct(incoming());
99
+ allow delete: if isValidId(tyreId);
100
+ }
101
+
102
+ // Hardened constraints for the /reviews/ collection
103
+ match /reviews/{reviewId} {
104
+ allow read: if true;
105
+ allow create: if isValidId(reviewId) && isValidUserReview(incoming());
106
+ allow update, delete: if false; // Reviews are write-once append-only
107
+ }
108
+
109
+ // Hardened constraints for the /visitors/ collection
110
+ match /visitors/{visitorId} {
111
+ allow read: if request.auth != null; // Admin-only read for visitor stats
112
+ allow create: if isValidId(visitorId) && isValidVisitorLog(incoming());
113
+ allow update, delete: if false; // Visitor logs are immutable
114
+ }
115
+
116
+ // Hardened constraints for the /subscribers/ collection (Public sign up, Admin-only read)
117
+ match /subscribers/{subscriberId} {
118
+ allow read: if request.auth != null; // Admin can view mailing list
119
+ allow create: if isValidId(subscriberId) && isValidSubscriber(incoming());
120
+ allow update, delete: if false; // Newsletter entries are append-only/immutable
121
+ }
122
+ }
123
+ }
index.html ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <meta name="description" content="Haider Brothers Traders in Faisal Town, Lahore. Top-rated 4.8★ tire shop offering 24/7 tire emergency service, wheel alignment, repairs, and high-quality tires from Bridgestone, Michelin, Yokohama, Autogrip." />
7
+ <title>Haider Brothers Traders | Premium Tires & 24/7 Service, Faisal Town Lahore</title>
8
+
9
+ <!-- Open Graph (Facebook / LinkedIn) Meta Tags -->
10
+ <meta property="og:type" content="website" />
11
+ <meta property="og:title" content="Haider Brothers Traders | Premium Tires & 24/7 Service, Lahore" />
12
+ <meta property="og:description" content=" Lahore's top-rated 4.8★ tyre shop offering certified brand-new tyres, 3D wheel alignment, and 24/7 emergency roadside flat tire assistance in Faisal Town." />
13
+ <meta property="og:image" content="https://images.unsplash.com/photo-1542282088-fe8426682b8f?auto=format&fit=crop&q=80&w=1200&h=630" />
14
+ <meta property="og:url" content="https://haidertrading.com" />
15
+ <meta property="og:site_name" content="Haider Brothers Traders" />
16
+ <meta property="og:locale" content="en_US" />
17
+
18
+ <!-- Twitter Card Meta Tags -->
19
+ <meta name="twitter:card" content="summary_large_image" />
20
+ <meta name="twitter:title" content="Haider Brothers Traders | Premium Tires & 24/7 Service, Lahore" />
21
+ <meta name="twitter:description" content="Certified brand-new tires, premium 3D wheel alignment, computerized balancing, and 24/7 roadside emergency tire repair in Faisal Town, Lahore." />
22
+ <meta name="twitter:image" content="https://images.unsplash.com/photo-1542282088-fe8426682b8f?auto=format&fit=crop&q=80&w=1200&h=630" />
23
+
24
+ <!-- JSON-LD LocalBusiness Schema -->
25
+ <script type="application/ld+json">
26
+ {
27
+ "@context": "https://schema.org",
28
+ "@type": "AutoPartsStore",
29
+ "name": "Haider Brothers Traders",
30
+ "image": "https://images.unsplash.com/photo-1542282088-fe8426682b8f?auto=format&fit=crop&q=80&w=800",
31
+ "description": "Premium 24/7 tire service, sales, alignment, and repair in Faisal Town, Block C 1, Lahore, Pakistan.",
32
+ "telephone": "+923024594403",
33
+ "priceRange": "PKR",
34
+ "address": {
35
+ "@type": "PostalAddress",
36
+ "streetAddress": "36-C-1, Maulana Shaukat Ali Road, Block C-1 Faisal Town",
37
+ "addressLocality": "Lahore",
38
+ "addressRegion": "Punjab",
39
+ "postalCode": "54700",
40
+ "addressCountry": "PK"
41
+ },
42
+ "geo": {
43
+ "@type": "GeoCoordinates",
44
+ "latitude": 31.4812,
45
+ "longitude": 74.3031
46
+ },
47
+ "openingHoursSpecification": {
48
+ "@type": "OpeningHoursSpecification",
49
+ "dayOfWeek": [
50
+ "Monday",
51
+ "Tuesday",
52
+ "Wednesday",
53
+ "Thursday",
54
+ "Friday",
55
+ "Saturday",
56
+ "Sunday"
57
+ ],
58
+ "opens": "00:00",
59
+ "closes": "23:59"
60
+ },
61
+ "aggregateRating": {
62
+ "@type": "AggregateRating",
63
+ "ratingValue": "4.8",
64
+ "reviewCount": "5"
65
+ },
66
+ "review": [
67
+ {
68
+ "@type": "Review",
69
+ "author": {
70
+ "@type": "Person",
71
+ "name": "gitsitmarketing"
72
+ },
73
+ "reviewRating": {
74
+ "@type": "Rating",
75
+ "ratingValue": "5"
76
+ },
77
+ "reviewBody": "Excellent service and quality of tires. My experience was good."
78
+ },
79
+ {
80
+ "@type": "Review",
81
+ "author": {
82
+ "@type": "Person",
83
+ "name": "M Bilal"
84
+ },
85
+ "reviewRating": {
86
+ "@type": "Rating",
87
+ "ratingValue": "5"
88
+ },
89
+ "reviewBody": "My experience was great with haider brother traders. They provided me good service and high quality product."
90
+ },
91
+ {
92
+ "@type": "Review",
93
+ "author": {
94
+ "@type": "Person",
95
+ "name": "Muhammad Ramzan"
96
+ },
97
+ "reviewRating": {
98
+ "@type": "Rating",
99
+ "ratingValue": "5"
100
+ },
101
+ "reviewBody": "Great quality and service."
102
+ }
103
+ ]
104
+ }
105
+ </script>
106
+ </head>
107
+ <body>
108
+ <div id="root"></div>
109
+ <script type="module" src="/src/main.tsx"></script>
110
+ </body>
111
+ </html>
112
+
metadata.json ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Haider Brothers Traders",
3
+ "description": "Premium 24/7 tire service, sales, alignment, and repair in Faisal Town, Block C 1, Lahore, Pakistan.",
4
+ "requestFramePermissions": [],
5
+ "majorCapabilities": ["MAJOR_CAPABILITY_SERVER_SIDE_GEMINI_API"]
6
+ }
package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
package.json ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "react-example",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite --port=3000 --host=0.0.0.0",
8
+ "build": "vite build",
9
+ "preview": "vite preview",
10
+ "clean": "rm -rf dist server.js",
11
+ "lint": "tsc --noEmit"
12
+ },
13
+ "dependencies": {
14
+ "@google/genai": "^2.4.0",
15
+ "@tailwindcss/vite": "^4.1.14",
16
+ "@vitejs/plugin-react": "^5.0.4",
17
+ "dotenv": "^17.2.3",
18
+ "express": "^4.21.2",
19
+ "firebase": "^12.14.0",
20
+ "lucide-react": "^0.546.0",
21
+ "motion": "^12.23.24",
22
+ "react": "^19.0.1",
23
+ "react-dom": "^19.0.1",
24
+ "vite": "^6.2.3"
25
+ },
26
+ "devDependencies": {
27
+ "@types/express": "^4.17.21",
28
+ "@types/node": "^22.14.0",
29
+ "autoprefixer": "^10.4.21",
30
+ "esbuild": "^0.25.0",
31
+ "tailwindcss": "^4.1.14",
32
+ "tsx": "^4.21.0",
33
+ "typescript": "~5.8.2",
34
+ "vite": "^6.2.3"
35
+ }
36
+ }
src/App.tsx ADDED
@@ -0,0 +1,798 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState, useEffect } from "react";
2
+ import { motion, AnimatePresence } from "motion/react";
3
+ import {
4
+ Sparkles,
5
+ MapPin,
6
+ Phone,
7
+ Clock,
8
+ Star,
9
+ Layers,
10
+ Compass,
11
+ MessageSquare,
12
+ Heart,
13
+ CheckCircle2,
14
+ ChevronRight,
15
+ TrendingUp,
16
+ Award,
17
+ Search,
18
+ ChevronDown,
19
+ ChevronUp,
20
+ Share2,
21
+ Lock,
22
+ Wrench,
23
+ HelpCircle,
24
+ FolderLock
25
+ } from "lucide-react";
26
+
27
+ import { BUSINESS_INFO, BRANDS, REVIEWS, TireProduct } from "./data";
28
+ import {
29
+ getTireProducts,
30
+ getReviews,
31
+ addReview,
32
+ SEED_TYRE_PRODUCTS
33
+ } from "./lib/tireService";
34
+ import { checkAuthState, logVisitorInfo, onProductsSnapshot } from "./lib/firebaseService";
35
+
36
+ import Navbar from "./components/Navbar";
37
+ import Logo from "./components/Logo";
38
+ import Hero from "./components/Hero";
39
+ import TireCard from "./components/TireCard";
40
+ import ProductDetailsModal from "./components/ProductDetailsModal";
41
+ import ReviewsSection from "./components/ReviewsSection";
42
+ import LocationContact from "./components/LocationContact";
43
+ import FloatingActions from "./components/FloatingActions";
44
+ import VehicleFitmentWidget from "./components/VehicleFitmentWidget";
45
+ import InventoryManager from "./components/InventoryManager";
46
+ import AdminLoginModal from "./components/AdminLoginModal";
47
+ import Promotions from "./components/Promotions";
48
+ import NewsletterSignup from "./components/NewsletterSignup";
49
+ import FAQSection from "./components/FAQSection";
50
+
51
+ export default function App() {
52
+ const [darkMode, setDarkMode] = useState<boolean>(true);
53
+ const [currentPage, setCurrentPage] = useState<string>("home");
54
+ const [currentUser, setCurrentUser] = useState<any | null>(null);
55
+ const [showAdminLoginModal, setShowAdminLoginModal] = useState<boolean>(false);
56
+
57
+ const isAuthorizedAdmin = currentUser?.email === "bahaduralimunnabhai@gmail.com";
58
+
59
+ // Redirect to home if unauthenticated user somehow sits on admin route
60
+ useEffect(() => {
61
+ if (currentPage === "admin" && !isAuthorizedAdmin) {
62
+ setCurrentPage("home");
63
+ }
64
+ }, [currentPage, isAuthorizedAdmin]);
65
+
66
+ // Track page navigation analytics safely
67
+ useEffect(() => {
68
+ logVisitorInfo(currentPage);
69
+ }, [currentPage]);
70
+
71
+ // Auth listener
72
+ useEffect(() => {
73
+ const unsubscribe = checkAuthState((user) => {
74
+ setCurrentUser(user);
75
+ });
76
+ return () => unsubscribe();
77
+ }, []);
78
+
79
+ // Master API database states loaded dynamically from Firestore
80
+ const [products, setProducts] = useState<TireProduct[]>([]);
81
+ const [reviews, setReviews] = useState<any[]>([]);
82
+ const [loading, setLoading] = useState<boolean>(true);
83
+
84
+ // Dynamic state selectors
85
+ const [activeSegment, setActiveSegment] = useState<'all' | 'hot' | 'new' | 'famous'>('all');
86
+ const [selectedBrand, setSelectedBrand] = useState<string>('All');
87
+ const [searchQuery, setSearchQuery] = useState<string>('');
88
+ const [priceSort, setPriceSort] = useState<'none' | 'low-high' | 'high-low'>('none');
89
+ const [selectedTire, setSelectedTire] = useState<TireProduct | null>(null);
90
+
91
+ // Load products dynamically in real-time and reviews on startup
92
+ useEffect(() => {
93
+ setLoading(true);
94
+
95
+ // Subscribe to live products collection updates
96
+ const unsubscribeProducts = onProductsSnapshot((snapshotList) => {
97
+ const mapped = snapshotList.map(p => ({
98
+ id: p.id,
99
+ name: p.name,
100
+ brand: p.brand,
101
+ size: p.size,
102
+ feature: p.feature,
103
+ price: p.price,
104
+ badge: p.badge || (p.category === "hot-selling" ? "Hot Selling" : p.category === "new-brands" ? "New Arrival" : "Famous"),
105
+ segment: p.segment || (p.category === "hot-selling" ? "hot" : p.category === "new-brands" ? "new" : "famous") as "hot" | "new" | "famous",
106
+ image: p.image || p.imageUrl,
107
+ stock: p.stock || 0,
108
+ description: p.description || `Premium quality tire from ${p.brand}.`
109
+ }));
110
+ setProducts(mapped);
111
+ setLoading(false);
112
+ });
113
+
114
+ loadReviews();
115
+
116
+ return () => {
117
+ unsubscribeProducts();
118
+ };
119
+ }, []);
120
+
121
+ const loadReviews = async () => {
122
+ try {
123
+ const dbReviews = await getReviews();
124
+ if (dbReviews && dbReviews.length > 0) {
125
+ setReviews(dbReviews);
126
+ } else {
127
+ setReviews(REVIEWS);
128
+ }
129
+ } catch (e) {
130
+ console.error("Firestore reviews fallback.", e);
131
+ setReviews(REVIEWS);
132
+ }
133
+ };
134
+
135
+ const handleAddNewReview = async (newReviewObj: any) => {
136
+ try {
137
+ await addReview(newReviewObj);
138
+ // Reload from cloud immediately
139
+ const dbReviews = await getReviews();
140
+ setReviews(dbReviews);
141
+ } catch (e) {
142
+ // Offline fallback append
143
+ setReviews((prev) => [newReviewObj, ...prev]);
144
+ }
145
+ };
146
+
147
+ // Helper action: when selecting recommended size from widget, immediately filter catalogue
148
+ const handleFilterSizeFromWidget = (sizeCode: string) => {
149
+ setSearchQuery(sizeCode);
150
+ setSelectedBrand("All");
151
+ setActiveSegment("all");
152
+ setPriceSort("none");
153
+ setCurrentPage("catalogue");
154
+
155
+ // Smooth scroll to top of database list
156
+ setTimeout(() => {
157
+ const el = document.getElementById("catalog-grid-top");
158
+ if (el) el.scrollIntoView({ behavior: "smooth", block: "start" });
159
+ }, 100);
160
+ };
161
+
162
+ // Derivative Filter for landing sections
163
+ const hotSellingProducts = products.filter(p => p.segment === 'hot');
164
+ const newArrivalProducts = products.filter(p => p.segment === 'new');
165
+
166
+ // Master Filter and Sorting engine for dynamic Catalogue list
167
+ const filteredCatalogProducts = products.filter((tire) => {
168
+ const matchesSegment = activeSegment === 'all' || tire.segment === activeSegment;
169
+ const matchesBrand = selectedBrand === 'All' || tire.brand.toLowerCase() === selectedBrand.toLowerCase();
170
+
171
+ const searchString = `${tire.name} ${tire.brand} ${tire.size} ${tire.feature} ${tire.description}`.toLowerCase();
172
+ const matchesSearch = searchString.includes(searchQuery.toLowerCase());
173
+
174
+ return matchesSegment && matchesBrand && matchesSearch;
175
+ });
176
+
177
+ // Apply Price Sorting configurations
178
+ const sortedCatalogProducts = [...filteredCatalogProducts].sort((a, b) => {
179
+ if (priceSort === "low-high") return a.price - b.price;
180
+ if (priceSort === "high-low") return b.price - a.price;
181
+ return 0; // retain database natural feed sorting
182
+ });
183
+
184
+ return (
185
+ <div className={`min-h-screen font-sans antialiased transition-colors duration-300 ${
186
+ darkMode ? "bg-brand-dark text-white" : "bg-slate-50 text-slate-900"
187
+ }`}>
188
+
189
+ {/* 24/7 Top Alert Ribbon with High Visibility SEO keyword highlights */}
190
+ <div className="bg-gradient-to-r from-brand-orange to-red-600 text-white text-center py-2 px-4 text-xs font-mono font-bold uppercase tracking-widest flex items-center justify-center space-x-2 relative z-50">
191
+ <span className="w-2.5 h-2.5 rounded-full bg-white animate-ping"></span>
192
+ <span className="hidden sm:inline">24/7 EMERGENCY TYRE PUNCTURE BREAKDOWN REPAIR ROAD ASSISTANCE IN LAHORE: </span>
193
+ <span className="sm:hidden">24/7 LAHORE EMERGENCY ASSISTANCE: </span>
194
+ <a href={`tel:${BUSINESS_INFO.phoneRaw}`} className="underline hover:opacity-85 ml-1 font-sans font-extrabold text-xs tracking-normal">
195
+ 📞 {BUSINESS_INFO.phone}
196
+ </a>
197
+ </div>
198
+
199
+ {/* State-driven Navigation Bar */}
200
+ <Navbar
201
+ darkMode={darkMode}
202
+ setDarkMode={setDarkMode}
203
+ currentPage={currentPage}
204
+ setCurrentPage={setCurrentPage}
205
+ isAuthorizedAdmin={isAuthorizedAdmin}
206
+ />
207
+
208
+ {/* Main Container Wrapper with Animation Transitions */}
209
+ <main className="transition-opacity duration-300">
210
+
211
+ {/* VIEW 1: HOME PAGE PAGE-TAB */}
212
+ {currentPage === "home" && (
213
+ <motion.div
214
+ initial={{ opacity: 0 }}
215
+ animate={{ opacity: 1 }}
216
+ exit={{ opacity: 0 }}
217
+ transition={{ duration: 0.4 }}
218
+ >
219
+ {/* Elegant Display Hero Header */}
220
+ <Hero darkMode={darkMode} />
221
+
222
+ {/* HIGH SEO TEXT SECTION & KEYWORDS */}
223
+ <section className={`py-6 border-y ${darkMode ? "bg-black/10 border-white/5" : "bg-slate-100 border-slate-200"}`}>
224
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center text-xs tracking-wide font-mono opacity-85">
225
+ <span>⚡ LAHORE INDUSTRIAL TRADING CORP • RETAIL & WHOLESALE RATES ON BRAND NEW TYRES IN FAISAL TOWN • CERTIFIED GERMAN TUNE-UP & ALIGNMENT STAGES</span>
226
+ </div>
227
+ </section>
228
+
229
+ {/* SEASONAL BUNDLE PROMOTIONS CAROUSEL SLIDER */}
230
+ <Promotions darkMode={darkMode} />
231
+
232
+ {/* DYNAMIC CAR MODEL VEHICLE INTEGRATION WIDGET */}
233
+ <section className="py-16 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
234
+ <div className="text-center max-w-2xl mx-auto mb-10">
235
+ <span className="text-xs font-mono tracking-widest uppercase bg-brand-orange/15 text-brand-orange px-3.5 py-1 rounded-full font-extrabold">
236
+ Car Fitment Search
237
+ </span>
238
+ <h2 className="text-3xl font-display font-extrabold tracking-tight mt-3">
239
+ Vehicle Recommended Tyre Size Finder
240
+ </h2>
241
+ <p className={`text-sm mt-2 ${darkMode ? "text-slate-400" : "text-slate-600"}`}>
242
+ Input or lookup your car name to pull custom rim specifications, load recommended profile compounds, and lookup live stock matching instantly.
243
+ </p>
244
+ </div>
245
+
246
+ {/* Mounted child fitment widget component */}
247
+ <VehicleFitmentWidget
248
+ darkMode={darkMode}
249
+ onFilterSize={handleFilterSizeFromWidget}
250
+ />
251
+ </section>
252
+
253
+ {/* FEATURED: HOT SELLING TYRES LISTING */}
254
+ <section id="hot" className={`py-12 ${darkMode ? "bg-brand-dark/95" : "bg-white"}`}>
255
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
256
+ <div className="flex flex-col sm:flex-row items-baseline justify-between mb-8 border-b pb-4 border-black/5 dark:border-white/5">
257
+ <div>
258
+ <span className="text-brand-orange font-mono text-xs uppercase font-extrabold tracking-widest">
259
+ 🔥 Top Seller Index
260
+ </span>
261
+ <h3 className="text-2xl sm:text-3xl font-display font-black tracking-tight mt-1">
262
+ Lahore's Demanded Tyres
263
+ </h3>
264
+ </div>
265
+ <button
266
+ onClick={() => { setCurrentPage("catalogue"); setActiveSegment("hot"); }}
267
+ className="text-xs font-bold text-brand-orange hover:underline focus:outline-none mt-2 sm:mt-0"
268
+ >
269
+ View All Popular Tyres →
270
+ </button>
271
+ </div>
272
+
273
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
274
+ {hotSellingProducts.slice(0, 4).map((tire) => (
275
+ <TireCard
276
+ key={tire.id}
277
+ tire={tire}
278
+ darkMode={darkMode}
279
+ onSelect={(t) => setSelectedTire(t)}
280
+ />
281
+ ))}
282
+ </div>
283
+ </div>
284
+ </section>
285
+
286
+ {/* BRAND DIFFERENTIATORS BENTO GRAPH DISPLAY SECTION */}
287
+ <section className={`py-20 ${darkMode ? "bg-black/30" : "bg-slate-100"}`}>
288
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
289
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-10 items-center">
290
+ <div>
291
+ <span className="text-brand-orange font-mono text-xs uppercase font-extrabold">
292
+ 🛡️ Premium Security and Quality Safety
293
+ </span>
294
+ <h3 className="text-3xl font-display font-black tracking-tight mt-2 leading-tight">
295
+ Why Faisal Town Chooses Haider Brothers Traders?
296
+ </h3>
297
+ <p className={`text-sm mt-3 leading-relaxed ${darkMode ? "text-slate-400" : "text-slate-600"}`}>
298
+ Our mission is to guarantee maximum safety rating, high speed response, comfortable suspensions, and durable tyre lifespan for everyday Pakistani passenger sedans and heavy commercial vehicles. We operate 24 hours a day to prevent vehicle emergencies and offer full high-grade premium warranty validation.
299
+ </p>
300
+
301
+ <div className="space-y-4 mt-6">
302
+ <div className="flex items-start space-x-3">
303
+ <span className="p-1 px-1.8 rounded-lg bg-brand-orange/10 text-brand-orange font-bold text-xs mt-0.5">✔</span>
304
+ <div>
305
+ <h4 className="font-bold text-sm">German 3D Wheel Alignment stages</h4>
306
+ <p className={`text-xs ${darkMode ? "text-slate-400" : "text-slate-500"}`}>We restore precise steering control & eliminate early tyre scrubbing.</p>
307
+ </div>
308
+ </div>
309
+
310
+ <div className="flex items-start space-x-3">
311
+ <span className="p-1 px-1.8 rounded-lg bg-brand-orange/10 text-brand-orange font-bold text-xs mt-0.5">✔</span>
312
+ <div>
313
+ <h4 className="font-bold text-sm">100% Genuine Certified Imports</h4>
314
+ <p className={`text-xs ${darkMode ? "text-slate-400" : "text-slate-500"}`}>Say no to smuggling issues. Enjoy legal security with real DOT manufacturing years.</p>
315
+ </div>
316
+ </div>
317
+ </div>
318
+ </div>
319
+
320
+ <div className={`p-6 sm:p-8 rounded-3xl border ${
321
+ darkMode ? "bg-[#1c1c24] border-white/5 shadow-2xl" : "bg-white border-slate-200/80 shadow-md"
322
+ }`}>
323
+ <h4 className="font-display font-black text-l uppercase text-brand-orange text-center mb-6">
324
+ ⚙️ Core Service Statistics
325
+ </h4>
326
+
327
+ <div className="space-y-4">
328
+ <div>
329
+ <div className="flex justify-between text-xs font-mono font-bold mb-1">
330
+ <span>Original Custom Warranty Claim Ratio</span>
331
+ <span className="text-brand-orange">100% Guaranteed</span>
332
+ </div>
333
+ <div className="w-full h-2 rounded-full bg-slate-200 dark:bg-black/30 overflow-hidden">
334
+ <div className="h-full bg-brand-orange w-full"></div>
335
+ </div>
336
+ </div>
337
+
338
+ <div>
339
+ <div className="flex justify-between text-xs font-mono font-bold mb-1">
340
+ <span>Response Speed under Emergency (Minutes)</span>
341
+ <span className="text-brand-orange">Within 25 Mins</span>
342
+ </div>
343
+ <div className="w-full h-2 rounded-full bg-slate-200 dark:bg-black/30 overflow-hidden">
344
+ <div className="h-full bg-brand-orange" style={{ width: "95%" }}></div>
345
+ </div>
346
+ </div>
347
+
348
+ <div>
349
+ <div className="flex justify-between text-xs font-mono font-bold mb-1">
350
+ <span>Certified Import Tyre Compound Durability</span>
351
+ <span className="text-brand-orange">50,000+ Kilometers</span>
352
+ </div>
353
+ <div className="w-full h-2 rounded-full bg-slate-200 dark:bg-black/30 overflow-hidden">
354
+ <div className="h-full bg-brand-orange" style={{ width: "90%" }}></div>
355
+ </div>
356
+ </div>
357
+ </div>
358
+
359
+ <div className="p-4 bg-brand-orange/5 border border-brand-orange/10 rounded-xl text-center mt-6">
360
+ <span className="text-xs font-semibold text-brand-orange font-mono">
361
+ 🔥 CERTIFIED REPUTATION FOR QUALITY FITTING
362
+ </span>
363
+ </div>
364
+ </div>
365
+ </div>
366
+ </div>
367
+ </section>
368
+
369
+ {/* SECTIONS: LAHORE STREET LOCATION CONTACT PREVIEW */}
370
+ <LocationContact darkMode={darkMode} />
371
+ </motion.div>
372
+ )}
373
+
374
+ {/* VIEW 2: TYRE CATALOGUE PAGE-TAB */}
375
+ {currentPage === "catalogue" && (
376
+ <motion.div
377
+ initial={{ opacity: 0, y: 15 }}
378
+ animate={{ opacity: 1, y: 0 }}
379
+ exit={{ opacity: 0 }}
380
+ className="pt-24 pb-20 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"
381
+ >
382
+ {/* Header intro */}
383
+ <div className="text-center max-w-2xl mx-auto mb-12" id="catalog-grid-top">
384
+ <span className="text-xs font-mono tracking-widest uppercase bg-brand-orange/10 text-brand-orange px-3.5 py-1.5 rounded-full font-bold inline-block">
385
+ Distributer Price Index
386
+ </span>
387
+ <h1 className="text-3xl sm:text-5xl font-display font-black tracking-tight mt-4">
388
+ Full Tyre Inventory Catalogue
389
+ </h1>
390
+ <p className={`text-sm mt-3 ${darkMode ? "text-slate-400" : "text-slate-600"}`}>
391
+ Live stock records connected to Faisal Town Lahore branch. Filter by sizes, manufacturers, segment highlights, or order by price index instantly.
392
+ </p>
393
+ </div>
394
+
395
+ {/* Advanced Filters Block */}
396
+ <div className={`p-6 rounded-3xl border mb-10 space-y-6 ${
397
+ darkMode ? "glassmorphism box-glow" : "glassmorphism-light shadow-md"
398
+ }`}>
399
+ {/* Row 1: Search and Sorting controls */}
400
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
401
+ {/* Search Bar input */}
402
+ <div className="md:col-span-2 relative">
403
+ <span className={`text-xs font-semibold font-mono block mb-1 px-1 ${darkMode ? "text-slate-300" : "text-slate-600"}`}>
404
+ Filter by Keywords (e.g. Michelin, 185/65R14, grabber)
405
+ </span>
406
+ <div className="relative">
407
+ <input
408
+ type="text"
409
+ placeholder="Type tire model, width, ratio, code, or feature description..."
410
+ value={searchQuery}
411
+ onChange={(e) => setSearchQuery(e.target.value)}
412
+ className={`w-full p-3 pl-10 rounded-xl text-xs border focus:ring-1 focus:ring-brand-orange focus:outline-none ${
413
+ darkMode ? "bg-black/35 border-white/10 text-white placeholder-slate-500" : "bg-white border-slate-200 text-slate-800"
414
+ }`}
415
+ id="search-tyres-input"
416
+ />
417
+ <Search className="w-4 h-4 text-slate-400 absolute left-3 top-1/2 -translate-y-1/2" />
418
+ </div>
419
+ </div>
420
+
421
+ {/* Price sorting selection dropdown */}
422
+ <div>
423
+ <span className={`text-xs font-semibold font-mono block mb-1 px-1 ${darkMode ? "text-slate-300" : "text-slate-600"}`}>
424
+ Sort by Price Index
425
+ </span>
426
+ <select
427
+ value={priceSort}
428
+ onChange={(e) => setPriceSort(e.target.value as any)}
429
+ className={`w-full p-3 rounded-xl text-xs border focus:ring-1 focus:ring-brand-orange focus:outline-none ${
430
+ darkMode ? "bg-black/35 border-white/10 text-white" : "bg-white border-slate-200 text-slate-800"
431
+ }`}
432
+ id="price-sort-select"
433
+ >
434
+ <option value="none">Database Standards</option>
435
+ <option value="low-high">Price: Low to High</option>
436
+ <option value="high-low">Price: High to Low</option>
437
+ </select>
438
+ </div>
439
+ </div>
440
+
441
+ {/* Row 2: Segments */}
442
+ <div className="space-y-2 pt-2 border-t border-black/5 dark:border-white/5">
443
+ <span className={`text-xs font-mono font-bold tracking-wider uppercase block ${darkMode ? "text-slate-400" : "text-slate-500"}`}>
444
+ Filter by Segment Categories
445
+ </span>
446
+ <div className="flex flex-wrap gap-2">
447
+ {[
448
+ { id: 'all', label: 'All Products' },
449
+ { id: 'hot', label: '🔥 Hot Selling' },
450
+ { id: 'new', label: '🆕 New arrivals' },
451
+ { id: 'famous', label: '⭐ Famous Brand Favorites' }
452
+ ].map((seg) => (
453
+ <button
454
+ key={seg.id}
455
+ onClick={() => setActiveSegment(seg.id as any)}
456
+ className={`px-3 py-2 rounded-xl text-xs font-semibold transition-all ${
457
+ activeSegment === seg.id
458
+ ? "bg-brand-orange text-white"
459
+ : darkMode
460
+ ? "bg-white/5 border border-white/5 text-slate-300 hover:bg-white/10"
461
+ : "bg-slate-100 border border-slate-200 text-slate-700 hover:bg-slate-200"
462
+ }`}
463
+ id={`segment-btn-${seg.id}`}
464
+ >
465
+ {seg.label}
466
+ </button>
467
+ ))}
468
+ </div>
469
+ </div>
470
+
471
+ {/* Row 3: Manufacturer filters */}
472
+ <div className="space-y-2 pt-2 border-t border-black/5 dark:border-white/5">
473
+ <span className={`text-xs font-mono font-bold tracking-wider uppercase block ${darkMode ? "text-slate-400" : "text-slate-500"}`}>
474
+ Filter by Brand Manufacturer
475
+ </span>
476
+ <div className="flex flex-wrap gap-1.5">
477
+ <button
478
+ onClick={() => setSelectedBrand('All')}
479
+ className={`px-3 py-1.5 rounded-lg text-xs font-medium transition-all ${
480
+ selectedBrand === 'All'
481
+ ? "border border-brand-orange text-brand-orange bg-brand-orange/10 font-bold"
482
+ : darkMode
483
+ ? "border border-white/5 bg-white/5 text-slate-300 hover:text-white"
484
+ : "border border-slate-200 bg-white text-slate-700 hover:text-slate-900"
485
+ }`}
486
+ id="brand-btn-all"
487
+ >
488
+ All Brands
489
+ </button>
490
+ {BRANDS.map((br) => (
491
+ <button
492
+ key={br.name}
493
+ onClick={() => setSelectedBrand(br.name)}
494
+ className={`px-3 py-1.5 rounded-lg text-xs font-medium transition-all ${
495
+ selectedBrand === br.name
496
+ ? "border border-brand-orange text-brand-orange bg-brand-orange/10 font-bold"
497
+ : darkMode
498
+ ? "border border-white/5 bg-white/5 text-slate-300 hover:text-white"
499
+ : "border border-slate-200 bg-white text-slate-700 hover:text-slate-900"
500
+ }`}
501
+ id={`brand-btn-${br.name.replace(/\s+/g, '-').toLowerCase()}`}
502
+ >
503
+ {br.name}
504
+ </button>
505
+ ))}
506
+ </div>
507
+ </div>
508
+ </div>
509
+
510
+ {/* Results bar */}
511
+ <div className="flex justify-between items-center mb-6">
512
+ <span className="text-xs font-mono text-slate-400">
513
+ Found <span className="text-brand-orange font-bold font-sans">{sortedCatalogProducts.length}</span> matching products in Lahore branch
514
+ </span>
515
+ {searchQuery && (
516
+ <button
517
+ onClick={() => setSearchQuery("")}
518
+ className="text-xs text-brand-orange hover:underline font-bold"
519
+ >
520
+ Clear size queries
521
+ </button>
522
+ )}
523
+ </div>
524
+
525
+ {/* Dynamic Tyre Catalog Cards Grid */}
526
+ {sortedCatalogProducts.length > 0 ? (
527
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
528
+ {sortedCatalogProducts.map((tire) => (
529
+ <TireCard
530
+ key={tire.id}
531
+ tire={tire}
532
+ darkMode={darkMode}
533
+ onSelect={(t) => setSelectedTire(t)}
534
+ />
535
+ ))}
536
+ </div>
537
+ ) : (
538
+ <div className={`p-12 text-center rounded-3xl border border-dashed text-slate-400 ${
539
+ darkMode ? "bg-white/5 border-white/10" : "bg-slate-50 border-slate-200"
540
+ } max-w-md mx-auto`}>
541
+ <span className="text-4xl block mb-3">🔍</span>
542
+ <h4 className="font-display font-bold text-md text-brand-orange">No exact specification tyre found</h4>
543
+ <p className={`text-xs mt-2 leading-relaxed ${darkMode ? "text-slate-400" : "text-slate-500"}`}>
544
+ We might still have warehouse stock of size custom model '{searchQuery}'! Please call or shoot a direct WhatsApp to our sales desk.
545
+ </p>
546
+ <div className="mt-6 flex justify-center space-x-3">
547
+ <button
548
+ onClick={() => { setSelectedBrand("All"); setActiveSegment("all"); setSearchQuery(""); }}
549
+ className="bg-brand-orange text-white text-xs px-4 py-2 rounded-xl transition-all hover:bg-brand-orange-dark font-medium"
550
+ >
551
+ Reset Filter options
552
+ </button>
553
+ <a
554
+ href={`tel:${BUSINESS_INFO.phoneRaw}`}
555
+ className={`text-xs px-4 py-2 border rounded-xl transition-all ${
556
+ darkMode ? "border-white/10 text-white" : "border-slate-300 text-slate-800 hover:bg-slate-50"
557
+ }`}
558
+ >
559
+ Call Sales Office
560
+ </a>
561
+ </div>
562
+ </div>
563
+ )}
564
+ </motion.div>
565
+ )}
566
+
567
+ {/* VIEW 3: LIVE REVIEWS BOARD PAGE-TAB */}
568
+ {currentPage === "reviews" && (
569
+ <motion.div
570
+ initial={{ opacity: 0, y: 15 }}
571
+ animate={{ opacity: 1, y: 0 }}
572
+ exit={{ opacity: 0 }}
573
+ className="pt-24"
574
+ >
575
+ {/* Direct mount child Reviews component */}
576
+ <ReviewsSection
577
+ darkMode={darkMode}
578
+ reviews={reviews}
579
+ onAddReview={handleAddNewReview}
580
+ />
581
+ </motion.div>
582
+ )}
583
+
584
+ {/* VIEW 4: SHOWROOM LOCATION DETAILS PAGE-TAB */}
585
+ {currentPage === "showroom" && (
586
+ <motion.div
587
+ initial={{ opacity: 0, y: 15 }}
588
+ animate={{ opacity: 1, y: 0 }}
589
+ exit={{ opacity: 0 }}
590
+ className="pt-24 pb-20"
591
+ >
592
+ {/* Showroom location address layouts */}
593
+ <LocationContact darkMode={darkMode} />
594
+
595
+ {/* FITTING & ALIGNMENT GUIDELINES EXTRA INFO PANEL */}
596
+ <section className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 mt-12 grid grid-cols-1 md:grid-cols-2 gap-8">
597
+ <div className={`p-6 sm:p-8 rounded-3xl border ${
598
+ darkMode ? "bg-[#1c1c24] border-white/5" : "bg-slate-100 border-slate-200"
599
+ }`}>
600
+ <h3 className="font-display font-black text-l text-brand-orange mb-4">
601
+ 🧭 ROAD EMERGENCIES IN LAHORE
602
+ </h3>
603
+ <p className={`text-xs sm:text-sm leading-relaxed ${darkMode ? "text-slate-400" : "text-slate-600"}`}>
604
+ Broke down late at midnight in Faisal Town, Garden Town, Model Town, Gulberg, or Johar Town due to tyre flat punctures? Do not panic. Simply trigger our emergency phone number 24/7. Our mobile fitment van is equipped with high quality compound kits to rescue you immediately on-site!
605
+ </p>
606
+ <div className="mt-4">
607
+ <a
608
+ href={`tel:${BUSINESS_INFO.phoneRaw}`}
609
+ className="inline-flex items-center space-x-1 bg-brand-orange text-white font-bold text-xs px-4 py-2 rounded-xl"
610
+ >
611
+ <span>Request Roadside Assistance</span>
612
+ </a>
613
+ </div>
614
+ </div>
615
+
616
+ <div className={`p-6 sm:p-8 rounded-3xl border ${
617
+ darkMode ? "bg-[#1c1c24] border-white/5" : "bg-slate-100 border-slate-200"
618
+ }`}>
619
+ <h3 className="font-display font-black text-l text-brand-orange mb-4">
620
+ 🛠️ ALIGNMENT & COMPUTERIZED STEERING ADJUSTMENTS
621
+ </h3>
622
+ <p className={`text-xs sm:text-sm leading-relaxed ${darkMode ? "text-slate-400" : "text-slate-600"}`}>
623
+ We recommend doing computerized 3D wheel alignment checks and wheel balancing every 8,000 Kilometers to avoid uneven tread wire wear, suspension pull, and secure complete control of the car across highways at high speeds. Stop by during the day for discount fitment checking.
624
+ </p>
625
+ <p className="text-[11px] font-mono text-slate-400 mt-3">
626
+ ⏱️ 3D Alignment Stage is hosted 24/7 for premium passenger cars.
627
+ </p>
628
+ </div>
629
+ </section>
630
+
631
+ {/* DYNAMIC FAQ ACCORDION COMPONENT WITH HIGHEST SEO RATINGS */}
632
+ <FAQSection darkMode={darkMode} />
633
+ </motion.div>
634
+ )}
635
+
636
+ {/* VIEW 5: INVENTORY MANAGER DB SEEDING PAGE-TAB */}
637
+ {currentPage === "admin" && (
638
+ <motion.div
639
+ initial={{ opacity: 0, y: 15 }}
640
+ animate={{ opacity: 1, y: 0 }}
641
+ exit={{ opacity: 0 }}
642
+ className="pt-28 pb-20 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"
643
+ >
644
+ <div className="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-2 border-b pb-4 border-black/5 dark:border-white/5">
645
+ <div>
646
+ <div className="inline-flex items-center space-x-2 bg-brand-orange/10 px-3 py-1 rounded-full text-brand-orange text-xs font-mono font-bold uppercase tracking-wider mb-2">
647
+ <FolderLock className="w-3.5 h-3.5" />
648
+ <span>Real Backend Integration Console</span>
649
+ </div>
650
+ <h1 className="text-3xl sm:text-4xl font-display font-black tracking-tight mt-1">
651
+ Firestore Inventory Manager
652
+ </h1>
653
+ <p className={`text-sm mt-1 max-w-2xl ${darkMode ? "text-slate-400" : "text-slate-600"}`}>
654
+ Admin board connected directly with our provisioned cloud Firestore. Any tyre product configured, edited, or removed in this environment reflects onto the landing page immediately.
655
+ </p>
656
+ </div>
657
+
658
+ <div className="flex items-center space-x-2 bg-green-500/10 border border-green-500/20 text-emerald-500 text-xs px-3 py-1.8 rounded-xl font-mono">
659
+ <span>Secure DB Mode</span>
660
+ </div>
661
+ </div>
662
+
663
+ {/* Main inventory administration dashboards */}
664
+ <InventoryManager
665
+ darkMode={darkMode}
666
+ onRefreshProducts={loadReviews}
667
+ />
668
+ </motion.div>
669
+ )}
670
+
671
+ </main>
672
+
673
+ {/* COMPREHENSIVE HIGH SEO FOOTER ROW SUMMARY SECTION */}
674
+ <footer className={`pt-16 pb-12 relative overflow-hidden transition-colors duration-300 border-t ${
675
+ darkMode ? "bg-brand-dark border-white/5 text-slate-400" : "bg-[#f1f3f6] border-slate-200 text-slate-600"
676
+ }`}>
677
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10 text-xs">
678
+ <div className="grid grid-cols-1 md:grid-cols-4 gap-12 mb-12">
679
+
680
+ {/* Column 1: About imports */}
681
+ <div className="md:col-span-1.5 space-y-4">
682
+ <div className="flex items-center">
683
+ <Logo variant="full" darkMode={darkMode} className="h-12 w-auto -ml-3" />
684
+ </div>
685
+ <p className="leading-relaxed">
686
+ Premium quality wholesaler and certified dealer of brand new tyres. We specialize in providing verified fresh manufacturing date imports, computer balancing, and 3D wheel alignments in Faisal Town Lahore.
687
+ </p>
688
+ <div className="flex items-center space-x-1 font-mono text-[10px] text-brand-orange">
689
+ <span>⭐ 4.8 Ratings • Direct Google Mapping verified</span>
690
+ </div>
691
+ </div>
692
+
693
+ {/* Column 2: Quick navigation */}
694
+ <div className="md:col-span-0.5 space-y-3 font-mono">
695
+ <h4 className={`font-display font-medium text-xs uppercase tracking-widest ${darkMode ? "text-slate-200" : "text-slate-950"}`}>
696
+ Navigation
697
+ </h4>
698
+ <div className="flex flex-col space-y-2">
699
+ <button onClick={() => { setCurrentPage("home"); window.scrollTo({ top: 0 }); }} className="text-left hover:text-brand-orange transition-colors">Showroom Home</button>
700
+ <button onClick={() => { setCurrentPage("catalogue"); window.scrollTo({ top: 0 }); }} className="text-left hover:text-brand-orange transition-colors">Tyre Catalogue</button>
701
+ <button onClick={() => { setCurrentPage("reviews"); window.scrollTo({ top: 0 }); }} className="text-left hover:text-brand-orange transition-colors">Customer Feed</button>
702
+ <button onClick={() => { setCurrentPage("showroom"); window.scrollTo({ top: 0 }); }} className="text-left hover:text-brand-orange transition-colors">Lahore Location</button>
703
+ {isAuthorizedAdmin && (
704
+ <button onClick={() => { setCurrentPage("admin"); window.scrollTo({ top: 0 }); }} className="text-left hover:text-brand-orange transition-colors flex items-center gap-1">
705
+ <span>Inventory Manager</span>
706
+ </button>
707
+ )}
708
+ </div>
709
+ </div>
710
+
711
+ {/* Column 3: Contact highlights & Location */}
712
+ <div className="md:col-span-1 space-y-3 font-mono">
713
+ <h4 className={`font-display font-medium text-xs uppercase tracking-widest ${darkMode ? "text-slate-200" : "text-slate-950"}`}>
714
+ Showroom Contact
715
+ </h4>
716
+ <div className="flex flex-col space-y-1.5">
717
+ <a href={`tel:${BUSINESS_INFO.phoneRaw}`} className="hover:text-brand-orange transition-colors">
718
+ 📞 Emergency: {BUSINESS_INFO.phone}
719
+ </a>
720
+ <a href={`${BUSINESS_INFO.whatsappUrl}?text=I'd%20like%20to%20inquire%20about%20tires`} target="_blank" rel="noreferrer" className="hover:text-brand-orange transition-colors">
721
+ 💬 WhatsApp: Chat Live
722
+ </a>
723
+ <div className="text-slate-500 font-sans text-xs pt-1">
724
+ 📍 {BUSINESS_INFO.address}
725
+ </div>
726
+ <div className="flex items-center space-x-1 text-brand-orange text-[9px] uppercase font-bold tracking-wider pt-0.5">
727
+ <MapPin className="w-3.5 h-3.5" />
728
+ <span>Faisal Town Lahore</span>
729
+ </div>
730
+ </div>
731
+ </div>
732
+
733
+ {/* Column 4: Newsletter signup */}
734
+ <div className="md:col-span-1">
735
+ <NewsletterSignup darkMode={darkMode} />
736
+ </div>
737
+
738
+ </div>
739
+
740
+ <div className="pt-8 border-t border-black/5 dark:border-white/5 flex flex-col sm:flex-row items-center justify-between text-[11px] text-slate-500 text-center">
741
+ <p className="mb-4 sm:mb-0">
742
+ © {new Date().getFullYear()} Haider Brothers Traders Lahore. All Rights Reserved. Fully optimized in production under Pakistan wholesale regulations.
743
+ <button
744
+ onClick={() => {
745
+ if (isAuthorizedAdmin) {
746
+ setCurrentPage("admin");
747
+ window.scrollTo({ top: 0 });
748
+ } else {
749
+ setShowAdminLoginModal(true);
750
+ }
751
+ }}
752
+ className="opacity-40 hover:opacity-100 transition-opacity ml-2 cursor-pointer inline-flex items-center gap-1 text-[10px]"
753
+ id="subtle-staff-entrance"
754
+ title="Management Console"
755
+ >
756
+ <Lock className="w-2.5 h-2.5 text-brand-orange inline" />
757
+ <span>Staff Portal</span>
758
+ </button>
759
+ </p>
760
+ <div className="flex items-center space-x-1 text-center justify-center">
761
+ <span>Drive Safely with certified tyre sizes</span>
762
+ <Heart className="w-3.5 h-3.5 text-brand-orange fill-current" />
763
+ <span>& robust grip safety rules</span>
764
+ </div>
765
+ </div>
766
+ </div>
767
+ </footer>
768
+
769
+ {/* Interactive Detail Modal view */}
770
+ <AnimatePresence>
771
+ {selectedTire && (
772
+ <ProductDetailsModal
773
+ tire={selectedTire}
774
+ onClose={() => setSelectedTire(null)}
775
+ darkMode={darkMode}
776
+ />
777
+ )}
778
+ </AnimatePresence>
779
+
780
+ {/* Staff Authentication Portal Modal */}
781
+ <AdminLoginModal
782
+ isOpen={showAdminLoginModal}
783
+ onClose={() => setShowAdminLoginModal(false)}
784
+ onSuccess={() => {
785
+ setCurrentPage("admin");
786
+ window.scrollTo({ top: 0 });
787
+ }}
788
+ darkMode={darkMode}
789
+ />
790
+
791
+ {/* Floating Corner CTAs handles */}
792
+ <FloatingActions
793
+ darkMode={darkMode}
794
+ selectedTire={selectedTire}
795
+ />
796
+ </div>
797
+ );
798
+ }
src/assets/images/comfort_tire_1781509452553.jpg ADDED

Git LFS Details

  • SHA256: e6517dddf333bf883910e29a35e4bfa806d807a2114c268c3d89f514a0f0f57d
  • Pointer size: 131 Bytes
  • Size of remote file: 371 kB
src/assets/images/sport_tire_1781509490802.jpg ADDED

Git LFS Details

  • SHA256: eec06d71132b2826473e11a8c917ba82138858d46673a99b4cc8ae9f09eadca6
  • Pointer size: 131 Bytes
  • Size of remote file: 412 kB
src/assets/images/suv_tire_1781509472580.jpg ADDED

Git LFS Details

  • SHA256: a3f18fd71c4c4ac0f9102782bdb429022f096d9d7e3947bf11f794c78add9503
  • Pointer size: 131 Bytes
  • Size of remote file: 425 kB
src/assets/images/winter_tire_1781509513584.jpg ADDED

Git LFS Details

  • SHA256: 7ee290ac8ac43a3e7d5915f0966251744e41360c0dd18b1d521c89ba1a9eceac
  • Pointer size: 131 Bytes
  • Size of remote file: 415 kB
src/components/AdminLoginModal.tsx ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState } from "react";
2
+ import { X, Lock, RefreshCw, Mail, KeyRound } from "lucide-react";
3
+ import { loginAdmin } from "../lib/firebaseService";
4
+
5
+ interface AdminLoginModalProps {
6
+ isOpen: boolean;
7
+ onClose: () => void;
8
+ onSuccess: () => void;
9
+ darkMode: boolean;
10
+ }
11
+
12
+ export default function AdminLoginModal({ isOpen, onClose, onSuccess, darkMode }: AdminLoginModalProps) {
13
+ const [email, setEmail] = useState("");
14
+ const [password, setPassword] = useState("");
15
+ const [loading, setLoading] = useState(false);
16
+ const [error, setError] = useState("");
17
+
18
+ if (!isOpen) return null;
19
+
20
+ const handleSubmit = async (e: React.FormEvent) => {
21
+ e.preventDefault();
22
+ if (!email || !password) {
23
+ setError("Please fill in all fields.");
24
+ return;
25
+ }
26
+ setLoading(true);
27
+ setError("");
28
+
29
+ try {
30
+ await loginAdmin(email, password);
31
+ onSuccess();
32
+ onClose();
33
+ } catch (err: any) {
34
+ setError(err.message || "Invalid authentication credentials.");
35
+ } finally {
36
+ setLoading(false);
37
+ }
38
+ };
39
+
40
+ return (
41
+ <div className="fixed inset-0 z-55 flex items-center justify-center p-4 bg-black/70 backdrop-blur-xs">
42
+ <div
43
+ className={`w-full max-w-md p-6 sm:p-8 rounded-3xl border shadow-2xl relative transition-colors duration-300 ${
44
+ darkMode ? "bg-[#1c1c24] border-white/10 text-white" : "bg-white border-slate-200 text-slate-900"
45
+ }`}
46
+ >
47
+ <button
48
+ onClick={onClose}
49
+ className={`absolute top-4 right-4 p-1.5 rounded-lg transition-colors ${
50
+ darkMode ? "hover:bg-white/10 text-slate-400 hover:text-white" : "hover:bg-black/5 text-slate-500 hover:text-black"
51
+ }`}
52
+ >
53
+ <X className="w-5 h-5" />
54
+ </button>
55
+
56
+ <div className="flex items-center space-x-3 mb-6">
57
+ <div className="p-2.5 rounded-xl bg-brand-orange/15 text-brand-orange">
58
+ <Lock className="w-6 h-6" />
59
+ </div>
60
+ <div>
61
+ <h3 className="font-display font-extrabold text-lg text-brand-orange">Staff Authentication</h3>
62
+ <p className={`text-xs ${darkMode ? "text-slate-400" : "text-slate-500"}`}>
63
+ Direct database verification for Haider Brothers Traders admin
64
+ </p>
65
+ </div>
66
+ </div>
67
+
68
+ {error && (
69
+ <div className="mb-4 p-3 bg-red-600/15 border border-red-500/20 text-red-400 text-xs rounded-xl font-medium">
70
+ {error}
71
+ </div>
72
+ )}
73
+
74
+ <form onSubmit={handleSubmit} className="space-y-4">
75
+ <div>
76
+ <label className="text-[10px] uppercase font-mono tracking-wider block mb-1.5">
77
+ Secure Email Address
78
+ </label>
79
+ <div className="relative">
80
+ <input
81
+ type="email"
82
+ required
83
+ placeholder="bahaduralimunnabhai@gmail.com"
84
+ value={email}
85
+ onChange={(e) => setEmail(e.target.value)}
86
+ className={`w-full p-2.5 pl-9 rounded-xl text-xs border focus:outline-none focus:ring-2 focus:ring-brand-orange/40 focus:border-brand-orange ${
87
+ darkMode ? "bg-black/40 border-white/10 text-white" : "bg-slate-50 border-slate-200"
88
+ }`}
89
+ />
90
+ <Mail className="w-4 h-4 text-slate-500 absolute left-3 top-1/2 -translate-y-1/2" />
91
+ </div>
92
+ </div>
93
+
94
+ <div>
95
+ <label className="text-[10px] uppercase font-mono tracking-wider block mb-1.5">
96
+ Enter Password
97
+ </label>
98
+ <div className="relative">
99
+ <input
100
+ type="password"
101
+ required
102
+ placeholder="••••••••"
103
+ value={password}
104
+ onChange={(e) => setPassword(e.target.value)}
105
+ className={`w-full p-2.5 pl-9 rounded-xl text-xs border focus:outline-none focus:ring-2 focus:ring-brand-orange/40 focus:border-brand-orange ${
106
+ darkMode ? "bg-black/40 border-white/10 text-white" : "bg-slate-50 border-slate-200"
107
+ }`}
108
+ />
109
+ <KeyRound className="w-4 h-4 text-slate-500 absolute left-3 top-1/2 -translate-y-1/2" />
110
+ </div>
111
+ </div>
112
+
113
+ <button
114
+ type="submit"
115
+ disabled={loading}
116
+ className="w-full bg-gradient-to-r from-brand-orange to-red-600 hover:from-brand-orange-light hover:to-red-500 text-white font-bold p-3 rounded-xl text-xs flex items-center justify-center space-x-1.5 shadow-md shadow-brand-orange/20 transition-all cursor-pointer mt-2"
117
+ >
118
+ {loading ? (
119
+ <RefreshCw className="w-3.5 h-3.5 animate-spin" />
120
+ ) : (
121
+ <Lock className="w-3.5 h-3.5" />
122
+ )}
123
+ <span>Verify Credentials</span>
124
+ </button>
125
+ </form>
126
+ </div>
127
+ </div>
128
+ );
129
+ }
src/components/FAQSection.tsx ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState } from "react";
2
+ import { motion, AnimatePresence } from "motion/react";
3
+ import {
4
+ HelpCircle,
5
+ ChevronDown,
6
+ ShieldCheck,
7
+ Wrench,
8
+ Clock,
9
+ Search,
10
+ BadgeHelp,
11
+ Tag
12
+ } from "lucide-react";
13
+
14
+ interface FAQItem {
15
+ id: string;
16
+ question: string;
17
+ answer: string;
18
+ keywords: string[];
19
+ category: "warranty" | "services" | "general";
20
+ }
21
+
22
+ interface FAQSectionProps {
23
+ darkMode: boolean;
24
+ }
25
+
26
+ export default function FAQSection({ darkMode }: FAQSectionProps) {
27
+ const [openId, setOpenId] = useState<string | null>("warranty-duration");
28
+ const [filterCategory, setFilterCategory] = useState<"all" | "warranty" | "services">("all");
29
+ const [searchQuery, setSearchQuery] = useState("");
30
+
31
+ const FAQS: FAQItem[] = [
32
+ {
33
+ id: "warranty-duration",
34
+ question: "What official warranty is provided with premium imported tyres from Yokohama, Michelin, and Continental?",
35
+ answer: "All premium brand tyres (including Yokohama, Michelin, and Continental) purchased from Haider Brothers Faisal Town come with a 5-Year Continuous Structural & Tread Warranty. This warranty protects against tread separation, structural defects, factory compounds failures, and early bulging under standard driving specifications. We assist with 100% genuine claims fast-tracked directly with manufacturer dealers without complications.",
36
+ keywords: ["warranty Yokohama", "Michelin warranty", "Continental tyres Lahore", "imported tyre warranty"],
37
+ category: "warranty"
38
+ },
39
+ {
40
+ id: "tire-installation-benefits",
41
+ question: "Is there a charge for tyre installation, computerized balancing, and nitrogen air filling in Faisal Town showroom?",
42
+ answer: "No, tyre installation at our Faisal Town showroom is 100% free with the purchase of any tyre set. For seasonal promotional packages or sets of 4 premium tyres, we include computerized 3D wheel alignment, premium computerized wheel balancing, durable tubeless valves, and lifetime nitrogen gas top-up sessions absolutely free. No hidden service charges apply.",
43
+ keywords: ["free tyre installation", "3D wheel alignment", "wheel balancing Faisal Town", "free nitrogen Lahore"],
44
+ category: "services"
45
+ },
46
+ {
47
+ id: "checking-dot-year",
48
+ question: "How can I verify the genuine manufacturing year and DOT marking of tyres purchased at Haider Brothers?",
49
+ answer: "We guarantee 100% authentic tyre imports with visible, untouched DOT stamps. The DOT code is a 4-digit code hot-stamped on the tire sidewall; the first two digits represent the manufacturing week, and the last two represent the year (e.g., '1226' means 12th week of 2026). Haider Brothers never stocks smuggled or expired stock, securing complete legal peace of mind with legal custom imports.",
50
+ keywords: ["tyre manufacturing year", "how to read DOT markings", "genuine imported tyres", "fresh tyre stock Lahore"],
51
+ category: "warranty"
52
+ },
53
+ {
54
+ id: "pothole-road-hazard",
55
+ question: "Does the warranty cover tyre punctures, roadside sidewall cuts, or pothole impact damage?",
56
+ answer: "Official manufacturer structural warranties cover material defects and manufacturing flaws. Road hazards such as standard tyre punctures, impact damage from deep Lahore potholes, sidewall kerb cuts, or debris damage are not traditionally covered under standard structural warranty terms. However, we offer premium tyre repair and patching services at our showroom anytime, or immediate tyre roadside replacements via our emergency van.",
57
+ keywords: ["pothole tyre damage warranty", "sidewall cuts repair Lahore", "tire puncture service", "road hazard assistance"],
58
+ category: "warranty"
59
+ },
60
+ {
61
+ id: "emergency-road-service",
62
+ question: "What should I do if my sedan tire bursts late at night in Lahore? Do you offer 24/7 emergency roadside support?",
63
+ answer: "Yes, Haider Brothers operates a dedicated 24 Hours Emergency Roadside Tyre Assistance service. If you encounter a flat tire, puncture, or rim issue late at night anywhere around Faisal Town, Model Town, Gulberg, Johar Town, or Garden Town, call our emergency hotline at +92 302 4594403 immediately. Our responsive mobile service van will dispatch with fresh stock to assist on-site.",
64
+ keywords: ["24/7 tyre repair Lahore", "midnight flat tire recovery", "emergency roadside assistance", "mobile tire van Faisal Town"],
65
+ category: "services"
66
+ },
67
+ {
68
+ id: "alignment-frequency",
69
+ question: "How often should I get computerized 3D wheel alignment checks and wheel balancing for Pakistani roads?",
70
+ answer: "For Pakistani road terrains, we strongly recommend a computerized 3D wheel alignment and professional wheel balancing check every 8,000 to 10,000 Kilometers, or whenever you feel alignment pulling to one side. Regular alignments prevent premature tread wear (uneven tire scrubbing), keep steering vibration-free on motorways at high speeds, and optimize vehicle fuel economy.",
71
+ keywords: ["computerized wheel alignment frequency", "tire balancing intervals", "how to prevent uneven tire wear", "Pakistani road maintenance"],
72
+ category: "services"
73
+ }
74
+ ];
75
+
76
+ const toggleFAQ = (id: string) => {
77
+ setOpenId((prev) => (prev === id ? null : id));
78
+ };
79
+
80
+ // Filter and search logic for great SEO reach
81
+ const filteredFAQs = FAQS.filter((faq) => {
82
+ const categoryMatches = filterCategory === "all" || faq.category === filterCategory;
83
+ const searchString = `${faq.question} ${faq.answer} ${faq.keywords.join(" ")}`.toLowerCase();
84
+ const queryMatches = searchString.includes(searchQuery.toLowerCase());
85
+ return categoryMatches && queryMatches;
86
+ });
87
+
88
+ return (
89
+ <section
90
+ id="showroom-faqs-accordion"
91
+ className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 mt-16 pt-12 border-t border-black/5 dark:border-white/5"
92
+ >
93
+ {/* Schema.org Structured Data Injection for high-fidelity Google SEO relevance */}
94
+ <script type="application/ld+json">
95
+ {JSON.stringify({
96
+ "@context": "https://schema.org",
97
+ "@type": "FAQPage",
98
+ "mainEntity": FAQS.map((faq) => ({
99
+ "@type": "Question",
100
+ "name": faq.question,
101
+ "acceptedAnswer": {
102
+ "@type": "Answer",
103
+ "text": faq.answer
104
+ }
105
+ }))
106
+ })}
107
+ </script>
108
+
109
+ <div className="text-center max-w-2xl mx-auto mb-8">
110
+ <span className="text-xs font-mono tracking-widest uppercase bg-brand-orange/15 text-brand-orange px-3.5 py-1 rounded-full font-bold inline-flex items-center gap-1.5 shadow-xs">
111
+ <BadgeHelp className="w-3.5 h-3.5 animate-pulse" />
112
+ <span>Haider Brothers FAQ Hub</span>
113
+ </span>
114
+ <h2 className="text-2xl sm:text-3.5xl font-display font-black tracking-tight mt-3">
115
+ Tire Warranty & Service FAQs
116
+ </h2>
117
+ <p className={`text-xs sm:text-sm mt-2 leading-relaxed ${darkMode ? "text-slate-400" : "text-slate-600"}`}>
118
+ Honest, transparent answers about premium tyre warranties, alignment services, and emergency 24/7 roadside assistances in Faisal Town Lahore.
119
+ </p>
120
+ </div>
121
+
122
+ {/* Segment Category Switcher + SEO Search Input */}
123
+ <div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-3 justify-between mb-6">
124
+ <div className="flex p-1 rounded-xl bg-slate-200/50 dark:bg-black/30 border border-slate-300/30 dark:border-white/5 self-start">
125
+ {[
126
+ { id: "all", label: "All Questions" },
127
+ { id: "warranty", label: "🛡️ Warranty" },
128
+ { id: "services", label: "🛠️ Services & 3D Align" }
129
+ ].map((cat) => (
130
+ <button
131
+ key={cat.id}
132
+ onClick={() => setFilterCategory(cat.id as any)}
133
+ className={`px-3 py-1.5 rounded-lg text-xs font-semibold transition-all ${
134
+ filterCategory === cat.id
135
+ ? "bg-brand-orange text-white"
136
+ : "text-slate-500 hover:text-slate-800 dark:hover:text-slate-200"
137
+ }`}
138
+ type="button"
139
+ >
140
+ {cat.label}
141
+ </button>
142
+ ))}
143
+ </div>
144
+
145
+ {/* Live Search inside FAQ content with key phrases */}
146
+ <div className="relative max-w-xs w-full">
147
+ <input
148
+ type="text"
149
+ placeholder="Search FAQs (e.g. alignment, DOT, Michelin)..."
150
+ value={searchQuery}
151
+ onChange={(e) => setSearchQuery(e.target.value)}
152
+ className={`w-full p-2.5 pl-8 text-xs rounded-xl border focus:ring-1 focus:ring-brand-orange focus:outline-hidden ${
153
+ darkMode
154
+ ? "bg-black/35 border-white/10 text-white placeholder-slate-500"
155
+ : "bg-white border-slate-200 text-slate-850 placeholder-slate-400"
156
+ }`}
157
+ />
158
+ <Search className="w-3.5 h-3.5 text-slate-400 absolute left-2.5 top-1/2 -translate-y-1/2" />
159
+ </div>
160
+ </div>
161
+
162
+ {/* Accordion List Container */}
163
+ {filteredFAQs.length > 0 ? (
164
+ <div className="space-y-3.5">
165
+ {filteredFAQs.map((faq) => {
166
+ const isOpen = openId === faq.id;
167
+ return (
168
+ <div
169
+ key={faq.id}
170
+ className={`rounded-2xl border transition-all duration-300 overflow-hidden ${
171
+ isOpen
172
+ ? darkMode
173
+ ? "bg-[#1c1c24] border-brand-orange/40"
174
+ : "bg-white border-brand-orange shadow-md"
175
+ : darkMode
176
+ ? "bg-black/25 border-white/5 hover:border-white/10"
177
+ : "bg-white border-slate-200 hover:border-slate-300"
178
+ }`}
179
+ id={`faq-item-${faq.id}`}
180
+ >
181
+ {/* Header Interactive Trigger Button */}
182
+ <button
183
+ onClick={() => toggleFAQ(faq.id)}
184
+ className="w-full p-4 sm:p-5 flex items-center justify-between font-sans font-bold text-xs sm:text-sm text-left transition-colors focus:outline-hidden relative group"
185
+ aria-expanded={isOpen}
186
+ type="button"
187
+ >
188
+ <div className="flex items-start space-x-3 pr-4">
189
+ {faq.category === "warranty" ? (
190
+ <ShieldCheck className="w-4 h-4 text-brand-orange mt-0.5 shrink-0" />
191
+ ) : faq.category === "services" ? (
192
+ <Wrench className="w-4 h-4 text-brand-orange mt-0.5 shrink-0" />
193
+ ) : (
194
+ <HelpCircle className="w-4 h-4 text-brand-orange mt-0.5 shrink-0" />
195
+ )}
196
+ <span className={darkMode ? "text-slate-100 group-hover:text-white" : "text-slate-850 group-hover:text-brand-orange"}>
197
+ {faq.question}
198
+ </span>
199
+ </div>
200
+
201
+ <span className={`p-1 rounded-lg transition-transform duration-300 ${
202
+ isOpen ? "bg-brand-orange/15 text-brand-orange rotate-180" : "text-slate-400 hover:text-slate-600"
203
+ }`}>
204
+ <ChevronDown className="w-4 h-4 shrink-0" />
205
+ </span>
206
+ </button>
207
+
208
+ {/* Smooth Expandable Accordion Body Panels */}
209
+ <AnimatePresence initial={false}>
210
+ {isOpen && (
211
+ <motion.div
212
+ initial={{ height: 0, opacity: 0 }}
213
+ animate={{ height: "auto", opacity: 1 }}
214
+ exit={{ height: 0, opacity: 0 }}
215
+ transition={{ duration: 0.25, ease: "easeInOut" }}
216
+ >
217
+ <div className={`p-4 sm:p-5 pt-0 border-t font-sans text-xs sm:text-[13px] leading-relaxed select-none ${
218
+ darkMode ? "border-white/5 text-slate-300" : "border-slate-100 text-slate-700"
219
+ }`}>
220
+ <p>{faq.answer}</p>
221
+
222
+ {/* SEO Keyword tags indicator for algorithmic reach */}
223
+ <div className="mt-3.5 flex flex-wrap gap-1.5 items-center opacity-80">
224
+ <Tag className="w-3 h-3 text-brand-orange shrink-0" />
225
+ <span className="text-[10px] font-mono text-slate-400">Related indexes:</span>
226
+ {faq.keywords.map((kw, i) => (
227
+ <span
228
+ key={i}
229
+ className="text-[9px] font-mono bg-brand-orange/5 text-brand-orange/80 px-2 py-0.5 rounded-md border border-brand-orange/10"
230
+ >
231
+ {kw}
232
+ </span>
233
+ ))}
234
+ </div>
235
+ </div>
236
+ </motion.div>
237
+ )}
238
+ </AnimatePresence>
239
+ </div>
240
+ );
241
+ })}
242
+ </div>
243
+ ) : (
244
+ <div className={`p-10 text-center rounded-2xl border border-dashed text-slate-400 ${
245
+ darkMode ? "bg-white/5 border-white/10" : "bg-slate-50 border-slate-200"
246
+ }`}>
247
+ <HelpCircle className="w-8 h-8 mx-auto text-slate-500 mb-2 animate-bounce" />
248
+ <h4 className="font-bold text-xs">No matching faq answers found</h4>
249
+ <p className="text-[11px] mt-1">Try searching for simple words like "warranty", "3D alignment", or "DOT".</p>
250
+ </div>
251
+ )}
252
+
253
+ {/* Extra Service Pledge stamp */}
254
+ <div className={`mt-8 p-4 rounded-2xl border text-center font-mono text-[10px] ${
255
+ darkMode ? "bg-brand-orange/5 border-brand-orange/10" : "bg-orange-50/50 border-brand-orange/20"
256
+ }`}>
257
+ <span className="text-brand-orange font-bold font-sans">🛡️ CERTIFIED DIRECT CLAIM GUARANTOR: </span>
258
+ <span className={darkMode ? "text-slate-400" : "text-slate-600"}>We deal strictly in direct legal brand imports on all 2026 batches. No duplicate or high wear compounds guaranteed.</span>
259
+ </div>
260
+ </section>
261
+ );
262
+ }
src/components/FloatingActions.tsx ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { motion, AnimatePresence } from "motion/react";
2
+ import { Phone, MessageSquare } from "lucide-react";
3
+ import { BUSINESS_INFO, TireProduct } from "../data";
4
+
5
+ interface FloatingActionsProps {
6
+ darkMode: boolean;
7
+ selectedTire?: TireProduct | null;
8
+ }
9
+
10
+ export default function FloatingActions({ darkMode, selectedTire }: FloatingActionsProps) {
11
+ // Format dynamic WhatsApp URL
12
+ const getWhatsAppUrl = () => {
13
+ if (!selectedTire) {
14
+ return `${BUSINESS_INFO.whatsappUrl}?text=${encodeURIComponent(
15
+ "Hello Haider Brothers, I need assistance with tyres right now"
16
+ )}`;
17
+ }
18
+
19
+ const message = `Hello Haider Brothers, I'm interested in the following tyre:
20
+
21
+ 🚗 *Tyre details:*
22
+ • *Brand:* ${selectedTire.brand}
23
+ • *Model:* ${selectedTire.name}
24
+ • *Size:* ${selectedTire.size}
25
+ • *Features:* ${selectedTire.feature}
26
+ • *Price:* PKR ${selectedTire.price.toLocaleString()}
27
+ • *Stock status:* ${selectedTire.stock !== undefined ? `${selectedTire.stock} Units Available` : "Available"}
28
+
29
+ Could you please confirm the current stock and availability for installation? Thank you!`;
30
+
31
+ return `${BUSINESS_INFO.whatsappUrl}?text=${encodeURIComponent(message)}`;
32
+ };
33
+
34
+ const hasSelectedTire = !!selectedTire;
35
+
36
+ return (
37
+ <div className="fixed bottom-6 right-6 z-40 flex flex-col space-y-3.5 pointer-events-auto items-end">
38
+ {/* Alert Tooltip for Selected Tyre */}
39
+ <AnimatePresence>
40
+ {selectedTire && (
41
+ <motion.div
42
+ initial={{ opacity: 0, scale: 0.85, y: 15 }}
43
+ animate={{ opacity: 1, scale: 1, y: 0 }}
44
+ exit={{ opacity: 0, scale: 0.85, y: 15 }}
45
+ className={`mr-1 mb-1 p-3.5 rounded-2xl shadow-xl max-w-xs border text-left scale-95 origin-bottom-right flex flex-col space-y-1 backdrop-blur-md ${
46
+ darkMode
47
+ ? "bg-black/95 border-white/10 text-white"
48
+ : "bg-white/95 border-slate-200 text-slate-800"
49
+ }`}
50
+ >
51
+ <div className="flex items-center space-x-1.5 text-brand-orange text-[10px] uppercase font-mono tracking-wider font-extrabold">
52
+ <span className="w-1.5 h-1.5 rounded-full bg-brand-orange animate-ping shrink-0" />
53
+ <span>Direct Product Inquiry</span>
54
+ </div>
55
+ <p className={`text-xs font-bold leading-tight line-clamp-1 ${darkMode ? "text-slate-100" : "text-slate-850"}`}>
56
+ {selectedTire.brand} {selectedTire.name}
57
+ </p>
58
+ <p className={`text-[10px] leading-snug ${darkMode ? "text-slate-400" : "text-slate-500"}`}>
59
+ Click below to send instant specifications over WhatsApp!
60
+ </p>
61
+ </motion.div>
62
+ )}
63
+ </AnimatePresence>
64
+
65
+ {/* Clickable Phone Call dialer */}
66
+ <a
67
+ href={`tel:${BUSINESS_INFO.phoneRaw}`}
68
+ className="w-12 h-12 rounded-full bg-gradient-to-br from-brand-orange to-red-600 text-white flex items-center justify-center shadow-lg shadow-brand-orange/30 hover:scale-110 active:scale-95 transition-all group"
69
+ title="Dial emergency tire service line"
70
+ id="floating-call-btn"
71
+ >
72
+ <Phone className="w-5 h-5 group-hover:rotate-12 transition-transform" />
73
+ </a>
74
+
75
+ {/* Floating interactive WhatsApp */}
76
+ <a
77
+ href={getWhatsAppUrl()}
78
+ target="_blank"
79
+ rel="noreferrer referrer"
80
+ className={`relative w-12 h-12 rounded-full text-white flex items-center justify-center shadow-lg transition-all group ${
81
+ hasSelectedTire
82
+ ? "bg-brand-orange hover:bg-brand-orange-dark shadow-brand-orange/40 hover:scale-112"
83
+ : "bg-green-500 hover:bg-green-600 shadow-green-500/30 hover:scale-110"
84
+ }`}
85
+ title={hasSelectedTire ? `Inquire about ${selectedTire.brand} ${selectedTire.name} on WhatsApp` : "Chat live on WhatsApp"}
86
+ id="floating-whatsapp-btn"
87
+ >
88
+ {/* Pulsating green or orange ring indication */}
89
+ <span className={`absolute inset-0 rounded-full animate-ping pointer-events-none scale-105 ${
90
+ hasSelectedTire ? "bg-brand-orange/40" : "bg-green-500/40"
91
+ }`}></span>
92
+ <MessageSquare className="w-5 h-5 group-hover:-translate-y-0.5 transition-transform" />
93
+ </a>
94
+ </div>
95
+ );
96
+ }
src/components/Hero.tsx ADDED
@@ -0,0 +1,275 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { motion } from "motion/react";
2
+ import { Phone, ArrowRight, Star, Clock, Sparkles, ShieldCheck, MapPin } from "lucide-react";
3
+ import { BUSINESS_INFO, BRANDS } from "../data";
4
+
5
+ interface HeroProps {
6
+ darkMode: boolean;
7
+ }
8
+
9
+ export default function Hero({ darkMode }: HeroProps) {
10
+ const containerVariants = {
11
+ hidden: { opacity: 0 },
12
+ visible: {
13
+ opacity: 1,
14
+ transition: {
15
+ staggerChildren: 0.1,
16
+ delayChildren: 0.1
17
+ }
18
+ }
19
+ };
20
+
21
+ const itemVariants = {
22
+ hidden: { opacity: 0, y: 20 },
23
+ visible: { opacity: 1, y: 0, transition: { duration: 0.6, ease: "easeOut" } }
24
+ };
25
+
26
+ return (
27
+ <section
28
+ id="home"
29
+ className={`relative min-h-screen pt-28 pb-16 flex flex-col justify-center overflow-hidden transition-colors duration-300 ${
30
+ darkMode ? "bg-brand-dark text-white" : "bg-slate-50 text-slate-900"
31
+ }`}
32
+ >
33
+ {/* Dynamic Background Playing Tire Video */}
34
+ <div className="absolute inset-0 z-0 pointer-events-none overflow-hidden">
35
+ <video
36
+ autoPlay
37
+ loop
38
+ muted
39
+ playsInline
40
+ className={`w-full h-full object-cover transition-opacity duration-700 ${
41
+ darkMode ? "opacity-25" : "opacity-10"
42
+ }`}
43
+ src="https://assets.mixkit.co/videos/preview/mixkit-tire-of-a-car-wheels-spinning-on-the-road-41484-large.mp4"
44
+ style={{ objectFit: "cover" }}
45
+ />
46
+ {/* Horizontal Dark Gradient Mask for Left-Column Text Readability */}
47
+ <div
48
+ className={`absolute inset-0 transition-opacity duration-500 bg-gradient-to-r ${
49
+ darkMode
50
+ ? "from-brand-dark via-brand-dark/85 to-transparent"
51
+ : "from-slate-100 via-slate-100/75 to-transparent"
52
+ }`}
53
+ />
54
+ {/* Vertical Transition Mask to Blend into the next sections flawlessly */}
55
+ <div
56
+ className={`absolute inset-0 transition-opacity duration-500 bg-gradient-to-t ${
57
+ darkMode
58
+ ? "from-brand-dark via-transparent to-transparent"
59
+ : "from-slate-50 via-transparent to-transparent"
60
+ }`}
61
+ />
62
+ </div>
63
+
64
+ {/* Decorative Orbs with glassmersion */}
65
+ <div className="absolute top-1/4 right-0 w-80 h-80 rounded-full bg-gradient-to-br from-brand-orange/30 to-rose-600/10 blur-[100px] pointer-events-none animate-pulse-slow"></div>
66
+ <div className="absolute bottom-1/4 -left-20 w-96 h-96 rounded-full bg-gradient-to-br from-blue-600/10 to-brand-orange/10 blur-[120px] pointer-events-none"></div>
67
+
68
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10 w-full">
69
+ <motion.div
70
+ variants={containerVariants}
71
+ initial="hidden"
72
+ animate="visible"
73
+ className="grid grid-cols-1 lg:grid-cols-12 gap-12 items-center"
74
+ >
75
+ {/* Left Hero Column */}
76
+ <div className="lg:col-span-7 space-y-8">
77
+ <motion.div variants={itemVariants} className="inline-flex items-center space-x-2 bg-brand-orange/10 dark:bg-brand-orange/15 border border-brand-orange/30 px-3.5 py-1.5 rounded-full text-brand-orange">
78
+ <Sparkles className="w-4 h-4" />
79
+ <span className="text-xs font-mono font-semibold tracking-wider uppercase">Faisal Town's #1 Rated Tyre Store</span>
80
+ </motion.div>
81
+
82
+ <motion.div variants={itemVariants} className="space-y-4">
83
+ <h1 className="font-display text-4xl sm:text-5xl md:text-6xl font-extrabold tracking-tight leading-[1.05]">
84
+ Premium Tyres <br />
85
+ <span className="text-transparent bg-clip-text bg-gradient-to-r from-brand-orange via-orange-500 to-red-600">
86
+ Built For Safety
87
+ </span>
88
+ </h1>
89
+ <p className={`text-base sm:text-lg max-w-xl ${
90
+ darkMode ? "text-slate-300" : "text-slate-600"
91
+ }`}>
92
+ Authorized distributor of world-renowned tyre brands in Lahore. From high-speed touring tyres to rugged off-road compounds, enjoy
93
+ <span className="text-brand-orange font-semibold"> 24/7 emergency tyre repairs</span> & alignment services.
94
+ </p>
95
+ </motion.div>
96
+
97
+ {/* Quick Actions Buttons */}
98
+ <motion.div variants={itemVariants} className="flex flex-col sm:flex-row gap-4">
99
+ <a
100
+ href="#all-products"
101
+ className="group flex items-center justify-center space-x-2 bg-gradient-to-br from-brand-orange to-red-600 hover:from-brand-orange-light hover:to-red-500 text-white px-7 py-4 rounded-xl text-base font-semibold shadow-lg shadow-brand-orange/25 hover:shadow-brand-orange/40 hover:-translate-y-0.5 transition-all text-center"
102
+ >
103
+ <span>View Tyre Catalog</span>
104
+ <ArrowRight className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
105
+ </a>
106
+
107
+ <a
108
+ href={`tel:${BUSINESS_INFO.phoneRaw}`}
109
+ className={`flex items-center justify-center space-x-2 border px-7 py-4 rounded-xl text-base font-semibold transition-all hover:-translate-y-0.5 ${
110
+ darkMode
111
+ ? "border-white/10 dark:hover:bg-white/5 bg-white/5 active:bg-white/10 text-white"
112
+ : "border-slate-300 hover:bg-slate-100 bg-white shadow-sm text-slate-800"
113
+ }`}
114
+ >
115
+ <Phone className="w-5 h-5 text-brand-orange animate-wiggle" />
116
+ <span>Call Hotline +92 302 4594403</span>
117
+ </a>
118
+ </motion.div>
119
+
120
+ {/* Micro badges */}
121
+ <motion.div variants={itemVariants} className="grid grid-cols-3 gap-4 border-t pt-8 border-black/5 dark:border-white/5">
122
+ <div className="flex flex-col">
123
+ <span className="text-brand-orange font-display text-2xl font-bold">100% Original</span>
124
+ <span className={`text-xs ${darkMode ? "text-slate-400" : "text-slate-500"}`}>Authentic Import Warranty</span>
125
+ </div>
126
+ <div className="flex flex-col">
127
+ <span className="text-brand-orange font-display text-2xl font-bold">24/7 Hours</span>
128
+ <span className={`text-xs ${darkMode ? "text-slate-400" : "text-slate-500"}`}>Night Emergency Road Rescue</span>
129
+ </div>
130
+ <div className="flex flex-col">
131
+ <span className="text-brand-orange font-display text-2xl font-bold">Same Day</span>
132
+ <span className={`text-xs ${darkMode ? "text-slate-400" : "text-slate-500"}`}>Instant Fitting & Tuning</span>
133
+ </div>
134
+ </motion.div>
135
+ </div>
136
+
137
+ {/* Right Bento Grid Column */}
138
+ <div className="lg:col-span-5 grid grid-cols-2 gap-4">
139
+ {/* Bento Card 1: Map Google Ratings */}
140
+ <motion.div
141
+ variants={itemVariants}
142
+ whileHover={{ y: -4, scale: 1.02 }}
143
+ className={`col-span-2 p-5 rounded-3xl ${
144
+ darkMode ? "glassmorphism relative overflow-hidden" : "glassmorphism-light shadow-md"
145
+ }`}
146
+ >
147
+ <div className="flex items-start justify-between">
148
+ <div>
149
+ <span className={`text-xs font-mono tracking-wider uppercase block ${darkMode ? "text-slate-400" : "text-slate-500"}`}>Google Reviews</span>
150
+ <h3 className="text-2xl font-display font-extrabold mt-1 text-brand-orange">4.8 ★ Rating</h3>
151
+ <p className={`text-xs mt-2 ${darkMode ? "text-slate-400" : "text-slate-600"}`}>Overwhelming praise for swift customer support & precise alignment.</p>
152
+ </div>
153
+ <div className="flex space-x-1 bg-brand-orange/10 p-2 rounded-xl text-brand-orange">
154
+ <Star className="w-5 h-5 fill-current" />
155
+ <Star className="w-5 h-5 fill-current" />
156
+ <Star className="w-5 h-5 fill-current" />
157
+ <Star className="w-5 h-5 fill-current" />
158
+ <Star className="w-5 h-5 fill-current" />
159
+ </div>
160
+ </div>
161
+ {/* Profile group miniature */}
162
+ <div className="flex items-center space-x-2.5 mt-4 pt-4 border-t border-black/5 dark:border-white/5">
163
+ <div className="flex items-center -space-x-2 overflow-hidden">
164
+ <div className="w-7 h-7 rounded-full ring-2 ring-white dark:ring-[#1c1c24] bg-gradient-to-br from-emerald-500 to-teal-600 text-[10px] text-white flex items-center justify-center font-bold uppercase shadow-sm">
165
+ GM
166
+ </div>
167
+ <div className="w-7 h-7 rounded-full ring-2 ring-white dark:ring-[#1c1c24] bg-gradient-to-br from-blue-500 to-indigo-600 text-[10px] text-white flex items-center justify-center font-bold uppercase shadow-sm">
168
+ MB
169
+ </div>
170
+ <div className="w-7 h-7 rounded-full ring-2 ring-white dark:ring-[#1c1c24] bg-gradient-to-br from-purple-500 to-fuchsia-600 text-[10px] text-white flex items-center justify-center font-bold uppercase shadow-sm">
171
+ MR
172
+ </div>
173
+ </div>
174
+ <span className={`text-[11px] font-medium tracking-wide ${darkMode ? "text-slate-400" : "text-slate-600"}`}>
175
+ Verified Customer Comments on Maps
176
+ </span>
177
+ </div>
178
+ </motion.div>
179
+
180
+ {/* Bento Card 2: 24/7 Hours */}
181
+ <motion.div
182
+ variants={itemVariants}
183
+ whileHover={{ y: -4 }}
184
+ className={`p-5 rounded-3xl ${
185
+ darkMode ? "glassmorphism" : "glassmorphism-light shadow-md"
186
+ }`}
187
+ >
188
+ <div className="w-10 h-10 rounded-xl bg-orange-500/10 text-brand-orange flex items-center justify-center mb-4">
189
+ <Clock className="w-5 h-5" />
190
+ </div>
191
+ <h4 className="font-display font-bold text-lg">Always Open</h4>
192
+ <p className={`text-xs mt-1 ${darkMode ? "text-slate-400" : "text-slate-500"}`}>
193
+ Emergency assistance, fitting & air services 24 hours open.
194
+ </p>
195
+ </motion.div>
196
+
197
+ {/* Bento Card 3: Secure Products */}
198
+ <motion.div
199
+ variants={itemVariants}
200
+ whileHover={{ y: -4 }}
201
+ className={`p-5 rounded-3xl ${
202
+ darkMode ? "glassmorphism" : "glassmorphism-light shadow-md"
203
+ }`}
204
+ >
205
+ <div className="w-10 h-10 rounded-xl bg-green-500/10 text-green-500 flex items-center justify-center mb-4">
206
+ <ShieldCheck className="w-5 h-5" />
207
+ </div>
208
+ <h4 className="font-display font-bold text-lg">100% Genuine</h4>
209
+ <p className={`text-xs mt-1 ${darkMode ? "text-slate-400" : "text-slate-500"}`}>
210
+ Authorized dealer of global and local major brands.
211
+ </p>
212
+ </motion.div>
213
+
214
+ {/* Bento Card 4: Location Pin */}
215
+ <motion.div
216
+ variants={itemVariants}
217
+ whileHover={{ y: -4 }}
218
+ className={`col-span-2 p-5 rounded-3xl flex items-center space-x-4 ${
219
+ darkMode ? "glassmorphism" : "glassmorphism-light shadow-md"
220
+ }`}
221
+ >
222
+ <div className="w-12 h-12 rounded-xl bg-red-500/10 text-red-500 flex items-center justify-center shrink-0">
223
+ <MapPin className="w-6 h-6 animate-bounce" />
224
+ </div>
225
+ <div>
226
+ <span className={`text-[10px] font-mono uppercase ${darkMode ? "text-slate-400" : "text-slate-500"}`}>Faisal Town, Block C 1, Lahore</span>
227
+ <h4 className="font-display font-bold text-md mt-0.5">Near Center Flats</h4>
228
+ <p className={`text-xs ${darkMode ? "text-slate-400" : "text-slate-500"}`}>F8G5+H9, Lahore, Pakistan</p>
229
+ </div>
230
+ </motion.div>
231
+ </div>
232
+ </motion.div>
233
+
234
+ {/* Brand Stripe Ribbons Area */}
235
+ <div className="mt-20 pt-8 border-t border-black/5 dark:border-white/5">
236
+ <p className={`text-center font-mono text-xs tracking-widest uppercase mb-6 ${
237
+ darkMode ? "text-slate-400" : "text-slate-500"
238
+ }`}>
239
+ Official Retailer / Authorized Distributor Of Premium Brands
240
+ </p>
241
+
242
+ <div className="relative w-full overflow-hidden whitespace-nowrap py-4">
243
+ {/* Smooth mask on edges */}
244
+ <div className={`absolute top-0 left-0 w-20 h-full z-10 pointer-events-none bg-gradient-to-r ${
245
+ darkMode ? "from-brand-dark to-transparent" : "from-slate-50 to-transparent"
246
+ }`}></div>
247
+ <div className={`absolute top-0 right-0 w-20 h-full z-10 pointer-events-none bg-gradient-to-l ${
248
+ darkMode ? "from-brand-dark to-transparent" : "from-slate-50 to-transparent"
249
+ }`}></div>
250
+
251
+ {/* Marquee scroll loop */}
252
+ <div className="inline-flex space-x-12 animate-marquee-scroll">
253
+ {[...BRANDS, ...BRANDS].map((brand, bIdx) => (
254
+ <div
255
+ key={`${brand.name}-${bIdx}`}
256
+ className={`inline-flex items-center space-x-2 bg-white/5 dark:bg-white/5 px-4 py-2.5 rounded-xl border ${
257
+ brand.hasDirectAccent
258
+ ? "border-brand-orange/40 text-brand-orange"
259
+ : darkMode
260
+ ? "border-white/5 text-slate-300 hover:border-white/25 hover:text-white"
261
+ : "border-slate-200 text-slate-700 hover:border-slate-400 hover:text-slate-900"
262
+ } transition-colors cursor-default`}
263
+ >
264
+ {/* Brand visual emoji / tire mockup symbol */}
265
+ <span className="text-sm font-semibold">⚙️</span>
266
+ <span className="font-display font-bold uppercase tracking-wider text-sm">{brand.name}</span>
267
+ </div>
268
+ ))}
269
+ </div>
270
+ </div>
271
+ </div>
272
+ </div>
273
+ </section>
274
+ );
275
+ }
src/components/InventoryManager.tsx ADDED
@@ -0,0 +1,1311 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useEffect, useRef } from "react";
2
+ import { TireProduct } from "../data";
3
+ import {
4
+ getTireProducts,
5
+ saveTireProduct,
6
+ deleteTireProduct
7
+ } from "../lib/tireService";
8
+ import {
9
+ loginAdmin,
10
+ logoutAdmin,
11
+ checkAuthState,
12
+ createAdminUser,
13
+ bulkUpdatePrices,
14
+ getLowStockProducts,
15
+ onProductsSnapshot,
16
+ exportToJSON,
17
+ importFromJSON,
18
+ isBrowserOnline,
19
+ registerNetworkStateListener,
20
+ uploadProductImage,
21
+ ProductDocument,
22
+ getVisitorLogs,
23
+ VisitorRecord
24
+ } from "../lib/firebaseService";
25
+ import {
26
+ Plus,
27
+ Trash2,
28
+ Edit,
29
+ Database,
30
+ Wrench,
31
+ RefreshCw,
32
+ Sparkles,
33
+ Save,
34
+ X,
35
+ Search,
36
+ Tag,
37
+ Check,
38
+ User,
39
+ Lock,
40
+ LogOut,
41
+ Download,
42
+ Upload,
43
+ AlertTriangle,
44
+ FileSpreadsheet,
45
+ Wifi,
46
+ WifiOff,
47
+ Image as ImageIcon,
48
+ Eye,
49
+ Users,
50
+ Monitor,
51
+ Globe
52
+ } from "lucide-react";
53
+ import { User as AuthUser } from "firebase/auth";
54
+
55
+ interface InventoryManagerProps {
56
+ darkMode: boolean;
57
+ onRefreshProducts: () => void;
58
+ }
59
+
60
+ export default function InventoryManager({ darkMode, onRefreshProducts }: InventoryManagerProps) {
61
+ const [products, setProducts] = useState<TireProduct[]>([]);
62
+ const [loading, setLoading] = useState<boolean>(true);
63
+ const [searchQuery, setSearchQuery] = useState<string>("");
64
+ const [showAddForm, setShowAddForm] = useState<boolean>(false);
65
+ const [editingProduct, setEditingProduct] = useState<any | null>(null);
66
+
67
+ // Connection State
68
+ const [isOnline, setIsOnline] = useState<boolean>(isBrowserOnline());
69
+
70
+ // Authentication State
71
+ const [currentUser, setCurrentUser] = useState<AuthUser | null>(null);
72
+ const [authEmail, setAuthEmail] = useState<string>("");
73
+ const [authPassword, setAuthPassword] = useState<string>("");
74
+ const [isRegisterMode, setIsRegisterMode] = useState<boolean>(false);
75
+ const [authLoading, setAuthLoading] = useState<boolean>(false);
76
+
77
+ // Bulk operation states
78
+ const [bulkPercent, setBulkPercent] = useState<number>(10);
79
+ const [bulkLoading, setBulkLoading] = useState<boolean>(false);
80
+
81
+ // Form State for Adding / Editing
82
+ const [formId, setFormId] = useState<string>("");
83
+ const [formName, setFormName] = useState<string>("");
84
+ const [formBrand, setFormBrand] = useState<string>("");
85
+ const [formSize, setFormSize] = useState<string>("");
86
+ const [formFeature, setFormFeature] = useState<string>("");
87
+ const [formPrice, setFormPrice] = useState<number>(15000);
88
+ const [formStock, setFormStock] = useState<number>(10);
89
+ const [formBadge, setFormBadge] = useState<string>("Hot Selling");
90
+ const [formSegment, setFormSegment] = useState<"hot" | "new" | "famous">("hot");
91
+ const [formImage, setFormImage] = useState<string>("");
92
+ const [formDescription, setFormDescription] = useState<string>("");
93
+
94
+ // Upload state
95
+ const [selectedFile, setSelectedFile] = useState<File | null>(null);
96
+ const [uploadLoading, setUploadLoading] = useState<boolean>(false);
97
+ const fileInputRef = useRef<HTMLInputElement>(null);
98
+
99
+ // Low stock products alert list
100
+ const [lowStockProducts, setLowStockProducts] = useState<ProductDocument[]>([]);
101
+
102
+ // Visitor Analytics State
103
+ const [visitorLogs, setVisitorLogs] = useState<VisitorRecord[]>([]);
104
+ const [visitorStatsLoading, setVisitorStatsLoading] = useState<boolean>(false);
105
+ const [activeAdminTab, setActiveAdminTab] = useState<"products" | "visitors">("products");
106
+
107
+ // Notification Toast log
108
+ const [logMessage, setLogMessage] = useState<{ text: string; type: "success" | "error" | "info" } | null>(null);
109
+
110
+ // Synchronize visitor analytics list when admin is authenticated
111
+ useEffect(() => {
112
+ if (currentUser) {
113
+ fetchVisitorAnalytics();
114
+ } else {
115
+ setVisitorLogs([]);
116
+ }
117
+ }, [currentUser]);
118
+
119
+ const fetchVisitorAnalytics = async () => {
120
+ setVisitorStatsLoading(true);
121
+ try {
122
+ const logs = await getVisitorLogs();
123
+ setVisitorLogs(logs);
124
+ } catch (err) {
125
+ console.error("Failed to load visitor logs:", err);
126
+ } finally {
127
+ setVisitorStatsLoading(false);
128
+ }
129
+ };
130
+
131
+ // Trigger custom state snapshots and online listener hooks
132
+ useEffect(() => {
133
+ // 1. Real-time online checker
134
+ const removeNetworkListener = registerNetworkStateListener((online) => {
135
+ setIsOnline(online);
136
+ triggerToast(online ? "System online. Real-time databases synced." : "Workspace offline. Some writes may be queued.", online ? "success" : "error");
137
+ });
138
+
139
+ // 2. Auth listener
140
+ const removeAuthListener = checkAuthState((user) => {
141
+ setCurrentUser(user);
142
+ });
143
+
144
+ // 3. Real-time Snapshot listeners
145
+ const removeSnapshotListener = onProductsSnapshot((snapshotList) => {
146
+ const mapped = snapshotList.map(p => ({
147
+ id: p.id,
148
+ name: p.name,
149
+ brand: p.brand,
150
+ size: p.size,
151
+ feature: p.feature,
152
+ price: p.price,
153
+ badge: p.badge || (p.category === "hot-selling" ? "Hot Selling" : p.category === "new-brands" ? "New Arrival" : "Famous"),
154
+ segment: p.segment || (p.category === "hot-selling" ? "hot" : p.category === "new-brands" ? "new" : "famous"),
155
+ image: p.image || p.imageUrl,
156
+ stock: p.stock || 0,
157
+ description: p.description || ""
158
+ }));
159
+ setProducts(mapped);
160
+ setLoading(false);
161
+
162
+ // Update low stock list
163
+ getLowStockProducts(5).then(lows => setLowStockProducts(lows)).catch(err => console.log(err));
164
+ });
165
+
166
+ return () => {
167
+ removeNetworkListener();
168
+ removeAuthListener();
169
+ removeSnapshotListener();
170
+ };
171
+ }, []);
172
+
173
+ const triggerToast = (text: string, type: "success" | "error" | "info" = "success") => {
174
+ setLogMessage({ text, type });
175
+ setTimeout(() => setLogMessage(null), 4000);
176
+ };
177
+
178
+ // Login handler
179
+ const handleAuthSubmit = async (e: React.FormEvent) => {
180
+ e.preventDefault();
181
+ if (!authEmail || !authPassword) {
182
+ triggerToast("Please supply email and password", "error");
183
+ return;
184
+ }
185
+ setAuthLoading(true);
186
+ try {
187
+ if (isRegisterMode) {
188
+ await createAdminUser(authEmail, authPassword);
189
+ triggerToast("Admin created and authenticated successfully!", "success");
190
+ } else {
191
+ await loginAdmin(authEmail, authPassword);
192
+ triggerToast("Authenticated successfully as Administrator!", "success");
193
+ }
194
+ setAuthEmail("");
195
+ setAuthPassword("");
196
+ } catch (err: any) {
197
+ triggerToast(err.message || "Authentication attempt rejected.", "error");
198
+ } finally {
199
+ setAuthLoading(false);
200
+ }
201
+ };
202
+
203
+ const handleSignOut = async () => {
204
+ try {
205
+ await logoutAdmin();
206
+ triggerToast("Signed out successfully.", "info");
207
+ } catch (err) {
208
+ triggerToast("Signout failed.", "error");
209
+ }
210
+ };
211
+
212
+ // Pre-fill edit form
213
+ const handleEditClick = (prod: any) => {
214
+ setEditingProduct(prod);
215
+ setFormId(prod.id);
216
+ setFormName(prod.name);
217
+ setFormBrand(prod.brand);
218
+ setFormSize(prod.size);
219
+ setFormFeature(prod.feature);
220
+ setFormPrice(prod.price);
221
+ setFormStock(prod.stock || 10);
222
+ setFormBadge(prod.badge);
223
+ setFormSegment(prod.segment);
224
+ setFormImage(prod.image);
225
+ setFormDescription(prod.description);
226
+ setShowAddForm(true); // Open the form drawer
227
+ window.scrollTo({ top: 120, behavior: "smooth" });
228
+ };
229
+
230
+ // Reset/Clear Form Fields
231
+ const clearForm = () => {
232
+ setEditingProduct(null);
233
+ setFormId("");
234
+ setFormName("");
235
+ setFormBrand("");
236
+ setFormSize("");
237
+ setFormFeature("");
238
+ setFormPrice(15000);
239
+ setFormStock(10);
240
+ setFormBadge("New Arrival");
241
+ setFormSegment("new");
242
+ setFormImage("");
243
+ setFormDescription("");
244
+ setSelectedFile(null);
245
+ };
246
+
247
+ // Handle storage file uploads
248
+ const handleFileUploadChange = (e: React.ChangeEvent<HTMLInputElement>) => {
249
+ if (e.target.files && e.target.files[0]) {
250
+ setSelectedFile(e.target.files[0]);
251
+ }
252
+ };
253
+
254
+ // Trigger storage upload
255
+ const handleImageUploadToStorage = async () => {
256
+ if (!selectedFile) {
257
+ triggerToast("Please select a file to upload first.", "info");
258
+ return;
259
+ }
260
+ if (!formName) {
261
+ triggerToast("Please define the Tyre Name first to tag your image correctly.", "info");
262
+ return;
263
+ }
264
+ setUploadLoading(true);
265
+ try {
266
+ const responseUrl = await uploadProductImage(selectedFile, formName);
267
+ setFormImage(responseUrl);
268
+ triggerToast("Image asset uploaded to Storage bucket successfully!", "success");
269
+ setSelectedFile(null);
270
+ } catch (err: any) {
271
+ triggerToast(err.message || "Failed to upload image. Writes are restricted.", "error");
272
+ } finally {
273
+ setUploadLoading(false);
274
+ }
275
+ };
276
+
277
+ // Create / Save Handler (triggers Firebase writing)
278
+ const handleSaveProduct = async (e: React.FormEvent) => {
279
+ e.preventDefault();
280
+ if (!formName || !formBrand || !formSize || !formPrice) {
281
+ triggerToast("Please fill in all mandatory inputs.", "error");
282
+ return;
283
+ }
284
+
285
+ if (!currentUser) {
286
+ triggerToast("Unauthorized operation. Please log in as Administrator first.", "error");
287
+ return;
288
+ }
289
+
290
+ const imgUrl = formImage.trim() || "https://images.unsplash.com/photo-1542282088-fe8426682b8f?auto=format&fit=crop&q=80&w=600";
291
+ const customId = formId || `custom-${Date.now()}`;
292
+
293
+ const parsedPrice = parseInt(String(formPrice), 10);
294
+ const parsedStock = parseInt(String(formStock), 10);
295
+ if (isNaN(parsedPrice) || parsedPrice <= 0) {
296
+ triggerToast("Price must be a valid positive integer.", "error");
297
+ return;
298
+ }
299
+
300
+ const categoryMapped = formSegment === "hot" ? "hot-selling" : formSegment === "new" ? "new-brands" : "famous";
301
+
302
+ const payload: TireProduct & { stock: number } = {
303
+ id: customId,
304
+ name: formName,
305
+ brand: formBrand,
306
+ size: formSize,
307
+ feature: formFeature,
308
+ price: parsedPrice,
309
+ badge: formBadge,
310
+ segment: formSegment,
311
+ image: imgUrl,
312
+ stock: parsedStock,
313
+ description: formDescription.trim() || `Premium high-durability tire configured specifically for the local roads of Lahore.`
314
+ };
315
+
316
+ try {
317
+ await saveTireProduct(payload);
318
+ triggerToast(
319
+ editingProduct
320
+ ? `Successfully saved modifications for '${payload.name}'!`
321
+ : `Successfully registered and seeded '${payload.name}' into the database!`,
322
+ "success"
323
+ );
324
+ clearForm();
325
+ setShowAddForm(false);
326
+ onRefreshProducts(); // Trigger app refetch
327
+ } catch (err: any) {
328
+ console.error(err);
329
+ triggerToast(`Operation rejected. Verify schema rules or Admin privileges.`, "error");
330
+ }
331
+ };
332
+
333
+ // Delete Handler
334
+ const handleDeleteClick = async (id: string, name: string) => {
335
+ if (!currentUser) {
336
+ triggerToast("Authentication required. Please log in to complete deletions.", "error");
337
+ return;
338
+ }
339
+ if (!window.confirm(`Are you absolutely sure you want to delete '${name}'?`)) {
340
+ return;
341
+ }
342
+
343
+ try {
344
+ await deleteTireProduct(id);
345
+ triggerToast(`Deleted '${name}' from Firestore!`, "info");
346
+ onRefreshProducts();
347
+ } catch (err) {
348
+ triggerToast("Restricted write. Confirm your role.", "error");
349
+ }
350
+ };
351
+
352
+ // Bulk rate updater
353
+ const handleBulkUpdate = async () => {
354
+ if (!currentUser) {
355
+ triggerToast("Admin authentication is required for bulk price variations.", "error");
356
+ return;
357
+ }
358
+ if (!window.confirm(`Are you sure you want to alter ALL inventory prices by ${bulkPercent}%?`)) {
359
+ return;
360
+ }
361
+ setBulkLoading(true);
362
+ try {
363
+ await bulkUpdatePrices(bulkPercent);
364
+ triggerToast(`Successfully adjusted all catalog rates in bulk by ${bulkPercent}%!`, "success");
365
+ onRefreshProducts();
366
+ } catch (err: any) {
367
+ triggerToast(err.message || "Bulk operation failed. Check rules permissions.", "error");
368
+ } finally {
369
+ setBulkLoading(false);
370
+ }
371
+ };
372
+
373
+ // JSON backup download
374
+ const handleExportJSON = async () => {
375
+ try {
376
+ await exportToJSON();
377
+ triggerToast("Product catalog downloaded successfully!", "success");
378
+ } catch (e) {
379
+ triggerToast("Export failed.", "error");
380
+ }
381
+ };
382
+
383
+ // JSON backup upload
384
+ const handleImportJSONChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
385
+ if (!currentUser) {
386
+ triggerToast("Admin authentication is required to run database imports.", "error");
387
+ return;
388
+ }
389
+ if (e.target.files && e.target.files[0]) {
390
+ const file = e.target.files[0];
391
+ if (!window.confirm(`This will import products from '${file.name}'. Duplicate profiles will be ignored. Proceed?`)) {
392
+ return;
393
+ }
394
+ try {
395
+ const outcomes = await importFromJSON(file);
396
+ triggerToast(`Import completed with success! Added: ${outcomes.imported} items, Ignored (Duplicates): ${outcomes.duplicates} items.`, "success");
397
+ onRefreshProducts();
398
+ } catch (err: any) {
399
+ triggerToast(err.message || "Failed to process database import file.", "error");
400
+ }
401
+ }
402
+ };
403
+
404
+ // Filter local inventory display list
405
+ const filteredProducts = products.filter((p) => {
406
+ const term = searchQuery.toLowerCase();
407
+ return (
408
+ p.name.toLowerCase().includes(term) ||
409
+ p.brand.toLowerCase().includes(term) ||
410
+ p.size.toLowerCase().includes(term) ||
411
+ p.segment.toLowerCase().includes(term)
412
+ );
413
+ });
414
+
415
+ return (
416
+ <div className="space-y-8">
417
+ {/* Toast Alert popup banner */}
418
+ {logMessage && (
419
+ <div className={`fixed bottom-6 right-6 z-55 max-w-sm rounded-2xl p-4 shadow-2xl border transition-all duration-300 flex items-center space-x-3 text-sm font-semibold animate-bounce ${
420
+ logMessage.type === "success"
421
+ ? "bg-green-600 text-white border-green-500"
422
+ : logMessage.type === "error"
423
+ ? "bg-red-600 text-white border-red-500"
424
+ : "bg-blue-600 text-white border-blue-500"
425
+ }`}>
426
+ <span>{logMessage.type === "success" ? "✔" : "ℹ"}</span>
427
+ <p className="flex-1">{logMessage.text}</p>
428
+ </div>
429
+ )}
430
+
431
+ {/* Connection Checker & Admin Identity Banner */}
432
+ <div className={`p-4 rounded-2xl border flex flex-col sm:flex-row items-center justify-between gap-4 font-mono text-xs ${
433
+ darkMode ? "bg-white/5 border-white/10" : "bg-white border-slate-200"
434
+ }`}>
435
+ <div className="flex items-center space-x-2">
436
+ {isOnline ? (
437
+ <span className="flex items-center space-x-1.5 text-emerald-500">
438
+ <Wifi className="w-4 h-4" />
439
+ <span className="font-bold">SYSTEM ONLINE (bahaduralimunnabhai@gmail.com)</span>
440
+ </span>
441
+ ) : (
442
+ <span className="flex items-center space-x-1.5 text-rose-500">
443
+ <WifiOff className="w-4 h-4 animate-pulse" />
444
+ <span className="font-bold">SYSTEM OFFLINE (LOCAL CACHE SHIELDS ACTIVE)</span>
445
+ </span>
446
+ )}
447
+ </div>
448
+
449
+ <div className="flex items-center space-x-3">
450
+ {currentUser ? (
451
+ <div className="flex items-center space-x-2">
452
+ <span className="text-emerald-400 font-semibold truncate max-w-xs">
453
+ 👤 Admin: {currentUser.email}
454
+ </span>
455
+ <button
456
+ onClick={handleSignOut}
457
+ className="bg-red-500/10 hover:bg-red-500/20 text-red-400 p-1.5 px-3 rounded-lg border border-red-500/20 flex items-center space-x-1 transition-all"
458
+ >
459
+ <LogOut className="w-3.5 h-3.5" />
460
+ <span>Logout</span>
461
+ </button>
462
+ </div>
463
+ ) : (
464
+ <span className="text-amber-500 font-semibold flex items-center space-x-1">
465
+ <Lock className="w-3.5 h-3.5" />
466
+ <span>Role: Unauthenticated Client (Sign in below to write to DB)</span>
467
+ </span>
468
+ )}
469
+ </div>
470
+ </div>
471
+
472
+ {/* Admin Credentials Login Form Panel (when not logged in) */}
473
+ {!currentUser && (
474
+ <form
475
+ onSubmit={handleAuthSubmit}
476
+ className={`p-6 rounded-3xl border ${
477
+ darkMode ? "bg-[#1c1c24] border-white/5" : "bg-white border-slate-200 shadow-sm"
478
+ }`}
479
+ id="admin-login-form"
480
+ >
481
+ <div className="flex items-center space-x-2 mb-4">
482
+ <Lock className="w-5 h-5 text-brand-orange" />
483
+ <h3 className="font-display font-bold text-base">
484
+ {isRegisterMode ? "Create New Security Admin Account" : "Registered Administrator Authentication"}
485
+ </h3>
486
+ </div>
487
+ <p className="text-xs text-slate-400 mb-4 leading-relaxed">
488
+ By default, Firestore security rules block unauthorized browser requests. Use this panel to sign in with an administrator email and password, or quickly register a credentials profile below!
489
+ </p>
490
+
491
+ <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
492
+ <div>
493
+ <label className="text-[10px] uppercase font-mono tracking-wider block mb-1">Email Username</label>
494
+ <input
495
+ type="email"
496
+ required
497
+ placeholder="e.g. admin@haiderbrothers.com"
498
+ value={authEmail}
499
+ onChange={(e) => setAuthEmail(e.target.value)}
500
+ className={`w-full p-2.5 rounded-xl text-xs border focus:outline-none focus:ring-1 focus:ring-brand-orange ${
501
+ darkMode ? "bg-black/35 border-white/10 text-white" : "bg-slate-50 border-slate-200"
502
+ }`}
503
+ id="admin-email-field"
504
+ />
505
+ </div>
506
+
507
+ <div>
508
+ <label className="text-[10px] uppercase font-mono tracking-wider block mb-1">Secure Password</label>
509
+ <input
510
+ type="password"
511
+ required
512
+ placeholder="••••••••"
513
+ value={authPassword}
514
+ onChange={(e) => setAuthPassword(e.target.value)}
515
+ className={`w-full p-2.5 rounded-xl text-xs border focus:outline-none focus:ring-1 focus:ring-brand-orange ${
516
+ darkMode ? "bg-black/35 border-white/10 text-white" : "bg-slate-50 border-slate-200"
517
+ }`}
518
+ id="admin-password-field"
519
+ />
520
+ </div>
521
+ </div>
522
+
523
+ <div className="mt-4 flex flex-col sm:flex-row items-stretch sm:items-center justify-between gap-3">
524
+ <button
525
+ type="button"
526
+ onClick={() => setIsRegisterMode(!isRegisterMode)}
527
+ className="text-xs text-brand-orange hover:underline self-start text-left"
528
+ >
529
+ {isRegisterMode ? "Already have an account? Sign in here" : "Don't have an admin credential? Register one instantly"}
530
+ </button>
531
+
532
+ <button
533
+ type="submit"
534
+ disabled={authLoading}
535
+ className="bg-brand-orange hover:bg-brand-orange-dark text-white font-bold p-2.5 px-6 rounded-xl text-xs flex items-center justify-center space-x-1.5 shadow-md shadow-brand-orange/20"
536
+ id="admin-auth-btn"
537
+ >
538
+ {authLoading ? (
539
+ <RefreshCw className="w-3.5 h-3.5 animate-spin" />
540
+ ) : (
541
+ <User className="w-3.5 h-3.5" />
542
+ )}
543
+ <span>{isRegisterMode ? "Create Admin Credentials" : "Authorize Session"}</span>
544
+ </button>
545
+ </div>
546
+ </form>
547
+ )}
548
+
549
+ {/* KPI stats display */}
550
+ <div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
551
+ <div className={`p-5 rounded-2xl border flex items-center space-x-4 transition-all ${
552
+ darkMode ? "bg-[#1c1c24] border-white/5" : "bg-white border-slate-200/60 shadow-xs"
553
+ }`}>
554
+ <div className="p-3 bg-brand-orange/10 rounded-xl text-brand-orange font-bold text-lg">
555
+ <Database className="w-5 h-5" />
556
+ </div>
557
+ <div>
558
+ <span className={`text-[10px] font-mono block ${darkMode ? "text-slate-400" : "text-slate-500"}`}>
559
+ FIRESTORE PRODUCTS COUNT
560
+ </span>
561
+ <span className="text-xl sm:text-2xl font-display font-black text-brand-orange leading-none">
562
+ {products.length} Items
563
+ </span>
564
+ </div>
565
+ </div>
566
+
567
+ <div className={`p-5 rounded-2xl border flex items-center space-x-4 transition-all ${
568
+ darkMode ? "bg-[#1c1c24] border-white/5" : "bg-white border-slate-200/60 shadow-xs"
569
+ }`}>
570
+ <div className="p-3 bg-green-500/10 rounded-xl text-green-500 font-bold text-lg">
571
+ <Tag className="w-5 h-5" />
572
+ </div>
573
+ <div>
574
+ <span className={`text-[10px] font-mono block ${darkMode ? "text-slate-400" : "text-slate-500"}`}>
575
+ AVERAGE WHOLESALE RATE
576
+ </span>
577
+ <span className="text-xl sm:text-2xl font-display font-black text-green-500 leading-none">
578
+ PKR {Math.round(products.reduce((acc, current) => acc + current.price, 0) / (products.length || 1)).toLocaleString()}
579
+ </span>
580
+ </div>
581
+ </div>
582
+
583
+ <div className={`p-5 rounded-2xl border flex items-center space-x-4 transition-all ${
584
+ darkMode ? "bg-[#1c1c24] border-white/5" : "bg-white border-slate-200/60 shadow-xs"
585
+ }`}>
586
+ <div className="p-3 bg-indigo-500/10 rounded-xl text-indigo-500 font-bold text-lg">
587
+ <AlertTriangle className="w-5 h-5" />
588
+ </div>
589
+ <div>
590
+ <span className={`text-[10px] font-mono block ${darkMode ? "text-slate-400" : "text-slate-500"}`}>
591
+ CRITICAL RESTOCK LOG (STOCK &lt; 5)
592
+ </span>
593
+ <span className="text-xl sm:text-2xl font-display font-black text-indigo-500 leading-none">
594
+ {lowStockProducts.length} Items Alerted
595
+ </span>
596
+ </div>
597
+ </div>
598
+ </div>
599
+
600
+ {/* Bulk editing & backup portability deck (visible when logged in) */}
601
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
602
+ {/* Module A: Bulk Adjustments */}
603
+ <div className={`p-5 rounded-3xl border flex flex-col justify-between ${
604
+ darkMode ? "bg-[#1c1c24] border-white/5" : "bg-white border-slate-200 shadow-sm"
605
+ }`}>
606
+ <div>
607
+ <h4 className="font-display font-bold text-sm tracking-tight flex items-center space-x-1">
608
+ <Tag className="w-4 h-4 text-emerald-500" />
609
+ <span>Bulk Margin Adjustment Command</span>
610
+ </h4>
611
+ <p className="text-xs text-slate-400 mt-1 leading-relaxed">
612
+ Instantly increment or decrement ALL prices of tires stored in your Firestore database using a single batch payload.
613
+ </p>
614
+
615
+ <div className="mt-4 space-y-3">
616
+ <label className="text-[10px] font-mono text-slate-400 block">
617
+ Adjust Rate (Percentage % Value): <span className="text-brand-orange font-bold font-sans text-sm">{bulkPercent > 0 ? `+${bulkPercent}` : bulkPercent}%</span>
618
+ </label>
619
+ <div className="flex items-center space-x-3">
620
+ <span className="text-[10px] text-slate-500 font-mono">-50%</span>
621
+ <input
622
+ type="range"
623
+ min="-50"
624
+ max="50"
625
+ step="5"
626
+ value={bulkPercent}
627
+ onChange={(e) => setBulkPercent(parseInt(e.target.value, 10))}
628
+ className="w-full accent-brand-orange bg-slate-200 dark:bg-neutral-800 rounded-lg"
629
+ />
630
+ <span className="text-[10px] text-slate-500 font-mono">+50%</span>
631
+ </div>
632
+ </div>
633
+ </div>
634
+
635
+ <div className="mt-6">
636
+ <button
637
+ onClick={handleBulkUpdate}
638
+ disabled={bulkLoading || !currentUser}
639
+ className={`w-full p-2.5 rounded-xl text-xs font-bold font-sans transition-all flex items-center justify-center space-x-1.5 ${
640
+ currentUser
641
+ ? "bg-[#10b981] text-white hover:bg-emerald-600 shadow-md shadow-emerald-500/10"
642
+ : "bg-slate-300 dark:bg-neutral-800 text-slate-500 cursor-not-allowed"
643
+ }`}
644
+ >
645
+ {bulkLoading ? (
646
+ <RefreshCw className="w-4 h-4 animate-spin" />
647
+ ) : (
648
+ <Wrench className="w-4 h-4" />
649
+ )}
650
+ <span>{currentUser ? `Apply ${bulkPercent}% Price Adjustment` : "Login to enable bulk command"}</span>
651
+ </button>
652
+ </div>
653
+ </div>
654
+
655
+ {/* Module B: JSON Data Portability (Export & Import) */}
656
+ <div className={`p-5 rounded-3xl border flex flex-col justify-between ${
657
+ darkMode ? "bg-[#1c1c24] border-white/5" : "bg-white border-slate-200 shadow-sm"
658
+ }`}>
659
+ <div>
660
+ <h4 className="font-display font-bold text-sm tracking-tight flex items-center space-x-1">
661
+ <FileSpreadsheet className="w-4 h-4 text-indigo-500" />
662
+ <span>Firebase Backup Data Portability</span>
663
+ </h4>
664
+ <p className="text-xs text-slate-400 mt-1 leading-relaxed">
665
+ Download your complete inventory collection as a standard JSON file, or restore assets from backups with automatic duplicate detection.
666
+ </p>
667
+ </div>
668
+
669
+ <div className="grid grid-cols-2 gap-3 mt-6">
670
+ {/* Export */}
671
+ <button
672
+ onClick={handleExportJSON}
673
+ className={`p-3 rounded-xl border text-xs font-bold transition-all flex items-center justify-center space-x-1.5 ${
674
+ darkMode
675
+ ? "border-indigo-500/20 bg-indigo-500/10 text-indigo-400 hover:bg-indigo-500/20"
676
+ : "border-indigo-200 bg-indigo-50 text-indigo-800 hover:bg-indigo-100"
677
+ }`}
678
+ >
679
+ <Download className="w-4 h-4" />
680
+ <span>Export Backup</span>
681
+ </button>
682
+
683
+ {/* Import file mask */}
684
+ <div className="relative">
685
+ <input
686
+ type="file"
687
+ accept=".json"
688
+ disabled={!currentUser}
689
+ onChange={handleImportJSONChange}
690
+ className="absolute inset-0 opacity-0 cursor-pointer disabled:cursor-not-allowed"
691
+ id="file-import-input"
692
+ />
693
+ <span className={`w-full p-3 rounded-xl border text-xs font-bold transition-all flex items-center justify-center space-x-1.5 ${
694
+ currentUser
695
+ ? darkMode
696
+ ? "border-violet-500/20 bg-violet-500/10 text-violet-400 hover:bg-violet-500/20"
697
+ : "border-violet-200 bg-violet-50 text-violet-800 hover:bg-violet-100"
698
+ : "border-slate-300 dark:border-neutral-800 bg-slate-300/10 dark:bg-neutral-800/25 text-slate-500 cursor-not-allowed"
699
+ }`}>
700
+ <Upload className="w-4 h-4" />
701
+ <span>Upload JSON</span>
702
+ </span>
703
+ </div>
704
+ </div>
705
+ </div>
706
+ </div>
707
+
708
+ {/* Button controls */}
709
+ <div className="flex flex-col sm:flex-row gap-3 justify-between items-stretch sm:items-center">
710
+ <div className="flex items-center gap-2">
711
+ {currentUser && (
712
+ <button
713
+ onClick={() => { clearForm(); setShowAddForm(!showAddForm); }}
714
+ className="bg-brand-orange hover:bg-brand-orange-dark text-white font-bold px-4 py-2.5 rounded-xl text-xs flex items-center gap-1.5 transition-all shadow-md shadow-brand-orange/20"
715
+ id="admin-new-tyre-btn"
716
+ >
717
+ {showAddForm ? <X className="w-4 h-4" /> : <Plus className="w-4 h-4" />}
718
+ <span>{showAddForm ? "Close Form Panel" : "Add New Tyre"}</span>
719
+ </button>
720
+ )}
721
+
722
+ <button
723
+ onClick={() => onRefreshProducts()}
724
+ className={`p-2.5 rounded-xl border flex items-center gap-1 text-xs transition-all ${
725
+ darkMode ? "border-white/10 hover:bg-white/5 text-slate-300" : "border-slate-200 hover:bg-slate-50 text-slate-700"
726
+ }`}
727
+ title="Refresh list"
728
+ id="admin-sync-btn"
729
+ >
730
+ <RefreshCw className="w-3.5 h-3.5" />
731
+ <span>Sync DB</span>
732
+ </button>
733
+ </div>
734
+
735
+ {/* Display badge for low stock warning items */}
736
+ {lowStockProducts.length > 0 && (
737
+ <div className="bg-amber-500/10 border border-amber-500/20 text-amber-500 py-2.5 px-3.5 rounded-xl text-xs font-semibold flex items-center space-x-1.5">
738
+ <AlertTriangle className="w-4.5 h-4.5 animate-pulse" />
739
+ <span>Stock alerts: {lowStockProducts.length} items cataloged under 5 units!</span>
740
+ </div>
741
+ )}
742
+ </div>
743
+
744
+ {/* Add / Edit Form Panel */}
745
+ {showAddForm && currentUser && (
746
+ <form
747
+ onSubmit={handleSaveProduct}
748
+ className={`p-6 sm:p-8 rounded-2xl border transition-all ${
749
+ darkMode ? "glassmorphism box-glow" : "glassmorphism-light shadow-xl"
750
+ }`}
751
+ id="tire-product-form"
752
+ >
753
+ <div className="flex items-center justify-between pb-4 border-b border-black/5 dark:border-white/5 mb-6">
754
+ <h4 className="font-display font-bold text-md flex items-center gap-2">
755
+ <Sparkles className="w-5 h-5 text-brand-orange" />
756
+ <span>{editingProduct ? `Edit Tyre Configuration: ${editingProduct.name}` : "Create New Tyre Product"}</span>
757
+ </h4>
758
+ <button
759
+ type="button"
760
+ onClick={clearForm}
761
+ className={`text-xs ${darkMode ? "text-slate-400 hover:text-white" : "text-slate-600 hover:text-black"}`}
762
+ >
763
+ Clear Values
764
+ </button>
765
+ </div>
766
+
767
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
768
+ {/* Tyre Name */}
769
+ <div>
770
+ <label className="text-xs font-mono font-semibold block mb-1">Tyre Name (Model) *</label>
771
+ <input
772
+ type="text"
773
+ required
774
+ placeholder="e.g. Pilot Sport 5"
775
+ value={formName}
776
+ onChange={(e) => setFormName(e.target.value)}
777
+ className={`w-full p-2.5 rounded-xl text-xs border focus:ring-1 focus:ring-brand-orange focus:outline-none ${
778
+ darkMode ? "bg-black/35 border-white/10 text-white" : "bg-white border-slate-200 text-slate-800"
779
+ }`}
780
+ />
781
+ </div>
782
+
783
+ {/* Brand */}
784
+ <div>
785
+ <label className="text-xs font-mono font-semibold block mb-1">Brand (Manufacturer) *</label>
786
+ <input
787
+ type="text"
788
+ required
789
+ placeholder="e.g. Michelin"
790
+ value={formBrand}
791
+ onChange={(e) => setFormBrand(e.target.value)}
792
+ className={`w-full p-2.5 rounded-xl text-xs border focus:ring-1 focus:ring-brand-orange focus:outline-none ${
793
+ darkMode ? "bg-black/35 border-white/10 text-white" : "bg-white border-slate-200 text-slate-800"
794
+ }`}
795
+ />
796
+ </div>
797
+
798
+ {/* Size */}
799
+ <div>
800
+ <label className="text-xs font-mono font-semibold block mb-1">Tyre Size Code *</label>
801
+ <input
802
+ type="text"
803
+ required
804
+ placeholder="e.g. 205/55R16"
805
+ value={formSize}
806
+ onChange={(e) => setFormSize(e.target.value)}
807
+ className={`w-full p-2.5 rounded-xl text-xs border focus:ring-1 focus:ring-brand-orange focus:outline-none ${
808
+ darkMode ? "bg-black/35 border-white/10 text-white" : "bg-white border-slate-200 text-slate-800"
809
+ }`}
810
+ />
811
+ </div>
812
+
813
+ {/* Feature Highlight */}
814
+ <div>
815
+ <label className="text-xs font-mono font-semibold block mb-1">Highlight Feature *</label>
816
+ <input
817
+ type="text"
818
+ required
819
+ placeholder="e.g. Comfort ride and wet braking compound"
820
+ value={formFeature}
821
+ onChange={(e) => setFormFeature(e.target.value)}
822
+ className={`w-full p-2.5 rounded-xl text-xs border focus:ring-1 focus:ring-brand-orange focus:outline-none ${
823
+ darkMode ? "bg-black/35 border-white/10 text-white" : "bg-white border-slate-200 text-slate-800"
824
+ }`}
825
+ />
826
+ </div>
827
+
828
+ {/* Price (PKR) */}
829
+ <div>
830
+ <label className="text-xs font-mono font-semibold block mb-1">Price (PKR Rate) *</label>
831
+ <input
832
+ type="number"
833
+ required
834
+ min={1}
835
+ placeholder="e.g. 15000"
836
+ value={formPrice}
837
+ onChange={(e) => setFormPrice(parseInt(e.target.value, 10) || 15000)}
838
+ className={`w-full p-2.5 rounded-xl text-xs border focus:ring-1 focus:ring-brand-orange focus:outline-none ${
839
+ darkMode ? "bg-black/35 border-white/10 text-white" : "bg-white border-slate-200 text-slate-800"
840
+ }`}
841
+ />
842
+ </div>
843
+
844
+ {/* Segment Type */}
845
+ <div>
846
+ <label className="text-xs font-mono font-semibold block mb-1">Segment Category *</label>
847
+ <select
848
+ required
849
+ value={formSegment}
850
+ onChange={(e) => {
851
+ setFormSegment(e.target.value as any);
852
+ if (e.target.value === "hot") setFormBadge("Hot Selling");
853
+ else if (e.target.value === "new") setFormBadge("New Arrival");
854
+ else setFormBadge("Famous");
855
+ }}
856
+ className={`w-full p-2.5 rounded-xl text-xs border focus:ring-1 focus:ring-brand-orange focus:outline-none ${
857
+ darkMode ? "bg-black/35 border-white/10 text-white" : "bg-white border-slate-200 text-slate-800"
858
+ }`}
859
+ >
860
+ <option value="hot">Hot Selling</option>
861
+ <option value="new">New Arrival</option>
862
+ <option value="famous">Famous Bestseller</option>
863
+ </select>
864
+ </div>
865
+
866
+ {/* Stock Levels */}
867
+ <div>
868
+ <label className="text-xs font-mono font-semibold block mb-1">Showroom Stock Units *</label>
869
+ <input
870
+ type="number"
871
+ required
872
+ min={0}
873
+ placeholder="e.g. 10"
874
+ value={formStock}
875
+ onChange={(e) => setFormStock(parseInt(e.target.value, 10) || 0)}
876
+ className={`w-full p-2.5 rounded-xl text-xs border focus:ring-1 focus:ring-brand-orange focus:outline-none ${
877
+ darkMode ? "bg-black/35 border-white/10 text-white" : "bg-white border-slate-200 text-slate-800"
878
+ }`}
879
+ />
880
+ </div>
881
+
882
+ {/* Image path */}
883
+ <div>
884
+ <label className="text-xs font-mono font-semibold block mb-1">Image Address URL</label>
885
+ <input
886
+ type="text"
887
+ placeholder="Leave blank to upload file instead"
888
+ value={formImage}
889
+ onChange={(e) => setFormImage(e.target.value)}
890
+ className={`w-full p-2.5 rounded-xl text-xs border focus:ring-1 focus:ring-brand-orange focus:outline-none ${
891
+ darkMode ? "bg-black/35 border-white/10 text-white" : "bg-white border-slate-200 text-slate-800"
892
+ }`}
893
+ />
894
+ </div>
895
+
896
+ {/* Firebase Storage Image Upload module */}
897
+ <div>
898
+ <label className="text-xs font-mono font-semibold block mb-1">Upload To Storage Bucket</label>
899
+ <div className="flex items-center space-x-1">
900
+ <input
901
+ type="file"
902
+ accept="image/*"
903
+ onChange={handleFileUploadChange}
904
+ ref={fileInputRef}
905
+ className="hidden"
906
+ />
907
+ <button
908
+ type="button"
909
+ onClick={() => fileInputRef.current?.click()}
910
+ className={`flex-1 p-2.5 rounded-xl text-[10px] font-bold border flex items-center justify-center space-x-1 ${
911
+ darkMode ? "border-white/10 bg-white/5 text-slate-300" : "border-slate-200 bg-slate-50 text-slate-700"
912
+ }`}
913
+ >
914
+ <ImageIcon className="w-3.5 h-3.5 text-brand-orange" />
915
+ <span className="truncate">{selectedFile ? selectedFile.name : "Select Asset"}</span>
916
+ </button>
917
+ {selectedFile && (
918
+ <button
919
+ type="button"
920
+ onClick={handleImageUploadToStorage}
921
+ disabled={uploadLoading}
922
+ className="p-2.5 bg-brand-orange text-white text-[10px] font-bold rounded-xl flex items-center space-x-0.5"
923
+ >
924
+ {uploadLoading ? <RefreshCw className="w-3 h-3 animate-spin" /> : <Upload className="w-3 h-3" />}
925
+ <span>Upload</span>
926
+ </button>
927
+ )}
928
+ </div>
929
+ </div>
930
+
931
+ {/* Display Badge */}
932
+ <div>
933
+ <label className="text-xs font-mono block mb-1">Display Flag Badge</label>
934
+ <input
935
+ type="text"
936
+ value={formBadge}
937
+ onChange={(e) => setFormBadge(e.target.value)}
938
+ className={`w-full p-2.5 rounded-xl text-xs border focus:ring-1 focus:ring-brand-orange focus:outline-none ${
939
+ darkMode ? "bg-black/35 border-white/10 text-white" : "bg-white border-slate-200 text-slate-800"
940
+ }`}
941
+ />
942
+ </div>
943
+
944
+ {/* Detailed Description */}
945
+ <div className="md:col-span-2">
946
+ <label className="text-xs font-mono font-semibold block mb-1">Detailed Commercial Description</label>
947
+ <textarea
948
+ rows={2}
949
+ placeholder="Provide details about speed rating, mileage targets, and wet grip attributes..."
950
+ value={formDescription}
951
+ onChange={(e) => setFormDescription(e.target.value)}
952
+ className={`w-full p-2.5 rounded-xl text-xs border focus:ring-1 focus:ring-brand-orange focus:outline-none ${
953
+ darkMode ? "bg-black/35 border-white/10 text-white" : "bg-white border-slate-200 text-slate-800"
954
+ }`}
955
+ />
956
+ </div>
957
+ </div>
958
+
959
+ <div className="mt-6 flex justify-end space-x-2">
960
+ <button
961
+ type="button"
962
+ onClick={() => { clearForm(); setShowAddForm(false); }}
963
+ className={`px-4 py-2 rounded-xl text-xs font-semibold ${
964
+ darkMode ? "bg-white/5 hover:bg-white/10 text-slate-300" : "bg-slate-100 hover:bg-slate-200 text-slate-700"
965
+ }`}
966
+ >
967
+ Cancel
968
+ </button>
969
+ <button
970
+ type="submit"
971
+ className="bg-brand-orange hover:bg-brand-orange-dark text-white font-bold px-5 py-2 rounded-xl text-xs flex items-center space-x-1 shadow-md shadow-brand-orange/25"
972
+ >
973
+ <Save className="w-3.5 h-3.5" />
974
+ <span>{editingProduct ? "Save Modifications" : "SeedTest/Publish to Firestore"}</span>
975
+ </button>
976
+ </div>
977
+ </form>
978
+ )}
979
+
980
+ {/* Admin Feature Tabs */}
981
+ {currentUser && (
982
+ <div className="flex border-b border-black/5 dark:border-white/10 pb-0.5 space-x-6">
983
+ <button
984
+ type="button"
985
+ onClick={() => setActiveAdminTab("products")}
986
+ className={`pb-3 text-sm font-bold font-display relative transition-all flex items-center space-x-2 ${
987
+ activeAdminTab === "products"
988
+ ? "text-brand-orange"
989
+ : "text-slate-400 hover:text-slate-300"
990
+ }`}
991
+ >
992
+ <span>Tire Store Catalog</span>
993
+ {activeAdminTab === "products" && (
994
+ <div className="absolute bottom-0 left-0 right-0 h-0.5 bg-brand-orange rounded-full" />
995
+ )}
996
+ </button>
997
+ <button
998
+ type="button"
999
+ onClick={() => setActiveAdminTab("visitors")}
1000
+ className={`pb-3 text-sm font-bold font-display relative transition-all flex items-center space-x-2 ${
1001
+ activeAdminTab === "visitors"
1002
+ ? "text-brand-orange"
1003
+ : "text-slate-400 hover:text-slate-300"
1004
+ }`}
1005
+ >
1006
+ <span className="flex items-center space-x-1.5">
1007
+ <span>Visitor Traffic Analytics</span>
1008
+ <span className="text-[10px] bg-brand-orange/15 text-brand-orange px-2 py-0.5 rounded-full font-mono">
1009
+ {visitorLogs.length} Logs
1010
+ </span>
1011
+ </span>
1012
+ {activeAdminTab === "visitors" && (
1013
+ <div className="absolute bottom-0 left-0 right-0 h-0.5 bg-brand-orange rounded-full" />
1014
+ )}
1015
+ </button>
1016
+ </div>
1017
+ )}
1018
+
1019
+ {activeAdminTab === "products" ? (
1020
+ /* Main Inventory Listings Search table panel */
1021
+ <div className={`rounded-2xl border overflow-hidden ${
1022
+ darkMode ? "bg-black/20 border-white/10" : "bg-white border-slate-200/80 shadow-xs"
1023
+ }`}>
1024
+ {/* Search header info */}
1025
+ <div className="p-4 border-b border-black/5 dark:border-white/5 flex flex-col md:flex-row gap-3 justify-between items-stretch md:items-center">
1026
+ <h5 className="font-display font-medium text-sm">
1027
+ Live Tire Catalog List ({filteredProducts.length} entries shown)
1028
+ </h5>
1029
+
1030
+ <div className="relative max-w-sm w-full">
1031
+ <input
1032
+ type="text"
1033
+ placeholder="Search by brand, model, size..."
1034
+ value={searchQuery}
1035
+ onChange={(e) => setSearchQuery(e.target.value)}
1036
+ className={`w-full p-2 pl-9 rounded-xl text-xs border focus:ring-1 focus:ring-brand-orange focus:outline-none ${
1037
+ darkMode ? "bg-black/30 border-white/10 text-white" : "bg-slate-50 border-slate-200 text-slate-800"
1038
+ }`}
1039
+ id="inventory-search-input"
1040
+ />
1041
+ <Search className="w-4 h-4 text-slate-400 absolute left-3 top-1/2 -translate-y-1/2" />
1042
+ </div>
1043
+ </div>
1044
+
1045
+ {/* Loading Indicator */}
1046
+ {loading ? (
1047
+ <div className="p-12 text-center">
1048
+ <RefreshCw className="w-8 h-8 text-brand-orange animate-spin mx-auto mb-2" />
1049
+ <p className="text-xs text-slate-400">Loading catalog indexing records...</p>
1050
+ </div>
1051
+ ) : filteredProducts.length > 0 ? (
1052
+ <div className="overflow-x-auto">
1053
+ <table className="w-full text-left text-xs">
1054
+ <thead className={`font-mono text-[10px] uppercase font-bold text-slate-500 border-b ${
1055
+ darkMode ? "bg-white/5 border-white/5" : "bg-slate-50 border-slate-100"
1056
+ }`}>
1057
+ <tr>
1058
+ <th className="p-3">Tyre (Make/Model)</th>
1059
+ <th className="p-3">Brand</th>
1060
+ <th className="p-3">Size Code</th>
1061
+ <th className="p-3">Wholesale Price</th>
1062
+ <th className="p-3">Stock level</th>
1063
+ <th className="p-3">Segment Highlight</th>
1064
+ {currentUser && <th className="p-3 text-right">Actions</th>}
1065
+ </tr>
1066
+ </thead>
1067
+ <tbody className="divide-y divide-black/5 dark:divide-white/5">
1068
+ {filteredProducts.map((p: any) => (
1069
+ <tr key={p.id} className={`hover:bg-brand-orange/5 transition-colors`}>
1070
+ <td className="p-3 font-semibold text-brand-orange flex items-center gap-2">
1071
+ <div className="w-6 h-6 rounded bg-slate-500/10 overflow-hidden shrink-0">
1072
+ <img src={p.image} className="w-full h-full object-cover" alt="" />
1073
+ </div>
1074
+ <span>{p.name}</span>
1075
+ </td>
1076
+ <td className="p-3 text-slate-400 dark:text-slate-300">{p.brand}</td>
1077
+ <td className="p-3 font-mono font-medium">{p.size}</td>
1078
+ <td className="p-3 font-bold text-green-500">PKR {p.price.toLocaleString()}</td>
1079
+ <td className="p-3 font-mono">
1080
+ <span className={`p-1 px-2.5 rounded-full text-[10px] font-bold truncate ${
1081
+ (p.stock || 0) < 5
1082
+ ? "bg-rose-500/15 text-rose-500 border border-rose-500/10 animate-pulse"
1083
+ : "bg-emerald-500/15 text-emerald-500 border border-emerald-500/10"
1084
+ }`}>
1085
+ {p.stock !== undefined ? `${p.stock} Units` : "10 Units"}
1086
+ </span>
1087
+ </td>
1088
+ <td className="p-3 uppercase font-mono text-[10px] tracking-wider text-slate-400">
1089
+ <span className="bg-slate-500/10 px-2 py-0.5 rounded-md">
1090
+ {p.segment}
1091
+ </span>
1092
+ </td>
1093
+ {currentUser && (
1094
+ <td className="p-3 text-right space-x-1">
1095
+ <button
1096
+ type="button"
1097
+ onClick={() => handleEditClick(p)}
1098
+ className={`p-1.5 rounded-lg text-blue-500 hover:bg-blue-500/10`}
1099
+ title="Edit product"
1100
+ >
1101
+ <Edit className="w-4 h-4" />
1102
+ </button>
1103
+ <button
1104
+ type="button"
1105
+ onClick={() => handleDeleteClick(p.id, p.name)}
1106
+ className={`p-1.5 rounded-lg text-red-500 hover:bg-red-500/10`}
1107
+ title="Delete product"
1108
+ >
1109
+ <Trash2 className="w-4 h-4" />
1110
+ </button>
1111
+ </td>
1112
+ )}
1113
+ </tr>
1114
+ ))}
1115
+ </tbody>
1116
+ </table>
1117
+ </div>
1118
+ ) : (
1119
+ <div className="p-10 text-center text-slate-400">
1120
+ <span className="text-2xl block mb-2">🎈</span>
1121
+ <p className="text-xs">No matching tyre configurations found in database.</p>
1122
+ </div>
1123
+ )}
1124
+ </div>
1125
+ ) : (
1126
+ /* Visitor Traffic Analytics Panel */
1127
+ <div className="space-y-6">
1128
+ {/* Analytics KPI grid */}
1129
+ <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4">
1130
+ <div className={`p-4 rounded-2xl border ${darkMode ? "bg-[#1c1c24] border-white/5" : "bg-white border-slate-200/80 shadow-xs"}`}>
1131
+ <div className="flex items-center justify-between mb-2">
1132
+ <span className="text-[10px] font-mono uppercase text-slate-400 font-bold">Total Page Views</span>
1133
+ <Eye className="w-4 h-4 text-brand-orange" />
1134
+ </div>
1135
+ <div className="text-2xl font-black font-display text-brand-orange">{visitorLogs.length} Views</div>
1136
+ <p className="text-[10px] text-slate-400 mt-1">Cumulative page navigation hits logged</p>
1137
+ </div>
1138
+
1139
+ <div className={`p-4 rounded-2xl border ${darkMode ? "bg-[#1c1c24] border-white/5" : "bg-white border-slate-200/80 shadow-xs"}`}>
1140
+ <div className="flex items-center justify-between mb-2">
1141
+ <span className="text-[10px] font-mono uppercase text-slate-400 font-bold">Unique Visitors</span>
1142
+ <Users className="w-4 h-4 text-emerald-500" />
1143
+ </div>
1144
+ <div className="text-2xl font-black font-display text-emerald-500">
1145
+ {new Set(visitorLogs.map(l => l.sessionId)).size} Sessions
1146
+ </div>
1147
+ <p className="text-[10px] text-slate-400 mt-1">Total singular client cookie sessions</p>
1148
+ </div>
1149
+
1150
+ <div className={`p-4 rounded-2xl border ${darkMode ? "bg-[#1c1c24] border-white/5" : "bg-white border-slate-200/80 shadow-xs"}`}>
1151
+ <div className="flex items-center justify-between mb-2">
1152
+ <span className="text-[10px] font-mono uppercase text-slate-400 font-bold">Primary Live Hub</span>
1153
+ <Globe className="w-4 h-4 text-indigo-500" />
1154
+ </div>
1155
+ <div className="text-sm font-black font-display text-indigo-400 truncate uppercase mt-1">
1156
+ {(() => {
1157
+ const counts: Record<string, number> = {};
1158
+ visitorLogs.forEach(l => { counts[l.pagePath] = (counts[l.pagePath] || 0) + 1; });
1159
+ const top = Object.entries(counts).sort((a,b) => b[1]-a[1])[0];
1160
+ return top ? `${top[0]} (${top[1]} views)` : "N/A";
1161
+ })()}
1162
+ </div>
1163
+ <p className="text-[10px] text-slate-400 mt-1">Section driving maximum user count</p>
1164
+ </div>
1165
+
1166
+ <div className={`p-4 rounded-2xl border ${darkMode ? "bg-[#1c1c24] border-white/5" : "bg-white border-slate-200/80 shadow-xs"}`}>
1167
+ <div className="flex items-center justify-between mb-2">
1168
+ <span className="text-[10px] font-mono uppercase text-slate-400 font-bold">Dominant OS Client</span>
1169
+ <Monitor className="w-4 h-4 text-purple-500" />
1170
+ </div>
1171
+ <div className="text-2xl font-black font-display text-purple-400">
1172
+ {(() => {
1173
+ const counts: Record<string, number> = {};
1174
+ visitorLogs.forEach(l => { counts[l.platform] = (counts[l.platform] || 0) + 1; });
1175
+ const top = Object.entries(counts).sort((a,b) => b[1]-a[1])[0];
1176
+ return top ? top[0] : "Web Portal";
1177
+ })()}
1178
+ </div>
1179
+ <p className="text-[10px] text-slate-400 mt-1">Identified operating system signature</p>
1180
+ </div>
1181
+ </div>
1182
+
1183
+ {/* Graphics layout columns */}
1184
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
1185
+ {/* Section visits bar breakdown */}
1186
+ <div className={`p-5 rounded-2xl border ${darkMode ? "bg-black/25 border-white/10" : "bg-white border-slate-200/80 shadow-xs"}`}>
1187
+ <h6 className="font-display font-medium text-xs mb-4 uppercase tracking-wider text-slate-400">Section Hits Proportion</h6>
1188
+ <div className="space-y-3.5">
1189
+ {(() => {
1190
+ const counts: Record<string, number> = {};
1191
+ visitorLogs.forEach(l => { counts[l.pagePath] = (counts[l.pagePath] || 0) + 1; });
1192
+ const items = Object.entries(counts).sort((a,b)=>b[1]-a[1]);
1193
+ const total = visitorLogs.length || 1;
1194
+ return items.slice(0, 5).map(([page, count]) => {
1195
+ const pct = Math.round((count / total) * 100);
1196
+ return (
1197
+ <div key={page} className="space-y-1">
1198
+ <div className="flex justify-between text-[11px] font-mono">
1199
+ <span className="text-slate-300 font-semibold uppercase">{page}</span>
1200
+ <span className="text-slate-400">{count} views ({pct}%)</span>
1201
+ </div>
1202
+ <div className="h-1.5 w-full bg-slate-800 rounded-full overflow-hidden">
1203
+ <div className="h-full bg-brand-orange" style={{ width: `${pct}%` }} />
1204
+ </div>
1205
+ </div>
1206
+ );
1207
+ });
1208
+ })()}
1209
+ {visitorLogs.length === 0 && <p className="text-xs text-slate-500 text-center py-4">No page flow patterns registered yet.</p>}
1210
+ </div>
1211
+ </div>
1212
+
1213
+ {/* Operating systems bar breakdown */}
1214
+ <div className={`p-5 rounded-2xl border ${darkMode ? "bg-black/25 border-white/10" : "bg-white border-slate-200/80 shadow-xs"}`}>
1215
+ <h6 className="font-display font-medium text-xs mb-4 uppercase tracking-wider text-slate-400">System Platforms Split</h6>
1216
+ <div className="space-y-3.5">
1217
+ {(() => {
1218
+ const counts: Record<string, number> = {};
1219
+ visitorLogs.forEach(l => { counts[l.platform] = (counts[l.platform] || 0) + 1; });
1220
+ const items = Object.entries(counts).sort((a,b)=>b[1]-a[1]);
1221
+ const total = visitorLogs.length || 1;
1222
+ return items.slice(0, 5).map(([plat, count]) => {
1223
+ const pct = Math.round((count / total) * 100);
1224
+ return (
1225
+ <div key={plat} className="space-y-1">
1226
+ <div className="flex justify-between text-[11px] font-mono">
1227
+ <span className="text-slate-300 font-semibold">{plat}</span>
1228
+ <span className="text-slate-400">{count} hits ({pct}%)</span>
1229
+ </div>
1230
+ <div className="h-1.5 w-full bg-slate-800 rounded-full overflow-hidden">
1231
+ <div className="h-full bg-emerald-500" style={{ width: `${pct}%` }} />
1232
+ </div>
1233
+ </div>
1234
+ );
1235
+ });
1236
+ })()}
1237
+ {visitorLogs.length === 0 && <p className="text-xs text-slate-500 text-center py-4">Device platform stream is currently empty.</p>}
1238
+ </div>
1239
+ </div>
1240
+ </div>
1241
+
1242
+ {/* Sequential Live Visitory Logs stream table */}
1243
+ <div className={`rounded-2xl border overflow-hidden ${darkMode ? "bg-black/20 border-white/10" : "bg-white border-slate-200/80 shadow-xs"}`}>
1244
+ <div className="p-4 border-b border-black/5 dark:border-white/5 flex justify-between items-center bg-black/10">
1245
+ <h6 className="font-display font-medium text-xs uppercase tracking-wider text-slate-400">Visitation Sequence Logs</h6>
1246
+ <button
1247
+ type="button"
1248
+ onClick={fetchVisitorAnalytics}
1249
+ className="text-xs font-semibold text-brand-orange hover:underline flex items-center space-x-1"
1250
+ >
1251
+ <RefreshCw className="w-3.5 h-3.5" />
1252
+ <span>Refresh Live Traffic</span>
1253
+ </button>
1254
+ </div>
1255
+
1256
+ {visitorStatsLoading ? (
1257
+ <div className="p-12 text-center">
1258
+ <RefreshCw className="w-8 h-8 text-brand-orange animate-spin mx-auto mb-2" />
1259
+ <p className="text-xs text-slate-400">Querying analytical traffic logs from cloud...</p>
1260
+ </div>
1261
+ ) : visitorLogs.length > 0 ? (
1262
+ <div className="overflow-x-auto max-h-[380px] overflow-y-auto">
1263
+ <table className="w-full text-left text-xs">
1264
+ <thead className={`font-mono text-[10px] uppercase font-bold text-slate-500 border-b ${
1265
+ darkMode ? "bg-neutral-900 border-white/5" : "bg-slate-50 border-slate-100"
1266
+ }`}>
1267
+ <tr>
1268
+ <th className="p-3 pl-4">Timestamp (Asia/Karachi)</th>
1269
+ <th className="p-3">Session Hash</th>
1270
+ <th className="p-3">Device OS</th>
1271
+ <th className="p-3">Client Language</th>
1272
+ <th className="p-3">Section Section ID</th>
1273
+ <th className="p-3 pr-4 truncate max-w-xs">User Agent string</th>
1274
+ </tr>
1275
+ </thead>
1276
+ <tbody className="divide-y divide-black/5 dark:divide-white/5 font-mono text-[11px]">
1277
+ {visitorLogs.map((log) => {
1278
+ const timeStr = log.timestamp
1279
+ ? new Date(log.timestamp.seconds * 1000).toLocaleString("en-PK", { timeZone: "Asia/Karachi" })
1280
+ : "Syncing timestamp...";
1281
+ return (
1282
+ <tr key={log.id} className="hover:bg-brand-orange/5 transition-colors text-slate-400 dark:text-slate-300">
1283
+ <td className="p-3 pl-4 font-bold text-slate-500">{timeStr}</td>
1284
+ <td className="p-3 font-semibold text-emerald-400">{log.sessionId.substring(4, 14)}...</td>
1285
+ <td className="p-3 font-sans">
1286
+ <span className="p-1 px-2 text-[10px] font-semibold bg-indigo-500/10 text-indigo-400 border border-indigo-500/5 rounded-md">
1287
+ {log.platform}
1288
+ </span>
1289
+ </td>
1290
+ <td className="p-3">{log.language}</td>
1291
+ <td className="p-3 font-bold text-brand-orange uppercase">{log.pagePath}</td>
1292
+ <td className="p-3 pr-4 truncate max-w-[200px] text-[10px] text-slate-500" title={log.userAgent}>
1293
+ {log.userAgent}
1294
+ </td>
1295
+ </tr>
1296
+ );
1297
+ })}
1298
+ </tbody>
1299
+ </table>
1300
+ </div>
1301
+ ) : (
1302
+ <div className="p-12 text-center text-slate-400">
1303
+ <p className="text-xs">No user navigation sessions registered in the Firebase storage logs yet.</p>
1304
+ </div>
1305
+ )}
1306
+ </div>
1307
+ </div>
1308
+ )}
1309
+ </div>
1310
+ );
1311
+ }
src/components/LocationContact.tsx ADDED
@@ -0,0 +1,243 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useEffect } from "react";
2
+ import { Phone, MapPin, Clock, MessageSquare, Compass, ClipboardCheck, Send, Sparkles } from "lucide-react";
3
+ import { BUSINESS_INFO } from "../data";
4
+
5
+ interface LocationContactProps {
6
+ darkMode: boolean;
7
+ }
8
+
9
+ export default function LocationContact({ darkMode }: LocationContactProps) {
10
+ const [lahoreTime, setLahoreTime] = useState("");
11
+ const [inquiryText, setInquiryText] = useState("");
12
+ const [inquirySuccess, setInquirySuccess] = useState(false);
13
+
14
+ useEffect(() => {
15
+ const updateTime = () => {
16
+ try {
17
+ const timeString = new Date().toLocaleTimeString("en-US", {
18
+ timeZone: "Asia/Karachi",
19
+ hour: "2-digit",
20
+ minute: "2-digit",
21
+ second: "2-digit",
22
+ hour12: true
23
+ });
24
+ setLahoreTime(timeString);
25
+ } catch (e) {
26
+ // Fallback if platform has minor timezone translation block
27
+ const now = new Date();
28
+ const localUTC = now.getTime() + now.getTimezoneOffset() * 60000;
29
+ const pktTime = new Date(localUTC + 3600000 * 5); // GMT+5
30
+ setLahoreTime(pktTime.toLocaleTimeString());
31
+ }
32
+ };
33
+
34
+ updateTime();
35
+ const timer = setInterval(updateTime, 1000);
36
+ return () => clearInterval(timer);
37
+ }, []);
38
+
39
+ const handleInquirySubmit = (e: React.FormEvent) => {
40
+ e.preventDefault();
41
+ if (!inquiryText.trim()) return;
42
+
43
+ // Direct redirection to WhatsApp web chat prefilled with customer inquiry
44
+ const encoded = encodeURIComponent(`Hi Haider Brothers Traders, I am interested in: ${inquiryText}`);
45
+ window.open(`${BUSINESS_INFO.whatsappUrl}?text=${encoded}`, "_blank");
46
+ setInquirySuccess(true);
47
+ setInquiryText("");
48
+ };
49
+
50
+ return (
51
+ <section
52
+ id="contact"
53
+ className={`py-24 relative overflow-hidden transition-colors duration-300 ${
54
+ darkMode ? "bg-brand-dark text-white" : "bg-slate-50 text-slate-900"
55
+ }`}
56
+ >
57
+ {/* Visual lighting accents */}
58
+ <div className="absolute right-0 top-1/2 w-80 h-80 rounded-full bg-red-600/10 blur-[130px] pointer-events-none"></div>
59
+
60
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
61
+ <div className="text-center max-w-2xl mx-auto mb-16">
62
+ <div className="inline-flex items-center space-x-2 bg-brand-orange/10 px-3.5 py-1.5 rounded-full text-brand-orange text-xs font-mono tracking-wider uppercase mb-3">
63
+ <MapPin className="w-3.5 h-3.5 animate-bounce" />
64
+ <span>Faisal Town Lahore Office</span>
65
+ </div>
66
+ <h2 className="text-3xl sm:text-4xl font-display font-extrabold tracking-tight">
67
+ Stop By Or Get Instant Support
68
+ </h2>
69
+ <p className={`text-sm mt-3 ${darkMode ? "text-slate-400" : "text-slate-600"}`}>
70
+ Located at the trade center of Faisal Town block C1, Lahore. Ready to assist you 24/7 with professional tyre fitting, alignment tuning, or roadside emergency fixes.
71
+ </p>
72
+ </div>
73
+
74
+ <div className="grid grid-cols-1 lg:grid-cols-12 gap-8 items-stretch">
75
+
76
+ {/* Left Column: Coordinates & Real Contact bento Cards (lg:col-span-5) */}
77
+ <div className="lg:col-span-5 flex flex-col justify-between gap-4">
78
+
79
+ {/* Live Indicator Card */}
80
+ <div className={`p-6 rounded-3xl border transition-all duration-300 ${
81
+ darkMode ? "glassmorphism" : "glassmorphism-light shadow-md"
82
+ }`}>
83
+ <div className="flex items-center justify-between">
84
+ <span className="text-[10px] font-mono tracking-widest uppercase text-slate-500">Live Showroom Status</span>
85
+ <div className="flex items-center space-x-1.5 bg-green-500/10 px-2.5 py-1 rounded-full border border-green-500/20 text-green-500">
86
+ <span className="w-2 h-2 rounded-full bg-green-500 animate-ping"></span>
87
+ <span className="text-[10px] font-bold font-mono">24/7 OPEN NOW</span>
88
+ </div>
89
+ </div>
90
+
91
+ <div className="mt-6 flex items-baseline space-x-2">
92
+ <span className="text-2xl sm:text-3xl font-mono font-extrabold tracking-tight text-brand-orange">
93
+ {lahoreTime || "Loading..."}
94
+ </span>
95
+ <span className={`text-xs ${darkMode ? "text-slate-400" : "text-slate-500"}`}>Lahore PKT Time</span>
96
+ </div>
97
+ <p className={`text-xs mt-3 leading-relaxed ${darkMode ? "text-slate-400" : "text-slate-500"}`}>
98
+ Midnight puncture repair? Highway wheel balance issue? Our crews never sleep. Call first or drive in at any hour.
99
+ </p>
100
+ </div>
101
+
102
+ {/* Quick Connection Options Card */}
103
+ <div className={`p-6 rounded-3xl border ${
104
+ darkMode ? "bg-white/5 border-white/5" : "bg-slate-50 border-slate-100 shadow-sm"
105
+ } space-y-4`}>
106
+ {/* Telephone */}
107
+ <a
108
+ href={`tel:${BUSINESS_INFO.phoneRaw}`}
109
+ className={`p-4 rounded-2xl border flex items-center space-x-4 transition-all hover:scale-[1.01] ${
110
+ darkMode
111
+ ? "bg-black/20 border-white/5 hover:border-brand-orange/40 hover:bg-black/40"
112
+ : "bg-white border-slate-200/60 hover:border-brand-orange/40"
113
+ }`}
114
+ id="contact-phone-link"
115
+ >
116
+ <div className="w-10 h-10 rounded-xl bg-orange-600/10 text-brand-orange flex items-center justify-center shrink-0">
117
+ <Phone className="w-5 h-5" />
118
+ </div>
119
+ <div>
120
+ <span className={`text-[9px] font-mono uppercase block ${darkMode ? "text-slate-500" : "text-slate-400"}`}>Direct Hotline Support</span>
121
+ <span className="font-display font-bold text-sm sm:text-base text-brand-orange">{BUSINESS_INFO.phone}</span>
122
+ </div>
123
+ </a>
124
+
125
+ {/* WhatsApp */}
126
+ <a
127
+ href={`${BUSINESS_INFO.whatsappUrl}?text=Hello%20Haider%20Brothers,%20I'd%20like%20to%20ask%20about%20your%20tyre%20inventory`}
128
+ target="_blank"
129
+ rel="noreferrer referrer"
130
+ className={`p-4 rounded-2xl border flex items-center space-x-4 transition-all hover:scale-[1.01] ${
131
+ darkMode
132
+ ? "bg-black/20 border-white/5 hover:border-green-500/45 hover:bg-black/40"
133
+ : "bg-white border-slate-200/60 hover:border-green-500/40"
134
+ }`}
135
+ id="contact-whatsapp-link"
136
+ >
137
+ <div className="w-10 h-10 rounded-xl bg-green-500/10 text-green-500 flex items-center justify-center shrink-0">
138
+ <MessageSquare className="w-5 h-5" />
139
+ </div>
140
+ <div>
141
+ <span className={`text-[9px] font-mono uppercase block ${darkMode ? "text-slate-500" : "text-slate-400"}`}>WhatsApp Instant Quote</span>
142
+ <span className="font-display font-semibold text-sm sm:text-base text-green-500">Secure Live Text Chat</span>
143
+ </div>
144
+ </a>
145
+
146
+ {/* Address */}
147
+ <div className={`p-4 rounded-2xl border flex items-start space-x-4 ${
148
+ darkMode ? "bg-black/20 border-white/5" : "bg-white border-slate-200/60"
149
+ }`}>
150
+ <div className="w-10 h-10 rounded-xl bg-red-500/10 text-red-500 flex items-center justify-center shrink-0 mt-0.5">
151
+ <MapPin className="w-5 h-5" />
152
+ </div>
153
+ <div>
154
+ <span className={`text-[9px] font-mono uppercase block ${darkMode ? "text-slate-400" : "text-slate-400"}`}>Postal Address</span>
155
+ <p className="text-xs font-semibold leading-relaxed mt-1">
156
+ {BUSINESS_INFO.address}
157
+ </p>
158
+ </div>
159
+ </div>
160
+ </div>
161
+
162
+ {/* Live Message Form */}
163
+ <div className={`p-6 rounded-3xl border ${
164
+ darkMode ? "glassmorphism" : "glassmorphism-light shadow-md"
165
+ }`}>
166
+ <h3 className="font-display font-bold text-sm mb-2">Search Stock or Request Quote</h3>
167
+
168
+ <form onSubmit={handleInquirySubmit} className="space-y-3">
169
+ <input
170
+ type="text"
171
+ required
172
+ placeholder="e.g., need 4 tyre price for Pirelli 205/60R16..."
173
+ value={inquiryText}
174
+ onChange={(e) => setInquiryText(e.target.value)}
175
+ className={`w-full p-3 rounded-xl text-xs border focus:ring-2 focus:ring-brand-orange focus:outline-none transition-all ${
176
+ darkMode
177
+ ? "bg-black/40 border-white/10 text-white placeholder-slate-500"
178
+ : "bg-white border-slate-200 text-slate-900 placeholder-slate-400"
179
+ }`}
180
+ id="contact-stock-search-input"
181
+ />
182
+
183
+ <button
184
+ type="submit"
185
+ className="w-full bg-gradient-to-r from-brand-orange to-red-600 text-white py-2.5 rounded-xl text-xs font-bold transition-all hover:-translate-y-0.5 flex items-center justify-center space-x-1"
186
+ id="contact-submit-btn"
187
+ >
188
+ <Send className="w-3.5 h-3.5" />
189
+ <span>Send WhatsApp Message</span>
190
+ </button>
191
+ </form>
192
+ </div>
193
+ </div>
194
+
195
+ {/* Right Column: Google Maps Location Interactive Container (lg:col-span-7) */}
196
+ <div className={`lg:col-span-7 flex flex-col justify-between rounded-3xl overflow-hidden border shadow-xl min-h-[400px] transition-all duration-300 hover:scale-[1.005] ${
197
+ darkMode
198
+ ? "bg-[#111115] border-white/5 hover:border-brand-orange/30 hover:shadow-[0_0_25px_rgba(255,85,0,0.2)]"
199
+ : "bg-white border-slate-200/60 hover:border-brand-orange/30 hover:shadow-2xl"
200
+ }`}>
201
+ {/* Maps Iframe */}
202
+ <div className="relative w-full h-full min-h-[350px] flex-1">
203
+ <iframe
204
+ title="Haider Brothers Traders Google Maps Location"
205
+ src={BUSINESS_INFO.mapIframeUrl}
206
+ width="100%"
207
+ height="100%"
208
+ style={{ border: 0 }}
209
+ allowFullScreen={true}
210
+ loading="lazy"
211
+ referrerPolicy="no-referrer-when-downgrade"
212
+ className="absolute inset-0 w-full h-full grayscale-0 dark:brightness-[0.85] dark:contrast-[1.15] transition-all duration-300"
213
+ />
214
+ </div>
215
+
216
+ {/* Map footer directions link */}
217
+ <div className={`p-4 sm:p-5 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 border-t ${
218
+ darkMode ? "bg-[#1c1c24] border-white/5" : "bg-white border-slate-100"
219
+ }`}>
220
+ <div>
221
+ <h4 className="font-display font-bold text-sm">Faisal Town C-1 Roadway</h4>
222
+ <p className={`text-xs ${darkMode ? "text-slate-400" : "text-slate-500"}`}>
223
+ Located right on Block C 1, Faisal Town, Lahore, Pakistan.
224
+ </p>
225
+ </div>
226
+
227
+ <a
228
+ href="https://www.google.com/maps/place/Haider+Brothers+Traders/@31.4764496,74.3084687,17z/data=!3m1!4b1!4m6!3m5!1s0x3919030037af462f:0xc479c225327c71d6!8m2!3d31.4764496!4d74.3084687!16s%2Fg%2F11zjmlbgth?entry=ttu&g_ep=EgoyMDI2MDYxMC4wIKXMDSoASAFQAw%3D%3D"
229
+ target="_blank"
230
+ rel="noreferrer referrer"
231
+ className="flex items-center justify-center space-x-2 bg-brand-orange hover:bg-brand-orange-dark text-white px-5 py-3 rounded-xl text-xs font-bold shadow-md shadow-brand-orange/20 transition-all text-center hover:scale-105 active:scale-95"
232
+ id="maps-directions-btn"
233
+ >
234
+ <Compass className="w-4 h-4 shrink-0" />
235
+ <span>Navigate on Google Maps</span>
236
+ </a>
237
+ </div>
238
+ </div>
239
+ </div>
240
+ </div>
241
+ </section>
242
+ );
243
+ }
src/components/Logo.tsx ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from "react";
2
+
3
+ interface LogoProps {
4
+ className?: string;
5
+ variant?: "full" | "icon" | "compact";
6
+ darkMode?: boolean;
7
+ }
8
+
9
+ export default function Logo({ className = "h-12", variant = "full", darkMode = true }: LogoProps) {
10
+ // Brand Colors matching the logo:
11
+ // Red: Racing red/orange-red (#EB1C24 or brand-orange)
12
+ // Dark text: Slate-900 / white depending on darkMode
13
+ const primaryRed = "#E11D48"; // vibrant racing red
14
+ const textDarkColor = darkMode ? "#FFFFFF" : "#0F172A";
15
+ const grayColor = darkMode ? "#94A3B8" : "#64748B";
16
+
17
+ if (variant === "icon") {
18
+ // A quick, highly-identifiable compact circular/square icon for favicon / tiny spots
19
+ return (
20
+ <svg
21
+ viewBox="0 0 100 100"
22
+ fill="none"
23
+ xmlns="http://www.w3.org/2000/svg"
24
+ className={className}
25
+ >
26
+ {/* Ambient Glow */}
27
+ <circle cx="50" cy="50" r="40" fill="url(#iconGlow)" opacity="0.15" />
28
+
29
+ {/* Tire track arc on top of icon */}
30
+ <path
31
+ d="M20,38 C30,22 70,22 80,38"
32
+ stroke={textDarkColor}
33
+ strokeWidth="4"
34
+ strokeDasharray="4 4"
35
+ strokeLinecap="round"
36
+ opacity="0.8"
37
+ />
38
+
39
+ {/* Slanted bold letter "H" and "T" */}
40
+ <g transform="skewX(-10) translate(8, 0)">
41
+ <text
42
+ x="20"
43
+ y="65"
44
+ fill={primaryRed}
45
+ fontSize="32"
46
+ fontWeight="900"
47
+ fontFamily="system-ui, -apple-system, sans-serif"
48
+ letterSpacing="-1"
49
+ >
50
+ H
51
+ </text>
52
+ <text
53
+ x="48"
54
+ y="65"
55
+ fill={textDarkColor}
56
+ fontSize="32"
57
+ fontWeight="900"
58
+ fontFamily="system-ui, -apple-system, sans-serif"
59
+ letterSpacing="-1"
60
+ >
61
+ B
62
+ </text>
63
+ </g>
64
+
65
+ {/* Tire wrapping right side */}
66
+ <circle cx="76" cy="55" r="14" stroke={textDarkColor} strokeWidth="4.5" />
67
+ <circle cx="76" cy="55" r="8" stroke={textDarkColor} strokeWidth="2.5" />
68
+ <path d="M76,41 L76,69 M62,55 L90,55" stroke={textDarkColor} strokeWidth="1.5" />
69
+
70
+ <defs>
71
+ <radialGradient id="iconGlow" cx="50%" cy="50%" r="50%">
72
+ <stop offset="0%" stopColor={primaryRed} />
73
+ <stop offset="100%" stopColor="transparent" />
74
+ </radialGradient>
75
+ </defs>
76
+ </svg>
77
+ );
78
+ }
79
+
80
+ // Full HBT Logo matching the shop's physical sign board and uploaded image
81
+ return (
82
+ <div className={`flex items-center select-none ${className}`}>
83
+ <svg
84
+ viewBox="0 0 350 115"
85
+ fill="none"
86
+ xmlns="http://www.w3.org/2000/svg"
87
+ className="w-full h-full"
88
+ >
89
+ {/* BACKGROUND GLOW (Subtle, professional) */}
90
+ <defs>
91
+ <filter id="glow" x="-10%" y="-10%" width="120%" height="120%">
92
+ <feGaussianBlur stdDeviation="3" result="blur" />
93
+ <feComposite in="SourceGraphic" in2="blur" operator="over" />
94
+ </filter>
95
+ </defs>
96
+
97
+ {/* 1. TIRE TREAD TRACK ARC (Top header curving down over the letters into the wheel) */}
98
+ <g id="Tire-Tread-Arc" transform="translate(45, 10)">
99
+ {/* Subtle guide curve */}
100
+ <path d="M 5,20 Q 100,-8 212,28" stroke={grayColor} strokeWidth="1" fill="none" opacity="0.15" />
101
+
102
+ {/* Authentic Herringbone/Leaning tread tracks from the uploaded sign */}
103
+ <g fill={textDarkColor} opacity="0.85">
104
+ {/* Left side rising treads */}
105
+ <path d="M 5,22 L 11,14 L 16,16 L 10,24 Z" />
106
+ <path d="M 23,17 L 29,9 L 34,11 L 28,19 Z" />
107
+ <path d="M 42,13 L 48,5 L 53,7 L 47,15 Z" />
108
+ <path d="M 62,10 L 68,2 L 73,4 L 67,12 Z" />
109
+ <path d="M 83,8 L 89,1 L 94,3 L 88,10 Z" />
110
+
111
+ {/* Center-right crowning treads bending towards the tire */}
112
+ <path d="M 105,7 L 111,1 L 116,3 L 110,9 Z" />
113
+ <path d="M 127,8 L 133,3 L 137,6 L 131,11 Z" />
114
+ <path d="M 149,11 L 154,6 L 158,9 L 153,14 Z" />
115
+ <path d="M 170,15 L 175,10 L 179,13 L 174,18 Z" />
116
+ <path d="M 190,21 L 194,16 L 198,19 L 194,24 Z" />
117
+ <path d="M 209,28 L 213,23 L 217,26 L 213,31 Z" />
118
+ </g>
119
+ </g>
120
+
121
+ {/* 2. MAIN LOGO BODY: STYLIZED SLANTED "HBT" */}
122
+ <g id="HBT-Acronym" transform="translate(56, 32)">
123
+ {/* High-fidelity, sharp racing red path for 'H' */}
124
+ <path
125
+ d="M 5,48 L 16,6 H 31 L 27,22 H 49 L 53,6 H 68 L 57,48 H 42 L 46,31 H 24 L 20,48 Z"
126
+ fill={primaryRed}
127
+ />
128
+
129
+ {/* High-fidelity block 'B' in Racing Red */}
130
+ <path
131
+ d="M 72,48 L 84,6 H 112 C 122,6 128,10 126,17 C 125,21 121,24 116,26 C 122,27 124,31 122,37 C 120,44 113,48 102,48 Z
132
+ M 94,21 H 105 C 109,21 111,19 112,16 C 113,13 111,11 107,11 H 97 Z
133
+ M 90,38 H 101 C 105,38 107,36 108,32 C 109,28 107,26 103,26 H 93 Z"
134
+ fill={primaryRed}
135
+ />
136
+
137
+ {/* High-fidelity slanted 'T' adjacent and integrated to the wheel */}
138
+ <path
139
+ d="M 124,13 L 126,6 H 168 L 166,13 H 152 L 143,48 H 128 L 137,13 Z"
140
+ fill={primaryRed}
141
+ />
142
+
143
+ {/* 3. SIMPLIFIED & AUTHENTIC TIRE & RIM SYSTEM (Clean, high-contrast circular wheel matching the real shop brand) */}
144
+ <g id="Wheel-Icon" transform="translate(178, 14)">
145
+ {/* Elegant, clean concentric circular ring in the rim disc */}
146
+ <circle cx="25" cy="25" r="9" stroke={grayColor} strokeWidth="1.5" fill="none" opacity="0.6" />
147
+ </g>
148
+ </g>
149
+
150
+ {/* 4. "HAIDER BROTHERS TRADERS" sub-text at the base */}
151
+ <text
152
+ x="175"
153
+ y="104"
154
+ fill={textDarkColor}
155
+ fontSize="15.5"
156
+ fontWeight="800"
157
+ fontFamily="system-ui, -apple-system, sans-serif"
158
+ letterSpacing="5.8"
159
+ textAnchor="middle"
160
+ id="hbt-signature-text"
161
+ >
162
+ HAIDER BROTHERS TRADERS
163
+ </text>
164
+
165
+ {/* Small trademark label */}
166
+ <text
167
+ x="332"
168
+ y="102"
169
+ fill={grayColor}
170
+ fontSize="7"
171
+ fontWeight="700"
172
+ fontFamily="monospace"
173
+ >
174
+ ®
175
+ </text>
176
+ </svg>
177
+ </div>
178
+ );
179
+ }
src/components/Navbar.tsx ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState, useEffect } from "react";
2
+ import { Phone, MessageSquare, Menu, X, Sun, Moon, Sparkles, FolderLock } from "lucide-react";
3
+ import { BUSINESS_INFO } from "../data";
4
+ import Logo from "./Logo";
5
+
6
+ interface NavbarProps {
7
+ darkMode: boolean;
8
+ setDarkMode: (val: boolean) => void;
9
+ currentPage: string;
10
+ setCurrentPage: (page: string) => void;
11
+ isAuthorizedAdmin: boolean;
12
+ }
13
+
14
+ export default function Navbar({ darkMode, setDarkMode, currentPage, setCurrentPage, isAuthorizedAdmin }: NavbarProps) {
15
+ const [isOpen, setIsOpen] = useState(false);
16
+ const [scrolled, setScrolled] = useState(false);
17
+
18
+ useEffect(() => {
19
+ const handleScroll = () => {
20
+ setScrolled(window.scrollY > 20);
21
+ };
22
+ window.addEventListener("scroll", handleScroll);
23
+ return () => window.removeEventListener("scroll", handleScroll);
24
+ }, []);
25
+
26
+ const baseNavItems = [
27
+ { label: "Home", page: "home" },
28
+ { label: "Catalogue", page: "catalogue" },
29
+ { label: "Reviews Feed", page: "reviews" },
30
+ { label: "Showroom", page: "showroom" },
31
+ { label: "Inventory Manager", page: "admin", isIconic: true }
32
+ ];
33
+
34
+ const navItems = isAuthorizedAdmin
35
+ ? baseNavItems
36
+ : baseNavItems.filter(item => item.page !== "admin");
37
+
38
+ return (
39
+ <nav
40
+ className={`sticky top-0 left-0 w-full z-50 transition-all duration-300 ${
41
+ scrolled || currentPage !== "home"
42
+ ? darkMode
43
+ ? "bg-[#121214]/95 backdrop-blur-md border-b border-white/10 py-3 shadow-lg shadow-black/20"
44
+ : "bg-white/95 backdrop-blur-md border-b border-black/5 py-3 shadow-sm"
45
+ : "bg-transparent py-5"
46
+ }`}
47
+ >
48
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
49
+ <div className="flex items-center justify-between">
50
+ {/* Logo Brand */}
51
+ <button
52
+ onClick={() => { setCurrentPage("home"); window.scrollTo({ top: 0, behavior: "smooth" }); }}
53
+ className="flex items-center group transition-all transform hover:scale-[1.02] active:scale-95 text-left"
54
+ id="navbar-logo-btn"
55
+ >
56
+ <Logo variant="full" darkMode={darkMode} className="h-10 sm:h-12 w-auto" />
57
+ </button>
58
+
59
+ {/* Desktop Navigation Links (Responsive breakpoint updated to lg to prevent overlapping on tablets) */}
60
+ <div className="hidden lg:flex items-center space-x-1 xl:space-x-2">
61
+ {navItems.map((item) => {
62
+ const isActive = currentPage === item.page;
63
+ return (
64
+ <button
65
+ key={item.page}
66
+ onClick={() => { setCurrentPage(item.page); window.scrollTo({ top: 0, behavior: "smooth" }); }}
67
+ className={`px-3 py-2 rounded-lg text-xs xl:text-sm font-medium transition-all relative flex items-center space-x-1 ${
68
+ isActive
69
+ ? "text-brand-orange font-bold bg-brand-orange/5"
70
+ : darkMode
71
+ ? "text-slate-300 hover:text-white hover:bg-white/5"
72
+ : "text-slate-600 hover:text-slate-900 hover:bg-black/5"
73
+ }`}
74
+ >
75
+ {item.isIconic && <FolderLock className="w-3.5 h-3.5 mr-1 text-brand-orange inline" />}
76
+ <span>{item.label}</span>
77
+ {isActive && (
78
+ <span className="absolute bottom-0 left-2 right-2 h-[2px] bg-brand-orange rounded-full" />
79
+ )}
80
+ </button>
81
+ );
82
+ })}
83
+ </div>
84
+
85
+ {/* Right Action panel for Desktop */}
86
+ <div className="hidden lg:flex items-center space-x-3">
87
+ {/* Theme Toggle Button */}
88
+ <button
89
+ onClick={() => setDarkMode(!darkMode)}
90
+ className={`p-2 rounded-xl transition-all ${
91
+ darkMode
92
+ ? "bg-white/5 hover:bg-white/10 text-amber-400"
93
+ : "bg-black/5 hover:bg-black/10 text-slate-800"
94
+ }`}
95
+ title={darkMode ? "Switch to Light Mode" : "Switch to Dark Mode"}
96
+ id="theme-toggle-desktop"
97
+ >
98
+ {darkMode ? <Sun className="w-5 h-5" /> : <Moon className="w-5 h-5" />}
99
+ </button>
100
+
101
+ {/* Emergency Hotline Button */}
102
+ <a
103
+ href={`tel:${BUSINESS_INFO.phoneRaw}`}
104
+ className="flex items-center space-x-1.5 bg-gradient-to-r from-brand-orange to-red-600 text-white px-3.5 py-2 rounded-xl text-xs font-semibold shadow-md shadow-brand-orange/20 hover:shadow-brand-orange/40 hover:-translate-y-0.5 transition-all"
105
+ >
106
+ <Phone className="w-3.5 h-3.5" />
107
+ <span>24/7 Helpline</span>
108
+ </a>
109
+ </div>
110
+
111
+ {/* Mobile Right Controls (Menu & Theme) */}
112
+ <div className="flex lg:hidden items-center space-x-2">
113
+ <button
114
+ onClick={() => setDarkMode(!darkMode)}
115
+ className={`p-1.5 rounded-lg transition-all ${
116
+ darkMode ? "bg-white/5 text-amber-400" : "bg-black/5 text-slate-800"
117
+ }`}
118
+ id="theme-toggle-mobile"
119
+ >
120
+ {darkMode ? <Sun className="w-4.5 h-4.5" /> : <Moon className="w-4.5 h-4.5" />}
121
+ </button>
122
+
123
+ <button
124
+ onClick={() => setIsOpen(!isOpen)}
125
+ className={`p-1.5 rounded-lg transition-all ${
126
+ darkMode ? "bg-white/5 text-white" : "bg-black/5 text-slate-800"
127
+ }`}
128
+ aria-label="Toggle navigation menu"
129
+ id="mobile-menu-btn"
130
+ >
131
+ {isOpen ? <X className="w-5.5 h-5.5" /> : <Menu className="w-5.5 h-5.5" />}
132
+ </button>
133
+ </div>
134
+ </div>
135
+ </div>
136
+
137
+ {/* Mobile Drawer Overlay */}
138
+ {isOpen && (
139
+ <div className="lg:hidden fixed inset-0 top-[60px] z-45 bg-black/60 backdrop-blur-sm">
140
+ <div
141
+ className={`w-4/5 max-w-sm h-[calc(100vh-60px)] p-6 shadow-2xl flex flex-col justify-between transition-transform duration-300 ${
142
+ darkMode ? "bg-[#1c1c24] text-white" : "bg-white text-slate-900"
143
+ }`}
144
+ >
145
+ <div className="space-y-4">
146
+ <span className={`text-[10px] font-mono tracking-widest block pb-2 border-b uppercase ${
147
+ darkMode ? "text-slate-400 border-white/5" : "text-slate-500 border-black/5"
148
+ }`}>
149
+ Browse Showroom Sections
150
+ </span>
151
+
152
+ <div className="flex flex-col space-y-1">
153
+ {navItems.map((item) => {
154
+ const isActive = currentPage === item.page;
155
+ return (
156
+ <button
157
+ key={item.page}
158
+ onClick={() => {
159
+ setCurrentPage(item.page);
160
+ setIsOpen(false);
161
+ window.scrollTo({ top: 0, behavior: "smooth" });
162
+ }}
163
+ className={`px-4 py-3 rounded-xl text-sm font-medium transition-all flex items-center justify-between text-left w-full ${
164
+ isActive
165
+ ? "bg-brand-orange/10 text-brand-orange font-bold"
166
+ : darkMode
167
+ ? "hover:bg-white/5 text-slate-300"
168
+ : "hover:bg-slate-100 text-slate-700"
169
+ }`}
170
+ >
171
+ <span className="flex items-center">
172
+ {item.isIconic && <FolderLock className="w-4 h-4 mr-2 text-brand-orange" />}
173
+ {item.label}
174
+ </span>
175
+ {isActive && <span className="w-2 h-2 rounded-full bg-brand-orange" />}
176
+ </button>
177
+ );
178
+ })}
179
+ </div>
180
+ </div>
181
+
182
+ <div className="space-y-4 pt-6 border-t border-black/5 dark:border-white/5">
183
+ <div className="flex items-center space-x-2 text-xs font-mono text-slate-500 dark:text-slate-400">
184
+ <Sparkles className="w-4 h-4 text-brand-orange animate-pulse" />
185
+ <span>OPEN 24 HOURS IN FAISAL TOWN</span>
186
+ </div>
187
+
188
+ <a
189
+ href={`tel:${BUSINESS_INFO.phoneRaw}`}
190
+ className="flex items-center justify-center space-x-2 w-full bg-gradient-to-r from-brand-orange to-red-600 text-white py-2.5 rounded-xl text-xs font-semibold shadow-md shadow-brand-orange/30 text-center"
191
+ >
192
+ <Phone className="w-4 h-4" />
193
+ <span>Call Emergency Service</span>
194
+ </a>
195
+
196
+ <a
197
+ href={`${BUSINESS_INFO.whatsappUrl}?text=Hello%20Haider%20Brothers,%20I%20have%20an%20emergency%20tyre%20request`}
198
+ target="_blank"
199
+ rel="noreferrer referrer"
200
+ className="flex items-center justify-center space-x-2 w-full border border-green-500/35 text-green-500 bg-green-500/5 py-2.5 rounded-xl text-xs font-medium"
201
+ >
202
+ <MessageSquare className="w-4 h-4" />
203
+ <span>WhatsApp Live Chat</span>
204
+ </a>
205
+ </div>
206
+ </div>
207
+ </div>
208
+ )}
209
+ </nav>
210
+ );
211
+ }
src/components/NewsletterSignup.tsx ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState } from "react";
2
+ import { Mail, Send, Check, Loader2, Sparkles } from "lucide-react";
3
+ import { subscribeNewsletter } from "../lib/firebaseService";
4
+
5
+ interface NewsletterSignupProps {
6
+ darkMode: boolean;
7
+ }
8
+
9
+ export default function NewsletterSignup({ darkMode }: NewsletterSignupProps) {
10
+ const [email, setEmail] = useState("");
11
+ const [loading, setLoading] = useState(false);
12
+ const [status, setStatus] = useState<"idle" | "success" | "error">("idle");
13
+ const [errorMessage, setErrorMessage] = useState("");
14
+
15
+ const handleSubscribe = async (e: React.FormEvent) => {
16
+ e.preventDefault();
17
+ if (!email.trim()) return;
18
+
19
+ setLoading(true);
20
+ setStatus("idle");
21
+ setErrorMessage("");
22
+
23
+ try {
24
+ await subscribeNewsletter(email);
25
+ setStatus("success");
26
+ setEmail("");
27
+ } catch (err: any) {
28
+ console.error("Subscription failed:", err);
29
+ setStatus("error");
30
+
31
+ // Extract a descriptive human error message
32
+ let msg = "Something went wrong. Please check your network or try again.";
33
+ if (err instanceof Error) {
34
+ try {
35
+ const parsed = JSON.parse(err.message);
36
+ if (parsed.error) {
37
+ msg = parsed.error;
38
+ }
39
+ } catch {
40
+ msg = err.message;
41
+ }
42
+ }
43
+ setErrorMessage(msg);
44
+ } finally {
45
+ setLoading(false);
46
+ }
47
+ };
48
+
49
+ return (
50
+ <div className="space-y-4" id="newsletter-signup-container">
51
+ <div className="space-y-1.5">
52
+ <h4 className={`font-display font-medium text-xs uppercase tracking-widest flex items-center gap-1.5 font-mono ${
53
+ darkMode ? "text-slate-200" : "text-slate-950"
54
+ }`}>
55
+ <Mail className="w-3.5 h-3.5 text-brand-orange" />
56
+ <span>Maintenance Newsletter</span>
57
+ </h4>
58
+ <p className="leading-relaxed text-[11px] text-slate-500">
59
+ Subscribe to receive seasonal tyre maintenance advice, road safety checks, and priority showroom discount vouchers in Lahore.
60
+ </p>
61
+ </div>
62
+
63
+ {status === "success" ? (
64
+ <div
65
+ className={`p-3 rounded-2xl border flex items-start space-x-2.5 transition-all animate-fadeIn ${
66
+ darkMode
67
+ ? "bg-emerald-500/10 border-emerald-500/20 text-emerald-400"
68
+ : "bg-emerald-50 border-emerald-200 text-emerald-800"
69
+ }`}
70
+ id="newsletter-success-alert"
71
+ >
72
+ <div className="p-1 rounded-full bg-emerald-500/20 text-emerald-400 mt-0.5 shrink-0">
73
+ <Check className="w-3.5 h-3.5" />
74
+ </div>
75
+ <div>
76
+ <p className="font-bold text-xs">Successfully Subscribed!</p>
77
+ <p className="text-[10px] mt-0.5 opacity-90">
78
+ You're registered to receive maintenance tips. Stay safe on the road!
79
+ </p>
80
+ </div>
81
+ </div>
82
+ ) : (
83
+ <form onSubmit={handleSubscribe} className="space-y-2">
84
+ <div className="relative">
85
+ <input
86
+ type="email"
87
+ value={email}
88
+ onChange={(e) => setEmail(e.target.value)}
89
+ placeholder="Enter your email address"
90
+ required
91
+ disabled={loading}
92
+ className={`w-full text-xs px-3.5 py-2.5 pr-10 rounded-xl border font-sans focus:outline-hidden focus:ring-1 focus:ring-brand-orange transition-all duration-200 ${
93
+ darkMode
94
+ ? "bg-black/40 border-white/10 text-white placeholder-slate-500 focus:border-brand-orange/40"
95
+ : "bg-white border-slate-200 text-slate-900 placeholder-slate-400 focus:border-brand-orange"
96
+ }`}
97
+ id="newsletter-email-input"
98
+ />
99
+ <button
100
+ type="submit"
101
+ disabled={loading || !email.trim()}
102
+ className="absolute right-1.5 top-1/2 -translate-y-1/2 p-1.5 rounded-lg bg-brand-orange hover:bg-brand-orange-dark text-white hover:scale-105 active:scale-95 transition-all duration-200 disabled:opacity-50 disabled:pointer-events-none"
103
+ title="Subscribe"
104
+ id="newsletter-submit-btn"
105
+ >
106
+ {loading ? (
107
+ <Loader2 className="w-3.5 h-3.5 animate-spin" />
108
+ ) : (
109
+ <Send className="w-3.5 h-3.5" />
110
+ )}
111
+ </button>
112
+ </div>
113
+
114
+ {status === "error" && (
115
+ <p className="text-[10px] text-rose-500 font-medium px-1 leading-snug" id="newsletter-error-message">
116
+ ⚠️ {errorMessage}
117
+ </p>
118
+ )}
119
+
120
+ <div className="flex items-center space-x-1.5 text-[10px] text-slate-500 font-mono px-1">
121
+ <Sparkles className="w-3 h-3 text-brand-orange/60 shrink-0" />
122
+ <span>Updates twice a month • Opt-out anytime</span>
123
+ </div>
124
+ </form>
125
+ )}
126
+ </div>
127
+ );
128
+ }
src/components/ProductDetailsModal.tsx ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { X, MessageSquare, ShieldCheck, CheckCircle2, Phone, Compass } from "lucide-react";
2
+ import { TireProduct, BUSINESS_INFO } from "../data";
3
+
4
+ interface ProductDetailsModalProps {
5
+ tire: TireProduct | null;
6
+ onClose: () => void;
7
+ darkMode: boolean;
8
+ }
9
+
10
+ export default function ProductDetailsModal({ tire, onClose, darkMode }: ProductDetailsModalProps) {
11
+ if (!tire) return null;
12
+
13
+ const waPreFilledText = `Hello Haider Brothers, I want price & stock for ${tire.name} (Size: ${tire.size})`;
14
+ const encodedText = encodeURIComponent(waPreFilledText);
15
+ const waLink = `${BUSINESS_INFO.whatsappUrl}?text=${encodedText}`;
16
+
17
+ // Calculated physical traits for mock premium display
18
+ const loadSpeedIndex = tire.size.includes("14") ? "88H" : tire.size.includes("15") ? "91H" : tire.size.includes("16") ? "94V" : tire.size.includes("17") ? "98W" : "103W";
19
+ const treadwear = tire.brand.toLowerCase() === "michelin" || tire.brand.toLowerCase() === "continental" ? "540 A A" : "400 A A";
20
+
21
+ return (
22
+ <div className="fixed inset-0 z-50 flex items-center justify-center p-4 sm:p-6 overflow-y-auto bg-black/80 backdrop-blur-sm animate-fadeIn">
23
+ {/* Container Card */}
24
+ <div
25
+ className={`relative w-full max-w-3xl rounded-3xl overflow-hidden shadow-2xl transition-all border duration-300 ${
26
+ darkMode ? "bg-[#1c1c24] border-white/10 text-white" : "bg-white border-slate-200 text-slate-900"
27
+ }`}
28
+ >
29
+ {/* Close Button */}
30
+ <button
31
+ onClick={onClose}
32
+ className={`absolute top-4 right-4 z-10 p-2 rounded-xl transition-all ${
33
+ darkMode ? "bg-black/40 hover:bg-black/60 text-white" : "bg-slate-100 hover:bg-slate-200 text-slate-800"
34
+ }`}
35
+ aria-label="Close specifications modal"
36
+ id="close-modal-btn"
37
+ >
38
+ <X className="w-5 h-5" />
39
+ </button>
40
+
41
+ <div className="grid grid-cols-1 md:grid-cols-2">
42
+ {/* Card Media Section */}
43
+ <div className="relative aspect-square md:h-full overflow-hidden bg-slate-950">
44
+ <img
45
+ src={tire.image}
46
+ alt={tire.name}
47
+ className="w-full h-full object-cover"
48
+ referrerPolicy="no-referrer"
49
+ />
50
+ <div className="absolute inset-0 bg-gradient-to-t from-black/80 via-transparent pointer-events-none"></div>
51
+
52
+ {/* Image caption absolute */}
53
+ <div className="absolute bottom-6 left-6 right-6">
54
+ <span className="bg-brand-orange text-white text-[10px] font-mono font-bold px-2.5 py-1 rounded-full uppercase tracking-wider block w-fit mb-2">
55
+ ORIGINAL SPECIFICATIONS
56
+ </span>
57
+ <h3 className="font-display text-2xl font-extrabold text-white leading-tight">
58
+ {tire.name}
59
+ </h3>
60
+ <p className="text-xs text-slate-300 mt-1">Authorized Import & Supply</p>
61
+ </div>
62
+ </div>
63
+
64
+ {/* Details / Spec column */}
65
+ <div className="p-6 sm:p-8 flex flex-col justify-between space-y-6">
66
+ <div className="space-y-4">
67
+ <div className="flex items-center space-x-2">
68
+ <span className="text-brand-orange font-bold text-xs uppercase tracking-widest font-mono">
69
+ {tire.brand} Tyre Details
70
+ </span>
71
+ <span className="w-1 h-1 rounded-full bg-slate-500"></span>
72
+ <span className={`text-xs font-semibold ${
73
+ tire.stock !== undefined && tire.stock < 5
74
+ ? "text-rose-500 animate-pulse"
75
+ : "text-emerald-500"
76
+ }`}>
77
+ Lahore Stock: {tire.stock !== undefined ? `${tire.stock} Units Remaining` : "Available (In Stock)"}
78
+ </span>
79
+ </div>
80
+
81
+ <div className="border-b pb-4 border-black/5 dark:border-white/5">
82
+ <p className={`text-xs leading-relaxed ${darkMode ? "text-slate-300" : "text-slate-600"}`}>
83
+ {tire.description} Perfect for drivers focused on safety, longevity, and comfortable ride acoustics on both dual highways and local Lahore residential lanes.
84
+ </p>
85
+ </div>
86
+
87
+ {/* Specs Bento Grid Table */}
88
+ <div className="grid grid-cols-2 gap-3 pt-2">
89
+ <div className={`p-3 rounded-xl border ${darkMode ? "bg-white/5 border-white/5" : "bg-slate-50 border-slate-100"}`}>
90
+ <span className={`text-[10px] uppercase font-mono tracking-wider block ${darkMode ? "text-slate-400" : "text-slate-500"}`}>
91
+ Tyre Size
92
+ </span>
93
+ <span className="font-mono text-sm font-bold text-brand-orange">{tire.size}</span>
94
+ </div>
95
+
96
+ <div className={`p-3 rounded-xl border ${darkMode ? "bg-white/5 border-white/5" : "bg-slate-50 border-slate-100"}`}>
97
+ <span className={`text-[10px] uppercase font-mono tracking-wider block ${darkMode ? "text-slate-400" : "text-slate-500"}`}>
98
+ Load/Speed Rating
99
+ </span>
100
+ <span className="font-mono text-sm font-bold">{loadSpeedIndex}</span>
101
+ </div>
102
+
103
+ <div className={`p-3 rounded-xl border ${darkMode ? "bg-white/5 border-white/5" : "bg-slate-50 border-slate-100"}`}>
104
+ <span className={`text-[10px] uppercase font-mono tracking-wider block ${darkMode ? "text-slate-400" : "text-slate-500"}`}>
105
+ UTQG / Treadwear
106
+ </span>
107
+ <span className="font-mono text-sm font-bold">{treadwear}</span>
108
+ </div>
109
+
110
+ <div className={`p-3 rounded-xl border ${darkMode ? "bg-white/5 border-white/5" : "bg-slate-50 border-slate-100"}`}>
111
+ <span className={`text-[10px] uppercase font-mono tracking-wider block ${darkMode ? "text-slate-400" : "text-slate-500"}`}>
112
+ Installation
113
+ </span>
114
+ <span className="text-green-500 text-xs font-bold font-mono">FREE AT STORE</span>
115
+ </div>
116
+ </div>
117
+
118
+ {/* Warranties flags */}
119
+ <div className="space-y-2 pt-1 font-mono text-[11px]">
120
+ <div className="flex items-center space-x-1.5 text-emerald-500">
121
+ <CheckCircle2 className="w-4 h-4" />
122
+ <span>Official Year Manufacturing Date Warranted</span>
123
+ </div>
124
+ <div className="flex items-center space-x-1.5 text-brand-orange">
125
+ <Compass className="w-4 h-4" />
126
+ <span>Optimized for Pakistan's heat cycles & road asphalt</span>
127
+ </div>
128
+ </div>
129
+ </div>
130
+
131
+ {/* Price & Primary Call-to-actions */}
132
+ <div className="pt-4 border-t border-black/5 dark:border-white/5 space-y-4">
133
+ <div className="flex items-baseline justify-between">
134
+ <span className={`text-xs font-mono tracking-wider ${darkMode ? "text-slate-400" : "text-slate-500"}`}>
135
+ Wholesale Unit Price
136
+ </span>
137
+ <span className="text-brand-orange font-display text-2xl font-black">
138
+ PKR {tire.price.toLocaleString()}
139
+ </span>
140
+ </div>
141
+
142
+ <div className="grid grid-cols-2 gap-3">
143
+ {/* Dial Store hotline */}
144
+ <a
145
+ href={`tel:${BUSINESS_INFO.phoneRaw}`}
146
+ className={`flex items-center justify-center space-x-2 py-3.5 rounded-xl text-xs font-bold transition-all border ${
147
+ darkMode
148
+ ? "border-white/10 bg-white/5 hover:bg-white/10 text-white"
149
+ : "border-slate-200 bg-slate-50 hover:bg-slate-100 text-slate-800"
150
+ }`}
151
+ id="modal-phone-btn"
152
+ >
153
+ <Phone className="w-4 h-4 text-brand-orange" />
154
+ <span>Call Store Office</span>
155
+ </a>
156
+
157
+ {/* WhatsApp Chat prefilled */}
158
+ <a
159
+ href={waLink}
160
+ target="_blank"
161
+ rel="noreferrer referrer"
162
+ className="flex items-center justify-center space-x-2 bg-gradient-to-r from-green-600 to-emerald-500 hover:from-green-500 hover:to-emerald-400 text-white py-3.5 rounded-xl text-xs font-bold shadow-md shadow-green-600/10 transition-all"
163
+ id="modal-wa-btn"
164
+ >
165
+ <MessageSquare className="w-4 h-4" />
166
+ <span>WhatsApp Secure Order</span>
167
+ </a>
168
+ </div>
169
+ </div>
170
+ </div>
171
+ </div>
172
+ </div>
173
+ </div>
174
+ );
175
+ }
src/components/Promotions.tsx ADDED
@@ -0,0 +1,318 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState, useEffect } from "react";
2
+ import { motion, AnimatePresence } from "motion/react";
3
+ import {
4
+ ChevronLeft,
5
+ ChevronRight,
6
+ Sparkles,
7
+ Percent,
8
+ Gift,
9
+ ShieldCheck,
10
+ Flame,
11
+ TrendingUp,
12
+ Calendar,
13
+ AlertCircle
14
+ } from "lucide-react";
15
+ import { BUSINESS_INFO } from "../data";
16
+
17
+ interface PromoOffer {
18
+ id: string;
19
+ title: string;
20
+ subtitle: string;
21
+ tagline: string;
22
+ description: string;
23
+ badge: {
24
+ text: string;
25
+ type: "discount" | "bundle" | "limited" | "exclusive";
26
+ };
27
+ highlightFeatures: string[];
28
+ duration: string;
29
+ actionText: string;
30
+ gradient: string;
31
+ icon: any;
32
+ }
33
+
34
+ export default function Promotions({ darkMode }: { darkMode: boolean }) {
35
+ const [currentIndex, setCurrentIndex] = useState<number>(0);
36
+ const [isAutoPlaying, setIsAutoPlaying] = useState<boolean>(true);
37
+
38
+ const PROMO_DATA: PromoOffer[] = [
39
+ {
40
+ id: "monsoon-alignment-bundle",
41
+ title: "Lahore Monsoon Safety Package",
42
+ subtitle: "FREE Computerized 3D Alignment & Wheel Balancing",
43
+ tagline: "Total peace-of-mind during rainy seasons on wet Lahore roads",
44
+ description: "Buy any premium set of 4 Tyres (Yokohama, Michelin, or Continental) and unlock complete computerized German 3D alignment, nitrogen air filling, and four heavy-duty brass safety valves absolutely free of charge.",
45
+ badge: {
46
+ text: "Special Bundle",
47
+ type: "bundle"
48
+ },
49
+ highlightFeatures: [
50
+ "Free 3D Alignment (PKR 3,500 value)",
51
+ "Premium German Tubeless Valves",
52
+ "Computerized Wheel Balancing Included",
53
+ "Lifetime Nitrogen topping-up sessions"
54
+ ],
55
+ duration: "Valid until July 31, 2026",
56
+ actionText: "Claim Bundle on WhatsApp",
57
+ gradient: "from-blue-600 via-indigo-900 to-[#0e1017]",
58
+ icon: Gift,
59
+ },
60
+ {
61
+ id: "yokohama-summer-blast",
62
+ title: "Summer Heat-Resistant Tyre Fest",
63
+ subtitle: "Save up to 15% on Eco-Friendly Japanese Yokohama Imports",
64
+ tagline: "Advanced rubber compounds designed for Pakistan's extreme summer asphalt",
65
+ description: "Yokohama BluEarth and Advan series tyres have advanced silica compound protection to withhold high highway friction and temperature spikes during summer without risking tire decompression.",
66
+ badge: {
67
+ text: "Flat Discount",
68
+ type: "discount"
69
+ },
70
+ highlightFeatures: [
71
+ "15% Flat Rebate on selected sizes",
72
+ "Genuine 2026 Import DOT markings",
73
+ "Wet traction Class-A safety ratings",
74
+ "Reduced fuel consumption thread patterns"
75
+ ],
76
+ duration: "Limited stock quota",
77
+ actionText: "Check Size Compatibility",
78
+ gradient: "from-brand-orange via-amber-850 to-[#120703]",
79
+ icon: Percent,
80
+ },
81
+ {
82
+ id: "executive-continental-bundle",
83
+ title: "Continental Premium Executive Deal",
84
+ subtitle: "Get PKR 8,000 Instant Voucher on Set of 4 Continental Tyres",
85
+ tagline: "Engineered in Germany, trusted on Pakistani Motorways",
86
+ description: "Elevate your sedan's comfort level. Continental offers exceptional noise reduction and high-speed stability. Install at our Faisal Town showroom to claim your premium service cashback voucher.",
87
+ badge: {
88
+ text: "Exclusive Reward",
89
+ type: "exclusive"
90
+ },
91
+ highlightFeatures: [
92
+ "PKR 8,000 Instant Cash Voucher",
93
+ "Free Professional fitting & balancing",
94
+ "5-Year official structural backup warranty",
95
+ "Complimentary Suspension checkups"
96
+ ],
97
+ duration: "Hurry! Active this week only",
98
+ actionText: "Secure Cash Voucher",
99
+ gradient: "from-emerald-600 via-teal-950 to-[#0c1613]",
100
+ icon: Sparkles,
101
+ },
102
+ {
103
+ id: "general-tyre-pakistan",
104
+ title: "General Tyre Durability Drive",
105
+ subtitle: "Flat 8% Off on All Local General Tyre Products",
106
+ tagline: "The absolute rugged champion for everyday Lahore traffic and pothole terrains",
107
+ description: "Specially formulated for Pakistani road conditions. Extreme puncture-resistance and thick reinforcement sidewalls guarantee reliable performance for cabs, sedans, and commercial wagons.",
108
+ badge: {
109
+ text: "High Durability",
110
+ type: "limited"
111
+ },
112
+ highlightFeatures: [
113
+ "Economical Lahore pricing",
114
+ "Reinforced steel belt core protection",
115
+ "Excellent water-channeling grooves",
116
+ "Unbeatable lifetime warranty terms"
117
+ ],
118
+ duration: "Offer valid on cash payments",
119
+ actionText: "Inquire stock levels",
120
+ gradient: "from-red-600 via-rose-900 to-[#1e0a0f]",
121
+ icon: ShieldCheck,
122
+ }
123
+ ];
124
+
125
+ // Auto-play timer
126
+ useEffect(() => {
127
+ if (!isAutoPlaying) return;
128
+ const interval = setInterval(() => {
129
+ setCurrentIndex((prev) => (prev + 1) % PROMO_DATA.length);
130
+ }, 6000);
131
+ return () => clearInterval(interval);
132
+ }, [isAutoPlaying, PROMO_DATA.length]);
133
+
134
+ const handlePrev = () => {
135
+ setIsAutoPlaying(false);
136
+ setCurrentIndex((prev) => (prev - 1 + PROMO_DATA.length) % PROMO_DATA.length);
137
+ };
138
+
139
+ const handleNext = () => {
140
+ setIsAutoPlaying(false);
141
+ setCurrentIndex((prev) => (prev + 1) % PROMO_DATA.length);
142
+ };
143
+
144
+ const getBadgeStyles = (type: string) => {
145
+ switch (type) {
146
+ case "discount":
147
+ return "bg-amber-500/15 text-amber-400 border border-amber-500/30";
148
+ case "bundle":
149
+ return "bg-blue-500/15 text-blue-400 border border-blue-500/30";
150
+ case "exclusive":
151
+ return "bg-purple-500/15 text-purple-400 border border-purple-500/30";
152
+ default:
153
+ return "bg-red-500/15 text-red-400 border border-red-500/30";
154
+ }
155
+ };
156
+
157
+ const currentPromo = PROMO_DATA[currentIndex];
158
+ const IconComponent = currentPromo.icon;
159
+
160
+ return (
161
+ <section
162
+ id="promotions"
163
+ className="py-16 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"
164
+ onMouseEnter={() => setIsAutoPlaying(false)}
165
+ onMouseLeave={() => setIsAutoPlaying(true)}
166
+ >
167
+ <div className="text-center max-w-2xl mx-auto mb-10">
168
+ <span className="text-xs font-mono tracking-widest uppercase bg-brand-orange/15 text-brand-orange px-3.5 py-1 rounded-full font-extrabold flex items-center justify-center w-fit mx-auto gap-1.5 shadow-sm">
169
+ <Flame className="w-3.5 h-3.5 animate-pulse" />
170
+ <span>Seasonal Promotions & Offers</span>
171
+ </span>
172
+ <h2 className="text-2xl sm:text-4xl font-display font-extrabold tracking-tight mt-3">
173
+ Special Bundle Packages & Discounts
174
+ </h2>
175
+ <p className={`text-sm mt-2 ${darkMode ? "text-slate-400" : "text-slate-600"}`}>
176
+ Exclusively designed packages for Lahore drivers. Enjoy free alignment services, road hazard upgrades, and seasonal cash savings.
177
+ </p>
178
+ </div>
179
+
180
+ {/* Main Carousel Frame */}
181
+ <div
182
+ className={`relative rounded-3xl overflow-hidden border ${
183
+ darkMode ? "border-white/10 bg-black/40 shadow-2xl" : "border-slate-200 bg-white shadow-xl"
184
+ }`}
185
+ id="promotions-carousel-stage"
186
+ >
187
+ <div className="absolute inset-0 bg-linear-to-r from-black/80 to-transparent z-10 pointer-events-none md:block hidden" />
188
+
189
+ {/* Carousel Content Render */}
190
+ <div className="relative min-h-[460px] md:min-h-[400px] flex flex-col justify-center overflow-hidden">
191
+ <AnimatePresence mode="wait">
192
+ <motion.div
193
+ key={currentPromo.id}
194
+ initial={{ opacity: 0, x: 50 }}
195
+ animate={{ opacity: 1, x: 0 }}
196
+ exit={{ opacity: 0, x: -50 }}
197
+ transition={{ duration: 0.5, ease: "easeInOut" }}
198
+ className={`absolute inset-0 w-full h-full bg-gradient-to-r ${currentPromo.gradient} flex flex-col md:flex-row items-stretch`}
199
+ >
200
+ {/* Left Column: Promotion Info */}
201
+ <div className="flex-1 p-6 sm:p-10 md:p-12 flex flex-col justify-between z-20 text-white select-none">
202
+ <div className="space-y-4">
203
+ <div className="flex flex-wrap items-center gap-2.5">
204
+ <span className={`text-[10px] uppercase font-mono tracking-widest px-3 py-1 rounded-full font-bold ${getBadgeStyles(currentPromo.badge.type)}`}>
205
+ {currentPromo.badge.text}
206
+ </span>
207
+ <span className="text-[10px] font-mono text-slate-300 flex items-center gap-1">
208
+ <Calendar className="w-3.5 h-3.5 text-brand-orange" />
209
+ {currentPromo.duration}
210
+ </span>
211
+ </div>
212
+
213
+ <div className="space-y-1">
214
+ <h3 className="text-xl sm:text-2xl md:text-3xl font-display font-black tracking-tight text-white leading-tight">
215
+ {currentPromo.title}
216
+ </h3>
217
+ <h4 className="text-sm sm:text-base font-semibold text-brand-orange font-mono">
218
+ {currentPromo.subtitle}
219
+ </h4>
220
+ </div>
221
+
222
+ <p className="text-xs sm:text-sm text-slate-300 leading-relaxed font-sans max-w-xl">
223
+ {currentPromo.description}
224
+ </p>
225
+
226
+ {/* Highlights Grid */}
227
+ <div className="grid grid-cols-1 sm:grid-cols-2 gap-2 pt-2">
228
+ {currentPromo.highlightFeatures.map((feat, i) => (
229
+ <div key={i} className="flex items-center space-x-2 text-xs text-slate-200">
230
+ <span className="w-1.5 h-1.5 rounded-full bg-brand-orange shrink-0" />
231
+ <span>{feat}</span>
232
+ </div>
233
+ ))}
234
+ </div>
235
+ </div>
236
+
237
+ {/* Bottom Interactive Trigger Area */}
238
+ <div className="pt-6 sm:pt-0 mt-6 md:mt-0 flex flex-col sm:flex-row items-start sm:items-center gap-4">
239
+ <a
240
+ href={`${BUSINESS_INFO.whatsappUrl}?text=${encodeURIComponent(
241
+ `Hello Haider Brothers, I'm interested in the promotion: *${currentPromo.title}* (${currentPromo.subtitle}).`
242
+ )}`}
243
+ target="_blank"
244
+ rel="noreferrer referrer"
245
+ className="bg-brand-orange hover:bg-brand-orange-dark text-white font-bold text-xs px-5 py-3 rounded-xl flex items-center space-x-2 transition-all shadow-lg shadow-brand-orange/25"
246
+ >
247
+ <span>{currentPromo.actionText}</span>
248
+ <ChevronRight className="w-4 h-4" />
249
+ </a>
250
+ <span className="text-[10px] text-slate-400 font-mono italic flex items-center gap-1">
251
+ <AlertCircle className="w-3.5 h-3.5 text-slate-500 shrink-0" />
252
+ Premium standard tire sizes apply. Rates subject to stock.
253
+ </span>
254
+ </div>
255
+ </div>
256
+
257
+ {/* Right Column: High Fidelity Aesthetic Visual Cover art */}
258
+ <div className="w-full md:w-[40%] bg-black/40 md:bg-transparent relative overflow-hidden flex items-center justify-center p-8 md:p-0">
259
+ {/* Glowing aesthetic backdrop circle matching promotion icon */}
260
+ <div className="absolute w-52 h-52 sm:w-64 sm:h-64 rounded-full bg-brand-orange/15 blur-3xl" />
261
+
262
+ <div className="relative text-center flex flex-col items-center justify-center space-y-3 z-20">
263
+ <div className="p-5 rounded-3xl bg-white/5 border border-white/10 shadow-lg backdrop-blur-md">
264
+ <IconComponent className="w-12 h-12 text-brand-orange animate-pulse" />
265
+ </div>
266
+ <span className="text-xs font-mono tracking-widest uppercase text-slate-300 font-bold block">
267
+ Haider Brothers Deals
268
+ </span>
269
+ <p className="text-[10px] text-slate-400 max-w-[200px] text-center mx-auto">
270
+ {currentPromo.tagline}
271
+ </p>
272
+ </div>
273
+ </div>
274
+ </motion.div>
275
+ </AnimatePresence>
276
+ </div>
277
+
278
+ {/* Manual Carousel Chevron Controls */}
279
+ <button
280
+ onClick={handlePrev}
281
+ className="absolute left-4 top-1/2 -translate-y-1/2 z-30 p-2 rounded-xl border border-white/10 bg-black/30 hover:bg-black/50 text-white hover:scale-105 active:scale-95 transition-all text-sm backdrop-blur-md"
282
+ title="Previous slide"
283
+ type="button"
284
+ id="btn-promo-prev"
285
+ >
286
+ <ChevronLeft className="w-4 h-4" />
287
+ </button>
288
+ <button
289
+ onClick={handleNext}
290
+ className="absolute right-4 top-1/2 -translate-y-1/2 z-30 p-2 rounded-xl border border-white/10 bg-black/30 hover:bg-black/50 text-white hover:scale-105 active:scale-95 transition-all text-sm backdrop-blur-md"
291
+ title="Next slide"
292
+ type="button"
293
+ id="btn-promo-next"
294
+ >
295
+ <ChevronRight className="w-4 h-4" />
296
+ </button>
297
+
298
+ {/* Bullet Progress Indicator bar */}
299
+ <div className="absolute bottom-4 left-1/2 -translate-y-0 -translate-x-1/2 z-30 flex items-center space-x-2 bg-black/20 p-1.5 px-3 rounded-full backdrop-blur-sm">
300
+ {PROMO_DATA.map((promo, idx) => (
301
+ <button
302
+ key={promo.id}
303
+ onClick={() => {
304
+ setIsAutoPlaying(false);
305
+ setCurrentIndex(idx);
306
+ }}
307
+ className={`h-2.5 rounded-full transition-all duration-300 ${
308
+ idx === currentIndex ? "w-6 bg-brand-orange" : "w-2.5 bg-slate-500/50"
309
+ }`}
310
+ title={`Go to slide ${idx + 1}`}
311
+ type="button"
312
+ />
313
+ ))}
314
+ </div>
315
+ </div>
316
+ </section>
317
+ );
318
+ }
src/components/ReviewsSection.tsx ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState } from "react";
2
+ import { Star, Sparkles, CheckCircle2 } from "lucide-react";
3
+
4
+ interface ReviewsSectionProps {
5
+ darkMode: boolean;
6
+ reviews: any[];
7
+ onAddReview: (review: { name: string; rating: number; text: string; date: string; initials: string }) => Promise<void>;
8
+ }
9
+
10
+ export default function ReviewsSection({ darkMode, reviews, onAddReview }: ReviewsSectionProps) {
11
+ const [newReview, setNewReview] = useState({
12
+ name: "",
13
+ text: "",
14
+ rating: 5
15
+ });
16
+ const [formSuccess, setFormSuccess] = useState(false);
17
+ const [hoverRating, setHoverRating] = useState<number | null>(null);
18
+
19
+ const handleSubmit = async (e: React.FormEvent) => {
20
+ e.preventDefault();
21
+ if (!newReview.name.trim() || !newReview.text.trim()) return;
22
+
23
+ // Grab initials
24
+ const initials = newReview.name
25
+ .split(" ")
26
+ .map((n) => n[0])
27
+ .join("")
28
+ .toUpperCase()
29
+ .slice(0, 2) || "U";
30
+
31
+ const newlyCreated = {
32
+ name: newReview.name,
33
+ rating: newReview.rating,
34
+ text: newReview.text,
35
+ date: "Just now",
36
+ initials: initials
37
+ };
38
+
39
+ try {
40
+ await onAddReview(newlyCreated);
41
+ setNewReview({ name: "", text: "", rating: 5 });
42
+ setFormSuccess(true);
43
+ setTimeout(() => setFormSuccess(false), 4500);
44
+ } catch (e) {
45
+ console.error(e);
46
+ }
47
+ };
48
+
49
+ return (
50
+ <section
51
+ id="reviews"
52
+ className={`py-24 relative overflow-hidden transition-colors duration-300 ${
53
+ darkMode ? "bg-brand-dark/95 text-white" : "bg-white text-slate-900"
54
+ }`}
55
+ >
56
+ {/* Visual glowing orbs */}
57
+ <div className="absolute top-1/3 left-0 w-72 h-72 rounded-full bg-brand-orange/10 blur-[90px] pointer-events-none"></div>
58
+
59
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
60
+ <div className="text-center max-w-2xl mx-auto mb-16">
61
+ <div className="inline-flex items-center space-x-2 bg-brand-orange/10 px-3.5 py-1.5 rounded-full text-brand-orange text-xs font-mono tracking-wider uppercase mb-3">
62
+ <Sparkles className="w-3.5 h-3.5" />
63
+ <span>4.8★ Ratings On Google Maps</span>
64
+ </div>
65
+ <h2 className="text-3xl sm:text-4xl font-display font-extrabold tracking-tight">
66
+ Loved By Lahore's Car Community
67
+ </h2>
68
+ <p className={`text-sm mt-3 ${darkMode ? "text-slate-400" : "text-slate-600"}`}>
69
+ Real testimonials from car, SUV, and commercial truck owners who buy their Michelin, Yokohama, and Autogrip tyres from us.
70
+ </p>
71
+ </div>
72
+
73
+ <div className="grid grid-cols-1 lg:grid-cols-12 gap-10 items-start">
74
+ {/* Left Column: List of Reviews */}
75
+ <div className="lg:col-span-7 space-y-6">
76
+ <div className="grid grid-cols-1 gap-4">
77
+ {reviews.map((review, rIdx) => (
78
+ <div
79
+ key={`${review.name}-${rIdx}`}
80
+ className={`p-6 rounded-2xl border transition-all hover:scale-[1.01] duration-300 ${
81
+ darkMode
82
+ ? "bg-[#1c1c24] border-white/5 hover:border-brand-orange/30 shadow-md"
83
+ : "bg-slate-50 border-slate-100 hover:border-brand-orange/30 shadow-sm"
84
+ }`}
85
+ >
86
+ <div className="flex items-start justify-between">
87
+ <div className="flex items-center space-x-3.5">
88
+ <div className="w-10 h-10 rounded-xl bg-orange-600/10 text-brand-orange text-sm font-bold flex items-center justify-center shrink-0">
89
+ {review.initials}
90
+ </div>
91
+ <div>
92
+ <h4 className="font-display font-bold text-sm">{review.name}</h4>
93
+ <span className={`text-[10px] font-mono ${darkMode ? "text-slate-500" : "text-slate-400"}`}>
94
+ Verified User • {review.date}
95
+ </span>
96
+ </div>
97
+ </div>
98
+
99
+ <div className="flex space-x-0.5 text-amber-500">
100
+ {Array.from({ length: 5 }).map((_, sIdx) => (
101
+ <Star
102
+ key={sIdx}
103
+ className={`w-3.5 h-3.5 fill-current ${
104
+ sIdx < Math.floor(review.rating)
105
+ ? "text-amber-500"
106
+ : "text-slate-300 dark:text-slate-600"
107
+ }`}
108
+ />
109
+ ))}
110
+ </div>
111
+ </div>
112
+
113
+ <p className={`text-xs sm:text-sm mt-4 italic leading-relaxed ${
114
+ darkMode ? "text-slate-300" : "text-slate-600"
115
+ }`}>
116
+ "{review.text}"
117
+ </p>
118
+ </div>
119
+ ))}
120
+ </div>
121
+ </div>
122
+
123
+ {/* Right Column: Custom aggregate and submission form */}
124
+ <div className="lg:col-span-5 space-y-6">
125
+ {/* Aggregate Dashboard breakout */}
126
+ <div className={`p-6 rounded-3xl border ${
127
+ darkMode ? "bg-white/5 border-white/5" : "bg-slate-50 border-slate-100 shadow-sm"
128
+ }`}>
129
+ <h3 className="font-display font-extrabold text-lg mb-4">Google Maps Score</h3>
130
+ <div className="flex items-center space-x-4">
131
+ <span className="text-4xl sm:text-5xl font-black text-brand-orange font-display">4.8</span>
132
+ <div>
133
+ <div className="flex space-x-0.5 text-amber-500">
134
+ <Star className="w-5 h-5 fill-current text-amber-500" />
135
+ <Star className="w-5 h-5 fill-current text-amber-500" />
136
+ <Star className="w-5 h-5 fill-current text-amber-500" />
137
+ <Star className="w-5 h-5 fill-current text-amber-500" />
138
+ <Star className="w-5 h-5 fill-current text-amber-500" />
139
+ </div>
140
+ <span className={`text-xs font-mono block mt-1 ${darkMode ? "text-slate-400" : "text-slate-500"}`}>
141
+ Based on {reviews.length} Genuine Feedback Records
142
+ </span>
143
+ </div>
144
+ </div>
145
+ </div>
146
+
147
+ {/* Leave a review interactive form */}
148
+ <div className={`p-6 sm:p-8 rounded-3xl border relative ${
149
+ darkMode ? "glassmorphism" : "glassmorphism-light shadow-md"
150
+ }`}>
151
+ <h3 className="font-display font-bold text-lg">Leave Your Review</h3>
152
+ <p className={`text-xs mt-1.5 ${darkMode ? "text-slate-400" : "text-slate-500"}`}>
153
+ Submit your experience with Haider Brothers Traders. Your feedback will write directly to our Firestore node.
154
+ </p>
155
+
156
+ {formSuccess && (
157
+ <div className="mt-4 p-3 bg-green-500/10 border border-green-500/35 text-green-500 rounded-xl text-xs flex items-center space-x-2">
158
+ <CheckCircle2 className="w-4 h-4 shrink-0" />
159
+ <span>Review recorded in Firestore database successfully!</span>
160
+ </div>
161
+ )}
162
+
163
+ <form onSubmit={handleSubmit} className="space-y-4 mt-6">
164
+ {/* Star rating selector */}
165
+ <div className="space-y-1">
166
+ <label className={`text-xs font-semibold uppercase font-mono block ${darkMode ? "text-slate-300" : "text-slate-600"}`}>
167
+ Your Rating:
168
+ </label>
169
+ <div className="flex space-x-1">
170
+ {Array.from({ length: 5 }).map((_, idx) => {
171
+ const ratingVal = idx + 1;
172
+ return (
173
+ <button
174
+ key={idx}
175
+ type="button"
176
+ onClick={() => setNewReview({ ...newReview, rating: ratingVal })}
177
+ onMouseEnter={() => setHoverRating(ratingVal)}
178
+ onMouseLeave={() => setHoverRating(null)}
179
+ className="p-1 transition-transform active:scale-95"
180
+ title={`Rate ${ratingVal} Stars`}
181
+ id={`star-btn-${ratingVal}`}
182
+ >
183
+ <Star
184
+ className={`w-6 h-6 ${
185
+ ratingVal <= (hoverRating ?? newReview.rating)
186
+ ? "text-amber-500 fill-current"
187
+ : "text-slate-400 dark:text-slate-600"
188
+ }`}
189
+ />
190
+ </button>
191
+ );
192
+ })}
193
+ </div>
194
+ </div>
195
+
196
+ {/* Name field */}
197
+ <div className="space-y-1">
198
+ <label className={`text-xs font-semibold uppercase font-mono block ${darkMode ? "text-slate-300" : "text-slate-600"}`}>
199
+ Full Name
200
+ </label>
201
+ <input
202
+ type="text"
203
+ required
204
+ placeholder="e.g. Hammad Butt"
205
+ value={newReview.name}
206
+ onChange={(e) => setNewReview({ ...newReview, name: e.target.value })}
207
+ className={`w-full p-3 rounded-xl text-sm border focus:ring-2 focus:ring-brand-orange focus:outline-none transition-all ${
208
+ darkMode
209
+ ? "bg-black/40 border-white/10 text-white placeholder-slate-500"
210
+ : "bg-white border-slate-200 text-slate-900 placeholder-slate-400"
211
+ }`}
212
+ id="review-name-input"
213
+ />
214
+ </div>
215
+
216
+ {/* Feedback text */}
217
+ <div className="space-y-1">
218
+ <label className={`text-xs font-semibold uppercase font-mono block ${darkMode ? "text-slate-300" : "text-slate-600"}`}>
219
+ Your Message
220
+ </label>
221
+ <textarea
222
+ required
223
+ rows={3}
224
+ placeholder="Describe your tire purchase, prices, or alignment support experience..."
225
+ value={newReview.text}
226
+ onChange={(e) => setNewReview({ ...newReview, text: e.target.value })}
227
+ className={`w-full p-3 rounded-xl text-sm border focus:ring-2 focus:ring-brand-orange focus:outline-none transition-all ${
228
+ darkMode
229
+ ? "bg-black/40 border-white/10 text-white placeholder-slate-500"
230
+ : "bg-white border-slate-200 text-slate-900 placeholder-slate-400"
231
+ }`}
232
+ id="review-text-input"
233
+ />
234
+ </div>
235
+
236
+ {/* Submit button */}
237
+ <button
238
+ type="submit"
239
+ className="w-full bg-brand-orange hover:bg-brand-orange-dark text-white font-bold py-3.5 rounded-xl text-sm shadow-md shadow-brand-orange/20 transition-all hover:-translate-y-0.5"
240
+ id="submit-review-btn"
241
+ >
242
+ Post Review Onto Feed
243
+ </button>
244
+ </form>
245
+ </div>
246
+ </div>
247
+ </div>
248
+ </div>
249
+ </section>
250
+ );
251
+ }
src/components/TireCard.tsx ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { motion } from "motion/react";
2
+ import { MessageSquare, ShieldCheck, Compass, Eye } from "lucide-react";
3
+ import { TireProduct, BUSINESS_INFO } from "../data";
4
+
5
+ interface TireCardProps {
6
+ tire: TireProduct;
7
+ darkMode: boolean;
8
+ onSelect: (tire: TireProduct) => void;
9
+ key?: string;
10
+ }
11
+
12
+ export default function TireCard({ tire, darkMode, onSelect }: TireCardProps) {
13
+ // Format WhatsApp Link
14
+ const waPreFilledText = `Hello Haider Brothers, I want price & stock for ${tire.name} (Size: ${tire.size})`;
15
+ const encodedText = encodeURIComponent(waPreFilledText);
16
+ const waLink = `${BUSINESS_INFO.whatsappUrl}?text=${encodedText}`;
17
+
18
+ // Get brand indicator styling
19
+ const isAutogrip = tire.brand.toLowerCase() === "autogrip";
20
+
21
+ return (
22
+ <motion.div
23
+ layout
24
+ initial={{ opacity: 0, scale: 0.95 }}
25
+ animate={{ opacity: 1, scale: 1 }}
26
+ exit={{ opacity: 0, scale: 0.95 }}
27
+ whileHover={{ y: -6, transition: { duration: 0.2 } }}
28
+ transition={{ duration: 0.3 }}
29
+ className={`relative rounded-3xl overflow-hidden flex flex-col justify-between border transition-all duration-300 group ${
30
+ darkMode
31
+ ? "bg-[#1c1c24] border-white/5 hover:border-brand-orange/40 shadow-xl shadow-black/20"
32
+ : "bg-white border-slate-100/90 hover:border-brand-orange/40 shadow-md shadow-slate-100"
33
+ }`}
34
+ >
35
+ {/* Glow highlight on hover */}
36
+ <div className="absolute top-0 left-0 right-0 h-[3px] bg-gradient-to-r from-transparent via-brand-orange/0 to-transparent group-hover:via-brand-orange/70 transition-all duration-500"></div>
37
+
38
+ {/* Card Header Media area */}
39
+ <div className="relative aspect-video overflow-hidden bg-slate-900 group">
40
+ {/* Dynamic Image with Lazy Load */}
41
+ <img
42
+ src={tire.image}
43
+ alt={tire.name}
44
+ loading="lazy"
45
+ className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-500 brightness-95"
46
+ referrerPolicy="no-referrer"
47
+ />
48
+
49
+ {/* Shadow overlays */}
50
+ <div className="absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-black/30 pointer-events-none"></div>
51
+
52
+ {/* Top-row Glass badges */}
53
+ <div className="absolute top-3 left-3 right-3 flex justify-between items-center">
54
+ <span className="backdrop-blur-md bg-black/50 text-white border border-white/10 text-[10px] font-mono font-semibold px-2.5 py-1 rounded-full uppercase tracking-wider">
55
+ {tire.badge}
56
+ </span>
57
+ <span className="backdrop-blur-md bg-brand-orange/90 text-white text-[10px] font-mono font-bold px-2.5 py-1 rounded-full uppercase tracking-wider shadow-sm shadow-brand-orange/20">
58
+ {tire.size}
59
+ </span>
60
+ </div>
61
+
62
+ {/* Brand Label block (Bottom-left absolute overlay over media) */}
63
+ <div className="absolute bottom-3 left-3 flex items-center space-x-1.5 backdrop-blur-md bg-black/60 px-2.5 py-1 rounded-lg border border-white/5 text-white">
64
+ <span className="text-xs">⚙️</span>
65
+ <span className={`text-xs font-display font-extrabold tracking-wider uppercase ${isAutogrip ? "text-brand-orange-light" : "text-white"}`}>
66
+ {tire.brand}
67
+ </span>
68
+ </div>
69
+ </div>
70
+
71
+ {/* Card Detail Content */}
72
+ <div className="p-5 flex-1 flex flex-col justify-between space-y-4">
73
+ <div>
74
+ {/* Tire Name */}
75
+ <h4 className={`font-display text-lg font-bold tracking-tight leading-snug group-hover:text-brand-orange transition-colors ${
76
+ darkMode ? "text-white" : "text-slate-900"
77
+ }`}>
78
+ {tire.name}
79
+ </h4>
80
+
81
+ {/* Quick specs / Description */}
82
+ <p className={`text-xs mt-1.5 leading-relaxed line-clamp-2 ${
83
+ darkMode ? "text-slate-400" : "text-slate-500"
84
+ }`}>
85
+ {tire.description}
86
+ </p>
87
+
88
+ {/* Core Feature highlight badge */}
89
+ <div className={`mt-3 flex items-center space-x-1.5 text-xs font-semibold ${
90
+ darkMode ? "text-slate-300" : "text-slate-700"
91
+ }`}>
92
+ <Compass className="w-3.5 h-3.5 text-brand-orange shrink-0" />
93
+ <span className="truncate">{tire.feature}</span>
94
+ </div>
95
+ </div>
96
+
97
+ {/* Price & Action row */}
98
+ <div className="pt-4 border-t border-black/5 dark:border-white/5">
99
+ <div className="flex items-baseline justify-between mb-4">
100
+ <span className={`text-[10px] uppercase font-mono tracking-wider ${darkMode ? "text-slate-400" : "text-slate-500"}`}>
101
+ Dealer Net Price
102
+ </span>
103
+ <span className="text-brand-orange font-display text-xl font-extrabold tracking-tight">
104
+ PKR {tire.price.toLocaleString()}
105
+ </span>
106
+ </div>
107
+
108
+ <div className="grid grid-cols-5 gap-2">
109
+ {/* Quick Specs Quickview Button */}
110
+ <button
111
+ onClick={() => onSelect(tire)}
112
+ className={`col-span-2 flex items-center justify-center p-3 rounded-xl transition-all ${
113
+ darkMode
114
+ ? "bg-white/5 hover:bg-white/10 border border-white/5 text-slate-300 hover:text-white"
115
+ : "bg-slate-100 hover:bg-slate-200 border border-slate-200/50 text-slate-700 hover:text-slate-950"
116
+ }`}
117
+ title="View Specifications"
118
+ id={`quickview-${tire.id}`}
119
+ >
120
+ <Eye className="w-4.5 h-4.5" />
121
+ </button>
122
+
123
+ {/* WhatsApp Enquiry Button */}
124
+ <a
125
+ href={waLink}
126
+ target="_blank"
127
+ rel="noreferrer referrer"
128
+ className="col-span-3 flex items-center justify-center space-x-1.5 bg-green-600 hover:bg-green-500 text-white py-3 px-3 rounded-xl text-xs font-bold transition-all hover:shadow-md hover:shadow-green-500/10"
129
+ id={`whatsapp-enquiry-${tire.id}`}
130
+ >
131
+ <MessageSquare className="w-4.5 h-4.5 shrink-0" />
132
+ <span>Quick Enquiry</span>
133
+ </a>
134
+ </div>
135
+ </div>
136
+ </div>
137
+ </motion.div>
138
+ );
139
+ }
src/components/VehicleFitmentWidget.tsx ADDED
@@ -0,0 +1,296 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState } from "react";
2
+ import { Search, Car, HelpCircle, ArrowRight, MessageSquare, CheckCircle } from "lucide-react";
3
+ import { PAK_CAR_FITMENTS, CarFitment } from "../lib/tireService";
4
+ import { BUSINESS_INFO } from "../data";
5
+
6
+ interface VehicleFitmentWidgetProps {
7
+ darkMode: boolean;
8
+ onFilterSize: (size: string) => void;
9
+ }
10
+
11
+ export default function VehicleFitmentWidget({ darkMode, onFilterSize }: VehicleFitmentWidgetProps) {
12
+ const [selectedMake, setSelectedMake] = useState<string>("");
13
+ const [selectedModel, setSelectedModel] = useState<string>("");
14
+ const [selectedVariant, setSelectedVariant] = useState<any>(null);
15
+ const [typedSearch, setTypedSearch] = useState<string>("");
16
+
17
+ // Get models for selected make
18
+ const availableModels = selectedMake
19
+ ? PAK_CAR_FITMENTS.filter((f) => f.make.toLowerCase() === selectedMake.toLowerCase())
20
+ : [];
21
+
22
+ // Handle Make changes
23
+ const handleMakeChange = (make: string) => {
24
+ setSelectedMake(make);
25
+ setSelectedModel("");
26
+ setSelectedVariant(null);
27
+ };
28
+
29
+ // Handle Model changes
30
+ const handleModelChange = (modelName: string) => {
31
+ setSelectedModel(modelName);
32
+ const fitment = availableModels.find((m) => m.model.toLowerCase() === modelName.toLowerCase());
33
+ if (fitment && fitment.variants.length > 0) {
34
+ setSelectedVariant(fitment.variants[0]);
35
+ } else {
36
+ setSelectedVariant(null);
37
+ }
38
+ };
39
+
40
+ // Quick auto-search matching
41
+ const filteredSearchFitments = typedSearch.trim()
42
+ ? PAK_CAR_FITMENTS.flatMap((fit) =>
43
+ fit.variants.map((v) => ({
44
+ make: fit.make,
45
+ model: fit.model,
46
+ variantName: v.name,
47
+ engine: v.engine,
48
+ size: v.recommendedSize
49
+ }))
50
+ ).filter((item) =>
51
+ `${item.make} ${item.model} ${item.variantName}`.toLowerCase().includes(typedSearch.toLowerCase())
52
+ ).slice(0, 4)
53
+ : [];
54
+
55
+ const handleSelectQuickMatch = (item: any) => {
56
+ setSelectedMake(item.make);
57
+ setSelectedModel(item.model);
58
+ const fitObj = PAK_CAR_FITMENTS.find(f => f.make === item.make && f.model === item.model);
59
+ const varObj = fitObj?.variants.find(v => v.name === item.variantName);
60
+ setSelectedVariant(varObj || null);
61
+ setTypedSearch("");
62
+ };
63
+
64
+ return (
65
+ <div className={`p-6 sm:p-8 rounded-3xl border transition-all duration-300 hover:scale-[1.005] hover:border-brand-orange/30 ${
66
+ darkMode ? "glassmorphism box-glow hover:shadow-[0_0_25px_rgba(255,85,0,0.25)]" : "glassmorphism-light shadow-xl hover:shadow-2xl"
67
+ }`}>
68
+ <div className="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-6">
69
+ <div>
70
+ <span className="text-xs font-mono font-bold tracking-widest text-brand-orange uppercase">
71
+ ⚡ Fitment Recommendations Guide
72
+ </span>
73
+ <h3 className="text-xl sm:text-2xl font-display font-bold mt-1 tracking-tight">
74
+ Find Recommended Tyre Size by Vehicle model
75
+ </h3>
76
+ <p className={`text-xs mt-1 ${darkMode ? "text-slate-400" : "text-slate-500"}`}>
77
+ Configure your car's profile to extract the certified OEM tire dimensions immediately.
78
+ </p>
79
+ </div>
80
+
81
+ <div className="flex items-center space-x-2 text-xs font-mono bg-brand-orange/5 text-brand-orange px-3 py-1.5 rounded-xl border border-brand-orange/10 self-start md:self-auto">
82
+ <Car className="w-3.5 h-3.5" />
83
+ <span>Pakistan Specs Database</span>
84
+ </div>
85
+ </div>
86
+
87
+ {/* Interactive Tabs */}
88
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
89
+ {/* Left Side: Selectors */}
90
+ <div className="space-y-4">
91
+ {/* Quick Search Input */}
92
+ <div className="relative">
93
+ <span className={`text-xs font-mono font-semibold block mb-1 px-1 ${darkMode ? "text-slate-300" : "text-slate-600"}`}>
94
+ Search Car Name (e.g. 'Corolla', 'Civic', 'Alto')
95
+ </span>
96
+ <div className="relative">
97
+ <input
98
+ type="text"
99
+ placeholder="Type car name..."
100
+ value={typedSearch}
101
+ onChange={(e) => setTypedSearch(e.target.value)}
102
+ className={`w-full p-3 pl-10 rounded-xl text-xs border focus:ring-1 focus:ring-brand-orange focus:outline-none ${
103
+ darkMode ? "bg-black/40 border-white/10 text-white placeholder-slate-500" : "bg-white border-slate-200 text-slate-800 placeholder-slate-400"
104
+ }`}
105
+ id="car-search-input"
106
+ />
107
+ <Search className="w-4 h-4 text-slate-400 absolute left-3.5 top-1/2 -translate-y-1/2" />
108
+ </div>
109
+
110
+ {/* Auto-suggest results */}
111
+ {filteredSearchFitments.length > 0 && (
112
+ <div className={`absolute left-0 right-0 mt-1.5 rounded-xl border p-2 z-10 shadow-2xl ${
113
+ darkMode ? "bg-[#1c1c24] border-white/10" : "bg-white border-slate-200"
114
+ }`}>
115
+ {filteredSearchFitments.map((fit, idx) => (
116
+ <button
117
+ key={idx}
118
+ type="button"
119
+ onClick={() => handleSelectQuickMatch(fit)}
120
+ className={`w-full text-left px-3 py-2 rounded-lg text-xs flex justify-between items-center transition-all ${
121
+ darkMode ? "hover:bg-white/5 text-slate-200" : "hover:bg-slate-50 text-slate-800"
122
+ }`}
123
+ >
124
+ <div>
125
+ <span className="font-bold text-brand-orange">{fit.make}</span> {fit.model} <span className="text-slate-400">({fit.variantName})</span>
126
+ </div>
127
+ <span className="font-mono text-[10px] bg-slate-500/10 px-2 py-0.5 rounded text-brand-orange font-bold">
128
+ {fit.size}
129
+ </span>
130
+ </button>
131
+ ))}
132
+ </div>
133
+ )}
134
+ </div>
135
+
136
+ <div className="grid grid-cols-2 gap-4">
137
+ {/* Make Selector */}
138
+ <div>
139
+ <label className={`text-xs font-mono font-semibold block mb-1 px-1 ${darkMode ? "text-slate-300" : "text-slate-600"}`}>
140
+ Select Brand (Make)
141
+ </label>
142
+ <select
143
+ value={selectedMake}
144
+ onChange={(e) => handleMakeChange(e.target.value)}
145
+ className={`w-full p-3 rounded-xl text-xs border focus:ring-1 focus:ring-brand-orange focus:outline-none transition-all ${
146
+ darkMode ? "bg-black/40 border-white/10 text-white" : "bg-white border-slate-200 text-slate-800"
147
+ }`}
148
+ id="vehicle-make-select"
149
+ >
150
+ <option value="">-- Choose Make --</option>
151
+ <option value="toyota">Toyota Pakistan</option>
152
+ <option value="honda">Honda Atlas</option>
153
+ <option value="suzuki">Pak Suzuki</option>
154
+ <option value="kia">KIA Lucky Motors</option>
155
+ <option value="hyundai">Hyundai Nishat</option>
156
+ <option value="changan">Changan Pakistan</option>
157
+ <option value="mg">MG Motors</option>
158
+ <option value="haval">Haval Pakistan</option>
159
+ <option value="proton">Proton Pakistan</option>
160
+ <option value="dfsk">DFSK Motors</option>
161
+ </select>
162
+ </div>
163
+
164
+ {/* Model Selector */}
165
+ <div>
166
+ <label className={`text-xs font-mono font-semibold block mb-1 px-1 ${darkMode ? "text-slate-300" : "text-slate-600"}`}>
167
+ Select Model
168
+ </label>
169
+ <select
170
+ value={selectedModel}
171
+ disabled={!selectedMake}
172
+ onChange={(e) => handleModelChange(e.target.value)}
173
+ className={`w-full p-3 rounded-xl text-xs border focus:ring-1 focus:ring-brand-orange focus:outline-none transition-all disabled:opacity-50 ${
174
+ darkMode ? "bg-black/40 border-white/10 text-white" : "bg-white border-slate-200 text-slate-800"
175
+ }`}
176
+ id="vehicle-model-select"
177
+ >
178
+ <option value="">-- Choose Model --</option>
179
+ {Array.from(new Set(availableModels.map((m) => m.model))).map((model) => (
180
+ <option key={model} value={model.toLowerCase()}>
181
+ {model}
182
+ </option>
183
+ ))}
184
+ </select>
185
+ </div>
186
+ </div>
187
+
188
+ {/* Variant Selector */}
189
+ {selectedModel && (
190
+ <div>
191
+ <label className={`text-xs font-mono font-semibold block mb-1 px-1 ${darkMode ? "text-slate-300" : "text-slate-600"}`}>
192
+ Select Car Variant / Engine Specifications
193
+ </label>
194
+ <div className="flex flex-wrap gap-2">
195
+ {availableModels
196
+ .find((m) => m.model.toLowerCase() === selectedModel.toLowerCase())
197
+ ?.variants.map((v) => (
198
+ <button
199
+ key={v.name}
200
+ onClick={() => setSelectedVariant(v)}
201
+ className={`px-3 py-2 rounded-xl text-xs font-medium border transition-all ${
202
+ selectedVariant?.name === v.name
203
+ ? "border-brand-orange text-brand-orange bg-brand-orange/10 font-bold"
204
+ : darkMode
205
+ ? "border-white/5 bg-white/5 text-slate-300 hover:border-white/20"
206
+ : "border-slate-200 bg-white text-slate-700 hover:border-slate-300"
207
+ }`}
208
+ id={`variant-btn-${v.name.replace(/\s+/g, '-').toLowerCase()}`}
209
+ >
210
+ {v.name} <span className="text-[10px] opacity-75">({v.engine})</span>
211
+ </button>
212
+ ))}
213
+ </div>
214
+ </div>
215
+ )}
216
+ </div>
217
+
218
+ {/* Right Side: Recommendation Results Card container */}
219
+ <div className="flex flex-col justify-between h-full">
220
+ {selectedVariant ? (
221
+ <div className={`p-5 rounded-2xl border flex flex-col justify-between h-full bg-linear-to-br transition-all duration-300 ${
222
+ darkMode ? "bg-white/5 border-white/10 text-white" : "bg-slate-100/70 border-slate-200 text-slate-900"
223
+ }`}>
224
+ <div>
225
+ <div className="flex justify-between items-start mb-4">
226
+ <div>
227
+ <span className="text-[10px] font-mono tracking-widest text-brand-orange uppercase font-bold">
228
+ PROFILING MATCH FOUND
229
+ </span>
230
+ <h4 className="font-display font-black text-lg sm:text-l tracking-tight mt-0.5 uppercase">
231
+ {selectedMake} {selectedModel}
232
+ </h4>
233
+ <p className={`text-xs ${darkMode ? "text-slate-400" : "text-slate-500"}`}>
234
+ Variant: <span className="font-semibold text-brand-orange">{selectedVariant.name}</span> ({selectedVariant.engine})
235
+ </p>
236
+ </div>
237
+ <span className="p-2 rounded-lg bg-green-500/10 text-green-500">
238
+ <CheckCircle className="w-5 h-5" />
239
+ </span>
240
+ </div>
241
+
242
+ <div className={`p-4 rounded-xl text-center border my-3 ${
243
+ darkMode ? "bg-black/30 border-white/5" : "bg-white border-slate-200"
244
+ }`}>
245
+ <span className={`text-[11px] font-mono block ${darkMode ? "text-slate-400" : "text-slate-500"}`}>
246
+ RECOMMENDED TYRE SIZE
247
+ </span>
248
+ <span className="font-display font-black text-2xl sm:text-3xl tracking-wider text-brand-orange block my-1">
249
+ {selectedVariant.recommendedSize}
250
+ </span>
251
+ <span className={`text-[10px] block leading-normal ${darkMode ? "text-slate-500" : "text-slate-400"}`}>
252
+ Spec matches the exact manufacturing profile for road pressure distribution & safety rules of Lahore.
253
+ </span>
254
+ </div>
255
+ </div>
256
+
257
+ {/* Practical CTAs */}
258
+ <div className="flex flex-col sm:flex-row gap-2 mt-4">
259
+ <button
260
+ onClick={() => onFilterSize(selectedVariant.recommendedSize)}
261
+ className="flex-1 bg-gradient-to-r from-brand-orange to-red-600 hover:from-brand-orange hover:to-red-700 text-white font-bold p-2.5 rounded-xl text-xs flex items-center justify-center space-x-1 shadow-md hover:shadow-lg transition-all"
262
+ id="fitment-search-match-btn"
263
+ >
264
+ <span>Filter Catalogue for this Size</span>
265
+ <ArrowRight className="w-3.5 h-3.5" />
266
+ </button>
267
+
268
+ <a
269
+ href={`${BUSINESS_INFO.whatsappUrl}?text=Asalam-o-ALaykum%20Haider%20Brothers%20Traders,%20I'm%20looking%20for%20tyres%20in%20specification%20size%20${selectedVariant.recommendedSize}%20for%20my%20${selectedMake}%20${selectedModel}%20${selectedVariant.name}%20car.%20What%20brands%20are%20available%20now?`}
270
+ target="_blank"
271
+ rel="noreferrer referrer"
272
+ className="bg-green-600 hover:bg-green-700 text-white font-bold px-3 py-2.5 rounded-xl text-xs flex items-center justify-center space-x-1 shadow-md transition-all shrink-0"
273
+ >
274
+ <MessageSquare className="w-4 h-4" />
275
+ <span>WhatsApp Query</span>
276
+ </a>
277
+ </div>
278
+ </div>
279
+ ) : (
280
+ <div className={`p-10 rounded-2xl border border-dashed flex flex-col items-center justify-center text-center h-full ${
281
+ darkMode ? "bg-white/5 border-white/10" : "bg-slate-100/50 border-slate-200"
282
+ }`}>
283
+ <span className="text-4xl animate-pulse block mb-3">🚗</span>
284
+ <h4 className={`font-display font-bold text-sm ${darkMode ? "text-slate-300" : "text-slate-700"}`}>
285
+ Select Car to Extract Fitments
286
+ </h4>
287
+ <p className={`text-xs max-w-[280px] mt-2 leading-relaxed ${darkMode ? "text-slate-500" : "text-slate-400"}`}>
288
+ Select brand make/model variables on the left, or query the car name to load the customized profile recommendations instantly.
289
+ </p>
290
+ </div>
291
+ )}
292
+ </div>
293
+ </div>
294
+ </div>
295
+ );
296
+ }
src/data.ts ADDED
@@ -0,0 +1,393 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export interface TireProduct {
2
+ id: string;
3
+ name: string;
4
+ brand: string;
5
+ size: string;
6
+ feature: string;
7
+ price: number;
8
+ badge: string;
9
+ segment: 'hot' | 'new' | 'famous';
10
+ image: string;
11
+ description: string;
12
+ stock?: number;
13
+ }
14
+
15
+ export interface Review {
16
+ name: string;
17
+ rating: number;
18
+ text: string;
19
+ date: string;
20
+ initials: string;
21
+ }
22
+
23
+ export const BUSINESS_INFO = {
24
+ name: "Haider Brothers Traders",
25
+ rating: 4.8,
26
+ reviewsCount: 5,
27
+ phone: "+92 302 4594403",
28
+ phoneRaw: "+923024594403",
29
+ whatsappUrl: "https://wa.me/923024594403",
30
+ address: "36-C-1, Maulana Shaukat Ali Road, Block C-1 Faisal Town, Lahore, Punjab 54700, Pakistan",
31
+ locationCode: "F8G5+H9, Lahore",
32
+ hours: "Open 24 hours (24/7)",
33
+ mapIframeUrl: "https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3402.1385474320295!2d74.30589377519962!3d31.476454149202534!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x3919030037af462f%3A0xc479c225327c71d6!2sHaider%20Brothers%20Traders!5e0!3m2!1sen!2spk!4v1718500000000!5m2!1sen!2spk"
34
+ };
35
+
36
+ export const BRANDS = [
37
+ { name: "Autogrip", hasDirectAccent: true },
38
+ { name: "Michelin", hasDirectAccent: false },
39
+ { name: "Bridgestone", hasDirectAccent: false },
40
+ { name: "Pirelli", hasDirectAccent: false },
41
+ { name: "Goodyear", hasDirectAccent: false },
42
+ { name: "Dunlop", hasDirectAccent: false },
43
+ { name: "General Tire", hasDirectAccent: false },
44
+ { name: "Continental", hasDirectAccent: false },
45
+ { name: "Yokohama", hasDirectAccent: false },
46
+ { name: "Hankook", hasDirectAccent: false }
47
+ ];
48
+
49
+ export const REVIEWS: Review[] = [
50
+ {
51
+ name: "gitsitmarketing",
52
+ rating: 5,
53
+ text: "Excellent service and quality of tires. My experience was good.",
54
+ date: "1 month ago",
55
+ initials: "GM"
56
+ },
57
+ {
58
+ name: "M Bilal",
59
+ rating: 5,
60
+ text: "My experience was great with haider brother traders. They provided me good service and high quality product.",
61
+ date: "2 months ago",
62
+ initials: "MB"
63
+ },
64
+ {
65
+ name: "Muhammad Ramzan",
66
+ rating: 5,
67
+ text: "Great quality and service.",
68
+ date: "3 months ago",
69
+ initials: "MR"
70
+ },
71
+ {
72
+ name: "Zain Ul Abideen",
73
+ rating: 4.5,
74
+ text: "Very reliable place for 24/7 emergencies. Broke down late at night, and they fixed my alignment and tires quickly.",
75
+ date: "4 months ago",
76
+ initials: "ZA"
77
+ },
78
+ {
79
+ name: "Ahmad Raza",
80
+ rating: 5,
81
+ text: "Genuine products at reasonable wholesale-level rates in Lahore. Highly recommended!",
82
+ date: "5 months ago",
83
+ initials: "AR"
84
+ }
85
+ ];
86
+
87
+ export const TIRE_PRODUCTS: TireProduct[] = [
88
+ // --- HOT SELLING (9 products) ---
89
+ {
90
+ id: "hot-1",
91
+ name: "Autogrip F107 Comfort",
92
+ brand: "Autogrip",
93
+ size: "185/65R14",
94
+ feature: "All-season wet stability & 185/65R14 smooth ride",
95
+ price: 12500,
96
+ badge: "Hot Selling",
97
+ segment: "hot",
98
+ image: "/src/assets/images/comfort_tire_1781509452553.jpg",
99
+ description: "Budget-friendly comfort tyre optimized for classic sedan and hatchback daily driving. Low rolling noise, enhanced tread compound, and great drainage to prevent aquaplaning in Lahore's heavy monsoon seasons. Highly compatible with Corolla and older Civic variants."
100
+ },
101
+ {
102
+ id: "hot-2",
103
+ name: "Dunlop SP Touring T1",
104
+ brand: "Dunlop",
105
+ size: "175/65R15",
106
+ feature: "Extra-durable rubber & premium fuel saver - 175/65R15",
107
+ price: 14800,
108
+ badge: "Best Seller",
109
+ segment: "hot",
110
+ image: "/src/assets/images/comfort_tire_1781509452553.jpg",
111
+ description: "Highly stable and optimized touring compound engineered to withstand rough tarmac, heatwaves, and long mileage. Outstanding choice for Toyota Yaris, Honda City, and Suzuki Swift owners in Pakistan who value long-lasting performance and maximum fuel economy."
112
+ },
113
+ {
114
+ id: "hot-3",
115
+ name: "Yokohama BluEarth-Es ES32",
116
+ brand: "Yokohama",
117
+ size: "195/65R15",
118
+ feature: "Premium silica compound & low noise - 195/65R15",
119
+ price: 21500,
120
+ badge: "Premium Hot",
121
+ segment: "hot",
122
+ image: "/src/assets/images/comfort_tire_1781509452553.jpg",
123
+ description: "Premium JDM quiet tire with state-of-the-art nanocomposite silica compound. Delivers superior wet/dry road grip, luxurious shock-absorption over Lahore potholes, and unmatched stability at high speeds for modern Honda Civic, Toyota Corolla, and luxury hybrid vehicles."
124
+ },
125
+ {
126
+ id: "hot-4",
127
+ name: "General Tire Euro Glide",
128
+ brand: "General Tire",
129
+ size: "165/70R14",
130
+ feature: "Locally optimized rugged tread - 165/70R14",
131
+ price: 9800,
132
+ badge: "Budget King",
133
+ segment: "hot",
134
+ image: "/src/assets/images/comfort_tire_1781509452553.jpg",
135
+ description: "Genuine local heavy-duty tire manufactured explicitly for Pakistani hot climates and uneven street grids. Strong side casing provides extreme puncture protection that is loved by Suzuki Cultus, Wagon R, and JDM imported hatchback drivers in Punjab."
136
+ },
137
+ {
138
+ id: "hot-5",
139
+ name: "Bridgestone Ecopia EP150",
140
+ brand: "Bridgestone",
141
+ size: "185/65R15",
142
+ feature: "Ultra eco-friendly, fuel-efficiency design - 185/65R15",
143
+ price: 18200,
144
+ badge: "Hot Selling",
145
+ segment: "hot",
146
+ image: "/src/assets/images/comfort_tire_1781509452553.jpg",
147
+ description: "Advanced low-rolling technology reduces carbon footprint while saving up to 6% in fuel fuel-consumption on daily commutes. Solid wet braking performance makes this Ecopia series a prime product for Toyota Yaris, Honda City, and Suzuki Swift."
148
+ },
149
+ {
150
+ id: "hot-6",
151
+ name: "Mirage MR-162 Utility",
152
+ brand: "General Tire", // Classifying Mirage as premium general import or general tier
153
+ size: "145/80R13",
154
+ feature: "Affordable Alto spec, long durability - 145/80R13",
155
+ price: 8300,
156
+ badge: "Alto Spec",
157
+ segment: "hot",
158
+ image: "/src/assets/images/comfort_tire_1781509452553.jpg",
159
+ description: "Extremely affordable imported tire featuring deep treads and extra-thick sidewalls designed to survive bad terrain. Specifically matches the highly popular Suzuki Alto, Daihatsu Mira, and generic 660cc imported Japanese kei cars in Lahore."
160
+ },
161
+ {
162
+ id: "hot-7",
163
+ name: "Dunlop ECO EC201 Eco-Drive",
164
+ brand: "Dunlop",
165
+ size: "145/80R13",
166
+ feature: "Premium Alto comfort & safety grip - 145/80R13",
167
+ price: 10500,
168
+ badge: "Alto Premium",
169
+ segment: "hot",
170
+ image: "/src/assets/images/comfort_tire_1781509452553.jpg",
171
+ description: "The ultimate premium radial upgrade for the Suzuki Alto and Wagon R community. Features ultra-quiet rolling technology, outstanding grip on dry asphalt, and superior control under emergency braking at tight city crossings."
172
+ },
173
+ {
174
+ id: "hot-8",
175
+ name: "Yokohama Advan dB V552",
176
+ brand: "Yokohama",
177
+ size: "215/55R17",
178
+ feature: "Extreme noise reduction & supreme cushion - 215/55R17",
179
+ price: 38000,
180
+ badge: "Ultra Luxury",
181
+ segment: "hot",
182
+ image: "/src/assets/images/sport_tire_1781509490802.jpg",
183
+ description: "Incredibly quiet premium tyre designed for executive luxury. Uses Yokohama's signature chemical compound to cancel asphalt resonance. Provides a cloud-like ride for Honda Civic Oriel/RS, Toyota Corolla Altis Grande, and imported Honda Vezel models."
184
+ },
185
+ {
186
+ id: "hot-9",
187
+ name: "Michelin Primacy 4 Premium",
188
+ brand: "Michelin",
189
+ size: "215/60R16",
190
+ feature: "World-class quiet touring & wet defense - 215/60R16",
191
+ price: 44500,
192
+ badge: "Elite Safety",
193
+ segment: "hot",
194
+ image: "/src/assets/images/comfort_tire_1781509452553.jpg",
195
+ description: "Industry-standard for safety and comfort. Even after extensive mileage, its patented tire pattern drains water with consistent grip. Excellent match for high-profile luxury sedans like Honda Accord, Toyota Camry, and customized JDM vehicles."
196
+ },
197
+
198
+ // --- NEW BRANDS & ARRIVALS (8 products) ---
199
+ {
200
+ id: "new-1",
201
+ name: "Autogrip SUV A606 Sport",
202
+ brand: "Autogrip",
203
+ size: "235/60R18",
204
+ feature: "Rugged road tracking & heavy SUV compound - 235/60R18",
205
+ price: 22000,
206
+ badge: "New Arrival",
207
+ segment: "new",
208
+ image: "/src/assets/images/suv_tire_1781509472580.jpg",
209
+ description: "Rugged block styling and dual steel belts for midsize crossovers. Delivers long tread life, off-road traction, and stable high-speed tracking on Ring Road Lahore. Excellent fit for KIA Sportage, Hyundai Tucson, and Proton X70."
210
+ },
211
+ {
212
+ id: "new-2",
213
+ name: "Yokohama Geolandar A/T G015",
214
+ brand: "Yokohama",
215
+ size: "265/65R17",
216
+ feature: "Extreme double-shield all-terrain - 265/65R17",
217
+ price: 46000,
218
+ badge: "New Arrival",
219
+ segment: "new",
220
+ image: "/src/assets/images/suv_tire_1781509472580.jpg",
221
+ description: "Designed for full-sized off-road SUVs. Offers severe snow-rated traction blocks and triple-polymer compound to resist extreme cuts/chipping on rough gravel and inter-city routes. Ideal for Toyota Hilux Revo, Toyota Fortuner, and Prado owners."
222
+ },
223
+ {
224
+ id: "new-3",
225
+ name: "Bridgestone Dueler H/P Sport",
226
+ brand: "Bridgestone",
227
+ size: "225/55R18",
228
+ feature: "High-horsepower highway response - 225/55R18",
229
+ price: 39500,
230
+ badge: "Premium SUV",
231
+ segment: "new",
232
+ image: "/src/assets/images/suv_tire_1781509472580.jpg",
233
+ description: "Outstanding summer tire built to extract extreme cornering response and stable wet traction for premium crossover profiles. Perfect OEM spec for KIA Sportage, Hyundai Tucson, and MG HS sport package upgrades."
234
+ },
235
+ {
236
+ id: "new-4",
237
+ name: "Michelin Pilot Sport 5 Precision",
238
+ brand: "Michelin",
239
+ size: "225/45R17",
240
+ feature: "Max sport handling & quick track response - 225/45R17",
241
+ price: 35000,
242
+ badge: "New Arrival",
243
+ segment: "new",
244
+ image: "/src/assets/images/sport_tire_1781509490802.jpg",
245
+ description: "Experience incredible luxury precise control. Features Dynamic Response technology for high cornering precision and immediate dry track response. Perfect premium upgrade for modern luxury sedans and modified sports custom vehicles."
246
+ },
247
+ {
248
+ id: "new-5",
249
+ name: "Continental PremiumContact 7 Pro",
250
+ brand: "Continental",
251
+ size: "195/55R15",
252
+ feature: "Adaptive contact patch & top handling - 195/55R15",
253
+ price: 19500,
254
+ badge: "German Tech",
255
+ segment: "new",
256
+ image: "/src/assets/images/comfort_tire_1781509452553.jpg",
257
+ description: "Adaptive tread design matches itself dynamically to wet or dry conditions for steady, confident braking. Brings elite European safety limits, low vibration, and precise handling to local JDM imports and sport sedans."
258
+ },
259
+ {
260
+ id: "new-6",
261
+ name: "General Tire Grabber AT3",
262
+ brand: "General Tire",
263
+ size: "265/65R17",
264
+ feature: "All-terrain dual action traction - 265/65R17",
265
+ price: 28500,
266
+ badge: "New Arrival",
267
+ segment: "new",
268
+ image: "/src/assets/images/suv_tire_1781509472580.jpg",
269
+ description: "High-traction all-terrain tyre with aggressive self-cleaning blocks. Developed specifically for SUV and pickup truck enthusiasts looking for tough off-road capability combined with stable highway dynamics inside Pakistan."
270
+ },
271
+ {
272
+ id: "new-7",
273
+ name: "Pirelli Cinturato P7 Verde",
274
+ brand: "Pirelli",
275
+ size: "205/60R16",
276
+ feature: "Eco-impact compound & silent highway ride - 205/60R16",
277
+ price: 24500,
278
+ badge: "Italian Craft",
279
+ segment: "new",
280
+ image: "/src/assets/images/comfort_tire_1781509452553.jpg",
281
+ description: "High-end touring tire featuring hybrid polymer materials. Reduced tire weight and rolling resistance lead to immense fuel consumption cuts and lower road vibrations. Universally fits Honda Civic, Toyota Corolla, and imported JDM hatchbacks."
282
+ },
283
+ {
284
+ id: "new-8",
285
+ name: "Autogrip EcoSaver Radial",
286
+ brand: "Autogrip",
287
+ size: "165/65R14",
288
+ feature: "Fuel conscious hatchback radial - 165/65R14",
289
+ price: 11200,
290
+ badge: "New Arrival",
291
+ segment: "new",
292
+ image: "/src/assets/images/comfort_tire_1781509452553.jpg",
293
+ description: "Optimized radial tire designed with a robust eco-compound that evenly distributes vehicle weight. Extremely budget-friendly, delivering a quieter ride and reliable long treadwear for Suzuki Wagon R and imported Japanese JDM hatchbacks."
294
+ },
295
+
296
+ // --- BESTSELLING GLOBAL / FAMOUS (8 products) ---
297
+ {
298
+ id: "fam-1",
299
+ name: "Dunlop Grandtrek AT20 Premium",
300
+ brand: "Dunlop",
301
+ size: "265/60R18",
302
+ feature: "4x4 executive tire & heavy highway duty - 265/60R18",
303
+ price: 48000,
304
+ badge: "Famous",
305
+ segment: "famous",
306
+ image: "/src/assets/images/suv_tire_1781509472580.jpg",
307
+ description: "Wholesale-level standard luxury SUV highway tire. Combines quiet road manners with high structural integrity capable of carrying extreme loads. Highly popular for premium Toyota Fortuner, Land Cruiser Prado, and high-end 4x4 trucks."
308
+ },
309
+ {
310
+ id: "fam-2",
311
+ name: "General Tire Euro Star Radial",
312
+ brand: "General Tire",
313
+ size: "175/70R13",
314
+ feature: "Classic local design, unbeatable cost - 175/70R13",
315
+ price: 8900,
316
+ badge: "Famous Classic",
317
+ segment: "famous",
318
+ image: "/src/assets/images/comfort_tire_1781509452553.jpg",
319
+ description: "The most famous local tire model of Pakistan. Engineered specifically to survive deep potholes and scorching heat. Highly cost-effective and dependable daily workhorse for Suzuki Mehran, Cultus, Wagon R, and small commercial taxis in Lahore."
320
+ },
321
+ {
322
+ id: "fam-3",
323
+ name: "Yokohama Geolandar CV G058",
324
+ brand: "Yokohama",
325
+ size: "215/70R16",
326
+ feature: "Grand crossover luxury silent touring - 215/70R16",
327
+ price: 26000,
328
+ badge: "Famous",
329
+ segment: "famous",
330
+ image: "/src/assets/images/suv_tire_1781509472580.jpg",
331
+ description: "Engineered specifically to transform midsize crossover rides. Provides supreme dry/wet braking safety with Yokohama's proprietary micro-silica compound to drastically reduce interior cabin vibrations and low frequency hums. Fits Toyota RAV4, Kia Sportage base, and Tucson imports."
332
+ },
333
+ {
334
+ id: "fam-4",
335
+ name: "Pirelli P Zero High Performance",
336
+ brand: "Pirelli",
337
+ size: "245/40R19",
338
+ feature: "Precision sports tracking & F1 DNA - 245/40R19",
339
+ price: 58000,
340
+ badge: "Elite Track",
341
+ segment: "famous",
342
+ image: "/src/assets/images/sport_tire_1781509490802.jpg",
343
+ description: "Legendary sports tire built using genuine Formula 1 racetrack heritage materials. Implements severe cornering stiffeners and specialized compound ratios for incredible braking under extreme speed. Ideal for Mercedes C-Class, E-Class, Audi A6, and sports cars."
344
+ },
345
+ {
346
+ id: "fam-5",
347
+ name: "Dunlop Sport Maxx RT2 Premium",
348
+ brand: "Dunlop",
349
+ size: "225/40R18",
350
+ feature: "Motorsport grip & short braking - 225/40R18",
351
+ price: 32000,
352
+ badge: "Famous Sport",
353
+ segment: "famous",
354
+ image: "/src/assets/images/sport_tire_1781509490802.jpg",
355
+ description: "Bridges the gap between street driving and track-level responsiveness. Dual compound notches adapt dynamically to high cornering heat to preserve high traction limits. Excellent choice for custom Civic upgrades, Audi A3, and Mercedes models."
356
+ },
357
+ {
358
+ id: "fam-6",
359
+ name: "Goodyear Eagle F1 Asymmetric 6",
360
+ brand: "Goodyear",
361
+ size: "235/35R19",
362
+ feature: "Asphalt adaptation & high force braking - 235/35R19",
363
+ price: 42000,
364
+ badge: "Famous",
365
+ segment: "famous",
366
+ image: "/src/assets/images/sport_tire_1781509490802.jpg",
367
+ description: "Highly adaptive contact profile reacts instantly to rapid weight shifts. Patented carbon black compounds offer outstanding asphalt feedback and top-level wet braking metrics. Perfectly matches premium sports packages and high-performance sedans."
368
+ },
369
+ {
370
+ id: "fam-7",
371
+ name: "Hankook Ventus S1 evo3 Elite",
372
+ brand: "Hankook",
373
+ size: "225/40R18",
374
+ feature: "OE-grade precise tracking - 225/40R18",
375
+ price: 30000,
376
+ badge: "Famous",
377
+ segment: "famous",
378
+ image: "/src/assets/images/sport_tire_1781509490802.jpg",
379
+ description: "Original Equipment (OE) selected by Audi, BMW, and Mercedes-Benz. Employs advanced aramid reinforcement belts to prevent tread expansion, maximizing speed stability and minimizing road vibration noise. Elite high-speed passenger car tire."
380
+ },
381
+ {
382
+ id: "fam-8",
383
+ name: "Autogrip Winter Grip W609 Radial",
384
+ brand: "Autogrip",
385
+ size: "195/65R15",
386
+ feature: "Snow/mud traction & deep siped wet protection",
387
+ price: 14000,
388
+ badge: "Famous",
389
+ segment: "famous",
390
+ image: "/src/assets/images/winter_tire_1781509513584.jpg",
391
+ description: "Featuring deep self-cleaning channels and microscopic sipes, this model delivers superior road traction during wet rainy days and heavy sludge. Highly recommended for winters and mountainous tourism in Murree, Swat, or Gilgit. Matches Corolla/Civic sizing."
392
+ }
393
+ ];
src/index.css ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;450;550;600;700&family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap');
2
+ @import "tailwindcss";
3
+
4
+ @theme {
5
+ --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
6
+ --font-display: "Space Grotesk", sans-serif;
7
+ --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;
8
+
9
+ --color-brand-orange: #ff5500;
10
+ --color-brand-dark: #121214;
11
+ --color-brand-card: #1c1c24;
12
+ }
13
+
14
+ /* Custom styles for smooth scroll & glow effects */
15
+ html {
16
+ scroll-behavior: smooth;
17
+ }
18
+
19
+ body {
20
+ font-family: var(--font-sans);
21
+ }
22
+
23
+ .text-glow {
24
+ text-shadow: 0 0 10px rgba(255, 85, 0, 0.4);
25
+ }
26
+
27
+ .box-glow {
28
+ box-shadow: 0 0 20px rgba(255, 85, 0, 0.15);
29
+ }
30
+
31
+ .glassmorphism {
32
+ background: rgba(28, 28, 36, 0.7);
33
+ backdrop-filter: blur(12px);
34
+ -webkit-backdrop-filter: blur(12px);
35
+ border: 1px solid rgba(255, 255, 255, 0.06);
36
+ }
37
+
38
+ .glassmorphism-light {
39
+ background: rgba(255, 255, 255, 0.75);
40
+ backdrop-filter: blur(12px);
41
+ -webkit-backdrop-filter: blur(12px);
42
+ border: 1px solid rgba(0, 0, 0, 0.05);
43
+ }
44
+
45
+ /* Custom styled scrollbar */
46
+ ::-webkit-scrollbar {
47
+ width: 8px;
48
+ }
49
+ ::-webkit-scrollbar-track {
50
+ background: #121214;
51
+ }
52
+ ::-webkit-scrollbar-thumb {
53
+ background: #2a2a35;
54
+ border-radius: 4px;
55
+ }
56
+ ::-webkit-scrollbar-thumb:hover {
57
+ background: #ff5500;
58
+ }
59
+
60
+ @keyframes marquee {
61
+ 0% { transform: translateX(0); }
62
+ 100% { transform: translateX(-50%); }
63
+ }
64
+
65
+ .animate-marquee-scroll {
66
+ display: flex;
67
+ width: max-content;
68
+ animation: marquee 40s linear infinite;
69
+ }
70
+
71
+ .animate-spin-slow {
72
+ animation: spin 20s linear infinite;
73
+ }
74
+
75
+ @keyframes spin {
76
+ 0% { transform: rotate(0deg); }
77
+ 100% { transform: rotate(360deg); }
78
+ }
79
+
80
+ @keyframes wiggle {
81
+ 0%, 100% { transform: rotate(0deg); }
82
+ 25% { transform: rotate(-8deg); }
83
+ 75% { transform: rotate(8deg); }
84
+ }
85
+
86
+ .animate-wiggle {
87
+ animation: wiggle 1s ease-in-out infinite;
88
+ }
89
+
90
+ .animate-pulse-slow {
91
+ animation: pulse 4s cubic-bezier(0.4, 0, 0.6, 1) infinite;
92
+ }
src/lib/firebase.ts ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { initializeApp } from "firebase/app";
2
+ import { getAuth } from "firebase/auth";
3
+ import { getFirestore, doc, getDocFromServer } from "firebase/firestore";
4
+ import { getStorage } from "firebase/storage";
5
+ import firebaseConfig from "../../firebase-applet-config.json";
6
+
7
+ /*
8
+ ===================================================================
9
+ FIREBASE CONFIGURATION GUIDE (bahaduralimunnabhai@gmail.com)
10
+ ===================================================================
11
+ To connect this application to your custom Firebase project:
12
+
13
+ 1. Go to Firebase Console (https://console.firebase.google.com/)
14
+ 2. Create or select your project.
15
+ 3. Under Project Settings -> General, add a Web App.
16
+ 4. Copy the config object and paste it below, replacing this configuration:
17
+
18
+ const manualConfig = {
19
+ apiKey: "YOUR_API_KEY",
20
+ authDomain: "YOUR_AUTH_DOMAIN",
21
+ projectId: "YOUR_PROJECT_ID",
22
+ storageBucket: "YOUR_STORAGE_BUCKET",
23
+ messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
24
+ appId: "YOUR_APP_ID"
25
+ };
26
+ ===================================================================
27
+ */
28
+
29
+ // Use provisioned dynamic config from setup by default
30
+ const app = initializeApp(firebaseConfig);
31
+
32
+ // Initialize Firestore, Auth, and Storage services
33
+ export const db = getFirestore(app, firebaseConfig.firestoreDatabaseId);
34
+ export const auth = getAuth(app);
35
+ export const storage = getStorage(app);
36
+
37
+ // Connectivity check as requested by system verification guidelines
38
+ export async function testConnection() {
39
+ try {
40
+ await getDocFromServer(doc(db, "test", "connection"));
41
+ console.log("Firebase Connection verified successfully.");
42
+ } catch (error) {
43
+ if (error instanceof Error && error.message.includes("offline")) {
44
+ console.error("Firebase is offline. Please check your network or configuration.");
45
+ } else {
46
+ console.log("Firebase server connectivity confirmed.", error);
47
+ }
48
+ }
49
+ }
50
+
51
+ // Fallback error handler following guidelines
52
+ export enum OperationType {
53
+ CREATE = "create",
54
+ UPDATE = "update",
55
+ DELETE = "delete",
56
+ LIST = "list",
57
+ GET = "get",
58
+ WRITE = "write",
59
+ }
60
+
61
+ export interface FirestoreErrorInfo {
62
+ error: string;
63
+ operationType: OperationType;
64
+ path: string | null;
65
+ authInfo: {
66
+ userId?: string | null;
67
+ email?: string | null;
68
+ emailVerified?: boolean | null;
69
+ };
70
+ }
71
+
72
+ export function handleFirestoreError(error: unknown, operationType: OperationType, path: string | null) {
73
+ const errInfo: FirestoreErrorInfo = {
74
+ error: error instanceof Error ? error.message : String(error),
75
+ authInfo: {
76
+ userId: auth.currentUser?.uid || null,
77
+ email: auth.currentUser?.email || null,
78
+ emailVerified: auth.currentUser?.emailVerified || null,
79
+ },
80
+ operationType,
81
+ path,
82
+ };
83
+ console.error("Firestore Error Detailed: ", JSON.stringify(errInfo));
84
+ throw new Error(JSON.stringify(errInfo));
85
+ }
86
+
src/lib/firebaseService.ts ADDED
@@ -0,0 +1,827 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ collection,
3
+ getDocs,
4
+ addDoc,
5
+ setDoc,
6
+ updateDoc,
7
+ deleteDoc,
8
+ doc,
9
+ query,
10
+ where,
11
+ onSnapshot,
12
+ serverTimestamp,
13
+ Timestamp,
14
+ writeBatch
15
+ } from "firebase/firestore";
16
+ import {
17
+ signInWithEmailAndPassword,
18
+ signOut,
19
+ onAuthStateChanged,
20
+ createUserWithEmailAndPassword,
21
+ User
22
+ } from "firebase/auth";
23
+ import {
24
+ ref,
25
+ uploadBytes,
26
+ getDownloadURL,
27
+ deleteObject
28
+ } from "firebase/storage";
29
+ import { db, auth, storage, handleFirestoreError, OperationType } from "./firebase";
30
+
31
+ // Collection Constant
32
+ const COLLECTION_NAME = "products";
33
+
34
+ // Fully compatible type interface satisfying both the user requested structure and frontend components
35
+ export interface ProductDocument {
36
+ id: string;
37
+ name: string;
38
+ brand: string;
39
+ size: string;
40
+ price: number;
41
+ category: "hot-selling" | "new-brands" | "famous";
42
+ segment?: "hot" | "new" | "famous"; // UI fallback field
43
+ badge?: string; // UI fallback field
44
+ stock: number;
45
+ feature: string;
46
+ imageUrl: string;
47
+ image?: string; // UI fallback field
48
+ description?: string; // UI fallback field
49
+ createdAt?: Timestamp | Date | any;
50
+ }
51
+
52
+ // ---------------------------------------------------------
53
+ // 9. ERROR HANDLING & NETWORK DETECTOR
54
+ // ---------------------------------------------------------
55
+
56
+ /**
57
+ * Checks if the system browser environment has active internet connectivity
58
+ */
59
+ export function isBrowserOnline(): boolean {
60
+ return typeof navigator !== "undefined" ? navigator.onLine : true;
61
+ }
62
+
63
+ /**
64
+ * Registers a callback for changes in native internet connectivity state
65
+ */
66
+ export function registerNetworkStateListener(callback: (online: boolean) => void): () => void {
67
+ if (typeof window === "undefined") return () => {};
68
+
69
+ const handleOnline = () => callback(true);
70
+ const handleOffline = () => callback(false);
71
+
72
+ window.addEventListener("online", handleOnline);
73
+ window.addEventListener("offline", handleOffline);
74
+
75
+ return () => {
76
+ window.removeEventListener("online", handleOnline);
77
+ window.removeEventListener("offline", handleOffline);
78
+ };
79
+ }
80
+
81
+ // ---------------------------------------------------------
82
+ // 3. CRUD FUNCTIONS (TypeScript / JavaScript Module)
83
+ // ---------------------------------------------------------
84
+
85
+ /**
86
+ * Fills helper values into the model object so that it remains completely compatible
87
+ * across the vintage UI segments & new strict collection layouts.
88
+ */
89
+ function fillCompatibilityFields(data: any): any {
90
+ const result = { ...data };
91
+
92
+ // Set alternative images
93
+ if (result.imageUrl && !result.image) {
94
+ result.image = result.imageUrl;
95
+ }
96
+ if (result.image && !result.imageUrl) {
97
+ result.imageUrl = result.image;
98
+ }
99
+
100
+ // Set category/segment mapping
101
+ if (result.category && !result.segment) {
102
+ if (result.category === "hot-selling") result.segment = "hot";
103
+ else if (result.category === "new-brands") result.segment = "new";
104
+ else if (result.category === "famous") result.segment = "famous";
105
+ }
106
+ if (result.segment && !result.category) {
107
+ if (result.segment === "hot") result.category = "hot-selling";
108
+ else if (result.segment === "new") result.category = "new-brands";
109
+ else if (result.segment === "famous") result.category = "famous";
110
+ }
111
+
112
+ // Supply default badges
113
+ if (!result.badge) {
114
+ if (result.category === "hot-selling") result.badge = "Hot Selling";
115
+ else if (result.category === "new-brands") result.badge = "New Arrival";
116
+ else result.badge = "Famous";
117
+ }
118
+
119
+ // Fallback description
120
+ if (!result.description) {
121
+ result.description = `Premium tire profile configured for high grip and safety under high heat-cycle Pakistani road asphalt conditions.`;
122
+ }
123
+
124
+ // Type coercions
125
+ if (result.price !== undefined) result.price = Number(result.price);
126
+ if (result.stock !== undefined) result.stock = Number(result.stock);
127
+
128
+ return result;
129
+ }
130
+
131
+ /**
132
+ * Adds a new product document to Firestore 'products' collection
133
+ */
134
+ export async function addProduct(productData: Omit<ProductDocument, "id">): Promise<string> {
135
+ if (!isBrowserOnline()) {
136
+ throw new Error("Unable to add product. You are currently offline.");
137
+ }
138
+ const payload = fillCompatibilityFields(productData);
139
+ try {
140
+ const docRef = await addDoc(collection(db, COLLECTION_NAME), {
141
+ ...payload,
142
+ createdAt: serverTimestamp()
143
+ });
144
+ // Set ID on document itself
145
+ await updateDoc(docRef, { id: docRef.id });
146
+ return docRef.id;
147
+ } catch (error) {
148
+ handleFirestoreError(error, OperationType.WRITE, COLLECTION_NAME);
149
+ throw error;
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Fetches all products with real-time snapshot support optionally, or via one-shot call
155
+ */
156
+ export async function getAllProducts(isRetry: boolean = false): Promise<ProductDocument[]> {
157
+ try {
158
+ const q = query(collection(db, COLLECTION_NAME));
159
+ const querySnapshot = await getDocs(q);
160
+ const results: ProductDocument[] = [];
161
+ querySnapshot.forEach((doc) => {
162
+ const data = doc.data();
163
+ results.push(fillCompatibilityFields({ ...data, id: doc.id }) as ProductDocument);
164
+ });
165
+
166
+ // Check and trigger auto-seeding if collection returns empty
167
+ if (results.length === 0 && !isRetry) {
168
+ console.log("Empty 'products' collection found. Seeding with 12 defaults.");
169
+ await seedDefaultProducts();
170
+ return await getAllProducts(true);
171
+ }
172
+ return results;
173
+ } catch (error) {
174
+ handleFirestoreError(error, OperationType.GET, COLLECTION_NAME);
175
+ throw error;
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Updates an existing product document
181
+ */
182
+ export async function updateProduct(productId: string, updatedData: Partial<ProductDocument>): Promise<void> {
183
+ if (!isBrowserOnline()) {
184
+ throw new Error("Unable to update product. You are currently offline.");
185
+ }
186
+ const payload = fillCompatibilityFields(updatedData);
187
+ try {
188
+ const docRef = doc(db, COLLECTION_NAME, productId);
189
+ await updateDoc(docRef, payload);
190
+ } catch (error) {
191
+ handleFirestoreError(error, OperationType.WRITE, `${COLLECTION_NAME}/${productId}`);
192
+ throw error;
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Deletes a product from the database
198
+ */
199
+ export async function deleteProduct(productId: string): Promise<void> {
200
+ if (!isBrowserOnline()) {
201
+ throw new Error("Unable to delete product. You are currently offline.");
202
+ }
203
+ try {
204
+ await deleteDoc(doc(db, COLLECTION_NAME, productId));
205
+ } catch (error) {
206
+ handleFirestoreError(error, OperationType.DELETE, `${COLLECTION_NAME}/${productId}`);
207
+ throw error;
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Increases or decreases all prices in the inventory database by a given percentage.
213
+ * Example: bulkUpdatePrices(10) increases all rates by 10%, while bulkUpdatePrices(-5) cuts them by 5%.
214
+ */
215
+ export async function bulkUpdatePrices(percentage: number): Promise<void> {
216
+ if (!isBrowserOnline()) {
217
+ throw new Error("No network connectivity. Bulk operations are disabled.");
218
+ }
219
+ try {
220
+ const q = query(collection(db, COLLECTION_NAME));
221
+ const querySnapshot = await getDocs(q);
222
+
223
+ const batch = writeBatch(db);
224
+ let count = 0;
225
+
226
+ querySnapshot.forEach((document) => {
227
+ const data = document.data() as ProductDocument;
228
+ const originalPrice = Number(data.price) || 0;
229
+ const multiplier = 1 + (percentage / 100);
230
+ const newPrice = Math.round(originalPrice * multiplier);
231
+
232
+ batch.update(doc(db, COLLECTION_NAME, document.id), { price: newPrice });
233
+ count++;
234
+ });
235
+
236
+ if (count > 0) {
237
+ await batch.commit();
238
+ console.log(`Successfully completed bulk price update on ${count} products.`);
239
+ }
240
+ } catch (error) {
241
+ handleFirestoreError(error, OperationType.WRITE, `${COLLECTION_NAME}/bulkUpdate`);
242
+ throw error;
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Returns products with stock levels strictly below the threshold value
248
+ */
249
+ export async function getLowStockProducts(threshold: number = 5): Promise<ProductDocument[]> {
250
+ try {
251
+ const q = query(collection(db, COLLECTION_NAME), where("stock", "<", threshold));
252
+ const querySnapshot = await getDocs(q);
253
+ const results: ProductDocument[] = [];
254
+ querySnapshot.forEach((doc) => {
255
+ results.push(fillCompatibilityFields({ ...doc.data(), id: doc.id }));
256
+ });
257
+ return results;
258
+ } catch (error) {
259
+ handleFirestoreError(error, OperationType.GET, COLLECTION_NAME);
260
+ throw error;
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Searches the collection for products with matching names or manufacturer brands
266
+ */
267
+ export async function searchProducts(searchQuery: string): Promise<ProductDocument[]> {
268
+ // Firestore does not natively support multi-field partial matching easily in client SDK,
269
+ // so we fetch all and apply high-performance filter locally to ensure maximum reliability and speed.
270
+ try {
271
+ const all = await getAllProducts();
272
+ const queryLower = searchQuery.toLowerCase().trim();
273
+ if (!queryLower) return all;
274
+
275
+ return all.filter(p =>
276
+ p.name.toLowerCase().includes(queryLower) ||
277
+ p.brand.toLowerCase().includes(queryLower) ||
278
+ p.size.toLowerCase().includes(queryLower)
279
+ );
280
+ } catch (error) {
281
+ throw error;
282
+ }
283
+ }
284
+
285
+ /**
286
+ * Filter products specifically by database category classification
287
+ */
288
+ export async function filterByCategory(category: "hot-selling" | "new-brands" | "famous"): Promise<ProductDocument[]> {
289
+ try {
290
+ const q = query(collection(db, COLLECTION_NAME), where("category", "==", category));
291
+ const querySnapshot = await getDocs(q);
292
+ const results: ProductDocument[] = [];
293
+ querySnapshot.forEach((doc) => {
294
+ results.push(fillCompatibilityFields({ ...doc.data(), id: doc.id }));
295
+ });
296
+ return results;
297
+ } catch (error) {
298
+ handleFirestoreError(error, OperationType.GET, COLLECTION_NAME);
299
+ throw error;
300
+ }
301
+ }
302
+
303
+ // ---------------------------------------------------------
304
+ // 4. AUTHENTICATION FUNCTIONS
305
+ // ---------------------------------------------------------
306
+
307
+ /**
308
+ * Authenticates an administrator using standard email and password
309
+ */
310
+ export async function loginAdmin(email: string, password: string): Promise<User> {
311
+ try {
312
+ const userCredential = await signInWithEmailAndPassword(auth, email, password);
313
+ return userCredential.user;
314
+ } catch (error: any) {
315
+ console.error("Login attempt failed:", error);
316
+ throw new Error(error.message || "Invalid authentication credentials.");
317
+ }
318
+ }
319
+
320
+ /**
321
+ * Standard security signout for administrators
322
+ */
323
+ export async function logoutAdmin(): Promise<void> {
324
+ try {
325
+ await signOut(auth);
326
+ } catch (error: any) {
327
+ throw new Error(error.message || "Signout failed.");
328
+ }
329
+ }
330
+
331
+ /**
332
+ * Attaches a state change listener tracking authentications
333
+ */
334
+ export function checkAuthState(callback: (user: User | null) => void): () => void {
335
+ return onAuthStateChanged(auth, (user) => {
336
+ callback(user);
337
+ });
338
+ }
339
+
340
+ /**
341
+ * Utility to register fresh admin personnel accounts
342
+ */
343
+ export async function createAdminUser(email: string, password: string): Promise<User> {
344
+ try {
345
+ const userCredential = await createUserWithEmailAndPassword(auth, email, password);
346
+ return userCredential.user;
347
+ } catch (error: any) {
348
+ throw new Error(error.message || "Failed to create administrator user.");
349
+ }
350
+ }
351
+
352
+ // ---------------------------------------------------------
353
+ // 5. STORAGE FUNCTIONS
354
+ // ---------------------------------------------------------
355
+
356
+ /**
357
+ * Uploads a tire image binary file to the Firebase Storage bucket
358
+ */
359
+ export async function uploadProductImage(file: File, productName: string): Promise<string> {
360
+ if (!isBrowserOnline()) {
361
+ throw new Error("Unable to upload image. Network is currently offline.");
362
+ }
363
+ try {
364
+ const cleanName = productName.replace(/[^a-zA-Z0-9]/g, "_").toLowerCase();
365
+ const timestamp = Date.now();
366
+ const storageRef = ref(storage, `product-images/${cleanName}_${timestamp}`);
367
+
368
+ const snapshot = await uploadBytes(storageRef, file);
369
+ const downloadUrl = await getDownloadURL(snapshot.ref);
370
+ return downloadUrl;
371
+ } catch (error: any) {
372
+ console.error("Storage image upload failure:", error);
373
+ throw new Error(error.message || "Failed to store product asset image.");
374
+ }
375
+ }
376
+
377
+ /**
378
+ * Deletes standard file objects from the Firebase storage bucket using their download addresses
379
+ */
380
+ export async function deleteProductImage(imageUrl: string): Promise<void> {
381
+ if (!isBrowserOnline()) {
382
+ throw new Error("Unable to deleted image. Offline mode.");
383
+ }
384
+ try {
385
+ // Extract file reference path from firebase download URI
386
+ const decodedUrl = decodeURIComponent(imageUrl);
387
+ const relativePathStart = decodedUrl.indexOf("/o/") + 3;
388
+ const relativePathEnd = decodedUrl.indexOf("?alt=");
389
+
390
+ if (relativePathStart > 2 && relativePathEnd > relativePathStart) {
391
+ const filePath = decodedUrl.substring(relativePathStart, relativePathEnd);
392
+ const fileRef = ref(storage, filePath);
393
+ await deleteObject(fileRef);
394
+ }
395
+ } catch (error: any) {
396
+ console.warn("Storage item cleanup ignored (possibly using an external image link):", error.message);
397
+ }
398
+ }
399
+
400
+ // ---------------------------------------------------------
401
+ // 7. SEED DATA FUNCTION
402
+ // ---------------------------------------------------------
403
+
404
+ // Exhaustive real Pakistan market default dataset encompassing exactly 12 products
405
+ export const DEFAULT_TIR_PRODUCTS_SEED: Omit<ProductDocument, "id">[] = [
406
+ // --- HOT SELLING ---
407
+ {
408
+ name: "Autogrip F107 Comfort Grip",
409
+ brand: "Autogrip",
410
+ size: "185/65R14",
411
+ price: 12500,
412
+ category: "hot-selling",
413
+ stock: 15,
414
+ feature: "All-season wet grip & stability - 185/65R14",
415
+ imageUrl: "/src/assets/images/comfort_tire_1781509452553.jpg",
416
+ description: "Designed for premium everyday passenger cars. Delivers superior compound performance in all seasons with low road noise levels. Size: 185/65R14."
417
+ },
418
+ {
419
+ name: "Autogrip SUV A606 Rugged",
420
+ brand: "Autogrip",
421
+ size: "235/60R18",
422
+ price: 22000,
423
+ category: "hot-selling",
424
+ stock: 4, // low stock flag
425
+ feature: "Off-road rugged tread design - 235/60R18",
426
+ imageUrl: "/src/assets/images/suv_tire_1781509472580.jpg",
427
+ description: "Aggressive tread blocks and heavy-duty compound for SUVs. Provides high durability on rugged tracks, gravel, and highway driving alike. Size: 235/60R18."
428
+ },
429
+ {
430
+ name: "General Tire Grabber AT3",
431
+ brand: "General Tire",
432
+ size: "265/65R17",
433
+ price: 28500,
434
+ category: "hot-selling",
435
+ stock: 8,
436
+ feature: "All-terrain robust capability - 265/65R17",
437
+ imageUrl: "/src/assets/images/suv_tire_1781509472580.jpg",
438
+ description: "Developed to meet the needs of SUV, pick-up truck and off-road vehicle drivers who want high command and high-traction performance. Size: 265/65R17."
439
+ },
440
+ {
441
+ name: "Bridgestone Turanza T005",
442
+ brand: "Bridgestone",
443
+ size: "205/55R16",
444
+ price: 21000,
445
+ category: "hot-selling",
446
+ stock: 12,
447
+ feature: "Premium comfort & low noise rating - 205/55R16",
448
+ imageUrl: "/src/assets/images/sport_tire_1781509490802.jpg",
449
+ description: "Flagship luxury touring tire providing superior control in wet and dry conditions, with fuel efficiency optimization. Size: 205/55R16."
450
+ },
451
+
452
+ // --- NEW BRANDS / NEW ARRIVALS ---
453
+ {
454
+ name: "Michelin Pilot Sport 5",
455
+ brand: "Michelin",
456
+ size: "225/45R17",
457
+ price: 35000,
458
+ category: "new-brands",
459
+ stock: 3, // low stock flag
460
+ feature: "Ultra-high performance & control - 225/45R17",
461
+ imageUrl: "/src/assets/images/sport_tire_1781509490802.jpg",
462
+ description: "Live each driving moment to the fullest. Dynamic response technology ensures high precision handling and short braking distances. Size: 225/45R17."
463
+ },
464
+ {
465
+ name: "Pirelli Cinturato P7 Eco",
466
+ brand: "Pirelli",
467
+ size: "205/60R16",
468
+ price: 24500,
469
+ category: "new-brands",
470
+ stock: 7,
471
+ feature: "Eco-friendly, low rolling resistance - 205/60R16",
472
+ imageUrl: "/src/assets/images/comfort_tire_1781509452553.jpg",
473
+ description: "Green performance luxury tire developed for modern cars. High safety levels, low fuel consumption, and eco-friendly raw materials. Size: 205/60R16."
474
+ },
475
+ {
476
+ name: "Yokohama Geolandar CV G058",
477
+ brand: "Yokohama",
478
+ size: "215/70R16",
479
+ price: 26000,
480
+ category: "new-brands",
481
+ stock: 9,
482
+ feature: "Crossover/SUV quiet touring - 215/70R16",
483
+ imageUrl: "/src/assets/images/suv_tire_1781509472580.jpg",
484
+ description: "Formidable wet grip with advanced CV silica compound. Designed to deliver confident handling and long wear life for modern crossovers. Size: 215/70R16."
485
+ },
486
+ {
487
+ name: "Continental PremiumContact 7",
488
+ brand: "Continental",
489
+ size: "195/55R15",
490
+ price: 19500,
491
+ category: "new-brands",
492
+ stock: 14,
493
+ feature: "Outstanding safety & wet braking - 195/55R15",
494
+ imageUrl: "/src/assets/images/comfort_tire_1781509452553.jpg",
495
+ description: "Experience absolute driving comfort and peace of mind. Outstanding safety on wet roads and superior wet braking efficiency. Size: 195/55R15."
496
+ },
497
+
498
+ // --- BESTSELLING GLOBAL / FAMOUS Products ---
499
+ {
500
+ name: "Dunlop Sport Maxx RT2",
501
+ brand: "Dunlop",
502
+ size: "225/40R18",
503
+ price: 32000,
504
+ category: "famous",
505
+ stock: 2, // low stock flag
506
+ feature: "Ultra-high performance sport traction - 225/40R18",
507
+ imageUrl: "/src/assets/images/sport_tire_1781509490802.jpg",
508
+ description: "Champion grip and braking performance. Motorsport-derived compound offers supreme steering precision and curve cornering. Size: 225/40R18."
509
+ },
510
+ {
511
+ name: "Goodyear Eagle F1 Asymmetric 6",
512
+ brand: "Goodyear",
513
+ size: "235/35R19",
514
+ price: 42000,
515
+ category: "famous",
516
+ stock: 11,
517
+ feature: "Max grip, sports performance - 235/35R19",
518
+ imageUrl: "/src/assets/images/sport_tire_1781509490802.jpg",
519
+ description: "Ready for anything. Adaptable contact patch ensures extreme asphalt feedback. Excellent performance in both wet and dry tracks. Size: 235/35R19."
520
+ },
521
+ {
522
+ name: "Hankook Ventus S1 evo3 Elite",
523
+ brand: "Hankook",
524
+ size: "225/40R18",
525
+ price: 30000,
526
+ category: "famous",
527
+ stock: 6,
528
+ feature: "Luxury sports OE high response rating - 225/40R18",
529
+ imageUrl: "/src/assets/images/sport_tire_1781509490802.jpg",
530
+ description: "Original Equipment (OE) for BMW, Audi, and Mercedes. Highlights high response speed, high durability, and lower rolling noise. Size: 225/40R18."
531
+ },
532
+ {
533
+ name: "Autogrip Winter Grip W609 Extreme",
534
+ brand: "Autogrip",
535
+ size: "195/65R15",
536
+ price: 14000,
537
+ category: "famous",
538
+ stock: 5,
539
+ feature: "Extreme snow & water grip traction - 195/65R15",
540
+ imageUrl: "/src/assets/images/winter_tire_1781509513584.jpg",
541
+ description: "Heavy-siped tread design that grips perfectly during heavy rain, wet mud, and cold winter situations. Ensures reliable road security. Size: 195/65R15."
542
+ }
543
+ ];
544
+
545
+ /**
546
+ * Checks if the products collection is empty and fills it with 12 premium tyres if needed
547
+ */
548
+ export async function seedDefaultProducts(): Promise<void> {
549
+ try {
550
+ const q = query(collection(db, COLLECTION_NAME));
551
+ const querySnapshot = await getDocs(q);
552
+
553
+ if (!querySnapshot.empty) {
554
+ console.log("Database 'products' collection is already populated. Seeding aborted.");
555
+ return;
556
+ }
557
+
558
+ console.log("Initiating premium 12 product configurations cloud seed operation.");
559
+
560
+ // Batch write to optimize bandwidth
561
+ const batch = writeBatch(db);
562
+
563
+ for (let i = 0; i < DEFAULT_TIR_PRODUCTS_SEED.length; i++) {
564
+ const item = DEFAULT_TIR_PRODUCTS_SEED[i];
565
+ const cleanBrand = item.brand.toLowerCase().replace(/[^a-z0-9]/g, "-").replace(/-+/g, "-");
566
+ const customId = `seed-${cleanBrand}-${i + 1}`;
567
+ const payload = fillCompatibilityFields({
568
+ ...item,
569
+ id: customId,
570
+ createdAt: new Date()
571
+ });
572
+
573
+ const docRef = doc(db, COLLECTION_NAME, customId);
574
+ batch.set(docRef, payload);
575
+ }
576
+
577
+ await batch.commit();
578
+ console.log("Database seeded successfully with exactly 12 default tires!");
579
+ } catch (error) {
580
+ console.error("Failure while running cloud seeding:", error);
581
+ }
582
+ }
583
+
584
+ // ---------------------------------------------------------
585
+ // 8. REAL-TIME LISTENERS
586
+ // ---------------------------------------------------------
587
+
588
+ /**
589
+ * Sets up a live listener on the main collections. Realizes reactive UI state
590
+ * updates on item modifications.
591
+ */
592
+ export function onProductsSnapshot(callback: (products: ProductDocument[]) => void): () => void {
593
+ const q = query(collection(db, COLLECTION_NAME));
594
+
595
+ return onSnapshot(q, (snapshot) => {
596
+ const results: ProductDocument[] = [];
597
+ snapshot.forEach((doc) => {
598
+ const data = doc.data();
599
+ results.push(fillCompatibilityFields({ ...data, id: doc.id }) as ProductDocument);
600
+ });
601
+ callback(results);
602
+ }, (error) => {
603
+ handleFirestoreError(error, OperationType.LIST, COLLECTION_NAME);
604
+ });
605
+ }
606
+
607
+ // ---------------------------------------------------------
608
+ // 10. EXPORT / IMPORT FUNCTIONS
609
+ // ---------------------------------------------------------
610
+
611
+ /**
612
+ * Downloads the complete active database catalog as a structured JSON file instantly from the browser
613
+ */
614
+ export async function exportToJSON(): Promise<void> {
615
+ try {
616
+ const allProducts = await getAllProducts();
617
+ const dataString = JSON.stringify(allProducts, null, 2);
618
+
619
+ // Trigger localized file download in client runtime
620
+ const blob = new Blob([dataString], { type: "application/json" });
621
+ const fileAnchor = document.createElement("a");
622
+ fileAnchor.href = URL.createObjectURL(blob);
623
+ fileAnchor.download = `haider_brothers_products_backup_${Date.now()}.json`;
624
+ document.body.appendChild(fileAnchor);
625
+ fileAnchor.click();
626
+ document.body.removeChild(fileAnchor);
627
+ } catch (error) {
628
+ console.error("Export file creation error: ", error);
629
+ alert("Export operation encountered failures. Check network status.");
630
+ }
631
+ }
632
+
633
+ /**
634
+ * Uploads a user supplied JSON backup file, checks for duplicates, and uploads novel configurations
635
+ */
636
+ export async function importFromJSON(jsonFile: File): Promise<{ imported: number; duplicates: number }> {
637
+ if (!isBrowserOnline()) {
638
+ throw new Error("Unable to import product index while offline.");
639
+ }
640
+ return new Promise((resolve, reject) => {
641
+ const reader = new FileReader();
642
+ reader.onload = async (event) => {
643
+ try {
644
+ const fileContent = event.target?.result;
645
+ if (typeof fileContent !== "string") {
646
+ throw new Error("Invalid file readings.");
647
+ }
648
+
649
+ const parsedData = JSON.parse(fileContent);
650
+ const productsToImport: any[] = Array.isArray(parsedData) ? parsedData : [parsedData];
651
+
652
+ // Fetch existing configurations to handle duplicate prevention by names and size matching
653
+ const existingProducts = await getAllProducts();
654
+
655
+ let importCount = 0;
656
+ let duplicateCount = 0;
657
+
658
+ const batch = writeBatch(db);
659
+
660
+ for (const item of productsToImport) {
661
+ // Normalize names & sizes check for solid uniqueness check
662
+ const isDuplicate = existingProducts.some(ep =>
663
+ ep.name.toLowerCase().trim() === item.name?.toLowerCase().trim() &&
664
+ ep.size.toLowerCase().trim() === item.size?.toLowerCase().trim()
665
+ );
666
+
667
+ if (isDuplicate) {
668
+ duplicateCount++;
669
+ continue;
670
+ }
671
+
672
+ const uniqueId = item.id || `imported-${Date.now()}-${Math.floor(Math.random() * 1000)}`;
673
+ const payload = fillCompatibilityFields({
674
+ ...item,
675
+ id: uniqueId,
676
+ createdAt: new Date()
677
+ });
678
+
679
+ const docRef = doc(db, COLLECTION_NAME, uniqueId);
680
+ batch.set(docRef, payload);
681
+ importCount++;
682
+ }
683
+
684
+ if (importCount > 0) {
685
+ await batch.commit();
686
+ }
687
+
688
+ resolve({ imported: importCount, duplicates: duplicateCount });
689
+ } catch (error: any) {
690
+ console.error("Failed to parse or save backup file:", error);
691
+ reject(new Error(error.message || "Invalid JSON or database security rules violation during upload."));
692
+ }
693
+ };
694
+ reader.onerror = () => reject(new Error("File read error."));
695
+ reader.readAsText(jsonFile);
696
+ });
697
+ }
698
+
699
+ // ---------------------------------------------------------
700
+ // 11. VISITOR TRACKING & TRAFFIC METRICS
701
+ // ---------------------------------------------------------
702
+
703
+ function getOrCreateSessionId(): string {
704
+ if (typeof window === "undefined" || !window.sessionStorage) {
705
+ return "session-fallback";
706
+ }
707
+ let sid = window.sessionStorage.getItem("hb_visitor_sid");
708
+ if (!sid) {
709
+ sid = "sid-" + Math.random().toString(36).substring(2, 15) + "-" + Date.now();
710
+ window.sessionStorage.setItem("hb_visitor_sid", sid);
711
+ }
712
+ return sid;
713
+ }
714
+
715
+ /**
716
+ * Logs a standard visitor page view event into the Firestore visitors collection
717
+ */
718
+ export async function logVisitorInfo(pagePath: string): Promise<void> {
719
+ if (typeof window === "undefined") return;
720
+ try {
721
+ const sessionId = getOrCreateSessionId();
722
+ const userAgent = window.navigator.userAgent || "Unknown UA";
723
+ const language = window.navigator.language || "en";
724
+
725
+ // Quick OS signature parsing
726
+ let platform = "Other / Web";
727
+ const ua = userAgent.toLowerCase();
728
+ if (ua.includes("windows")) platform = "Windows";
729
+ else if (ua.includes("macintosh") || ua.includes("mac os")) platform = "macOS";
730
+ else if (ua.includes("android")) platform = "Android";
731
+ else if (ua.includes("iphone") || ua.includes("ipad")) platform = "iOS";
732
+ else if (ua.includes("linux")) platform = "Linux";
733
+
734
+ // Build unique document key
735
+ const customId = `vis-${sessionId.substring(4, 16)}-${Date.now()}`;
736
+ const docRef = doc(db, "visitors", customId);
737
+
738
+ await setDoc(docRef, {
739
+ sessionId,
740
+ userAgent: userAgent.substring(0, 500),
741
+ platform,
742
+ language,
743
+ pagePath,
744
+ timestamp: serverTimestamp()
745
+ });
746
+ } catch (error) {
747
+ // Fail silently so analytics tracking never disrupts primary showroom client flow
748
+ console.warn("Analytics logging bypassed:", error);
749
+ }
750
+ }
751
+
752
+ export interface VisitorRecord {
753
+ id: string;
754
+ sessionId: string;
755
+ userAgent: string;
756
+ platform: string;
757
+ language: string;
758
+ pagePath: string;
759
+ timestamp: any;
760
+ }
761
+
762
+ /**
763
+ * Retrieves the sequential log of website visitation reports
764
+ */
765
+ export async function getVisitorLogs(): Promise<VisitorRecord[]> {
766
+ try {
767
+ const q = query(collection(db, "visitors"));
768
+ const querySnapshot = await getDocs(q);
769
+ const results: VisitorRecord[] = [];
770
+ querySnapshot.forEach((doc) => {
771
+ const data = doc.data();
772
+ results.push({
773
+ id: doc.id,
774
+ sessionId: data.sessionId || "",
775
+ userAgent: data.userAgent || "",
776
+ platform: data.platform || "",
777
+ language: data.language || "",
778
+ pagePath: data.pagePath || "",
779
+ timestamp: data.timestamp
780
+ });
781
+ });
782
+
783
+ // Order chronologically by descending timestamp order
784
+ results.sort((a, b) => {
785
+ const tA = a.timestamp?.seconds || 0;
786
+ const tB = b.timestamp?.seconds || 0;
787
+ return tB - tA;
788
+ });
789
+
790
+ return results;
791
+ } catch (error) {
792
+ handleFirestoreError(error, OperationType.GET, "visitors");
793
+ throw error;
794
+ }
795
+ }
796
+
797
+ /**
798
+ * Adds a new email address to the newsletter subscribers list in Firestore.
799
+ */
800
+ export async function subscribeNewsletter(email: string): Promise<void> {
801
+ const collectionName = "subscribers";
802
+ if (!isBrowserOnline()) {
803
+ throw new Error("Unable to subscribe. You are currently offline.");
804
+ }
805
+
806
+ const cleanEmail = email.trim().toLowerCase();
807
+ const emailRegex = /^[^@]+@[^@]+\.[^@]+$/;
808
+ if (!emailRegex.test(cleanEmail)) {
809
+ throw new Error("Please present a valid email format.");
810
+ }
811
+
812
+ try {
813
+ // Build a secure document ID based on the email (replaces special characters) matching the rules regex limit.
814
+ const customId = `sub-${cleanEmail.replace(/[^a-zA-Z0-9_-]/g, "_")}`.substring(0, 128);
815
+ const docRef = doc(db, collectionName, customId);
816
+
817
+ await setDoc(docRef, {
818
+ email: cleanEmail,
819
+ createdAt: serverTimestamp()
820
+ });
821
+ } catch (error) {
822
+ handleFirestoreError(error, OperationType.WRITE, collectionName);
823
+ throw error;
824
+ }
825
+ }
826
+
827
+
src/lib/tireService.ts ADDED
@@ -0,0 +1,372 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Bridge file forwarding products data queries to our clean Firebase Service
2
+ import { TireProduct } from "../data";
3
+ import {
4
+ getAllProducts,
5
+ addProduct,
6
+ updateProduct,
7
+ deleteProduct,
8
+ ProductDocument,
9
+ DEFAULT_TIR_PRODUCTS_SEED
10
+ } from "./firebaseService";
11
+ import {
12
+ collection,
13
+ getDocs,
14
+ addDoc
15
+ } from "firebase/firestore";
16
+ import { db, handleFirestoreError, OperationType } from "./firebase";
17
+
18
+ export interface CustomReview {
19
+ id?: string;
20
+ name: string;
21
+ rating: number;
22
+ text: string;
23
+ date: string;
24
+ initials: string;
25
+ }
26
+
27
+ // Map compatibility fields from FirebaseService Product to TireProduct
28
+ function mapToTireProduct(p: ProductDocument): TireProduct {
29
+ return {
30
+ id: p.id,
31
+ name: p.name,
32
+ brand: p.brand,
33
+ size: p.size,
34
+ feature: p.feature,
35
+ price: p.price,
36
+ badge: p.badge || (p.category === "hot-selling" ? "Hot Selling" : p.category === "new-brands" ? "New Arrival" : "Famous"),
37
+ segment: p.segment || (p.category === "hot-selling" ? "hot" : p.category === "new-brands" ? "new" : "famous"),
38
+ image: p.image || p.imageUrl,
39
+ description: p.description || `Premium quality tire from ${p.brand}.`
40
+ };
41
+ }
42
+
43
+ // 1. Fetch Tyres from Firestore (migrated to the new 'products' collection)
44
+ export async function getTireProducts(): Promise<TireProduct[]> {
45
+ try {
46
+ const list = await getAllProducts();
47
+ return list.map(mapToTireProduct);
48
+ } catch (error) {
49
+ console.warn("Could not load from Firestore 'products', falling back.", error);
50
+ return SEED_TYRE_PRODUCTS;
51
+ }
52
+ }
53
+
54
+ // 2. Add or Edit Tyre in Firestore (migrated to the new 'products' collection)
55
+ export async function saveTireProduct(tire: TireProduct): Promise<void> {
56
+ const categoryMapped = tire.segment === "hot" ? "hot-selling" : tire.segment === "new" ? "new-brands" : "famous";
57
+
58
+ const payload: Partial<ProductDocument> = {
59
+ name: tire.name,
60
+ brand: tire.brand,
61
+ size: tire.size,
62
+ feature: tire.feature,
63
+ price: Number(tire.price),
64
+ category: categoryMapped as any,
65
+ segment: tire.segment,
66
+ badge: tire.badge,
67
+ imageUrl: tire.image,
68
+ image: tire.image,
69
+ stock: 10, // default stock allocation
70
+ description: tire.description
71
+ };
72
+
73
+ try {
74
+ // If tire ID starts with custom- or looks like a temp, we check if it is existing in firestore
75
+ const existing = await getTireProducts();
76
+ const match = existing.find(e => e.id === tire.id);
77
+
78
+ if (match) {
79
+ await updateProduct(tire.id, payload);
80
+ } else {
81
+ await addProduct({
82
+ ...payload,
83
+ name: tire.name,
84
+ brand: tire.brand,
85
+ size: tire.size,
86
+ feature: tire.feature,
87
+ price: Number(tire.price),
88
+ category: categoryMapped as any,
89
+ stock: 10,
90
+ imageUrl: tire.image
91
+ } as ProductDocument);
92
+ }
93
+ } catch (error) {
94
+ handleFirestoreError(error, OperationType.WRITE, `products/${tire.id}`);
95
+ }
96
+ }
97
+
98
+ // 3. Delete Tyre from Firestore 'products'
99
+ export async function deleteTireProduct(tireId: string): Promise<void> {
100
+ try {
101
+ await deleteProduct(tireId);
102
+ } catch (error) {
103
+ handleFirestoreError(error, OperationType.DELETE, `products/${tireId}`);
104
+ }
105
+ }
106
+
107
+ // 4. Fetch Reviews from Firestore
108
+ export async function getReviews(): Promise<CustomReview[]> {
109
+ const collectionName = "reviews";
110
+ try {
111
+ const list: CustomReview[] = [];
112
+ const querySnapshot = await getDocs(collection(db, collectionName));
113
+ querySnapshot.forEach((doc) => {
114
+ list.push({ id: doc.id, ...doc.data() } as CustomReview);
115
+ });
116
+ return list;
117
+ } catch (error) {
118
+ console.warn("Could not retrieve custom reviews from Firestore.", error);
119
+ return [];
120
+ }
121
+ }
122
+
123
+ // 5. Add Review to Firestore
124
+ export async function addReview(review: Omit<CustomReview, "id">): Promise<string> {
125
+ const collectionName = "reviews";
126
+ try {
127
+ const docRef = await addDoc(collection(db, collectionName), {
128
+ name: review.name,
129
+ rating: Number(review.rating),
130
+ text: review.text,
131
+ date: review.date,
132
+ initials: review.initials,
133
+ });
134
+ return docRef.id;
135
+ } catch (error) {
136
+ handleFirestoreError(error, OperationType.WRITE, collectionName);
137
+ return "error";
138
+ }
139
+ }
140
+
141
+ // Fallback local seed data
142
+ export const SEED_TYRE_PRODUCTS: TireProduct[] = DEFAULT_TIR_PRODUCTS_SEED.map((p, i) => ({
143
+ id: `seed-${p.brand.toLowerCase()}-${i + 1}`,
144
+ name: p.name,
145
+ brand: p.brand,
146
+ size: p.size,
147
+ feature: p.feature,
148
+ price: p.price,
149
+ badge: p.category === "hot-selling" ? "Hot Selling" : p.category === "new-brands" ? "New Arrival" : "Famous",
150
+ segment: p.category === "hot-selling" ? "hot" : p.category === "new-brands" ? "new" : "famous",
151
+ image: p.imageUrl,
152
+ description: p.description || ""
153
+ }));
154
+
155
+ // Pak-Specific Fitments specifications model structure
156
+ export interface CarFitment {
157
+ make: string;
158
+ model: string;
159
+ variants: {
160
+ name: string;
161
+ recommendedSize: string;
162
+ engine: string;
163
+ pressure: string;
164
+ }[];
165
+ }
166
+
167
+ export const PAK_CAR_FITMENTS: CarFitment[] = [
168
+ {
169
+ make: "Toyota",
170
+ model: "Corolla",
171
+ variants: [
172
+ { name: "Altis Grande", recommendedSize: "195/65R15", engine: "1800cc", pressure: "32 PSI" },
173
+ { name: "GLI / XLI", recommendedSize: "185/65R15", engine: "1300cc", pressure: "30 PSI" },
174
+ { name: "Altis 1.6", recommendedSize: "195/65R15", engine: "1600cc", pressure: "32 PSI" }
175
+ ]
176
+ },
177
+ {
178
+ make: "Toyota",
179
+ model: "Yaris",
180
+ variants: [
181
+ { name: "ATIV X CVT", recommendedSize: "185/60R15", engine: "1500cc", pressure: "32 PSI" },
182
+ { name: "GLI MT / CVT", recommendedSize: "185/60R15", engine: "1300cc", pressure: "30 PSI" }
183
+ ]
184
+ },
185
+ {
186
+ make: "Toyota",
187
+ model: "Fortuner",
188
+ variants: [
189
+ { name: "Sigma 4 Diesel", recommendedSize: "265/60R18", engine: "2800cc", pressure: "32 PSI" },
190
+ { name: "2.7 G Petrol", recommendedSize: "265/65R17", engine: "2700cc", pressure: "30 PSI" }
191
+ ]
192
+ },
193
+ {
194
+ make: "Toyota",
195
+ model: "Hilux Revo",
196
+ variants: [
197
+ { name: "Revo V", recommendedSize: "265/60R18", engine: "2800cc", pressure: "32 PSI" },
198
+ { name: "Revo G / Single Cabin", recommendedSize: "265/65R17", engine: "2800cc", pressure: "32 PSI" }
199
+ ]
200
+ },
201
+ {
202
+ make: "Toyota",
203
+ model: "Vitz",
204
+ variants: [
205
+ { name: "Hybrid / F", recommendedSize: "165/70R14", engine: "1000cc / 1500cc", pressure: "30 PSI" },
206
+ { name: "Jewela / U", recommendedSize: "185/60R15", engine: "1300cc", pressure: "30 PSI" }
207
+ ]
208
+ },
209
+ {
210
+ make: "Honda",
211
+ model: "Civic",
212
+ variants: [
213
+ { name: "Oriel", recommendedSize: "215/55R16", engine: "1800cc", pressure: "32 PSI" },
214
+ { name: "RS Turbo", recommendedSize: "215/50R17", engine: "1500cc", pressure: "32 PSI" }
215
+ ]
216
+ },
217
+ {
218
+ make: "Honda",
219
+ model: "City",
220
+ variants: [
221
+ { name: "1.5L Aspire", recommendedSize: "185/55R16", engine: "1500cc", pressure: "32 PSI" },
222
+ { name: "1.2L i-VTEC", recommendedSize: "175/65R15", engine: "1200cc", pressure: "30 PSI" }
223
+ ]
224
+ },
225
+ {
226
+ make: "Honda",
227
+ model: "BR-V",
228
+ variants: [
229
+ { name: "i-VTEC S", recommendedSize: "195/60R16", engine: "1500cc", pressure: "32 PSI" }
230
+ ]
231
+ },
232
+ {
233
+ make: "Suzuki",
234
+ model: "Alto",
235
+ variants: [
236
+ { name: "VXL / AGS", recommendedSize: "145/80R13", engine: "660cc", pressure: "30 PSI" },
237
+ { name: "VXR / VX", recommendedSize: "145/80R13", engine: "660cc", pressure: "28 PSI" }
238
+ ]
239
+ },
240
+ {
241
+ make: "Suzuki",
242
+ model: "Cultus",
243
+ variants: [
244
+ { name: "VXL / AGS", recommendedSize: "165/70R14", engine: "1000cc", pressure: "30 PSI" },
245
+ { name: "VXR", recommendedSize: "165/70R14", engine: "1000cc", pressure: "30 PSI" }
246
+ ]
247
+ },
248
+ {
249
+ make: "Suzuki",
250
+ model: "Swift",
251
+ variants: [
252
+ { name: "GLX CVT", recommendedSize: "185/55R16", engine: "1200cc", pressure: "32 PSI" },
253
+ { name: "GL MT / CVT", recommendedSize: "185/60R15", engine: "1200cc", pressure: "32 PSI" }
254
+ ]
255
+ },
256
+ {
257
+ make: "Suzuki",
258
+ model: "Wagon R",
259
+ variants: [
260
+ { name: "VXL / AGS", recommendedSize: "145/80R13", engine: "1000cc", pressure: "30 PSI" },
261
+ { name: "VXR", recommendedSize: "145/80R13", engine: "1000cc", pressure: "30 PSI" }
262
+ ]
263
+ },
264
+ {
265
+ make: "Suzuki",
266
+ model: "Mehran",
267
+ variants: [
268
+ { name: "VXR / VX", recommendedSize: "145/70R12", engine: "800cc", pressure: "28 PSI" }
269
+ ]
270
+ },
271
+ {
272
+ make: "Kia",
273
+ model: "Sportage",
274
+ variants: [
275
+ { name: "Alpha", recommendedSize: "225/55R18", engine: "2000cc", pressure: "34 PSI" },
276
+ { name: "AWD / FWD", recommendedSize: "225/55R18", engine: "2000cc", pressure: "34 PSI" }
277
+ ]
278
+ },
279
+ {
280
+ make: "Kia",
281
+ model: "Picanto",
282
+ variants: [
283
+ { name: "Automatic 1.0", recommendedSize: "165/60R14", engine: "1000cc", pressure: "30 PSI" },
284
+ { name: "Manual 1.0", recommendedSize: "165/60R14", engine: "1000cc", pressure: "30 PSI" }
285
+ ]
286
+ },
287
+ {
288
+ make: "Kia",
289
+ model: "Stonic",
290
+ variants: [
291
+ { name: "EX Plus", recommendedSize: "195/55R16", engine: "1400cc", pressure: "32 PSI" }
292
+ ]
293
+ },
294
+ {
295
+ make: "Hyundai",
296
+ model: "Tucson",
297
+ variants: [
298
+ { name: "GLS Sport", recommendedSize: "225/55R18", engine: "2000cc", pressure: "33 PSI" },
299
+ { name: "Ultimate", recommendedSize: "225/55R18", engine: "2000cc", pressure: "33 PSI" }
300
+ ]
301
+ },
302
+ {
303
+ make: "Hyundai",
304
+ model: "Elantra",
305
+ variants: [
306
+ { name: "GLS 2.0", recommendedSize: "205/55R16", engine: "2000cc", pressure: "32 PSI" },
307
+ { name: "GL 1.6", recommendedSize: "205/55R16", engine: "1600cc", pressure: "32 PSI" }
308
+ ]
309
+ },
310
+ {
311
+ make: "Hyundai",
312
+ model: "Sonata",
313
+ variants: [
314
+ { name: "Sonata 2.5L", recommendedSize: "235/45R18", engine: "2500cc", pressure: "32 PSI" },
315
+ { name: "Sonata 2.0L", recommendedSize: "215/55R17", engine: "2000cc", pressure: "32 PSI" }
316
+ ]
317
+ },
318
+ {
319
+ make: "Changan",
320
+ model: "Alsvin",
321
+ variants: [
322
+ { name: "Lumiere 1.5", recommendedSize: "185/55R15", engine: "1500cc", pressure: "32 PSI" },
323
+ { name: "Comfort 1.3 / 1.5", recommendedSize: "185/55R15", engine: "1300cc / 1500cc", pressure: "30 PSI" }
324
+ ]
325
+ },
326
+ {
327
+ make: "Changan",
328
+ model: "Oshan X7",
329
+ variants: [
330
+ { name: "FutureSense", recommendedSize: "225/55R19", engine: "1500cc Turbo", pressure: "34 PSI" },
331
+ { name: "Comfort", recommendedSize: "225/55R19", engine: "1500cc Turbo", pressure: "34 PSI" }
332
+ ]
333
+ },
334
+ {
335
+ make: "MG",
336
+ model: "HS",
337
+ variants: [
338
+ { name: "Essence / Trophy", recommendedSize: "235/50R18", engine: "1500cc Turbo", pressure: "32 PSI" }
339
+ ]
340
+ },
341
+ {
342
+ make: "Haval",
343
+ model: "H6",
344
+ variants: [
345
+ { name: "HEV (Hybrid)", recommendedSize: "225/55R19", engine: "1500cc HEV", pressure: "33 PSI" },
346
+ { name: "1.5T / 2.0T AWD", recommendedSize: "225/55R19", engine: "1500cc / 2000cc", pressure: "33 PSI" }
347
+ ]
348
+ },
349
+ {
350
+ make: "Haval",
351
+ model: "Jolion",
352
+ variants: [
353
+ { name: "High Grade", recommendedSize: "225/55R18", engine: "1500cc Turbo", pressure: "32 PSI" }
354
+ ]
355
+ },
356
+ {
357
+ make: "Proton",
358
+ model: "Saga",
359
+ variants: [
360
+ { name: "Ace A/T", recommendedSize: "185/55R15", engine: "1300cc", pressure: "32 PSI" },
361
+ { name: "Standard M/T / A/T", recommendedSize: "185/60R14", engine: "1300cc", pressure: "30 PSI" }
362
+ ]
363
+ },
364
+ {
365
+ make: "DFSK",
366
+ model: "Glory 580",
367
+ variants: [
368
+ { name: "Glory 580 Pro", recommendedSize: "225/60R17", engine: "1500cc Turbo", pressure: "32 PSI" }
369
+ ]
370
+ }
371
+ ];
372
+
src/main.tsx ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ import {StrictMode} from 'react';
2
+ import {createRoot} from 'react-dom/client';
3
+ import App from './App.tsx';
4
+ import './index.css';
5
+
6
+ createRoot(document.getElementById('root')!).render(
7
+ <StrictMode>
8
+ <App />
9
+ </StrictMode>,
10
+ );
storage.rules ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ rules_version = '2';
2
+ service firebase.storage {
3
+ match /b/{bucket}/o {
4
+
5
+ // Default-deny policy for entire storage registry
6
+ match /{allPaths=**} {
7
+ allow read, write: if false;
8
+ }
9
+
10
+ // Rules for the product-images folder: public read allowed, authenticated writes only
11
+ match /product-images/{imageId} {
12
+ allow read: if true;
13
+ allow write: if request.auth != null
14
+ && request.resource.size < 5 * 1024 * 1024 // limit size to 5MB
15
+ && request.resource.contentType.matches('image/.*'); // strictly image MIME types
16
+ allow delete: if request.auth != null;
17
+ }
18
+ }
19
+ }
tsconfig.json ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "experimentalDecorators": true,
5
+ "useDefineForClassFields": false,
6
+ "module": "ESNext",
7
+ "lib": [
8
+ "ES2022",
9
+ "DOM",
10
+ "DOM.Iterable"
11
+ ],
12
+ "skipLibCheck": true,
13
+ "moduleResolution": "bundler",
14
+ "isolatedModules": true,
15
+ "moduleDetection": "force",
16
+ "allowJs": true,
17
+ "jsx": "react-jsx",
18
+ "paths": {
19
+ "@/*": [
20
+ "./*"
21
+ ]
22
+ },
23
+ "allowImportingTsExtensions": true,
24
+ "noEmit": true
25
+ }
26
+ }
vite.config.ts ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import tailwindcss from '@tailwindcss/vite';
2
+ import react from '@vitejs/plugin-react';
3
+ import path from 'path';
4
+ import {defineConfig} from 'vite';
5
+
6
+ export default defineConfig(() => {
7
+ return {
8
+ plugins: [react(), tailwindcss()],
9
+ resolve: {
10
+ alias: {
11
+ '@': path.resolve(__dirname, '.'),
12
+ },
13
+ },
14
+ server: {
15
+ // HMR is disabled in AI Studio via DISABLE_HMR env var.
16
+ // Do not modify—file watching is disabled to prevent flickering during agent edits.
17
+ hmr: process.env.DISABLE_HMR !== 'true',
18
+ // Disable file watching when DISABLE_HMR is true to save CPU during agent edits.
19
+ watch: process.env.DISABLE_HMR === 'true' ? null : {},
20
+ },
21
+ };
22
+ });