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