This content originally appeared on DEV Community and was authored by Kishan Srivastava
import React, { useState, useCallback } from 'react';
import { useUnleashContext, useUnleashClient } from '@unleash/proxy-client-react';
import styled, { keyframes, css } from 'styled-components';
// --- Styled Components ---
const fadeIn = keyframes`
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
`;
const spin = keyframes`
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
`;
const Container = styled.div`
font-family: 'Inter', sans-serif;
background-color: #f8fafc; /* Tailwind gray-50 */
min-height: 100vh;
padding: 2rem;
display: flex;
justify-content: center;
align-items: flex-start;
`;
const MaxWidthWrapper = styled.div`
max-width: 56rem; /* max-w-xl (56rem) */
width: 100%;
display: flex;
flex-direction: column;
gap: 1.5rem; /* gap-6 */
`;
const Card = styled.div`
background-color: white;
border-radius: 0.75rem; /* rounded-xl */
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); /* shadow-xl */
padding: 1.5rem; /* p-6 */
border: 1px solid #e2e8f0; /* border-gray-200 */
`;
const Header = styled.div`
text-align: center;
margin-bottom: 1rem; /* mb-4 */
`;
const Title = styled.h1`
font-size: 1.875rem; /* text-3xl */
font-weight: 700; /* font-bold */
color: #1a202c; /* gray-900 */
margin-bottom: 0.5rem; /* mb-2 */
`;
const Subtitle = styled.p`
font-size: 1.125rem; /* text-lg */
color: #4a5568; /* gray-600 */
`;
const SectionTitle = styled.h2`
font-size: 1.5rem; /* text-2xl */
font-weight: 600; /* font-semibold */
color: #2d3748; /* gray-800 */
margin-bottom: 1rem; /* mb-4 */
`;
const InputGroup = styled.div`
display: flex;
flex-direction: column;
gap: 1rem; /* gap-4 */
`;
const Label = styled.label`
display: block;
font-size: 0.875rem; /* text-sm */
font-weight: 500; /* font-medium */
color: #2d3748; /* gray-700 */
margin-bottom: 0.25rem; /* mb-1 */
`;
const Input = styled.input`
width: 100%;
padding: 0.75rem 1rem; /* px-4 py-3 */
border: 1px solid #cbd5e0; /* border-gray-300 */
border-radius: 0.5rem; /* rounded-lg */
font-size: 1rem; /* text-base */
color: #2d3748; /* text-gray-800 */
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); /* shadow-sm */
&:focus {
outline: none;
border-color: #3b82f6; /* border-blue-500 */
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.3); /* ring-2 ring-blue-300 */
}
`;
const Button = styled.button`
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem; /* gap-2 */
padding: 0.75rem 1.5rem; /* px-6 py-3 */
background-color: #3b82f6; /* blue-500 */
color: white;
font-weight: 600; /* font-semibold */
border-radius: 0.5rem; /* rounded-lg */
border: none;
cursor: pointer;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); /* shadow-md */
transition: background-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
&:hover {
background-color: #2563eb; /* blue-600 */
box-shadow: 0 6px 8px -1px rgba(0, 0, 0, 0.15), 0 4px 6px -2px rgba(0, 0, 0, 0.08);
}
&:disabled {
background-color: #9ca3af; /* gray-400 */
cursor: not-allowed;
box-shadow: none;
}
`;
const Spinner = styled.div`
border: 4px solid rgba(255, 255, 255, 0.3);
border-top: 4px solid white;
border-radius: 50%;
width: 1.5rem; /* h-6 w-6 */
height: 1.5rem; /* h-6 w-6 */
animation: ${spin} 1s linear infinite;
`;
const ErrorMessage = styled.p`
color: #dc2626; /* red-600 */
font-size: 0.875rem; /* text-sm */
margin-top: 0.75rem; /* mt-3 */
text-align: center;
`;
const ResultItem = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 0.5rem; /* py-2 */
padding-bottom: 0.5rem; /* py-2 */
border-bottom: 1px solid #f3f4f6; /* border-gray-100 */
&:last-child {
border-bottom: none;
}
`;
const ResultLabel = styled.span`
color: #4a5568; /* gray-600 */
font-weight: 500; /* font-medium */
`;
const ResultValue = styled.span`
color: #1a202c; /* gray-900 */
font-weight: 700; /* font-bold */
`;
const StatusBadge = styled.span`
display: inline-flex;
padding: 0.25rem 0.75rem; /* px-3 py-1 */
border-radius: 9999px; /* rounded-full */
font-size: 0.875rem; /* text-sm */
font-weight: 600; /* font-semibold */
${(props) => {
switch (props.type) {
case 'enabled':
return css`
background-color: #dcfce7; /* green-100 */
color: #16a34a; /* green-700 */
`;
case 'disabled':
return css`
background-color: #fee2e2; /* red-100 */
color: #dc2626; /* red-700 */
`;
case 'error':
return css`
background-color: #fee2e2; /* red-100 */
color: #dc2626; /* red-700 */
`;
default:
return css`
background-color: #e2e8f0;
color: #4a5568;
`;
}
}}
`;
const EmptyState = styled.div`
text-align: center;
padding: 3rem; /* p-12 */
background-color: white;
border-radius: 0.75rem; /* rounded-xl */
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); /* shadow-xl */
border: 1px solid #e2e8f0; /* border-gray-200 */
animation: ${fadeIn} 0.5s ease-out;
`;
const EmptyStateIcon = styled.span`
font-size: 3rem; /* text-5xl */
margin-bottom: 1rem; /* mb-4 */
display: block;
`;
const EmptyStateTitle = styled.h3`
font-size: 1.5rem; /* text-2xl */
font-weight: 600; /* font-semibold */
color: #2d3748; /* gray-800 */
margin-bottom: 0.5rem; /* mb-2 */
`;
const EmptyStateText = styled.p`
font-size: 1rem; /* text-base */
color: #4a5568; /* gray-600 */
margin-bottom: 1rem; /* mb-4 */
`;
const EmptyStateNote = styled.p`
font-size: 0.875rem; /* text-sm */
color: #718096; /* gray-500 */
font-style: italic;
`;
// --- Main Component ---
const IPStrategySimulator = () => {
const [featureFlagName, setFeatureFlagName] = useState('geo-targeted-feature'); // Default flag name
const [simulatedIpAddress, setSimulatedIpAddress] = useState('192.168.1.10'); // Default example IP
const [testResult, setTestResult] = useState(null); // Stores the result: { ip, enabled, timestamp }
const [isLoading, setIsLoading] = useState(false);
const [errorMessage, setErrorMessage] = useState('');
const updateContext = useUnleashContext();
const unleashClient = useUnleashClient();
const testIpAddress = useCallback(async () => {
setErrorMessage('');
setTestResult(null);
setIsLoading(true);
if (!simulatedIpAddress) {
setErrorMessage('Please enter a simulated IP address.');
setIsLoading(false);
return;
}
if (!featureFlagName) {
setErrorMessage('Please enter the feature flag name.');
setIsLoading(false);
return;
}
try {
// Update Unleash context with the simulated IP address
// For IP strategy, 'remoteAddress' is the key context field.
await updateContext({
remoteAddress: simulatedIpAddress,
// It's good practice to also provide a userId for consistency,
// even if the strategy doesn't explicitly use it.
userId: 'simulator-user-' + simulatedIpAddress.replace(/\./g, '-'),
});
// Small delay to ensure context updates are processed
await new Promise((resolve) => setTimeout(resolve, 100));
// Get the actual flag status based on the updated context
const isEnabled = unleashClient.isEnabled(featureFlagName);
setTestResult({
ip: simulatedIpAddress,
enabled: isEnabled,
timestamp: new Date().toLocaleTimeString(),
});
} catch (error) {
console.error("Error during IP strategy test:", error);
setErrorMessage("An error occurred during the test. Check console for details.");
setTestResult({
ip: simulatedIpAddress,
enabled: false,
timestamp: new Date().toLocaleTimeString(),
error: true,
});
} finally {
setIsLoading(false);
}
}, [simulatedIpAddress, featureFlagName, updateContext, unleashClient]);
return (
<Container>
<MaxWidthWrapper>
{/* Header */}
<Card>
<Header>
<Title>📍 Unleash IP Strategy Simulator</Title>
<Subtitle>
Test feature flag behavior based on simulated IP addresses.
</Subtitle>
</Header>
</Card>
{/* Configuration Panel */}
<Card>
<SectionTitle>Test Configuration</SectionTitle>
<InputGroup>
<div>
<Label htmlFor="flagName">Feature Flag Name</Label>
<Input
id="flagName"
type="text"
value={featureFlagName}
onChange={(e) => setFeatureFlagName(e.target.value)}
placeholder="e.g., geo-targeted-feature"
/>
</div>
<div>
<Label htmlFor="simulatedIp">Simulated IP Address</Label>
<Input
id="simulatedIp"
type="text"
value={simulatedIpAddress}
onChange={(e) => setSimulatedIpAddress(e.target.value)}
placeholder="e.g., 192.168.1.10 or 203.0.113.50"
/>
</div>
<Button onClick={testIpAddress} disabled={isLoading}>
{isLoading ? (
<>
<Spinner />
Testing IP...
</>
) : (
<>🌐 Test IP Address</>
)}
</Button>
</InputGroup>
{errorMessage && (
<ErrorMessage>{errorMessage}</ErrorMessage>
)}
</Card>
{/* Test Result Display */}
{testResult && (
<Card style={{ animation: `${fadeIn} 0.5s ease-out` }}>
<SectionTitle>Test Result</SectionTitle>
<div className="flex flex-col gap-3">
<ResultItem>
<ResultLabel>Simulated IP:</ResultLabel>
<ResultValue>{testResult.ip}</ResultValue>
</ResultItem>
<ResultItem>
<ResultLabel>Feature Flag Status:</ResultLabel>
{testResult.error ? (
<StatusBadge type="error">❌ Error</StatusBadge>
) : testResult.enabled ? (
<StatusBadge type="enabled">✅ Enabled</StatusBadge>
) : (
<StatusBadge type="disabled">❌ Disabled</StatusBadge>
)}
</ResultItem>
<ResultItem>
<ResultLabel>Test Time:</ResultLabel>
<ResultValue>{testResult.timestamp}</ResultValue>
</ResultItem>
</div>
</Card>
)}
{/* Initial Empty State */}
{!testResult && !isLoading && !errorMessage && (
<EmptyState>
<EmptyStateIcon>🧪</EmptyStateIcon>
<EmptyStateTitle>Ready to Test IP-Based Features</EmptyStateTitle>
<EmptyStateText>
Enter a feature flag name and a simulated IP address, then click "Test IP Address" to see the result.
</EmptyStateText>
<EmptyStateNote>
Make sure your feature flag "{featureFlagName}" has an "IP Address" strategy configured in Unleash.
</EmptyStateNote>
</EmptyState>
)}
</MaxWidthWrapper>
</Container>
);
};
export default IPStrategySimulator;
This content originally appeared on DEV Community and was authored by Kishan Srivastava
Print
Share
Comment
Cite
Upload
Translate
Updates
There are no updates yet.
Click the Upload button above to add an update.

APA
MLA
Kishan Srivastava | Sciencx (2025-06-30T08:36:20+00:00) iptester. Retrieved from https://www.scien.cx/2025/06/30/iptester/
" » iptester." Kishan Srivastava | Sciencx - Monday June 30, 2025, https://www.scien.cx/2025/06/30/iptester/
HARVARDKishan Srivastava | Sciencx Monday June 30, 2025 » iptester., viewed ,<https://www.scien.cx/2025/06/30/iptester/>
VANCOUVERKishan Srivastava | Sciencx - » iptester. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2025/06/30/iptester/
CHICAGO" » iptester." Kishan Srivastava | Sciencx - Accessed . https://www.scien.cx/2025/06/30/iptester/
IEEE" » iptester." Kishan Srivastava | Sciencx [Online]. Available: https://www.scien.cx/2025/06/30/iptester/. [Accessed: ]
rf:citation » iptester | Kishan Srivastava | Sciencx | https://www.scien.cx/2025/06/30/iptester/ |
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.