anton-bakker-feedback 0.3.2 → 0.3.4
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/chunks/{i18n-Du-or3T4.js → i18n-Cba0xHU-.js} +35 -1
- package/dist/chunks/i18n-Cba0xHU-.js.map +1 -0
- package/dist/core/locales/en.d.ts.map +1 -1
- package/dist/core/locales/nl.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/react/admin/FeedbackDetailPage.d.ts +2 -1
- package/dist/react/admin/FeedbackDetailPage.d.ts.map +1 -1
- package/dist/react/admin/index.js +50 -4
- package/dist/react/admin/index.js.map +1 -1
- package/dist/react/admin/types.d.ts +2 -0
- package/dist/react/admin/types.d.ts.map +1 -1
- package/dist/react.js +17 -17
- package/dist/react.js.map +1 -1
- package/package.json +6 -2
- package/dist/chunks/i18n-Du-or3T4.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anton-bakker-feedback",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "Zero-dependency business logic layer for feedback collection — types, capture, transport, config, i18n, observability",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,7 +39,11 @@
|
|
|
39
39
|
"test": "vitest",
|
|
40
40
|
"test:ci": "vitest run",
|
|
41
41
|
"lint": "eslint src --max-warnings 0",
|
|
42
|
-
"clean": "rm -rf dist"
|
|
42
|
+
"clean": "rm -rf dist",
|
|
43
|
+
"prepublishOnly": "npm run build",
|
|
44
|
+
"publish:patch": "npm run build && npm version patch && npm publish",
|
|
45
|
+
"publish:minor": "npm run build && npm version minor && npm publish",
|
|
46
|
+
"publish:major": "npm run build && npm version major && npm publish"
|
|
43
47
|
},
|
|
44
48
|
"peerDependencies": {
|
|
45
49
|
"anton-bakker-tailwind-components": ">=0.7.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"i18n-Du-or3T4.js","sources":["../../src/core/config.ts","../../src/core/locales/en.ts","../../src/core/locales/nl.ts","../../src/core/i18n.ts"],"sourcesContent":["/**\n * Configuration schema and validation for the anton-bakker-feedback package.\n */\n\nimport type { FeedbackEnvironment } from './types.js';\n\n// ─────────────────────────────────────────────────────────────────\n// Config Interface\n// ─────────────────────────────────────────────────────────────────\n\n/** Full configuration object for the feedback package */\nexport interface FeedbackConfig {\n /** Required — alphanumeric, hyphens, underscores only */\n appName: string;\n version?: string;\n /** Defaults to 'production' if absent */\n environment?: FeedbackEnvironment;\n\n transport: {\n /** POST endpoint — app provides its own route */\n submitUrl: string;\n /** If absent → dialogue disabled, form shown */\n dialogueUrl?: string;\n /** If absent → screenshots disabled */\n screenshotUploadUrl?: string;\n };\n\n dialogue?: {\n /** Default 3; min 1, max 10 */\n maxTurns?: number;\n /** Default 30_000 */\n timeoutMs?: number;\n };\n\n screenshots?: {\n /** Default true if screenshotUploadUrl set */\n enabled?: boolean;\n /** Default 5_242_880 (5 MB) */\n maxSizeBytes?: number;\n };\n\n ui?: {\n /** Default 'bottom-right' */\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\n /** Default 'auto' */\n theme?: 'light' | 'dark' | 'auto';\n };\n\n observability?: {\n /** Default true */\n enabled?: boolean;\n /** Default 'Feedback/Service' */\n namespace?: string;\n /** Default ['x-trace-id', 'x-request-id'] */\n traceIdHeaders?: string[];\n };\n\n i18n?: {\n /** Default 'en' */\n locale?: string;\n customTranslations?: Record<string, Record<string, string>>;\n };\n\n signOff?: {\n /** Default 7; 0 = manual only */\n autoConfirmDays?: number;\n /** Default 3 */\n reminderDays?: number;\n };\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Resolved Config — all fields have values, no undefined\n// ─────────────────────────────────────────────────────────────────\n\n/** Fully-resolved config with all defaults applied — no undefined fields */\nexport interface ResolvedFeedbackConfig {\n appName: string;\n version: string;\n environment: FeedbackEnvironment;\n\n transport: {\n submitUrl: string;\n dialogueUrl: string | undefined;\n screenshotUploadUrl: string | undefined;\n };\n\n dialogue: {\n maxTurns: number;\n timeoutMs: number;\n };\n\n screenshots: {\n enabled: boolean;\n maxSizeBytes: number;\n };\n\n ui: {\n position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\n theme: 'light' | 'dark' | 'auto';\n };\n\n observability: {\n enabled: boolean;\n namespace: string;\n traceIdHeaders: string[];\n };\n\n i18n: {\n locale: string;\n customTranslations: Record<string, Record<string, string>>;\n };\n\n signOff: {\n autoConfirmDays: number;\n reminderDays: number;\n };\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Validation\n// ─────────────────────────────────────────────────────────────────\n\nconst APP_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;\nconst URL_PATTERN = /^https?:\\/\\/.+/;\n\n/** Validation result with structured errors */\nexport interface ConfigValidationResult {\n valid: boolean;\n errors: string[];\n}\n\n/**\n * Validates a FeedbackConfig and returns structured errors.\n * Returns { valid: false, errors: [...] } with actionable messages.\n */\nexport function validateConfig(config: Partial<FeedbackConfig>): ConfigValidationResult {\n const errors: string[] = [];\n\n // Validate appName\n if (!config.appName || config.appName.trim() === '') {\n errors.push('appName is required and must not be empty');\n } else if (!APP_NAME_PATTERN.test(config.appName)) {\n errors.push(\n `appName \"${config.appName}\" is invalid — must match /^[a-zA-Z0-9_-]+$/ (no spaces, slashes, or special characters)`,\n );\n }\n\n // Validate transport.submitUrl\n if (!config.transport?.submitUrl || config.transport.submitUrl.trim() === '') {\n errors.push('transport.submitUrl is required and must not be empty');\n } else if (!URL_PATTERN.test(config.transport.submitUrl)) {\n errors.push(\n `transport.submitUrl \"${config.transport.submitUrl}\" must be a valid URL starting with http:// or https://`,\n );\n }\n\n // Validate dialogue.maxTurns\n if (config.dialogue?.maxTurns !== undefined) {\n const turns = config.dialogue.maxTurns;\n if (!Number.isInteger(turns) || turns < 1 || turns > 10) {\n errors.push(\n `dialogue.maxTurns must be an integer between 1 and 10 (got ${turns})`,\n );\n }\n }\n\n // Validate screenshots.maxSizeBytes\n if (config.screenshots?.maxSizeBytes !== undefined) {\n const size = config.screenshots.maxSizeBytes;\n if (!Number.isFinite(size) || size <= 0) {\n errors.push(`screenshots.maxSizeBytes must be a positive number (got ${size})`);\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n\n/**\n * Merges user-supplied partial config with defaults.\n * Returns a fully-resolved config with no undefined fields.\n */\nexport function resolveConfig(config: FeedbackConfig): ResolvedFeedbackConfig {\n const screenshotsEnabled =\n config.screenshots?.enabled !== undefined\n ? config.screenshots.enabled\n : !!config.transport.screenshotUploadUrl;\n\n return {\n appName: config.appName,\n version: config.version ?? '0.0.0',\n environment: config.environment ?? 'production',\n\n transport: {\n submitUrl: config.transport.submitUrl,\n dialogueUrl: config.transport.dialogueUrl,\n screenshotUploadUrl: config.transport.screenshotUploadUrl,\n },\n\n dialogue: {\n maxTurns: config.dialogue?.maxTurns ?? 3,\n timeoutMs: config.dialogue?.timeoutMs ?? 30_000,\n },\n\n screenshots: {\n enabled: screenshotsEnabled,\n maxSizeBytes: config.screenshots?.maxSizeBytes ?? 5_242_880,\n },\n\n ui: {\n position: config.ui?.position ?? 'bottom-right',\n theme: config.ui?.theme ?? 'auto',\n },\n\n observability: {\n enabled: config.observability?.enabled ?? true,\n namespace: config.observability?.namespace ?? 'Feedback/Service',\n traceIdHeaders: config.observability?.traceIdHeaders ?? [\n 'x-trace-id',\n 'x-request-id',\n ],\n },\n\n i18n: {\n locale: config.i18n?.locale ?? 'en',\n customTranslations: config.i18n?.customTranslations ?? {},\n },\n\n signOff: {\n autoConfirmDays: config.signOff?.autoConfirmDays ?? 7,\n reminderDays: config.signOff?.reminderDays ?? 3,\n },\n };\n}\n","/**\n * English (en) translations for the feedback package.\n * Key format: feedback.{component}.{element}\n */\nexport const en: Record<string, string> = {\n // Button labels\n 'feedback.button.open': 'Feedback',\n 'feedback.button.close': 'Close',\n 'feedback.button.screenshot': 'Add screenshot',\n 'feedback.button.removeScreenshot': 'Remove screenshot',\n 'feedback.button.submit': 'Submit feedback',\n\n // Form labels\n 'feedback.form.contentLabel': 'What would you like to share?',\n 'feedback.form.contentPlaceholder': 'Describe the issue or suggestion in detail...',\n 'feedback.form.typeLabel': 'Type',\n 'feedback.form.typePlaceholder': 'Select a type',\n\n // Dialogue states\n 'feedback.dialogue.opening': 'Thank you for your feedback. I have a few questions to help us understand better.',\n 'feedback.dialogue.thinking': 'Thinking...',\n 'feedback.dialogue.error': 'Sorry, something went wrong. Please try again.',\n 'feedback.dialogue.complete': 'Thank you! Your feedback has been recorded.',\n 'feedback.dialogue.turnPrompt': 'Your response:',\n\n // Feedback types\n 'feedback.types.bug': 'Bug report',\n 'feedback.types.feature': 'Feature request',\n 'feedback.types.ux': 'UX improvement',\n 'feedback.types.other': 'Other',\n\n // Status messages\n 'feedback.status.submitting': 'Submitting your feedback...',\n 'feedback.status.success': 'Feedback submitted successfully!',\n 'feedback.status.error': 'Failed to submit feedback. Please try again.',\n 'feedback.status.retry': 'Retry',\n 'feedback.status.duplicate': 'This feedback has already been submitted.',\n\n // Toast messages\n 'feedback.toast.screenshotAdded': 'Screenshot attached',\n 'feedback.toast.screenshotRemoved': 'Screenshot removed',\n 'feedback.toast.screenshotError': 'Failed to capture screenshot',\n\n // Sign-off messages\n 'feedback.signoff.confirmLabel': 'Mark as resolved',\n 'feedback.signoff.reopenLabel': 'Reopen',\n 'feedback.signoff.autoConfirmNotice':\n 'This issue will be marked resolved in {{days}} days unless you reopen it.',\n\n // Correction messages\n 'feedback.correction.editLabel': 'Edit',\n 'feedback.correction.reasonLabel': 'Reason for change',\n 'feedback.correction.saveLabel': 'Save changes',\n};\n","/**\n * Dutch (nl) translations for the feedback package.\n * Key format: feedback.{component}.{element}\n */\nexport const nl: Record<string, string> = {\n // Button labels\n 'feedback.button.open': 'Feedback',\n 'feedback.button.close': 'Sluiten',\n 'feedback.button.screenshot': 'Screenshot toevoegen',\n 'feedback.button.removeScreenshot': 'Screenshot verwijderen',\n 'feedback.button.submit': 'Feedback versturen',\n\n // Form labels\n 'feedback.form.contentLabel': 'Wat wilt u delen?',\n 'feedback.form.contentPlaceholder': 'Beschrijf het probleem of de suggestie in detail...',\n 'feedback.form.typeLabel': 'Type',\n 'feedback.form.typePlaceholder': 'Selecteer een type',\n\n // Dialogue states\n 'feedback.dialogue.opening': 'Bedankt voor uw feedback. Ik heb een paar vragen om dit beter te begrijpen.',\n 'feedback.dialogue.thinking': 'Aan het nadenken...',\n 'feedback.dialogue.error': 'Er is iets misgegaan. Probeer het opnieuw.',\n 'feedback.dialogue.complete': 'Bedankt! Uw feedback is geregistreerd.',\n 'feedback.dialogue.turnPrompt': 'Uw antwoord:',\n\n // Feedback types\n 'feedback.types.bug': 'Foutmelding',\n 'feedback.types.feature': 'Functiewens',\n 'feedback.types.ux': 'UX-verbetering',\n 'feedback.types.other': 'Overig',\n\n // Status messages\n 'feedback.status.submitting': 'Feedback verzenden...',\n 'feedback.status.success': 'Feedback succesvol verzonden!',\n 'feedback.status.error': 'Verzenden mislukt. Probeer het opnieuw.',\n 'feedback.status.retry': 'Opnieuw proberen',\n 'feedback.status.duplicate': 'Deze feedback is al ingediend.',\n\n // Toast messages\n 'feedback.toast.screenshotAdded': 'Screenshot bijgevoegd',\n 'feedback.toast.screenshotRemoved': 'Screenshot verwijderd',\n 'feedback.toast.screenshotError': 'Screenshot maken mislukt',\n\n // Sign-off messages\n 'feedback.signoff.confirmLabel': 'Markeer als opgelost',\n 'feedback.signoff.reopenLabel': 'Heropenen',\n 'feedback.signoff.autoConfirmNotice':\n 'Dit probleem wordt als opgelost gemarkeerd over {{days}} dagen, tenzij u het heropent.',\n\n // Correction messages\n 'feedback.correction.editLabel': 'Bewerken',\n 'feedback.correction.reasonLabel': 'Reden voor wijziging',\n 'feedback.correction.saveLabel': 'Wijzigingen opslaan',\n};\n","/**\n * Internationalisation utilities for the feedback package.\n *\n * t() falls back gracefully:\n * - Unknown locale → English\n * - Unknown key → returns the key itself (never undefined)\n *\n * Template interpolation: {{param}} → replaced with params values\n */\n\nimport { en } from './locales/en.js';\nimport { nl } from './locales/nl.js';\n\n// ─────────────────────────────────────────────────────────────────\n// Constants\n// ─────────────────────────────────────────────────────────────────\n\n/** Supported locale codes */\nexport const SUPPORTED_LOCALES: readonly string[] = ['en', 'nl'] as const;\n\n/** Default locale when unknown locale is requested */\nexport const DEFAULT_LOCALE = 'en';\n\n/** Built-in translations keyed by locale */\nconst BUILT_IN_TRANSLATIONS: Record<string, Record<string, string>> = {\n en,\n nl,\n};\n\n// ─────────────────────────────────────────────────────────────────\n// Translation Function\n// ─────────────────────────────────────────────────────────────────\n\n/**\n * Looks up a translation key.\n *\n * Fallback chain:\n * 1. customTranslations[locale][key]\n * 2. BUILT_IN_TRANSLATIONS[locale][key]\n * 3. BUILT_IN_TRANSLATIONS['en'][key]\n * 4. key itself (never returns undefined or throws)\n *\n * Template interpolation: {{days}} → params.days (if params provided)\n *\n * @param key - Dot-notation key e.g. 'feedback.button.open'\n * @param locale - BCP 47 locale code e.g. 'en', 'nl'\n * @param customTranslations - Optional custom translations (overrides built-in)\n * @param params - Optional template parameters e.g. { days: 7 }\n */\nexport function t(\n key: string,\n locale: string,\n customTranslations?: Record<string, Record<string, string>>,\n params?: Record<string, string | number>,\n): string {\n // 1. Check custom translations for this locale\n const customForLocale = customTranslations?.[locale];\n if (customForLocale?.[key] !== undefined) {\n return interpolate(customForLocale[key], params);\n }\n\n // 2. Check custom translations for English fallback\n const customEn = customTranslations?.['en'];\n if (locale !== 'en' && customEn?.[key] !== undefined) {\n // Only use English custom if locale not found\n }\n\n // 3. Check built-in translations for this locale\n const builtInForLocale = BUILT_IN_TRANSLATIONS[locale];\n if (builtInForLocale?.[key] !== undefined) {\n return interpolate(builtInForLocale[key] as string, params);\n }\n\n // 4. Fall back to English built-in\n const englishBuiltIn = BUILT_IN_TRANSLATIONS['en'];\n if (englishBuiltIn?.[key] !== undefined) {\n return interpolate(englishBuiltIn[key] as string, params);\n }\n\n // 5. Fall back to English custom (if provided)\n if (customEn?.[key] !== undefined) {\n return interpolate(customEn[key] as string, params);\n }\n\n // 6. Return the key itself — never undefined\n return key;\n}\n\n/**\n * Replaces {{param}} placeholders with values from params.\n */\nfunction interpolate(\n template: string,\n params?: Record<string, string | number>,\n): string {\n if (!params) return template;\n return template.replace(/\\{\\{(\\w+)\\}\\}/g, (_match, name: string) => {\n const value = params[name];\n return value !== undefined ? String(value) : `{{${name}}}`;\n });\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Locale Detection\n// ─────────────────────────────────────────────────────────────────\n\n/**\n * Returns the locale to use.\n * If preferredLocale is a supported locale → return it.\n * Otherwise → return DEFAULT_LOCALE ('en').\n *\n * @param preferredLocale - BCP 47 locale code or undefined\n */\nexport function detectLocale(preferredLocale?: string): string {\n if (preferredLocale && SUPPORTED_LOCALES.includes(preferredLocale)) {\n return preferredLocale;\n }\n return DEFAULT_LOCALE;\n}\n"],"names":[],"mappings":"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"}
|