Feedback
Insight Chart Service
Overview
What is Insight?
At Feedier, Insights are specialized charts used across the platform. Each Insight comes with its own logic and configuration, adapted to provide specific graphical overviews. They are mainly used in reports or the activity page, where visual representation of data is essential.
Why using a Insight Service?
The purpose of the Insight Service is to centralize the logic and rendering of these charts. This improves consistency, simplifies reuse, and allows for easier maintenance of complex charting logic across the platform.
Use the Insight Service
Context
Currently, only two charts make use of the Insight Service: OperationalKPI and Benchmark. Eventually, the goal is to migrate all complex charts to this centralized system, especially those whose logic can be reused elsewhere.
How to use it
Backend Side
To use the Insight Service in the backend, you must call the factory for the specific type of Insight you want to generate. Due to the varying complexity of each Insight type, there is no generic factory.
Below is an example of how to use the service in a controller returning data via Inertia (this would also work in an API context):
<?php
namespace App\Http\Controllers;
use App\Services\Insights\Components\Benchmark\BenchmarkFactory;
use App\Services\Insights\Configurations\BenchmarkTypeEnum;
use Illuminate\Http\Request;
use Inertia\Response;
class MyController extends Controller
{
public function index(Request $request): Response
{
$benchmark = BenchmarkFactory::make(
BenchmarkTypeEnum::BAR
)->setDefaultConfigurations();
return inertia()->render('Insight/Demo', [
'insightData' => $benchmark->results(),
]);
}
}The results() method returns an array with the following keys:
type: The type of chart returned (e.g., 'bar', 'line', etc.)configuration: The configuration used to generate the chart. In some cases, when no editing interface is available, this is used to generate specific chart components.results: The computed and generated data based on the Insight type and configuration.
Note: If no configuration is provided—or if it's incomplete—errors may occur when generating the chart.
Configuration
Each Insight extends the base Insight class, which includes a InsightConfiguration object. This configuration allows you to define key-value pairs required to render the chart correctly.
The keys must use the appropriate Enum associated with the Insight type. You'll find all the available configuration options in these enums.
Here’s an example of manually setting Insight configurations:
$benchmark = BenchmarkFactory::make(
BenchmarkTypeEnum::BAR
);
$benchmark->configurations()->set(BenchmarkEnum::KPI, 'nps');
$benchmark->configurations()->set(BenchmarkEnum::BREAKDOWN, BenchmarkBreakdownEnum::FILTERS->value);
$benchmark->configurations()->set(BenchmarkEnum::TITLE, __('kpi.nps'));
$benchmark->configurations()->set(BenchmarkEnum::QUESTIONS, []);
$fql = (new Fql)
->addSelector(new TimePeriodSelector('$eq', 'date_last_month', 'and'))
->addSelector(new TimePeriodSelector('$eq', 'date_last_quarter', 'and'))
->getFqls();
$benchmark->configurations()->set(BenchmarkEnum::VALUES, json_encode($fql));
$benchmark->configurations()->set(BenchmarkEnum::TIMELINE, 'date_last_year');
return $benchmark->results(),Frontend side
On the frontend, the Insights directory contains all the visual components related to chart rendering. The frontend consumes the backend data and dynamically builds the chart using a dedicated manager.
Rendering an Insight Component
To render an Insight component, use the InsightManager. This manager handles the component selection and associated design classes.
You must pass the following props:
is: The component name to render.classes: The design classes applied to the chart. By default, it uses predefined classes.insightData: The data returned by the backend's->results()method.
<script setup>
import { ref } from 'vue';
import { InsightManager } from '@/Components/Insights/InsightManager.ts';
defineProps({
insightData: Object,
});
const insightManager = ref(new InsightManager('benchmark'));
</script>
<template>
<component
v-if="insightData"
:is="insightManager.component()"
:classes="insightManager.classes()"
:insightData="insightData"
/>
</template>Custom design
You can override the default design classes to customize the look and feel of the chart. All available class definitions can be found in the InsightComponents configuration file located at:
resources/js/Components/Insights/InsightComponents
Here’s an example of customizing the design:
<script setup>
import { ref } from 'vue';
import { InsightManager } from '@/Components/Insights/InsightManager.ts';
defineProps({
insightData: Object,
});
const insightManager = ref(new InsightManager('benchmark'));
const initializeClasses = () => {
insightManager.value.overrideClasses('title', ['text-6xl', 'font-bold']);
insightManager.value.overrideClasses('chart', [props.component.width === 2 ? 'w-[800px]!' : 'w-[600px]!']);
insightManager.value.overrideClasses('comparison', ['text-base']);
insightManager.value.overrideClasses('valueColor', [props.colors[0]]);
insightManager.value.overrideClasses('direction', ['flex-col']);
};
onMounted(() => {
initializeClasses();
});
</script>
<template>
<component
v-if="insightData"
:is="benchmark.component()"
:classes="benchmark.classes()"
:insightData="insightData"
/>
</template>Custom options
Besides styling, you can also define custom options to further tailor the chart behavior. These options go beyond CSS—they allow you to configure layout, responsiveness, value limits, and more.
These should not alter the default behavior of the Insight but provide additional flexibility for specific cases.
Use the ComponentOptionsManager to define options:
<script setup>
import { ref } from 'vue';
import { InsightManager } from '@/Components/Insights/InsightManager.ts';
import ComponentOptionsManager from '@/Components/Insights/ComponentOptionsManager.ts';
defineProps({
insightData: Object,
});
const insightManager = ref(new InsightManager('benchmark'));
const configurationOptionsManager = ref(new ComponentOptionsManager());
const initializeConfiguration = () => {
configurationOptionsManager.value.set('component', props.component);
configurationOptionsManager.value.set('componentFql', props.component.fql);
configurationOptionsManager.value.set('responsive', false);
configurationOptionsManager.value.set('limitValues', 50);
};
onMounted(() => {
initializeConfiguration();
});
</script>
<template>
<component
v-if="insightData"
:is="insightManager.component()"
:classes="insightManager.classes()"
:insightData="insightData"
:configurations="configurationOptionsManager"
/>
</template>