Skip to content

Frontend Library Usage Examples

Practical examples for using the @productifyfw/core library and its framework bindings (@productifyfw/vue, @productifyfw/react).

Basic Examples

User-Aware UI

typescript
import { ProductifyClient } from "@productifyfw/core";

const pfy = new ProductifyClient();

function showGreeting() {
  const user = pfy.getUser();
  if (user) {
    document.getElementById("greeting").textContent = `Welcome, ${user.name}!`;
  }
}

Feature Flag Usage

typescript
import { getProductifyClient } from "@productifyfw/core";

const pfy = getProductifyClient();

// Toggle feature based on a boolean configuration value
function initializeApp() {
  const useNewCheckout = pfy.isConfigEnabled("newCheckoutFlow");

  if (useNewCheckout) {
    import("./checkout-v2").then((m) => m.init());
  } else {
    import("./checkout-v1").then((m) => m.init());
  }
}

Dynamic Configuration

typescript
const pfy = new ProductifyClient();

// Get pagination settings
const itemsPerPage = pfy.getConfig<number>("itemsPerPage", {
  defaultValue: 20,
});
const enableInfiniteScroll = pfy.isConfigEnabled("infiniteScroll");

// Configure data grid
const gridConfig = {
  pageSize: itemsPerPage,
  infiniteScroll: enableInfiniteScroll,
};

Vue 3 Examples

Conditional Rendering

vue
<script setup lang="ts">
import { computed } from "vue";
import { useProductify } from "@productifyfw/vue";

const pfy = useProductify();

const canEdit = computed(() => pfy.isConfigEnabled("editingEnabled"));

const hasReports = computed(() => pfy.isModuleEnabled("reports"));
</script>

<template>
  <div class="content">
    <button v-if="canEdit" @click="edit">Edit</button>
    <ReportsWidget v-if="hasReports" />
  </div>
</template>

Settings-Based UI

vue
<script setup lang="ts">
import { computed } from "vue";
import { useProductify } from "@productifyfw/vue";

const pfy = useProductify();

const theme = computed(() => pfy.getDefaultTheme() ?? "light");
const locale = computed(() => pfy.getDefaultLocale() ?? "en");
const compactMode = computed(() => pfy.isConfigEnabled("compactView"));
</script>

<template>
  <div
    :class="['app', `theme-${theme}`, { compact: compactMode }]"
    :lang="locale"
  >
    <!-- Your app content -->
  </div>
</template>

User Profile Display

vue
<script setup lang="ts">
import { useProductify } from "@productifyfw/vue";

const pfy = useProductify();
const user = pfy.getUser();
const tenant = pfy.getTenant();
</script>

<template>
  <div v-if="user" class="user-profile">
    <img :src="`/avatars/${user.id}`" :alt="user.name" />
    <div>
      <h3>{{ user.name }}</h3>
      <p>{{ user.email }}</p>
      <span v-if="tenant" class="badge">{{ tenant.name }}</span>
    </div>
  </div>
</template>

Language Switcher

vue
<script setup lang="ts">
import { ref } from "vue";
import { createI18nComposable } from "@productifyfw/vue";
import { i18n } from "../plugins/i18n";

const useProductifyI18n = createI18nComposable(i18n);
const { changeLocale, enabledLocales, defaultLocale } = useProductifyI18n();

const selected = ref(defaultLocale);

async function switchLanguage() {
  await changeLocale(selected.value);
}
</script>

<template>
  <select v-model="selected" @change="switchLanguage">
    <option v-for="loc in enabledLocales" :key="loc" :value="loc">
      {{ loc.toUpperCase() }}
    </option>
  </select>
</template>

React Examples

Feature-Gated Route

typescript
import { Navigate } from "react-router-dom";
import { useProductify } from "@productifyfw/react";

interface FeatureRouteProps {
  children: React.ReactNode;
  requiredModule?: string;
  requiredConfig?: string;
}

function FeatureRoute({
  children,
  requiredModule,
  requiredConfig,
}: FeatureRouteProps) {
  const pfy = useProductify();

  const hasAccess = requiredModule
    ? pfy.isModuleEnabled(requiredModule)
    : requiredConfig
    ? pfy.isConfigEnabled(requiredConfig)
    : true;

  if (!hasAccess) {
    return <Navigate to="/not-available" replace />;
  }

  return <>{children}</>;
}

// Usage
<Route
  path="/reports"
  element={
    <FeatureRoute requiredModule="reports">
      <ReportsDashboard />
    </FeatureRoute>
  }
/>;

Settings Hook

typescript
import { useMemo } from "react";
import { useProductify, useProductifyConfig } from "@productifyfw/react";

function useAppSettings() {
  const pfy = useProductify();
  const itemsPerPage = useProductifyConfig<number>("itemsPerPage", {
    defaultValue: 20,
  });

  return useMemo(
    () => ({
      theme: pfy.getDefaultTheme() ?? "light",
      locale: pfy.getDefaultLocale() ?? "en",
      itemsPerPage,
      enableAnimations: pfy.isConfigEnabled("animations"),
      debugMode: pfy.isConfigEnabled("debug"),
    }),
    [pfy, itemsPerPage]
  );
}

// Usage in component
function MyComponent() {
  const settings = useAppSettings();

  return (
    <div className={`theme-${settings.theme}`}>
      {settings.debugMode && <DebugPanel />}
      <ItemList itemsPerPage={settings.itemsPerPage} />
    </div>
  );
}

Conditional Feature Rendering

typescript
import { useProductify } from "@productifyfw/react";

function Dashboard() {
  const pfy = useProductify();

  const showNewFeature = pfy.isConfigEnabled("betaFeatures");
  const hasAnalytics = pfy.isModuleEnabled("analytics");

  return (
    <div>
      <h1>Dashboard</h1>

      {showNewFeature && <NewFeatureWidget />}

      {hasAnalytics && <AnalyticsDashboard />}
    </div>
  );
}

Advanced Examples

Module Availability Check

typescript
import { getProductifyClient } from "@productifyfw/core";

const pfy = getProductifyClient();

class ModuleManager {
  async loadModule(name: string) {
    if (!pfy.isModuleEnabled(name)) {
      console.warn(`Module ${name} not enabled`);
      return null;
    }

    // Enabled modules are formatted "name@version"
    const entry = pfy
      .getEnabledModules()
      .find((m) => m.split("@")[0] === name);
    console.log(`Loading ${entry}`);

    // Load module dynamically
    const module = await import(`./modules/${name}`);
    return module.default;
  }
}

// Usage
const manager = new ModuleManager();
const checkout = await manager.loadModule("checkout");
if (checkout) {
  checkout.initialize();
}

Multi-Tenant Theme Switcher

typescript
import { ProductifyClient } from "@productifyfw/core";

class ThemeManager {
  private currentTheme: string | null = null;
  private pfy: ProductifyClient;

  constructor() {
    this.pfy = new ProductifyClient({
      onChange: () => this.applyTheme(),
    });
  }

  initialize() {
    this.applyTheme();
  }

  private applyTheme() {
    const theme = this.pfy.getDefaultTheme();
    const tenantId = this.pfy.getTenantId();

    if (!theme || !tenantId || theme === this.currentTheme) return;

    // Load CSS variables
    import(`./themes/${tenantId}/${theme}.css`);

    document.documentElement.setAttribute("data-theme", theme);
    document.documentElement.setAttribute("data-tenant", tenantId);

    this.currentTheme = theme;
  }

  // Call pfy.refresh() when configuration may have changed at runtime;
  // onChange fires only when the content actually differs
  refresh() {
    this.pfy.refresh();
  }
}

Feature Flag Rollout

typescript
import { getProductifyClient } from "@productifyfw/core";

const pfy = getProductifyClient();

class FeatureFlagManager {
  isFeatureEnabled(featureName: string): boolean {
    // Check global flag
    const globalEnabled = pfy.isConfigEnabled(featureName);
    if (!globalEnabled) return false;

    // Check percentage rollout
    const rolloutPercentage = pfy.getConfig<number>(`${featureName}_rollout`, {
      defaultValue: 100,
    });

    const userId = pfy.getUserId();
    if (!userId) return false;

    // Simple hash-based percentage check
    const hash = this.hashUserId(userId);
    return hash % 100 < (rolloutPercentage ?? 0);
  }

  private hashUserId(userId: string): number {
    let hash = 0;
    for (let i = 0; i < userId.length; i++) {
      hash = (hash << 5) - hash + userId.charCodeAt(i);
      hash = hash & hash;
    }
    return Math.abs(hash);
  }
}

// Usage
const flags = new FeatureFlagManager();

if (flags.isFeatureEnabled("newCheckout")) {
  // Use new checkout flow
}

Testing Examples

Mock Setup for Unit Tests

typescript
// test-utils.ts
import type { ProductifyFrontendConfig } from "@productifyfw/core";

export function mockProductifyConfig(
  overrides?: Partial<ProductifyFrontendConfig>
) {
  const defaultConfig: ProductifyFrontendConfig = {
    user: {
      id: "test-user-id",
      email: "test@example.com",
      name: "Test User",
      username: "testuser",
    },
    tenant: {
      id: "test-tenant",
      name: "Test Tenant",
    },
    application: {
      id: "test-app-id",
    },
    auth_type: "oauth",
    config: {
      enabled_modules: [],
      default_theme: "light",
      default_locale: "en",
    },
  };

  window.__PRODUCTIFY__ = { ...defaultConfig, ...overrides };
}

export function clearProductifyConfig() {
  delete window.__PRODUCTIFY__;
}

Component Test with Mock

typescript
// MyComponent.test.ts
import { describe, it, expect, beforeEach, afterEach } from "vitest";
import { mount } from "@vue/test-utils";
import { resetProductifyClient } from "@productifyfw/core";
import { mockProductifyConfig, clearProductifyConfig } from "./test-utils";
import MyComponent from "./MyComponent.vue";

describe("MyComponent", () => {
  beforeEach(() => {
    mockProductifyConfig({
      config: {
        enabled_modules: ["reports@1.0.0"],
        maxItems: 50,
      },
    });
    resetProductifyClient(); // Reset the singleton between tests
  });

  afterEach(() => {
    clearProductifyConfig();
  });

  it("shows reports widget when the module is enabled", () => {
    const wrapper = mount(MyComponent);
    expect(wrapper.find(".reports-widget").exists()).toBe(true);
  });

  it("uses settings from config", () => {
    const wrapper = mount(MyComponent);
    expect(wrapper.vm.maxItems).toBe(50);
  });
});

Error Handling Examples

Graceful Degradation

typescript
import { ProductifyClient } from "@productifyfw/core";

function initializeApp() {
  const pfy = new ProductifyClient();

  if (!pfy.isConfigured()) {
    console.warn("Running without Productify configuration");

    // Use fallback configuration
    return {
      theme: "light",
      locale: "en",
      itemsPerPage: 20,
      isAuthenticated: false,
    };
  }

  return {
    theme: pfy.getDefaultTheme() ?? "light",
    locale: pfy.getDefaultLocale() ?? "en",
    itemsPerPage: pfy.getConfig<number>("itemsPerPage", { defaultValue: 20 }),
    isAuthenticated: pfy.getUserId() !== null,
  };
}

Safe Configuration Access

typescript
function getAppConfig(pfy: ProductifyClient) {
  const safeConfig = <T>(key: string, defaultValue: T): T => {
    try {
      return pfy.getConfig<T>(key, { defaultValue }) ?? defaultValue;
    } catch (error) {
      console.error(`Error reading configuration ${key}:`, error);
      return defaultValue;
    }
  };

  return {
    theme: safeConfig("theme", "light"),
    maxItems: safeConfig("maxItemsPerPage", 20),
    enableCache: safeConfig("enableCache", true),
  };
}