diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 00000000..d738ab87 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,29 @@ +# Robots.txt for https://dewe.dev +# Generated automatically + +User-agent: * +Allow: / + +# Sitemap location +Sitemap: https://dewe.dev/sitemap.xml + +# Block any future admin or private routes +Disallow: /admin/ +Disallow: /api/ +Disallow: /.well-known/ + +# Allow all major search engines +User-agent: Googlebot +Allow: / + +User-agent: Bingbot +Allow: / + +User-agent: Slurp +Allow: / + +User-agent: DuckDuckBot +Allow: / + +# Crawl delay for politeness +Crawl-delay: 1 diff --git a/public/sitemap.xml b/public/sitemap.xml new file mode 100644 index 00000000..2a69644b --- /dev/null +++ b/public/sitemap.xml @@ -0,0 +1,66 @@ + + + + https://dewe.dev/ + 2025-01-23 + weekly + 1.0 + + + https://dewe.dev/object-editor + 2025-01-23 + monthly + 0.8 + + + https://dewe.dev/table-editor + 2025-01-23 + monthly + 0.8 + + + https://dewe.dev/url + 2025-01-23 + monthly + 0.8 + + + https://dewe.dev/base64 + 2025-01-23 + monthly + 0.8 + + + https://dewe.dev/beautifier + 2025-01-23 + monthly + 0.8 + + + https://dewe.dev/diff + 2025-01-23 + monthly + 0.8 + + + https://dewe.dev/text-length + 2025-01-23 + monthly + 0.8 + + + https://dewe.dev/privacy + 2025-01-23 + yearly + 0.3 + + + https://dewe.dev/terms + 2025-01-23 + yearly + 0.3 + + diff --git a/src/App.js b/src/App.js index 35073856..efca6312 100644 --- a/src/App.js +++ b/src/App.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; import Layout from './components/Layout'; import ErrorBoundary from './components/ErrorBoundary'; @@ -13,10 +13,18 @@ import DiffTool from './pages/DiffTool'; import TextLengthTool from './pages/TextLengthTool'; import ObjectEditor from './pages/ObjectEditor'; import TableEditor from './pages/TableEditor'; +import TermsOfService from './pages/TermsOfService'; +import PrivacyPolicy from './pages/PrivacyPolicy'; +import { initGA } from './utils/analytics'; import './index.css'; function App() { + // Initialize Google Analytics on app startup + useEffect(() => { + initGA(); + }, []); + return ( @@ -33,7 +41,8 @@ function App() { } /> } /> } /> - + } /> + } /> diff --git a/src/components/ConsentBanner.js b/src/components/ConsentBanner.js new file mode 100644 index 00000000..6a6ff2ad --- /dev/null +++ b/src/components/ConsentBanner.js @@ -0,0 +1,191 @@ +import React, { useState, useEffect } from 'react'; +import { Link } from 'react-router-dom'; +import { X, Shield, Settings, Check } from 'lucide-react'; +import { + shouldShowConsentBanner, + updateConsent, + CONSENT_CONFIGS, + getConsentBannerData +} from '../utils/consentManager'; + +const ConsentBanner = () => { + const [isVisible, setIsVisible] = useState(false); + const [showCustomize, setShowCustomize] = useState(false); + const [customConsent, setCustomConsent] = useState({ + analytics_storage: false, + ad_storage: false, + ad_personalization: false, + ad_user_data: false + }); + + const bannerData = getConsentBannerData(); + + useEffect(() => { + setIsVisible(shouldShowConsentBanner()); + }, []); + + const handleAcceptAll = () => { + updateConsent(CONSENT_CONFIGS.ACCEPT_ALL); + setIsVisible(false); + }; + + const handleEssentialOnly = () => { + updateConsent(CONSENT_CONFIGS.ESSENTIAL_ONLY); + setIsVisible(false); + }; + + const handleCustomSave = () => { + const consentChoices = { + necessary: 'granted', + analytics_storage: customConsent.analytics_storage ? 'granted' : 'denied', + ad_storage: customConsent.ad_storage ? 'granted' : 'denied', + ad_personalization: customConsent.ad_personalization ? 'granted' : 'denied', + ad_user_data: customConsent.ad_user_data ? 'granted' : 'denied' + }; + + updateConsent(consentChoices); + setIsVisible(false); + }; + + const toggleCustomConsent = (category) => { + setCustomConsent(prev => ({ + ...prev, + [category]: !prev[category] + })); + }; + + if (!isVisible) return null; + + return ( +
+
+ {!showCustomize ? ( + // Main consent banner +
+
+
+ +
+
+

+ {bannerData.title} +

+

+ {bannerData.description} +

+
+ + Privacy Policy + + + + Terms of Service + +
+
+
+ +
+ + + +
+
+ ) : ( + // Customization panel +
+
+

+ Customize Cookie Preferences +

+ +
+ +
+ {bannerData.purposes.map(purpose => ( +
+
+ {purpose.required ? ( +
+ +
+ ) : ( + toggleCustomConsent(purpose.id)} + className="w-4 h-4 text-blue-600 bg-slate-100 border-slate-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-slate-800 focus:ring-2 dark:bg-slate-700 dark:border-slate-600" + /> + )} +
+
+ +

+ {purpose.description} +

+
+
+ ))} +
+ +
+ + +
+
+ )} +
+
+ ); +}; + +export default ConsentBanner; diff --git a/src/components/Layout.js b/src/components/Layout.js index 1e90f331..06badbda 100644 --- a/src/components/Layout.js +++ b/src/components/Layout.js @@ -1,8 +1,12 @@ import React, { useState, useEffect, useRef } from 'react'; import { Link, useLocation } from 'react-router-dom'; -import { Home, Hash, FileSpreadsheet, Wand2, GitCompare, Menu, X, LinkIcon, Code2, ChevronDown, Type, Edit3, Table } from 'lucide-react'; +import { Home, Menu, X, ChevronDown, Terminal, Sparkles } from 'lucide-react'; import ThemeToggle from './ThemeToggle'; import ToolSidebar from './ToolSidebar'; +import SEOHead from './SEOHead'; +import ConsentBanner from './ConsentBanner'; +import { TOOLS, SITE_CONFIG, getCategoryConfig } from '../config/tools'; +import { useAnalytics } from '../hooks/useAnalytics'; const Layout = ({ children }) => { const location = useLocation(); @@ -10,6 +14,9 @@ const Layout = ({ children }) => { const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); const dropdownRef = useRef(null); + // Initialize analytics tracking + useAnalytics(); + const isActive = (path) => { return location.pathname === path; }; @@ -34,30 +41,27 @@ const Layout = ({ children }) => { setIsDropdownOpen(false); }, [location.pathname]); - const tools = [ - { path: '/object-editor', name: 'Object Editor', icon: Edit3, description: 'Visual editor for JSON & PHP objects' }, - { path: '/table-editor', name: 'Table Editor', icon: Table, description: 'Import, edit & export tabular data' }, - { path: '/url', name: 'URL Tool', icon: LinkIcon, description: 'URL encode/decode' }, - { path: '/base64', name: 'Base64 Tool', icon: Hash, description: 'Base64 encode/decode' }, - { path: '/csv-json', name: 'CSV/JSON Tool', icon: FileSpreadsheet, description: 'Convert CSV ↔ JSON' }, - { path: '/beautifier', name: 'Beautifier Tool', icon: Wand2, description: 'Beautify/minify code' }, - { path: '/diff', name: 'Diff Tool', icon: GitCompare, description: 'Compare text differences' }, - { path: '/text-length', name: 'Text Length Checker', icon: Type, description: 'Analyze text length & stats' }, - ]; - // Check if we're on a tool page (not homepage) const isToolPage = location.pathname !== '/'; return ( -
+
+ {/* SEO Head Management */} + + {/* Header */} -
+
- - - - DevTools + +
+
+
+ +
+
+ + {SITE_CONFIG.title} @@ -67,10 +71,10 @@ const Layout = ({ children }) => {