<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kishan Srivastava</title>
    <description>The latest articles on DEV Community by Kishan Srivastava (@superman01).</description>
    <link>https://dev.to/superman01</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3308249%2F2feb1e11-75ad-4169-8340-2f781a3420f1.jpg</url>
      <title>DEV Community: Kishan Srivastava</title>
      <link>https://dev.to/superman01</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/superman01"/>
    <language>en</language>
    <item>
      <title>microservices</title>
      <dc:creator>Kishan Srivastava</dc:creator>
      <pubDate>Thu, 15 Oct 2026 09:50:36 +0000</pubDate>
      <link>https://dev.to/superman01/microservices-4dfl</link>
      <guid>https://dev.to/superman01/microservices-4dfl</guid>
      <description>&lt;p&gt;question&lt;br&gt;
Model Microservice&lt;/p&gt;

&lt;p&gt;This repo is intended as a starter project for anyone intending to write a new microservice. It demonstrates the following key features:&lt;br&gt;
A basic gradle spring-cloud project&lt;br&gt;
Integration with the service directory&lt;br&gt;
Implementing a REST service with tests&lt;br&gt;
Calling microservices in DWS fabric using webclient and feign.&lt;br&gt;
After cloning you should, at the very least, change the following things before you import it into your IDE.&lt;br&gt;
Change the package name from modelmicroservice to something more appropriate.&lt;br&gt;
Edit settings.gradle and change the rootProject.name property name to your service name.&lt;br&gt;
Edit src/main/resources/application.yaml and change spring.application.name to the name of your project. Review the other properties in this file.&lt;br&gt;
Edit gradle.properties and review all the properties commented with TODO.&lt;br&gt;
Generate a keystore for local testing. More info: Guide&lt;br&gt;
This repo will never be finished. As we evolve our capability and update recommended versions this repo will change and evolve accordingly.&lt;br&gt;
Auto-generated documentation&lt;/p&gt;

&lt;p&gt;Swagger&lt;br&gt;
Code Quality: SonarQube&lt;/p&gt;

&lt;p&gt;Follow these steps to run a SonarQube instance locally to measure your code quality.&lt;br&gt;
docker-compose -f src/main/docker/sonar.yml up&lt;br&gt;
./gradlew sonar&lt;br&gt;
Visit SonarQube Dashboard&lt;br&gt;
Spring Boot 3&lt;/p&gt;

&lt;p&gt;The model-microservice is now (Aug 2023) built against spring boot 3.&lt;/p&gt;

&lt;p&gt;i need to understand properly what this repo is for &lt;br&gt;
And what all technical knowledge should i have to use this and understand everything properly&lt;br&gt;
what skills do i need to learn&lt;/p&gt;

&lt;p&gt;gemini response &lt;br&gt;
Of course. Based on the documentation you provided, here is an explanation of the "Model Microservice" repository and the skills needed to use it effectively.&lt;br&gt;
What is this Repository For?&lt;br&gt;
This repository serves as a starter project or template for developers who need to create a new microservice. Its primary purpose is to provide a standardized, pre-configured foundation that includes best practices and essential integrations, saving developers time and effort during the initial setup phase.&lt;br&gt;
Instead of starting from an empty project, you can clone this repository and have a functional service skeleton that already demonstrates key capabilities:&lt;/p&gt;

&lt;p&gt;Foundation: It's a project built with Gradle and the Spring Cloud framework, which are standard for building distributed systems.&lt;br&gt;
Service Discovery: It includes integration with a "service directory," allowing your new service to register itself and discover other services within the network.&lt;br&gt;
API Implementation: It provides a template for creating a RESTful API, complete with example tests, which is the standard way microservices communicate.&lt;br&gt;
Inter-Service Communication: It shows how to call other microservices using two popular methods: WebClient (a modern, reactive web client) and Feign (a declarative web service client).&lt;br&gt;
Modern Framework: The project is updated to use Spring Boot 3, ensuring it leverages the latest features and performance improvements.&lt;/p&gt;

&lt;p&gt;In short, this repository is a blueprint that helps you bootstrap a new microservice quickly while adhering to established architectural patterns and tooling.&lt;br&gt;
Technical Knowledge and Skills Required&lt;br&gt;
To fully understand and effectively use this starter project, you should have a solid grasp of the following technologies and concepts. They are listed in order of importance, from foundational to specific.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Foundational Skills
These are the core competencies you'll need before you can work with the specifics of the repository.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Java Programming:&lt;/p&gt;

&lt;p&gt;Description: The project is written in Java. You need strong proficiency in the language, including object-oriented principles, data structures, and core libraries. Familiarity with features from Java 17 or later is beneficial, as Spring Boot 3 requires it.&lt;br&gt;
Why it's needed: To write the business logic, controllers, services, and repository layers of your microservice.&lt;/p&gt;

&lt;p&gt;RESTful APIs:&lt;/p&gt;

&lt;p&gt;Description: Understanding the principles of Representational State Transfer (REST). This includes concepts like resources, URIs, HTTP methods (GET, POST, PUT, DELETE), status codes, and data formats like JSON.&lt;br&gt;
Why it's needed: Microservices primarily communicate via REST APIs. You will be building and consuming them.&lt;/p&gt;

&lt;p&gt;Microservice Architecture:&lt;/p&gt;

&lt;p&gt;Description: A conceptual understanding of why and how applications are broken down into smaller, independent services. This includes concepts like service discovery, configuration management, fault tolerance, and API gateways.&lt;br&gt;
Why it's needed: This repository is a tool for implementing this architectural style. Knowing the "big picture" helps you understand the purpose of each feature (like service directory integration).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Core Frameworks &amp;amp; Tools
These are the specific technologies used directly in the repository.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Spring Framework / Spring Boot 3:&lt;/p&gt;

&lt;p&gt;Description: This is the most critical framework. You should understand the core concepts of Spring, such as Dependency Injection (@Autowired), Inversion of Control (IoC), and aspect-oriented programming. For Spring Boot specifically, know how it simplifies configuration with auto-configuration, starters, and the application.yaml file.&lt;br&gt;
Why it's needed: The entire application is built on Spring Boot. You'll use its annotations (@RestController, @Service, etc.) and configuration mechanisms daily.&lt;/p&gt;

&lt;p&gt;Gradle:&lt;/p&gt;

&lt;p&gt;Description: A build automation tool used to manage project dependencies, run tests, and build the application artifact. You should understand the structure of build.gradle and settings.gradle files and how to execute common tasks like gradlew build.&lt;br&gt;
Why it's needed: You need to interact with Gradle to build the project, manage its libraries, and run tasks like the SonarQube analysis.&lt;/p&gt;

&lt;p&gt;Spring Cloud:&lt;/p&gt;

&lt;p&gt;Description: A sub-project of Spring that provides tools for building distributed systems. The repository mentions "service directory" integration, which is a core feature of Spring Cloud (often implemented with Netflix Eureka or Spring Cloud Consul).&lt;br&gt;
Why it's needed: To make your service work correctly within a larger ecosystem of microservices.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Specific Skills &amp;amp; Tooling
These are skills for specific tasks mentioned in the repository's documentation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Web Clients (WebClient and Feign):&lt;/p&gt;

&lt;p&gt;Description: WebClient is a non-blocking, reactive client for making HTTP requests, part of the Spring WebFlux project. Feign is a declarative client that lets you define an API client by creating a simple Java interface.&lt;br&gt;
Why it's needed: Your service will likely need to communicate with other services. The repository provides examples of both, and you should understand the trade-offs between them (e.g., reactive vs. blocking, verbosity vs. simplicity).&lt;/p&gt;

&lt;p&gt;Docker and Docker Compose:&lt;/p&gt;

&lt;p&gt;Description: Docker is a platform for creating, deploying, and running applications in containers. Docker Compose is a tool for defining and running multi-container Docker applications.&lt;br&gt;
Why it's needed: The repository uses Docker Compose to run a local instance of SonarQube for code quality analysis. Basic knowledge of docker-compose up is required to follow the instructions.&lt;/p&gt;

&lt;p&gt;API Documentation (Swagger/OpenAPI):&lt;/p&gt;

&lt;p&gt;Description: Swagger (now known as the OpenAPI Specification) is a standard for documenting REST APIs. Tools like Springdoc-openapi can automatically generate this documentation from your code.&lt;br&gt;
Why it's needed: To provide clear, interactive documentation for the API your service exposes, making it easy for other developers to understand and use.&lt;/p&gt;

&lt;p&gt;Code Quality (SonarQube):&lt;/p&gt;

&lt;p&gt;Description: A platform for continuous inspection of code quality to perform automatic reviews with static analysis of code to detect bugs, code smells, and security vulnerabilities.&lt;br&gt;
Why it's needed: To ensure your code meets quality standards. You'll need to know how to run the analysis and interpret the results on the SonarQube dashboard.&lt;/p&gt;

&lt;p&gt;question 2 &lt;br&gt;
explain with a simple example of a microservice that can be build with this starter template&lt;br&gt;
how would it be different when this template was not present&lt;/p&gt;

&lt;p&gt;gemini response &lt;br&gt;
Of course. Let's walk through a simple, practical example of a microservice you could build with this template and then compare the development process with and without it.&lt;br&gt;
Simple Example: A "Currency Exchange Rate" Microservice&lt;br&gt;
Let's imagine you need to build a microservice called exchange-rate-service. Its sole purpose is to provide the current exchange rate between two currencies (e.g., USD to EUR).&lt;br&gt;
What the Microservice Does:&lt;/p&gt;

&lt;p&gt;Exposes a REST API Endpoint: It will have an endpoint like GET /api/v1/rates. You can query it with parameters, for example: GET /api/v1/rates?from=USD&amp;amp;to=EUR.&lt;br&gt;
Returns a JSON Response: A successful request would return a simple JSON object:&lt;br&gt;
json{&lt;br&gt;
  "fromCurrency": "USD",&lt;br&gt;
  "toCurrency": "EUR",&lt;br&gt;
  "rate": 0.95,&lt;br&gt;
  "timestamp": "2026-10-15T10:00:00Z"&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;Gets Data from Another Service: This exchange-rate-service doesn't calculate rates itself. It fetches them from a central, authoritative source, let's call it the market-data-provider service.&lt;br&gt;
Registers Itself: It announces its presence on the network so other services (e.g., a trade-booking-service) can find and use it.&lt;/p&gt;

&lt;p&gt;How You Would Build This Using the "Model Microservice" Template&lt;br&gt;
Following the instructions in the repository, the process would be streamlined:&lt;/p&gt;

&lt;p&gt;Clone and Rename:&lt;/p&gt;

&lt;p&gt;You clone the model-microservice repository.&lt;br&gt;
You perform the initial setup:&lt;/p&gt;

&lt;p&gt;Change the package name from modelmicroservice to com.db.trading.exchangerate.&lt;br&gt;
Edit settings.gradle to set rootProject.name = 'exchange-rate-service'.&lt;br&gt;
Edit application.yaml to set spring.application.name: exchange-rate-service.&lt;/p&gt;

&lt;p&gt;Result: You now have a working, buildable Spring Boot project correctly named and configured.&lt;/p&gt;

&lt;p&gt;Define the API Endpoint:&lt;/p&gt;

&lt;p&gt;You would create a new Java class, ExchangeRateController.java.&lt;br&gt;
Using Spring's annotations (which are already in the project's dependencies), you define the endpoint. The template already has an example controller you can adapt.&lt;/p&gt;

&lt;p&gt;java@RestController&lt;br&gt;
@RequestMapping("/api/v1/rates")&lt;br&gt;
public class ExchangeRateController {&lt;br&gt;
    // ... inject your service here&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@GetMapping
public Rate getRate(@RequestParam String from, @RequestParam String to) {
    return exchangeRateService.getExchangeRate(from, to);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;Call the market-data-provider Service:&lt;/p&gt;

&lt;p&gt;The template already includes WebClient and Feign as dependencies and provides examples of how to use them.&lt;br&gt;
You could create a Feign client interface to declaratively define how to call the other service. The setup is already mostly done for you.&lt;/p&gt;

&lt;p&gt;java@FeignClient(name = "market-data-provider")&lt;br&gt;
public interface MarketDataProviderClient {&lt;br&gt;
    @GetMapping("/data/forex")&lt;br&gt;
    BigDecimal getRate(@RequestParam("pair") String currencyPair);&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;Your service logic would then simply call this Java interface, and Spring Cloud would handle the underlying HTTP request, service discovery, and response parsing.&lt;/p&gt;

&lt;p&gt;Testing and Quality:&lt;/p&gt;

&lt;p&gt;The template includes a testing framework (JUnit 5) and example tests for REST controllers. You can follow this pattern to write your own tests quickly.&lt;br&gt;
To check your code quality, you just run the provided commands: docker-compose -f src/main/docker/sonar.yml up and ./gradlew sonar. The configuration is already done.&lt;/p&gt;

&lt;p&gt;Run the Service:&lt;/p&gt;

&lt;p&gt;You run the main application class. Because the template includes the Spring Cloud service discovery client, your service will automatically register itself with the service directory upon startup.&lt;/p&gt;

&lt;p&gt;Key takeaway: The template provides the scaffolding. You don't spend time on setup, configuration, or figuring out which libraries to use. You focus almost immediately on writing the business logic for your specific service.&lt;/p&gt;

&lt;p&gt;How It Would Be Different Without the Template (Starting from Scratch)&lt;br&gt;
If this starter template did not exist, you would have to perform all of the following steps manually.&lt;/p&gt;

&lt;p&gt;Project Initialization:&lt;/p&gt;

&lt;p&gt;Go to a tool like start.spring.io or use your IDE's project wizard.&lt;br&gt;
Manually select the project type (Gradle), language (Java), and Spring Boot version.&lt;br&gt;
Dilemma: Which version of Spring Boot is standard? Is it compatible with the company's internal libraries? The template pre-answers this.&lt;br&gt;
Manual Dependency Selection: You would need to manually add every single dependency:&lt;/p&gt;

&lt;p&gt;Spring Web (for REST controllers)&lt;br&gt;
Spring Boot Actuator (for health checks)&lt;br&gt;
Spring Cloud Starter Netflix Eureka (or a similar client for service discovery)&lt;br&gt;
Spring Cloud Starter OpenFeign (for the Feign client)&lt;br&gt;
Spring Boot Starter Test (for testing)&lt;br&gt;
Lombok (optional, for less boilerplate code)&lt;br&gt;
Springdoc OpenAPI (for Swagger documentation)&lt;/p&gt;

&lt;p&gt;Problem: You need to know exactly which dependencies are required and ensure their versions are compatible with each other. This is a common source of errors. The template provides a tested, known-good set of dependencies.&lt;/p&gt;

&lt;p&gt;Configuration:&lt;/p&gt;

&lt;p&gt;Create the application.yaml file from scratch.&lt;br&gt;
Manually add configuration for the server port, application name, and the address of the service directory.&lt;br&gt;
Manually configure logging patterns and levels.&lt;br&gt;
The template provides a well-structured application.yaml with comments and placeholders for standard properties, which you would have to look up or guess.&lt;/p&gt;

&lt;p&gt;Implementing Inter-Service Communication:&lt;/p&gt;

&lt;p&gt;You would need to manually configure the Feign client or WebClient. This involves adding annotations like @EnableFeignClients to your main application class and potentially creating configuration beans for things like timeouts, retries, and error handling. The template would have this already set up.&lt;/p&gt;

&lt;p&gt;Setting up Code Quality and Documentation:&lt;/p&gt;

&lt;p&gt;SonarQube: You would need to find the correct SonarQube Docker image, write your own docker-compose.yml file, and configure the Gradle SonarQube plugin from scratch in your build.gradle file, connecting it to the Docker container.&lt;br&gt;
Swagger: You would have to add the dependency and potentially write Java configuration beans to customize the generated API documentation (e.g., adding security definitions or metadata).&lt;/p&gt;

&lt;p&gt;Build Logic:&lt;/p&gt;

&lt;p&gt;You would write a build.gradle file from scratch. The template's gradle.properties and build.gradle files contain standardized version management, plugin configurations, and other best practices you would otherwise have to implement yourself.&lt;/p&gt;

&lt;p&gt;In summary, without the template, a developer would spend a significant amount of time on boilerplate setup, configuration, and dependency management. This process is error-prone and leads to inconsistencies between microservices.&lt;br&gt;
The template's value is that it encapsulates all of these initial decisions and configurations into a reusable, best-practice starting point, dramatically accelerating development and promoting standardization.&lt;/p&gt;

&lt;p&gt;question 3&lt;br&gt;
what things should i learn in proper sequence so that i completely understand each step of creation of a microservice with this template, so that i may understand the things used and also maybe later able to create a new microservice like this manually by my own&lt;/p&gt;

&lt;p&gt;gemini response &lt;br&gt;
Excellent question. Building a solid understanding requires a structured approach. Learning these concepts in the right order will ensure you grasp not just how to use the template, but why it is designed the way it is. This will empower you to use it effectively and eventually build a similar service from scratch.&lt;br&gt;
Here is a recommended, sequential learning path, broken down into logical phases.&lt;/p&gt;

&lt;p&gt;Phase 1: The Bedrock - Core Programming and Concepts&lt;br&gt;
Start here. Without these fundamentals, the frameworks and tools in the later phases will be difficult to comprehend.&lt;/p&gt;

&lt;p&gt;Java (Intermediate to Advanced):&lt;/p&gt;

&lt;p&gt;What to Learn: Don't just learn the syntax. Focus on Object-Oriented Programming (OOP) principles (encapsulation, inheritance, polymorphism), interfaces, abstract classes, collections (Lists, Sets, Maps), and exception handling. Since Spring Boot 3 uses Java 17+, familiarize yourself with modern Java features like Records, var for local variables, and the Stream API for data manipulation.&lt;br&gt;
Why: The entire application is written in Java. A deep understanding is non-negotiable for writing clean, efficient business logic.&lt;/p&gt;

&lt;p&gt;HTTP and RESTful APIs:&lt;/p&gt;

&lt;p&gt;What to Learn: Understand the client-server model of the web. Learn the core HTTP methods (GET, POST, PUT, DELETE), what they semantically represent, and the importance of HTTP status codes (e.g., 200 OK, 201 Created, 400 Bad Request, 404 Not Found, 500 Internal Server Error). Grasp the core principles of REST: resources, statelessness, and using JSON as a data format.&lt;br&gt;
Why: This is the universal language of microservices. You will be designing, building, and consuming REST APIs.&lt;/p&gt;

&lt;p&gt;Phase 2: The Engine - The Spring Framework&lt;br&gt;
This is the most critical framework in the template. Master it, and you're halfway there.&lt;/p&gt;

&lt;p&gt;Spring Core Concepts:&lt;/p&gt;

&lt;p&gt;What to Learn: Focus on the two pillars of Spring: Inversion of Control (IoC) and Dependency Injection (DI). Understand what the Spring "Application Context" or "IoC Container" is. Learn the most common annotations: @Component, @Service, @Repository, and @Autowired.&lt;br&gt;
Why: Spring manages the lifecycle of your objects (beans). Understanding DI is fundamental to how all the pieces of a Spring application are wired together.&lt;/p&gt;

&lt;p&gt;Spring Boot:&lt;/p&gt;

&lt;p&gt;What to Learn: Understand how Spring Boot simplifies Spring development. Focus on auto-configuration (how it automatically configures beans based on the JARs on your classpath), starter dependencies (e.g., spring-boot-starter-web), and externalized configuration using application.yaml or application.properties. Learn to build a simple REST controller using @RestController and @GetMapping/@PostMapping.&lt;br&gt;
Why: The template is a Spring Boot project. This knowledge will allow you to understand how the service starts, how its configuration is managed, and how to add new API endpoints.&lt;/p&gt;

&lt;p&gt;Gradle (Build Tool):&lt;/p&gt;

&lt;p&gt;What to Learn: You don't need to be an expert, but you must understand the basics. Learn the purpose of build.gradle (declares dependencies and tasks) and settings.gradle (defines project structure). Know how to execute common tasks from the command line: ./gradlew build (compiles and packages the code), ./gradlew test (runs tests), and ./gradlew bootRun (runs the application).&lt;br&gt;
Why: Gradle is the tool used to build, test, and manage all the libraries your project needs. You will interact with it constantly.&lt;/p&gt;

&lt;p&gt;Phase 3: The Ecosystem - Connecting Microservices&lt;br&gt;
Once you can build a standalone service, this phase teaches you how to make it part of a larger distributed system.&lt;/p&gt;

&lt;p&gt;Microservice Architecture Concepts:&lt;/p&gt;

&lt;p&gt;What to Learn: Before diving into the tools, understand the "why." Learn about key patterns like Service Discovery (how services find each other), Centralized Configuration (managing configuration for many services), and Fault Tolerance (e.g., circuit breakers).&lt;br&gt;
Why: This provides the context for why tools like Spring Cloud are necessary.&lt;/p&gt;

&lt;p&gt;Spring Cloud &amp;amp; Service Discovery:&lt;/p&gt;

&lt;p&gt;What to Learn: Focus on the role of a "Service Directory" or "Discovery Server" (like Eureka, which is a common choice). Understand that when your service starts, it registers its name and address with this central server. When it needs to call another service, it asks the server for that service's address. Learn the annotations that enable this, like @EnableDiscoveryClient.&lt;br&gt;
Why: In a microservice environment, you don't hardcode IP addresses. Service discovery allows services to find each other dynamically. The template is pre-configured for this.&lt;/p&gt;

&lt;p&gt;Communicating Between Services (Feign &amp;amp; WebClient):&lt;/p&gt;

&lt;p&gt;What to Learn:&lt;/p&gt;

&lt;p&gt;Feign: Understand it as a "declarative" REST client. You create a Java interface, add annotations, and Spring Cloud automatically implements it for you. It's clean and easy to read.&lt;br&gt;
WebClient: Learn this as the modern, more powerful way to make HTTP requests in Spring. It is non-blocking and reactive, which can be more efficient, but has a slightly steeper learning curve.&lt;/p&gt;

&lt;p&gt;Why: Your microservice will inevitably need to call other microservices. The template provides examples of both, and you need to understand how to use them to fetch data.&lt;/p&gt;

&lt;p&gt;Phase 4: The Polish - Professional Tooling &amp;amp; Practices&lt;br&gt;
These tools ensure your microservice is maintainable, well-documented, and high-quality.&lt;/p&gt;

&lt;p&gt;Testing in Spring:&lt;/p&gt;

&lt;p&gt;What to Learn: Learn how to write unit tests for your services (@ExtendWith(MockitoExtension.class)) and integration tests for your REST controllers (@SpringBootTest with @AutoConfigureMockMvc or WebTestClient). Understand the difference between mocking dependencies and running a test with a full application context.&lt;br&gt;
Why: The template includes a testing setup. Writing automated tests is crucial for ensuring your service works correctly and doesn't break when you make changes.&lt;/p&gt;

&lt;p&gt;API Documentation (Swagger/OpenAPI):&lt;/p&gt;

&lt;p&gt;What to Learn: Understand that tools like Springdoc-openapi scan your @RestController code to automatically generate interactive API documentation (a Swagger UI page). Learn how to add annotations (@Operation, @ApiResponse) to make this documentation more descriptive.&lt;br&gt;
Why: Good documentation makes your service easy to use for other developers and teams. The template automates the setup.&lt;/p&gt;

&lt;p&gt;Docker &amp;amp; Docker Compose:&lt;/p&gt;

&lt;p&gt;What to Learn: Understand what a Docker container is (a lightweight, portable package of an application and its dependencies). Learn the basics of Docker Compose for running multi-container applications from a single docker-compose.yml file. You only need to know how to run docker-compose up to start.&lt;br&gt;
Why: The template uses this to run external dependencies locally, like the SonarQube database and server. This avoids complex local installations.&lt;/p&gt;

&lt;p&gt;Code Quality (SonarQube):&lt;/p&gt;

&lt;p&gt;What to Learn: Understand that SonarQube is a static analysis tool that scans your code for bugs, vulnerabilities, and "code smells" (bad practices). Learn how to run the Gradle task (./gradlew sonar) and how to interpret the dashboard to identify areas for improvement in your code.&lt;br&gt;
Why: This helps you maintain high code quality and write more secure, robust applications.&lt;/p&gt;

&lt;p&gt;By following this path, you will progressively build the knowledge required to not only use the template, but to understand its value and replicate its functionality on your own.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>java</category>
      <category>microservices</category>
      <category>spring</category>
    </item>
    <item>
      <title>images</title>
      <dc:creator>Kishan Srivastava</dc:creator>
      <pubDate>Wed, 07 Oct 2026 21:32:09 +0000</pubDate>
      <link>https://dev.to/superman01/images-5ai7</link>
      <guid>https://dev.to/superman01/images-5ai7</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh8bsyx5p9ieijaw9c2ew.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh8bsyx5p9ieijaw9c2ew.png" alt=" " width="799" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwtb01oyc4ut7a8930vfi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwtb01oyc4ut7a8930vfi.png" alt=" " width="800" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>updates</title>
      <dc:creator>Kishan Srivastava</dc:creator>
      <pubDate>Tue, 08 Sep 2026 19:54:37 +0000</pubDate>
      <link>https://dev.to/superman01/updates-1cgh</link>
      <guid>https://dev.to/superman01/updates-1cgh</guid>
      <description>&lt;p&gt;Explored SharePoint’s page/section customization and web parts, and recreated the Key Contacts section to understand the layout and styling possibilities. Got a better idea of what we can achieve natively and the current limitations.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>SharePoint</title>
      <dc:creator>Kishan Srivastava</dc:creator>
      <pubDate>Tue, 08 Sep 2026 08:55:59 +0000</pubDate>
      <link>https://dev.to/superman01/sharepoint-2nhd</link>
      <guid>https://dev.to/superman01/sharepoint-2nhd</guid>
      <description>&lt;p&gt;Use this as the follow-up prompt in SharePoint Copilot:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Refine the current Key Contacts section with these specific changes while keeping the existing content, layout, and overall styling:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Global Footprint: Change the US, EMEA, APAC and India options from full-width rectangular tabs into small pill/capsule-shaped buttons. Keep all four buttons grouped together and aligned to the left of the section rather than stretching across the entire width. Keep India as the active/selected option.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Section background: Change the entire Key Contacts section background to solid black. Remove the current teal/green background. Keep the existing cyan/green accent elements.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Contact cards: Keep the four cards in the same horizontal row. For each card, move the initials (PA, AR, MD, UG) to the left of the person's name and designation. Display the initials inside a circular avatar with a green/cyan background matching the accent color currently used in the section.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keep the names, designations, text, card borders, spacing and bottom-right accent elements unchanged. Maintain a clean, premium corporate/technology appearance.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Change footprint to left-aligned pills; make section black; put initials in green circles left of names.&lt;/p&gt;

</description>
      <category>design</category>
      <category>frontend</category>
      <category>webdev</category>
      <category>website</category>
    </item>
    <item>
      <title>Automate</title>
      <dc:creator>Kishan Srivastava</dc:creator>
      <pubDate>Tue, 25 Aug 2026 10:08:39 +0000</pubDate>
      <link>https://dev.to/superman01/automate-2j15</link>
      <guid>https://dev.to/superman01/automate-2j15</guid>
      <description>&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;playwright.sync_api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sync_playwright&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;TimeoutError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;PlaywrightTimeoutError&lt;/span&gt;

&lt;span class="n"&gt;SITE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://YOUR-SITE-URL-HERE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;EMAIL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kishan-a.srivastava@db.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;PASSWORD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;abc123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wait_for_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;120_000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait_for_load_state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;domcontentloaded&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;PlaywrightTimeoutError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Page is taking longer than expected to load...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Continuing anyway.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;sync_playwright&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;headless&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;slow_mo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_default_timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_default_navigation_timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;120_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Opening website...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;SITE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;wait_until&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;domcontentloaded&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;120_000&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Website opened.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Looking for &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Continue with DWS UAT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;continue_button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;button[type=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;submit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;has_text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Continue with DWS UAT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;continue_button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;visible&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;continue_button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Clicked &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Continue with DWS UAT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;wait_for_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Waiting for email field...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;email_field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;input#i0116[type=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;email_field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;visible&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;email_field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EMAIL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Email entered.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;next_button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;input#idSIButton9[type=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;submit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;next_button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;visible&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;next_button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Next clicked.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;wait_for_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Waiting for password field...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;password_field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;input#i0118[type=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;password_field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;visible&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;60_000&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;PlaywrightTimeoutError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Password field has not appeared yet.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A company verification/MFA step may be running.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Complete the verification manually if required.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Waiting for the password field...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;password_field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;visible&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;300_000&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Password field found.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;password_field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PASSWORD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Password entered.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;submit_button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;input#idSIButton9[type=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;submit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;submit_button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;visible&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;submit_button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Password submitted.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Waiting for final page to load...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;wait_for_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;120_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;==========================================&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LOGIN FLOW COMPLETED&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;==========================================&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Current URL:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Browser will remain open for 30 seconds...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>iptester-redesigned</title>
      <dc:creator>Kishan Srivastava</dc:creator>
      <pubDate>Mon, 30 Jun 2025 09:50:56 +0000</pubDate>
      <link>https://dev.to/superman01/iptester-redesigned-2hle</link>
      <guid>https://dev.to/superman01/iptester-redesigned-2hle</guid>
      <description>&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState, useCallback } from 'react';
import { useUnleashContext, useUnleashClient } from '@unleash/proxy-client-react';
import styled, { keyframes, css } from 'styled-components';

// --- Keyframe Animations ---

const fadeIn = keyframes`
  from { 
    opacity: 0; 
    transform: translateY(20px) scale(0.95); 
  }
  to { 
    opacity: 1; 
    transform: translateY(0) scale(1); 
  }
`;

const slideIn = keyframes`
  from { 
    opacity: 0; 
    transform: translateX(-20px); 
  }
  to { 
    opacity: 1; 
    transform: translateX(0); 
  }
`;

const spin = keyframes`
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
`;

const pulse = keyframes`
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.05); }
`;

const gradientShift = keyframes`
  0% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
`;

const float = keyframes`
  0%, 100% { transform: translateY(0px); }
  50% { transform: translateY(-10px); }
`;

// --- Modern Styled Components ---

const Container = styled.div`
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  min-height: 100vh;
  padding: 2rem 1rem;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  position: relative;

  &amp;amp;::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: 
      radial-gradient(circle at 20% 50%, rgba(120, 119, 198, 0.3) 0%, transparent 50%),
      radial-gradient(circle at 80% 20%, rgba(255, 118, 117, 0.3) 0%, transparent 50%),
      radial-gradient(circle at 40% 80%, rgba(255, 177, 153, 0.3) 0%, transparent 50%);
    pointer-events: none;
  }
`;

const MaxWidthWrapper = styled.div`
  max-width: 1400px;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 2rem;
  position: relative;
  z-index: 1;
`;

const Card = styled.div`
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(20px);
  border-radius: 24px;
  box-shadow: 
    0 32px 64px -12px rgba(0, 0, 0, 0.25),
    0 0 0 1px rgba(255, 255, 255, 0.05);
  padding: 2rem;
  border: 1px solid rgba(255, 255, 255, 0.2);
  animation: ${fadeIn} 0.6s ease-out;
  transition: all 0.3s ease;

  &amp;amp;:hover {
    transform: translateY(-2px);
    box-shadow: 
      0 40px 80px -12px rgba(0, 0, 0, 0.3),
      0 0 0 1px rgba(255, 255, 255, 0.1);
  }
`;

const Header = styled.div`
  text-align: center;
  margin-bottom: 1.5rem;
  animation: ${slideIn} 0.8s ease-out;
`;

const Title = styled.h1`
  font-size: 3rem;
  font-weight: 800;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%);
  background-size: 200% 200%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  margin-bottom: 0.75rem;
  animation: ${gradientShift} 4s ease infinite;
  line-height: 1.2;
`;

const Subtitle = styled.p`
  font-size: 1.25rem;
  color: #64748b;
  font-weight: 500;
  max-width: 600px;
  margin: 0 auto;
  line-height: 1.6;
`;

const SectionTitle = styled.h2`
  font-size: 1.75rem;
  font-weight: 700;
  background: linear-gradient(135deg, #1e293b, #475569);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  margin-bottom: 1.5rem;
  display: flex;
  align-items: center;
  gap: 0.75rem;

  &amp;amp;::before {
    content: '';
    width: 4px;
    height: 24px;
    background: linear-gradient(135deg, #667eea, #764ba2);
    border-radius: 2px;
  }
`;

const ConfigGrid = styled.div`
  display: grid;
  gap: 2rem;
  margin-bottom: 2rem;
`;

const InputGroup = styled.div`
  display: grid;
  grid-template-columns: 1fr 1fr auto;
  gap: 1rem;
  align-items: end;

  @media (max-width: 768px) {
    grid-template-columns: 1fr;
  }
`;

const DiscreteInputGroup = styled.div`
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 1rem;
  align-items: end;

  @media (max-width: 768px) {
    grid-template-columns: 1fr;
  }
`;

const InputField = styled.div`
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
`;

const Label = styled.label`
  font-size: 0.875rem;
  font-weight: 600;
  color: #374151;
  letter-spacing: 0.025em;
`;

const Input = styled.input`
  width: 100%;
  padding: 1rem 1.25rem;
  border: 2px solid #e2e8f0;
  border-radius: 16px;
  font-size: 1rem;
  color: #1f2937;
  background: rgba(255, 255, 255, 0.8);
  backdrop-filter: blur(10px);
  transition: all 0.3s ease;
  font-weight: 500;

  &amp;amp;::placeholder {
    color: #9ca3af;
    font-weight: 400;
  }

  &amp;amp;:focus {
    outline: none;
    border-color: #667eea;
    background: rgba(255, 255, 255, 0.95);
    box-shadow: 
      0 0 0 4px rgba(102, 126, 234, 0.1),
      0 8px 25px -5px rgba(102, 126, 234, 0.2);
    transform: translateY(-1px);
  }
`;

const Button = styled.button`
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  padding: 1rem 2rem;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  font-weight: 600;
  border-radius: 16px;
  border: none;
  cursor: pointer;
  transition: all 0.3s ease;
  white-space: nowrap;
  font-size: 0.975rem;
  letter-spacing: 0.025em;
  position: relative;
  overflow: hidden;

  &amp;amp;::before {
    content: '';
    position: absolute;
    top: 0;
    left: -100%;
    width: 100%;
    height: 100%;
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
    transition: left 0.5s;
  }

  &amp;amp;:hover {
    transform: translateY(-2px);
    box-shadow: 0 15px 35px -5px rgba(102, 126, 234, 0.4);

    &amp;amp;::before {
      left: 100%;
    }
  }

  &amp;amp;:active {
    transform: translateY(0);
  }

  &amp;amp;:disabled {
    background: linear-gradient(135deg, #9ca3af, #6b7280);
    cursor: not-allowed;
    transform: none;
    box-shadow: none;

    &amp;amp;::before {
      display: none;
    }
  }
`;

const Spinner = styled.div`
  border: 3px solid rgba(255, 255, 255, 0.3);
  border-top: 3px solid white;
  border-radius: 50%;
  width: 1.25rem;
  height: 1.25rem;
  animation: ${spin} 1s linear infinite;
`;

const ErrorMessage = styled.div`
  background: linear-gradient(135deg, #fee2e2, #fecaca);
  color: #dc2626;
  font-size: 0.875rem;
  padding: 1rem 1.5rem;
  border-radius: 12px;
  text-align: center;
  border: 1px solid #fca5a5;
  font-weight: 500;
  animation: ${fadeIn} 0.3s ease-out;
`;

const IpCardsGrid = styled.div`
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 1.5rem;
  margin-top: 2rem;
`;

const IpCardContainer = styled.div`
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(20px);
  border-radius: 20px;
  box-shadow: 
    0 20px 40px -12px rgba(0, 0, 0, 0.15),
    0 0 0 1px rgba(255, 255, 255, 0.1);
  padding: 1.5rem;
  border: 1px solid rgba(255, 255, 255, 0.2);
  display: flex;
  flex-direction: column;
  gap: 1rem;
  animation: ${fadeIn} 0.5s ease-out;
  transition: all 0.3s ease;
  position: relative;
  overflow: hidden;

  &amp;amp;::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 4px;
    background: linear-gradient(135deg, #667eea, #764ba2);
    border-radius: 20px 20px 0 0;
  }

  &amp;amp;:hover {
    transform: translateY(-4px) scale(1.02);
    box-shadow: 
      0 32px 64px -12px rgba(0, 0, 0, 0.25),
      0 0 0 1px rgba(255, 255, 255, 0.2);
  }
`;

const IpCardHeader = styled.h3`
  font-size: 1.5rem;
  font-weight: 700;
  color: #1f2937;
  margin: 0;
  font-family: 'Monaco', 'Menlo', monospace;
  background: linear-gradient(135deg, #1f2937, #374151);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
`;

const IpCardDetail = styled.div`
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 0.925rem;
  padding: 0.5rem 0;
`;

const IpCardLabel = styled.span`
  font-weight: 600;
  color: #6b7280;
  font-size: 0.875rem;
`;

const IpCardValue = styled.span`
  font-weight: 700;
  color: #1f2937;
  font-size: 0.875rem;
`;

const StatusBadge = styled.span`
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 1rem;
  border-radius: 50px;
  font-size: 0.875rem;
  font-weight: 600;
  transition: all 0.3s ease;

  ${(props) =&amp;gt; {
    switch (props.type) {
      case 'enabled':
        return css`
          background: linear-gradient(135deg, #d1fae5, #a7f3d0);
          color: #059669;
          border: 1px solid #6ee7b7;
          animation: ${pulse} 2s ease-in-out infinite;
        `;
      case 'disabled':
        return css`
          background: linear-gradient(135deg, #fee2e2, #fecaca);
          color: #dc2626;
          border: 1px solid #fca5a5;
        `;
      case 'loading':
        return css`
          background: linear-gradient(135deg, #eff6ff, #dbeafe);
          color: #2563eb;
          border: 1px solid #93c5fd;
        `;
      case 'error':
        return css`
          background: linear-gradient(135deg, #fee2e2, #fecaca);
          color: #dc2626;
          border: 1px solid #fca5a5;
        `;
      default:
        return css`
          background: linear-gradient(135deg, #f3f4f6, #e5e7eb);
          color: #6b7280;
          border: 1px solid #d1d5db;
        `;
    }
  }}
`;

const TestButton = styled(Button)`
  width: 100%;
  margin-top: 0.5rem;
  background: linear-gradient(135deg, #4f46e5, #7c3aed);
  font-size: 0.875rem;
  padding: 0.875rem 1.5rem;

  &amp;amp;:hover {
    box-shadow: 0 15px 35px -5px rgba(79, 70, 229, 0.4);
  }

  &amp;amp;:disabled {
    background: linear-gradient(135deg, #9ca3af, #6b7280);
  }
`;

const EmptyState = styled.div`
  text-align: center;
  padding: 4rem 2rem;
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(20px);
  border-radius: 24px;
  box-shadow: 
    0 32px 64px -12px rgba(0, 0, 0, 0.15),
    0 0 0 1px rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.2);
  animation: ${fadeIn} 0.6s ease-out;
  position: relative;
  overflow: hidden;

  &amp;amp;::before {
    content: '';
    position: absolute;
    top: -50%;
    left: -50%;
    width: 200%;
    height: 200%;
    background: 
      radial-gradient(circle, rgba(102, 126, 234, 0.1) 0%, transparent 50%);
    animation: ${float} 6s ease-in-out infinite;
  }
`;

const EmptyStateIcon = styled.div`
  font-size: 4rem;
  margin-bottom: 1.5rem;
  display: block;
  background: linear-gradient(135deg, #667eea, #764ba2);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: ${float} 3s ease-in-out infinite;
`;

const EmptyStateTitle = styled.h3`
  font-size: 1.75rem;
  font-weight: 700;
  background: linear-gradient(135deg, #1f2937, #374151);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  margin-bottom: 1rem;
`;

const EmptyStateText = styled.p`
  font-size: 1.125rem;
  color: #6b7280;
  margin-bottom: 1.5rem;
  line-height: 1.6;
  max-width: 400px;
  margin-left: auto;
  margin-right: auto;
`;

const EmptyStateNote = styled.p`
  font-size: 0.925rem;
  color: #9ca3af;
  font-style: italic;
  background: rgba(102, 126, 234, 0.05);
  padding: 1rem 1.5rem;
  border-radius: 12px;
  border: 1px solid rgba(102, 126, 234, 0.1);
  max-width: 500px;
  margin: 0 auto;
`;

const StatsBar = styled.div`
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 1rem;
  margin-top: 2rem;
  padding: 1.5rem;
  background: rgba(255, 255, 255, 0.6);
  backdrop-filter: blur(10px);
  border-radius: 16px;
  border: 1px solid rgba(255, 255, 255, 0.3);
`;

const StatItem = styled.div`
  text-align: center;
  padding: 0.5rem;
`;

const StatValue = styled.div`
  font-size: 1.5rem;
  font-weight: 800;
  background: linear-gradient(135deg, #667eea, #764ba2);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
`;

const StatLabel = styled.div`
  font-size: 0.75rem;
  color: #6b7280;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-top: 0.25rem;
`;

// --- Helper Functions for IP Conversion ---
const ipToNumber = (ip) =&amp;gt; {
  return ip.split('.').reduce((acc, octet) =&amp;gt; (acc &amp;lt;&amp;lt; 8) + parseInt(octet, 10), 0) &amp;gt;&amp;gt;&amp;gt; 0;
};

const numberToIp = (num) =&amp;gt; {
  return [
    (num &amp;gt;&amp;gt;&amp;gt; 24) &amp;amp; 0xFF,
    (num &amp;gt;&amp;gt;&amp;gt; 16) &amp;amp; 0xFF,
    (num &amp;gt;&amp;gt;&amp;gt; 8) &amp;amp; 0xFF,
    num &amp;amp; 0xFF
  ].join('.');
};

// --- IP Card Component ---
const IpCard = ({ ipData, featureFlagName, onTestFlag }) =&amp;gt; {
  const { id, ipAddress, testResult } = ipData;

  const handleTestClick = () =&amp;gt; {
    onTestFlag(id, ipAddress);
  };

  return (
    &amp;lt;IpCardContainer&amp;gt;
      &amp;lt;IpCardHeader&amp;gt;🌐 {ipAddress}&amp;lt;/IpCardHeader&amp;gt;
      &amp;lt;IpCardDetail&amp;gt;
        &amp;lt;IpCardLabel&amp;gt;Status:&amp;lt;/IpCardLabel&amp;gt;
        {testResult ? (
          testResult.isLoading ? (
            &amp;lt;StatusBadge type="loading"&amp;gt;
              &amp;lt;Spinner style={{ width: '1rem', height: '1rem', borderTopColor: '#2563eb' }} /&amp;gt;
              Testing...
            &amp;lt;/StatusBadge&amp;gt;
          ) : testResult.error ? (
            &amp;lt;StatusBadge type="error"&amp;gt;❌ Error&amp;lt;/StatusBadge&amp;gt;
          ) : testResult.enabled ? (
            &amp;lt;StatusBadge type="enabled"&amp;gt;✅ Enabled&amp;lt;/StatusBadge&amp;gt;
          ) : (
            &amp;lt;StatusBadge type="disabled"&amp;gt;❌ Disabled&amp;lt;/StatusBadge&amp;gt;
          )
        ) : (
          &amp;lt;StatusBadge&amp;gt;⏳ Not Tested&amp;lt;/StatusBadge&amp;gt;
        )}
      &amp;lt;/IpCardDetail&amp;gt;
      {testResult &amp;amp;&amp;amp; testResult.timestamp &amp;amp;&amp;amp; (
        &amp;lt;IpCardDetail&amp;gt;
          &amp;lt;IpCardLabel&amp;gt;Last Test:&amp;lt;/IpCardLabel&amp;gt;
          &amp;lt;IpCardValue&amp;gt;{testResult.timestamp}&amp;lt;/IpCardValue&amp;gt;
        &amp;lt;/IpCardDetail&amp;gt;
      )}
      &amp;lt;TestButton onClick={handleTestClick} disabled={testResult?.isLoading}&amp;gt;
        {testResult?.isLoading ? (
          &amp;lt;&amp;gt;
            &amp;lt;Spinner style={{ width: '1rem', height: '1rem' }} /&amp;gt;
            Testing...
          &amp;lt;/&amp;gt;
        ) : (
          &amp;lt;&amp;gt;
            🧪 Test Flag
          &amp;lt;/&amp;gt;
        )}
      &amp;lt;/TestButton&amp;gt;
    &amp;lt;/IpCardContainer&amp;gt;
  );
};

// --- Main IP Range Simulator Component ---
const IPRangeSimulator = () =&amp;gt; {
  const [featureFlagName, setFeatureFlagName] = useState('geo-targeted-feature');
  const [startIp, setStartIp] = useState('192.168.1.1');
  const [endIp, setEndIp] = useState('192.168.1.10');
  const [discreteIp, setDiscreteIp] = useState('');
  const [ipCards, setIpCards] = useState([]);
  const [errorMessage, setErrorMessage] = useState('');

  const unleashClient = useUnleashClient();
  const updateContext = useUnleashContext();

  // Calculate stats
  const stats = {
    total: ipCards.length,
    tested: ipCards.filter(card =&amp;gt; card.testResult &amp;amp;&amp;amp; !card.testResult.isLoading).length,
    enabled: ipCards.filter(card =&amp;gt; card.testResult &amp;amp;&amp;amp; card.testResult.enabled &amp;amp;&amp;amp; !card.testResult.error).length,
    disabled: ipCards.filter(card =&amp;gt; card.testResult &amp;amp;&amp;amp; !card.testResult.enabled &amp;amp;&amp;amp; !card.testResult.error).length,
  };

  const addRangeCards = useCallback(() =&amp;gt; {
    setErrorMessage('');
    try {
      const startNum = ipToNumber(startIp);
      const endNum = ipToNumber(endIp);

      if (isNaN(startNum) || isNaN(endNum) || startNum &amp;gt; endNum) {
        setErrorMessage('Invalid IP range. Please enter valid start and end IPs.');
        return;
      }

      const newCards = [];
      for (let i = startNum; i &amp;lt;= endNum; i++) {
        newCards.push({
          id: crypto.randomUUID(),
          ipAddress: numberToIp(i),
          testResult: null,
        });
        if (newCards.length &amp;gt; 100) {
          setErrorMessage('Too many IPs in range. Max 100 cards allowed per range generation.');
          break;
        }
      }
      setIpCards(prevCards =&amp;gt; [...prevCards, ...newCards]);
      setStartIp('');
      setEndIp('');
    } catch (e) {
      setErrorMessage('Error parsing IP range. Please check format.');
      console.error(e);
    }
  }, [startIp, endIp]);

  const addDiscreteCard = useCallback(() =&amp;gt; {
    setErrorMessage('');
    if (!discreteIp || !/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(discreteIp)) {
      setErrorMessage('Please enter a valid discrete IP address.');
      return;
    }
    setIpCards(prevCards =&amp;gt; [
      ...prevCards,
      {
        id: crypto.randomUUID(),
        ipAddress: discreteIp,
        testResult: null,
      },
    ]);
    setDiscreteIp('');
  }, [discreteIp]);

  const handleTestFlag = useCallback(async (cardId, ipAddress) =&amp;gt; {
    setIpCards(prevCards =&amp;gt;
      prevCards.map(card =&amp;gt;
        card.id === cardId ? { ...card, testResult: { ...card.testResult, isLoading: true, error: false } } : card
      )
    );
    setErrorMessage('');

    try {
      await updateContext({
        remoteAddress: ipAddress,
        userId: 'simulator-user-' + ipAddress.replace(/\./g, '-'),
      });
      await new Promise((resolve) =&amp;gt; setTimeout(resolve, 100));

      const isEnabled = unleashClient.isEnabled(featureFlagName);

      setIpCards(prevCards =&amp;gt;
        prevCards.map(card =&amp;gt;
          card.id === cardId
            ? {
                ...card,
                testResult: {
                  ip: ipAddress,
                  enabled: isEnabled,
                  timestamp: new Date().toLocaleTimeString(),
                  isLoading: false,
                  error: false,
                },
              }
            : card
        )
      );
    } catch (error) {
      console.error(`Error testing flag for IP ${ipAddress}:`, error);
      setIpCards(prevCards =&amp;gt;
        prevCards.map(card =&amp;gt;
          card.id === cardId
            ? {
                ...card,
                testResult: {
                  ip: ipAddress,
                  enabled: false,
                  timestamp: new Date().toLocaleTimeString(),
                  isLoading: false,
                  error: true,
                },
              }
            : card
        )
      );
      setErrorMessage(`Failed to test flag for ${ipAddress}. Check console.`);
    }
  }, [featureFlagName, updateContext, unleashClient]);

  return (
    &amp;lt;Container&amp;gt;
      &amp;lt;MaxWidthWrapper&amp;gt;
        {/* Header */}
        &amp;lt;Card&amp;gt;
          &amp;lt;Header&amp;gt;
            &amp;lt;Title&amp;gt;🗺️ Unleash IP Strategy Simulator&amp;lt;/Title&amp;gt;
            &amp;lt;Subtitle&amp;gt;
              Generate and test feature flag status for multiple IP addresses with modern, intuitive interface.
            &amp;lt;/Subtitle&amp;gt;
          &amp;lt;/Header&amp;gt;
        &amp;lt;/Card&amp;gt;

        {/* Configuration Panel */}
        &amp;lt;Card&amp;gt;
          &amp;lt;SectionTitle&amp;gt;⚙️ Configuration &amp;amp; IP Generation&amp;lt;/SectionTitle&amp;gt;

          &amp;lt;ConfigGrid&amp;gt;
            &amp;lt;InputField&amp;gt;
              &amp;lt;Label htmlFor="flagName"&amp;gt;🚩 Feature Flag Name&amp;lt;/Label&amp;gt;
              &amp;lt;Input
                id="flagName"
                type="text"
                value={featureFlagName}
                onChange={(e) =&amp;gt; setFeatureFlagName(e.target.value)}
                placeholder="e.g., geo-targeted-feature"
              /&amp;gt;
            &amp;lt;/InputField&amp;gt;

            &amp;lt;InputGroup&amp;gt;
              &amp;lt;InputField&amp;gt;
                &amp;lt;Label htmlFor="startIp"&amp;gt;🚀 Start IP (Range)&amp;lt;/Label&amp;gt;
                &amp;lt;Input
                  id="startIp"
                  type="text"
                  value={startIp}
                  onChange={(e) =&amp;gt; setStartIp(e.target.value)}
                  placeholder="e.g., 192.168.1.1"
                /&amp;gt;
              &amp;lt;/InputField&amp;gt;
              &amp;lt;InputField&amp;gt;
                &amp;lt;Label htmlFor="endIp"&amp;gt;🏁 End IP (Range)&amp;lt;/Label&amp;gt;
                &amp;lt;Input
                  id="endIp"
                  type="text"
                  value={endIp}
                  onChange={(e) =&amp;gt; setEndIp(e.target.value)}
                  placeholder="e.g., 192.168.1.10"
                /&amp;gt;
              &amp;lt;/InputField&amp;gt;
              &amp;lt;Button onClick={addRangeCards}&amp;gt;
                📊 Generate Range
              &amp;lt;/Button&amp;gt;
            &amp;lt;/InputGroup&amp;gt;

            &amp;lt;DiscreteInputGroup&amp;gt;
              &amp;lt;InputField&amp;gt;
                &amp;lt;Label htmlFor="discreteIp"&amp;gt;🎯 Discrete IP Address&amp;lt;/Label&amp;gt;
                &amp;lt;Input

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;DiscreteInputGroup&amp;gt;
              &amp;lt;InputField&amp;gt;
                &amp;lt;Label htmlFor="discreteIp"&amp;gt;🎯 Discrete IP Address&amp;lt;/Label&amp;gt;
                &amp;lt;Input
                  id="discreteIp"
                  type="text"
                  value={discreteIp}
                  onChange={(e) =&amp;gt; setDiscreteIp(e.target.value)}
                  placeholder="e.g., 203.0.113.45"
                /&amp;gt;
              &amp;lt;/InputField&amp;gt;
              &amp;lt;Button onClick={addDiscreteCard}&amp;gt;
                ➕ Add IP
              &amp;lt;/Button&amp;gt;
            &amp;lt;/DiscreteInputGroup&amp;gt;
          &amp;lt;/ConfigGrid&amp;gt;

          {errorMessage &amp;amp;&amp;amp; (
            &amp;lt;ErrorMessage&amp;gt;{errorMessage}&amp;lt;/ErrorMessage&amp;gt;
          )}
        &amp;lt;/Card&amp;gt;

        {/* Stats Bar */}
        {ipCards.length &amp;gt; 0 &amp;amp;&amp;amp; (
          &amp;lt;StatsBar&amp;gt;
            &amp;lt;StatItem&amp;gt;
              &amp;lt;StatValue&amp;gt;{stats.total}&amp;lt;/StatValue&amp;gt;
              &amp;lt;StatLabel&amp;gt;Total IPs&amp;lt;/StatLabel&amp;gt;
            &amp;lt;/StatItem&amp;gt;
            &amp;lt;StatItem&amp;gt;
              &amp;lt;StatValue&amp;gt;{stats.tested}&amp;lt;/StatValue&amp;gt;
              &amp;lt;StatLabel&amp;gt;Tested&amp;lt;/StatLabel&amp;gt;
            &amp;lt;/StatItem&amp;gt;
            &amp;lt;StatItem&amp;gt;
              &amp;lt;StatValue&amp;gt;{stats.enabled}&amp;lt;/StatValue&amp;gt;
              &amp;lt;StatLabel&amp;gt;Enabled&amp;lt;/StatLabel&amp;gt;
            &amp;lt;/StatItem&amp;gt;
            &amp;lt;StatItem&amp;gt;
              &amp;lt;StatValue&amp;gt;{stats.disabled}&amp;lt;/StatValue&amp;gt;
              &amp;lt;StatLabel&amp;gt;Disabled&amp;lt;/StatLabel&amp;gt;
            &amp;lt;/StatItem&amp;gt;
          &amp;lt;/StatsBar&amp;gt;
        )}

        {/* IP Cards Display */}
        {ipCards.length &amp;gt; 0 ? (
          &amp;lt;IpCardsGrid&amp;gt;
            {ipCards.map((ipData) =&amp;gt; (
              &amp;lt;IpCard
                key={ipData.id}
                ipData={ipData}
                featureFlagName={featureFlagName}
                onTestFlag={handleTestFlag}
              /&amp;gt;
            ))}
          &amp;lt;/IpCardsGrid&amp;gt;
        ) : (
          &amp;lt;EmptyState&amp;gt;
            &amp;lt;EmptyStateIcon&amp;gt;🚀&amp;lt;/EmptyStateIcon&amp;gt;
            &amp;lt;EmptyStateTitle&amp;gt;Ready to Start Testing&amp;lt;/EmptyStateTitle&amp;gt;
            &amp;lt;EmptyStateText&amp;gt;
              Enter an IP range or discrete IP address above to generate cards and begin testing your feature flags.
            &amp;lt;/EmptyStateText&amp;gt;
            &amp;lt;EmptyStateNote&amp;gt;
              💡 Ensure your feature flag "{featureFlagName}" has an "IP Address" strategy configured in Unleash for accurate testing results.
            &amp;lt;/EmptyStateNote&amp;gt;
          &amp;lt;/EmptyState&amp;gt;
        )}
      &amp;lt;/MaxWidthWrapper&amp;gt;
    &amp;lt;/Container&amp;gt;
  );
};

export default IPRangeSimulator;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>iptester2</title>
      <dc:creator>Kishan Srivastava</dc:creator>
      <pubDate>Mon, 30 Jun 2025 09:38:08 +0000</pubDate>
      <link>https://dev.to/superman01/iptester2-4lhi</link>
      <guid>https://dev.to/superman01/iptester2-4lhi</guid>
      <description>&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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: 1200px; /* Increased max-width for more cards */
  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 */
  @media (min-width: 640px) { /* sm */
    flex-direction: row;
    align-items: flex-end;
  }
`;

const InputField = styled.div`
  flex: 1;
`;

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 */
  &amp;amp;: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;
  white-space: nowrap; /* Prevent text wrapping */

  &amp;amp;: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);
  }

  &amp;amp;: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 IpCardsGrid = styled.div`
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); /* Responsive grid */
  gap: 1.5rem; /* gap-6 */
  margin-top: 1.5rem; /* mt-6 */
`;

const IpCardContainer = styled.div`
  background-color: white;
  border-radius: 0.75rem; /* rounded-xl */
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); /* shadow-md */
  padding: 1.25rem; /* p-5 */
  border: 1px solid #e2e8f0; /* border-gray-200 */
  display: flex;
  flex-direction: column;
  gap: 0.75rem; /* gap-3 */
  animation: ${fadeIn} 0.5s ease-out;
`;

const IpCardHeader = styled.h3`
  font-size: 1.25rem; /* text-xl */
  font-weight: 600; /* font-semibold */
  color: #2d3748; /* gray-800 */
  margin-bottom: 0.5rem;
`;

const IpCardDetail = styled.div`
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 0.9375rem; /* text-base */
  color: #4a5568; /* gray-600 */
`;

const IpCardLabel = styled.span`
  font-weight: 500;
`;

const IpCardValue = styled.span`
  font-weight: 700;
  color: #1a202c;
`;

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) =&amp;gt; {
    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 'loading':
        return css`
          background-color: #eff6ff; /* blue-50 */
          color: #2563eb; /* blue-600 */
        `;
      case 'error':
        return css`
          background-color: #fee2e2; /* red-100 */
          color: #dc2626; /* red-700 */
        `;
      default:
        return css`
          background-color: #e2e8f0;
          color: #4a5568;
        `;
    }
  }}
`;

const TestButton = styled(Button)`
  width: 100%;
  margin-top: 1rem; /* mt-4 */
  background-color: #4f46e5; /* indigo-600 */
  &amp;amp;:hover {
    background-color: #4338ca; /* indigo-700 */
  }
`;

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;
`;

// --- Helper Functions for IP Conversion ---
// These are crucial for handling IP ranges

const ipToNumber = (ip) =&amp;gt; {
  return ip.split('.').reduce((acc, octet) =&amp;gt; (acc &amp;lt;&amp;lt; 8) + parseInt(octet, 10), 0) &amp;gt;&amp;gt;&amp;gt; 0; // &amp;gt;&amp;gt;&amp;gt; 0 converts to unsigned 32-bit
};

const numberToIp = (num) =&amp;gt; {
  return [
    (num &amp;gt;&amp;gt;&amp;gt; 24) &amp;amp; 0xFF,
    (num &amp;gt;&amp;gt;&amp;gt; 16) &amp;amp; 0xFF,
    (num &amp;gt;&amp;gt;&amp;gt; 8) &amp;amp; 0xFF,
    num &amp;amp; 0xFF
  ].join('.');
};

// --- IP Card Component ---
// This component represents a single IP address card

const IpCard = ({ ipData, featureFlagName, onTestFlag }) =&amp;gt; {
  const { id, ipAddress, testResult } = ipData;

  const handleTestClick = () =&amp;gt; {
    onTestFlag(id, ipAddress);
  };

  return (
    &amp;lt;IpCardContainer&amp;gt;
      &amp;lt;IpCardHeader&amp;gt;{ipAddress}&amp;lt;/IpCardHeader&amp;gt;
      &amp;lt;IpCardDetail&amp;gt;
        &amp;lt;IpCardLabel&amp;gt;Status:&amp;lt;/IpCardLabel&amp;gt;
        {testResult ? (
          testResult.isLoading ? (
            &amp;lt;StatusBadge type="loading"&amp;gt;
              &amp;lt;Spinner style={{ width: '1rem', height: '1rem', borderTopColor: '#2563eb' }} /&amp;gt; Testing...
            &amp;lt;/StatusBadge&amp;gt;
          ) : testResult.error ? (
            &amp;lt;StatusBadge type="error"&amp;gt;❌ Error&amp;lt;/StatusBadge&amp;gt;
          ) : testResult.enabled ? (
            &amp;lt;StatusBadge type="enabled"&amp;gt;✅ Enabled&amp;lt;/StatusBadge&amp;gt;
          ) : (
            &amp;lt;StatusBadge type="disabled"&amp;gt;❌ Disabled&amp;lt;/StatusBadge&amp;gt;
          )
        ) : (
          &amp;lt;StatusBadge&amp;gt;Not Tested&amp;lt;/StatusBadge&amp;gt;
        )}
      &amp;lt;/IpCardDetail&amp;gt;
      {testResult &amp;amp;&amp;amp; testResult.timestamp &amp;amp;&amp;amp; (
        &amp;lt;IpCardDetail&amp;gt;
          &amp;lt;IpCardLabel&amp;gt;Last Test:&amp;lt;/IpCardLabel&amp;gt;
          &amp;lt;IpCardValue&amp;gt;{testResult.timestamp}&amp;lt;/IpCardValue&amp;gt;
        &amp;lt;/IpCardDetail&amp;gt;
      )}
      &amp;lt;TestButton onClick={handleTestClick} disabled={testResult?.isLoading}&amp;gt;
        {testResult?.isLoading ? 'Testing...' : 'Test Flag'}
      &amp;lt;/TestButton&amp;gt;
    &amp;lt;/IpCardContainer&amp;gt;
  );
};

// --- Main IP Range Simulator Component ---

const IPRangeSimulator = () =&amp;gt; {
  const [featureFlagName, setFeatureFlagName] = useState('geo-targeted-feature');
  const [startIp, setStartIp] = useState('192.168.1.1');
  const [endIp, setEndIp] = useState('192.168.1.10');
  const [discreteIp, setDiscreteIp] = useState('');
  const [ipCards, setIpCards] = useState([]); // Array to hold all IP card data
  const [errorMessage, setErrorMessage] = useState('');

  const unleashClient = useUnleashClient();
  const updateContext = useUnleashContext();

  // Function to add IPs from a range
  const addRangeCards = useCallback(() =&amp;gt; {
    setErrorMessage('');
    try {
      const startNum = ipToNumber(startIp);
      const endNum = ipToNumber(endIp);

      if (isNaN(startNum) || isNaN(endNum) || startNum &amp;gt; endNum) {
        setErrorMessage('Invalid IP range. Please enter valid start and end IPs.');
        return;
      }

      const newCards = [];
      for (let i = startNum; i &amp;lt;= endNum; i++) {
        newCards.push({
          id: crypto.randomUUID(), // Unique ID for each card
          ipAddress: numberToIp(i),
          testResult: null, // Initial state for test result
        });
        if (newCards.length &amp;gt; 100) { // Limit to prevent too many cards
            setErrorMessage('Too many IPs in range. Max 100 cards allowed per range generation.');
            break;
        }
      }
      setIpCards(prevCards =&amp;gt; [...prevCards, ...newCards]);
      setStartIp(''); // Clear inputs after adding
      setEndIp('');
    } catch (e) {
      setErrorMessage('Error parsing IP range. Please check format.');
      console.error(e);
    }
  }, [startIp, endIp]);

  // Function to add a discrete IP
  const addDiscreteCard = useCallback(() =&amp;gt; {
    setErrorMessage('');
    if (!discreteIp || !/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(discreteIp)) {
      setErrorMessage('Please enter a valid discrete IP address.');
      return;
    }
    setIpCards(prevCards =&amp;gt; [
      ...prevCards,
      {
        id: crypto.randomUUID(),
        ipAddress: discreteIp,
        testResult: null,
      },
    ]);
    setDiscreteIp(''); // Clear input after adding
  }, [discreteIp]);

  // Function to handle testing a single IP flag (called by IpCard)
  const handleTestFlag = useCallback(async (cardId, ipAddress) =&amp;gt; {
    setIpCards(prevCards =&amp;gt;
      prevCards.map(card =&amp;gt;
        card.id === cardId ? { ...card, testResult: { ...card.testResult, isLoading: true, error: false } } : card
      )
    );
    setErrorMessage(''); // Clear general error message

    try {
      await updateContext({
        remoteAddress: ipAddress,
        userId: 'simulator-user-' + ipAddress.replace(/\./g, '-'), // Ensure unique userId per IP test
      });
      await new Promise((resolve) =&amp;gt; setTimeout(resolve, 100)); // Small delay

      const isEnabled = unleashClient.isEnabled(featureFlagName);

      setIpCards(prevCards =&amp;gt;
        prevCards.map(card =&amp;gt;
          card.id === cardId
            ? {
                ...card,
                testResult: {
                  ip: ipAddress,
                  enabled: isEnabled,
                  timestamp: new Date().toLocaleTimeString(),
                  isLoading: false,
                  error: false,
                },
              }
            : card
        )
      );
    } catch (error) {
      console.error(`Error testing flag for IP ${ipAddress}:`, error);
      setIpCards(prevCards =&amp;gt;
        prevCards.map(card =&amp;gt;
          card.id === cardId
            ? {
                ...card,
                testResult: {
                  ip: ipAddress,
                  enabled: false,
                  timestamp: new Date().toLocaleTimeString(),
                  isLoading: false,
                  error: true,
                },
              }
            : card
        )
      );
      setErrorMessage(`Failed to test flag for ${ipAddress}. Check console.`);
    }
  }, [featureFlagName, updateContext, unleashClient]);

  return (
    &amp;lt;Container&amp;gt;
      &amp;lt;MaxWidthWrapper&amp;gt;
        {/* Header */}
        &amp;lt;Card&amp;gt;
          &amp;lt;Header&amp;gt;
            &amp;lt;Title&amp;gt;🗺️ Unleash IP Strategy Simulator&amp;lt;/Title&amp;gt;
            &amp;lt;Subtitle&amp;gt;
              Generate and test feature flag status for multiple IP addresses.
            &amp;lt;/Subtitle&amp;gt;
          &amp;lt;/Header&amp;gt;
        &amp;lt;/Card&amp;gt;

        {/* Configuration Panel */}
        &amp;lt;Card&amp;gt;
          &amp;lt;SectionTitle&amp;gt;Configuration &amp;amp; IP Generation&amp;lt;/SectionTitle&amp;gt;
          &amp;lt;InputField className="mb-4"&amp;gt;
            &amp;lt;Label htmlFor="flagName"&amp;gt;Feature Flag Name&amp;lt;/Label&amp;gt;
            &amp;lt;Input
              id="flagName"
              type="text"
              value={featureFlagName}
              onChange={(e) =&amp;gt; setFeatureFlagName(e.target.value)}
              placeholder="e.g., geo-targeted-feature"
            /&amp;gt;
          &amp;lt;/InputField&amp;gt;

          &amp;lt;div className="flex flex-col gap-4 mb-4 md:flex-row"&amp;gt;
            &amp;lt;InputField&amp;gt;
              &amp;lt;Label htmlFor="startIp"&amp;gt;Start IP (Range)&amp;lt;/Label&amp;gt;
              &amp;lt;Input
                id="startIp"
                type="text"
                value={startIp}
                onChange={(e) =&amp;gt; setStartIp(e.target.value)}
                placeholder="e.g., 192.168.1.1"
              /&amp;gt;
            &amp;lt;/InputField&amp;gt;
            &amp;lt;InputField&amp;gt;
              &amp;lt;Label htmlFor="endIp"&amp;gt;End IP (Range)&amp;lt;/Label&amp;gt;
              &amp;lt;Input
                id="endIp"
                type="text"
                value={endIp}
                onChange={(e) =&amp;gt; setEndIp(e.target.value)}
                placeholder="e.g., 192.168.1.10"
              /&amp;gt;
            &amp;lt;/InputField&amp;gt;
            &amp;lt;Button onClick={addRangeCards}&amp;gt;
              Generate Range Cards
            &amp;lt;/Button&amp;gt;
          &amp;lt;/div&amp;gt;

          &amp;lt;div className="flex flex-col gap-4 md:flex-row"&amp;gt;
            &amp;lt;InputField&amp;gt;
              &amp;lt;Label htmlFor="discreteIp"&amp;gt;Discrete IP Address&amp;lt;/Label&amp;gt;
              &amp;lt;Input
                id="discreteIp"
                type="text"
                value={discreteIp}
                onChange={(e) =&amp;gt; setDiscreteIp(e.target.value)}
                placeholder="e.g., 203.0.113.45"
              /&amp;gt;
            &amp;lt;/InputField&amp;gt;
            &amp;lt;Button onClick={addDiscreteCard}&amp;gt;
              Add Discrete IP Card
            &amp;lt;/Button&amp;gt;
          &amp;lt;/div&amp;gt;

          {errorMessage &amp;amp;&amp;amp; (
            &amp;lt;ErrorMessage&amp;gt;{errorMessage}&amp;lt;/ErrorMessage&amp;gt;
          )}
        &amp;lt;/Card&amp;gt;

        {/* IP Cards Display */}
        {ipCards.length &amp;gt; 0 ? (
          &amp;lt;IpCardsGrid&amp;gt;
            {ipCards.map((ipData) =&amp;gt; (
              &amp;lt;IpCard
                key={ipData.id}
                ipData={ipData}
                featureFlagName={featureFlagName}
                onTestFlag={handleTestFlag}
              /&amp;gt;
            ))}
          &amp;lt;/IpCardsGrid&amp;gt;
        ) : (
          &amp;lt;EmptyState&amp;gt;
            &amp;lt;EmptyStateIcon&amp;gt;➕&amp;lt;/EmptyStateIcon&amp;gt;
            &amp;lt;EmptyStateTitle&amp;gt;Start by Adding IPs&amp;lt;/EmptyStateTitle&amp;gt;
            &amp;lt;EmptyStateText&amp;gt;
              Enter an IP range or a discrete IP address to generate cards and begin testing.
            &amp;lt;/EmptyStateText&amp;gt;
            &amp;lt;EmptyStateNote&amp;gt;
              Ensure your feature flag "{featureFlagName}" has an "IP Address" strategy configured in Unleash.
            &amp;lt;/EmptyStateNote&amp;gt;
          &amp;lt;/EmptyState&amp;gt;
        )}
      &amp;lt;/MaxWidthWrapper&amp;gt;
    &amp;lt;/Container&amp;gt;
  );
};

export default IPRangeSimulator;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>iptester</title>
      <dc:creator>Kishan Srivastava</dc:creator>
      <pubDate>Mon, 30 Jun 2025 08:36:20 +0000</pubDate>
      <link>https://dev.to/superman01/iptester-ee5</link>
      <guid>https://dev.to/superman01/iptester-ee5</guid>
      <description>&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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 */
  &amp;amp;: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;

  &amp;amp;: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);
  }

  &amp;amp;: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 */
  &amp;amp;: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) =&amp;gt; {
    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 = () =&amp;gt; {
  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 () =&amp;gt; {
    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) =&amp;gt; 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 (
    &amp;lt;Container&amp;gt;
      &amp;lt;MaxWidthWrapper&amp;gt;
        {/* Header */}
        &amp;lt;Card&amp;gt;
          &amp;lt;Header&amp;gt;
            &amp;lt;Title&amp;gt;📍 Unleash IP Strategy Simulator&amp;lt;/Title&amp;gt;
            &amp;lt;Subtitle&amp;gt;
              Test feature flag behavior based on simulated IP addresses.
            &amp;lt;/Subtitle&amp;gt;
          &amp;lt;/Header&amp;gt;
        &amp;lt;/Card&amp;gt;

        {/* Configuration Panel */}
        &amp;lt;Card&amp;gt;
          &amp;lt;SectionTitle&amp;gt;Test Configuration&amp;lt;/SectionTitle&amp;gt;
          &amp;lt;InputGroup&amp;gt;
            &amp;lt;div&amp;gt;
              &amp;lt;Label htmlFor="flagName"&amp;gt;Feature Flag Name&amp;lt;/Label&amp;gt;
              &amp;lt;Input
                id="flagName"
                type="text"
                value={featureFlagName}
                onChange={(e) =&amp;gt; setFeatureFlagName(e.target.value)}
                placeholder="e.g., geo-targeted-feature"
              /&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div&amp;gt;
              &amp;lt;Label htmlFor="simulatedIp"&amp;gt;Simulated IP Address&amp;lt;/Label&amp;gt;
              &amp;lt;Input
                id="simulatedIp"
                type="text"
                value={simulatedIpAddress}
                onChange={(e) =&amp;gt; setSimulatedIpAddress(e.target.value)}
                placeholder="e.g., 192.168.1.10 or 203.0.113.50"
              /&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;Button onClick={testIpAddress} disabled={isLoading}&amp;gt;
              {isLoading ? (
                &amp;lt;&amp;gt;
                  &amp;lt;Spinner /&amp;gt;
                  Testing IP...
                &amp;lt;/&amp;gt;
              ) : (
                &amp;lt;&amp;gt;🌐 Test IP Address&amp;lt;/&amp;gt;
              )}
            &amp;lt;/Button&amp;gt;
          &amp;lt;/InputGroup&amp;gt;
          {errorMessage &amp;amp;&amp;amp; (
            &amp;lt;ErrorMessage&amp;gt;{errorMessage}&amp;lt;/ErrorMessage&amp;gt;
          )}
        &amp;lt;/Card&amp;gt;

        {/* Test Result Display */}
        {testResult &amp;amp;&amp;amp; (
          &amp;lt;Card style={{ animation: `${fadeIn} 0.5s ease-out` }}&amp;gt;
            &amp;lt;SectionTitle&amp;gt;Test Result&amp;lt;/SectionTitle&amp;gt;
            &amp;lt;div className="flex flex-col gap-3"&amp;gt;
              &amp;lt;ResultItem&amp;gt;
                &amp;lt;ResultLabel&amp;gt;Simulated IP:&amp;lt;/ResultLabel&amp;gt;
                &amp;lt;ResultValue&amp;gt;{testResult.ip}&amp;lt;/ResultValue&amp;gt;
              &amp;lt;/ResultItem&amp;gt;
              &amp;lt;ResultItem&amp;gt;
                &amp;lt;ResultLabel&amp;gt;Feature Flag Status:&amp;lt;/ResultLabel&amp;gt;
                {testResult.error ? (
                  &amp;lt;StatusBadge type="error"&amp;gt;❌ Error&amp;lt;/StatusBadge&amp;gt;
                ) : testResult.enabled ? (
                  &amp;lt;StatusBadge type="enabled"&amp;gt;✅ Enabled&amp;lt;/StatusBadge&amp;gt;
                ) : (
                  &amp;lt;StatusBadge type="disabled"&amp;gt;❌ Disabled&amp;lt;/StatusBadge&amp;gt;
                )}
              &amp;lt;/ResultItem&amp;gt;
              &amp;lt;ResultItem&amp;gt;
                &amp;lt;ResultLabel&amp;gt;Test Time:&amp;lt;/ResultLabel&amp;gt;
                &amp;lt;ResultValue&amp;gt;{testResult.timestamp}&amp;lt;/ResultValue&amp;gt;
              &amp;lt;/ResultItem&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/Card&amp;gt;
        )}

        {/* Initial Empty State */}
        {!testResult &amp;amp;&amp;amp; !isLoading &amp;amp;&amp;amp; !errorMessage &amp;amp;&amp;amp; (
          &amp;lt;EmptyState&amp;gt;
            &amp;lt;EmptyStateIcon&amp;gt;🧪&amp;lt;/EmptyStateIcon&amp;gt;
            &amp;lt;EmptyStateTitle&amp;gt;Ready to Test IP-Based Features&amp;lt;/EmptyStateTitle&amp;gt;
            &amp;lt;EmptyStateText&amp;gt;
              Enter a feature flag name and a simulated IP address, then click "Test IP Address" to see the result.
            &amp;lt;/EmptyStateText&amp;gt;
            &amp;lt;EmptyStateNote&amp;gt;
              Make sure your feature flag "{featureFlagName}" has an "IP Address" strategy configured in Unleash.
            &amp;lt;/EmptyStateNote&amp;gt;
          &amp;lt;/EmptyState&amp;gt;
        )}
      &amp;lt;/MaxWidthWrapper&amp;gt;
    &amp;lt;/Container&amp;gt;
  );
};

export default IPStrategySimulator;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
  </channel>
</rss>
