XYLEX Group

Flags

Flags and remote config

This app uses two mechanisms:

  • Vercel Web Analytics (SDK component) gated via config
  • Internal, DB-backed feature flags with an admin UI and a client hook

Vercel Web Analytics (SDK)

Rendered in the root layout when enabled:

{APP_CONFIG.analytics.vercel_web_analytics_enabled && (
  <Analytics />
)}
```typescript

Enable via config/env:

```typescript
analytics: {
  vercel_web_analytics_enabled:
    process.env.NEXT_PUBLIC_VERCEL_WEB_ANALYTICS_ENABLED || false,
}
```typescript

Set `NEXT_PUBLIC_VERCEL_WEB_ANALYTICS_ENABLED=true` to include the `<Analytics />` component.

### Internal feature flags (DB-backed)

These flags are first-class in the app, managed per-user via an admin page and exposed via a React hook.

#### Canonical flags

Add new flags to the enum:

```typescript
export enum FeatureFlagName {
 SUITS_CONNECT_ACCESS = "suits_connect_access",
}
```typescript

Current flags:

- `suits_connect_access`

#### Database schema

```sql
create table if not exists public.feature_flags (
 id bigint generated by default as identity not null,
 created_at timestamp with time zone not null default now(),
 user_id text null,
 name text null,
 description text null,
 enabled boolean null default false,
 flag text null,
 feature_flag_id uuid null default gen_random_uuid(),
 constraint feature_flags_pkey primary key (id),
 constraint feature_flags_user_id_fkey foreign key (user_id) references users (user_id)
) tablespace pg_default;

create index if not exists feature_flags_user_id_idx on public.feature_flags (user_id);
create index if not exists feature_flags_flag_idx on public.feature_flags (flag);
```typescript

The client reads from a view `public.v_flags` that should return a single row for the current user containing a `flags` column (JSON array or stringified array). Ensure your database creates/maintains this view.

#### Client hook

Use the hook to gate UI and logic:

```typescript
export function useFeatureFlags() {
 const { user } = useUserStore();
 const [cachedFlags, setCachedFlags] = useState<Set<string>>(() => {
  try {
   if (typeof window === "undefined") return new Set();
   const key = user?.user_id ? `flags:${user.user_id}` : null;
   if (!key) return new Set();
   const raw = window.localStorage.getItem(key);
```typescript

```typescript
 const effective = enabledFlags.size > 0 ? enabledFlags : cachedFlags;

 const hasFlag = useCallback((flag: string) => effective.has(flag), [effective]);

 return { isLoading, hasFlag };
```typescript

Example usage:

```tsx
import { useFeatureFlags } from "@/hooks/use-feature-flags";
import { FeatureFlagName } from "@/lib/enums/feature-flags";

export function SuitsConnectButton() {
  const { hasFlag } = useFeatureFlags();
  if (!hasFlag(FeatureFlagName.SUITS_CONNECT_ACCESS)) return null;
  return <button className="rounded-sm">Suits Connect</button>;
}
```typescript

#### Admin UI

Manage user flags at `app/admin/feature-flags`:

```tsx
"use client";

import { Plus } from "lucide-react";
import { useCallback, useState } from "react";
import { ErrorBoundary } from "react-error-boundary";
import PageHeader from "@/components/layouts/blocks/page-title";
import { Button } from "@/components/ui/button";
import AddFeatureFlagDialog from "@/features/feature-flags/components/add-feature-flag-dialog";
import FeatureFlagTable from "@/features/feature-flags/components/feature-flag-table";

export default function FeatureFlagsPage() {
```typescript

This page supports searching users, creating/updating flags, and deleting flags.

### Adding a new flag end-to-end

## File Structure

```files
lib/
└── enums/
    └── feature-flags.ts
database/
└── views/
    └── v_flags.sql

Steps

  • Add the name to lib/enums/feature-flags.ts
  • Ensure public.v_flags includes it for the appropriate users
  • Gate UI/logic with useFeatureFlags().hasFlag("my_flag")
  • Use the admin page to assign flags to users

Notes

  • Use the DB-backed flags for app logic and access control.
  • Keep the enum authoritative; avoid magic strings in feature checks.

Unified flags via Flags SDK (server + client)

Flags resolve from the DB (public.v_flags). Use a single API on the server and an optional client hook.

Server (RSC/route/middleware):

import { createFeatureFlag } from "@/lib/flags";

export default async function Page() {
  const isOn = await createFeatureFlag("my_first_gate")(); // resolves via DB
  return <div>myFeatureFlag is {isOn ? "on" : "off"}</div>;
}
```typescript

Client (hook):

```typescript
import { useUnifiedFlag } from "@/hooks/use-unified-flag";

export function MyButton() {
  const { enabled } = useUnifiedFlag("my_first_gate");
  if (!enabled) return null;
  return <button className="rounded-sm">Enabled</button>;
}
```typescript

Notes:

- The DB adapter uses the `fetch/data` API and the `public.v_flags` view. Ensure it returns `flags` for the current user.