insight-react-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,147 @@
1
+ # Insight React SDK
2
+
3
+ A lightweight, extensible **React frontend SDK** for rendering analytical insights such as **trend** and **contributor** views from raw metric data.
4
+ The SDK abstracts data transformation, time handling, and visualization selection to provide a clean, developer friendly API.
5
+
6
+
7
+ ## Features
8
+
9
+ - **Single public API**: `<Insight />`
10
+ - **Backend-agnostic** via resolver functions
11
+ - SDK-owned **time range calculation**
12
+ - Dedicated **data transformation layer**
13
+ - Automatic **chart selection** based on insight type
14
+ - Clear separation of responsibilities:
15
+ - data fetching
16
+ - transformation
17
+ - visualization
18
+ - Built-in loading, error, and empty states
19
+ - Easy to extend with new insight types
20
+
21
+ ---
22
+
23
+ ## Installation
24
+
25
+ ```
26
+ npm install insight-react-sdk
27
+ ```
28
+ ## usage
29
+
30
+ ### Step 1: Import the Insight component
31
+ ```
32
+ import { Insight } from "insight-react-sdk";
33
+ ```
34
+
35
+ ### Step 2: Implement a data resolver
36
+
37
+ The resolver is responsible for returning raw metric data.
38
+ The SDK handles everything else.
39
+
40
+ ```
41
+
42
+ const dataResolver = async (metric, grain, fromTime, toTime) => {
43
+ return [
44
+ { fromtime: "01-01-2025", totime: "07-01-2025", Revenue: 120 },
45
+ { fromtime: "08-01-2025", totime: "14-01-2025", Revenue: 180 },
46
+ ];
47
+ };
48
+ ```
49
+
50
+ ### Step 3: Render a Trend Insight
51
+ ```
52
+ export default function TrendExample() {
53
+ return (
54
+ <Insight
55
+ type="trend"
56
+ metric="Revenue"
57
+ timeGrain="weekly"
58
+ timeRange={30}
59
+ dataResolver={dataResolver}
60
+ />
61
+ );
62
+ }
63
+ ```
64
+
65
+ ### Step 4: Render a Contributor Insight
66
+
67
+ For contributor insights, provide a dimension and a dimension values resolver.
68
+ ```
69
+ const contributorDataResolver = async (metric, grain, fromTime, toTime) => {
70
+ return [
71
+ { fromtime: "01-01-2025", totime: "07-01-2025", India: 60, USA: 40 },
72
+ { fromtime: "08-01-2025", totime: "14-01-2025", India: 30, USA: 70 },
73
+ ];
74
+ };
75
+ ```
76
+ ```
77
+ const dimensionValuesResolver = async () => {
78
+ return ["India", "USA"];
79
+ };
80
+ ```
81
+ ```
82
+ export default function ContributorExample() {
83
+ return (
84
+ <Insight
85
+ type="contributor"
86
+ metric="Revenue"
87
+ dimension="location"
88
+ timeGrain="weekly"
89
+ timeRange={30}
90
+ dataResolver={contributorDataResolver}
91
+ dimensionValuesResolver={dimensionValuesResolver}
92
+ />
93
+ );
94
+ }
95
+ ```
96
+ ## Insight Types
97
+
98
+ ### Trend Insight
99
+
100
+ - Displays how a single metric changes over time
101
+
102
+ - Uses line/area visualization
103
+
104
+ - X-axis represents time
105
+
106
+ - Y-axis represents the metric value
107
+
108
+ ### Contributor Insight
109
+
110
+ - Displays how different dimension values contribute to a metric
111
+
112
+ - Uses stacked bar visualization
113
+
114
+ - Each dimension value is rendered as a separate series
115
+
116
+
117
+ ## Time Handling
118
+
119
+ - timeRange is specified in days
120
+
121
+ - The SDK calculates fromTime and toTime internally
122
+
123
+ - timeGrain controls aggregation semantics
124
+
125
+ - Monthly insights display labels as Jan 2025, Feb 2025, etc.
126
+
127
+ - Daily and weekly labels are passed through as provided
128
+
129
+ ## Extensibility
130
+
131
+ - New insight types can be added by:
132
+
133
+ - Creating a new transformer
134
+
135
+ - Creating a corresponding chart
136
+
137
+ - Registering both internally
138
+
139
+ - No changes are required in the public API.
140
+
141
+ ## Notes
142
+
143
+ - The SDK does not perform data fetching directly
144
+
145
+ - The consuming application controls data sources
146
+
147
+ - No external date or chart configuration libraries are required
@@ -0,0 +1,45 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ type InsightType = "trend" | "contributor";
4
+ type TimeGrain = "daily" | "weekly" | "monthly";
5
+ interface BaseInsightProps {
6
+ metric: string;
7
+ timeGrain: TimeGrain;
8
+ timeRange: number;
9
+ }
10
+ type DataResolver = (metric: string, grain: TimeGrain, fromTime: Date, toTime: Date) => Promise<Record<string, any>[]>;
11
+ type DimensionValuesResolver = (metric: string, dimension: string) => Promise<string[]>;
12
+ interface InsightProps extends BaseInsightProps {
13
+ type: InsightType;
14
+ dimension?: string;
15
+ dataResolver: DataResolver;
16
+ dimensionValuesResolver?: DimensionValuesResolver;
17
+ }
18
+ interface TimeRange {
19
+ fromTime: Date;
20
+ toTime: Date;
21
+ }
22
+ interface TrendPoint {
23
+ x: string;
24
+ y: number;
25
+ }
26
+ interface ChartProps {
27
+ title: string;
28
+ }
29
+ interface ContributorSeries {
30
+ id: string;
31
+ label: string;
32
+ color: string;
33
+ }
34
+ interface TrendChartProps extends ChartProps {
35
+ data: TrendPoint[];
36
+ }
37
+ interface ContributorChartProps extends ChartProps {
38
+ data: Record<string, number | string>[];
39
+ series: ContributorSeries[];
40
+ }
41
+ type TransformedInsightData = TrendChartProps | ContributorChartProps;
42
+
43
+ declare function Insight(props: InsightProps): react_jsx_runtime.JSX.Element;
44
+
45
+ export { type BaseInsightProps, type ChartProps, type ContributorChartProps, type ContributorSeries, type DataResolver, type DimensionValuesResolver, Insight, type InsightProps, type InsightType, type TimeGrain, type TimeRange, type TransformedInsightData, type TrendChartProps, type TrendPoint };
@@ -0,0 +1,45 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ type InsightType = "trend" | "contributor";
4
+ type TimeGrain = "daily" | "weekly" | "monthly";
5
+ interface BaseInsightProps {
6
+ metric: string;
7
+ timeGrain: TimeGrain;
8
+ timeRange: number;
9
+ }
10
+ type DataResolver = (metric: string, grain: TimeGrain, fromTime: Date, toTime: Date) => Promise<Record<string, any>[]>;
11
+ type DimensionValuesResolver = (metric: string, dimension: string) => Promise<string[]>;
12
+ interface InsightProps extends BaseInsightProps {
13
+ type: InsightType;
14
+ dimension?: string;
15
+ dataResolver: DataResolver;
16
+ dimensionValuesResolver?: DimensionValuesResolver;
17
+ }
18
+ interface TimeRange {
19
+ fromTime: Date;
20
+ toTime: Date;
21
+ }
22
+ interface TrendPoint {
23
+ x: string;
24
+ y: number;
25
+ }
26
+ interface ChartProps {
27
+ title: string;
28
+ }
29
+ interface ContributorSeries {
30
+ id: string;
31
+ label: string;
32
+ color: string;
33
+ }
34
+ interface TrendChartProps extends ChartProps {
35
+ data: TrendPoint[];
36
+ }
37
+ interface ContributorChartProps extends ChartProps {
38
+ data: Record<string, number | string>[];
39
+ series: ContributorSeries[];
40
+ }
41
+ type TransformedInsightData = TrendChartProps | ContributorChartProps;
42
+
43
+ declare function Insight(props: InsightProps): react_jsx_runtime.JSX.Element;
44
+
45
+ export { type BaseInsightProps, type ChartProps, type ContributorChartProps, type ContributorSeries, type DataResolver, type DimensionValuesResolver, Insight, type InsightProps, type InsightType, type TimeGrain, type TimeRange, type TransformedInsightData, type TrendChartProps, type TrendPoint };
package/dist/index.js ADDED
@@ -0,0 +1,286 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ Insight: () => Insight
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+
27
+ // src/core/errors.ts
28
+ var SDKError = class extends Error {
29
+ constructor(message) {
30
+ super(message);
31
+ this.name = "SDKError";
32
+ }
33
+ };
34
+ var ValidationError = class extends SDKError {
35
+ constructor(message) {
36
+ super(message);
37
+ this.name = "ValidationError";
38
+ }
39
+ };
40
+
41
+ // src/utils/validation.ts
42
+ function validateInsightProps(props) {
43
+ if (!props.metric) {
44
+ throw new ValidationError("metric is required");
45
+ }
46
+ if (props.type === "contributor" && !props.dimension) {
47
+ throw new ValidationError(
48
+ "dimension is required when type is 'contributor'"
49
+ );
50
+ }
51
+ if (!props.dataResolver) {
52
+ throw new ValidationError("dataResolver is required");
53
+ }
54
+ }
55
+
56
+ // src/hooks/useTimeRange.ts
57
+ var import_react = require("react");
58
+
59
+ // src/utils/time.ts
60
+ var calculateTimeRange = (days) => {
61
+ const toTime = /* @__PURE__ */ new Date();
62
+ const fromTime = /* @__PURE__ */ new Date();
63
+ fromTime.setDate(toTime.getDate() - days);
64
+ return { fromTime, toTime };
65
+ };
66
+ function formatTimeLabel(rawDate, grain) {
67
+ if (grain !== "monthly") {
68
+ return rawDate;
69
+ }
70
+ const [day, month, year] = rawDate.split("-");
71
+ const isoDate = `${year}-${month}-${day}`;
72
+ const date = new Date(isoDate);
73
+ return date.toLocaleString("en-US", {
74
+ month: "short",
75
+ year: "numeric"
76
+ });
77
+ }
78
+
79
+ // src/hooks/useTimeRange.ts
80
+ function useTimeRange(timeRange) {
81
+ return (0, import_react.useMemo)(() => calculateTimeRange(timeRange), [timeRange]);
82
+ }
83
+
84
+ // src/hooks/useInsightData.ts
85
+ var import_react2 = require("react");
86
+
87
+ // src/transformers/contributor.transformer.ts
88
+ var DEFAULT_COLORS = ["#9acbdc", "#f6b1ac", "#8da2fb", "#1f3b63"];
89
+ var transformContributorData = (raw, ctx) => {
90
+ const data = raw.map((row) => {
91
+ const point = {
92
+ x: formatTimeLabel(row.fromtime, ctx.timeGrain)
93
+ };
94
+ ctx.dimensionValues.forEach((dim) => {
95
+ var _a;
96
+ point[dim] = (_a = row[dim]) != null ? _a : 0;
97
+ });
98
+ return point;
99
+ });
100
+ const series = ctx.dimensionValues.map((dim, index) => ({
101
+ id: dim,
102
+ label: dim,
103
+ color: DEFAULT_COLORS[index % DEFAULT_COLORS.length]
104
+ }));
105
+ return {
106
+ title: `${ctx.metric} by ${ctx.dimension}`,
107
+ data,
108
+ series
109
+ };
110
+ };
111
+
112
+ // src/transformers/trend.transformer.ts
113
+ var transformTrendData = (raw, ctx) => {
114
+ return {
115
+ title: ctx.metric,
116
+ data: raw.map((bucket) => ({
117
+ x: formatTimeLabel(bucket.fromtime, ctx.timeGrain),
118
+ y: bucket[ctx.metric]
119
+ }))
120
+ };
121
+ };
122
+
123
+ // src/registry/transformerRegistry.ts
124
+ var transformerRegistry = {
125
+ trend: transformTrendData,
126
+ contributor: transformContributorData
127
+ };
128
+
129
+ // src/hooks/useInsightData.ts
130
+ function useInsightData(props, fromTime, toTime) {
131
+ const [data, setData] = (0, import_react2.useState)(null);
132
+ const [loading, setLoading] = (0, import_react2.useState)(true);
133
+ const [error, setError] = (0, import_react2.useState)(null);
134
+ (0, import_react2.useEffect)(() => {
135
+ let mounted = true;
136
+ async function run() {
137
+ try {
138
+ setLoading(true);
139
+ setError(null);
140
+ const rawData = await props.dataResolver(
141
+ props.metric,
142
+ props.timeGrain,
143
+ fromTime,
144
+ toTime
145
+ );
146
+ let dimensionValues = [];
147
+ if (props.type === "contributor" && props.dimensionValuesResolver) {
148
+ dimensionValues = await props.dimensionValuesResolver(
149
+ props.metric,
150
+ props.dimension
151
+ );
152
+ }
153
+ const transformer = transformerRegistry[props.type];
154
+ const transformed = transformer(rawData, {
155
+ metric: props.metric,
156
+ dimension: props.dimension,
157
+ dimensionValues,
158
+ timeGrain: props.timeGrain
159
+ });
160
+ if (mounted) setData(transformed);
161
+ } catch (e) {
162
+ if (mounted) setError(e);
163
+ } finally {
164
+ if (mounted) setLoading(false);
165
+ }
166
+ }
167
+ run();
168
+ return () => {
169
+ mounted = false;
170
+ };
171
+ }, [
172
+ props.type,
173
+ props.metric,
174
+ props.dimension,
175
+ props.timeGrain,
176
+ props.dataResolver,
177
+ props.dimensionValuesResolver,
178
+ fromTime,
179
+ toTime
180
+ ]);
181
+ return { data, loading, error };
182
+ }
183
+
184
+ // src/states/loading.tsx
185
+ var import_jsx_runtime = require("react/jsx-runtime");
186
+ var Loading = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: "Loading insight\u2026" });
187
+
188
+ // src/states/error.tsx
189
+ var import_jsx_runtime2 = require("react/jsx-runtime");
190
+ var ErrorState = ({ error }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
191
+ "Error: ",
192
+ error.message
193
+ ] });
194
+
195
+ // src/states/empty.tsx
196
+ var import_jsx_runtime3 = require("react/jsx-runtime");
197
+ var Empty = () => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { children: "No data available" });
198
+
199
+ // src/charts/contributorChart.tsx
200
+ var import_recharts = require("recharts");
201
+ var import_jsx_runtime4 = require("react/jsx-runtime");
202
+ function ContributorChart({
203
+ title,
204
+ data,
205
+ series
206
+ }) {
207
+ if (!data.length || !series.length) return null;
208
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
209
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { style: { marginBottom: 8 }, children: title }),
210
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_recharts.ResponsiveContainer, { width: "100%", minHeight: 300, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_recharts.BarChart, { data, children: [
211
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false }),
212
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_recharts.XAxis, { dataKey: "x" }),
213
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_recharts.YAxis, {}),
214
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_recharts.Tooltip, {}),
215
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_recharts.Legend, {}),
216
+ series.map((s) => {
217
+ var _a;
218
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
219
+ import_recharts.Bar,
220
+ {
221
+ dataKey: s.id,
222
+ stackId: "total",
223
+ fill: s.color,
224
+ name: (_a = s.label) != null ? _a : s.id
225
+ },
226
+ s.id
227
+ );
228
+ })
229
+ ] }) })
230
+ ] });
231
+ }
232
+
233
+ // src/charts/trendChart.tsx
234
+ var import_recharts2 = require("recharts");
235
+ var import_jsx_runtime5 = require("react/jsx-runtime");
236
+ function TrendChart({ title, data }) {
237
+ if (!data.length) return null;
238
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
239
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h3", { style: { marginBottom: 8 }, children: title }),
240
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_recharts2.ResponsiveContainer, { width: "100%", minHeight: 300, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_recharts2.AreaChart, { data, children: [
241
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("linearGradient", { id: "trendGradient", x1: "0", y1: "0", x2: "0", y2: "1", children: [
242
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("stop", { offset: "0%", stopColor: "#1e3a8a", stopOpacity: 0.25 }),
243
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("stop", { offset: "100%", stopColor: "#1e3a8a", stopOpacity: 0 })
244
+ ] }) }),
245
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_recharts2.CartesianGrid, { strokeDasharray: "3 3", vertical: false }),
246
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_recharts2.XAxis, { dataKey: "x" }),
247
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_recharts2.YAxis, {}),
248
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_recharts2.Tooltip, {}),
249
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
250
+ import_recharts2.Area,
251
+ {
252
+ type: "monotone",
253
+ dataKey: "y",
254
+ stroke: "#1e3a8a",
255
+ strokeWidth: 2,
256
+ fill: "url(#trendGradient)",
257
+ dot: false
258
+ }
259
+ )
260
+ ] }) })
261
+ ] });
262
+ }
263
+
264
+ // src/registry/insightRegistry.ts
265
+ var insightRegistry = {
266
+ trend: TrendChart,
267
+ contributor: ContributorChart
268
+ };
269
+
270
+ // src/Insight.tsx
271
+ var import_react3 = require("react");
272
+ var import_jsx_runtime6 = require("react/jsx-runtime");
273
+ function Insight(props) {
274
+ (0, import_react3.useMemo)(() => validateInsightProps(props), [props]);
275
+ const { fromTime, toTime } = useTimeRange(props.timeRange);
276
+ const { data, loading, error } = useInsightData(props, fromTime, toTime);
277
+ if (loading) return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Loading, {});
278
+ if (error) return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ErrorState, { error });
279
+ if (!data) return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Empty, {});
280
+ const Chart = insightRegistry[props.type];
281
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Chart, { ...data });
282
+ }
283
+ // Annotate the CommonJS export names for ESM import in node:
284
+ 0 && (module.exports = {
285
+ Insight
286
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,276 @@
1
+ // src/core/errors.ts
2
+ var SDKError = class extends Error {
3
+ constructor(message) {
4
+ super(message);
5
+ this.name = "SDKError";
6
+ }
7
+ };
8
+ var ValidationError = class extends SDKError {
9
+ constructor(message) {
10
+ super(message);
11
+ this.name = "ValidationError";
12
+ }
13
+ };
14
+
15
+ // src/utils/validation.ts
16
+ function validateInsightProps(props) {
17
+ if (!props.metric) {
18
+ throw new ValidationError("metric is required");
19
+ }
20
+ if (props.type === "contributor" && !props.dimension) {
21
+ throw new ValidationError(
22
+ "dimension is required when type is 'contributor'"
23
+ );
24
+ }
25
+ if (!props.dataResolver) {
26
+ throw new ValidationError("dataResolver is required");
27
+ }
28
+ }
29
+
30
+ // src/hooks/useTimeRange.ts
31
+ import { useMemo } from "react";
32
+
33
+ // src/utils/time.ts
34
+ var calculateTimeRange = (days) => {
35
+ const toTime = /* @__PURE__ */ new Date();
36
+ const fromTime = /* @__PURE__ */ new Date();
37
+ fromTime.setDate(toTime.getDate() - days);
38
+ return { fromTime, toTime };
39
+ };
40
+ function formatTimeLabel(rawDate, grain) {
41
+ if (grain !== "monthly") {
42
+ return rawDate;
43
+ }
44
+ const [day, month, year] = rawDate.split("-");
45
+ const isoDate = `${year}-${month}-${day}`;
46
+ const date = new Date(isoDate);
47
+ return date.toLocaleString("en-US", {
48
+ month: "short",
49
+ year: "numeric"
50
+ });
51
+ }
52
+
53
+ // src/hooks/useTimeRange.ts
54
+ function useTimeRange(timeRange) {
55
+ return useMemo(() => calculateTimeRange(timeRange), [timeRange]);
56
+ }
57
+
58
+ // src/hooks/useInsightData.ts
59
+ import { useEffect, useState } from "react";
60
+
61
+ // src/transformers/contributor.transformer.ts
62
+ var DEFAULT_COLORS = ["#9acbdc", "#f6b1ac", "#8da2fb", "#1f3b63"];
63
+ var transformContributorData = (raw, ctx) => {
64
+ const data = raw.map((row) => {
65
+ const point = {
66
+ x: formatTimeLabel(row.fromtime, ctx.timeGrain)
67
+ };
68
+ ctx.dimensionValues.forEach((dim) => {
69
+ var _a;
70
+ point[dim] = (_a = row[dim]) != null ? _a : 0;
71
+ });
72
+ return point;
73
+ });
74
+ const series = ctx.dimensionValues.map((dim, index) => ({
75
+ id: dim,
76
+ label: dim,
77
+ color: DEFAULT_COLORS[index % DEFAULT_COLORS.length]
78
+ }));
79
+ return {
80
+ title: `${ctx.metric} by ${ctx.dimension}`,
81
+ data,
82
+ series
83
+ };
84
+ };
85
+
86
+ // src/transformers/trend.transformer.ts
87
+ var transformTrendData = (raw, ctx) => {
88
+ return {
89
+ title: ctx.metric,
90
+ data: raw.map((bucket) => ({
91
+ x: formatTimeLabel(bucket.fromtime, ctx.timeGrain),
92
+ y: bucket[ctx.metric]
93
+ }))
94
+ };
95
+ };
96
+
97
+ // src/registry/transformerRegistry.ts
98
+ var transformerRegistry = {
99
+ trend: transformTrendData,
100
+ contributor: transformContributorData
101
+ };
102
+
103
+ // src/hooks/useInsightData.ts
104
+ function useInsightData(props, fromTime, toTime) {
105
+ const [data, setData] = useState(null);
106
+ const [loading, setLoading] = useState(true);
107
+ const [error, setError] = useState(null);
108
+ useEffect(() => {
109
+ let mounted = true;
110
+ async function run() {
111
+ try {
112
+ setLoading(true);
113
+ setError(null);
114
+ const rawData = await props.dataResolver(
115
+ props.metric,
116
+ props.timeGrain,
117
+ fromTime,
118
+ toTime
119
+ );
120
+ let dimensionValues = [];
121
+ if (props.type === "contributor" && props.dimensionValuesResolver) {
122
+ dimensionValues = await props.dimensionValuesResolver(
123
+ props.metric,
124
+ props.dimension
125
+ );
126
+ }
127
+ const transformer = transformerRegistry[props.type];
128
+ const transformed = transformer(rawData, {
129
+ metric: props.metric,
130
+ dimension: props.dimension,
131
+ dimensionValues,
132
+ timeGrain: props.timeGrain
133
+ });
134
+ if (mounted) setData(transformed);
135
+ } catch (e) {
136
+ if (mounted) setError(e);
137
+ } finally {
138
+ if (mounted) setLoading(false);
139
+ }
140
+ }
141
+ run();
142
+ return () => {
143
+ mounted = false;
144
+ };
145
+ }, [
146
+ props.type,
147
+ props.metric,
148
+ props.dimension,
149
+ props.timeGrain,
150
+ props.dataResolver,
151
+ props.dimensionValuesResolver,
152
+ fromTime,
153
+ toTime
154
+ ]);
155
+ return { data, loading, error };
156
+ }
157
+
158
+ // src/states/loading.tsx
159
+ import { jsx } from "react/jsx-runtime";
160
+ var Loading = () => /* @__PURE__ */ jsx("div", { children: "Loading insight\u2026" });
161
+
162
+ // src/states/error.tsx
163
+ import { jsxs } from "react/jsx-runtime";
164
+ var ErrorState = ({ error }) => /* @__PURE__ */ jsxs("div", { children: [
165
+ "Error: ",
166
+ error.message
167
+ ] });
168
+
169
+ // src/states/empty.tsx
170
+ import { jsx as jsx2 } from "react/jsx-runtime";
171
+ var Empty = () => /* @__PURE__ */ jsx2("div", { children: "No data available" });
172
+
173
+ // src/charts/contributorChart.tsx
174
+ import {
175
+ BarChart,
176
+ Bar,
177
+ XAxis,
178
+ YAxis,
179
+ Tooltip,
180
+ ResponsiveContainer,
181
+ CartesianGrid,
182
+ Legend
183
+ } from "recharts";
184
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
185
+ function ContributorChart({
186
+ title,
187
+ data,
188
+ series
189
+ }) {
190
+ if (!data.length || !series.length) return null;
191
+ return /* @__PURE__ */ jsxs2("div", { children: [
192
+ /* @__PURE__ */ jsx3("h3", { style: { marginBottom: 8 }, children: title }),
193
+ /* @__PURE__ */ jsx3(ResponsiveContainer, { width: "100%", minHeight: 300, children: /* @__PURE__ */ jsxs2(BarChart, { data, children: [
194
+ /* @__PURE__ */ jsx3(CartesianGrid, { strokeDasharray: "3 3", vertical: false }),
195
+ /* @__PURE__ */ jsx3(XAxis, { dataKey: "x" }),
196
+ /* @__PURE__ */ jsx3(YAxis, {}),
197
+ /* @__PURE__ */ jsx3(Tooltip, {}),
198
+ /* @__PURE__ */ jsx3(Legend, {}),
199
+ series.map((s) => {
200
+ var _a;
201
+ return /* @__PURE__ */ jsx3(
202
+ Bar,
203
+ {
204
+ dataKey: s.id,
205
+ stackId: "total",
206
+ fill: s.color,
207
+ name: (_a = s.label) != null ? _a : s.id
208
+ },
209
+ s.id
210
+ );
211
+ })
212
+ ] }) })
213
+ ] });
214
+ }
215
+
216
+ // src/charts/trendChart.tsx
217
+ import {
218
+ AreaChart,
219
+ Area,
220
+ XAxis as XAxis2,
221
+ YAxis as YAxis2,
222
+ Tooltip as Tooltip2,
223
+ ResponsiveContainer as ResponsiveContainer2,
224
+ CartesianGrid as CartesianGrid2
225
+ } from "recharts";
226
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
227
+ function TrendChart({ title, data }) {
228
+ if (!data.length) return null;
229
+ return /* @__PURE__ */ jsxs3("div", { children: [
230
+ /* @__PURE__ */ jsx4("h3", { style: { marginBottom: 8 }, children: title }),
231
+ /* @__PURE__ */ jsx4(ResponsiveContainer2, { width: "100%", minHeight: 300, children: /* @__PURE__ */ jsxs3(AreaChart, { data, children: [
232
+ /* @__PURE__ */ jsx4("defs", { children: /* @__PURE__ */ jsxs3("linearGradient", { id: "trendGradient", x1: "0", y1: "0", x2: "0", y2: "1", children: [
233
+ /* @__PURE__ */ jsx4("stop", { offset: "0%", stopColor: "#1e3a8a", stopOpacity: 0.25 }),
234
+ /* @__PURE__ */ jsx4("stop", { offset: "100%", stopColor: "#1e3a8a", stopOpacity: 0 })
235
+ ] }) }),
236
+ /* @__PURE__ */ jsx4(CartesianGrid2, { strokeDasharray: "3 3", vertical: false }),
237
+ /* @__PURE__ */ jsx4(XAxis2, { dataKey: "x" }),
238
+ /* @__PURE__ */ jsx4(YAxis2, {}),
239
+ /* @__PURE__ */ jsx4(Tooltip2, {}),
240
+ /* @__PURE__ */ jsx4(
241
+ Area,
242
+ {
243
+ type: "monotone",
244
+ dataKey: "y",
245
+ stroke: "#1e3a8a",
246
+ strokeWidth: 2,
247
+ fill: "url(#trendGradient)",
248
+ dot: false
249
+ }
250
+ )
251
+ ] }) })
252
+ ] });
253
+ }
254
+
255
+ // src/registry/insightRegistry.ts
256
+ var insightRegistry = {
257
+ trend: TrendChart,
258
+ contributor: ContributorChart
259
+ };
260
+
261
+ // src/Insight.tsx
262
+ import { useMemo as useMemo2 } from "react";
263
+ import { jsx as jsx5 } from "react/jsx-runtime";
264
+ function Insight(props) {
265
+ useMemo2(() => validateInsightProps(props), [props]);
266
+ const { fromTime, toTime } = useTimeRange(props.timeRange);
267
+ const { data, loading, error } = useInsightData(props, fromTime, toTime);
268
+ if (loading) return /* @__PURE__ */ jsx5(Loading, {});
269
+ if (error) return /* @__PURE__ */ jsx5(ErrorState, { error });
270
+ if (!data) return /* @__PURE__ */ jsx5(Empty, {});
271
+ const Chart = insightRegistry[props.type];
272
+ return /* @__PURE__ */ jsx5(Chart, { ...data });
273
+ }
274
+ export {
275
+ Insight
276
+ };
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "insight-react-sdk",
3
+ "version": "1.0.0",
4
+ "description": "React SDK for rendering insights",
5
+ "main": "dist/index.cjs",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.mjs",
15
+ "require": "./dist/index.cjs"
16
+ }
17
+ },
18
+ "scripts": {
19
+ "build": "tsup"
20
+ },
21
+ "keywords": [
22
+ "react",
23
+ "sdk",
24
+ "charts",
25
+ "analytics",
26
+ "insights",
27
+ "frontend"
28
+ ],
29
+ "author": "Anvesh Ramelli",
30
+ "license": "MIT",
31
+ "peerDependencies": {
32
+ "react": ">=18",
33
+ "react-dom": ">=18",
34
+ "recharts": ">=2.0.0"
35
+ },
36
+ "devDependencies": {
37
+ "@types/react": "^19.2.7",
38
+ "@types/react-dom": "^19.2.3",
39
+ "react": "^19.2.3",
40
+ "react-dom": "^19.2.3",
41
+ "recharts": "^3.6.0",
42
+ "tsup": "^8.5.1",
43
+ "typescript": "^5.9.3"
44
+ }
45
+ }