Skip to main content
Version: v1 (Current)

GxP Store

The GxP Store (gxpPortalConfigStore) is a Pinia store that provides reactive state management and platform integration for your plugin.

Importing the Store

import { useGxpStore } from '@gx-runtime/stores/gxpPortalConfigStore';

// In your component setup
const store = useGxpStore();

Store Sections

The store contains several reactive sections populated from your app-manifest.json and the platform:

SectionDescriptionSource
pluginVarsPlugin settings/configurationsettings in manifest
stringsListTranslatable stringsstrings.default in manifest
assetListAsset URLsassets in manifest
triggerStateDynamic runtime statetriggerState in manifest
dependencyListExternal dependenciesPlatform-injected
permissionFlagsGranted permissionsPlatform-injected
themePlatform theme colorsPlatform-injected
routerNavigation methodsPlatform-injected

Getter Methods

Use these methods to safely access store values with fallbacks:

getString(key, defaultValue)

Get a string from stringsList:

const title = store.getString('welcome_title', 'Welcome');
const button = store.getString('btn_submit', 'Submit');

getSetting(key, defaultValue)

Get a setting from pluginVars:

const color = store.getSetting('primary_color', '#000000');
const timeout = store.getSetting('idle_timeout', 30);
const enabled = store.getSetting('feature_enabled', false);

getAsset(key, defaultValue)

Get an asset URL from assetList:

const logo = store.getAsset('logo', '/fallback-logo.png');
const hero = store.getAsset('hero_image', '/placeholder.jpg');

getState(key, defaultValue)

Get a value from triggerState:

const step = store.getState('current_step', 1);
const isActive = store.getState('is_active', false);

hasPermission(permission)

Check if a permission is granted:

if (store.hasPermission('camera')) {
// Camera access is available
}

if (store.hasPermission('bluetooth')) {
// Bluetooth access is available
}

Update Methods

updateString(key, value)

Update a string value:

store.updateString('dynamic_message', 'Processing your request...');

updateSetting(key, value)

Update a setting value:

store.updateSetting('current_mode', 'advanced');

updateAsset(key, url)

Update an asset URL:

store.updateAsset('user_avatar', 'https://example.com/avatar.jpg');

updateState(key, value)

Update trigger state:

store.updateState('current_step', 2);
store.updateState('is_loading', true);
store.updateState('selected_item', { id: 123, name: 'Item' });

addDevAsset(key, filename)

Add a development asset with the dev server URL prefix:

// Automatically prefixes with dev server URL
store.addDevAsset('temp_image', 'screenshot.png');
// Result: https://localhost:3060/dev-assets/images/screenshot.png

Dependency API Client

The recommended way to make API calls is through the dependency system using callApi(). This method uses the operations defined in your app-manifest.json dependencies.

callApi(operationId, identifier, additionalData)

Call an API operation defined in your dependencies:

const store = useGxpStore();

// GET request - list resources
const items = await store.callApi('access-points.index', 'access_points');

// GET request - single resource (path parameter)
const item = await store.callApi('access-points.show', 'access_points', {
access_point: 123 // Path parameter
});

// POST request - create resource
const newItem = await store.callApi('access-points.store', 'access_points', {
name: 'Main Entrance',
location: 'Building A'
});

// PUT request - update resource
const updated = await store.callApi('access-points.update', 'access_points', {
access_point: 123, // Path parameter
name: 'Updated Name' // Body data
});

// DELETE request
await store.callApi('access-points.destroy', 'access_points', {
access_point: 123
});

Parameters:

ParameterTypeDescription
operationIdstringThe operation key from your dependency's operations object
identifierstringThe dependency identifier from app-manifest.json
additionalDataobjectPath parameters and/or request body data (optional)

Returns: response.data from the API response

How it works:

  1. Looks up the dependency by identifier in dependencyList
  2. Finds the operation by operationId in the dependency's operations
  3. Parses the method and path from the operation value (e.g., "get:/v1/...")
  4. Substitutes path parameters from additionalData (e.g., {access_point}123)
  5. Makes the HTTP request with remaining data as query params (GET) or body (POST/PUT)
  6. Returns response.data

Example with error handling:

try {
const accessPoints = await store.callApi('access-points.index', 'access_points');
console.log('Loaded', accessPoints.length, 'access points');
} catch (error) {
console.error('Failed to load access points:', error.message);
}
Define Dependencies First

Before using callApi, make sure you've added the dependency to your app-manifest.json. Use gxdev add-dependency to generate the configuration automatically.

Low-Level API Client

For direct API calls without the dependency system, use these methods:

apiGet(endpoint, params)

const response = await store.apiGet('/events/123');
const events = await store.apiGet('/events', { status: 'active' });

apiPost(endpoint, data)

const result = await store.apiPost('/checkin', {
attendee_id: 456,
timestamp: new Date().toISOString()
});

apiPut(endpoint, data)

await store.apiPut('/attendees/456', {
checked_in: true
});

apiDelete(endpoint)

await store.apiDelete('/sessions/789');

Socket.IO Integration

The store provides methods for real-time communication via Socket.IO:

emitSocket(channel, event, data)

Send a socket event:

store.emitSocket('primary', 'checkin-complete', {
attendee_id: 123,
badge_printed: true
});

listenSocket(channel, event, callback)

Listen for socket events:

store.listenSocket('primary', 'session-updated', (data) => {
console.log('Session updated:', data);
store.updateState('current_session', data);
});

useSocketListener(dependencyId, event, callback)

Set up a socket listener for a specific dependency:

store.useSocketListener('badge-printer', 'print-complete', (result) => {
if (result.success) {
store.updateState('badge_printing', false);
}
});

Reactive Usage in Templates

The store is fully reactive. Use it directly in your templates:

<template>
<div :style="{ backgroundColor: store.getSetting('bg_color', '#fff') }">
<h1>{{ store.getString('title', 'Default Title') }}</h1>

<p v-if="store.triggerState.is_loading">Loading...</p>

<div v-for="item in store.triggerState.items" :key="item.id">
{{ item.name }}
</div>
</div>
</template>

<script setup>
import { useGxpStore } from '@gx-runtime/stores/gxpPortalConfigStore';

const store = useGxpStore();
</script>

Watching Store Changes

Use Vue's watch to react to store changes:

import { watch } from 'vue';
import { useGxpStore } from '@gx-runtime/stores/gxpPortalConfigStore';

const store = useGxpStore();

// Watch a specific state value
watch(
() => store.triggerState.current_step,
(newStep, oldStep) => {
console.log(`Step changed from ${oldStep} to ${newStep}`);
}
);

// Watch multiple values
watch(
() => [store.triggerState.is_active, store.pluginVars.mode],
([isActive, mode]) => {
if (isActive && mode === 'kiosk') {
startKioskMode();
}
}
);

Computed Properties

Create computed properties based on store values:

import { computed } from 'vue';
import { useGxpStore } from '@gx-runtime/stores/gxpPortalConfigStore';

const store = useGxpStore();

const isReady = computed(() =>
!store.triggerState.is_loading &&
store.triggerState.data !== null
);

const formattedCount = computed(() =>
`${store.triggerState.checked_in_count} of ${store.pluginVars.total_expected}`
);

Theme Integration

Access platform theme values:

const store = useGxpStore();

// Theme colors
const primaryColor = store.theme?.primary || '#1976D2';
const backgroundColor = store.theme?.background || '#ffffff';

// Use in styles
const buttonStyle = computed(() => ({
backgroundColor: store.theme?.primary,
color: store.theme?.onPrimary
}));

Best Practices

  1. Use getters with defaults - Always provide fallback values
  2. Keep state updates atomic - Update one value at a time when possible
  3. Use computed for derived state - Don't duplicate logic
  4. Clean up listeners - Remove socket listeners when components unmount
  5. Avoid deep nesting - Keep triggerState relatively flat for reactivity