feat: Simplify home page and remove tracking form
- Remove complex tracking form from home page
- Replace with clean 'Analyze URL Redirects' call-to-action button
- Remove announcement badge '🚀 URL Tracker Tool V2 - Now Available'
- Clean up unused imports and form-related code
- Direct users to dedicated /track page for full functionality
- Improve user experience with cleaner, more focused home page
Changes:
- Simplified HomePage component with single CTA button
- Removed form validation, mutation handling, and result display
- Maintained all tracking functionality on /track page
- Professional appearance without promotional clutter
This commit is contained in:
@@ -2,8 +2,7 @@
|
||||
* Home Page - Landing page and quick start interface
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
import { trackUrlSubmission, trackAnalysisResult, trackError } from '../utils/analytics';
|
||||
|
||||
import {
|
||||
Box,
|
||||
Heading,
|
||||
@@ -13,48 +12,18 @@ import {
|
||||
HStack,
|
||||
Card,
|
||||
CardBody,
|
||||
CardHeader,
|
||||
SimpleGrid,
|
||||
Button,
|
||||
Badge,
|
||||
Input,
|
||||
Icon,
|
||||
useToast,
|
||||
useColorModeValue,
|
||||
Stat,
|
||||
StatLabel,
|
||||
StatNumber,
|
||||
StatHelpText,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
Select,
|
||||
Switch,
|
||||
FormHelperText,
|
||||
Slider,
|
||||
SliderTrack,
|
||||
SliderFilledTrack,
|
||||
SliderThumb,
|
||||
NumberInput,
|
||||
NumberInputField,
|
||||
NumberInputStepper,
|
||||
NumberIncrementStepper,
|
||||
NumberDecrementStepper,
|
||||
Textarea,
|
||||
Collapse,
|
||||
Divider,
|
||||
Alert,
|
||||
AlertIcon,
|
||||
// StatArrow,
|
||||
// Flex,
|
||||
// Stack,
|
||||
// Image,
|
||||
// Center,
|
||||
Badge,
|
||||
} from '@chakra-ui/react';
|
||||
import { Link as RouterLink } from 'react-router-dom';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { z } from 'zod';
|
||||
|
||||
import {
|
||||
FiTrendingUp,
|
||||
FiActivity,
|
||||
@@ -64,35 +33,16 @@ import {
|
||||
FiUpload,
|
||||
FiBarChart,
|
||||
FiZap,
|
||||
// FiGlobe,
|
||||
FiArrowRight,
|
||||
FiCheckCircle,
|
||||
} from 'react-icons/fi';
|
||||
|
||||
import { trackingApi, TrackRequestV2 } from '../services/api';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { TrackingResults } from '../components/Tracking/TrackingResults';
|
||||
|
||||
const trackingSchema = z.object({
|
||||
url: z.string().min(1, 'URL is required').url('Invalid URL format'),
|
||||
method: z.enum(['GET', 'POST', 'HEAD']),
|
||||
userAgent: z.string().optional(),
|
||||
maxHops: z.number().min(1).max(20),
|
||||
timeout: z.number().min(1000).max(30000),
|
||||
enableSSLAnalysis: z.boolean(),
|
||||
enableSEOAnalysis: z.boolean(),
|
||||
enableSecurityAnalysis: z.boolean(),
|
||||
customHeaders: z.string().optional(),
|
||||
});
|
||||
|
||||
type TrackingFormData = z.infer<typeof trackingSchema>;
|
||||
|
||||
export function HomePage() {
|
||||
const [showAdvanced, setShowAdvanced] = useState(false);
|
||||
const [trackingResult, setTrackingResult] = useState<any>(null);
|
||||
const { isAuthenticated } = useAuth();
|
||||
const toast = useToast();
|
||||
|
||||
const cardBg = useColorModeValue('white', 'gray.800');
|
||||
const borderColor = useColorModeValue('gray.200', 'gray.700');
|
||||
const bgGradient = useColorModeValue(
|
||||
@@ -100,112 +50,6 @@ export function HomePage() {
|
||||
'linear(to-br, gray.900, blue.900, purple.900)'
|
||||
);
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
watch,
|
||||
setValue,
|
||||
formState: { errors },
|
||||
} = useForm<TrackingFormData>({
|
||||
resolver: zodResolver(trackingSchema),
|
||||
defaultValues: {
|
||||
method: 'GET',
|
||||
maxHops: 10,
|
||||
timeout: 15000,
|
||||
enableSSLAnalysis: true,
|
||||
enableSEOAnalysis: true,
|
||||
enableSecurityAnalysis: true,
|
||||
},
|
||||
});
|
||||
|
||||
const maxHops = watch('maxHops');
|
||||
const timeout = watch('timeout');
|
||||
|
||||
// Tracking mutation
|
||||
const trackingMutation = useMutation({
|
||||
mutationFn: async (data: TrackRequestV2) => {
|
||||
// Track URL submission
|
||||
trackUrlSubmission(data.url, isAuthenticated);
|
||||
return await trackingApi.trackUrlV2(data);
|
||||
},
|
||||
onSuccess: (result) => {
|
||||
setTrackingResult(result);
|
||||
toast({
|
||||
title: 'Tracking completed',
|
||||
description: `Found ${result.check.redirectCount} redirects`,
|
||||
status: 'success',
|
||||
duration: 3000,
|
||||
isClosable: true,
|
||||
});
|
||||
|
||||
// Track analysis results
|
||||
const analysis = (result as any).check?.analysis;
|
||||
if (analysis) {
|
||||
trackAnalysisResult(
|
||||
(result as any).check.redirectCount || 0,
|
||||
analysis.ssl?.warningsJson?.length > 0 || false,
|
||||
!analysis.seo?.robotsTxtStatus || analysis.seo?.noindex || false,
|
||||
analysis.security?.mixedContent === 'PRESENT' || analysis.security?.safeBrowsingStatus !== 'safe' || false
|
||||
);
|
||||
}
|
||||
|
||||
// Don't auto-navigate on home page to allow users to see results
|
||||
// They can manually navigate to their dashboard to see saved results
|
||||
},
|
||||
onError: (error: any) => {
|
||||
// Track errors
|
||||
trackError('tracking_failed', error.response?.data?.message || error.message || 'Unknown error');
|
||||
|
||||
toast({
|
||||
title: 'Tracking failed',
|
||||
description: error.response?.data?.message || 'An error occurred',
|
||||
status: 'error',
|
||||
duration: 5000,
|
||||
isClosable: true,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = (data: TrackingFormData) => {
|
||||
let headers: Record<string, string> = {};
|
||||
|
||||
// Parse custom headers if provided
|
||||
if (data.customHeaders) {
|
||||
try {
|
||||
const lines = data.customHeaders.split('\n').filter(line => line.trim());
|
||||
for (const line of lines) {
|
||||
const [key, ...valueParts] = line.split(':');
|
||||
if (key && valueParts.length > 0) {
|
||||
headers[key.trim()] = valueParts.join(':').trim();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: 'Invalid headers format',
|
||||
description: 'Please use format: Header-Name: Header-Value',
|
||||
status: 'error',
|
||||
duration: 3000,
|
||||
isClosable: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const trackRequest: TrackRequestV2 = {
|
||||
url: data.url,
|
||||
method: data.method,
|
||||
userAgent: data.userAgent,
|
||||
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
||||
maxHops: data.maxHops,
|
||||
timeout: data.timeout,
|
||||
enableSSLAnalysis: data.enableSSLAnalysis,
|
||||
enableSEOAnalysis: data.enableSEOAnalysis,
|
||||
enableSecurityAnalysis: data.enableSecurityAnalysis,
|
||||
};
|
||||
|
||||
trackingMutation.mutate(trackRequest);
|
||||
};
|
||||
|
||||
const features = [
|
||||
{
|
||||
icon: FiActivity,
|
||||
@@ -278,9 +122,7 @@ export function HomePage() {
|
||||
<Box bgGradient={bgGradient} py={{ base: 16, md: 24 }}>
|
||||
<Container maxW="6xl">
|
||||
<VStack spacing={8} textAlign="center">
|
||||
<Badge colorScheme="brand" variant="subtle" px={4} py={2} borderRadius="full">
|
||||
🚀 URL Tracker Tool V2 - Now Available
|
||||
</Badge>
|
||||
|
||||
|
||||
<VStack spacing={4}>
|
||||
<Heading
|
||||
@@ -297,185 +139,32 @@ export function HomePage() {
|
||||
</Text>
|
||||
</VStack>
|
||||
|
||||
{/* Advanced Tracking Form */}
|
||||
{/* Call to Action */}
|
||||
<Card bg={cardBg} border="1px solid" borderColor={borderColor} maxW="4xl" w="full">
|
||||
<CardHeader>
|
||||
<HStack justify="space-between">
|
||||
<Heading as="h3" size="md">
|
||||
URL Redirect Tracker
|
||||
</Heading>
|
||||
<Badge colorScheme="brand">Enhanced v2</Badge>
|
||||
</HStack>
|
||||
</CardHeader>
|
||||
|
||||
<CardBody>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<VStack spacing={6} align="stretch">
|
||||
{/* URL Input */}
|
||||
<FormControl isInvalid={!!errors.url}>
|
||||
<FormLabel>URL to Track</FormLabel>
|
||||
<Input
|
||||
{...register('url')}
|
||||
placeholder="https://example.com or example.com"
|
||||
size="lg"
|
||||
/>
|
||||
{errors.url && (
|
||||
<Text color="red.500" fontSize="sm" mt={1}>
|
||||
{errors.url.message}
|
||||
</Text>
|
||||
)}
|
||||
<FormHelperText>
|
||||
Enter the URL you want to track. Protocol (http/https) is optional.
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
{/* Method Selection */}
|
||||
<FormControl>
|
||||
<FormLabel>HTTP Method</FormLabel>
|
||||
<Select {...register('method')}>
|
||||
<option value="GET">GET</option>
|
||||
<option value="HEAD">HEAD</option>
|
||||
<option value="POST">POST</option>
|
||||
</Select>
|
||||
<FormHelperText>
|
||||
HTTP method to use for the initial request
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
{/* Analysis Options */}
|
||||
<FormControl>
|
||||
<FormLabel>Analysis Options</FormLabel>
|
||||
<VStack align="start" spacing={3}>
|
||||
<HStack justify="space-between" w="full">
|
||||
<Text>SSL Certificate Analysis</Text>
|
||||
<Switch {...register('enableSSLAnalysis')} colorScheme="brand" />
|
||||
</HStack>
|
||||
<HStack justify="space-between" w="full">
|
||||
<Text>SEO Optimization Analysis</Text>
|
||||
<Switch {...register('enableSEOAnalysis')} colorScheme="brand" />
|
||||
</HStack>
|
||||
<HStack justify="space-between" w="full">
|
||||
<Text>Security Vulnerability Scan</Text>
|
||||
<Switch {...register('enableSecurityAnalysis')} colorScheme="brand" />
|
||||
</HStack>
|
||||
</VStack>
|
||||
<FormHelperText>
|
||||
Enable advanced analysis features (recommended)
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
{/* Advanced Options Toggle */}
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => setShowAdvanced(!showAdvanced)}
|
||||
size="sm"
|
||||
>
|
||||
{showAdvanced ? 'Hide' : 'Show'} Advanced Options
|
||||
</Button>
|
||||
|
||||
{/* Advanced Options */}
|
||||
<Collapse in={showAdvanced}>
|
||||
<VStack spacing={6} align="stretch">
|
||||
<Divider />
|
||||
|
||||
{/* Max Hops */}
|
||||
<FormControl>
|
||||
<FormLabel>Maximum Hops: {maxHops}</FormLabel>
|
||||
<Slider
|
||||
value={maxHops}
|
||||
onChange={(value) => setValue('maxHops', value)}
|
||||
min={1}
|
||||
max={20}
|
||||
step={1}
|
||||
colorScheme="brand"
|
||||
>
|
||||
<SliderTrack>
|
||||
<SliderFilledTrack />
|
||||
</SliderTrack>
|
||||
<SliderThumb />
|
||||
</Slider>
|
||||
<FormHelperText>
|
||||
Maximum number of redirects to follow (1-20)
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
{/* Timeout */}
|
||||
<FormControl>
|
||||
<FormLabel>Timeout (milliseconds)</FormLabel>
|
||||
<NumberInput
|
||||
value={timeout}
|
||||
onChange={(valueString) => setValue('timeout', parseInt(valueString) || 15000)}
|
||||
min={1000}
|
||||
max={30000}
|
||||
step={1000}
|
||||
>
|
||||
<NumberInputField />
|
||||
<NumberInputStepper>
|
||||
<NumberIncrementStepper />
|
||||
<NumberDecrementStepper />
|
||||
</NumberInputStepper>
|
||||
</NumberInput>
|
||||
<FormHelperText>
|
||||
Request timeout in milliseconds (1000-30000)
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
{/* User Agent */}
|
||||
<FormControl>
|
||||
<FormLabel>Custom User Agent</FormLabel>
|
||||
<Input
|
||||
{...register('userAgent')}
|
||||
placeholder="Mozilla/5.0 (compatible; RedirectTracker/2.0)"
|
||||
/>
|
||||
<FormHelperText>
|
||||
Custom User-Agent header (optional)
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
{/* Custom Headers */}
|
||||
<FormControl>
|
||||
<FormLabel>Custom Headers</FormLabel>
|
||||
<Textarea
|
||||
{...register('customHeaders')}
|
||||
placeholder="Accept: application/json X-Custom-Header: value"
|
||||
rows={4}
|
||||
resize="vertical"
|
||||
/>
|
||||
<FormHelperText>
|
||||
Custom headers, one per line in format: Header-Name: Header-Value
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</VStack>
|
||||
</Collapse>
|
||||
|
||||
{/* Rate Limiting Warning */}
|
||||
{!isAuthenticated && (
|
||||
<Alert status="info" borderRadius="md">
|
||||
<AlertIcon />
|
||||
<VStack align="start" spacing={1}>
|
||||
<Text fontWeight="medium">Anonymous Usage</Text>
|
||||
<Text fontSize="sm">
|
||||
Anonymous users are limited to 50 requests per hour.
|
||||
<Text as="span" color="brand.500" fontWeight="medium">
|
||||
{' '}Sign up for higher limits and saved results.
|
||||
</Text>
|
||||
</Text>
|
||||
</VStack>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{/* Submit Button */}
|
||||
<Button
|
||||
type="submit"
|
||||
colorScheme="brand"
|
||||
size="lg"
|
||||
isLoading={trackingMutation.isPending}
|
||||
loadingText="Tracking..."
|
||||
>
|
||||
Track URL
|
||||
</Button>
|
||||
</VStack>
|
||||
</form>
|
||||
<VStack spacing={6} textAlign="center">
|
||||
<Heading as="h3" size="lg">
|
||||
Ready to Analyze Your URLs?
|
||||
</Heading>
|
||||
<Text fontSize="lg" color="gray.600">
|
||||
Get comprehensive redirect analysis, SSL certificate checks, SEO insights, and security reports.
|
||||
</Text>
|
||||
<Button
|
||||
as={RouterLink}
|
||||
to="/track"
|
||||
colorScheme="brand"
|
||||
size="xl"
|
||||
leftIcon={<Icon as={FiSearch} />}
|
||||
px={8}
|
||||
py={6}
|
||||
fontSize="lg"
|
||||
>
|
||||
Analyze URL Redirects
|
||||
</Button>
|
||||
<Text fontSize="sm" color="gray.500">
|
||||
No registration required • Free analysis • Enhanced reporting
|
||||
</Text>
|
||||
</VStack>
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
@@ -702,12 +391,7 @@ export function HomePage() {
|
||||
</VStack>
|
||||
</Container>
|
||||
|
||||
{/* Results */}
|
||||
{trackingResult && (
|
||||
<Container maxW="7xl" py={16}>
|
||||
<TrackingResults result={trackingResult} />
|
||||
</Container>
|
||||
)}
|
||||
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user