UNPKG

feature-flag-core

Version:

🏁 Turn features on and off like a pro – no magic spells required.🎩✨

187 lines (186 loc) 7.79 kB
import React, { useEffect, useState } from "react"; // write a logic to remove an exisiting port from the URL const removeCurrentPortAndSetNewPort = (url, port) => { const baseUrl = new URL(url); // Create a new URL object baseUrl.port = ''; // Clear the existing port (if any) baseUrl.port = port.toString(); // Set the new port return baseUrl.toString(); }; // const API_BASE = `${window.location.origin}:${port}/`; // Dynamic port // const API_URL = `${API_BASE}flags`; export const FeatureFlagManager = ({ port = 3231, customUrl }) => { const [flags, setFlags] = useState({}); const [loading, setLoading] = useState(true); const [newFlagName, setNewFlagName] = useState(""); const [newFlagValue, setNewFlagValue] = useState(true); let API_BASE; if (customUrl) { API_BASE = customUrl.endsWith("/") ? customUrl : `${customUrl}/`; } else { API_BASE = removeCurrentPortAndSetNewPort(window.location.origin, port); } // const API_BASE = removeCurrentPortAndSetNewPort(window.location.origin, port); const API_URL = `${API_BASE}flags`; // // write a logic to remove an exisiting port from the URL // const removeCurrentPortAndSetNewPort = (url: string, port: number) => { // const baseUrl = new URL(url); // Create a new URL object // baseUrl.port = ''; // Clear the existing port (if any) // baseUrl.port = port.toString(); // Set the new port // return baseUrl.toString(); // }; // // const API_BASE = `${window.location.origin}:${port}/`; // Dynamic port // // const API_URL = `${API_BASE}flags`; // const API_BASE = removeCurrentPortAndSetNewPort(window.location.origin, port); // const API_URL = `${API_BASE}flags`; useEffect(() => { if (port) { // If a port is passed, set it to localStorage localStorage.setItem("featureFlagPort", port.toString()); } fetchFlags(); }, []); const fetchFlags = async () => { setLoading(true); try { const res = await fetch(API_URL); const data = await res.json(); setFlags(data); } catch (err) { console.error("Failed to fetch flags:", err); } finally { setLoading(false); } }; const handleToggle = async (key, currentValue) => { try { await fetch(API_URL, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ key, value: !currentValue }), }); setFlags((prev) => ({ ...prev, [key]: !currentValue })); } catch (err) { console.error("Failed to update flag:", err); } }; const handleDelete = async (key) => { const confirmed = window.confirm(`Are you sure you want to delete "${key}"?`); if (!confirmed) return; try { await fetch(`${API_URL}/${key}`, { method: "DELETE" }); setFlags((prev) => { const updated = { ...prev }; delete updated[key]; return updated; }); } catch (err) { console.error("Failed to delete flag:", err); } }; const handleAdd = async () => { const trimmedKey = newFlagName.trim(); if (!trimmedKey) return alert("Flag name cannot be empty"); if (flags.hasOwnProperty(trimmedKey)) return alert("Flag already exists"); try { await fetch(API_URL, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ key: trimmedKey, value: newFlagValue }), }); setFlags((prev) => ({ ...prev, [trimmedKey]: newFlagValue })); setNewFlagName(""); setNewFlagValue(true); } catch (err) { console.error("Failed to add flag:", err); } }; if (loading) return React.createElement("div", { style: styles.loading }, "Loading feature flags..."); return (React.createElement("div", { style: styles.container }, React.createElement("h2", { style: styles.heading }, "\uD83C\uDF1F Feature Flag Manager"), React.createElement("a", { href: "https://github.com/cinfinit", style: { margin: "0px", marginBottom: "0.5rem", color: 'cyan' } }, "@ cinfinit"), React.createElement("table", { style: styles.table }, React.createElement("thead", null, React.createElement("tr", null, React.createElement("th", { style: styles.th }, "Flag Name"), React.createElement("th", { style: styles.th }, "Enabled"), React.createElement("th", { style: styles.th }, "Actions"))), React.createElement("tbody", null, Object.entries(flags).map(([key, value]) => (React.createElement("tr", { key: key }, React.createElement("td", { style: styles.td }, key), React.createElement("td", { style: styles.td }, React.createElement("label", { className: "switch" }, React.createElement("input", { type: "checkbox", checked: value, onChange: () => handleToggle(key, value) }), React.createElement("span", { className: "slider" }))), React.createElement("td", { style: styles.td }, React.createElement("button", { style: styles.button, onClick: () => handleDelete(key) }, "\u274C Delete"))))), React.createElement("tr", null, React.createElement("td", { style: styles.td }, React.createElement("input", { type: "text", placeholder: "New flag name", value: newFlagName, onChange: (e) => setNewFlagName(e.target.value), style: styles.input })), React.createElement("td", { style: styles.td }, React.createElement("label", { className: "switch" }, React.createElement("input", { type: "checkbox", checked: newFlagValue, onChange: () => handleToggle(newFlagName, newFlagValue) }), React.createElement("span", { className: "slider" }))), React.createElement("td", { style: styles.td }, React.createElement("button", { style: styles.button, onClick: handleAdd }, "\u254B Add"))))))); }; // Dark theme styles const styles = { container: { maxWidth: 700, margin: "2rem auto", backgroundColor: "#2C3E50", padding: "2rem", borderRadius: "8px", color: "#ECF0F1", fontFamily: "Arial, sans-serif", }, heading: { textAlign: "center", marginBottom: "0.5rem", }, loading: { color: "#ECF0F1", textAlign: "center", marginTop: "2rem", }, table: { width: "100%", borderCollapse: "collapse", }, th: { textAlign: "center", padding: "12px 8px", borderBottom: "2px solid #3C4A5A", color: "#ECF0F1", }, td: { padding: "10px 8px", borderBottom: "1px solid #3C4A5A", }, input: { width: "100%", padding: "6px 8px", backgroundColor: "#34495E", border: "1px solid #3C4A5A", borderRadius: "4px", color: "#ECF0F1", }, button: { background: "transparent", border: "1px solid #ECF0F1", color: "#ECF0F1", padding: "4px 10px", borderRadius: "4px", cursor: "pointer", }, };