@cstar.help/react 0.5.0 → 0.6.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/dist/index.cjs CHANGED
@@ -24,19 +24,28 @@ __export(src_exports, {
24
24
  CStarChatProvider: () => CStarChatProvider,
25
25
  CStarCommunityContext: () => CStarCommunityContext,
26
26
  CStarCommunityProvider: () => CStarCommunityProvider,
27
+ CStarDashboardProvider: () => CStarDashboardProvider,
27
28
  CStarLibraryContext: () => CStarLibraryContext,
28
29
  CStarLibraryProvider: () => CStarLibraryProvider,
29
30
  useArticle: () => useArticle,
30
31
  useArticleSearch: () => useArticleSearch,
31
32
  useArticles: () => useArticles,
33
+ useBossBattle: () => useBossBattle,
34
+ useCStarEvents: () => useCStarEvents,
35
+ useCStarMutation: () => useCStarMutation,
36
+ useCStarQuery: () => useCStarQuery,
32
37
  useCategories: () => useCategories,
33
38
  useChat: () => useChat,
34
39
  useChatClient: () => useChatClient,
35
40
  useCommunityClient: () => useCommunityClient,
36
41
  useCommunitySearch: () => useCommunitySearch,
37
42
  useConversations: () => useConversations,
43
+ useDashboardClient: () => useDashboardClient,
44
+ useGameActivity: () => useGameActivity,
45
+ useLeaderboard: () => useLeaderboard,
38
46
  useLibraryClient: () => useLibraryClient,
39
47
  useMessages: () => useMessages,
48
+ usePlayerStats: () => usePlayerStats,
40
49
  usePost: () => usePost,
41
50
  usePosts: () => usePosts,
42
51
  useTopics: () => useTopics,
@@ -664,25 +673,322 @@ function useCommunitySearch() {
664
673
  );
665
674
  return { results, isLoading, error, search };
666
675
  }
676
+
677
+ // src/dashboard/CStarDashboardProvider.tsx
678
+ var import_react17 = require("react");
679
+ var import_jsx_runtime4 = require("react/jsx-runtime");
680
+ var CStarDashboardContext = (0, import_react17.createContext)(null);
681
+ function CStarDashboardProvider({ client, children }) {
682
+ const contextValue = (0, import_react17.useRef)({ client }).current;
683
+ (0, import_react17.useEffect)(() => {
684
+ return () => {
685
+ client.realtime.disconnect();
686
+ };
687
+ }, [client]);
688
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CStarDashboardContext.Provider, { value: contextValue, children });
689
+ }
690
+ function useDashboardClient() {
691
+ const ctx = (0, import_react17.useContext)(CStarDashboardContext);
692
+ if (!ctx) {
693
+ throw new Error("useDashboardClient must be used within a <CStarDashboardProvider>");
694
+ }
695
+ return ctx.client;
696
+ }
697
+
698
+ // src/dashboard/useCStarQuery.ts
699
+ var import_react18 = require("react");
700
+ function resolveResource(client, path) {
701
+ const parts = path.split(".");
702
+ if (parts.length < 2 || parts[1] !== "list") return null;
703
+ const resourceName = parts[0];
704
+ const resource = client[resourceName];
705
+ if (!resource || typeof resource !== "object") return null;
706
+ const typed = resource;
707
+ if (typeof typed.list !== "function" || typeof typed.subscribe !== "function") return null;
708
+ return { resource, basePath: `/${resourceName}` };
709
+ }
710
+ function useCStarQuery(resource, params) {
711
+ const client = useDashboardClient();
712
+ const [data, setData] = (0, import_react18.useState)(null);
713
+ const [loading, setLoading] = (0, import_react18.useState)(true);
714
+ const [error, setError] = (0, import_react18.useState)(null);
715
+ const subscriptionRef = (0, import_react18.useRef)(null);
716
+ const paramsKey = params ? JSON.stringify(params) : "";
717
+ (0, import_react18.useEffect)(() => {
718
+ const resolved = resolveResource(client, resource);
719
+ if (!resolved) {
720
+ setError(new Error(`Invalid resource path: ${resource}. Expected format: 'tickets.list'`));
721
+ setLoading(false);
722
+ return;
723
+ }
724
+ const sub = resolved.resource.subscribe(client.realtime, params);
725
+ subscriptionRef.current = sub;
726
+ sub.on("change", (newData) => {
727
+ setData(newData);
728
+ setLoading(false);
729
+ setError(null);
730
+ });
731
+ sub.on("error", (err) => {
732
+ setError(err);
733
+ setLoading(false);
734
+ });
735
+ return () => {
736
+ sub.unsubscribe();
737
+ subscriptionRef.current = null;
738
+ };
739
+ }, [client, resource, paramsKey]);
740
+ const refresh = (0, import_react18.useCallback)(async () => {
741
+ if (subscriptionRef.current) {
742
+ await subscriptionRef.current.refresh();
743
+ }
744
+ }, []);
745
+ return { data, loading, error, refresh };
746
+ }
747
+
748
+ // src/dashboard/useCStarMutation.ts
749
+ var import_react19 = require("react");
750
+ function resolveMutation(client, path) {
751
+ const parts = path.split(".");
752
+ if (parts.length < 2) return null;
753
+ const resourceName = parts[0];
754
+ const methodName = parts[1];
755
+ const resource = client[resourceName];
756
+ if (!resource || typeof resource !== "object") return null;
757
+ const method = resource[methodName];
758
+ if (typeof method !== "function") return null;
759
+ if (methodName === "update") {
760
+ return (params) => {
761
+ const p = params;
762
+ const { id, ...rest } = p;
763
+ return method.call(resource, id, rest);
764
+ };
765
+ }
766
+ if (methodName === "del" || methodName === "delete") {
767
+ return (params) => {
768
+ const p = params;
769
+ return method.call(resource, p.id);
770
+ };
771
+ }
772
+ return (params) => method.call(resource, params);
773
+ }
774
+ function useCStarMutation(resource) {
775
+ const client = useDashboardClient();
776
+ const [loading, setLoading] = (0, import_react19.useState)(false);
777
+ const [error, setError] = (0, import_react19.useState)(null);
778
+ const mutate = (0, import_react19.useCallback)(
779
+ async (params) => {
780
+ const fn = resolveMutation(client, resource);
781
+ if (!fn) {
782
+ throw new Error(`Invalid mutation path: ${resource}. Expected format: 'tickets.create'`);
783
+ }
784
+ setLoading(true);
785
+ setError(null);
786
+ try {
787
+ const result = await fn(params);
788
+ setLoading(false);
789
+ return result;
790
+ } catch (err) {
791
+ const error2 = err instanceof Error ? err : new Error(String(err));
792
+ setError(error2);
793
+ setLoading(false);
794
+ throw error2;
795
+ }
796
+ },
797
+ [client, resource]
798
+ );
799
+ return { mutate, loading, error };
800
+ }
801
+
802
+ // src/dashboard/useCStarEvents.ts
803
+ var import_react20 = require("react");
804
+ var MAX_EVENTS = 100;
805
+ function useCStarEvents(types) {
806
+ const client = useDashboardClient();
807
+ const [events, setEvents] = (0, import_react20.useState)([]);
808
+ const [connected, setConnected] = (0, import_react20.useState)(false);
809
+ const typesKey = types ? types.join(",") : "";
810
+ (0, import_react20.useEffect)(() => {
811
+ const realtime = client.realtime;
812
+ const handler = (event) => {
813
+ if (types && types.length > 0) {
814
+ const matches = types.some((pattern) => {
815
+ if (pattern.endsWith(".*")) {
816
+ return event.type.startsWith(pattern.slice(0, -2) + ".");
817
+ }
818
+ return event.type === pattern;
819
+ });
820
+ if (!matches) return;
821
+ }
822
+ setEvents((prev) => [event, ...prev].slice(0, MAX_EVENTS));
823
+ };
824
+ const connHandler = (isConnected) => {
825
+ setConnected(isConnected);
826
+ };
827
+ realtime.onAny(handler);
828
+ realtime.onConnectionChange(connHandler);
829
+ if (!realtime.connected) {
830
+ realtime.connect();
831
+ }
832
+ setConnected(realtime.connected);
833
+ return () => {
834
+ realtime.offAny(handler);
835
+ realtime.offConnectionChange(connHandler);
836
+ };
837
+ }, [client, typesKey]);
838
+ return { events, connected };
839
+ }
840
+
841
+ // src/dashboard/useBossBattle.ts
842
+ var import_react21 = require("react");
843
+ function useBossBattle() {
844
+ const client = useDashboardClient();
845
+ const [boss, setBoss] = (0, import_react21.useState)(null);
846
+ const [loading, setLoading] = (0, import_react21.useState)(true);
847
+ const [error, setError] = (0, import_react21.useState)(null);
848
+ const subRef = (0, import_react21.useRef)(null);
849
+ (0, import_react21.useEffect)(() => {
850
+ const sub = client.game.subscribeBoss(client.realtime);
851
+ subRef.current = sub;
852
+ sub.on("change", (data) => {
853
+ setBoss(data.length > 0 ? data[0] : null);
854
+ setLoading(false);
855
+ setError(null);
856
+ });
857
+ sub.on("error", (err) => {
858
+ setError(err);
859
+ setLoading(false);
860
+ });
861
+ return () => {
862
+ sub.unsubscribe();
863
+ subRef.current = null;
864
+ };
865
+ }, [client]);
866
+ const refresh = (0, import_react21.useCallback)(async () => {
867
+ if (subRef.current) await subRef.current.refresh();
868
+ }, []);
869
+ return { boss, active: boss?.active ?? false, loading, error, refresh };
870
+ }
871
+
872
+ // src/dashboard/usePlayerStats.ts
873
+ var import_react22 = require("react");
874
+ function usePlayerStats(memberId) {
875
+ const client = useDashboardClient();
876
+ const [player, setPlayer] = (0, import_react22.useState)(null);
877
+ const [loading, setLoading] = (0, import_react22.useState)(true);
878
+ const [error, setError] = (0, import_react22.useState)(null);
879
+ const subRef = (0, import_react22.useRef)(null);
880
+ (0, import_react22.useEffect)(() => {
881
+ const sub = client.game.subscribePlayer(client.realtime, memberId);
882
+ subRef.current = sub;
883
+ sub.on("change", (data) => {
884
+ setPlayer(data.length > 0 ? data[0] : null);
885
+ setLoading(false);
886
+ setError(null);
887
+ });
888
+ sub.on("error", (err) => {
889
+ setError(err);
890
+ setLoading(false);
891
+ });
892
+ return () => {
893
+ sub.unsubscribe();
894
+ subRef.current = null;
895
+ };
896
+ }, [client, memberId]);
897
+ const refresh = (0, import_react22.useCallback)(async () => {
898
+ if (subRef.current) await subRef.current.refresh();
899
+ }, []);
900
+ return { player, loading, error, refresh };
901
+ }
902
+
903
+ // src/dashboard/useLeaderboard.ts
904
+ var import_react23 = require("react");
905
+ function useLeaderboard(period) {
906
+ const client = useDashboardClient();
907
+ const [entries, setEntries] = (0, import_react23.useState)([]);
908
+ const [loading, setLoading] = (0, import_react23.useState)(true);
909
+ const [error, setError] = (0, import_react23.useState)(null);
910
+ const subRef = (0, import_react23.useRef)(null);
911
+ (0, import_react23.useEffect)(() => {
912
+ const sub = client.game.subscribeLeaderboard(client.realtime, period ? { period } : void 0);
913
+ subRef.current = sub;
914
+ sub.on("change", (data) => {
915
+ setEntries(data);
916
+ setLoading(false);
917
+ setError(null);
918
+ });
919
+ sub.on("error", (err) => {
920
+ setError(err);
921
+ setLoading(false);
922
+ });
923
+ return () => {
924
+ sub.unsubscribe();
925
+ subRef.current = null;
926
+ };
927
+ }, [client, period]);
928
+ const refresh = (0, import_react23.useCallback)(async () => {
929
+ if (subRef.current) await subRef.current.refresh();
930
+ }, []);
931
+ return { entries, loading, error, refresh };
932
+ }
933
+
934
+ // src/dashboard/useGameActivity.ts
935
+ var import_react24 = require("react");
936
+ function useGameActivity() {
937
+ const client = useDashboardClient();
938
+ const [events, setEvents] = (0, import_react24.useState)([]);
939
+ const [loading, setLoading] = (0, import_react24.useState)(true);
940
+ const [error, setError] = (0, import_react24.useState)(null);
941
+ const subRef = (0, import_react24.useRef)(null);
942
+ (0, import_react24.useEffect)(() => {
943
+ const sub = client.game.subscribeActivity(client.realtime);
944
+ subRef.current = sub;
945
+ sub.on("change", (data) => {
946
+ setEvents(data);
947
+ setLoading(false);
948
+ setError(null);
949
+ });
950
+ sub.on("error", (err) => {
951
+ setError(err);
952
+ setLoading(false);
953
+ });
954
+ return () => {
955
+ sub.unsubscribe();
956
+ subRef.current = null;
957
+ };
958
+ }, [client]);
959
+ const refresh = (0, import_react24.useCallback)(async () => {
960
+ if (subRef.current) await subRef.current.refresh();
961
+ }, []);
962
+ return { events, loading, error, refresh };
963
+ }
667
964
  // Annotate the CommonJS export names for ESM import in node:
668
965
  0 && (module.exports = {
669
966
  CStarChatContext,
670
967
  CStarChatProvider,
671
968
  CStarCommunityContext,
672
969
  CStarCommunityProvider,
970
+ CStarDashboardProvider,
673
971
  CStarLibraryContext,
674
972
  CStarLibraryProvider,
675
973
  useArticle,
676
974
  useArticleSearch,
677
975
  useArticles,
976
+ useBossBattle,
977
+ useCStarEvents,
978
+ useCStarMutation,
979
+ useCStarQuery,
678
980
  useCategories,
679
981
  useChat,
680
982
  useChatClient,
681
983
  useCommunityClient,
682
984
  useCommunitySearch,
683
985
  useConversations,
986
+ useDashboardClient,
987
+ useGameActivity,
988
+ useLeaderboard,
684
989
  useLibraryClient,
685
990
  useMessages,
991
+ usePlayerStats,
686
992
  usePost,
687
993
  usePosts,
688
994
  useTopics,
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as react from 'react';
2
- import { ReactNode } from 'react';
2
+ import react__default, { ReactNode } from 'react';
3
3
  import { ChatClient, ChatConfig, IdentifyParams, IdentifyResult, Conversation, ConversationCreateParams, WidgetMessage } from '@cstar.help/js/chat';
4
4
  export { ChatConfig, Conversation, ConversationCreateParams, ConversationListResponse, IdentifyParams, IdentifyResult, TypingPayload, WidgetMessage } from '@cstar.help/js/chat';
5
5
  import { LibraryClient, LibraryConfig, Category, LibraryArticle, LibraryArticleListParams } from '@cstar.help/js/library';
@@ -7,6 +7,7 @@ export { Category, LibraryArticle, LibraryArticleListParams, LibraryConfig, Libr
7
7
  import { CommunityClient, CommunityConfig, CommunityTopic, CommunityPost, CommunityPostListParams, CommunityPostDetail, CommunitySearchResult } from '@cstar.help/js/community';
8
8
  export { CommunityComment, CommunityConfig, CommunityPost, CommunityPostDetail, CommunityPostListParams, CommunitySearchResult, CommunityTopic } from '@cstar.help/js/community';
9
9
  import * as react_jsx_runtime from 'react/jsx-runtime';
10
+ import { CStarClient, RealtimeEvent, Boss, Player, LeaderboardEntry } from '@cstar.help/js';
10
11
 
11
12
  declare const CStarChatContext: react.Context<ChatClient | null>;
12
13
  declare const CStarLibraryContext: react.Context<LibraryClient | null>;
@@ -320,4 +321,160 @@ interface UseCommunitySearchReturn {
320
321
  */
321
322
  declare function useCommunitySearch(): UseCommunitySearchReturn;
322
323
 
323
- export { CStarChatContext, CStarChatProvider, type CStarChatProviderProps, CStarCommunityContext, CStarCommunityProvider, type CStarCommunityProviderProps, CStarLibraryContext, CStarLibraryProvider, type CStarLibraryProviderProps, type TypingAgent, type UseArticleReturn, type UseArticleSearchReturn, type UseArticlesReturn, type UseCategoriesReturn, type UseChatReturn, type UseCommunitySearchReturn, type UseConversationsReturn, type UseMessagesReturn, type UsePostReturn, type UsePostsReturn, type UseTopicsReturn, type UseTypingReturn, useArticle, useArticleSearch, useArticles, useCategories, useChat, useChatClient, useCommunityClient, useCommunitySearch, useConversations, useLibraryClient, useMessages, usePost, usePosts, useTopics, useTyping };
324
+ interface CStarDashboardProviderProps {
325
+ /** A pre-configured CStarClient instance (server-side, secret key). */
326
+ client: CStarClient;
327
+ children: react__default.ReactNode;
328
+ }
329
+ /**
330
+ * Provides a CStarClient to dashboard hooks.
331
+ *
332
+ * Unlike `CStarChatProvider` (which uses a publishable key for customer-facing
333
+ * chat), this provider wraps a full admin client with a secret key for building
334
+ * custom dashboards.
335
+ *
336
+ * ```tsx
337
+ * const client = new CStarClient({ apiKey: 'sk_test_...', teamId: '...' });
338
+ *
339
+ * <CStarDashboardProvider client={client}>
340
+ * <TicketList />
341
+ * </CStarDashboardProvider>
342
+ * ```
343
+ */
344
+ declare function CStarDashboardProvider({ client, children }: CStarDashboardProviderProps): react_jsx_runtime.JSX.Element;
345
+ /**
346
+ * Access the CStarClient from the nearest CStarDashboardProvider.
347
+ * Throws if used outside a provider.
348
+ */
349
+ declare function useDashboardClient(): CStarClient;
350
+
351
+ interface UseCStarQueryReturn<T> {
352
+ /** Current data (auto-updated via SSE). Null until first fetch completes. */
353
+ data: T[] | null;
354
+ /** True during initial data fetch. */
355
+ loading: boolean;
356
+ /** Last error, if any. */
357
+ error: Error | null;
358
+ /** Manually refresh data from the server. */
359
+ refresh: () => Promise<void>;
360
+ }
361
+ /**
362
+ * Subscribe to a real-time resource query. Data auto-updates via SSE.
363
+ *
364
+ * ```tsx
365
+ * const { data: tickets, loading } = useCStarQuery('tickets.list', { status: 'open' });
366
+ * ```
367
+ *
368
+ * @param resource - Dot-path resource method (e.g., 'tickets.list', 'customers.list')
369
+ * @param params - Optional filter/pagination params
370
+ */
371
+ declare function useCStarQuery<T = unknown>(resource: string, params?: Record<string, unknown>): UseCStarQueryReturn<T>;
372
+
373
+ interface UseCStarMutationReturn<TParams, TResult> {
374
+ /** Execute the mutation. */
375
+ mutate: (params: TParams) => Promise<TResult>;
376
+ /** True while the mutation is in flight. */
377
+ loading: boolean;
378
+ /** Last error, if any. */
379
+ error: Error | null;
380
+ }
381
+ /**
382
+ * Create a mutation function for a resource method.
383
+ *
384
+ * ```tsx
385
+ * const { mutate: createTicket, loading } = useCStarMutation('tickets.create');
386
+ * await createTicket({ title: 'New ticket', customerId: 'cus_123' });
387
+ * ```
388
+ *
389
+ * @param resource - Dot-path resource method (e.g., 'tickets.create', 'tickets.update')
390
+ */
391
+ declare function useCStarMutation<TParams = Record<string, unknown>, TResult = unknown>(resource: string): UseCStarMutationReturn<TParams, TResult>;
392
+
393
+ interface UseCStarEventsReturn {
394
+ /** Rolling buffer of recent events (newest first). */
395
+ events: RealtimeEvent[];
396
+ /** Whether the SSE connection is active. */
397
+ connected: boolean;
398
+ }
399
+ /**
400
+ * Access the raw SSE event stream for custom real-time UIs.
401
+ *
402
+ * ```tsx
403
+ * const { events, connected } = useCStarEvents(['boss.*', 'achievement.*']);
404
+ * ```
405
+ *
406
+ * @param types - Optional event type filter (supports wildcards)
407
+ */
408
+ declare function useCStarEvents(types?: string[]): UseCStarEventsReturn;
409
+
410
+ interface UseBossBattleReturn {
411
+ /** Current boss state (null if no boss active or loading). */
412
+ boss: Boss | null;
413
+ /** Whether a boss is currently active. */
414
+ active: boolean;
415
+ /** True during initial data fetch. */
416
+ loading: boolean;
417
+ /** Last error, if any. */
418
+ error: Error | null;
419
+ /** Manually refresh boss data. */
420
+ refresh: () => Promise<void>;
421
+ }
422
+ /**
423
+ * Subscribe to live boss battle state.
424
+ *
425
+ * ```tsx
426
+ * const { boss, active, loading, error } = useBossBattle();
427
+ * if (active) {
428
+ * return <BossHPBar hp={boss.health} max={boss.maxHealth} />;
429
+ * }
430
+ * ```
431
+ */
432
+ declare function useBossBattle(): UseBossBattleReturn;
433
+
434
+ interface UsePlayerStatsReturn {
435
+ player: Player | null;
436
+ loading: boolean;
437
+ error: Error | null;
438
+ refresh: () => Promise<void>;
439
+ }
440
+ /**
441
+ * Subscribe to player stats (XP, level, streaks).
442
+ *
443
+ * ```tsx
444
+ * const { player, loading, error } = usePlayerStats('user_123');
445
+ * ```
446
+ */
447
+ declare function usePlayerStats(memberId?: string): UsePlayerStatsReturn;
448
+
449
+ interface UseLeaderboardReturn {
450
+ entries: LeaderboardEntry[];
451
+ loading: boolean;
452
+ error: Error | null;
453
+ refresh: () => Promise<void>;
454
+ }
455
+ /**
456
+ * Subscribe to team leaderboard rankings.
457
+ *
458
+ * ```tsx
459
+ * const { entries, loading, error } = useLeaderboard('weekly');
460
+ * ```
461
+ */
462
+ declare function useLeaderboard(period?: 'daily' | 'weekly' | 'allTime'): UseLeaderboardReturn;
463
+
464
+ interface UseGameActivityReturn {
465
+ /** Rolling feed of recent game events. */
466
+ events: Record<string, unknown>[];
467
+ loading: boolean;
468
+ error: Error | null;
469
+ refresh: () => Promise<void>;
470
+ }
471
+ /**
472
+ * Subscribe to the team game activity feed (achievements, boss defeats, level ups).
473
+ *
474
+ * ```tsx
475
+ * const { events, loading } = useGameActivity();
476
+ * ```
477
+ */
478
+ declare function useGameActivity(): UseGameActivityReturn;
479
+
480
+ export { CStarChatContext, CStarChatProvider, type CStarChatProviderProps, CStarCommunityContext, CStarCommunityProvider, type CStarCommunityProviderProps, CStarDashboardProvider, type CStarDashboardProviderProps, CStarLibraryContext, CStarLibraryProvider, type CStarLibraryProviderProps, type TypingAgent, type UseArticleReturn, type UseArticleSearchReturn, type UseArticlesReturn, type UseBossBattleReturn, type UseCStarEventsReturn, type UseCStarMutationReturn, type UseCStarQueryReturn, type UseCategoriesReturn, type UseChatReturn, type UseCommunitySearchReturn, type UseConversationsReturn, type UseGameActivityReturn, type UseLeaderboardReturn, type UseMessagesReturn, type UsePlayerStatsReturn, type UsePostReturn, type UsePostsReturn, type UseTopicsReturn, type UseTypingReturn, useArticle, useArticleSearch, useArticles, useBossBattle, useCStarEvents, useCStarMutation, useCStarQuery, useCategories, useChat, useChatClient, useCommunityClient, useCommunitySearch, useConversations, useDashboardClient, useGameActivity, useLeaderboard, useLibraryClient, useMessages, usePlayerStats, usePost, usePosts, useTopics, useTyping };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as react from 'react';
2
- import { ReactNode } from 'react';
2
+ import react__default, { ReactNode } from 'react';
3
3
  import { ChatClient, ChatConfig, IdentifyParams, IdentifyResult, Conversation, ConversationCreateParams, WidgetMessage } from '@cstar.help/js/chat';
4
4
  export { ChatConfig, Conversation, ConversationCreateParams, ConversationListResponse, IdentifyParams, IdentifyResult, TypingPayload, WidgetMessage } from '@cstar.help/js/chat';
5
5
  import { LibraryClient, LibraryConfig, Category, LibraryArticle, LibraryArticleListParams } from '@cstar.help/js/library';
@@ -7,6 +7,7 @@ export { Category, LibraryArticle, LibraryArticleListParams, LibraryConfig, Libr
7
7
  import { CommunityClient, CommunityConfig, CommunityTopic, CommunityPost, CommunityPostListParams, CommunityPostDetail, CommunitySearchResult } from '@cstar.help/js/community';
8
8
  export { CommunityComment, CommunityConfig, CommunityPost, CommunityPostDetail, CommunityPostListParams, CommunitySearchResult, CommunityTopic } from '@cstar.help/js/community';
9
9
  import * as react_jsx_runtime from 'react/jsx-runtime';
10
+ import { CStarClient, RealtimeEvent, Boss, Player, LeaderboardEntry } from '@cstar.help/js';
10
11
 
11
12
  declare const CStarChatContext: react.Context<ChatClient | null>;
12
13
  declare const CStarLibraryContext: react.Context<LibraryClient | null>;
@@ -320,4 +321,160 @@ interface UseCommunitySearchReturn {
320
321
  */
321
322
  declare function useCommunitySearch(): UseCommunitySearchReturn;
322
323
 
323
- export { CStarChatContext, CStarChatProvider, type CStarChatProviderProps, CStarCommunityContext, CStarCommunityProvider, type CStarCommunityProviderProps, CStarLibraryContext, CStarLibraryProvider, type CStarLibraryProviderProps, type TypingAgent, type UseArticleReturn, type UseArticleSearchReturn, type UseArticlesReturn, type UseCategoriesReturn, type UseChatReturn, type UseCommunitySearchReturn, type UseConversationsReturn, type UseMessagesReturn, type UsePostReturn, type UsePostsReturn, type UseTopicsReturn, type UseTypingReturn, useArticle, useArticleSearch, useArticles, useCategories, useChat, useChatClient, useCommunityClient, useCommunitySearch, useConversations, useLibraryClient, useMessages, usePost, usePosts, useTopics, useTyping };
324
+ interface CStarDashboardProviderProps {
325
+ /** A pre-configured CStarClient instance (server-side, secret key). */
326
+ client: CStarClient;
327
+ children: react__default.ReactNode;
328
+ }
329
+ /**
330
+ * Provides a CStarClient to dashboard hooks.
331
+ *
332
+ * Unlike `CStarChatProvider` (which uses a publishable key for customer-facing
333
+ * chat), this provider wraps a full admin client with a secret key for building
334
+ * custom dashboards.
335
+ *
336
+ * ```tsx
337
+ * const client = new CStarClient({ apiKey: 'sk_test_...', teamId: '...' });
338
+ *
339
+ * <CStarDashboardProvider client={client}>
340
+ * <TicketList />
341
+ * </CStarDashboardProvider>
342
+ * ```
343
+ */
344
+ declare function CStarDashboardProvider({ client, children }: CStarDashboardProviderProps): react_jsx_runtime.JSX.Element;
345
+ /**
346
+ * Access the CStarClient from the nearest CStarDashboardProvider.
347
+ * Throws if used outside a provider.
348
+ */
349
+ declare function useDashboardClient(): CStarClient;
350
+
351
+ interface UseCStarQueryReturn<T> {
352
+ /** Current data (auto-updated via SSE). Null until first fetch completes. */
353
+ data: T[] | null;
354
+ /** True during initial data fetch. */
355
+ loading: boolean;
356
+ /** Last error, if any. */
357
+ error: Error | null;
358
+ /** Manually refresh data from the server. */
359
+ refresh: () => Promise<void>;
360
+ }
361
+ /**
362
+ * Subscribe to a real-time resource query. Data auto-updates via SSE.
363
+ *
364
+ * ```tsx
365
+ * const { data: tickets, loading } = useCStarQuery('tickets.list', { status: 'open' });
366
+ * ```
367
+ *
368
+ * @param resource - Dot-path resource method (e.g., 'tickets.list', 'customers.list')
369
+ * @param params - Optional filter/pagination params
370
+ */
371
+ declare function useCStarQuery<T = unknown>(resource: string, params?: Record<string, unknown>): UseCStarQueryReturn<T>;
372
+
373
+ interface UseCStarMutationReturn<TParams, TResult> {
374
+ /** Execute the mutation. */
375
+ mutate: (params: TParams) => Promise<TResult>;
376
+ /** True while the mutation is in flight. */
377
+ loading: boolean;
378
+ /** Last error, if any. */
379
+ error: Error | null;
380
+ }
381
+ /**
382
+ * Create a mutation function for a resource method.
383
+ *
384
+ * ```tsx
385
+ * const { mutate: createTicket, loading } = useCStarMutation('tickets.create');
386
+ * await createTicket({ title: 'New ticket', customerId: 'cus_123' });
387
+ * ```
388
+ *
389
+ * @param resource - Dot-path resource method (e.g., 'tickets.create', 'tickets.update')
390
+ */
391
+ declare function useCStarMutation<TParams = Record<string, unknown>, TResult = unknown>(resource: string): UseCStarMutationReturn<TParams, TResult>;
392
+
393
+ interface UseCStarEventsReturn {
394
+ /** Rolling buffer of recent events (newest first). */
395
+ events: RealtimeEvent[];
396
+ /** Whether the SSE connection is active. */
397
+ connected: boolean;
398
+ }
399
+ /**
400
+ * Access the raw SSE event stream for custom real-time UIs.
401
+ *
402
+ * ```tsx
403
+ * const { events, connected } = useCStarEvents(['boss.*', 'achievement.*']);
404
+ * ```
405
+ *
406
+ * @param types - Optional event type filter (supports wildcards)
407
+ */
408
+ declare function useCStarEvents(types?: string[]): UseCStarEventsReturn;
409
+
410
+ interface UseBossBattleReturn {
411
+ /** Current boss state (null if no boss active or loading). */
412
+ boss: Boss | null;
413
+ /** Whether a boss is currently active. */
414
+ active: boolean;
415
+ /** True during initial data fetch. */
416
+ loading: boolean;
417
+ /** Last error, if any. */
418
+ error: Error | null;
419
+ /** Manually refresh boss data. */
420
+ refresh: () => Promise<void>;
421
+ }
422
+ /**
423
+ * Subscribe to live boss battle state.
424
+ *
425
+ * ```tsx
426
+ * const { boss, active, loading, error } = useBossBattle();
427
+ * if (active) {
428
+ * return <BossHPBar hp={boss.health} max={boss.maxHealth} />;
429
+ * }
430
+ * ```
431
+ */
432
+ declare function useBossBattle(): UseBossBattleReturn;
433
+
434
+ interface UsePlayerStatsReturn {
435
+ player: Player | null;
436
+ loading: boolean;
437
+ error: Error | null;
438
+ refresh: () => Promise<void>;
439
+ }
440
+ /**
441
+ * Subscribe to player stats (XP, level, streaks).
442
+ *
443
+ * ```tsx
444
+ * const { player, loading, error } = usePlayerStats('user_123');
445
+ * ```
446
+ */
447
+ declare function usePlayerStats(memberId?: string): UsePlayerStatsReturn;
448
+
449
+ interface UseLeaderboardReturn {
450
+ entries: LeaderboardEntry[];
451
+ loading: boolean;
452
+ error: Error | null;
453
+ refresh: () => Promise<void>;
454
+ }
455
+ /**
456
+ * Subscribe to team leaderboard rankings.
457
+ *
458
+ * ```tsx
459
+ * const { entries, loading, error } = useLeaderboard('weekly');
460
+ * ```
461
+ */
462
+ declare function useLeaderboard(period?: 'daily' | 'weekly' | 'allTime'): UseLeaderboardReturn;
463
+
464
+ interface UseGameActivityReturn {
465
+ /** Rolling feed of recent game events. */
466
+ events: Record<string, unknown>[];
467
+ loading: boolean;
468
+ error: Error | null;
469
+ refresh: () => Promise<void>;
470
+ }
471
+ /**
472
+ * Subscribe to the team game activity feed (achievements, boss defeats, level ups).
473
+ *
474
+ * ```tsx
475
+ * const { events, loading } = useGameActivity();
476
+ * ```
477
+ */
478
+ declare function useGameActivity(): UseGameActivityReturn;
479
+
480
+ export { CStarChatContext, CStarChatProvider, type CStarChatProviderProps, CStarCommunityContext, CStarCommunityProvider, type CStarCommunityProviderProps, CStarDashboardProvider, type CStarDashboardProviderProps, CStarLibraryContext, CStarLibraryProvider, type CStarLibraryProviderProps, type TypingAgent, type UseArticleReturn, type UseArticleSearchReturn, type UseArticlesReturn, type UseBossBattleReturn, type UseCStarEventsReturn, type UseCStarMutationReturn, type UseCStarQueryReturn, type UseCategoriesReturn, type UseChatReturn, type UseCommunitySearchReturn, type UseConversationsReturn, type UseGameActivityReturn, type UseLeaderboardReturn, type UseMessagesReturn, type UsePlayerStatsReturn, type UsePostReturn, type UsePostsReturn, type UseTopicsReturn, type UseTypingReturn, useArticle, useArticleSearch, useArticles, useBossBattle, useCStarEvents, useCStarMutation, useCStarQuery, useCategories, useChat, useChatClient, useCommunityClient, useCommunitySearch, useConversations, useDashboardClient, useGameActivity, useLeaderboard, useLibraryClient, useMessages, usePlayerStats, usePost, usePosts, useTopics, useTyping };
package/dist/index.js CHANGED
@@ -618,24 +618,321 @@ function useCommunitySearch() {
618
618
  );
619
619
  return { results, isLoading, error, search };
620
620
  }
621
+
622
+ // src/dashboard/CStarDashboardProvider.tsx
623
+ import { createContext as createContext2, useContext as useContext2, useEffect as useEffect13, useRef as useRef15 } from "react";
624
+ import { jsx as jsx4 } from "react/jsx-runtime";
625
+ var CStarDashboardContext = createContext2(null);
626
+ function CStarDashboardProvider({ client, children }) {
627
+ const contextValue = useRef15({ client }).current;
628
+ useEffect13(() => {
629
+ return () => {
630
+ client.realtime.disconnect();
631
+ };
632
+ }, [client]);
633
+ return /* @__PURE__ */ jsx4(CStarDashboardContext.Provider, { value: contextValue, children });
634
+ }
635
+ function useDashboardClient() {
636
+ const ctx = useContext2(CStarDashboardContext);
637
+ if (!ctx) {
638
+ throw new Error("useDashboardClient must be used within a <CStarDashboardProvider>");
639
+ }
640
+ return ctx.client;
641
+ }
642
+
643
+ // src/dashboard/useCStarQuery.ts
644
+ import { useState as useState13, useEffect as useEffect14, useRef as useRef16, useCallback as useCallback12 } from "react";
645
+ function resolveResource(client, path) {
646
+ const parts = path.split(".");
647
+ if (parts.length < 2 || parts[1] !== "list") return null;
648
+ const resourceName = parts[0];
649
+ const resource = client[resourceName];
650
+ if (!resource || typeof resource !== "object") return null;
651
+ const typed = resource;
652
+ if (typeof typed.list !== "function" || typeof typed.subscribe !== "function") return null;
653
+ return { resource, basePath: `/${resourceName}` };
654
+ }
655
+ function useCStarQuery(resource, params) {
656
+ const client = useDashboardClient();
657
+ const [data, setData] = useState13(null);
658
+ const [loading, setLoading] = useState13(true);
659
+ const [error, setError] = useState13(null);
660
+ const subscriptionRef = useRef16(null);
661
+ const paramsKey = params ? JSON.stringify(params) : "";
662
+ useEffect14(() => {
663
+ const resolved = resolveResource(client, resource);
664
+ if (!resolved) {
665
+ setError(new Error(`Invalid resource path: ${resource}. Expected format: 'tickets.list'`));
666
+ setLoading(false);
667
+ return;
668
+ }
669
+ const sub = resolved.resource.subscribe(client.realtime, params);
670
+ subscriptionRef.current = sub;
671
+ sub.on("change", (newData) => {
672
+ setData(newData);
673
+ setLoading(false);
674
+ setError(null);
675
+ });
676
+ sub.on("error", (err) => {
677
+ setError(err);
678
+ setLoading(false);
679
+ });
680
+ return () => {
681
+ sub.unsubscribe();
682
+ subscriptionRef.current = null;
683
+ };
684
+ }, [client, resource, paramsKey]);
685
+ const refresh = useCallback12(async () => {
686
+ if (subscriptionRef.current) {
687
+ await subscriptionRef.current.refresh();
688
+ }
689
+ }, []);
690
+ return { data, loading, error, refresh };
691
+ }
692
+
693
+ // src/dashboard/useCStarMutation.ts
694
+ import { useState as useState14, useCallback as useCallback13 } from "react";
695
+ function resolveMutation(client, path) {
696
+ const parts = path.split(".");
697
+ if (parts.length < 2) return null;
698
+ const resourceName = parts[0];
699
+ const methodName = parts[1];
700
+ const resource = client[resourceName];
701
+ if (!resource || typeof resource !== "object") return null;
702
+ const method = resource[methodName];
703
+ if (typeof method !== "function") return null;
704
+ if (methodName === "update") {
705
+ return (params) => {
706
+ const p = params;
707
+ const { id, ...rest } = p;
708
+ return method.call(resource, id, rest);
709
+ };
710
+ }
711
+ if (methodName === "del" || methodName === "delete") {
712
+ return (params) => {
713
+ const p = params;
714
+ return method.call(resource, p.id);
715
+ };
716
+ }
717
+ return (params) => method.call(resource, params);
718
+ }
719
+ function useCStarMutation(resource) {
720
+ const client = useDashboardClient();
721
+ const [loading, setLoading] = useState14(false);
722
+ const [error, setError] = useState14(null);
723
+ const mutate = useCallback13(
724
+ async (params) => {
725
+ const fn = resolveMutation(client, resource);
726
+ if (!fn) {
727
+ throw new Error(`Invalid mutation path: ${resource}. Expected format: 'tickets.create'`);
728
+ }
729
+ setLoading(true);
730
+ setError(null);
731
+ try {
732
+ const result = await fn(params);
733
+ setLoading(false);
734
+ return result;
735
+ } catch (err) {
736
+ const error2 = err instanceof Error ? err : new Error(String(err));
737
+ setError(error2);
738
+ setLoading(false);
739
+ throw error2;
740
+ }
741
+ },
742
+ [client, resource]
743
+ );
744
+ return { mutate, loading, error };
745
+ }
746
+
747
+ // src/dashboard/useCStarEvents.ts
748
+ import { useState as useState15, useEffect as useEffect15 } from "react";
749
+ var MAX_EVENTS = 100;
750
+ function useCStarEvents(types) {
751
+ const client = useDashboardClient();
752
+ const [events, setEvents] = useState15([]);
753
+ const [connected, setConnected] = useState15(false);
754
+ const typesKey = types ? types.join(",") : "";
755
+ useEffect15(() => {
756
+ const realtime = client.realtime;
757
+ const handler = (event) => {
758
+ if (types && types.length > 0) {
759
+ const matches = types.some((pattern) => {
760
+ if (pattern.endsWith(".*")) {
761
+ return event.type.startsWith(pattern.slice(0, -2) + ".");
762
+ }
763
+ return event.type === pattern;
764
+ });
765
+ if (!matches) return;
766
+ }
767
+ setEvents((prev) => [event, ...prev].slice(0, MAX_EVENTS));
768
+ };
769
+ const connHandler = (isConnected) => {
770
+ setConnected(isConnected);
771
+ };
772
+ realtime.onAny(handler);
773
+ realtime.onConnectionChange(connHandler);
774
+ if (!realtime.connected) {
775
+ realtime.connect();
776
+ }
777
+ setConnected(realtime.connected);
778
+ return () => {
779
+ realtime.offAny(handler);
780
+ realtime.offConnectionChange(connHandler);
781
+ };
782
+ }, [client, typesKey]);
783
+ return { events, connected };
784
+ }
785
+
786
+ // src/dashboard/useBossBattle.ts
787
+ import { useState as useState16, useEffect as useEffect16, useRef as useRef17, useCallback as useCallback14 } from "react";
788
+ function useBossBattle() {
789
+ const client = useDashboardClient();
790
+ const [boss, setBoss] = useState16(null);
791
+ const [loading, setLoading] = useState16(true);
792
+ const [error, setError] = useState16(null);
793
+ const subRef = useRef17(null);
794
+ useEffect16(() => {
795
+ const sub = client.game.subscribeBoss(client.realtime);
796
+ subRef.current = sub;
797
+ sub.on("change", (data) => {
798
+ setBoss(data.length > 0 ? data[0] : null);
799
+ setLoading(false);
800
+ setError(null);
801
+ });
802
+ sub.on("error", (err) => {
803
+ setError(err);
804
+ setLoading(false);
805
+ });
806
+ return () => {
807
+ sub.unsubscribe();
808
+ subRef.current = null;
809
+ };
810
+ }, [client]);
811
+ const refresh = useCallback14(async () => {
812
+ if (subRef.current) await subRef.current.refresh();
813
+ }, []);
814
+ return { boss, active: boss?.active ?? false, loading, error, refresh };
815
+ }
816
+
817
+ // src/dashboard/usePlayerStats.ts
818
+ import { useState as useState17, useEffect as useEffect17, useRef as useRef18, useCallback as useCallback15 } from "react";
819
+ function usePlayerStats(memberId) {
820
+ const client = useDashboardClient();
821
+ const [player, setPlayer] = useState17(null);
822
+ const [loading, setLoading] = useState17(true);
823
+ const [error, setError] = useState17(null);
824
+ const subRef = useRef18(null);
825
+ useEffect17(() => {
826
+ const sub = client.game.subscribePlayer(client.realtime, memberId);
827
+ subRef.current = sub;
828
+ sub.on("change", (data) => {
829
+ setPlayer(data.length > 0 ? data[0] : null);
830
+ setLoading(false);
831
+ setError(null);
832
+ });
833
+ sub.on("error", (err) => {
834
+ setError(err);
835
+ setLoading(false);
836
+ });
837
+ return () => {
838
+ sub.unsubscribe();
839
+ subRef.current = null;
840
+ };
841
+ }, [client, memberId]);
842
+ const refresh = useCallback15(async () => {
843
+ if (subRef.current) await subRef.current.refresh();
844
+ }, []);
845
+ return { player, loading, error, refresh };
846
+ }
847
+
848
+ // src/dashboard/useLeaderboard.ts
849
+ import { useState as useState18, useEffect as useEffect18, useRef as useRef19, useCallback as useCallback16 } from "react";
850
+ function useLeaderboard(period) {
851
+ const client = useDashboardClient();
852
+ const [entries, setEntries] = useState18([]);
853
+ const [loading, setLoading] = useState18(true);
854
+ const [error, setError] = useState18(null);
855
+ const subRef = useRef19(null);
856
+ useEffect18(() => {
857
+ const sub = client.game.subscribeLeaderboard(client.realtime, period ? { period } : void 0);
858
+ subRef.current = sub;
859
+ sub.on("change", (data) => {
860
+ setEntries(data);
861
+ setLoading(false);
862
+ setError(null);
863
+ });
864
+ sub.on("error", (err) => {
865
+ setError(err);
866
+ setLoading(false);
867
+ });
868
+ return () => {
869
+ sub.unsubscribe();
870
+ subRef.current = null;
871
+ };
872
+ }, [client, period]);
873
+ const refresh = useCallback16(async () => {
874
+ if (subRef.current) await subRef.current.refresh();
875
+ }, []);
876
+ return { entries, loading, error, refresh };
877
+ }
878
+
879
+ // src/dashboard/useGameActivity.ts
880
+ import { useState as useState19, useEffect as useEffect19, useRef as useRef20, useCallback as useCallback17 } from "react";
881
+ function useGameActivity() {
882
+ const client = useDashboardClient();
883
+ const [events, setEvents] = useState19([]);
884
+ const [loading, setLoading] = useState19(true);
885
+ const [error, setError] = useState19(null);
886
+ const subRef = useRef20(null);
887
+ useEffect19(() => {
888
+ const sub = client.game.subscribeActivity(client.realtime);
889
+ subRef.current = sub;
890
+ sub.on("change", (data) => {
891
+ setEvents(data);
892
+ setLoading(false);
893
+ setError(null);
894
+ });
895
+ sub.on("error", (err) => {
896
+ setError(err);
897
+ setLoading(false);
898
+ });
899
+ return () => {
900
+ sub.unsubscribe();
901
+ subRef.current = null;
902
+ };
903
+ }, [client]);
904
+ const refresh = useCallback17(async () => {
905
+ if (subRef.current) await subRef.current.refresh();
906
+ }, []);
907
+ return { events, loading, error, refresh };
908
+ }
621
909
  export {
622
910
  CStarChatContext,
623
911
  CStarChatProvider,
624
912
  CStarCommunityContext,
625
913
  CStarCommunityProvider,
914
+ CStarDashboardProvider,
626
915
  CStarLibraryContext,
627
916
  CStarLibraryProvider,
628
917
  useArticle,
629
918
  useArticleSearch,
630
919
  useArticles,
920
+ useBossBattle,
921
+ useCStarEvents,
922
+ useCStarMutation,
923
+ useCStarQuery,
631
924
  useCategories,
632
925
  useChat,
633
926
  useChatClient,
634
927
  useCommunityClient,
635
928
  useCommunitySearch,
636
929
  useConversations,
930
+ useDashboardClient,
931
+ useGameActivity,
932
+ useLeaderboard,
637
933
  useLibraryClient,
638
934
  useMessages,
935
+ usePlayerStats,
639
936
  usePost,
640
937
  usePosts,
641
938
  useTopics,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cstar.help/react",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "React hooks for the cStar customer support platform",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",