> ## Documentation Index
> Fetch the complete documentation index at: https://docs.communityhub.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Live Variable Filter Playground

> An interactive, in-browser tool that fetches live filter options from GET /variable/get-variable-filter-data and lets you build and run a real GET /variables request from your selections.

## Why a custom playground

`GET /variables` doesn't have a static Mintlify OpenAPI playground here, on purpose: its filter parameters aren't confirmed yet (see [Discovery Endpoints](/api-reference/query/saved-queries)), and the option values for each filter come from a *different, live* endpoint — [`GET /variable/get-variable-filter-data`](/api-reference/query/variable-filters) — rather than a fixed enum. A static OpenAPI spec can't express "fetch the real choices at request time," so this page does it with a small React component instead.

<Warning>
  This tool runs entirely in your browser. Your token is never sent anywhere except directly to `api.communityhub.cloud` — but treat it like any other credential, and don't paste a token you don't want visible in browser dev tools. It also requires `api.communityhub.cloud` to allow cross-origin requests (CORS) from this docs site; if that isn't enabled yet, the requests below will fail with a CORS error rather than a normal API error.
</Warning>

<Note>
  If this site's login is wired up to auto-fill your token (via Mintlify's JWT/OAuth authentication + a `user` object with a `content.token` or `apiPlaygroundInputs.header.Authorization` value), the field below fills in automatically once you're signed in. Until that's wired up on the backend, paste your token manually — see [Authentication](/api-reference/authentication) for how to generate one.
</Note>

export const FilterVariablesPlayground = () => {
  const BASE_URL = "https://api.communityhub.cloud/dh/public/v1";
  const loggedInUser = typeof user !== "undefined" ? user : {};
  const autoToken = loggedInUser?.content?.token || loggedInUser?.apiPlaygroundInputs?.header?.Authorization?.replace(/^Bearer\s+/i, "") || "";
  const [token, setToken] = useState(autoToken);
  const [orgIdsInput, setOrgIdsInput] = useState("");
  const [orgIdsEncoding, setOrgIdsEncoding] = useState("comma");
  const [facetGroups, setFacetGroups] = useState(null);
  const [facetsLoading, setFacetsLoading] = useState(false);
  const [facetsError, setFacetsError] = useState(null);
  const [selected, setSelected] = useState({});
  const [filterEncoding, setFilterEncoding] = useState("repeated");
  const [variablesResult, setVariablesResult] = useState(null);
  const [variablesError, setVariablesError] = useState(null);
  const [variablesLoading, setVariablesLoading] = useState(false);
  const buildOrgIdsQuery = () => {
    const ids = orgIdsInput.split(",").map(s => s.trim()).filter(Boolean);
    if (ids.length === 0) return "";
    if (orgIdsEncoding === "comma") {
      return "orgIds=" + encodeURIComponent(ids.join(","));
    }
    return ids.map(id => "orgIds=" + encodeURIComponent(id)).join("&");
  };
  const loadFacets = () => {
    setFacetsLoading(true);
    setFacetsError(null);
    setFacetGroups(null);
    setSelected({});
    const query = buildOrgIdsQuery();
    const url = BASE_URL + "/variable/get-variable-filter-data" + (query ? "?" + query : "");
    fetch(url, {
      headers: {
        Authorization: "Bearer " + token
      }
    }).then(res => {
      if (!res.ok) throw new Error("Request failed with status " + res.status);
      return res.json();
    }).then(data => {
      setFacetGroups(Array.isArray(data) ? data : []);
      setFacetsLoading(false);
    }).catch(err => {
      setFacetsError(err.message || String(err));
      setFacetsLoading(false);
    });
  };
  const toggleChild = (facetId, childId) => {
    setSelected(prev => {
      const current = prev[facetId] || [];
      const idStr = String(childId);
      const next = current.includes(idStr) ? current.filter(v => v !== idStr) : [...current, idStr];
      return {
        ...prev,
        [facetId]: next
      };
    });
  };
  const buildVariablesQuery = () => {
    const parts = [];
    Object.keys(selected).forEach(facetId => {
      const values = selected[facetId] || [];
      if (values.length === 0) return;
      if (filterEncoding === "comma") {
        parts.push(facetId + "=" + encodeURIComponent(values.join(",")));
      } else {
        values.forEach(v => parts.push(facetId + "=" + encodeURIComponent(v)));
      }
    });
    return parts.join("&");
  };
  const variablesUrl = BASE_URL + "/variables" + (buildVariablesQuery() ? "?" + buildVariablesQuery() : "");
  const runVariablesQuery = () => {
    setVariablesLoading(true);
    setVariablesError(null);
    setVariablesResult(null);
    fetch(variablesUrl, {
      headers: {
        Authorization: "Bearer " + token
      }
    }).then(res => {
      if (!res.ok) throw new Error("Request failed with status " + res.status);
      return res.json();
    }).then(data => {
      setVariablesResult(data);
      setVariablesLoading(false);
    }).catch(err => {
      setVariablesError(err.message || String(err));
      setVariablesLoading(false);
    });
  };
  const inputStyle = {
    width: "100%",
    padding: "6px 10px",
    borderRadius: "8px",
    border: "1px solid rgba(0,0,0,0.2)",
    fontSize: "14px"
  };
  return <div className="not-prose" style={{
    border: "1px solid rgba(0,0,0,0.1)",
    borderRadius: "16px",
    padding: "20px",
    display: "flex",
    flexDirection: "column",
    gap: "16px"
  }}>
      <div style={{
    display: "flex",
    flexDirection: "column",
    gap: "8px"
  }}>
        {autoToken && <div style={{
    fontSize: "12px",
    color: "#166534",
    background: "rgba(22,101,52,0.08)",
    borderRadius: "8px",
    padding: "6px 10px"
  }}>
            Signed in via Data Hub — token auto-filled below. Edit it if you want to test with a different token.
          </div>}
        <label style={{
    fontSize: "13px",
    fontWeight: 600
  }}>
          Personal access token
          <input type="password" value={token} onChange={e => setToken(e.target.value)} placeholder="Bearer token" style={{
    ...inputStyle,
    marginTop: "4px"
  }} />
        </label>

        <label style={{
    fontSize: "13px",
    fontWeight: 600
  }}>
          Org IDs (comma-separated)
          <input type="text" value={orgIdsInput} onChange={e => setOrgIdsInput(e.target.value)} placeholder="e.g. 3 or 3,9,13" style={{
    ...inputStyle,
    marginTop: "4px"
  }} />
        </label>

        <label style={{
    fontSize: "13px"
  }}>
          <input type="checkbox" checked={orgIdsEncoding === "repeated"} onChange={e => setOrgIdsEncoding(e.target.checked ? "repeated" : "comma")} />
          {" "}Send multiple org IDs as repeated <code>orgIds=</code> params instead of comma-separated (encoding isn't confirmed — try both against the real API)
        </label>

        <button onClick={loadFacets} disabled={!token || facetsLoading} style={{
    padding: "8px 14px",
    borderRadius: "8px",
    border: "none",
    background: "#ef7025",
    color: "white",
    fontWeight: 600,
    cursor: token ? "pointer" : "not-allowed",
    width: "fit-content"
  }}>
            {facetsLoading ? "Loading filter options…" : "Load filter options"}
        </button>

        {facetsError && <div style={{
    color: "#b91c1c",
    fontSize: "13px"
  }}>Error: {facetsError}</div>}
      </div>

      {facetGroups && <div style={{
    display: "flex",
    flexDirection: "column",
    gap: "16px"
  }}>
          <div style={{
    fontSize: "13px",
    opacity: 0.7
  }}>
            Loaded {facetGroups.length} facet group(s) live from <code>GET /variable/get-variable-filter-data</code>. Select values below to build a <code>GET /variables</code> request.
          </div>

          {facetGroups.filter(g => g.id !== "organizations").map(group => <div key={group.id} style={{
    borderTop: "1px solid rgba(0,0,0,0.08)",
    paddingTop: "12px"
  }}>
              <div style={{
    fontWeight: 600,
    fontSize: "14px",
    marginBottom: "6px"
  }}>{group.label} <span style={{
    opacity: 0.5,
    fontWeight: 400
  }}>({group.id})</span></div>
              <div style={{
    display: "flex",
    flexWrap: "wrap",
    gap: "10px"
  }}>
                {(group.children || []).map(child => <label key={String(child.id)} style={{
    fontSize: "13px",
    display: "flex",
    alignItems: "center",
    gap: "4px",
    border: "1px solid rgba(0,0,0,0.15)",
    borderRadius: "999px",
    padding: "4px 10px"
  }}>
                    <input type="checkbox" checked={(selected[group.id] || []).includes(String(child.id))} onChange={() => toggleChild(group.id, child.id)} />
                    {child.label}
                  </label>)}
              </div>
            </div>)}

          <label style={{
    fontSize: "13px"
  }}>
            <input type="checkbox" checked={filterEncoding === "comma"} onChange={e => setFilterEncoding(e.target.checked ? "comma" : "repeated")} />
            {" "}Send each filter's selected values as one comma-separated param instead of repeated params (also unconfirmed — try both)
          </label>

          <div>
            <div style={{
    fontSize: "13px",
    fontWeight: 600,
    marginBottom: "4px"
  }}>Request preview</div>
            <pre style={{
    background: "rgba(0,0,0,0.05)",
    borderRadius: "8px",
    padding: "10px",
    fontSize: "12px",
    overflowX: "auto"
  }}>
              <code>GET {variablesUrl}</code>
            </pre>
          </div>

          <button onClick={runVariablesQuery} disabled={!token || variablesLoading} style={{
    padding: "8px 14px",
    borderRadius: "8px",
    border: "none",
    background: "#4fa1ab",
    color: "white",
    fontWeight: 600,
    cursor: token ? "pointer" : "not-allowed",
    width: "fit-content"
  }}>
            {variablesLoading ? "Running…" : "Run GET /variables"}
          </button>

          {variablesError && <div style={{
    color: "#b91c1c",
    fontSize: "13px"
  }}>Error: {variablesError}</div>}

          {variablesResult && <div>
              <div style={{
    fontSize: "13px",
    fontWeight: 600,
    marginBottom: "4px"
  }}>Response</div>
              <pre style={{
    background: "rgba(0,0,0,0.05)",
    borderRadius: "8px",
    padding: "10px",
    fontSize: "12px",
    overflowX: "auto",
    maxHeight: "320px"
  }}>
                <code>{JSON.stringify(variablesResult, null, 2)}</code>
              </pre>
            </div>}
        </div>}
    </div>;
};

<FilterVariablesPlayground />

## What this proves — and what it doesn't

This confirms the *shape* of a working filter flow: fetch facets live, let a user pick values, build a request from those picks. It does **not** confirm that `GET /variables` actually accepts `resourceType`, `pointType`, `buildingType`, `building`, `status`, and `dataReception` as query parameter names — those are still the same unconfirmed guesses flagged in [Discovery Endpoints](/api-reference/query/saved-queries). Use this tool against a real token to find out, then update this page's parameter names (and the static [`openapi.json`](/api-reference/introduction)) to match once confirmed.
