@shellapps/experience-react 1.5.0 → 1.5.1

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.d.mts CHANGED
@@ -1,15 +1,15 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import * as React from 'react';
3
- import React__default from 'react';
4
2
  import * as _shellapps_experience from '@shellapps/experience';
5
3
  import { ExperienceConfig, Experience } from '@shellapps/experience';
4
+ import * as React from 'react';
5
+ import React__default from 'react';
6
6
 
7
7
  interface ExperienceProviderProps {
8
8
  appId: string;
9
9
  apiKey: string;
10
10
  profileId?: string;
11
11
  options?: Partial<Omit<ExperienceConfig, 'appId' | 'apiKey'>>;
12
- children: React__default.ReactNode;
12
+ children: any;
13
13
  }
14
14
  declare function ExperienceProvider({ appId, apiKey, profileId, options, children }: ExperienceProviderProps): react_jsx_runtime.JSX.Element;
15
15
 
@@ -17,7 +17,7 @@ interface ErrorBoundaryProps {
17
17
  fallback?: React__default.ReactNode;
18
18
  showCommentForm?: boolean;
19
19
  onError?: (error: Error, errorInfo: React__default.ErrorInfo) => void;
20
- children: React__default.ReactNode;
20
+ children: any;
21
21
  }
22
22
  interface ErrorBoundaryState {
23
23
  hasError: boolean;
@@ -32,7 +32,7 @@ declare class ErrorBoundary extends React__default.Component<ErrorBoundaryProps,
32
32
  static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState>;
33
33
  componentDidCatch(error: Error, errorInfo: React__default.ErrorInfo): void;
34
34
  private handleSubmitComment;
35
- render(): string | number | boolean | Iterable<React__default.ReactNode> | react_jsx_runtime.JSX.Element | null | undefined;
35
+ render(): any;
36
36
  }
37
37
 
38
38
  interface FeedbackButtonProps {
package/dist/index.d.ts CHANGED
@@ -1,15 +1,15 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import * as React from 'react';
3
- import React__default from 'react';
4
2
  import * as _shellapps_experience from '@shellapps/experience';
5
3
  import { ExperienceConfig, Experience } from '@shellapps/experience';
4
+ import * as React from 'react';
5
+ import React__default from 'react';
6
6
 
7
7
  interface ExperienceProviderProps {
8
8
  appId: string;
9
9
  apiKey: string;
10
10
  profileId?: string;
11
11
  options?: Partial<Omit<ExperienceConfig, 'appId' | 'apiKey'>>;
12
- children: React__default.ReactNode;
12
+ children: any;
13
13
  }
14
14
  declare function ExperienceProvider({ appId, apiKey, profileId, options, children }: ExperienceProviderProps): react_jsx_runtime.JSX.Element;
15
15
 
@@ -17,7 +17,7 @@ interface ErrorBoundaryProps {
17
17
  fallback?: React__default.ReactNode;
18
18
  showCommentForm?: boolean;
19
19
  onError?: (error: Error, errorInfo: React__default.ErrorInfo) => void;
20
- children: React__default.ReactNode;
20
+ children: any;
21
21
  }
22
22
  interface ErrorBoundaryState {
23
23
  hasError: boolean;
@@ -32,7 +32,7 @@ declare class ErrorBoundary extends React__default.Component<ErrorBoundaryProps,
32
32
  static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState>;
33
33
  componentDidCatch(error: Error, errorInfo: React__default.ErrorInfo): void;
34
34
  private handleSubmitComment;
35
- render(): string | number | boolean | Iterable<React__default.ReactNode> | react_jsx_runtime.JSX.Element | null | undefined;
35
+ render(): any;
36
36
  }
37
37
 
38
38
  interface FeedbackButtonProps {
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  "use strict";
2
3
  var __create = Object.create;
3
4
  var __defProp = Object.defineProperty;
@@ -53,11 +54,20 @@ var ExperienceContext = (0, import_react.createContext)(null);
53
54
  var import_jsx_runtime = require("react/jsx-runtime");
54
55
  function ExperienceProvider({ appId, apiKey, profileId, options, children }) {
55
56
  const instanceRef = (0, import_react2.useRef)(null);
56
- if (!instanceRef.current) {
57
+ const [, forceUpdate] = (0, import_react2.useState)(0);
58
+ if (typeof window !== "undefined" && !instanceRef.current) {
57
59
  const instance = import_experience.Experience.init({ appId, apiKey, ...options });
58
60
  if (profileId) instance.identify(profileId);
59
61
  instanceRef.current = instance;
60
62
  }
63
+ (0, import_react2.useEffect)(() => {
64
+ if (!instanceRef.current) {
65
+ const instance = import_experience.Experience.init({ appId, apiKey, ...options });
66
+ if (profileId) instance.identify(profileId);
67
+ instanceRef.current = instance;
68
+ forceUpdate((n) => n + 1);
69
+ }
70
+ }, []);
61
71
  (0, import_react2.useEffect)(() => {
62
72
  if (profileId && instanceRef.current) {
63
73
  instanceRef.current.identify(profileId);
@@ -764,7 +774,7 @@ function useTranslation() {
764
774
  }
765
775
  if (params) {
766
776
  for (const [k, v] of Object.entries(params)) {
767
- result = result.replace(new RegExp(`\\{\\{${k}\\}\\}`, "g"), String(v));
777
+ result = result.replace(new RegExp(`{{${k}}}`, "g"), String(v));
768
778
  }
769
779
  }
770
780
  return result;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/ExperienceProvider.tsx","../src/context.ts","../src/ErrorBoundary.tsx","../src/FeedbackButton.tsx","../src/hooks.ts"],"sourcesContent":["export { ExperienceProvider } from './ExperienceProvider';\nexport type { ExperienceProviderProps } from './ExperienceProvider';\nexport { ErrorBoundary } from './ErrorBoundary';\nexport { FeedbackButton } from './FeedbackButton';\nexport { useExperience, useTrack, useFlag, useTranslation } from './hooks';\nexport { ExperienceContext } from './context';\n","import React, { useEffect, useRef } from 'react';\nimport { Experience } from '@shellapps/experience';\nimport type { ExperienceConfig } from '@shellapps/experience';\nimport { ExperienceContext } from './context';\n\nexport interface ExperienceProviderProps {\n appId: string;\n apiKey: string;\n profileId?: string;\n options?: Partial<Omit<ExperienceConfig, 'appId' | 'apiKey'>>;\n children: React.ReactNode;\n}\n\nexport function ExperienceProvider({ appId, apiKey, profileId, options, children }: ExperienceProviderProps) {\n const instanceRef = useRef<Experience | null>(null);\n\n if (!instanceRef.current) {\n const instance = Experience.init({ appId, apiKey, ...options });\n if (profileId) instance.identify(profileId);\n instanceRef.current = instance;\n }\n\n useEffect(() => {\n if (profileId && instanceRef.current) {\n instanceRef.current.identify(profileId);\n }\n }, [profileId]);\n\n // data-t auto-tracking\n useEffect(() => {\n const handler = (e: MouseEvent) => {\n let el = e.target as HTMLElement | null;\n while (el) {\n const tid = el.getAttribute?.('data-t');\n if (tid) {\n instanceRef.current?.track('element_click', { elementTid: tid });\n break;\n }\n el = el.parentElement;\n }\n };\n document.addEventListener('click', handler, true);\n return () => document.removeEventListener('click', handler, true);\n }, []);\n\n useEffect(() => {\n return () => {\n instanceRef.current?.shutdown();\n };\n }, []);\n\n return (\n <ExperienceContext.Provider value={instanceRef.current}>\n {children}\n </ExperienceContext.Provider>\n );\n}\n","import { createContext } from 'react';\nimport type { Experience } from '@shellapps/experience';\n\nexport const ExperienceContext = createContext<Experience | null>(null);\n","import React from 'react';\nimport type { Experience } from '@shellapps/experience';\nimport { ExperienceContext } from './context';\n\ninterface ErrorBoundaryProps {\n fallback?: React.ReactNode;\n showCommentForm?: boolean;\n onError?: (error: Error, errorInfo: React.ErrorInfo) => void;\n children: React.ReactNode;\n}\n\ninterface ErrorBoundaryState {\n hasError: boolean;\n error: Error | null;\n comment: string;\n submitted: boolean;\n}\n\nexport class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {\n static contextType = ExperienceContext;\n declare context: Experience | null;\n\n state: ErrorBoundaryState = {\n hasError: false,\n error: null,\n comment: '',\n submitted: false,\n };\n\n static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> {\n return { hasError: true, error };\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {\n this.context?.captureError(error, {\n extra: { componentStack: errorInfo.componentStack || '' },\n severity: 'fatal' as any,\n });\n this.props.onError?.(error, errorInfo);\n }\n\n private handleSubmitComment = () => {\n if (this.state.comment.trim() && this.state.error) {\n this.context?.captureMessage(\n `User feedback: ${this.state.comment}`,\n 'info',\n );\n this.setState({ submitted: true });\n }\n };\n\n render() {\n if (this.state.hasError) {\n if (this.props.fallback) {\n return this.props.fallback;\n }\n\n return (\n <div style={{ padding: 20, textAlign: 'center' }}>\n <h2>Something went wrong</h2>\n <p>{this.state.error?.message}</p>\n {this.props.showCommentForm && !this.state.submitted && (\n <div>\n <textarea\n placeholder=\"Tell us what happened...\"\n value={this.state.comment}\n onChange={(e) => this.setState({ comment: e.target.value })}\n style={{ width: '100%', minHeight: 80, marginTop: 10 }}\n />\n <button onClick={this.handleSubmitComment} style={{ marginTop: 8 }}>\n Submit\n </button>\n </div>\n )}\n {this.state.submitted && <p>Thank you for your feedback!</p>}\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n","import React, { useState, useEffect, useCallback, useRef, useContext } from 'react';\nimport { ExperienceContext } from './context';\n\n// ── Types ──────────────────────────────────────────────────────────────────\n\ntype FeedbackType = 'bug' | 'feature-request' | 'comment' | 'praise';\ntype FeedbackStatus = 'new' | 'acknowledged' | 'in-progress' | 'resolved' | 'wont-fix';\n\ninterface FeedbackItem {\n _id: string;\n type: FeedbackType;\n title: string;\n content: string;\n status: FeedbackStatus;\n tags: string[];\n vote_count: number;\n reply_count: number;\n created_at: string;\n}\n\ninterface FeedbackReply {\n _id: string;\n author_type: 'user' | 'developer';\n author_name?: string;\n content: string;\n created_at: string;\n}\n\ninterface FeedbackButtonProps {\n /** Position of the floating button */\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\n /** Custom button label */\n label?: string;\n /** Button colour */\n color?: string;\n /** Hide the floating button (use exp.openFeedback() instead) */\n hidden?: boolean;\n /** API endpoint base URL */\n apiUrl?: string;\n /** Auth token for user endpoints */\n authToken?: string;\n}\n\n// ── Styles ─────────────────────────────────────────────────────────────────\n\nconst TYPE_META: Record<FeedbackType, { icon: string; label: string; color: string }> = {\n 'bug': { icon: '🐛', label: 'Bug Report', color: '#ef4444' },\n 'feature-request': { icon: '💡', label: 'Feature Request', color: '#8b5cf6' },\n 'comment': { icon: '💬', label: 'Comment', color: '#3b82f6' },\n 'praise': { icon: '🎉', label: 'Praise', color: '#10b981' },\n};\n\nconst STATUS_LABELS: Record<FeedbackStatus, string> = {\n 'new': '🆕 New',\n 'acknowledged': '👀 Acknowledged',\n 'in-progress': '🔧 In Progress',\n 'resolved': '✅ Resolved',\n 'wont-fix': '🚫 Won\\'t Fix',\n};\n\n// ── Component ──────────────────────────────────────────────────────────────\n\nexport function FeedbackButton({\n position = 'bottom-right',\n label = 'Feedback',\n color = '#6366f1',\n hidden = false,\n apiUrl,\n authToken,\n}: FeedbackButtonProps) {\n const experience = useContext(ExperienceContext);\n const [isOpen, setIsOpen] = useState(false);\n const [view, setView] = useState<'list' | 'new' | 'detail'>('list');\n const [selectedType, setSelectedType] = useState<FeedbackType>('comment');\n const [title, setTitle] = useState('');\n const [content, setContent] = useState('');\n const [submitting, setSubmitting] = useState(false);\n const [items, setItems] = useState<FeedbackItem[]>([]);\n const [selectedItem, setSelectedItem] = useState<FeedbackItem | null>(null);\n const [replies, setReplies] = useState<FeedbackReply[]>([]);\n const [replyText, setReplyText] = useState('');\n const [loading, setLoading] = useState(false);\n const [screenshot, setScreenshot] = useState<string | null>(null);\n const panelRef = useRef<HTMLDivElement>(null);\n\n const baseUrl = apiUrl || (experience as any)?.config?.apiUrl || 'https://experience.shellapps.com';\n const appId = (experience as any)?.config?.appId || '';\n\n const headers = useCallback((): Record<string, string> => {\n const h: Record<string, string> = { 'Content-Type': 'application/json' };\n if (authToken) h['Authorization'] = `Bearer ${authToken}`;\n return h;\n }, [authToken]);\n\n // Expose openFeedback on the experience instance\n useEffect(() => {\n if (experience) {\n (experience as any).openFeedback = () => setIsOpen(true);\n }\n return () => {\n if (experience) delete (experience as any).openFeedback;\n };\n }, [experience]);\n\n // Load feedback list when opening\n useEffect(() => {\n if (isOpen && view === 'list') {\n loadFeedback();\n }\n }, [isOpen, view]);\n\n const loadFeedback = async () => {\n setLoading(true);\n try {\n const res = await fetch(\n `${baseUrl}/api/v1/feedback?appId=${appId}&limit=20`,\n { headers: headers() },\n );\n if (res.ok) {\n const data = await res.json();\n setItems(data.items || []);\n }\n } catch {\n // silently fail\n }\n setLoading(false);\n };\n\n const loadDetail = async (item: FeedbackItem) => {\n setSelectedItem(item);\n setView('detail');\n try {\n const res = await fetch(\n `${baseUrl}/api/v1/feedback/${item._id}`,\n { headers: headers() },\n );\n if (res.ok) {\n const data = await res.json();\n setReplies(data.replies || []);\n }\n } catch {\n // silently fail\n }\n };\n\n const captureScreenshot = async () => {\n try {\n const html2canvas = (window as any).html2canvas;\n if (!html2canvas) return;\n const canvas = await html2canvas(document.body, { scale: 0.5, logging: false });\n setScreenshot(canvas.toDataURL('image/jpeg', 0.6));\n } catch {\n // screenshot not available\n }\n };\n\n const submit = async () => {\n if (!title.trim() || !content.trim()) return;\n setSubmitting(true);\n try {\n const ctx = {\n userAgent: navigator.userAgent,\n url: window.location.href,\n viewport: { width: window.innerWidth, height: window.innerHeight },\n locale: navigator.language,\n referrer: document.referrer || undefined,\n };\n\n const res = await fetch(`${baseUrl}/api/v1/feedback`, {\n method: 'POST',\n headers: headers(),\n body: JSON.stringify({\n appId,\n type: selectedType,\n title: title.trim(),\n content: content.trim(),\n context: ctx,\n screenshot: screenshot || undefined,\n }),\n });\n\n if (res.ok) {\n setTitle('');\n setContent('');\n setScreenshot(null);\n setView('list');\n loadFeedback();\n }\n } catch {\n // silently fail\n }\n setSubmitting(false);\n };\n\n const submitReply = async () => {\n if (!replyText.trim() || !selectedItem) return;\n try {\n const res = await fetch(`${baseUrl}/api/v1/feedback/${selectedItem._id}/reply`, {\n method: 'POST',\n headers: headers(),\n body: JSON.stringify({ content: replyText.trim() }),\n });\n if (res.ok) {\n const reply = await res.json();\n setReplies((prev) => [...prev, reply]);\n setReplyText('');\n }\n } catch {\n // silently fail\n }\n };\n\n const vote = async (id: string) => {\n try {\n await fetch(`${baseUrl}/api/v1/feedback/${id}/vote`, {\n method: 'POST',\n headers: headers(),\n body: JSON.stringify({ type: 'upvote' }),\n });\n loadFeedback();\n } catch {\n // silently fail\n }\n };\n\n // ── Position styles ──────────────────────────────────────────────────────\n\n const posStyles: Record<string, React.CSSProperties> = {\n 'bottom-right': { bottom: 20, right: 20 },\n 'bottom-left': { bottom: 20, left: 20 },\n 'top-right': { top: 20, right: 20 },\n 'top-left': { top: 20, left: 20 },\n };\n\n const panelPos: Record<string, React.CSSProperties> = {\n 'bottom-right': { bottom: 70, right: 20 },\n 'bottom-left': { bottom: 70, left: 20 },\n 'top-right': { top: 70, right: 20 },\n 'top-left': { top: 70, left: 20 },\n };\n\n // ── Render ───────────────────────────────────────────────────────────────\n\n return (\n <>\n {/* Floating button */}\n {!hidden && (\n <button\n onClick={() => setIsOpen(!isOpen)}\n style={{\n position: 'fixed',\n ...posStyles[position],\n zIndex: 99999,\n background: color,\n color: '#fff',\n border: 'none',\n borderRadius: 28,\n padding: '10px 20px',\n fontSize: 14,\n fontWeight: 600,\n cursor: 'pointer',\n boxShadow: '0 4px 12px rgba(0,0,0,0.15)',\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n }}\n >\n 💬 {label}\n </button>\n )}\n\n {/* Panel */}\n {isOpen && (\n <div\n ref={panelRef}\n style={{\n position: 'fixed',\n ...panelPos[position],\n zIndex: 100000,\n width: 380,\n maxHeight: 520,\n background: '#fff',\n borderRadius: 12,\n boxShadow: '0 8px 32px rgba(0,0,0,0.18)',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n fontSize: 14,\n color: '#1f2937',\n }}\n >\n {/* Header */}\n <div\n style={{\n padding: '14px 16px',\n background: color,\n color: '#fff',\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n }}\n >\n <span style={{ fontWeight: 600 }}>\n {view === 'new' ? 'New Feedback' : view === 'detail' ? 'Feedback' : 'Feedback'}\n </span>\n <div style={{ display: 'flex', gap: 8 }}>\n {view !== 'list' && (\n <button\n onClick={() => { setView('list'); setSelectedItem(null); }}\n style={{ background: 'none', border: 'none', color: '#fff', cursor: 'pointer', fontSize: 14 }}\n >\n ← Back\n </button>\n )}\n <button\n onClick={() => setIsOpen(false)}\n style={{ background: 'none', border: 'none', color: '#fff', cursor: 'pointer', fontSize: 18 }}\n >\n ✕\n </button>\n </div>\n </div>\n\n {/* Body */}\n <div style={{ flex: 1, overflow: 'auto', padding: 16 }}>\n {/* LIST VIEW */}\n {view === 'list' && (\n <>\n <button\n onClick={() => setView('new')}\n style={{\n width: '100%',\n padding: '10px',\n background: color,\n color: '#fff',\n border: 'none',\n borderRadius: 8,\n cursor: 'pointer',\n fontWeight: 600,\n marginBottom: 12,\n }}\n >\n + New Feedback\n </button>\n {loading ? (\n <p style={{ textAlign: 'center', color: '#9ca3af' }}>Loading…</p>\n ) : items.length === 0 ? (\n <p style={{ textAlign: 'center', color: '#9ca3af' }}>No feedback yet</p>\n ) : (\n items.map((item) => (\n <div\n key={item._id}\n onClick={() => loadDetail(item)}\n style={{\n padding: 10,\n borderRadius: 8,\n border: '1px solid #e5e7eb',\n marginBottom: 8,\n cursor: 'pointer',\n }}\n >\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 4 }}>\n <span>\n {TYPE_META[item.type]?.icon} <strong>{item.title}</strong>\n </span>\n {item.type === 'feature-request' && (\n <button\n onClick={(e) => { e.stopPropagation(); vote(item._id); }}\n style={{\n background: 'none',\n border: '1px solid #d1d5db',\n borderRadius: 4,\n padding: '2px 6px',\n cursor: 'pointer',\n fontSize: 12,\n }}\n >\n ▲ {item.vote_count}\n </button>\n )}\n </div>\n <div style={{ fontSize: 12, color: '#6b7280' }}>\n {STATUS_LABELS[item.status]} · {item.reply_count} replies · {new Date(item.created_at).toLocaleDateString()}\n </div>\n </div>\n ))\n )}\n </>\n )}\n\n {/* NEW FEEDBACK VIEW */}\n {view === 'new' && (\n <>\n {/* Type selector */}\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginBottom: 12 }}>\n {(Object.entries(TYPE_META) as [FeedbackType, typeof TYPE_META['bug']][]).map(\n ([type, meta]) => (\n <button\n key={type}\n onClick={() => setSelectedType(type)}\n style={{\n padding: 8,\n borderRadius: 8,\n border: `2px solid ${selectedType === type ? meta.color : '#e5e7eb'}`,\n background: selectedType === type ? `${meta.color}10` : '#fff',\n cursor: 'pointer',\n fontSize: 13,\n textAlign: 'center',\n }}\n >\n {meta.icon} {meta.label}\n </button>\n ),\n )}\n </div>\n\n <input\n placeholder=\"Title\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n style={{\n width: '100%',\n padding: 8,\n borderRadius: 6,\n border: '1px solid #d1d5db',\n marginBottom: 8,\n fontSize: 14,\n boxSizing: 'border-box',\n }}\n />\n\n <textarea\n placeholder=\"Describe your feedback…\"\n value={content}\n onChange={(e) => setContent(e.target.value)}\n rows={4}\n style={{\n width: '100%',\n padding: 8,\n borderRadius: 6,\n border: '1px solid #d1d5db',\n marginBottom: 8,\n fontSize: 14,\n resize: 'vertical',\n boxSizing: 'border-box',\n }}\n />\n\n {/* Screenshot button */}\n {(window as any).html2canvas && (\n <button\n onClick={captureScreenshot}\n style={{\n background: 'none',\n border: '1px solid #d1d5db',\n borderRadius: 6,\n padding: '6px 10px',\n cursor: 'pointer',\n fontSize: 12,\n marginBottom: 8,\n color: screenshot ? '#10b981' : '#6b7280',\n }}\n >\n 📷 {screenshot ? 'Screenshot captured ✓' : 'Capture screenshot'}\n </button>\n )}\n\n <button\n onClick={submit}\n disabled={submitting || !title.trim() || !content.trim()}\n style={{\n width: '100%',\n padding: 10,\n background: submitting ? '#9ca3af' : color,\n color: '#fff',\n border: 'none',\n borderRadius: 8,\n cursor: submitting ? 'default' : 'pointer',\n fontWeight: 600,\n }}\n >\n {submitting ? 'Submitting…' : 'Submit'}\n </button>\n </>\n )}\n\n {/* DETAIL VIEW */}\n {view === 'detail' && selectedItem && (\n <>\n <div style={{ marginBottom: 12 }}>\n <div style={{ fontSize: 12, color: TYPE_META[selectedItem.type]?.color, fontWeight: 600, marginBottom: 4 }}>\n {TYPE_META[selectedItem.type]?.icon} {TYPE_META[selectedItem.type]?.label}\n </div>\n <h3 style={{ margin: '0 0 4px', fontSize: 16 }}>{selectedItem.title}</h3>\n <div style={{ fontSize: 12, color: '#6b7280', marginBottom: 8 }}>\n {STATUS_LABELS[selectedItem.status]} · {new Date(selectedItem.created_at).toLocaleDateString()}\n </div>\n <p style={{ margin: 0, lineHeight: 1.5 }}>{selectedItem.content}</p>\n </div>\n\n <hr style={{ border: 'none', borderTop: '1px solid #e5e7eb', margin: '12px 0' }} />\n\n {/* Replies */}\n <div style={{ marginBottom: 12 }}>\n <strong style={{ fontSize: 13 }}>Replies ({replies.length})</strong>\n {replies.map((r) => (\n <div\n key={r._id}\n style={{\n padding: 8,\n marginTop: 8,\n borderRadius: 6,\n background: r.author_type === 'developer' ? '#eff6ff' : '#f9fafb',\n borderLeft: `3px solid ${r.author_type === 'developer' ? '#3b82f6' : '#d1d5db'}`,\n }}\n >\n <div style={{ fontSize: 11, color: '#6b7280', marginBottom: 2 }}>\n {r.author_type === 'developer' ? '🛠️ Developer' : '👤 You'}{r.author_name ? ` · ${r.author_name}` : ''} · {new Date(r.created_at).toLocaleDateString()}\n </div>\n <div style={{ fontSize: 13 }}>{r.content}</div>\n </div>\n ))}\n </div>\n\n {/* Reply input */}\n <div style={{ display: 'flex', gap: 6 }}>\n <input\n placeholder=\"Write a reply…\"\n value={replyText}\n onChange={(e) => setReplyText(e.target.value)}\n onKeyDown={(e) => { if (e.key === 'Enter') submitReply(); }}\n style={{\n flex: 1,\n padding: 8,\n borderRadius: 6,\n border: '1px solid #d1d5db',\n fontSize: 13,\n }}\n />\n <button\n onClick={submitReply}\n disabled={!replyText.trim()}\n style={{\n padding: '8px 12px',\n background: color,\n color: '#fff',\n border: 'none',\n borderRadius: 6,\n cursor: 'pointer',\n fontSize: 13,\n }}\n >\n Send\n </button>\n </div>\n </>\n )}\n </div>\n </div>\n )}\n </>\n );\n}\n","import { useContext, useState, useCallback, useMemo, useEffect, useRef } from 'react';\nimport { ExperienceContext } from './context';\n\nexport function useExperience() {\n const ctx = useContext(ExperienceContext);\n if (!ctx) throw new Error('useExperience must be used within ExperienceProvider');\n return ctx;\n}\n\nexport function useTrack() {\n const experience = useExperience();\n return useMemo(() => ({\n track: (eventName: string, metadata?: Record<string, string>) => experience.track(eventName, metadata),\n trackPageView: () => experience.trackPageView(),\n }), [experience]);\n}\n\nexport function useFlag<T>(flagName: string, defaultValue: T): T {\n const experience = useExperience();\n return experience.getFlag(flagName, defaultValue);\n}\n\n// ─── Translation Types ──────────────────────────────────────────\n\ninterface TranslationResult {\n t: (key: string, params?: Record<string, string | number>) => string;\n locale: string;\n setLocale: (locale: string) => void;\n locales: Array<{ code: string; name: string; progress?: number }>;\n isLoading: boolean;\n}\n\ninterface TranslationCache {\n [locale: string]: Record<string, string>;\n}\n\n// Module-level cache shared across hook instances\nconst translationCache: TranslationCache = {};\nconst fetchPromises = new Map<string, Promise<Record<string, string>>>();\n\n/**\n * Translation hook for the Experience Platform.\n *\n * Fetches translations from the Experience API, caches in memory + localStorage,\n * and provides `t()` for interpolation and `setLocale()` for switching.\n *\n * Usage:\n * ```tsx\n * const { t, locale, setLocale, locales } = useTranslation();\n * return <h1>{t('greeting', { name: 'Alex' })}</h1>;\n * ```\n */\nexport function useTranslation(): TranslationResult {\n const experience = useExperience();\n const config = (experience as any).config || {};\n const appId: string = config.appId || '';\n const apiBaseUrl: string = config.apiUrl || config.baseUrl || 'https://experience-api.shellapps.com';\n\n const [locale, setLocaleState] = useState(() => {\n if (typeof window === 'undefined') return 'en';\n // Check localStorage for persisted locale preference\n const saved = localStorage.getItem(`exp_locale_${appId}`);\n if (saved) return saved;\n return navigator.language?.split('-')[0] || 'en';\n });\n\n const [translations, setTranslations] = useState<Record<string, string>>(() => {\n // Try memory cache first\n if (translationCache[locale]) return translationCache[locale]!;\n // Try localStorage\n if (typeof window !== 'undefined') {\n try {\n const cached = localStorage.getItem(`exp_translations_${appId}_${locale}`);\n if (cached) {\n const parsed = JSON.parse(cached);\n translationCache[locale] = parsed;\n return parsed;\n }\n } catch { /* ignore */ }\n }\n return {};\n });\n\n const [locales, setLocales] = useState<Array<{ code: string; name: string; progress?: number }>>([]);\n const [isLoading, setIsLoading] = useState(false);\n\n // Fetch translations for a locale\n const fetchTranslations = useCallback(async (loc: string): Promise<Record<string, string>> => {\n if (!appId) return {};\n\n // Deduplicate in-flight requests\n const cacheKey = `${appId}_${loc}`;\n const existing = fetchPromises.get(cacheKey);\n if (existing) return existing;\n\n const promise = (async () => {\n try {\n const res = await fetch(`${apiBaseUrl}/api/v1/translations/${appId}/${loc}`);\n if (!res.ok) return {};\n const data = await res.json();\n // Cache in memory and localStorage\n translationCache[loc] = data;\n if (typeof window !== 'undefined') {\n try {\n localStorage.setItem(`exp_translations_${appId}_${loc}`, JSON.stringify(data));\n } catch { /* quota exceeded, ignore */ }\n }\n return data as Record<string, string>;\n } catch {\n return {};\n } finally {\n fetchPromises.delete(cacheKey);\n }\n })();\n\n fetchPromises.set(cacheKey, promise);\n return promise;\n }, [appId, apiBaseUrl]);\n\n // Fetch available locales\n useEffect(() => {\n if (!appId) return;\n fetch(`${apiBaseUrl}/api/v1/translations/${appId}/locales`)\n .then(res => res.ok ? res.json() : { locales: [] })\n .then(data => setLocales(data.locales || []))\n .catch(() => {});\n }, [appId, apiBaseUrl]);\n\n // Fetch translations when locale changes\n useEffect(() => {\n if (!appId) return;\n\n // Use cached if available\n if (translationCache[locale]) {\n setTranslations(translationCache[locale]!);\n return;\n }\n\n setIsLoading(true);\n fetchTranslations(locale).then(data => {\n setTranslations(data);\n setIsLoading(false);\n });\n }, [locale, appId, fetchTranslations]);\n\n // t() function with interpolation and plural support\n const t = useCallback((key: string, params?: Record<string, string | number>): string => {\n let result = translations[key] ?? key;\n\n // Handle plurals: \"singular||plural\" with count param\n if (result.includes('||') && params && 'count' in params) {\n const parts = result.split('||');\n const count = Number(params.count);\n result = count === 1 ? (parts[0] ?? result) : (parts[1] ?? result);\n }\n\n // Handle interpolation: {{variable}}\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n result = result.replace(new RegExp(`\\\\{\\\\{${k}\\\\}\\\\}`, 'g'), String(v));\n }\n }\n\n return result;\n }, [translations]);\n\n // setLocale with persistence\n const setLocale = useCallback((newLocale: string) => {\n setLocaleState(newLocale);\n if (typeof window !== 'undefined') {\n localStorage.setItem(`exp_locale_${appId}`, newLocale);\n }\n }, [appId]);\n\n return { t, locale, setLocale, locales, isLoading };\n}\n"],"mappings":"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","names":["import_react","import_react","import_jsx_runtime","React","import_react","import_jsx_runtime","import_react"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/ExperienceProvider.tsx","../src/context.ts","../src/ErrorBoundary.tsx","../src/FeedbackButton.tsx","../src/hooks.ts"],"sourcesContent":["export { ExperienceProvider } from './ExperienceProvider';\nexport type { ExperienceProviderProps } from './ExperienceProvider';\nexport { ErrorBoundary } from './ErrorBoundary';\nexport { FeedbackButton } from './FeedbackButton';\nexport { useExperience, useTrack, useFlag, useTranslation } from './hooks';\nexport { ExperienceContext } from './context';\n","'use client';\n\nimport React, { useEffect, useRef, useState } from 'react';\nimport { Experience } from '@shellapps/experience';\nimport type { ExperienceConfig } from '@shellapps/experience';\nimport { ExperienceContext } from './context';\n\nexport interface ExperienceProviderProps {\n appId: string;\n apiKey: string;\n profileId?: string;\n options?: Partial<Omit<ExperienceConfig, 'appId' | 'apiKey'>>;\n children: any;\n}\n\nexport function ExperienceProvider({ appId, apiKey, profileId, options, children }: ExperienceProviderProps) {\n const instanceRef = useRef<Experience | null>(null);\n const [, forceUpdate] = useState(0);\n\n // Only initialize on the client (browser) — skip during SSR/prerendering\n if (typeof window !== 'undefined' && !instanceRef.current) {\n const instance = Experience.init({ appId, apiKey, ...options });\n if (profileId) instance.identify(profileId);\n instanceRef.current = instance;\n }\n\n useEffect(() => {\n // Handle case where ref wasn't set during SSR render pass\n if (!instanceRef.current) {\n const instance = Experience.init({ appId, apiKey, ...options });\n if (profileId) instance.identify(profileId);\n instanceRef.current = instance;\n forceUpdate((n) => n + 1);\n }\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n useEffect(() => {\n if (profileId && instanceRef.current) {\n instanceRef.current.identify(profileId);\n }\n }, [profileId]);\n\n // data-t auto-tracking\n useEffect(() => {\n const handler = (e: MouseEvent) => {\n let el = e.target as HTMLElement | null;\n while (el) {\n const tid = el.getAttribute?.('data-t');\n if (tid) {\n instanceRef.current?.track('element_click', { elementTid: tid });\n break;\n }\n el = el.parentElement;\n }\n };\n document.addEventListener('click', handler, true);\n return () => document.removeEventListener('click', handler, true);\n }, []);\n\n useEffect(() => {\n return () => {\n instanceRef.current?.shutdown();\n };\n }, []);\n\n return (\n <ExperienceContext.Provider value={instanceRef.current}>\n {children}\n </ExperienceContext.Provider>\n );\n}\n","'use client';\nimport { createContext } from 'react';\nimport type { Experience } from '@shellapps/experience';\n\nexport const ExperienceContext = createContext<Experience | null>(null);\n","'use client';\nimport React from 'react';\nimport type { Experience } from '@shellapps/experience';\nimport { ExperienceContext } from './context';\n\ninterface ErrorBoundaryProps {\n fallback?: React.ReactNode;\n showCommentForm?: boolean;\n onError?: (error: Error, errorInfo: React.ErrorInfo) => void;\n children: any;\n}\n\ninterface ErrorBoundaryState {\n hasError: boolean;\n error: Error | null;\n comment: string;\n submitted: boolean;\n}\n\nexport class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {\n static contextType = ExperienceContext;\n declare context: Experience | null;\n\n state: ErrorBoundaryState = {\n hasError: false,\n error: null,\n comment: '',\n submitted: false,\n };\n\n static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> {\n return { hasError: true, error };\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {\n this.context?.captureError(error, {\n extra: { componentStack: errorInfo.componentStack || '' },\n severity: 'fatal' as any,\n });\n this.props.onError?.(error, errorInfo);\n }\n\n private handleSubmitComment = () => {\n if (this.state.comment.trim() && this.state.error) {\n this.context?.captureMessage(\n `User feedback: ${this.state.comment}`,\n 'info',\n );\n this.setState({ submitted: true });\n }\n };\n\n render() {\n if (this.state.hasError) {\n if (this.props.fallback) {\n return this.props.fallback;\n }\n\n return (\n <div style={{ padding: 20, textAlign: 'center' }}>\n <h2>Something went wrong</h2>\n <p>{this.state.error?.message}</p>\n {this.props.showCommentForm && !this.state.submitted && (\n <div>\n <textarea\n placeholder=\"Tell us what happened...\"\n value={this.state.comment}\n onChange={(e) => this.setState({ comment: e.target.value })}\n style={{ width: '100%', minHeight: 80, marginTop: 10 }}\n />\n <button onClick={this.handleSubmitComment} style={{ marginTop: 8 }}>\n Submit\n </button>\n </div>\n )}\n {this.state.submitted && <p>Thank you for your feedback!</p>}\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n","'use client';\nimport React, { useState, useEffect, useCallback, useRef, useContext } from 'react';\nimport { ExperienceContext } from './context';\n\n// ── Types ──────────────────────────────────────────────────────────────────\n\ntype FeedbackType = 'bug' | 'feature-request' | 'comment' | 'praise';\ntype FeedbackStatus = 'new' | 'acknowledged' | 'in-progress' | 'resolved' | 'wont-fix';\n\ninterface FeedbackItem {\n _id: string;\n type: FeedbackType;\n title: string;\n content: string;\n status: FeedbackStatus;\n tags: string[];\n vote_count: number;\n reply_count: number;\n created_at: string;\n}\n\ninterface FeedbackReply {\n _id: string;\n author_type: 'user' | 'developer';\n author_name?: string;\n content: string;\n created_at: string;\n}\n\ninterface FeedbackButtonProps {\n /** Position of the floating button */\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\n /** Custom button label */\n label?: string;\n /** Button colour */\n color?: string;\n /** Hide the floating button (use exp.openFeedback() instead) */\n hidden?: boolean;\n /** API endpoint base URL */\n apiUrl?: string;\n /** Auth token for user endpoints */\n authToken?: string;\n}\n\n// ── Styles ─────────────────────────────────────────────────────────────────\n\nconst TYPE_META: Record<FeedbackType, { icon: string; label: string; color: string }> = {\n 'bug': { icon: '🐛', label: 'Bug Report', color: '#ef4444' },\n 'feature-request': { icon: '💡', label: 'Feature Request', color: '#8b5cf6' },\n 'comment': { icon: '💬', label: 'Comment', color: '#3b82f6' },\n 'praise': { icon: '🎉', label: 'Praise', color: '#10b981' },\n};\n\nconst STATUS_LABELS: Record<FeedbackStatus, string> = {\n 'new': '🆕 New',\n 'acknowledged': '👀 Acknowledged',\n 'in-progress': '🔧 In Progress',\n 'resolved': '✅ Resolved',\n 'wont-fix': '🚫 Won\\'t Fix',\n};\n\n// ── Component ──────────────────────────────────────────────────────────────\n\nexport function FeedbackButton({\n position = 'bottom-right',\n label = 'Feedback',\n color = '#6366f1',\n hidden = false,\n apiUrl,\n authToken,\n}: FeedbackButtonProps) {\n const experience = useContext(ExperienceContext);\n const [isOpen, setIsOpen] = useState(false);\n const [view, setView] = useState<'list' | 'new' | 'detail'>('list');\n const [selectedType, setSelectedType] = useState<FeedbackType>('comment');\n const [title, setTitle] = useState('');\n const [content, setContent] = useState('');\n const [submitting, setSubmitting] = useState(false);\n const [items, setItems] = useState<FeedbackItem[]>([]);\n const [selectedItem, setSelectedItem] = useState<FeedbackItem | null>(null);\n const [replies, setReplies] = useState<FeedbackReply[]>([]);\n const [replyText, setReplyText] = useState('');\n const [loading, setLoading] = useState(false);\n const [screenshot, setScreenshot] = useState<string | null>(null);\n const panelRef = useRef<HTMLDivElement>(null);\n\n const baseUrl = apiUrl || (experience as any)?.config?.apiUrl || 'https://experience.shellapps.com';\n const appId = (experience as any)?.config?.appId || '';\n\n const headers = useCallback((): Record<string, string> => {\n const h: Record<string, string> = { 'Content-Type': 'application/json' };\n if (authToken) h['Authorization'] = `Bearer ${authToken}`;\n return h;\n }, [authToken]);\n\n // Expose openFeedback on the experience instance\n useEffect(() => {\n if (experience) {\n (experience as any).openFeedback = () => setIsOpen(true);\n }\n return () => {\n if (experience) delete (experience as any).openFeedback;\n };\n }, [experience]);\n\n // Load feedback list when opening\n useEffect(() => {\n if (isOpen && view === 'list') {\n loadFeedback();\n }\n }, [isOpen, view]);\n\n const loadFeedback = async () => {\n setLoading(true);\n try {\n const res = await fetch(\n `${baseUrl}/api/v1/feedback?appId=${appId}&limit=20`,\n { headers: headers() },\n );\n if (res.ok) {\n const data = await res.json();\n setItems(data.items || []);\n }\n } catch {\n // silently fail\n }\n setLoading(false);\n };\n\n const loadDetail = async (item: FeedbackItem) => {\n setSelectedItem(item);\n setView('detail');\n try {\n const res = await fetch(\n `${baseUrl}/api/v1/feedback/${item._id}`,\n { headers: headers() },\n );\n if (res.ok) {\n const data = await res.json();\n setReplies(data.replies || []);\n }\n } catch {\n // silently fail\n }\n };\n\n const captureScreenshot = async () => {\n try {\n const html2canvas = (window as any).html2canvas;\n if (!html2canvas) return;\n const canvas = await html2canvas(document.body, { scale: 0.5, logging: false });\n setScreenshot(canvas.toDataURL('image/jpeg', 0.6));\n } catch {\n // screenshot not available\n }\n };\n\n const submit = async () => {\n if (!title.trim() || !content.trim()) return;\n setSubmitting(true);\n try {\n const ctx = {\n userAgent: navigator.userAgent,\n url: window.location.href,\n viewport: { width: window.innerWidth, height: window.innerHeight },\n locale: navigator.language,\n referrer: document.referrer || undefined,\n };\n\n const res = await fetch(`${baseUrl}/api/v1/feedback`, {\n method: 'POST',\n headers: headers(),\n body: JSON.stringify({\n appId,\n type: selectedType,\n title: title.trim(),\n content: content.trim(),\n context: ctx,\n screenshot: screenshot || undefined,\n }),\n });\n\n if (res.ok) {\n setTitle('');\n setContent('');\n setScreenshot(null);\n setView('list');\n loadFeedback();\n }\n } catch {\n // silently fail\n }\n setSubmitting(false);\n };\n\n const submitReply = async () => {\n if (!replyText.trim() || !selectedItem) return;\n try {\n const res = await fetch(`${baseUrl}/api/v1/feedback/${selectedItem._id}/reply`, {\n method: 'POST',\n headers: headers(),\n body: JSON.stringify({ content: replyText.trim() }),\n });\n if (res.ok) {\n const reply = await res.json();\n setReplies((prev) => [...prev, reply]);\n setReplyText('');\n }\n } catch {\n // silently fail\n }\n };\n\n const vote = async (id: string) => {\n try {\n await fetch(`${baseUrl}/api/v1/feedback/${id}/vote`, {\n method: 'POST',\n headers: headers(),\n body: JSON.stringify({ type: 'upvote' }),\n });\n loadFeedback();\n } catch {\n // silently fail\n }\n };\n\n // ── Position styles ──────────────────────────────────────────────────────\n\n const posStyles: Record<string, React.CSSProperties> = {\n 'bottom-right': { bottom: 20, right: 20 },\n 'bottom-left': { bottom: 20, left: 20 },\n 'top-right': { top: 20, right: 20 },\n 'top-left': { top: 20, left: 20 },\n };\n\n const panelPos: Record<string, React.CSSProperties> = {\n 'bottom-right': { bottom: 70, right: 20 },\n 'bottom-left': { bottom: 70, left: 20 },\n 'top-right': { top: 70, right: 20 },\n 'top-left': { top: 70, left: 20 },\n };\n\n // ── Render ───────────────────────────────────────────────────────────────\n\n return (\n <>\n {/* Floating button */}\n {!hidden && (\n <button\n onClick={() => setIsOpen(!isOpen)}\n style={{\n position: 'fixed',\n ...posStyles[position],\n zIndex: 99999,\n background: color,\n color: '#fff',\n border: 'none',\n borderRadius: 28,\n padding: '10px 20px',\n fontSize: 14,\n fontWeight: 600,\n cursor: 'pointer',\n boxShadow: '0 4px 12px rgba(0,0,0,0.15)',\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n }}\n >\n 💬 {label}\n </button>\n )}\n\n {/* Panel */}\n {isOpen && (\n <div\n ref={panelRef}\n style={{\n position: 'fixed',\n ...panelPos[position],\n zIndex: 100000,\n width: 380,\n maxHeight: 520,\n background: '#fff',\n borderRadius: 12,\n boxShadow: '0 8px 32px rgba(0,0,0,0.18)',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n fontSize: 14,\n color: '#1f2937',\n }}\n >\n {/* Header */}\n <div\n style={{\n padding: '14px 16px',\n background: color,\n color: '#fff',\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n }}\n >\n <span style={{ fontWeight: 600 }}>\n {view === 'new' ? 'New Feedback' : view === 'detail' ? 'Feedback' : 'Feedback'}\n </span>\n <div style={{ display: 'flex', gap: 8 }}>\n {view !== 'list' && (\n <button\n onClick={() => { setView('list'); setSelectedItem(null); }}\n style={{ background: 'none', border: 'none', color: '#fff', cursor: 'pointer', fontSize: 14 }}\n >\n ← Back\n </button>\n )}\n <button\n onClick={() => setIsOpen(false)}\n style={{ background: 'none', border: 'none', color: '#fff', cursor: 'pointer', fontSize: 18 }}\n >\n ✕\n </button>\n </div>\n </div>\n\n {/* Body */}\n <div style={{ flex: 1, overflow: 'auto', padding: 16 }}>\n {/* LIST VIEW */}\n {view === 'list' && (\n <>\n <button\n onClick={() => setView('new')}\n style={{\n width: '100%',\n padding: '10px',\n background: color,\n color: '#fff',\n border: 'none',\n borderRadius: 8,\n cursor: 'pointer',\n fontWeight: 600,\n marginBottom: 12,\n }}\n >\n + New Feedback\n </button>\n {loading ? (\n <p style={{ textAlign: 'center', color: '#9ca3af' }}>Loading…</p>\n ) : items.length === 0 ? (\n <p style={{ textAlign: 'center', color: '#9ca3af' }}>No feedback yet</p>\n ) : (\n items.map((item) => (\n <div\n key={item._id}\n onClick={() => loadDetail(item)}\n style={{\n padding: 10,\n borderRadius: 8,\n border: '1px solid #e5e7eb',\n marginBottom: 8,\n cursor: 'pointer',\n }}\n >\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 4 }}>\n <span>\n {TYPE_META[item.type]?.icon} <strong>{item.title}</strong>\n </span>\n {item.type === 'feature-request' && (\n <button\n onClick={(e) => { e.stopPropagation(); vote(item._id); }}\n style={{\n background: 'none',\n border: '1px solid #d1d5db',\n borderRadius: 4,\n padding: '2px 6px',\n cursor: 'pointer',\n fontSize: 12,\n }}\n >\n ▲ {item.vote_count}\n </button>\n )}\n </div>\n <div style={{ fontSize: 12, color: '#6b7280' }}>\n {STATUS_LABELS[item.status]} · {item.reply_count} replies · {new Date(item.created_at).toLocaleDateString()}\n </div>\n </div>\n ))\n )}\n </>\n )}\n\n {/* NEW FEEDBACK VIEW */}\n {view === 'new' && (\n <>\n {/* Type selector */}\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginBottom: 12 }}>\n {(Object.entries(TYPE_META) as [FeedbackType, typeof TYPE_META['bug']][]).map(\n ([type, meta]) => (\n <button\n key={type}\n onClick={() => setSelectedType(type)}\n style={{\n padding: 8,\n borderRadius: 8,\n border: `2px solid ${selectedType === type ? meta.color : '#e5e7eb'}`,\n background: selectedType === type ? `${meta.color}10` : '#fff',\n cursor: 'pointer',\n fontSize: 13,\n textAlign: 'center',\n }}\n >\n {meta.icon} {meta.label}\n </button>\n ),\n )}\n </div>\n\n <input\n placeholder=\"Title\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n style={{\n width: '100%',\n padding: 8,\n borderRadius: 6,\n border: '1px solid #d1d5db',\n marginBottom: 8,\n fontSize: 14,\n boxSizing: 'border-box',\n }}\n />\n\n <textarea\n placeholder=\"Describe your feedback…\"\n value={content}\n onChange={(e) => setContent(e.target.value)}\n rows={4}\n style={{\n width: '100%',\n padding: 8,\n borderRadius: 6,\n border: '1px solid #d1d5db',\n marginBottom: 8,\n fontSize: 14,\n resize: 'vertical',\n boxSizing: 'border-box',\n }}\n />\n\n {/* Screenshot button */}\n {(window as any).html2canvas && (\n <button\n onClick={captureScreenshot}\n style={{\n background: 'none',\n border: '1px solid #d1d5db',\n borderRadius: 6,\n padding: '6px 10px',\n cursor: 'pointer',\n fontSize: 12,\n marginBottom: 8,\n color: screenshot ? '#10b981' : '#6b7280',\n }}\n >\n 📷 {screenshot ? 'Screenshot captured ✓' : 'Capture screenshot'}\n </button>\n )}\n\n <button\n onClick={submit}\n disabled={submitting || !title.trim() || !content.trim()}\n style={{\n width: '100%',\n padding: 10,\n background: submitting ? '#9ca3af' : color,\n color: '#fff',\n border: 'none',\n borderRadius: 8,\n cursor: submitting ? 'default' : 'pointer',\n fontWeight: 600,\n }}\n >\n {submitting ? 'Submitting…' : 'Submit'}\n </button>\n </>\n )}\n\n {/* DETAIL VIEW */}\n {view === 'detail' && selectedItem && (\n <>\n <div style={{ marginBottom: 12 }}>\n <div style={{ fontSize: 12, color: TYPE_META[selectedItem.type]?.color, fontWeight: 600, marginBottom: 4 }}>\n {TYPE_META[selectedItem.type]?.icon} {TYPE_META[selectedItem.type]?.label}\n </div>\n <h3 style={{ margin: '0 0 4px', fontSize: 16 }}>{selectedItem.title}</h3>\n <div style={{ fontSize: 12, color: '#6b7280', marginBottom: 8 }}>\n {STATUS_LABELS[selectedItem.status]} · {new Date(selectedItem.created_at).toLocaleDateString()}\n </div>\n <p style={{ margin: 0, lineHeight: 1.5 }}>{selectedItem.content}</p>\n </div>\n\n <hr style={{ border: 'none', borderTop: '1px solid #e5e7eb', margin: '12px 0' }} />\n\n {/* Replies */}\n <div style={{ marginBottom: 12 }}>\n <strong style={{ fontSize: 13 }}>Replies ({replies.length})</strong>\n {replies.map((r) => (\n <div\n key={r._id}\n style={{\n padding: 8,\n marginTop: 8,\n borderRadius: 6,\n background: r.author_type === 'developer' ? '#eff6ff' : '#f9fafb',\n borderLeft: `3px solid ${r.author_type === 'developer' ? '#3b82f6' : '#d1d5db'}`,\n }}\n >\n <div style={{ fontSize: 11, color: '#6b7280', marginBottom: 2 }}>\n {r.author_type === 'developer' ? '🛠️ Developer' : '👤 You'}{r.author_name ? ` · ${r.author_name}` : ''} · {new Date(r.created_at).toLocaleDateString()}\n </div>\n <div style={{ fontSize: 13 }}>{r.content}</div>\n </div>\n ))}\n </div>\n\n {/* Reply input */}\n <div style={{ display: 'flex', gap: 6 }}>\n <input\n placeholder=\"Write a reply…\"\n value={replyText}\n onChange={(e) => setReplyText(e.target.value)}\n onKeyDown={(e) => { if (e.key === 'Enter') submitReply(); }}\n style={{\n flex: 1,\n padding: 8,\n borderRadius: 6,\n border: '1px solid #d1d5db',\n fontSize: 13,\n }}\n />\n <button\n onClick={submitReply}\n disabled={!replyText.trim()}\n style={{\n padding: '8px 12px',\n background: color,\n color: '#fff',\n border: 'none',\n borderRadius: 6,\n cursor: 'pointer',\n fontSize: 13,\n }}\n >\n Send\n </button>\n </div>\n </>\n )}\n </div>\n </div>\n )}\n </>\n );\n}\n","'use client';\nimport { useContext, useState, useCallback, useMemo, useEffect, useRef } from 'react';\nimport { ExperienceContext } from './context';\n\nexport function useExperience() {\n const ctx = useContext(ExperienceContext);\n if (!ctx) throw new Error('useExperience must be used within ExperienceProvider');\n return ctx;\n}\n\nexport function useTrack() {\n const experience = useExperience();\n return useMemo(() => ({\n track: (eventName: string, metadata?: Record<string, string>) => experience.track(eventName, metadata),\n trackPageView: () => experience.trackPageView(),\n }), [experience]);\n}\n\nexport function useFlag<T>(flagName: string, defaultValue: T): T {\n const experience = useExperience();\n return experience.getFlag(flagName, defaultValue);\n}\n\n// ─── Translation Types ──────────────────────────────────────────\n\ninterface TranslationResult {\n t: (key: string, params?: Record<string, string | number>) => string;\n locale: string;\n setLocale: (locale: string) => void;\n locales: Array<{ code: string; name: string; progress?: number }>;\n isLoading: boolean;\n}\n\ninterface TranslationCache {\n [locale: string]: Record<string, string>;\n}\n\n// Module-level cache shared across hook instances\nconst translationCache: TranslationCache = {};\nconst fetchPromises = new Map<string, Promise<Record<string, string>>>();\n\n/**\n * Translation hook for the Experience Platform.\n *\n * Fetches translations from the Experience API, caches in memory + localStorage,\n * and provides `t()` for interpolation and `setLocale()` for switching.\n *\n * Usage:\n * ```tsx\n * const { t, locale, setLocale, locales } = useTranslation();\n * return <h1>{t('greeting', { name: 'Alex' })}</h1>;\n * ```\n */\nexport function useTranslation(): TranslationResult {\n const experience = useExperience();\n const config = (experience as any).config || {};\n const appId: string = config.appId || '';\n const apiBaseUrl: string = config.apiUrl || config.baseUrl || 'https://experience-api.shellapps.com';\n\n const [locale, setLocaleState] = useState(() => {\n if (typeof window === 'undefined') return 'en';\n // Check localStorage for persisted locale preference\n const saved = localStorage.getItem(`exp_locale_${appId}`);\n if (saved) return saved;\n return navigator.language?.split('-')[0] || 'en';\n });\n\n const [translations, setTranslations] = useState<Record<string, string>>(() => {\n // Try memory cache first\n if (translationCache[locale]) return translationCache[locale]!;\n // Try localStorage\n if (typeof window !== 'undefined') {\n try {\n const cached = localStorage.getItem(`exp_translations_${appId}_${locale}`);\n if (cached) {\n const parsed = JSON.parse(cached);\n translationCache[locale] = parsed;\n return parsed;\n }\n } catch { /* ignore */ }\n }\n return {};\n });\n\n const [locales, setLocales] = useState<Array<{ code: string; name: string; progress?: number }>>([]);\n const [isLoading, setIsLoading] = useState(false);\n\n // Fetch translations for a locale\n const fetchTranslations = useCallback(async (loc: string): Promise<Record<string, string>> => {\n if (!appId) return {};\n\n // Deduplicate in-flight requests\n const cacheKey = `${appId}_${loc}`;\n const existing = fetchPromises.get(cacheKey);\n if (existing) return existing;\n\n const promise = (async () => {\n try {\n const res = await fetch(`${apiBaseUrl}/api/v1/translations/${appId}/${loc}`);\n if (!res.ok) return {};\n const data = await res.json();\n // Cache in memory and localStorage\n translationCache[loc] = data;\n if (typeof window !== 'undefined') {\n try {\n localStorage.setItem(`exp_translations_${appId}_${loc}`, JSON.stringify(data));\n } catch { /* quota exceeded, ignore */ }\n }\n return data as Record<string, string>;\n } catch {\n return {};\n } finally {\n fetchPromises.delete(cacheKey);\n }\n })();\n\n fetchPromises.set(cacheKey, promise);\n return promise;\n }, [appId, apiBaseUrl]);\n\n // Fetch available locales\n useEffect(() => {\n if (!appId) return;\n fetch(`${apiBaseUrl}/api/v1/translations/${appId}/locales`)\n .then(res => res.ok ? res.json() : { locales: [] })\n .then(data => setLocales(data.locales || []))\n .catch(() => {});\n }, [appId, apiBaseUrl]);\n\n // Fetch translations when locale changes\n useEffect(() => {\n if (!appId) return;\n\n // Use cached if available\n if (translationCache[locale]) {\n setTranslations(translationCache[locale]!);\n return;\n }\n\n setIsLoading(true);\n fetchTranslations(locale).then(data => {\n setTranslations(data);\n setIsLoading(false);\n });\n }, [locale, appId, fetchTranslations]);\n\n // t() function with interpolation and plural support\n const t = useCallback((key: string, params?: Record<string, string | number>): string => {\n let result = translations[key] ?? key;\n\n // Handle plurals: \"singular||plural\" with count param\n if (result.includes('||') && params && 'count' in params) {\n const parts = result.split('||');\n const count = Number(params.count);\n result = count === 1 ? (parts[0] ?? result) : (parts[1] ?? result);\n }\n\n // Handle interpolation: {{variable}}\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n result = result.replace(new RegExp(`\\{\\{${k}\\}\\}`, 'g'), String(v));\n }\n }\n\n return result;\n }, [translations]);\n\n // setLocale with persistence\n const setLocale = useCallback((newLocale: string) => {\n setLocaleState(newLocale);\n if (typeof window !== 'undefined') {\n localStorage.setItem(`exp_locale_${appId}`, newLocale);\n }\n }, [appId]);\n\n return { t, locale, setLocale, locales, isLoading };\n}\n"],"mappings":"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","names":["import_react","import_react","import_jsx_runtime","React","import_react","import_jsx_runtime","import_react"]}
package/dist/index.mjs CHANGED
@@ -1,5 +1,7 @@
1
+ "use client";
2
+
1
3
  // src/ExperienceProvider.tsx
2
- import { useEffect, useRef } from "react";
4
+ import { useEffect, useRef, useState } from "react";
3
5
  import { Experience } from "@shellapps/experience";
4
6
 
5
7
  // src/context.ts
@@ -10,11 +12,20 @@ var ExperienceContext = createContext(null);
10
12
  import { jsx } from "react/jsx-runtime";
11
13
  function ExperienceProvider({ appId, apiKey, profileId, options, children }) {
12
14
  const instanceRef = useRef(null);
13
- if (!instanceRef.current) {
15
+ const [, forceUpdate] = useState(0);
16
+ if (typeof window !== "undefined" && !instanceRef.current) {
14
17
  const instance = Experience.init({ appId, apiKey, ...options });
15
18
  if (profileId) instance.identify(profileId);
16
19
  instanceRef.current = instance;
17
20
  }
21
+ useEffect(() => {
22
+ if (!instanceRef.current) {
23
+ const instance = Experience.init({ appId, apiKey, ...options });
24
+ if (profileId) instance.identify(profileId);
25
+ instanceRef.current = instance;
26
+ forceUpdate((n) => n + 1);
27
+ }
28
+ }, []);
18
29
  useEffect(() => {
19
30
  if (profileId && instanceRef.current) {
20
31
  instanceRef.current.identify(profileId);
@@ -104,7 +115,7 @@ var ErrorBoundary = class extends React2.Component {
104
115
  ErrorBoundary.contextType = ExperienceContext;
105
116
 
106
117
  // src/FeedbackButton.tsx
107
- import { useState, useEffect as useEffect2, useCallback, useRef as useRef2, useContext } from "react";
118
+ import { useState as useState2, useEffect as useEffect2, useCallback, useRef as useRef2, useContext } from "react";
108
119
  import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
109
120
  var TYPE_META = {
110
121
  "bug": { icon: "\u{1F41B}", label: "Bug Report", color: "#ef4444" },
@@ -128,18 +139,18 @@ function FeedbackButton({
128
139
  authToken
129
140
  }) {
130
141
  const experience = useContext(ExperienceContext);
131
- const [isOpen, setIsOpen] = useState(false);
132
- const [view, setView] = useState("list");
133
- const [selectedType, setSelectedType] = useState("comment");
134
- const [title, setTitle] = useState("");
135
- const [content, setContent] = useState("");
136
- const [submitting, setSubmitting] = useState(false);
137
- const [items, setItems] = useState([]);
138
- const [selectedItem, setSelectedItem] = useState(null);
139
- const [replies, setReplies] = useState([]);
140
- const [replyText, setReplyText] = useState("");
141
- const [loading, setLoading] = useState(false);
142
- const [screenshot, setScreenshot] = useState(null);
142
+ const [isOpen, setIsOpen] = useState2(false);
143
+ const [view, setView] = useState2("list");
144
+ const [selectedType, setSelectedType] = useState2("comment");
145
+ const [title, setTitle] = useState2("");
146
+ const [content, setContent] = useState2("");
147
+ const [submitting, setSubmitting] = useState2(false);
148
+ const [items, setItems] = useState2([]);
149
+ const [selectedItem, setSelectedItem] = useState2(null);
150
+ const [replies, setReplies] = useState2([]);
151
+ const [replyText, setReplyText] = useState2("");
152
+ const [loading, setLoading] = useState2(false);
153
+ const [screenshot, setScreenshot] = useState2(null);
143
154
  const panelRef = useRef2(null);
144
155
  const baseUrl = apiUrl || experience?.config?.apiUrl || "https://experience.shellapps.com";
145
156
  const appId = experience?.config?.appId || "";
@@ -621,7 +632,7 @@ function FeedbackButton({
621
632
  }
622
633
 
623
634
  // src/hooks.ts
624
- import { useContext as useContext2, useState as useState2, useCallback as useCallback2, useMemo, useEffect as useEffect3 } from "react";
635
+ import { useContext as useContext2, useState as useState3, useCallback as useCallback2, useMemo, useEffect as useEffect3 } from "react";
625
636
  function useExperience() {
626
637
  const ctx = useContext2(ExperienceContext);
627
638
  if (!ctx) throw new Error("useExperience must be used within ExperienceProvider");
@@ -645,13 +656,13 @@ function useTranslation() {
645
656
  const config = experience.config || {};
646
657
  const appId = config.appId || "";
647
658
  const apiBaseUrl = config.apiUrl || config.baseUrl || "https://experience-api.shellapps.com";
648
- const [locale, setLocaleState] = useState2(() => {
659
+ const [locale, setLocaleState] = useState3(() => {
649
660
  if (typeof window === "undefined") return "en";
650
661
  const saved = localStorage.getItem(`exp_locale_${appId}`);
651
662
  if (saved) return saved;
652
663
  return navigator.language?.split("-")[0] || "en";
653
664
  });
654
- const [translations, setTranslations] = useState2(() => {
665
+ const [translations, setTranslations] = useState3(() => {
655
666
  if (translationCache[locale]) return translationCache[locale];
656
667
  if (typeof window !== "undefined") {
657
668
  try {
@@ -666,8 +677,8 @@ function useTranslation() {
666
677
  }
667
678
  return {};
668
679
  });
669
- const [locales, setLocales] = useState2([]);
670
- const [isLoading, setIsLoading] = useState2(false);
680
+ const [locales, setLocales] = useState3([]);
681
+ const [isLoading, setIsLoading] = useState3(false);
671
682
  const fetchTranslations = useCallback2(async (loc) => {
672
683
  if (!appId) return {};
673
684
  const cacheKey = `${appId}_${loc}`;
@@ -721,7 +732,7 @@ function useTranslation() {
721
732
  }
722
733
  if (params) {
723
734
  for (const [k, v] of Object.entries(params)) {
724
- result = result.replace(new RegExp(`\\{\\{${k}\\}\\}`, "g"), String(v));
735
+ result = result.replace(new RegExp(`{{${k}}}`, "g"), String(v));
725
736
  }
726
737
  }
727
738
  return result;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ExperienceProvider.tsx","../src/context.ts","../src/ErrorBoundary.tsx","../src/FeedbackButton.tsx","../src/hooks.ts"],"sourcesContent":["import React, { useEffect, useRef } from 'react';\nimport { Experience } from '@shellapps/experience';\nimport type { ExperienceConfig } from '@shellapps/experience';\nimport { ExperienceContext } from './context';\n\nexport interface ExperienceProviderProps {\n appId: string;\n apiKey: string;\n profileId?: string;\n options?: Partial<Omit<ExperienceConfig, 'appId' | 'apiKey'>>;\n children: React.ReactNode;\n}\n\nexport function ExperienceProvider({ appId, apiKey, profileId, options, children }: ExperienceProviderProps) {\n const instanceRef = useRef<Experience | null>(null);\n\n if (!instanceRef.current) {\n const instance = Experience.init({ appId, apiKey, ...options });\n if (profileId) instance.identify(profileId);\n instanceRef.current = instance;\n }\n\n useEffect(() => {\n if (profileId && instanceRef.current) {\n instanceRef.current.identify(profileId);\n }\n }, [profileId]);\n\n // data-t auto-tracking\n useEffect(() => {\n const handler = (e: MouseEvent) => {\n let el = e.target as HTMLElement | null;\n while (el) {\n const tid = el.getAttribute?.('data-t');\n if (tid) {\n instanceRef.current?.track('element_click', { elementTid: tid });\n break;\n }\n el = el.parentElement;\n }\n };\n document.addEventListener('click', handler, true);\n return () => document.removeEventListener('click', handler, true);\n }, []);\n\n useEffect(() => {\n return () => {\n instanceRef.current?.shutdown();\n };\n }, []);\n\n return (\n <ExperienceContext.Provider value={instanceRef.current}>\n {children}\n </ExperienceContext.Provider>\n );\n}\n","import { createContext } from 'react';\nimport type { Experience } from '@shellapps/experience';\n\nexport const ExperienceContext = createContext<Experience | null>(null);\n","import React from 'react';\nimport type { Experience } from '@shellapps/experience';\nimport { ExperienceContext } from './context';\n\ninterface ErrorBoundaryProps {\n fallback?: React.ReactNode;\n showCommentForm?: boolean;\n onError?: (error: Error, errorInfo: React.ErrorInfo) => void;\n children: React.ReactNode;\n}\n\ninterface ErrorBoundaryState {\n hasError: boolean;\n error: Error | null;\n comment: string;\n submitted: boolean;\n}\n\nexport class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {\n static contextType = ExperienceContext;\n declare context: Experience | null;\n\n state: ErrorBoundaryState = {\n hasError: false,\n error: null,\n comment: '',\n submitted: false,\n };\n\n static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> {\n return { hasError: true, error };\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {\n this.context?.captureError(error, {\n extra: { componentStack: errorInfo.componentStack || '' },\n severity: 'fatal' as any,\n });\n this.props.onError?.(error, errorInfo);\n }\n\n private handleSubmitComment = () => {\n if (this.state.comment.trim() && this.state.error) {\n this.context?.captureMessage(\n `User feedback: ${this.state.comment}`,\n 'info',\n );\n this.setState({ submitted: true });\n }\n };\n\n render() {\n if (this.state.hasError) {\n if (this.props.fallback) {\n return this.props.fallback;\n }\n\n return (\n <div style={{ padding: 20, textAlign: 'center' }}>\n <h2>Something went wrong</h2>\n <p>{this.state.error?.message}</p>\n {this.props.showCommentForm && !this.state.submitted && (\n <div>\n <textarea\n placeholder=\"Tell us what happened...\"\n value={this.state.comment}\n onChange={(e) => this.setState({ comment: e.target.value })}\n style={{ width: '100%', minHeight: 80, marginTop: 10 }}\n />\n <button onClick={this.handleSubmitComment} style={{ marginTop: 8 }}>\n Submit\n </button>\n </div>\n )}\n {this.state.submitted && <p>Thank you for your feedback!</p>}\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n","import React, { useState, useEffect, useCallback, useRef, useContext } from 'react';\nimport { ExperienceContext } from './context';\n\n// ── Types ──────────────────────────────────────────────────────────────────\n\ntype FeedbackType = 'bug' | 'feature-request' | 'comment' | 'praise';\ntype FeedbackStatus = 'new' | 'acknowledged' | 'in-progress' | 'resolved' | 'wont-fix';\n\ninterface FeedbackItem {\n _id: string;\n type: FeedbackType;\n title: string;\n content: string;\n status: FeedbackStatus;\n tags: string[];\n vote_count: number;\n reply_count: number;\n created_at: string;\n}\n\ninterface FeedbackReply {\n _id: string;\n author_type: 'user' | 'developer';\n author_name?: string;\n content: string;\n created_at: string;\n}\n\ninterface FeedbackButtonProps {\n /** Position of the floating button */\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\n /** Custom button label */\n label?: string;\n /** Button colour */\n color?: string;\n /** Hide the floating button (use exp.openFeedback() instead) */\n hidden?: boolean;\n /** API endpoint base URL */\n apiUrl?: string;\n /** Auth token for user endpoints */\n authToken?: string;\n}\n\n// ── Styles ─────────────────────────────────────────────────────────────────\n\nconst TYPE_META: Record<FeedbackType, { icon: string; label: string; color: string }> = {\n 'bug': { icon: '🐛', label: 'Bug Report', color: '#ef4444' },\n 'feature-request': { icon: '💡', label: 'Feature Request', color: '#8b5cf6' },\n 'comment': { icon: '💬', label: 'Comment', color: '#3b82f6' },\n 'praise': { icon: '🎉', label: 'Praise', color: '#10b981' },\n};\n\nconst STATUS_LABELS: Record<FeedbackStatus, string> = {\n 'new': '🆕 New',\n 'acknowledged': '👀 Acknowledged',\n 'in-progress': '🔧 In Progress',\n 'resolved': '✅ Resolved',\n 'wont-fix': '🚫 Won\\'t Fix',\n};\n\n// ── Component ──────────────────────────────────────────────────────────────\n\nexport function FeedbackButton({\n position = 'bottom-right',\n label = 'Feedback',\n color = '#6366f1',\n hidden = false,\n apiUrl,\n authToken,\n}: FeedbackButtonProps) {\n const experience = useContext(ExperienceContext);\n const [isOpen, setIsOpen] = useState(false);\n const [view, setView] = useState<'list' | 'new' | 'detail'>('list');\n const [selectedType, setSelectedType] = useState<FeedbackType>('comment');\n const [title, setTitle] = useState('');\n const [content, setContent] = useState('');\n const [submitting, setSubmitting] = useState(false);\n const [items, setItems] = useState<FeedbackItem[]>([]);\n const [selectedItem, setSelectedItem] = useState<FeedbackItem | null>(null);\n const [replies, setReplies] = useState<FeedbackReply[]>([]);\n const [replyText, setReplyText] = useState('');\n const [loading, setLoading] = useState(false);\n const [screenshot, setScreenshot] = useState<string | null>(null);\n const panelRef = useRef<HTMLDivElement>(null);\n\n const baseUrl = apiUrl || (experience as any)?.config?.apiUrl || 'https://experience.shellapps.com';\n const appId = (experience as any)?.config?.appId || '';\n\n const headers = useCallback((): Record<string, string> => {\n const h: Record<string, string> = { 'Content-Type': 'application/json' };\n if (authToken) h['Authorization'] = `Bearer ${authToken}`;\n return h;\n }, [authToken]);\n\n // Expose openFeedback on the experience instance\n useEffect(() => {\n if (experience) {\n (experience as any).openFeedback = () => setIsOpen(true);\n }\n return () => {\n if (experience) delete (experience as any).openFeedback;\n };\n }, [experience]);\n\n // Load feedback list when opening\n useEffect(() => {\n if (isOpen && view === 'list') {\n loadFeedback();\n }\n }, [isOpen, view]);\n\n const loadFeedback = async () => {\n setLoading(true);\n try {\n const res = await fetch(\n `${baseUrl}/api/v1/feedback?appId=${appId}&limit=20`,\n { headers: headers() },\n );\n if (res.ok) {\n const data = await res.json();\n setItems(data.items || []);\n }\n } catch {\n // silently fail\n }\n setLoading(false);\n };\n\n const loadDetail = async (item: FeedbackItem) => {\n setSelectedItem(item);\n setView('detail');\n try {\n const res = await fetch(\n `${baseUrl}/api/v1/feedback/${item._id}`,\n { headers: headers() },\n );\n if (res.ok) {\n const data = await res.json();\n setReplies(data.replies || []);\n }\n } catch {\n // silently fail\n }\n };\n\n const captureScreenshot = async () => {\n try {\n const html2canvas = (window as any).html2canvas;\n if (!html2canvas) return;\n const canvas = await html2canvas(document.body, { scale: 0.5, logging: false });\n setScreenshot(canvas.toDataURL('image/jpeg', 0.6));\n } catch {\n // screenshot not available\n }\n };\n\n const submit = async () => {\n if (!title.trim() || !content.trim()) return;\n setSubmitting(true);\n try {\n const ctx = {\n userAgent: navigator.userAgent,\n url: window.location.href,\n viewport: { width: window.innerWidth, height: window.innerHeight },\n locale: navigator.language,\n referrer: document.referrer || undefined,\n };\n\n const res = await fetch(`${baseUrl}/api/v1/feedback`, {\n method: 'POST',\n headers: headers(),\n body: JSON.stringify({\n appId,\n type: selectedType,\n title: title.trim(),\n content: content.trim(),\n context: ctx,\n screenshot: screenshot || undefined,\n }),\n });\n\n if (res.ok) {\n setTitle('');\n setContent('');\n setScreenshot(null);\n setView('list');\n loadFeedback();\n }\n } catch {\n // silently fail\n }\n setSubmitting(false);\n };\n\n const submitReply = async () => {\n if (!replyText.trim() || !selectedItem) return;\n try {\n const res = await fetch(`${baseUrl}/api/v1/feedback/${selectedItem._id}/reply`, {\n method: 'POST',\n headers: headers(),\n body: JSON.stringify({ content: replyText.trim() }),\n });\n if (res.ok) {\n const reply = await res.json();\n setReplies((prev) => [...prev, reply]);\n setReplyText('');\n }\n } catch {\n // silently fail\n }\n };\n\n const vote = async (id: string) => {\n try {\n await fetch(`${baseUrl}/api/v1/feedback/${id}/vote`, {\n method: 'POST',\n headers: headers(),\n body: JSON.stringify({ type: 'upvote' }),\n });\n loadFeedback();\n } catch {\n // silently fail\n }\n };\n\n // ── Position styles ──────────────────────────────────────────────────────\n\n const posStyles: Record<string, React.CSSProperties> = {\n 'bottom-right': { bottom: 20, right: 20 },\n 'bottom-left': { bottom: 20, left: 20 },\n 'top-right': { top: 20, right: 20 },\n 'top-left': { top: 20, left: 20 },\n };\n\n const panelPos: Record<string, React.CSSProperties> = {\n 'bottom-right': { bottom: 70, right: 20 },\n 'bottom-left': { bottom: 70, left: 20 },\n 'top-right': { top: 70, right: 20 },\n 'top-left': { top: 70, left: 20 },\n };\n\n // ── Render ───────────────────────────────────────────────────────────────\n\n return (\n <>\n {/* Floating button */}\n {!hidden && (\n <button\n onClick={() => setIsOpen(!isOpen)}\n style={{\n position: 'fixed',\n ...posStyles[position],\n zIndex: 99999,\n background: color,\n color: '#fff',\n border: 'none',\n borderRadius: 28,\n padding: '10px 20px',\n fontSize: 14,\n fontWeight: 600,\n cursor: 'pointer',\n boxShadow: '0 4px 12px rgba(0,0,0,0.15)',\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n }}\n >\n 💬 {label}\n </button>\n )}\n\n {/* Panel */}\n {isOpen && (\n <div\n ref={panelRef}\n style={{\n position: 'fixed',\n ...panelPos[position],\n zIndex: 100000,\n width: 380,\n maxHeight: 520,\n background: '#fff',\n borderRadius: 12,\n boxShadow: '0 8px 32px rgba(0,0,0,0.18)',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n fontSize: 14,\n color: '#1f2937',\n }}\n >\n {/* Header */}\n <div\n style={{\n padding: '14px 16px',\n background: color,\n color: '#fff',\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n }}\n >\n <span style={{ fontWeight: 600 }}>\n {view === 'new' ? 'New Feedback' : view === 'detail' ? 'Feedback' : 'Feedback'}\n </span>\n <div style={{ display: 'flex', gap: 8 }}>\n {view !== 'list' && (\n <button\n onClick={() => { setView('list'); setSelectedItem(null); }}\n style={{ background: 'none', border: 'none', color: '#fff', cursor: 'pointer', fontSize: 14 }}\n >\n ← Back\n </button>\n )}\n <button\n onClick={() => setIsOpen(false)}\n style={{ background: 'none', border: 'none', color: '#fff', cursor: 'pointer', fontSize: 18 }}\n >\n ✕\n </button>\n </div>\n </div>\n\n {/* Body */}\n <div style={{ flex: 1, overflow: 'auto', padding: 16 }}>\n {/* LIST VIEW */}\n {view === 'list' && (\n <>\n <button\n onClick={() => setView('new')}\n style={{\n width: '100%',\n padding: '10px',\n background: color,\n color: '#fff',\n border: 'none',\n borderRadius: 8,\n cursor: 'pointer',\n fontWeight: 600,\n marginBottom: 12,\n }}\n >\n + New Feedback\n </button>\n {loading ? (\n <p style={{ textAlign: 'center', color: '#9ca3af' }}>Loading…</p>\n ) : items.length === 0 ? (\n <p style={{ textAlign: 'center', color: '#9ca3af' }}>No feedback yet</p>\n ) : (\n items.map((item) => (\n <div\n key={item._id}\n onClick={() => loadDetail(item)}\n style={{\n padding: 10,\n borderRadius: 8,\n border: '1px solid #e5e7eb',\n marginBottom: 8,\n cursor: 'pointer',\n }}\n >\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 4 }}>\n <span>\n {TYPE_META[item.type]?.icon} <strong>{item.title}</strong>\n </span>\n {item.type === 'feature-request' && (\n <button\n onClick={(e) => { e.stopPropagation(); vote(item._id); }}\n style={{\n background: 'none',\n border: '1px solid #d1d5db',\n borderRadius: 4,\n padding: '2px 6px',\n cursor: 'pointer',\n fontSize: 12,\n }}\n >\n ▲ {item.vote_count}\n </button>\n )}\n </div>\n <div style={{ fontSize: 12, color: '#6b7280' }}>\n {STATUS_LABELS[item.status]} · {item.reply_count} replies · {new Date(item.created_at).toLocaleDateString()}\n </div>\n </div>\n ))\n )}\n </>\n )}\n\n {/* NEW FEEDBACK VIEW */}\n {view === 'new' && (\n <>\n {/* Type selector */}\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginBottom: 12 }}>\n {(Object.entries(TYPE_META) as [FeedbackType, typeof TYPE_META['bug']][]).map(\n ([type, meta]) => (\n <button\n key={type}\n onClick={() => setSelectedType(type)}\n style={{\n padding: 8,\n borderRadius: 8,\n border: `2px solid ${selectedType === type ? meta.color : '#e5e7eb'}`,\n background: selectedType === type ? `${meta.color}10` : '#fff',\n cursor: 'pointer',\n fontSize: 13,\n textAlign: 'center',\n }}\n >\n {meta.icon} {meta.label}\n </button>\n ),\n )}\n </div>\n\n <input\n placeholder=\"Title\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n style={{\n width: '100%',\n padding: 8,\n borderRadius: 6,\n border: '1px solid #d1d5db',\n marginBottom: 8,\n fontSize: 14,\n boxSizing: 'border-box',\n }}\n />\n\n <textarea\n placeholder=\"Describe your feedback…\"\n value={content}\n onChange={(e) => setContent(e.target.value)}\n rows={4}\n style={{\n width: '100%',\n padding: 8,\n borderRadius: 6,\n border: '1px solid #d1d5db',\n marginBottom: 8,\n fontSize: 14,\n resize: 'vertical',\n boxSizing: 'border-box',\n }}\n />\n\n {/* Screenshot button */}\n {(window as any).html2canvas && (\n <button\n onClick={captureScreenshot}\n style={{\n background: 'none',\n border: '1px solid #d1d5db',\n borderRadius: 6,\n padding: '6px 10px',\n cursor: 'pointer',\n fontSize: 12,\n marginBottom: 8,\n color: screenshot ? '#10b981' : '#6b7280',\n }}\n >\n 📷 {screenshot ? 'Screenshot captured ✓' : 'Capture screenshot'}\n </button>\n )}\n\n <button\n onClick={submit}\n disabled={submitting || !title.trim() || !content.trim()}\n style={{\n width: '100%',\n padding: 10,\n background: submitting ? '#9ca3af' : color,\n color: '#fff',\n border: 'none',\n borderRadius: 8,\n cursor: submitting ? 'default' : 'pointer',\n fontWeight: 600,\n }}\n >\n {submitting ? 'Submitting…' : 'Submit'}\n </button>\n </>\n )}\n\n {/* DETAIL VIEW */}\n {view === 'detail' && selectedItem && (\n <>\n <div style={{ marginBottom: 12 }}>\n <div style={{ fontSize: 12, color: TYPE_META[selectedItem.type]?.color, fontWeight: 600, marginBottom: 4 }}>\n {TYPE_META[selectedItem.type]?.icon} {TYPE_META[selectedItem.type]?.label}\n </div>\n <h3 style={{ margin: '0 0 4px', fontSize: 16 }}>{selectedItem.title}</h3>\n <div style={{ fontSize: 12, color: '#6b7280', marginBottom: 8 }}>\n {STATUS_LABELS[selectedItem.status]} · {new Date(selectedItem.created_at).toLocaleDateString()}\n </div>\n <p style={{ margin: 0, lineHeight: 1.5 }}>{selectedItem.content}</p>\n </div>\n\n <hr style={{ border: 'none', borderTop: '1px solid #e5e7eb', margin: '12px 0' }} />\n\n {/* Replies */}\n <div style={{ marginBottom: 12 }}>\n <strong style={{ fontSize: 13 }}>Replies ({replies.length})</strong>\n {replies.map((r) => (\n <div\n key={r._id}\n style={{\n padding: 8,\n marginTop: 8,\n borderRadius: 6,\n background: r.author_type === 'developer' ? '#eff6ff' : '#f9fafb',\n borderLeft: `3px solid ${r.author_type === 'developer' ? '#3b82f6' : '#d1d5db'}`,\n }}\n >\n <div style={{ fontSize: 11, color: '#6b7280', marginBottom: 2 }}>\n {r.author_type === 'developer' ? '🛠️ Developer' : '👤 You'}{r.author_name ? ` · ${r.author_name}` : ''} · {new Date(r.created_at).toLocaleDateString()}\n </div>\n <div style={{ fontSize: 13 }}>{r.content}</div>\n </div>\n ))}\n </div>\n\n {/* Reply input */}\n <div style={{ display: 'flex', gap: 6 }}>\n <input\n placeholder=\"Write a reply…\"\n value={replyText}\n onChange={(e) => setReplyText(e.target.value)}\n onKeyDown={(e) => { if (e.key === 'Enter') submitReply(); }}\n style={{\n flex: 1,\n padding: 8,\n borderRadius: 6,\n border: '1px solid #d1d5db',\n fontSize: 13,\n }}\n />\n <button\n onClick={submitReply}\n disabled={!replyText.trim()}\n style={{\n padding: '8px 12px',\n background: color,\n color: '#fff',\n border: 'none',\n borderRadius: 6,\n cursor: 'pointer',\n fontSize: 13,\n }}\n >\n Send\n </button>\n </div>\n </>\n )}\n </div>\n </div>\n )}\n </>\n );\n}\n","import { useContext, useState, useCallback, useMemo, useEffect, useRef } from 'react';\nimport { ExperienceContext } from './context';\n\nexport function useExperience() {\n const ctx = useContext(ExperienceContext);\n if (!ctx) throw new Error('useExperience must be used within ExperienceProvider');\n return ctx;\n}\n\nexport function useTrack() {\n const experience = useExperience();\n return useMemo(() => ({\n track: (eventName: string, metadata?: Record<string, string>) => experience.track(eventName, metadata),\n trackPageView: () => experience.trackPageView(),\n }), [experience]);\n}\n\nexport function useFlag<T>(flagName: string, defaultValue: T): T {\n const experience = useExperience();\n return experience.getFlag(flagName, defaultValue);\n}\n\n// ─── Translation Types ──────────────────────────────────────────\n\ninterface TranslationResult {\n t: (key: string, params?: Record<string, string | number>) => string;\n locale: string;\n setLocale: (locale: string) => void;\n locales: Array<{ code: string; name: string; progress?: number }>;\n isLoading: boolean;\n}\n\ninterface TranslationCache {\n [locale: string]: Record<string, string>;\n}\n\n// Module-level cache shared across hook instances\nconst translationCache: TranslationCache = {};\nconst fetchPromises = new Map<string, Promise<Record<string, string>>>();\n\n/**\n * Translation hook for the Experience Platform.\n *\n * Fetches translations from the Experience API, caches in memory + localStorage,\n * and provides `t()` for interpolation and `setLocale()` for switching.\n *\n * Usage:\n * ```tsx\n * const { t, locale, setLocale, locales } = useTranslation();\n * return <h1>{t('greeting', { name: 'Alex' })}</h1>;\n * ```\n */\nexport function useTranslation(): TranslationResult {\n const experience = useExperience();\n const config = (experience as any).config || {};\n const appId: string = config.appId || '';\n const apiBaseUrl: string = config.apiUrl || config.baseUrl || 'https://experience-api.shellapps.com';\n\n const [locale, setLocaleState] = useState(() => {\n if (typeof window === 'undefined') return 'en';\n // Check localStorage for persisted locale preference\n const saved = localStorage.getItem(`exp_locale_${appId}`);\n if (saved) return saved;\n return navigator.language?.split('-')[0] || 'en';\n });\n\n const [translations, setTranslations] = useState<Record<string, string>>(() => {\n // Try memory cache first\n if (translationCache[locale]) return translationCache[locale]!;\n // Try localStorage\n if (typeof window !== 'undefined') {\n try {\n const cached = localStorage.getItem(`exp_translations_${appId}_${locale}`);\n if (cached) {\n const parsed = JSON.parse(cached);\n translationCache[locale] = parsed;\n return parsed;\n }\n } catch { /* ignore */ }\n }\n return {};\n });\n\n const [locales, setLocales] = useState<Array<{ code: string; name: string; progress?: number }>>([]);\n const [isLoading, setIsLoading] = useState(false);\n\n // Fetch translations for a locale\n const fetchTranslations = useCallback(async (loc: string): Promise<Record<string, string>> => {\n if (!appId) return {};\n\n // Deduplicate in-flight requests\n const cacheKey = `${appId}_${loc}`;\n const existing = fetchPromises.get(cacheKey);\n if (existing) return existing;\n\n const promise = (async () => {\n try {\n const res = await fetch(`${apiBaseUrl}/api/v1/translations/${appId}/${loc}`);\n if (!res.ok) return {};\n const data = await res.json();\n // Cache in memory and localStorage\n translationCache[loc] = data;\n if (typeof window !== 'undefined') {\n try {\n localStorage.setItem(`exp_translations_${appId}_${loc}`, JSON.stringify(data));\n } catch { /* quota exceeded, ignore */ }\n }\n return data as Record<string, string>;\n } catch {\n return {};\n } finally {\n fetchPromises.delete(cacheKey);\n }\n })();\n\n fetchPromises.set(cacheKey, promise);\n return promise;\n }, [appId, apiBaseUrl]);\n\n // Fetch available locales\n useEffect(() => {\n if (!appId) return;\n fetch(`${apiBaseUrl}/api/v1/translations/${appId}/locales`)\n .then(res => res.ok ? res.json() : { locales: [] })\n .then(data => setLocales(data.locales || []))\n .catch(() => {});\n }, [appId, apiBaseUrl]);\n\n // Fetch translations when locale changes\n useEffect(() => {\n if (!appId) return;\n\n // Use cached if available\n if (translationCache[locale]) {\n setTranslations(translationCache[locale]!);\n return;\n }\n\n setIsLoading(true);\n fetchTranslations(locale).then(data => {\n setTranslations(data);\n setIsLoading(false);\n });\n }, [locale, appId, fetchTranslations]);\n\n // t() function with interpolation and plural support\n const t = useCallback((key: string, params?: Record<string, string | number>): string => {\n let result = translations[key] ?? key;\n\n // Handle plurals: \"singular||plural\" with count param\n if (result.includes('||') && params && 'count' in params) {\n const parts = result.split('||');\n const count = Number(params.count);\n result = count === 1 ? (parts[0] ?? result) : (parts[1] ?? result);\n }\n\n // Handle interpolation: {{variable}}\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n result = result.replace(new RegExp(`\\\\{\\\\{${k}\\\\}\\\\}`, 'g'), String(v));\n }\n }\n\n return result;\n }, [translations]);\n\n // setLocale with persistence\n const setLocale = useCallback((newLocale: string) => {\n setLocaleState(newLocale);\n if (typeof window !== 'undefined') {\n localStorage.setItem(`exp_locale_${appId}`, newLocale);\n }\n }, [appId]);\n\n return { t, locale, setLocale, locales, isLoading };\n}\n"],"mappings":"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","names":["React","jsx","React","useEffect","useRef","jsx","jsxs","useRef","useEffect","useContext","useState","useCallback","useEffect","useContext","useState","useCallback","useEffect"]}
1
+ {"version":3,"sources":["../src/ExperienceProvider.tsx","../src/context.ts","../src/ErrorBoundary.tsx","../src/FeedbackButton.tsx","../src/hooks.ts"],"sourcesContent":["'use client';\n\nimport React, { useEffect, useRef, useState } from 'react';\nimport { Experience } from '@shellapps/experience';\nimport type { ExperienceConfig } from '@shellapps/experience';\nimport { ExperienceContext } from './context';\n\nexport interface ExperienceProviderProps {\n appId: string;\n apiKey: string;\n profileId?: string;\n options?: Partial<Omit<ExperienceConfig, 'appId' | 'apiKey'>>;\n children: any;\n}\n\nexport function ExperienceProvider({ appId, apiKey, profileId, options, children }: ExperienceProviderProps) {\n const instanceRef = useRef<Experience | null>(null);\n const [, forceUpdate] = useState(0);\n\n // Only initialize on the client (browser) — skip during SSR/prerendering\n if (typeof window !== 'undefined' && !instanceRef.current) {\n const instance = Experience.init({ appId, apiKey, ...options });\n if (profileId) instance.identify(profileId);\n instanceRef.current = instance;\n }\n\n useEffect(() => {\n // Handle case where ref wasn't set during SSR render pass\n if (!instanceRef.current) {\n const instance = Experience.init({ appId, apiKey, ...options });\n if (profileId) instance.identify(profileId);\n instanceRef.current = instance;\n forceUpdate((n) => n + 1);\n }\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n useEffect(() => {\n if (profileId && instanceRef.current) {\n instanceRef.current.identify(profileId);\n }\n }, [profileId]);\n\n // data-t auto-tracking\n useEffect(() => {\n const handler = (e: MouseEvent) => {\n let el = e.target as HTMLElement | null;\n while (el) {\n const tid = el.getAttribute?.('data-t');\n if (tid) {\n instanceRef.current?.track('element_click', { elementTid: tid });\n break;\n }\n el = el.parentElement;\n }\n };\n document.addEventListener('click', handler, true);\n return () => document.removeEventListener('click', handler, true);\n }, []);\n\n useEffect(() => {\n return () => {\n instanceRef.current?.shutdown();\n };\n }, []);\n\n return (\n <ExperienceContext.Provider value={instanceRef.current}>\n {children}\n </ExperienceContext.Provider>\n );\n}\n","'use client';\nimport { createContext } from 'react';\nimport type { Experience } from '@shellapps/experience';\n\nexport const ExperienceContext = createContext<Experience | null>(null);\n","'use client';\nimport React from 'react';\nimport type { Experience } from '@shellapps/experience';\nimport { ExperienceContext } from './context';\n\ninterface ErrorBoundaryProps {\n fallback?: React.ReactNode;\n showCommentForm?: boolean;\n onError?: (error: Error, errorInfo: React.ErrorInfo) => void;\n children: any;\n}\n\ninterface ErrorBoundaryState {\n hasError: boolean;\n error: Error | null;\n comment: string;\n submitted: boolean;\n}\n\nexport class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {\n static contextType = ExperienceContext;\n declare context: Experience | null;\n\n state: ErrorBoundaryState = {\n hasError: false,\n error: null,\n comment: '',\n submitted: false,\n };\n\n static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> {\n return { hasError: true, error };\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {\n this.context?.captureError(error, {\n extra: { componentStack: errorInfo.componentStack || '' },\n severity: 'fatal' as any,\n });\n this.props.onError?.(error, errorInfo);\n }\n\n private handleSubmitComment = () => {\n if (this.state.comment.trim() && this.state.error) {\n this.context?.captureMessage(\n `User feedback: ${this.state.comment}`,\n 'info',\n );\n this.setState({ submitted: true });\n }\n };\n\n render() {\n if (this.state.hasError) {\n if (this.props.fallback) {\n return this.props.fallback;\n }\n\n return (\n <div style={{ padding: 20, textAlign: 'center' }}>\n <h2>Something went wrong</h2>\n <p>{this.state.error?.message}</p>\n {this.props.showCommentForm && !this.state.submitted && (\n <div>\n <textarea\n placeholder=\"Tell us what happened...\"\n value={this.state.comment}\n onChange={(e) => this.setState({ comment: e.target.value })}\n style={{ width: '100%', minHeight: 80, marginTop: 10 }}\n />\n <button onClick={this.handleSubmitComment} style={{ marginTop: 8 }}>\n Submit\n </button>\n </div>\n )}\n {this.state.submitted && <p>Thank you for your feedback!</p>}\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n","'use client';\nimport React, { useState, useEffect, useCallback, useRef, useContext } from 'react';\nimport { ExperienceContext } from './context';\n\n// ── Types ──────────────────────────────────────────────────────────────────\n\ntype FeedbackType = 'bug' | 'feature-request' | 'comment' | 'praise';\ntype FeedbackStatus = 'new' | 'acknowledged' | 'in-progress' | 'resolved' | 'wont-fix';\n\ninterface FeedbackItem {\n _id: string;\n type: FeedbackType;\n title: string;\n content: string;\n status: FeedbackStatus;\n tags: string[];\n vote_count: number;\n reply_count: number;\n created_at: string;\n}\n\ninterface FeedbackReply {\n _id: string;\n author_type: 'user' | 'developer';\n author_name?: string;\n content: string;\n created_at: string;\n}\n\ninterface FeedbackButtonProps {\n /** Position of the floating button */\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\n /** Custom button label */\n label?: string;\n /** Button colour */\n color?: string;\n /** Hide the floating button (use exp.openFeedback() instead) */\n hidden?: boolean;\n /** API endpoint base URL */\n apiUrl?: string;\n /** Auth token for user endpoints */\n authToken?: string;\n}\n\n// ── Styles ─────────────────────────────────────────────────────────────────\n\nconst TYPE_META: Record<FeedbackType, { icon: string; label: string; color: string }> = {\n 'bug': { icon: '🐛', label: 'Bug Report', color: '#ef4444' },\n 'feature-request': { icon: '💡', label: 'Feature Request', color: '#8b5cf6' },\n 'comment': { icon: '💬', label: 'Comment', color: '#3b82f6' },\n 'praise': { icon: '🎉', label: 'Praise', color: '#10b981' },\n};\n\nconst STATUS_LABELS: Record<FeedbackStatus, string> = {\n 'new': '🆕 New',\n 'acknowledged': '👀 Acknowledged',\n 'in-progress': '🔧 In Progress',\n 'resolved': '✅ Resolved',\n 'wont-fix': '🚫 Won\\'t Fix',\n};\n\n// ── Component ──────────────────────────────────────────────────────────────\n\nexport function FeedbackButton({\n position = 'bottom-right',\n label = 'Feedback',\n color = '#6366f1',\n hidden = false,\n apiUrl,\n authToken,\n}: FeedbackButtonProps) {\n const experience = useContext(ExperienceContext);\n const [isOpen, setIsOpen] = useState(false);\n const [view, setView] = useState<'list' | 'new' | 'detail'>('list');\n const [selectedType, setSelectedType] = useState<FeedbackType>('comment');\n const [title, setTitle] = useState('');\n const [content, setContent] = useState('');\n const [submitting, setSubmitting] = useState(false);\n const [items, setItems] = useState<FeedbackItem[]>([]);\n const [selectedItem, setSelectedItem] = useState<FeedbackItem | null>(null);\n const [replies, setReplies] = useState<FeedbackReply[]>([]);\n const [replyText, setReplyText] = useState('');\n const [loading, setLoading] = useState(false);\n const [screenshot, setScreenshot] = useState<string | null>(null);\n const panelRef = useRef<HTMLDivElement>(null);\n\n const baseUrl = apiUrl || (experience as any)?.config?.apiUrl || 'https://experience.shellapps.com';\n const appId = (experience as any)?.config?.appId || '';\n\n const headers = useCallback((): Record<string, string> => {\n const h: Record<string, string> = { 'Content-Type': 'application/json' };\n if (authToken) h['Authorization'] = `Bearer ${authToken}`;\n return h;\n }, [authToken]);\n\n // Expose openFeedback on the experience instance\n useEffect(() => {\n if (experience) {\n (experience as any).openFeedback = () => setIsOpen(true);\n }\n return () => {\n if (experience) delete (experience as any).openFeedback;\n };\n }, [experience]);\n\n // Load feedback list when opening\n useEffect(() => {\n if (isOpen && view === 'list') {\n loadFeedback();\n }\n }, [isOpen, view]);\n\n const loadFeedback = async () => {\n setLoading(true);\n try {\n const res = await fetch(\n `${baseUrl}/api/v1/feedback?appId=${appId}&limit=20`,\n { headers: headers() },\n );\n if (res.ok) {\n const data = await res.json();\n setItems(data.items || []);\n }\n } catch {\n // silently fail\n }\n setLoading(false);\n };\n\n const loadDetail = async (item: FeedbackItem) => {\n setSelectedItem(item);\n setView('detail');\n try {\n const res = await fetch(\n `${baseUrl}/api/v1/feedback/${item._id}`,\n { headers: headers() },\n );\n if (res.ok) {\n const data = await res.json();\n setReplies(data.replies || []);\n }\n } catch {\n // silently fail\n }\n };\n\n const captureScreenshot = async () => {\n try {\n const html2canvas = (window as any).html2canvas;\n if (!html2canvas) return;\n const canvas = await html2canvas(document.body, { scale: 0.5, logging: false });\n setScreenshot(canvas.toDataURL('image/jpeg', 0.6));\n } catch {\n // screenshot not available\n }\n };\n\n const submit = async () => {\n if (!title.trim() || !content.trim()) return;\n setSubmitting(true);\n try {\n const ctx = {\n userAgent: navigator.userAgent,\n url: window.location.href,\n viewport: { width: window.innerWidth, height: window.innerHeight },\n locale: navigator.language,\n referrer: document.referrer || undefined,\n };\n\n const res = await fetch(`${baseUrl}/api/v1/feedback`, {\n method: 'POST',\n headers: headers(),\n body: JSON.stringify({\n appId,\n type: selectedType,\n title: title.trim(),\n content: content.trim(),\n context: ctx,\n screenshot: screenshot || undefined,\n }),\n });\n\n if (res.ok) {\n setTitle('');\n setContent('');\n setScreenshot(null);\n setView('list');\n loadFeedback();\n }\n } catch {\n // silently fail\n }\n setSubmitting(false);\n };\n\n const submitReply = async () => {\n if (!replyText.trim() || !selectedItem) return;\n try {\n const res = await fetch(`${baseUrl}/api/v1/feedback/${selectedItem._id}/reply`, {\n method: 'POST',\n headers: headers(),\n body: JSON.stringify({ content: replyText.trim() }),\n });\n if (res.ok) {\n const reply = await res.json();\n setReplies((prev) => [...prev, reply]);\n setReplyText('');\n }\n } catch {\n // silently fail\n }\n };\n\n const vote = async (id: string) => {\n try {\n await fetch(`${baseUrl}/api/v1/feedback/${id}/vote`, {\n method: 'POST',\n headers: headers(),\n body: JSON.stringify({ type: 'upvote' }),\n });\n loadFeedback();\n } catch {\n // silently fail\n }\n };\n\n // ── Position styles ──────────────────────────────────────────────────────\n\n const posStyles: Record<string, React.CSSProperties> = {\n 'bottom-right': { bottom: 20, right: 20 },\n 'bottom-left': { bottom: 20, left: 20 },\n 'top-right': { top: 20, right: 20 },\n 'top-left': { top: 20, left: 20 },\n };\n\n const panelPos: Record<string, React.CSSProperties> = {\n 'bottom-right': { bottom: 70, right: 20 },\n 'bottom-left': { bottom: 70, left: 20 },\n 'top-right': { top: 70, right: 20 },\n 'top-left': { top: 70, left: 20 },\n };\n\n // ── Render ───────────────────────────────────────────────────────────────\n\n return (\n <>\n {/* Floating button */}\n {!hidden && (\n <button\n onClick={() => setIsOpen(!isOpen)}\n style={{\n position: 'fixed',\n ...posStyles[position],\n zIndex: 99999,\n background: color,\n color: '#fff',\n border: 'none',\n borderRadius: 28,\n padding: '10px 20px',\n fontSize: 14,\n fontWeight: 600,\n cursor: 'pointer',\n boxShadow: '0 4px 12px rgba(0,0,0,0.15)',\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n }}\n >\n 💬 {label}\n </button>\n )}\n\n {/* Panel */}\n {isOpen && (\n <div\n ref={panelRef}\n style={{\n position: 'fixed',\n ...panelPos[position],\n zIndex: 100000,\n width: 380,\n maxHeight: 520,\n background: '#fff',\n borderRadius: 12,\n boxShadow: '0 8px 32px rgba(0,0,0,0.18)',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n fontSize: 14,\n color: '#1f2937',\n }}\n >\n {/* Header */}\n <div\n style={{\n padding: '14px 16px',\n background: color,\n color: '#fff',\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n }}\n >\n <span style={{ fontWeight: 600 }}>\n {view === 'new' ? 'New Feedback' : view === 'detail' ? 'Feedback' : 'Feedback'}\n </span>\n <div style={{ display: 'flex', gap: 8 }}>\n {view !== 'list' && (\n <button\n onClick={() => { setView('list'); setSelectedItem(null); }}\n style={{ background: 'none', border: 'none', color: '#fff', cursor: 'pointer', fontSize: 14 }}\n >\n ← Back\n </button>\n )}\n <button\n onClick={() => setIsOpen(false)}\n style={{ background: 'none', border: 'none', color: '#fff', cursor: 'pointer', fontSize: 18 }}\n >\n ✕\n </button>\n </div>\n </div>\n\n {/* Body */}\n <div style={{ flex: 1, overflow: 'auto', padding: 16 }}>\n {/* LIST VIEW */}\n {view === 'list' && (\n <>\n <button\n onClick={() => setView('new')}\n style={{\n width: '100%',\n padding: '10px',\n background: color,\n color: '#fff',\n border: 'none',\n borderRadius: 8,\n cursor: 'pointer',\n fontWeight: 600,\n marginBottom: 12,\n }}\n >\n + New Feedback\n </button>\n {loading ? (\n <p style={{ textAlign: 'center', color: '#9ca3af' }}>Loading…</p>\n ) : items.length === 0 ? (\n <p style={{ textAlign: 'center', color: '#9ca3af' }}>No feedback yet</p>\n ) : (\n items.map((item) => (\n <div\n key={item._id}\n onClick={() => loadDetail(item)}\n style={{\n padding: 10,\n borderRadius: 8,\n border: '1px solid #e5e7eb',\n marginBottom: 8,\n cursor: 'pointer',\n }}\n >\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 4 }}>\n <span>\n {TYPE_META[item.type]?.icon} <strong>{item.title}</strong>\n </span>\n {item.type === 'feature-request' && (\n <button\n onClick={(e) => { e.stopPropagation(); vote(item._id); }}\n style={{\n background: 'none',\n border: '1px solid #d1d5db',\n borderRadius: 4,\n padding: '2px 6px',\n cursor: 'pointer',\n fontSize: 12,\n }}\n >\n ▲ {item.vote_count}\n </button>\n )}\n </div>\n <div style={{ fontSize: 12, color: '#6b7280' }}>\n {STATUS_LABELS[item.status]} · {item.reply_count} replies · {new Date(item.created_at).toLocaleDateString()}\n </div>\n </div>\n ))\n )}\n </>\n )}\n\n {/* NEW FEEDBACK VIEW */}\n {view === 'new' && (\n <>\n {/* Type selector */}\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginBottom: 12 }}>\n {(Object.entries(TYPE_META) as [FeedbackType, typeof TYPE_META['bug']][]).map(\n ([type, meta]) => (\n <button\n key={type}\n onClick={() => setSelectedType(type)}\n style={{\n padding: 8,\n borderRadius: 8,\n border: `2px solid ${selectedType === type ? meta.color : '#e5e7eb'}`,\n background: selectedType === type ? `${meta.color}10` : '#fff',\n cursor: 'pointer',\n fontSize: 13,\n textAlign: 'center',\n }}\n >\n {meta.icon} {meta.label}\n </button>\n ),\n )}\n </div>\n\n <input\n placeholder=\"Title\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n style={{\n width: '100%',\n padding: 8,\n borderRadius: 6,\n border: '1px solid #d1d5db',\n marginBottom: 8,\n fontSize: 14,\n boxSizing: 'border-box',\n }}\n />\n\n <textarea\n placeholder=\"Describe your feedback…\"\n value={content}\n onChange={(e) => setContent(e.target.value)}\n rows={4}\n style={{\n width: '100%',\n padding: 8,\n borderRadius: 6,\n border: '1px solid #d1d5db',\n marginBottom: 8,\n fontSize: 14,\n resize: 'vertical',\n boxSizing: 'border-box',\n }}\n />\n\n {/* Screenshot button */}\n {(window as any).html2canvas && (\n <button\n onClick={captureScreenshot}\n style={{\n background: 'none',\n border: '1px solid #d1d5db',\n borderRadius: 6,\n padding: '6px 10px',\n cursor: 'pointer',\n fontSize: 12,\n marginBottom: 8,\n color: screenshot ? '#10b981' : '#6b7280',\n }}\n >\n 📷 {screenshot ? 'Screenshot captured ✓' : 'Capture screenshot'}\n </button>\n )}\n\n <button\n onClick={submit}\n disabled={submitting || !title.trim() || !content.trim()}\n style={{\n width: '100%',\n padding: 10,\n background: submitting ? '#9ca3af' : color,\n color: '#fff',\n border: 'none',\n borderRadius: 8,\n cursor: submitting ? 'default' : 'pointer',\n fontWeight: 600,\n }}\n >\n {submitting ? 'Submitting…' : 'Submit'}\n </button>\n </>\n )}\n\n {/* DETAIL VIEW */}\n {view === 'detail' && selectedItem && (\n <>\n <div style={{ marginBottom: 12 }}>\n <div style={{ fontSize: 12, color: TYPE_META[selectedItem.type]?.color, fontWeight: 600, marginBottom: 4 }}>\n {TYPE_META[selectedItem.type]?.icon} {TYPE_META[selectedItem.type]?.label}\n </div>\n <h3 style={{ margin: '0 0 4px', fontSize: 16 }}>{selectedItem.title}</h3>\n <div style={{ fontSize: 12, color: '#6b7280', marginBottom: 8 }}>\n {STATUS_LABELS[selectedItem.status]} · {new Date(selectedItem.created_at).toLocaleDateString()}\n </div>\n <p style={{ margin: 0, lineHeight: 1.5 }}>{selectedItem.content}</p>\n </div>\n\n <hr style={{ border: 'none', borderTop: '1px solid #e5e7eb', margin: '12px 0' }} />\n\n {/* Replies */}\n <div style={{ marginBottom: 12 }}>\n <strong style={{ fontSize: 13 }}>Replies ({replies.length})</strong>\n {replies.map((r) => (\n <div\n key={r._id}\n style={{\n padding: 8,\n marginTop: 8,\n borderRadius: 6,\n background: r.author_type === 'developer' ? '#eff6ff' : '#f9fafb',\n borderLeft: `3px solid ${r.author_type === 'developer' ? '#3b82f6' : '#d1d5db'}`,\n }}\n >\n <div style={{ fontSize: 11, color: '#6b7280', marginBottom: 2 }}>\n {r.author_type === 'developer' ? '🛠️ Developer' : '👤 You'}{r.author_name ? ` · ${r.author_name}` : ''} · {new Date(r.created_at).toLocaleDateString()}\n </div>\n <div style={{ fontSize: 13 }}>{r.content}</div>\n </div>\n ))}\n </div>\n\n {/* Reply input */}\n <div style={{ display: 'flex', gap: 6 }}>\n <input\n placeholder=\"Write a reply…\"\n value={replyText}\n onChange={(e) => setReplyText(e.target.value)}\n onKeyDown={(e) => { if (e.key === 'Enter') submitReply(); }}\n style={{\n flex: 1,\n padding: 8,\n borderRadius: 6,\n border: '1px solid #d1d5db',\n fontSize: 13,\n }}\n />\n <button\n onClick={submitReply}\n disabled={!replyText.trim()}\n style={{\n padding: '8px 12px',\n background: color,\n color: '#fff',\n border: 'none',\n borderRadius: 6,\n cursor: 'pointer',\n fontSize: 13,\n }}\n >\n Send\n </button>\n </div>\n </>\n )}\n </div>\n </div>\n )}\n </>\n );\n}\n","'use client';\nimport { useContext, useState, useCallback, useMemo, useEffect, useRef } from 'react';\nimport { ExperienceContext } from './context';\n\nexport function useExperience() {\n const ctx = useContext(ExperienceContext);\n if (!ctx) throw new Error('useExperience must be used within ExperienceProvider');\n return ctx;\n}\n\nexport function useTrack() {\n const experience = useExperience();\n return useMemo(() => ({\n track: (eventName: string, metadata?: Record<string, string>) => experience.track(eventName, metadata),\n trackPageView: () => experience.trackPageView(),\n }), [experience]);\n}\n\nexport function useFlag<T>(flagName: string, defaultValue: T): T {\n const experience = useExperience();\n return experience.getFlag(flagName, defaultValue);\n}\n\n// ─── Translation Types ──────────────────────────────────────────\n\ninterface TranslationResult {\n t: (key: string, params?: Record<string, string | number>) => string;\n locale: string;\n setLocale: (locale: string) => void;\n locales: Array<{ code: string; name: string; progress?: number }>;\n isLoading: boolean;\n}\n\ninterface TranslationCache {\n [locale: string]: Record<string, string>;\n}\n\n// Module-level cache shared across hook instances\nconst translationCache: TranslationCache = {};\nconst fetchPromises = new Map<string, Promise<Record<string, string>>>();\n\n/**\n * Translation hook for the Experience Platform.\n *\n * Fetches translations from the Experience API, caches in memory + localStorage,\n * and provides `t()` for interpolation and `setLocale()` for switching.\n *\n * Usage:\n * ```tsx\n * const { t, locale, setLocale, locales } = useTranslation();\n * return <h1>{t('greeting', { name: 'Alex' })}</h1>;\n * ```\n */\nexport function useTranslation(): TranslationResult {\n const experience = useExperience();\n const config = (experience as any).config || {};\n const appId: string = config.appId || '';\n const apiBaseUrl: string = config.apiUrl || config.baseUrl || 'https://experience-api.shellapps.com';\n\n const [locale, setLocaleState] = useState(() => {\n if (typeof window === 'undefined') return 'en';\n // Check localStorage for persisted locale preference\n const saved = localStorage.getItem(`exp_locale_${appId}`);\n if (saved) return saved;\n return navigator.language?.split('-')[0] || 'en';\n });\n\n const [translations, setTranslations] = useState<Record<string, string>>(() => {\n // Try memory cache first\n if (translationCache[locale]) return translationCache[locale]!;\n // Try localStorage\n if (typeof window !== 'undefined') {\n try {\n const cached = localStorage.getItem(`exp_translations_${appId}_${locale}`);\n if (cached) {\n const parsed = JSON.parse(cached);\n translationCache[locale] = parsed;\n return parsed;\n }\n } catch { /* ignore */ }\n }\n return {};\n });\n\n const [locales, setLocales] = useState<Array<{ code: string; name: string; progress?: number }>>([]);\n const [isLoading, setIsLoading] = useState(false);\n\n // Fetch translations for a locale\n const fetchTranslations = useCallback(async (loc: string): Promise<Record<string, string>> => {\n if (!appId) return {};\n\n // Deduplicate in-flight requests\n const cacheKey = `${appId}_${loc}`;\n const existing = fetchPromises.get(cacheKey);\n if (existing) return existing;\n\n const promise = (async () => {\n try {\n const res = await fetch(`${apiBaseUrl}/api/v1/translations/${appId}/${loc}`);\n if (!res.ok) return {};\n const data = await res.json();\n // Cache in memory and localStorage\n translationCache[loc] = data;\n if (typeof window !== 'undefined') {\n try {\n localStorage.setItem(`exp_translations_${appId}_${loc}`, JSON.stringify(data));\n } catch { /* quota exceeded, ignore */ }\n }\n return data as Record<string, string>;\n } catch {\n return {};\n } finally {\n fetchPromises.delete(cacheKey);\n }\n })();\n\n fetchPromises.set(cacheKey, promise);\n return promise;\n }, [appId, apiBaseUrl]);\n\n // Fetch available locales\n useEffect(() => {\n if (!appId) return;\n fetch(`${apiBaseUrl}/api/v1/translations/${appId}/locales`)\n .then(res => res.ok ? res.json() : { locales: [] })\n .then(data => setLocales(data.locales || []))\n .catch(() => {});\n }, [appId, apiBaseUrl]);\n\n // Fetch translations when locale changes\n useEffect(() => {\n if (!appId) return;\n\n // Use cached if available\n if (translationCache[locale]) {\n setTranslations(translationCache[locale]!);\n return;\n }\n\n setIsLoading(true);\n fetchTranslations(locale).then(data => {\n setTranslations(data);\n setIsLoading(false);\n });\n }, [locale, appId, fetchTranslations]);\n\n // t() function with interpolation and plural support\n const t = useCallback((key: string, params?: Record<string, string | number>): string => {\n let result = translations[key] ?? key;\n\n // Handle plurals: \"singular||plural\" with count param\n if (result.includes('||') && params && 'count' in params) {\n const parts = result.split('||');\n const count = Number(params.count);\n result = count === 1 ? (parts[0] ?? result) : (parts[1] ?? result);\n }\n\n // Handle interpolation: {{variable}}\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n result = result.replace(new RegExp(`\\{\\{${k}\\}\\}`, 'g'), String(v));\n }\n }\n\n return result;\n }, [translations]);\n\n // setLocale with persistence\n const setLocale = useCallback((newLocale: string) => {\n setLocaleState(newLocale);\n if (typeof window !== 'undefined') {\n localStorage.setItem(`exp_locale_${appId}`, newLocale);\n }\n }, [appId]);\n\n return { t, locale, setLocale, locales, isLoading };\n}\n"],"mappings":"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","names":["React","jsx","React","useState","useEffect","useRef","jsx","jsxs","useState","useRef","useEffect","useContext","useState","useCallback","useEffect","useContext","useState","useCallback","useEffect"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shellapps/experience-react",
3
- "version": "1.5.0",
3
+ "version": "1.5.1",
4
4
  "description": "React SDK for @shellapps/experience",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -43,5 +43,5 @@
43
43
  "url": "git+https://github.com/ShellTechnology/shellapps-js.git",
44
44
  "directory": "packages/experience-react"
45
45
  },
46
- "gitHead": "bad156fc51505903e8cbed4381c0b5f5e17341fa"
46
+ "gitHead": "7197532df3daa87b53338c610978af3c27800402"
47
47
  }