Merge version_2 into main #2
@@ -1,55 +1,45 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Halant } from "next/font/google";
|
||||
import { Inter } from "next/font/google";
|
||||
import { Raleway } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import { ServiceWrapper } from "@/components/ServiceWrapper";
|
||||
import Tag from "@/tag/Tag";
|
||||
|
||||
const halant = Halant({
|
||||
variable: "--font-halant", subsets: ["latin"],
|
||||
weight: ["300", "400", "500", "600", "700"],
|
||||
});
|
||||
|
||||
const inter = Inter({
|
||||
variable: "--font-inter", subsets: ["latin"],
|
||||
});
|
||||
|
||||
const raleway = Raleway({
|
||||
variable: "--font-raleway", subsets: ["latin"],
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Dataset Cleaner - Privacy-First Data Cleaning Tool", description: "Clean, validate, and transform your datasets with Dataset Cleaner. 100% browser-based, no data leaves your computer. Remove duplicates, trim whitespace, handle special characters with AI-powered precision.", keywords: "data cleaning, data validation, dataset tool, CSV cleaner, data quality, AI cleaning, browser-based", metadataBase: new URL("https://datasetcleaner.com"),
|
||||
alternates: {
|
||||
canonical: "https://datasetcleaner.com"},
|
||||
canonical: "https://datasetcleaner.com"
|
||||
},
|
||||
openGraph: {
|
||||
title: "Dataset Cleaner - Privacy-First Data Cleaning Tool", description: "Clean your datasets with confidence. 100% private, browser-based data cleaning powered by AI.", url: "https://datasetcleaner.com", siteName: "Dataset Cleaner", type: "website", images: [
|
||||
{
|
||||
url: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AUGg4tVcwa1xJbmjLxOEngSK3W/a-modern-professional-software-dashboard-1772640055925-4dba2f06.png", alt: "Dataset Cleaner Dashboard"},
|
||||
],
|
||||
url: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AUGg4tVcwa1xJbmjLxOEngSK3W/a-modern-professional-software-dashboard-1772640055925-4dba2f06.png", alt: "Dataset Cleaner Dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image", title: "Dataset Cleaner - Privacy-First Data Cleaning", description: "Clean your datasets with confidence. 100% private, browser-based data cleaning powered by AI.", images: [
|
||||
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AUGg4tVcwa1xJbmjLxOEngSK3W/a-modern-professional-software-dashboard-1772640055925-4dba2f06.png"],
|
||||
"https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AUGg4tVcwa1xJbmjLxOEngSK3W/a-modern-professional-software-dashboard-1772640055925-4dba2f06.png"
|
||||
]
|
||||
},
|
||||
robots: {
|
||||
index: true,
|
||||
follow: true,
|
||||
},
|
||||
follow: true
|
||||
}
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
children
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en" suppressHydrationWarning>
|
||||
<ServiceWrapper>
|
||||
<body
|
||||
className={`${halant.variable} ${inter.variable} ${raleway.variable} antialiased`}
|
||||
>
|
||||
<body className={`${inter.variable} antialiased`}>
|
||||
<Tag />
|
||||
{children}
|
||||
|
||||
|
||||
232
src/app/page.tsx
232
src/app/page.tsx
@@ -1,5 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react";
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import NavbarStyleFullscreen from '@/components/navbar/NavbarStyleFullscreen/NavbarStyleFullscreen';
|
||||
import HeroBillboardGallery from '@/components/sections/hero/HeroBillboardGallery';
|
||||
@@ -7,9 +8,62 @@ import FeatureCardTwentyOne from '@/components/sections/feature/FeatureCardTwent
|
||||
import ContactSplitForm from '@/components/sections/contact/ContactSplitForm';
|
||||
import TestimonialCardThirteen from '@/components/sections/testimonial/TestimonialCardThirteen';
|
||||
import FooterMedia from '@/components/sections/footer/FooterMedia';
|
||||
import { Shield, Sparkles, Star } from 'lucide-react';
|
||||
import { Shield, Sparkles, Star, Upload, Download, Zap, Lock, RefreshCw, AlertCircle } from 'lucide-react';
|
||||
|
||||
export default function LandingPage() {
|
||||
const [tokenValue, setTokenValue] = useState("");
|
||||
const [datasetId, setDatasetId] = useState("");
|
||||
const [verificationStatus, setVerificationStatus] = useState<"idle" | "verified" | "invalid">("idle");
|
||||
const [cleaningProgress, setCleaningProgress] = useState(0);
|
||||
const [selectedCleaningOptions, setSelectedCleaningOptions] = useState<string[]>([]);
|
||||
const [isProcessing, setIsProcessing] = useState(false);
|
||||
|
||||
const handleTokenVerification = () => {
|
||||
if (tokenValue && tokenValue.length >= 8) {
|
||||
setVerificationStatus("verified");
|
||||
} else {
|
||||
setVerificationStatus("invalid");
|
||||
}
|
||||
};
|
||||
|
||||
const handleDatasetFetch = () => {
|
||||
if (datasetId && verificationStatus === "verified") {
|
||||
// Simulate dataset fetch
|
||||
console.log("Fetching dataset:", datasetId);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCleaningOptionToggle = (option: string) => {
|
||||
setSelectedCleaningOptions(prev =>
|
||||
prev.includes(option)
|
||||
? prev.filter(o => o !== option)
|
||||
: [...prev, option]
|
||||
);
|
||||
};
|
||||
|
||||
const handleStartCleaning = () => {
|
||||
if (selectedCleaningOptions.length === 0) return;
|
||||
|
||||
setIsProcessing(true);
|
||||
setCleaningProgress(0);
|
||||
|
||||
// Simulate cleaning pipeline with progress tracking
|
||||
const interval = setInterval(() => {
|
||||
setCleaningProgress(prev => {
|
||||
if (prev >= 100) {
|
||||
clearInterval(interval);
|
||||
setIsProcessing(false);
|
||||
return 100;
|
||||
}
|
||||
return prev + Math.random() * 30;
|
||||
});
|
||||
}, 500);
|
||||
};
|
||||
|
||||
const handleDownloadOutput = () => {
|
||||
console.log("Downloading cleaned dataset...");
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="icon-arrow"
|
||||
@@ -30,7 +84,6 @@ export default function LandingPage() {
|
||||
{ name: "Features", id: "features" },
|
||||
{ name: "App", id: "app" },
|
||||
{ name: "Documentation", id: "docs" },
|
||||
{ name: "Pricing", id: "pricing" },
|
||||
{ name: "Contact", id: "contact" }
|
||||
]}
|
||||
bottomLeftText="Privacy First — No data leaves your browser"
|
||||
@@ -93,22 +146,163 @@ export default function LandingPage() {
|
||||
</div>
|
||||
|
||||
<div id="app" data-section="app">
|
||||
<ContactSplitForm
|
||||
title="Start Cleaning Your Data"
|
||||
description="Upload your dataset or paste data directly. Our tool processes everything locally in your browser for maximum privacy and security."
|
||||
inputs={[
|
||||
{ name: "email", type: "email", placeholder: "your@email.com", required: true },
|
||||
{ name: "datasetName", type: "text", placeholder: "My Dataset Name", required: true }
|
||||
]}
|
||||
textarea={{ name: "dataDescription", placeholder: "Describe your dataset and cleaning needs...", rows: 5, required: false }}
|
||||
imageSrc="https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AUGg4tVcwa1xJbmjLxOEngSK3W/uploaded-1772640003528-ovou18nk.png?_wi=2"
|
||||
imageAlt="Dataset Cleaner Application"
|
||||
useInvertedBackground={true}
|
||||
mediaPosition="right"
|
||||
mediaAnimation="slide-up"
|
||||
buttonText="Start Cleaning"
|
||||
containerClassName="py-20"
|
||||
/>
|
||||
<div className="w-full py-20 px-6">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
{/* Token Integration Section */}
|
||||
<div className="mb-12 p-8 bg-white rounded-2xl shadow-lg border border-gray-200">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<Lock className="w-6 h-6 text-blue-600" />
|
||||
<h2 className="text-2xl font-semibold">Token Verification & Integration</h2>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">API Token</label>
|
||||
<input
|
||||
type="password"
|
||||
value={tokenValue}
|
||||
onChange={(e) => setTokenValue(e.target.value)}
|
||||
placeholder="Enter your API token"
|
||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Dataset ID</label>
|
||||
<input
|
||||
type="text"
|
||||
value={datasetId}
|
||||
onChange={(e) => setDatasetId(e.target.value)}
|
||||
placeholder="Enter dataset ID"
|
||||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-4">
|
||||
<button
|
||||
onClick={handleTokenVerification}
|
||||
className="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition flex items-center gap-2"
|
||||
>
|
||||
<AlertCircle className="w-4 h-4" />
|
||||
Verify Token
|
||||
</button>
|
||||
<button
|
||||
onClick={handleDatasetFetch}
|
||||
disabled={verificationStatus !== "verified"}
|
||||
className="px-6 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2"
|
||||
>
|
||||
<Upload className="w-4 h-4" />
|
||||
Fetch Dataset
|
||||
</button>
|
||||
</div>
|
||||
{verificationStatus === "verified" && (
|
||||
<div className="mt-4 p-3 bg-green-50 text-green-700 rounded-lg flex items-center gap-2">
|
||||
<Sparkles className="w-4 h-4" />
|
||||
Token verified successfully!
|
||||
</div>
|
||||
)}
|
||||
{verificationStatus === "invalid" && (
|
||||
<div className="mt-4 p-3 bg-red-50 text-red-700 rounded-lg flex items-center gap-2">
|
||||
<AlertCircle className="w-4 h-4" />
|
||||
Invalid token or insufficient length
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Cleaning Options Section */}
|
||||
<div className="mb-12 p-8 bg-white rounded-2xl shadow-lg border border-gray-200">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<Zap className="w-6 h-6 text-orange-500" />
|
||||
<h2 className="text-2xl font-semibold">Cleaning Pipeline Configuration</h2>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-6">
|
||||
{[
|
||||
{ id: "duplicates", label: "Remove Duplicates", icon: RefreshCw },
|
||||
{ id: "whitespace", label: "Whitespace Normalization", icon: Zap },
|
||||
{ id: "repeated", label: "Remove Repeated Values", icon: RefreshCw },
|
||||
{ id: "special", label: "Special Characters Removal", icon: AlertCircle },
|
||||
{ id: "formatting", label: "Format Standardization", icon: Zap },
|
||||
{ id: "encoding", label: "Character Encoding Fix", icon: Zap }
|
||||
].map(option => {
|
||||
const IconComponent = option.icon;
|
||||
return (
|
||||
<label key={option.id} className="flex items-center gap-3 p-4 border border-gray-200 rounded-lg hover:bg-gray-50 cursor-pointer transition">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selectedCleaningOptions.includes(option.id)}
|
||||
onChange={() => handleCleaningOptionToggle(option.id)}
|
||||
className="w-4 h-4 text-blue-600 rounded"
|
||||
/>
|
||||
<IconComponent className="w-4 h-4 text-gray-600" />
|
||||
<span className="text-sm font-medium">{option.label}</span>
|
||||
</label>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<button
|
||||
onClick={handleStartCleaning}
|
||||
disabled={selectedCleaningOptions.length === 0 || isProcessing}
|
||||
className="w-full px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2 font-semibold"
|
||||
>
|
||||
<Zap className="w-5 h-5" />
|
||||
{isProcessing ? "Processing..." : "Start Cleaning Pipeline"}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Progress Tracking Section */}
|
||||
{isProcessing && (
|
||||
<div className="mb-12 p-8 bg-white rounded-2xl shadow-lg border border-gray-200">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<RefreshCw className="w-6 h-6 text-blue-600 animate-spin" />
|
||||
<h2 className="text-2xl font-semibold">Cleaning Progress</h2>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<div className="flex justify-between mb-2">
|
||||
<span className="text-sm font-medium">Overall Progress</span>
|
||||
<span className="text-sm font-medium">{Math.round(cleaningProgress)}%</span>
|
||||
</div>
|
||||
<div className="w-full bg-gray-200 rounded-full h-3 overflow-hidden">
|
||||
<div
|
||||
className="bg-gradient-to-r from-blue-500 to-blue-600 h-full transition-all duration-300"
|
||||
style={{ width: `${cleaningProgress}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-4 text-sm">
|
||||
{selectedCleaningOptions.map(option => (
|
||||
<div key={option} className="p-3 bg-blue-50 rounded-lg">
|
||||
<div className="font-medium text-gray-700 capitalize">{option.replace(/([A-Z])/g, ' $1').trim()}</div>
|
||||
<div className="text-gray-500 text-xs">In progress...</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Download Section */}
|
||||
{cleaningProgress === 100 && (
|
||||
<div className="p-8 bg-white rounded-2xl shadow-lg border border-green-200">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<Download className="w-6 h-6 text-green-600" />
|
||||
<h2 className="text-2xl font-semibold">Download Cleaned Dataset</h2>
|
||||
</div>
|
||||
<p className="text-gray-600 mb-6">Your dataset has been successfully cleaned! Choose your preferred format to download.</p>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
{["CSV", "XLSX", "JSON", "XML"].map(format => (
|
||||
<button
|
||||
key={format}
|
||||
onClick={handleDownloadOutput}
|
||||
className="px-4 py-3 bg-green-600 text-white rounded-lg hover:bg-green-700 transition font-semibold flex items-center justify-center gap-2"
|
||||
>
|
||||
<Download className="w-4 h-4" />
|
||||
{format}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="testimonials" data-section="testimonials">
|
||||
@@ -139,7 +333,7 @@ export default function LandingPage() {
|
||||
logoText="Dataset Cleaner"
|
||||
copyrightText="© 2025 Dataset Cleaner. All rights reserved."
|
||||
columns={[
|
||||
{ title: "Product", items: [{ label: "Features", href: "#features" }, { label: "Pricing", href: "#pricing" }, { label: "Documentation", href: "https://docs.datasetcleaner.com" }, { label: "API Reference", href: "https://api.datasetcleaner.com" }] },
|
||||
{ title: "Product", items: [{ label: "Features", href: "#features" }, { label: "Documentation", href: "#docs" }, { label: "API Reference", href: "https://api.datasetcleaner.com" }, { label: "Status", href: "https://status.datasetcleaner.com" }] },
|
||||
{ title: "Company", items: [{ label: "About Us", href: "#about" }, { label: "Blog", href: "https://blog.datasetcleaner.com" }, { label: "Careers", href: "#careers" }, { label: "Contact", href: "#contact" }] },
|
||||
{ title: "Legal", items: [{ label: "Privacy Policy", href: "#privacy" }, { label: "Terms of Service", href: "#terms" }, { label: "Cookie Policy", href: "#cookies" }, { label: "Security", href: "https://security.datasetcleaner.com" }] }
|
||||
]}
|
||||
|
||||
Reference in New Issue
Block a user