@patch-adams/plugin-feedback 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -122,15 +122,19 @@ var FeedbackConfigSchema = zod.z.object({
122
122
 
123
123
  // src/i18n/en.json
124
124
  var en_default = {
125
- title: "Send Feedback",
126
- ratingLabel: "How would you rate this content?",
127
- issueTypeLabel: "What type of feedback?",
128
- selectIssueType: "Select type...",
129
- subtypeLabel: "More specifically:",
125
+ title: "Give Feedback",
126
+ ratingLabel: "How would you rate this course?",
127
+ issueTypeLabel: "What type of issue are you experiencing?",
128
+ selectIssueType: "Select an issue type...",
129
+ subtypeLabel: "Please specify:",
130
130
  selectSubtype: "Select...",
131
- commentLabel: "Your feedback",
132
- commentPlaceholder: "Please describe your feedback in detail...",
133
- submit: "Send Feedback",
131
+ commentLabel: "Tell us more:",
132
+ commentLabelPositive: "What did you like?",
133
+ commentLabelNegative: "What should we improve?",
134
+ commentPlaceholder: "Share your feedback...",
135
+ commentPlaceholderPositive: "Tell us what worked well...",
136
+ commentPlaceholderNegative: "Describe the issue or what could be improved...",
137
+ submit: "Submit",
134
138
  submitting: "Sending...",
135
139
  thankYou: "Thank you for your feedback!",
136
140
  errorSubmitting: "Failed to send feedback. Please try again.",
@@ -140,17 +144,21 @@ var en_default = {
140
144
 
141
145
  // src/i18n/fr.json
142
146
  var fr_default = {
143
- title: "Envoyer un commentaire",
144
- ratingLabel: "Comment \xE9valuez-vous ce contenu?",
145
- issueTypeLabel: "Type de commentaire",
146
- selectIssueType: "S\xE9lectionner...",
147
- subtypeLabel: "Plus pr\xE9cis\xE9ment:",
148
- selectSubtype: "S\xE9lectionner...",
149
- commentLabel: "Votre commentaire",
150
- commentPlaceholder: "Veuillez d\xE9crire votre commentaire en d\xE9tail...",
151
- submit: "Envoyer",
147
+ title: "Donner votre avis",
148
+ ratingLabel: "Comment \xE9valuez-vous ce cours?",
149
+ issueTypeLabel: "Quel type de probl\xE8me rencontrez-vous?",
150
+ selectIssueType: "S\xE9lectionnez un type de probl\xE8me...",
151
+ subtypeLabel: "Veuillez pr\xE9ciser:",
152
+ selectSubtype: "S\xE9lectionnez...",
153
+ commentLabel: "Dites-nous en plus:",
154
+ commentLabelPositive: "Qu'avez-vous aim\xE9?",
155
+ commentLabelNegative: "Que devrions-nous am\xE9liorer?",
156
+ commentPlaceholder: "Partagez vos commentaires...",
157
+ commentPlaceholderPositive: "Dites-nous ce qui a bien fonctionn\xE9...",
158
+ commentPlaceholderNegative: "D\xE9crivez le probl\xE8me ou ce qui pourrait \xEAtre am\xE9lior\xE9...",
159
+ submit: "Soumettre",
152
160
  submitting: "Envoi en cours...",
153
- thankYou: "Merci pour votre commentaire!",
161
+ thankYou: "Merci pour vos commentaires!",
154
162
  errorSubmitting: "\xC9chec de l'envoi. Veuillez r\xE9essayer.",
155
163
  errorRequired: "Veuillez remplir les champs obligatoires.",
156
164
  characterCount: "{current}/{max}"
@@ -263,9 +271,9 @@ function generateFeedbackWidget(config) {
263
271
  html += '</div>';
264
272
  ` : ""}
265
273
 
266
- // Issue Type (if enabled)
274
+ // Issue Type (if enabled) - hidden by default, shown when rating <= 3
267
275
  ${config.showIssueType ? `
268
- html += '<div class="pa-feedback-field">';
276
+ html += '<div id="pa-feedback-issue-type-field" class="pa-feedback-field pa-feedback-hidden">';
269
277
  html += '<label class="pa-feedback-label" for="pa-feedback-issue-type">' + FB.translations.issueTypeLabel + '${config.issueTypeRequired ? ' <span class="pa-feedback-required">*</span>' : ""}</label>';
270
278
  html += '<select id="pa-feedback-issue-type" class="pa-feedback-select">';
271
279
  html += '<option value="">' + FB.translations.selectIssueType + '</option>';
@@ -287,8 +295,8 @@ function generateFeedbackWidget(config) {
287
295
  // Comment (if enabled)
288
296
  ${config.showComment ? `
289
297
  html += '<div class="pa-feedback-field">';
290
- html += '<label class="pa-feedback-label" for="pa-feedback-comment">' + FB.translations.commentLabel + '${config.commentRequired ? ' <span class="pa-feedback-required">*</span>' : ""}</label>';
291
- html += '<textarea id="pa-feedback-comment" class="pa-feedback-textarea" maxlength="${config.commentMaxLength}" placeholder="${config.commentPlaceholder || ""}" rows="4"></textarea>';
298
+ html += '<label id="pa-feedback-comment-label" class="pa-feedback-label" for="pa-feedback-comment">' + FB.translations.commentLabel + '${config.commentRequired ? ' <span class="pa-feedback-required">*</span>' : ""}</label>';
299
+ html += '<textarea id="pa-feedback-comment" class="pa-feedback-textarea" maxlength="${config.commentMaxLength}" placeholder="' + FB.translations.commentPlaceholder + '" rows="4"></textarea>';
292
300
  html += '<div class="pa-feedback-counter"><span id="pa-feedback-char-count">0</span>/${config.commentMaxLength}</div>';
293
301
  html += '</div>';
294
302
  ` : ""}
@@ -347,6 +355,7 @@ function generateFeedbackWidget(config) {
347
355
  star.addEventListener('click', function() {
348
356
  FB.rating = index + 1;
349
357
  updateStars();
358
+ handleRatingChange();
350
359
  });
351
360
  star.addEventListener('mouseenter', function() {
352
361
  highlightStars(index + 1);
@@ -409,6 +418,43 @@ function generateFeedbackWidget(config) {
409
418
  star.classList.toggle('pa-feedback-star-hover', isFilled && count > FB.rating);
410
419
  });
411
420
  }
421
+
422
+ function handleRatingChange() {
423
+ ${config.showIssueType ? `
424
+ var issueTypeField = document.getElementById('pa-feedback-issue-type-field');
425
+ var subtypeField = document.getElementById('pa-feedback-subtype-field');
426
+ var issueTypeSelect = document.getElementById('pa-feedback-issue-type');
427
+ var subtypeSelect = document.getElementById('pa-feedback-subtype');
428
+ ` : ""}
429
+
430
+ ${config.showComment ? `
431
+ var commentLabel = document.getElementById('pa-feedback-comment-label');
432
+ var commentTextarea = document.getElementById('pa-feedback-comment');
433
+ ` : ""}
434
+
435
+ if (FB.rating <= 3) {
436
+ // Negative/neutral feedback path - show issue type
437
+ ${config.showIssueType ? `
438
+ issueTypeField.classList.remove('pa-feedback-hidden');
439
+ ` : ""}
440
+ ${config.showComment ? `
441
+ commentLabel.innerHTML = FB.translations.commentLabelNegative + '${config.commentRequired ? ' <span class="pa-feedback-required">*</span>' : ""}';
442
+ commentTextarea.placeholder = FB.translations.commentPlaceholderNegative;
443
+ ` : ""}
444
+ } else {
445
+ // Positive feedback path - hide issue type
446
+ ${config.showIssueType ? `
447
+ issueTypeField.classList.add('pa-feedback-hidden');
448
+ subtypeField.classList.add('pa-feedback-hidden');
449
+ issueTypeSelect.value = '';
450
+ subtypeSelect.value = '';
451
+ ` : ""}
452
+ ${config.showComment ? `
453
+ commentLabel.innerHTML = FB.translations.commentLabelPositive + '${config.commentRequired ? ' <span class="pa-feedback-required">*</span>' : ""}';
454
+ commentTextarea.placeholder = FB.translations.commentPlaceholderPositive;
455
+ ` : ""}
456
+ }
457
+ }
412
458
  ` : ""}
413
459
 
414
460
  ${config.showIssueType ? `
@@ -457,7 +503,8 @@ function generateFeedbackWidget(config) {
457
503
  ` : ""}
458
504
 
459
505
  ${config.showIssueType && config.issueTypeRequired ? `
460
- if (!document.getElementById('pa-feedback-issue-type').value) {
506
+ // Issue type is only required when rating <= 3
507
+ if (FB.rating <= 3 && !document.getElementById('pa-feedback-issue-type').value) {
461
508
  errors.push('Issue type is required');
462
509
  }
463
510
  ` : ""}
@@ -490,9 +537,17 @@ function generateFeedbackWidget(config) {
490
537
  function collectMetadata() {
491
538
  var meta = {};
492
539
  var cfg = FB.config.includeMetadata;
540
+ var htmlEl = document.documentElement;
493
541
 
542
+ // Get courseId from HTML data attribute (the manifest course ID)
543
+ // This is the unique identifier for this specific patched package
544
+ var courseId = htmlEl.getAttribute('data-pa-course-id');
545
+ if (courseId) {
546
+ meta.courseId = courseId;
547
+ }
548
+
549
+ // Get course title from LRS bridge if available
494
550
  if (cfg.courseId && window.pa_patcher && window.pa_patcher.lrs && window.pa_patcher.lrs.courseInfo) {
495
- meta.courseId = window.pa_patcher.lrs.courseInfo.id;
496
551
  meta.courseTitle = window.pa_patcher.lrs.courseInfo.title;
497
552
  }
498
553
 
@@ -556,8 +611,17 @@ function generateFeedbackWidget(config) {
556
611
 
557
612
  FB.rating = 0;
558
613
  ${config.showRating ? "updateStars();" : ""}
559
- ${config.showComment ? "updateCharCount();" : ""}
614
+ ${config.showComment ? `
615
+ updateCharCount();
616
+ // Reset comment label and placeholder to default
617
+ var commentLabel = document.getElementById('pa-feedback-comment-label');
618
+ var commentTextarea = document.getElementById('pa-feedback-comment');
619
+ commentLabel.innerHTML = FB.translations.commentLabel + '${config.commentRequired ? ' <span class="pa-feedback-required">*</span>' : ""}';
620
+ commentTextarea.placeholder = FB.translations.commentPlaceholder;
621
+ ` : ""}
560
622
  ${config.showIssueType ? `
623
+ // Hide issue type fields on reset
624
+ document.getElementById('pa-feedback-issue-type-field').classList.add('pa-feedback-hidden');
561
625
  document.getElementById('pa-feedback-subtype-field').classList.add('pa-feedback-hidden');
562
626
  ` : ""}
563
627
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/config.ts","../src/i18n/en.json","../src/i18n/fr.json","../src/i18n/index.ts","../src/widget.ts","../src/styles.ts","../src/index.ts"],"names":["z"],"mappings":"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","file":"index.cjs","sourcesContent":["import { z } from 'zod';\n\n/**\n * Issue subtype definition\n */\nexport const IssueSubtypeSchema = z.object({\n /** Unique identifier for the subtype */\n id: z.string().min(1),\n /** Display label for the subtype */\n label: z.string().min(1),\n});\n\n/**\n * Issue type definition with optional subtypes\n */\nexport const IssueTypeSchema = z.object({\n /** Unique identifier for the issue type */\n id: z.string().min(1),\n /** Display label for the issue type */\n label: z.string().min(1),\n /** Optional subtypes that appear when this type is selected */\n subtypes: z.array(IssueSubtypeSchema).optional(),\n});\n\n/**\n * Metadata options - what to include with feedback submissions\n */\nexport const MetadataOptionsSchema = z.object({\n /** Include the course ID from LRS bridge (if available) */\n courseId: z.boolean().default(true),\n /** Include the current lesson/page ID (URL hash or pathname) */\n lessonId: z.boolean().default(true),\n /** Include the browser user agent */\n userAgent: z.boolean().default(true),\n /** Include timestamp of submission */\n timestamp: z.boolean().default(true),\n /** Include the full current URL */\n url: z.boolean().default(true),\n});\n\n/**\n * Default issue types if none are provided\n */\nexport const DEFAULT_ISSUE_TYPES: z.infer<typeof IssueTypeSchema>[] = [\n {\n id: 'content',\n label: 'Content Issue',\n subtypes: [\n { id: 'typo', label: 'Typo / Grammar' },\n { id: 'incorrect', label: 'Incorrect Information' },\n { id: 'unclear', label: 'Unclear / Confusing' },\n { id: 'outdated', label: 'Outdated Content' },\n ],\n },\n {\n id: 'technical',\n label: 'Technical Issue',\n subtypes: [\n { id: 'audio', label: 'Audio Problem' },\n { id: 'video', label: 'Video Problem' },\n { id: 'navigation', label: 'Navigation Issue' },\n { id: 'display', label: 'Display / Layout Issue' },\n ],\n },\n {\n id: 'suggestion',\n label: 'Suggestion',\n },\n {\n id: 'other',\n label: 'Other',\n },\n];\n\n/**\n * Full feedback plugin configuration schema\n */\nexport const FeedbackConfigSchema = z.object({\n /** Whether the feedback plugin is enabled */\n enabled: z.boolean().default(true),\n\n // === Endpoint Configuration ===\n\n /** API endpoint URL for submitting feedback (optional - if not set, logs to console) */\n endpoint: z.string().url().optional(),\n\n /** HTTP method for the endpoint (default: POST) */\n method: z.enum(['POST', 'PUT']).default('POST'),\n\n /** Additional headers to send with the request */\n headers: z.record(z.string()).optional(),\n\n // === Appearance ===\n\n /** Position of the feedback tab (left or right side of screen) */\n position: z.enum(['left', 'right']).default('right'),\n\n /** Text displayed on the feedback tab */\n tabText: z.string().default('Feedback'),\n\n /** Background color of the feedback tab */\n tabColor: z.string().default('#da291c'),\n\n /** Text color of the feedback tab */\n tabTextColor: z.string().default('#ffffff'),\n\n /** Z-index for the feedback widget (default: 9999) */\n zIndex: z.number().default(9999),\n\n // === Form Fields ===\n\n /** Show star rating field */\n showRating: z.boolean().default(true),\n\n /** Whether rating is required to submit */\n ratingRequired: z.boolean().default(false),\n\n /** Number of stars in the rating (default: 5) */\n ratingStars: z.number().min(3).max(10).default(5),\n\n /** Show issue type dropdown */\n showIssueType: z.boolean().default(true),\n\n /** Whether issue type is required to submit */\n issueTypeRequired: z.boolean().default(false),\n\n /** Available issue types (uses defaults if not provided) */\n issueTypes: z.array(IssueTypeSchema).optional(),\n\n /** Show comment textarea */\n showComment: z.boolean().default(true),\n\n /** Whether comment is required to submit */\n commentRequired: z.boolean().default(false),\n\n /** Maximum length for comments (default: 500) */\n commentMaxLength: z.number().min(50).max(5000).default(500),\n\n /** Placeholder text for comment field (uses translation if not set) */\n commentPlaceholder: z.string().optional(),\n\n // === Language / i18n ===\n\n /** Locale for UI text (en or fr) */\n locale: z.enum(['en', 'fr']).default('en'),\n\n /** Custom translations (overrides built-in translations) */\n translations: z.record(z.string()).optional(),\n\n // === Metadata ===\n\n /** What metadata to include with feedback submissions */\n includeMetadata: MetadataOptionsSchema.default({}),\n\n // === Behavior ===\n\n /** Auto-close modal after successful submission (default: true) */\n autoClose: z.boolean().default(true),\n\n /** Delay in ms before auto-closing after success (default: 2000) */\n autoCloseDelay: z.number().min(500).max(10000).default(2000),\n\n /** Enable debug logging (default: false) */\n debug: z.boolean().default(false),\n});\n\nexport type FeedbackConfig = z.infer<typeof FeedbackConfigSchema>;\nexport type IssueType = z.infer<typeof IssueTypeSchema>;\nexport type IssueSubtype = z.infer<typeof IssueSubtypeSchema>;\nexport type MetadataOptions = z.infer<typeof MetadataOptionsSchema>;\n","{\n \"title\": \"Send Feedback\",\n \"ratingLabel\": \"How would you rate this content?\",\n \"issueTypeLabel\": \"What type of feedback?\",\n \"selectIssueType\": \"Select type...\",\n \"subtypeLabel\": \"More specifically:\",\n \"selectSubtype\": \"Select...\",\n \"commentLabel\": \"Your feedback\",\n \"commentPlaceholder\": \"Please describe your feedback in detail...\",\n \"submit\": \"Send Feedback\",\n \"submitting\": \"Sending...\",\n \"thankYou\": \"Thank you for your feedback!\",\n \"errorSubmitting\": \"Failed to send feedback. Please try again.\",\n \"errorRequired\": \"Please fill in the required fields.\",\n \"characterCount\": \"{current}/{max}\"\n}\n","{\n \"title\": \"Envoyer un commentaire\",\n \"ratingLabel\": \"Comment évaluez-vous ce contenu?\",\n \"issueTypeLabel\": \"Type de commentaire\",\n \"selectIssueType\": \"Sélectionner...\",\n \"subtypeLabel\": \"Plus précisément:\",\n \"selectSubtype\": \"Sélectionner...\",\n \"commentLabel\": \"Votre commentaire\",\n \"commentPlaceholder\": \"Veuillez décrire votre commentaire en détail...\",\n \"submit\": \"Envoyer\",\n \"submitting\": \"Envoi en cours...\",\n \"thankYou\": \"Merci pour votre commentaire!\",\n \"errorSubmitting\": \"Échec de l'envoi. Veuillez réessayer.\",\n \"errorRequired\": \"Veuillez remplir les champs obligatoires.\",\n \"characterCount\": \"{current}/{max}\"\n}\n","import en from './en.json';\nimport fr from './fr.json';\n\nexport interface Translations {\n title: string;\n ratingLabel: string;\n issueTypeLabel: string;\n selectIssueType: string;\n subtypeLabel: string;\n selectSubtype: string;\n commentLabel: string;\n commentPlaceholder: string;\n submit: string;\n submitting: string;\n thankYou: string;\n errorSubmitting: string;\n errorRequired: string;\n characterCount: string;\n}\n\nexport const translations: Record<string, Translations> = {\n en: en as Translations,\n fr: fr as Translations,\n};\n\nexport function getTranslations(locale: string, overrides?: Record<string, string>): Translations {\n const base = translations[locale] || translations.en;\n if (overrides) {\n return { ...base, ...overrides } as Translations;\n }\n return base;\n}\n\nexport { en, fr };\n","import type { FeedbackConfig, IssueType } from './config.js';\nimport { DEFAULT_ISSUE_TYPES } from './config.js';\nimport { getTranslations } from './i18n/index.js';\n\n/**\n * Generate the feedback widget JavaScript code.\n * This produces a self-contained IIFE that creates and manages the feedback widget.\n */\nexport function generateFeedbackWidget(config: FeedbackConfig): string {\n const t = getTranslations(config.locale, config.translations);\n const issueTypes = config.issueTypes || DEFAULT_ISSUE_TYPES;\n\n return `\n(function() {\n 'use strict';\n\n // ============================================================================\n // FEEDBACK WIDGET - Patch-Adams Plugin v1.0.0\n // ============================================================================\n\n var FEEDBACK = window.pa_patcher = window.pa_patcher || {};\n FEEDBACK.feedback = {\n version: '1.0.0',\n isOpen: false,\n rating: 0,\n config: ${JSON.stringify({\n endpoint: config.endpoint,\n method: config.method,\n headers: config.headers,\n position: config.position,\n showRating: config.showRating,\n ratingRequired: config.ratingRequired,\n ratingStars: config.ratingStars,\n showIssueType: config.showIssueType,\n issueTypeRequired: config.issueTypeRequired,\n showComment: config.showComment,\n commentRequired: config.commentRequired,\n commentMaxLength: config.commentMaxLength,\n autoClose: config.autoClose,\n autoCloseDelay: config.autoCloseDelay,\n debug: config.debug,\n includeMetadata: config.includeMetadata,\n })},\n translations: ${JSON.stringify(t)},\n issueTypes: ${JSON.stringify(issueTypes)},\n };\n\n var FB = FEEDBACK.feedback;\n\n function log() {\n if (FB.config.debug) {\n console.log.apply(console, ['[PA-Feedback]'].concat(Array.prototype.slice.call(arguments)));\n }\n }\n\n // ============================================================================\n // DOM CREATION\n // ============================================================================\n\n function createWidget() {\n var container = document.createElement('div');\n container.id = 'pa-feedback-container';\n container.className = 'pa-feedback-${config.position}';\n container.innerHTML = buildWidgetHtml();\n document.body.appendChild(container);\n setupEventListeners();\n log('Widget created');\n }\n\n function buildWidgetHtml() {\n var html = '';\n\n // Tab button\n html += '<button id=\"pa-feedback-tab\" class=\"pa-feedback-tab\" aria-label=\"' + FB.translations.title + '\">';\n html += '<span class=\"pa-feedback-tab-text\">${config.tabText}</span>';\n html += '</button>';\n\n // Modal\n html += '<div id=\"pa-feedback-modal\" class=\"pa-feedback-modal pa-feedback-hidden\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"pa-feedback-title\">';\n html += '<div class=\"pa-feedback-content\">';\n\n // Header\n html += '<div class=\"pa-feedback-header\">';\n html += '<h3 id=\"pa-feedback-title\">' + FB.translations.title + '</h3>';\n html += '<button id=\"pa-feedback-close\" class=\"pa-feedback-close\" aria-label=\"Close\">&times;</button>';\n html += '</div>';\n\n // Form\n html += '<form id=\"pa-feedback-form\">';\n\n // Rating (if enabled)\n ${config.showRating ? `\n html += '<div class=\"pa-feedback-field\">';\n html += '<label class=\"pa-feedback-label\">' + FB.translations.ratingLabel + '${config.ratingRequired ? ' <span class=\"pa-feedback-required\">*</span>' : ''}</label>';\n html += '<div class=\"pa-feedback-stars\" role=\"radiogroup\" aria-label=\"Rating\">';\n for (var i = 1; i <= ${config.ratingStars}; i++) {\n html += '<button type=\"button\" class=\"pa-feedback-star\" data-value=\"' + i + '\" role=\"radio\" aria-checked=\"false\" aria-label=\"' + i + ' star\">&#9734;</button>';\n }\n html += '</div>';\n html += '</div>';\n ` : ''}\n\n // Issue Type (if enabled)\n ${config.showIssueType ? `\n html += '<div class=\"pa-feedback-field\">';\n html += '<label class=\"pa-feedback-label\" for=\"pa-feedback-issue-type\">' + FB.translations.issueTypeLabel + '${config.issueTypeRequired ? ' <span class=\"pa-feedback-required\">*</span>' : ''}</label>';\n html += '<select id=\"pa-feedback-issue-type\" class=\"pa-feedback-select\">';\n html += '<option value=\"\">' + FB.translations.selectIssueType + '</option>';\n FB.issueTypes.forEach(function(type) {\n html += '<option value=\"' + type.id + '\">' + type.label + '</option>';\n });\n html += '</select>';\n html += '</div>';\n\n // Subtype container (hidden by default)\n html += '<div id=\"pa-feedback-subtype-field\" class=\"pa-feedback-field pa-feedback-hidden\">';\n html += '<label class=\"pa-feedback-label\" for=\"pa-feedback-subtype\">' + FB.translations.subtypeLabel + '</label>';\n html += '<select id=\"pa-feedback-subtype\" class=\"pa-feedback-select\">';\n html += '<option value=\"\">' + FB.translations.selectSubtype + '</option>';\n html += '</select>';\n html += '</div>';\n ` : ''}\n\n // Comment (if enabled)\n ${config.showComment ? `\n html += '<div class=\"pa-feedback-field\">';\n html += '<label class=\"pa-feedback-label\" for=\"pa-feedback-comment\">' + FB.translations.commentLabel + '${config.commentRequired ? ' <span class=\"pa-feedback-required\">*</span>' : ''}</label>';\n html += '<textarea id=\"pa-feedback-comment\" class=\"pa-feedback-textarea\" maxlength=\"${config.commentMaxLength}\" placeholder=\"${config.commentPlaceholder || ''}\" rows=\"4\"></textarea>';\n html += '<div class=\"pa-feedback-counter\"><span id=\"pa-feedback-char-count\">0</span>/${config.commentMaxLength}</div>';\n html += '</div>';\n ` : ''}\n\n // Error message area\n html += '<div id=\"pa-feedback-error\" class=\"pa-feedback-error pa-feedback-hidden\"></div>';\n\n // Submit button\n html += '<button type=\"submit\" id=\"pa-feedback-submit\" class=\"pa-feedback-submit\">';\n html += '<span class=\"pa-feedback-submit-text\">' + FB.translations.submit + '</span>';\n html += '<span class=\"pa-feedback-submit-loading pa-feedback-hidden\">' + FB.translations.submitting + '</span>';\n html += '</button>';\n\n html += '</form>';\n\n // Success message\n html += '<div id=\"pa-feedback-success\" class=\"pa-feedback-success pa-feedback-hidden\">';\n html += '<div class=\"pa-feedback-checkmark\">&#10003;</div>';\n html += '<p>' + FB.translations.thankYou + '</p>';\n html += '</div>';\n\n html += '</div>'; // content\n html += '</div>'; // modal\n\n return html;\n }\n\n // ============================================================================\n // EVENT HANDLERS\n // ============================================================================\n\n function setupEventListeners() {\n // Tab click\n document.getElementById('pa-feedback-tab').addEventListener('click', toggleModal);\n\n // Close button\n document.getElementById('pa-feedback-close').addEventListener('click', closeModal);\n\n // Form submit\n document.getElementById('pa-feedback-form').addEventListener('submit', handleSubmit);\n\n // Click outside to close\n document.getElementById('pa-feedback-modal').addEventListener('click', function(e) {\n if (e.target === this) closeModal();\n });\n\n // Escape key to close\n document.addEventListener('keydown', function(e) {\n if (e.key === 'Escape' && FB.isOpen) closeModal();\n });\n\n ${config.showRating ? `\n // Star rating\n var stars = document.querySelectorAll('.pa-feedback-star');\n stars.forEach(function(star, index) {\n star.addEventListener('click', function() {\n FB.rating = index + 1;\n updateStars();\n });\n star.addEventListener('mouseenter', function() {\n highlightStars(index + 1);\n });\n });\n document.querySelector('.pa-feedback-stars').addEventListener('mouseleave', function() {\n highlightStars(FB.rating);\n });\n ` : ''}\n\n ${config.showIssueType ? `\n // Issue type change\n document.getElementById('pa-feedback-issue-type').addEventListener('change', updateSubtypes);\n ` : ''}\n\n ${config.showComment ? `\n // Character counter\n document.getElementById('pa-feedback-comment').addEventListener('input', updateCharCount);\n ` : ''}\n }\n\n function toggleModal() {\n if (FB.isOpen) {\n closeModal();\n } else {\n openModal();\n }\n }\n\n function openModal() {\n FB.isOpen = true;\n document.getElementById('pa-feedback-modal').classList.remove('pa-feedback-hidden');\n document.getElementById('pa-feedback-tab').classList.add('pa-feedback-tab-active');\n log('Modal opened');\n }\n\n function closeModal() {\n FB.isOpen = false;\n document.getElementById('pa-feedback-modal').classList.add('pa-feedback-hidden');\n document.getElementById('pa-feedback-tab').classList.remove('pa-feedback-tab-active');\n log('Modal closed');\n }\n\n ${config.showRating ? `\n function updateStars() {\n var stars = document.querySelectorAll('.pa-feedback-star');\n stars.forEach(function(star, index) {\n var isFilled = index < FB.rating;\n star.innerHTML = isFilled ? '&#9733;' : '&#9734;';\n star.classList.toggle('pa-feedback-star-filled', isFilled);\n star.setAttribute('aria-checked', isFilled ? 'true' : 'false');\n });\n }\n\n function highlightStars(count) {\n var stars = document.querySelectorAll('.pa-feedback-star');\n stars.forEach(function(star, index) {\n var isFilled = index < count;\n star.innerHTML = isFilled ? '&#9733;' : '&#9734;';\n star.classList.toggle('pa-feedback-star-hover', isFilled && count > FB.rating);\n });\n }\n ` : ''}\n\n ${config.showIssueType ? `\n function updateSubtypes() {\n var typeSelect = document.getElementById('pa-feedback-issue-type');\n var subtypeField = document.getElementById('pa-feedback-subtype-field');\n var subtypeSelect = document.getElementById('pa-feedback-subtype');\n var selectedType = typeSelect.value;\n\n // Find the selected issue type\n var issueType = FB.issueTypes.find(function(t) { return t.id === selectedType; });\n\n if (issueType && issueType.subtypes && issueType.subtypes.length > 0) {\n // Populate subtypes\n subtypeSelect.innerHTML = '<option value=\"\">' + FB.translations.selectSubtype + '</option>';\n issueType.subtypes.forEach(function(subtype) {\n subtypeSelect.innerHTML += '<option value=\"' + subtype.id + '\">' + subtype.label + '</option>';\n });\n subtypeField.classList.remove('pa-feedback-hidden');\n } else {\n subtypeField.classList.add('pa-feedback-hidden');\n subtypeSelect.value = '';\n }\n }\n ` : ''}\n\n ${config.showComment ? `\n function updateCharCount() {\n var textarea = document.getElementById('pa-feedback-comment');\n var counter = document.getElementById('pa-feedback-char-count');\n counter.textContent = textarea.value.length;\n }\n ` : ''}\n\n // ============================================================================\n // FORM SUBMISSION\n // ============================================================================\n\n function validateForm() {\n var errors = [];\n\n ${config.showRating && config.ratingRequired ? `\n if (FB.rating === 0) {\n errors.push('Rating is required');\n }\n ` : ''}\n\n ${config.showIssueType && config.issueTypeRequired ? `\n if (!document.getElementById('pa-feedback-issue-type').value) {\n errors.push('Issue type is required');\n }\n ` : ''}\n\n ${config.showComment && config.commentRequired ? `\n if (!document.getElementById('pa-feedback-comment').value.trim()) {\n errors.push('Comment is required');\n }\n ` : ''}\n\n return errors;\n }\n\n function collectFormData() {\n var data = {};\n\n ${config.showRating ? `data.rating = FB.rating;` : ''}\n ${config.showIssueType ? `\n data.issueType = document.getElementById('pa-feedback-issue-type').value || null;\n data.issueSubtype = document.getElementById('pa-feedback-subtype').value || null;\n ` : ''}\n ${config.showComment ? `data.comment = document.getElementById('pa-feedback-comment').value.trim();` : ''}\n\n // Add metadata\n data.metadata = collectMetadata();\n\n return data;\n }\n\n function collectMetadata() {\n var meta = {};\n var cfg = FB.config.includeMetadata;\n\n if (cfg.courseId && window.pa_patcher && window.pa_patcher.lrs && window.pa_patcher.lrs.courseInfo) {\n meta.courseId = window.pa_patcher.lrs.courseInfo.id;\n meta.courseTitle = window.pa_patcher.lrs.courseInfo.title;\n }\n\n if (cfg.lessonId) {\n meta.lessonId = window.location.hash || window.location.pathname;\n }\n\n if (cfg.userAgent) {\n meta.userAgent = navigator.userAgent;\n }\n\n if (cfg.timestamp) {\n meta.timestamp = new Date().toISOString();\n }\n\n if (cfg.url) {\n meta.url = window.location.href;\n }\n\n return meta;\n }\n\n function showError(message) {\n var errorEl = document.getElementById('pa-feedback-error');\n errorEl.textContent = message;\n errorEl.classList.remove('pa-feedback-hidden');\n }\n\n function hideError() {\n document.getElementById('pa-feedback-error').classList.add('pa-feedback-hidden');\n }\n\n function setSubmitting(isSubmitting) {\n var submitBtn = document.getElementById('pa-feedback-submit');\n var textEl = submitBtn.querySelector('.pa-feedback-submit-text');\n var loadingEl = submitBtn.querySelector('.pa-feedback-submit-loading');\n\n submitBtn.disabled = isSubmitting;\n textEl.classList.toggle('pa-feedback-hidden', isSubmitting);\n loadingEl.classList.toggle('pa-feedback-hidden', !isSubmitting);\n }\n\n function showSuccess() {\n document.getElementById('pa-feedback-form').classList.add('pa-feedback-hidden');\n document.getElementById('pa-feedback-success').classList.remove('pa-feedback-hidden');\n\n if (FB.config.autoClose) {\n setTimeout(function() {\n closeModal();\n resetForm();\n }, FB.config.autoCloseDelay);\n }\n }\n\n function resetForm() {\n var form = document.getElementById('pa-feedback-form');\n form.reset();\n form.classList.remove('pa-feedback-hidden');\n document.getElementById('pa-feedback-success').classList.add('pa-feedback-hidden');\n hideError();\n\n FB.rating = 0;\n ${config.showRating ? 'updateStars();' : ''}\n ${config.showComment ? 'updateCharCount();' : ''}\n ${config.showIssueType ? `\n document.getElementById('pa-feedback-subtype-field').classList.add('pa-feedback-hidden');\n ` : ''}\n }\n\n async function handleSubmit(e) {\n e.preventDefault();\n hideError();\n\n // Validate\n var errors = validateForm();\n if (errors.length > 0) {\n showError(FB.translations.errorRequired);\n return;\n }\n\n // Collect data\n var data = collectFormData();\n log('Submitting:', data);\n\n // Submit\n setSubmitting(true);\n\n try {\n ${config.endpoint ? `\n var response = await fetch('${config.endpoint}', {\n method: '${config.method}',\n headers: Object.assign({\n 'Content-Type': 'application/json',\n }, FB.config.headers || {}),\n body: JSON.stringify(data),\n });\n\n if (!response.ok) {\n throw new Error('HTTP ' + response.status);\n }\n\n log('Submitted successfully');\n ` : `\n // No endpoint configured - just log\n console.log('[PA-Feedback] Feedback submitted:', data);\n log('No endpoint configured, feedback logged to console');\n `}\n\n showSuccess();\n } catch (err) {\n log('Submit error:', err);\n showError(FB.translations.errorSubmitting);\n } finally {\n setSubmitting(false);\n }\n }\n\n // ============================================================================\n // INITIALIZATION\n // ============================================================================\n\n function init() {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', createWidget);\n } else {\n createWidget();\n }\n log('Initialized');\n }\n\n init();\n\n})();\n`;\n}\n","import type { FeedbackConfig } from './config.js';\n\n/**\n * Generate the CSS styles for the feedback widget.\n * Styles are scoped with #pa-feedback-container to avoid conflicts.\n */\nexport function generateFeedbackStyles(config: FeedbackConfig): string {\n const position = config.position;\n const tabColor = config.tabColor;\n const tabTextColor = config.tabTextColor;\n const zIndex = config.zIndex;\n\n // Pre-compute position-dependent values\n const isRight = position === 'right';\n const tabTransform = isRight ? '-90deg' : '90deg';\n const tabTransformOrigin = isRight ? 'right center' : 'left center';\n const tabOffset = isRight ? 'right: 20px; left: auto;' : 'left: 20px; right: auto;';\n const tabBorderRadius = isRight ? '8px 8px 0 0' : '0 0 8px 8px';\n const modalPosition = isRight ? 'right: 0; left: auto;' : 'left: 0; right: auto;';\n const modalJustify = isRight ? 'flex-end' : 'flex-start';\n const contentShadow = isRight ? '-4px 0 20px rgba(0, 0, 0, 0.15)' : '4px 0 20px rgba(0, 0, 0, 0.15)';\n const slideFrom = isRight ? '100%' : '-100%';\n\n return `\n/* ============================================================================\n FEEDBACK WIDGET STYLES - Patch-Adams Plugin v1.0.0\n ============================================================================ */\n\n#pa-feedback-container {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;\n font-size: 14px;\n line-height: 1.5;\n color: #333;\n box-sizing: border-box;\n}\n\n#pa-feedback-container *,\n#pa-feedback-container *::before,\n#pa-feedback-container *::after {\n box-sizing: inherit;\n}\n\n/* Hidden utility class */\n#pa-feedback-container .pa-feedback-hidden {\n display: none !important;\n}\n\n/* ============================================================================\n TAB BUTTON\n ============================================================================ */\n\n#pa-feedback-tab {\n position: fixed;\n top: 50%;\n ${tabOffset}\n transform: translateY(-50%) rotate(${tabTransform});\n transform-origin: ${tabTransformOrigin};\n z-index: ${zIndex};\n background: ${tabColor};\n color: ${tabTextColor};\n border: none;\n padding: 10px 20px;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n border-radius: ${tabBorderRadius};\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n transition: all 0.2s ease;\n white-space: nowrap;\n}\n\n#pa-feedback-tab:hover {\n background: ${adjustColor(tabColor, -15)};\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);\n}\n\n#pa-feedback-tab:focus {\n outline: 2px solid ${tabColor};\n outline-offset: 2px;\n}\n\n#pa-feedback-tab.pa-feedback-tab-active {\n background: ${adjustColor(tabColor, -20)};\n}\n\n/* ============================================================================\n MODAL\n ============================================================================ */\n\n#pa-feedback-modal {\n position: fixed;\n top: 0;\n ${modalPosition}\n width: 360px;\n height: 100%;\n z-index: ${zIndex + 1};\n background: rgba(0, 0, 0, 0.3);\n display: flex;\n justify-content: ${modalJustify};\n animation: pa-feedback-fade-in 0.2s ease;\n}\n\n@keyframes pa-feedback-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n#pa-feedback-container .pa-feedback-content {\n width: 100%;\n max-width: 360px;\n height: 100%;\n background: #fff;\n box-shadow: ${contentShadow};\n display: flex;\n flex-direction: column;\n animation: pa-feedback-slide-in 0.2s ease;\n}\n\n@keyframes pa-feedback-slide-in {\n from {\n transform: translateX(${slideFrom});\n }\n to {\n transform: translateX(0);\n }\n}\n\n/* ============================================================================\n HEADER\n ============================================================================ */\n\n#pa-feedback-container .pa-feedback-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 16px 20px;\n border-bottom: 1px solid #e5e5e5;\n background: #fafafa;\n}\n\n#pa-feedback-container .pa-feedback-header h3 {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: #222;\n}\n\n#pa-feedback-container .pa-feedback-close {\n background: none;\n border: none;\n font-size: 24px;\n color: #666;\n cursor: pointer;\n padding: 4px 8px;\n line-height: 1;\n border-radius: 4px;\n transition: all 0.15s ease;\n}\n\n#pa-feedback-container .pa-feedback-close:hover {\n background: #e5e5e5;\n color: #333;\n}\n\n/* ============================================================================\n FORM\n ============================================================================ */\n\n#pa-feedback-form {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n display: flex;\n flex-direction: column;\n gap: 20px;\n}\n\n#pa-feedback-container .pa-feedback-field {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n#pa-feedback-container .pa-feedback-label {\n font-weight: 500;\n color: #444;\n font-size: 14px;\n}\n\n#pa-feedback-container .pa-feedback-required {\n color: #dc3545;\n}\n\n/* ============================================================================\n STAR RATING\n ============================================================================ */\n\n#pa-feedback-container .pa-feedback-stars {\n display: flex;\n gap: 4px;\n}\n\n#pa-feedback-container .pa-feedback-star {\n background: none;\n border: none;\n font-size: 28px;\n color: #ccc;\n cursor: pointer;\n padding: 4px;\n line-height: 1;\n transition: all 0.15s ease;\n}\n\n#pa-feedback-container .pa-feedback-star:hover {\n transform: scale(1.1);\n}\n\n#pa-feedback-container .pa-feedback-star-filled,\n#pa-feedback-container .pa-feedback-star-hover {\n color: #ffc107;\n}\n\n/* ============================================================================\n SELECT & TEXTAREA\n ============================================================================ */\n\n#pa-feedback-container .pa-feedback-select {\n width: 100%;\n padding: 10px 12px;\n font-size: 14px;\n border: 1px solid #ddd;\n border-radius: 6px;\n background: #fff;\n color: #333;\n cursor: pointer;\n transition: border-color 0.15s ease;\n}\n\n#pa-feedback-container .pa-feedback-select:focus {\n outline: none;\n border-color: ${tabColor};\n box-shadow: 0 0 0 3px ${hexToRgba(tabColor, 0.1)};\n}\n\n#pa-feedback-container .pa-feedback-textarea {\n width: 100%;\n padding: 12px;\n font-size: 14px;\n font-family: inherit;\n border: 1px solid #ddd;\n border-radius: 6px;\n resize: vertical;\n min-height: 100px;\n transition: border-color 0.15s ease;\n}\n\n#pa-feedback-container .pa-feedback-textarea:focus {\n outline: none;\n border-color: ${tabColor};\n box-shadow: 0 0 0 3px ${hexToRgba(tabColor, 0.1)};\n}\n\n#pa-feedback-container .pa-feedback-textarea::placeholder {\n color: #999;\n}\n\n#pa-feedback-container .pa-feedback-counter {\n text-align: right;\n font-size: 12px;\n color: #888;\n}\n\n/* ============================================================================\n ERROR MESSAGE\n ============================================================================ */\n\n#pa-feedback-container .pa-feedback-error {\n background: #fee2e2;\n color: #dc2626;\n padding: 10px 14px;\n border-radius: 6px;\n font-size: 13px;\n}\n\n/* ============================================================================\n SUBMIT BUTTON\n ============================================================================ */\n\n#pa-feedback-container .pa-feedback-submit {\n width: 100%;\n padding: 12px 20px;\n font-size: 16px;\n font-weight: 600;\n color: #fff;\n background: ${tabColor};\n border: none;\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.15s ease;\n margin-top: auto;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n}\n\n#pa-feedback-container .pa-feedback-submit:hover:not(:disabled) {\n background: ${adjustColor(tabColor, -15)};\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);\n transform: translateY(-1px);\n}\n\n#pa-feedback-container .pa-feedback-submit:focus {\n outline: 2px solid ${tabColor};\n outline-offset: 2px;\n}\n\n#pa-feedback-container .pa-feedback-submit:disabled {\n opacity: 0.7;\n cursor: not-allowed;\n}\n\n/* ============================================================================\n SUCCESS MESSAGE\n ============================================================================ */\n\n#pa-feedback-container .pa-feedback-success {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px 20px;\n text-align: center;\n}\n\n#pa-feedback-container .pa-feedback-checkmark {\n width: 64px;\n height: 64px;\n background: #10b981;\n color: #fff;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 32px;\n margin-bottom: 20px;\n animation: pa-feedback-pop 0.3s ease;\n}\n\n@keyframes pa-feedback-pop {\n 0% { transform: scale(0); }\n 50% { transform: scale(1.2); }\n 100% { transform: scale(1); }\n}\n\n#pa-feedback-container .pa-feedback-success p {\n margin: 0;\n font-size: 18px;\n font-weight: 500;\n color: #333;\n}\n`;\n}\n\n/**\n * Adjust a hex color by a percentage (positive = lighter, negative = darker)\n */\nfunction adjustColor(hex: string, percent: number): string {\n // Remove # if present\n hex = hex.replace(/^#/, '');\n\n // Parse the color\n let r = parseInt(hex.substring(0, 2), 16);\n let g = parseInt(hex.substring(2, 4), 16);\n let b = parseInt(hex.substring(4, 6), 16);\n\n // Adjust\n r = Math.min(255, Math.max(0, r + (r * percent) / 100));\n g = Math.min(255, Math.max(0, g + (g * percent) / 100));\n b = Math.min(255, Math.max(0, b + (b * percent) / 100));\n\n // Convert back to hex\n return (\n '#' +\n Math.round(r).toString(16).padStart(2, '0') +\n Math.round(g).toString(16).padStart(2, '0') +\n Math.round(b).toString(16).padStart(2, '0')\n );\n}\n\n/**\n * Convert hex color to rgba\n */\nfunction hexToRgba(hex: string, alpha: number): string {\n hex = hex.replace(/^#/, '');\n const r = parseInt(hex.substring(0, 2), 16);\n const g = parseInt(hex.substring(2, 4), 16);\n const b = parseInt(hex.substring(4, 6), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n}\n","/**\n * @patch-adams/plugin-feedback\n *\n * Feedback widget plugin for Patch-Adams.\n * Injects a customizable feedback form into patched Rise courses.\n *\n * Features:\n * - Star rating\n * - Issue type/subtype selection\n * - Comment text area\n * - Multi-language support (en/fr)\n * - Customizable appearance\n * - Automatic metadata collection\n * - Configurable API endpoint\n */\n\nimport type { PatchAdamsPlugin } from '@patch-adams/core';\nimport { FeedbackConfigSchema, type FeedbackConfig } from './config.js';\nimport { generateFeedbackWidget } from './widget.js';\nimport { generateFeedbackStyles } from './styles.js';\n\n/**\n * Feedback plugin for Patch-Adams\n */\nexport const feedbackPlugin: PatchAdamsPlugin<FeedbackConfig> = {\n name: 'feedback',\n version: '1.0.0',\n description: 'Inject a feedback form widget into Rise courses for collecting user feedback',\n configSchema: FeedbackConfigSchema,\n\n generateCss(config: FeedbackConfig) {\n return {\n after: generateFeedbackStyles(config),\n };\n },\n\n generateJs(config: FeedbackConfig) {\n return {\n after: generateFeedbackWidget(config),\n };\n },\n};\n\n// Default export for convenience\nexport default feedbackPlugin;\n\n// Re-export types and utilities\nexport {\n FeedbackConfigSchema,\n type FeedbackConfig,\n type IssueType,\n type IssueSubtype,\n type MetadataOptions,\n DEFAULT_ISSUE_TYPES,\n} from './config.js';\n\nexport { generateFeedbackWidget } from './widget.js';\nexport { generateFeedbackStyles } from './styles.js';\nexport { getTranslations, translations, type Translations } from './i18n/index.js';\n"]}
1
+ {"version":3,"sources":["../src/config.ts","../src/i18n/en.json","../src/i18n/fr.json","../src/i18n/index.ts","../src/widget.ts","../src/styles.ts","../src/index.ts"],"names":["z"],"mappings":"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","file":"index.cjs","sourcesContent":["import { z } from 'zod';\n\n/**\n * Issue subtype definition\n */\nexport const IssueSubtypeSchema = z.object({\n /** Unique identifier for the subtype */\n id: z.string().min(1),\n /** Display label for the subtype */\n label: z.string().min(1),\n});\n\n/**\n * Issue type definition with optional subtypes\n */\nexport const IssueTypeSchema = z.object({\n /** Unique identifier for the issue type */\n id: z.string().min(1),\n /** Display label for the issue type */\n label: z.string().min(1),\n /** Optional subtypes that appear when this type is selected */\n subtypes: z.array(IssueSubtypeSchema).optional(),\n});\n\n/**\n * Metadata options - what to include with feedback submissions\n */\nexport const MetadataOptionsSchema = z.object({\n /** Include the course ID from LRS bridge (if available) */\n courseId: z.boolean().default(true),\n /** Include the current lesson/page ID (URL hash or pathname) */\n lessonId: z.boolean().default(true),\n /** Include the browser user agent */\n userAgent: z.boolean().default(true),\n /** Include timestamp of submission */\n timestamp: z.boolean().default(true),\n /** Include the full current URL */\n url: z.boolean().default(true),\n});\n\n/**\n * Default issue types if none are provided\n */\nexport const DEFAULT_ISSUE_TYPES: z.infer<typeof IssueTypeSchema>[] = [\n {\n id: 'content',\n label: 'Content Issue',\n subtypes: [\n { id: 'typo', label: 'Typo / Grammar' },\n { id: 'incorrect', label: 'Incorrect Information' },\n { id: 'unclear', label: 'Unclear / Confusing' },\n { id: 'outdated', label: 'Outdated Content' },\n ],\n },\n {\n id: 'technical',\n label: 'Technical Issue',\n subtypes: [\n { id: 'audio', label: 'Audio Problem' },\n { id: 'video', label: 'Video Problem' },\n { id: 'navigation', label: 'Navigation Issue' },\n { id: 'display', label: 'Display / Layout Issue' },\n ],\n },\n {\n id: 'suggestion',\n label: 'Suggestion',\n },\n {\n id: 'other',\n label: 'Other',\n },\n];\n\n/**\n * Full feedback plugin configuration schema\n */\nexport const FeedbackConfigSchema = z.object({\n /** Whether the feedback plugin is enabled */\n enabled: z.boolean().default(true),\n\n // === Endpoint Configuration ===\n\n /** API endpoint URL for submitting feedback (optional - if not set, logs to console) */\n endpoint: z.string().url().optional(),\n\n /** HTTP method for the endpoint (default: POST) */\n method: z.enum(['POST', 'PUT']).default('POST'),\n\n /** Additional headers to send with the request */\n headers: z.record(z.string()).optional(),\n\n // === Appearance ===\n\n /** Position of the feedback tab (left or right side of screen) */\n position: z.enum(['left', 'right']).default('right'),\n\n /** Text displayed on the feedback tab */\n tabText: z.string().default('Feedback'),\n\n /** Background color of the feedback tab */\n tabColor: z.string().default('#da291c'),\n\n /** Text color of the feedback tab */\n tabTextColor: z.string().default('#ffffff'),\n\n /** Z-index for the feedback widget (default: 9999) */\n zIndex: z.number().default(9999),\n\n // === Form Fields ===\n\n /** Show star rating field */\n showRating: z.boolean().default(true),\n\n /** Whether rating is required to submit */\n ratingRequired: z.boolean().default(false),\n\n /** Number of stars in the rating (default: 5) */\n ratingStars: z.number().min(3).max(10).default(5),\n\n /** Show issue type dropdown */\n showIssueType: z.boolean().default(true),\n\n /** Whether issue type is required to submit */\n issueTypeRequired: z.boolean().default(false),\n\n /** Available issue types (uses defaults if not provided) */\n issueTypes: z.array(IssueTypeSchema).optional(),\n\n /** Show comment textarea */\n showComment: z.boolean().default(true),\n\n /** Whether comment is required to submit */\n commentRequired: z.boolean().default(false),\n\n /** Maximum length for comments (default: 500) */\n commentMaxLength: z.number().min(50).max(5000).default(500),\n\n /** Placeholder text for comment field (uses translation if not set) */\n commentPlaceholder: z.string().optional(),\n\n // === Language / i18n ===\n\n /** Locale for UI text (en or fr) */\n locale: z.enum(['en', 'fr']).default('en'),\n\n /** Custom translations (overrides built-in translations) */\n translations: z.record(z.string()).optional(),\n\n // === Metadata ===\n\n /** What metadata to include with feedback submissions */\n includeMetadata: MetadataOptionsSchema.default({}),\n\n // === Behavior ===\n\n /** Auto-close modal after successful submission (default: true) */\n autoClose: z.boolean().default(true),\n\n /** Delay in ms before auto-closing after success (default: 2000) */\n autoCloseDelay: z.number().min(500).max(10000).default(2000),\n\n /** Enable debug logging (default: false) */\n debug: z.boolean().default(false),\n});\n\nexport type FeedbackConfig = z.infer<typeof FeedbackConfigSchema>;\nexport type IssueType = z.infer<typeof IssueTypeSchema>;\nexport type IssueSubtype = z.infer<typeof IssueSubtypeSchema>;\nexport type MetadataOptions = z.infer<typeof MetadataOptionsSchema>;\n","{\n \"title\": \"Give Feedback\",\n \"ratingLabel\": \"How would you rate this course?\",\n \"issueTypeLabel\": \"What type of issue are you experiencing?\",\n \"selectIssueType\": \"Select an issue type...\",\n \"subtypeLabel\": \"Please specify:\",\n \"selectSubtype\": \"Select...\",\n \"commentLabel\": \"Tell us more:\",\n \"commentLabelPositive\": \"What did you like?\",\n \"commentLabelNegative\": \"What should we improve?\",\n \"commentPlaceholder\": \"Share your feedback...\",\n \"commentPlaceholderPositive\": \"Tell us what worked well...\",\n \"commentPlaceholderNegative\": \"Describe the issue or what could be improved...\",\n \"submit\": \"Submit\",\n \"submitting\": \"Sending...\",\n \"thankYou\": \"Thank you for your feedback!\",\n \"errorSubmitting\": \"Failed to send feedback. Please try again.\",\n \"errorRequired\": \"Please fill in the required fields.\",\n \"characterCount\": \"{current}/{max}\"\n}\n","{\n \"title\": \"Donner votre avis\",\n \"ratingLabel\": \"Comment évaluez-vous ce cours?\",\n \"issueTypeLabel\": \"Quel type de problème rencontrez-vous?\",\n \"selectIssueType\": \"Sélectionnez un type de problème...\",\n \"subtypeLabel\": \"Veuillez préciser:\",\n \"selectSubtype\": \"Sélectionnez...\",\n \"commentLabel\": \"Dites-nous en plus:\",\n \"commentLabelPositive\": \"Qu'avez-vous aimé?\",\n \"commentLabelNegative\": \"Que devrions-nous améliorer?\",\n \"commentPlaceholder\": \"Partagez vos commentaires...\",\n \"commentPlaceholderPositive\": \"Dites-nous ce qui a bien fonctionné...\",\n \"commentPlaceholderNegative\": \"Décrivez le problème ou ce qui pourrait être amélioré...\",\n \"submit\": \"Soumettre\",\n \"submitting\": \"Envoi en cours...\",\n \"thankYou\": \"Merci pour vos commentaires!\",\n \"errorSubmitting\": \"Échec de l'envoi. Veuillez réessayer.\",\n \"errorRequired\": \"Veuillez remplir les champs obligatoires.\",\n \"characterCount\": \"{current}/{max}\"\n}\n","import en from './en.json';\nimport fr from './fr.json';\n\nexport interface Translations {\n title: string;\n ratingLabel: string;\n issueTypeLabel: string;\n selectIssueType: string;\n subtypeLabel: string;\n selectSubtype: string;\n commentLabel: string;\n commentLabelPositive: string;\n commentLabelNegative: string;\n commentPlaceholder: string;\n commentPlaceholderPositive: string;\n commentPlaceholderNegative: string;\n submit: string;\n submitting: string;\n thankYou: string;\n errorSubmitting: string;\n errorRequired: string;\n characterCount: string;\n}\n\nexport const translations: Record<string, Translations> = {\n en: en as Translations,\n fr: fr as Translations,\n};\n\nexport function getTranslations(locale: string, overrides?: Record<string, string>): Translations {\n const base = translations[locale] || translations.en;\n if (overrides) {\n return { ...base, ...overrides } as Translations;\n }\n return base;\n}\n\nexport { en, fr };\n","import type { FeedbackConfig, IssueType } from './config.js';\nimport { DEFAULT_ISSUE_TYPES } from './config.js';\nimport { getTranslations } from './i18n/index.js';\n\n/**\n * Generate the feedback widget JavaScript code.\n * This produces a self-contained IIFE that creates and manages the feedback widget.\n */\nexport function generateFeedbackWidget(config: FeedbackConfig): string {\n const t = getTranslations(config.locale, config.translations);\n const issueTypes = config.issueTypes || DEFAULT_ISSUE_TYPES;\n\n return `\n(function() {\n 'use strict';\n\n // ============================================================================\n // FEEDBACK WIDGET - Patch-Adams Plugin v1.0.0\n // ============================================================================\n\n var FEEDBACK = window.pa_patcher = window.pa_patcher || {};\n FEEDBACK.feedback = {\n version: '1.0.0',\n isOpen: false,\n rating: 0,\n config: ${JSON.stringify({\n endpoint: config.endpoint,\n method: config.method,\n headers: config.headers,\n position: config.position,\n showRating: config.showRating,\n ratingRequired: config.ratingRequired,\n ratingStars: config.ratingStars,\n showIssueType: config.showIssueType,\n issueTypeRequired: config.issueTypeRequired,\n showComment: config.showComment,\n commentRequired: config.commentRequired,\n commentMaxLength: config.commentMaxLength,\n autoClose: config.autoClose,\n autoCloseDelay: config.autoCloseDelay,\n debug: config.debug,\n includeMetadata: config.includeMetadata,\n })},\n translations: ${JSON.stringify(t)},\n issueTypes: ${JSON.stringify(issueTypes)},\n };\n\n var FB = FEEDBACK.feedback;\n\n function log() {\n if (FB.config.debug) {\n console.log.apply(console, ['[PA-Feedback]'].concat(Array.prototype.slice.call(arguments)));\n }\n }\n\n // ============================================================================\n // DOM CREATION\n // ============================================================================\n\n function createWidget() {\n var container = document.createElement('div');\n container.id = 'pa-feedback-container';\n container.className = 'pa-feedback-${config.position}';\n container.innerHTML = buildWidgetHtml();\n document.body.appendChild(container);\n setupEventListeners();\n log('Widget created');\n }\n\n function buildWidgetHtml() {\n var html = '';\n\n // Tab button\n html += '<button id=\"pa-feedback-tab\" class=\"pa-feedback-tab\" aria-label=\"' + FB.translations.title + '\">';\n html += '<span class=\"pa-feedback-tab-text\">${config.tabText}</span>';\n html += '</button>';\n\n // Modal\n html += '<div id=\"pa-feedback-modal\" class=\"pa-feedback-modal pa-feedback-hidden\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"pa-feedback-title\">';\n html += '<div class=\"pa-feedback-content\">';\n\n // Header\n html += '<div class=\"pa-feedback-header\">';\n html += '<h3 id=\"pa-feedback-title\">' + FB.translations.title + '</h3>';\n html += '<button id=\"pa-feedback-close\" class=\"pa-feedback-close\" aria-label=\"Close\">&times;</button>';\n html += '</div>';\n\n // Form\n html += '<form id=\"pa-feedback-form\">';\n\n // Rating (if enabled)\n ${config.showRating ? `\n html += '<div class=\"pa-feedback-field\">';\n html += '<label class=\"pa-feedback-label\">' + FB.translations.ratingLabel + '${config.ratingRequired ? ' <span class=\"pa-feedback-required\">*</span>' : ''}</label>';\n html += '<div class=\"pa-feedback-stars\" role=\"radiogroup\" aria-label=\"Rating\">';\n for (var i = 1; i <= ${config.ratingStars}; i++) {\n html += '<button type=\"button\" class=\"pa-feedback-star\" data-value=\"' + i + '\" role=\"radio\" aria-checked=\"false\" aria-label=\"' + i + ' star\">&#9734;</button>';\n }\n html += '</div>';\n html += '</div>';\n ` : ''}\n\n // Issue Type (if enabled) - hidden by default, shown when rating <= 3\n ${config.showIssueType ? `\n html += '<div id=\"pa-feedback-issue-type-field\" class=\"pa-feedback-field pa-feedback-hidden\">';\n html += '<label class=\"pa-feedback-label\" for=\"pa-feedback-issue-type\">' + FB.translations.issueTypeLabel + '${config.issueTypeRequired ? ' <span class=\"pa-feedback-required\">*</span>' : ''}</label>';\n html += '<select id=\"pa-feedback-issue-type\" class=\"pa-feedback-select\">';\n html += '<option value=\"\">' + FB.translations.selectIssueType + '</option>';\n FB.issueTypes.forEach(function(type) {\n html += '<option value=\"' + type.id + '\">' + type.label + '</option>';\n });\n html += '</select>';\n html += '</div>';\n\n // Subtype container (hidden by default)\n html += '<div id=\"pa-feedback-subtype-field\" class=\"pa-feedback-field pa-feedback-hidden\">';\n html += '<label class=\"pa-feedback-label\" for=\"pa-feedback-subtype\">' + FB.translations.subtypeLabel + '</label>';\n html += '<select id=\"pa-feedback-subtype\" class=\"pa-feedback-select\">';\n html += '<option value=\"\">' + FB.translations.selectSubtype + '</option>';\n html += '</select>';\n html += '</div>';\n ` : ''}\n\n // Comment (if enabled)\n ${config.showComment ? `\n html += '<div class=\"pa-feedback-field\">';\n html += '<label id=\"pa-feedback-comment-label\" class=\"pa-feedback-label\" for=\"pa-feedback-comment\">' + FB.translations.commentLabel + '${config.commentRequired ? ' <span class=\"pa-feedback-required\">*</span>' : ''}</label>';\n html += '<textarea id=\"pa-feedback-comment\" class=\"pa-feedback-textarea\" maxlength=\"${config.commentMaxLength}\" placeholder=\"' + FB.translations.commentPlaceholder + '\" rows=\"4\"></textarea>';\n html += '<div class=\"pa-feedback-counter\"><span id=\"pa-feedback-char-count\">0</span>/${config.commentMaxLength}</div>';\n html += '</div>';\n ` : ''}\n\n // Error message area\n html += '<div id=\"pa-feedback-error\" class=\"pa-feedback-error pa-feedback-hidden\"></div>';\n\n // Submit button\n html += '<button type=\"submit\" id=\"pa-feedback-submit\" class=\"pa-feedback-submit\">';\n html += '<span class=\"pa-feedback-submit-text\">' + FB.translations.submit + '</span>';\n html += '<span class=\"pa-feedback-submit-loading pa-feedback-hidden\">' + FB.translations.submitting + '</span>';\n html += '</button>';\n\n html += '</form>';\n\n // Success message\n html += '<div id=\"pa-feedback-success\" class=\"pa-feedback-success pa-feedback-hidden\">';\n html += '<div class=\"pa-feedback-checkmark\">&#10003;</div>';\n html += '<p>' + FB.translations.thankYou + '</p>';\n html += '</div>';\n\n html += '</div>'; // content\n html += '</div>'; // modal\n\n return html;\n }\n\n // ============================================================================\n // EVENT HANDLERS\n // ============================================================================\n\n function setupEventListeners() {\n // Tab click\n document.getElementById('pa-feedback-tab').addEventListener('click', toggleModal);\n\n // Close button\n document.getElementById('pa-feedback-close').addEventListener('click', closeModal);\n\n // Form submit\n document.getElementById('pa-feedback-form').addEventListener('submit', handleSubmit);\n\n // Click outside to close\n document.getElementById('pa-feedback-modal').addEventListener('click', function(e) {\n if (e.target === this) closeModal();\n });\n\n // Escape key to close\n document.addEventListener('keydown', function(e) {\n if (e.key === 'Escape' && FB.isOpen) closeModal();\n });\n\n ${config.showRating ? `\n // Star rating\n var stars = document.querySelectorAll('.pa-feedback-star');\n stars.forEach(function(star, index) {\n star.addEventListener('click', function() {\n FB.rating = index + 1;\n updateStars();\n handleRatingChange();\n });\n star.addEventListener('mouseenter', function() {\n highlightStars(index + 1);\n });\n });\n document.querySelector('.pa-feedback-stars').addEventListener('mouseleave', function() {\n highlightStars(FB.rating);\n });\n ` : ''}\n\n ${config.showIssueType ? `\n // Issue type change\n document.getElementById('pa-feedback-issue-type').addEventListener('change', updateSubtypes);\n ` : ''}\n\n ${config.showComment ? `\n // Character counter\n document.getElementById('pa-feedback-comment').addEventListener('input', updateCharCount);\n ` : ''}\n }\n\n function toggleModal() {\n if (FB.isOpen) {\n closeModal();\n } else {\n openModal();\n }\n }\n\n function openModal() {\n FB.isOpen = true;\n document.getElementById('pa-feedback-modal').classList.remove('pa-feedback-hidden');\n document.getElementById('pa-feedback-tab').classList.add('pa-feedback-tab-active');\n log('Modal opened');\n }\n\n function closeModal() {\n FB.isOpen = false;\n document.getElementById('pa-feedback-modal').classList.add('pa-feedback-hidden');\n document.getElementById('pa-feedback-tab').classList.remove('pa-feedback-tab-active');\n log('Modal closed');\n }\n\n ${config.showRating ? `\n function updateStars() {\n var stars = document.querySelectorAll('.pa-feedback-star');\n stars.forEach(function(star, index) {\n var isFilled = index < FB.rating;\n star.innerHTML = isFilled ? '&#9733;' : '&#9734;';\n star.classList.toggle('pa-feedback-star-filled', isFilled);\n star.setAttribute('aria-checked', isFilled ? 'true' : 'false');\n });\n }\n\n function highlightStars(count) {\n var stars = document.querySelectorAll('.pa-feedback-star');\n stars.forEach(function(star, index) {\n var isFilled = index < count;\n star.innerHTML = isFilled ? '&#9733;' : '&#9734;';\n star.classList.toggle('pa-feedback-star-hover', isFilled && count > FB.rating);\n });\n }\n\n function handleRatingChange() {\n ${config.showIssueType ? `\n var issueTypeField = document.getElementById('pa-feedback-issue-type-field');\n var subtypeField = document.getElementById('pa-feedback-subtype-field');\n var issueTypeSelect = document.getElementById('pa-feedback-issue-type');\n var subtypeSelect = document.getElementById('pa-feedback-subtype');\n ` : ''}\n\n ${config.showComment ? `\n var commentLabel = document.getElementById('pa-feedback-comment-label');\n var commentTextarea = document.getElementById('pa-feedback-comment');\n ` : ''}\n\n if (FB.rating <= 3) {\n // Negative/neutral feedback path - show issue type\n ${config.showIssueType ? `\n issueTypeField.classList.remove('pa-feedback-hidden');\n ` : ''}\n ${config.showComment ? `\n commentLabel.innerHTML = FB.translations.commentLabelNegative + '${config.commentRequired ? ' <span class=\"pa-feedback-required\">*</span>' : ''}';\n commentTextarea.placeholder = FB.translations.commentPlaceholderNegative;\n ` : ''}\n } else {\n // Positive feedback path - hide issue type\n ${config.showIssueType ? `\n issueTypeField.classList.add('pa-feedback-hidden');\n subtypeField.classList.add('pa-feedback-hidden');\n issueTypeSelect.value = '';\n subtypeSelect.value = '';\n ` : ''}\n ${config.showComment ? `\n commentLabel.innerHTML = FB.translations.commentLabelPositive + '${config.commentRequired ? ' <span class=\"pa-feedback-required\">*</span>' : ''}';\n commentTextarea.placeholder = FB.translations.commentPlaceholderPositive;\n ` : ''}\n }\n }\n ` : ''}\n\n ${config.showIssueType ? `\n function updateSubtypes() {\n var typeSelect = document.getElementById('pa-feedback-issue-type');\n var subtypeField = document.getElementById('pa-feedback-subtype-field');\n var subtypeSelect = document.getElementById('pa-feedback-subtype');\n var selectedType = typeSelect.value;\n\n // Find the selected issue type\n var issueType = FB.issueTypes.find(function(t) { return t.id === selectedType; });\n\n if (issueType && issueType.subtypes && issueType.subtypes.length > 0) {\n // Populate subtypes\n subtypeSelect.innerHTML = '<option value=\"\">' + FB.translations.selectSubtype + '</option>';\n issueType.subtypes.forEach(function(subtype) {\n subtypeSelect.innerHTML += '<option value=\"' + subtype.id + '\">' + subtype.label + '</option>';\n });\n subtypeField.classList.remove('pa-feedback-hidden');\n } else {\n subtypeField.classList.add('pa-feedback-hidden');\n subtypeSelect.value = '';\n }\n }\n ` : ''}\n\n ${config.showComment ? `\n function updateCharCount() {\n var textarea = document.getElementById('pa-feedback-comment');\n var counter = document.getElementById('pa-feedback-char-count');\n counter.textContent = textarea.value.length;\n }\n ` : ''}\n\n // ============================================================================\n // FORM SUBMISSION\n // ============================================================================\n\n function validateForm() {\n var errors = [];\n\n ${config.showRating && config.ratingRequired ? `\n if (FB.rating === 0) {\n errors.push('Rating is required');\n }\n ` : ''}\n\n ${config.showIssueType && config.issueTypeRequired ? `\n // Issue type is only required when rating <= 3\n if (FB.rating <= 3 && !document.getElementById('pa-feedback-issue-type').value) {\n errors.push('Issue type is required');\n }\n ` : ''}\n\n ${config.showComment && config.commentRequired ? `\n if (!document.getElementById('pa-feedback-comment').value.trim()) {\n errors.push('Comment is required');\n }\n ` : ''}\n\n return errors;\n }\n\n function collectFormData() {\n var data = {};\n\n ${config.showRating ? `data.rating = FB.rating;` : ''}\n ${config.showIssueType ? `\n data.issueType = document.getElementById('pa-feedback-issue-type').value || null;\n data.issueSubtype = document.getElementById('pa-feedback-subtype').value || null;\n ` : ''}\n ${config.showComment ? `data.comment = document.getElementById('pa-feedback-comment').value.trim();` : ''}\n\n // Add metadata\n data.metadata = collectMetadata();\n\n return data;\n }\n\n function collectMetadata() {\n var meta = {};\n var cfg = FB.config.includeMetadata;\n var htmlEl = document.documentElement;\n\n // Get courseId from HTML data attribute (the manifest course ID)\n // This is the unique identifier for this specific patched package\n var courseId = htmlEl.getAttribute('data-pa-course-id');\n if (courseId) {\n meta.courseId = courseId;\n }\n\n // Get course title from LRS bridge if available\n if (cfg.courseId && window.pa_patcher && window.pa_patcher.lrs && window.pa_patcher.lrs.courseInfo) {\n meta.courseTitle = window.pa_patcher.lrs.courseInfo.title;\n }\n\n if (cfg.lessonId) {\n meta.lessonId = window.location.hash || window.location.pathname;\n }\n\n if (cfg.userAgent) {\n meta.userAgent = navigator.userAgent;\n }\n\n if (cfg.timestamp) {\n meta.timestamp = new Date().toISOString();\n }\n\n if (cfg.url) {\n meta.url = window.location.href;\n }\n\n return meta;\n }\n\n function showError(message) {\n var errorEl = document.getElementById('pa-feedback-error');\n errorEl.textContent = message;\n errorEl.classList.remove('pa-feedback-hidden');\n }\n\n function hideError() {\n document.getElementById('pa-feedback-error').classList.add('pa-feedback-hidden');\n }\n\n function setSubmitting(isSubmitting) {\n var submitBtn = document.getElementById('pa-feedback-submit');\n var textEl = submitBtn.querySelector('.pa-feedback-submit-text');\n var loadingEl = submitBtn.querySelector('.pa-feedback-submit-loading');\n\n submitBtn.disabled = isSubmitting;\n textEl.classList.toggle('pa-feedback-hidden', isSubmitting);\n loadingEl.classList.toggle('pa-feedback-hidden', !isSubmitting);\n }\n\n function showSuccess() {\n document.getElementById('pa-feedback-form').classList.add('pa-feedback-hidden');\n document.getElementById('pa-feedback-success').classList.remove('pa-feedback-hidden');\n\n if (FB.config.autoClose) {\n setTimeout(function() {\n closeModal();\n resetForm();\n }, FB.config.autoCloseDelay);\n }\n }\n\n function resetForm() {\n var form = document.getElementById('pa-feedback-form');\n form.reset();\n form.classList.remove('pa-feedback-hidden');\n document.getElementById('pa-feedback-success').classList.add('pa-feedback-hidden');\n hideError();\n\n FB.rating = 0;\n ${config.showRating ? 'updateStars();' : ''}\n ${config.showComment ? `\n updateCharCount();\n // Reset comment label and placeholder to default\n var commentLabel = document.getElementById('pa-feedback-comment-label');\n var commentTextarea = document.getElementById('pa-feedback-comment');\n commentLabel.innerHTML = FB.translations.commentLabel + '${config.commentRequired ? ' <span class=\"pa-feedback-required\">*</span>' : ''}';\n commentTextarea.placeholder = FB.translations.commentPlaceholder;\n ` : ''}\n ${config.showIssueType ? `\n // Hide issue type fields on reset\n document.getElementById('pa-feedback-issue-type-field').classList.add('pa-feedback-hidden');\n document.getElementById('pa-feedback-subtype-field').classList.add('pa-feedback-hidden');\n ` : ''}\n }\n\n async function handleSubmit(e) {\n e.preventDefault();\n hideError();\n\n // Validate\n var errors = validateForm();\n if (errors.length > 0) {\n showError(FB.translations.errorRequired);\n return;\n }\n\n // Collect data\n var data = collectFormData();\n log('Submitting:', data);\n\n // Submit\n setSubmitting(true);\n\n try {\n ${config.endpoint ? `\n var response = await fetch('${config.endpoint}', {\n method: '${config.method}',\n headers: Object.assign({\n 'Content-Type': 'application/json',\n }, FB.config.headers || {}),\n body: JSON.stringify(data),\n });\n\n if (!response.ok) {\n throw new Error('HTTP ' + response.status);\n }\n\n log('Submitted successfully');\n ` : `\n // No endpoint configured - just log\n console.log('[PA-Feedback] Feedback submitted:', data);\n log('No endpoint configured, feedback logged to console');\n `}\n\n showSuccess();\n } catch (err) {\n log('Submit error:', err);\n showError(FB.translations.errorSubmitting);\n } finally {\n setSubmitting(false);\n }\n }\n\n // ============================================================================\n // INITIALIZATION\n // ============================================================================\n\n function init() {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', createWidget);\n } else {\n createWidget();\n }\n log('Initialized');\n }\n\n init();\n\n})();\n`;\n}\n","import type { FeedbackConfig } from './config.js';\n\n/**\n * Generate the CSS styles for the feedback widget.\n * Styles are scoped with #pa-feedback-container to avoid conflicts.\n */\nexport function generateFeedbackStyles(config: FeedbackConfig): string {\n const position = config.position;\n const tabColor = config.tabColor;\n const tabTextColor = config.tabTextColor;\n const zIndex = config.zIndex;\n\n // Pre-compute position-dependent values\n const isRight = position === 'right';\n const tabTransform = isRight ? '-90deg' : '90deg';\n const tabTransformOrigin = isRight ? 'right center' : 'left center';\n const tabOffset = isRight ? 'right: 20px; left: auto;' : 'left: 20px; right: auto;';\n const tabBorderRadius = isRight ? '8px 8px 0 0' : '0 0 8px 8px';\n const modalPosition = isRight ? 'right: 0; left: auto;' : 'left: 0; right: auto;';\n const modalJustify = isRight ? 'flex-end' : 'flex-start';\n const contentShadow = isRight ? '-4px 0 20px rgba(0, 0, 0, 0.15)' : '4px 0 20px rgba(0, 0, 0, 0.15)';\n const slideFrom = isRight ? '100%' : '-100%';\n\n return `\n/* ============================================================================\n FEEDBACK WIDGET STYLES - Patch-Adams Plugin v1.0.0\n ============================================================================ */\n\n#pa-feedback-container {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;\n font-size: 14px;\n line-height: 1.5;\n color: #333;\n box-sizing: border-box;\n}\n\n#pa-feedback-container *,\n#pa-feedback-container *::before,\n#pa-feedback-container *::after {\n box-sizing: inherit;\n}\n\n/* Hidden utility class */\n#pa-feedback-container .pa-feedback-hidden {\n display: none !important;\n}\n\n/* ============================================================================\n TAB BUTTON\n ============================================================================ */\n\n#pa-feedback-tab {\n position: fixed;\n top: 50%;\n ${tabOffset}\n transform: translateY(-50%) rotate(${tabTransform});\n transform-origin: ${tabTransformOrigin};\n z-index: ${zIndex};\n background: ${tabColor};\n color: ${tabTextColor};\n border: none;\n padding: 10px 20px;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n border-radius: ${tabBorderRadius};\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n transition: all 0.2s ease;\n white-space: nowrap;\n}\n\n#pa-feedback-tab:hover {\n background: ${adjustColor(tabColor, -15)};\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);\n}\n\n#pa-feedback-tab:focus {\n outline: 2px solid ${tabColor};\n outline-offset: 2px;\n}\n\n#pa-feedback-tab.pa-feedback-tab-active {\n background: ${adjustColor(tabColor, -20)};\n}\n\n/* ============================================================================\n MODAL\n ============================================================================ */\n\n#pa-feedback-modal {\n position: fixed;\n top: 0;\n ${modalPosition}\n width: 360px;\n height: 100%;\n z-index: ${zIndex + 1};\n background: rgba(0, 0, 0, 0.3);\n display: flex;\n justify-content: ${modalJustify};\n animation: pa-feedback-fade-in 0.2s ease;\n}\n\n@keyframes pa-feedback-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n#pa-feedback-container .pa-feedback-content {\n width: 100%;\n max-width: 360px;\n height: 100%;\n background: #fff;\n box-shadow: ${contentShadow};\n display: flex;\n flex-direction: column;\n animation: pa-feedback-slide-in 0.2s ease;\n}\n\n@keyframes pa-feedback-slide-in {\n from {\n transform: translateX(${slideFrom});\n }\n to {\n transform: translateX(0);\n }\n}\n\n/* ============================================================================\n HEADER\n ============================================================================ */\n\n#pa-feedback-container .pa-feedback-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 16px 20px;\n border-bottom: 1px solid #e5e5e5;\n background: #fafafa;\n}\n\n#pa-feedback-container .pa-feedback-header h3 {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: #222;\n}\n\n#pa-feedback-container .pa-feedback-close {\n background: none;\n border: none;\n font-size: 24px;\n color: #666;\n cursor: pointer;\n padding: 4px 8px;\n line-height: 1;\n border-radius: 4px;\n transition: all 0.15s ease;\n}\n\n#pa-feedback-container .pa-feedback-close:hover {\n background: #e5e5e5;\n color: #333;\n}\n\n/* ============================================================================\n FORM\n ============================================================================ */\n\n#pa-feedback-form {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n display: flex;\n flex-direction: column;\n gap: 20px;\n}\n\n#pa-feedback-container .pa-feedback-field {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n#pa-feedback-container .pa-feedback-label {\n font-weight: 500;\n color: #444;\n font-size: 14px;\n}\n\n#pa-feedback-container .pa-feedback-required {\n color: #dc3545;\n}\n\n/* ============================================================================\n STAR RATING\n ============================================================================ */\n\n#pa-feedback-container .pa-feedback-stars {\n display: flex;\n gap: 4px;\n}\n\n#pa-feedback-container .pa-feedback-star {\n background: none;\n border: none;\n font-size: 28px;\n color: #ccc;\n cursor: pointer;\n padding: 4px;\n line-height: 1;\n transition: all 0.15s ease;\n}\n\n#pa-feedback-container .pa-feedback-star:hover {\n transform: scale(1.1);\n}\n\n#pa-feedback-container .pa-feedback-star-filled,\n#pa-feedback-container .pa-feedback-star-hover {\n color: #ffc107;\n}\n\n/* ============================================================================\n SELECT & TEXTAREA\n ============================================================================ */\n\n#pa-feedback-container .pa-feedback-select {\n width: 100%;\n padding: 10px 12px;\n font-size: 14px;\n border: 1px solid #ddd;\n border-radius: 6px;\n background: #fff;\n color: #333;\n cursor: pointer;\n transition: border-color 0.15s ease;\n}\n\n#pa-feedback-container .pa-feedback-select:focus {\n outline: none;\n border-color: ${tabColor};\n box-shadow: 0 0 0 3px ${hexToRgba(tabColor, 0.1)};\n}\n\n#pa-feedback-container .pa-feedback-textarea {\n width: 100%;\n padding: 12px;\n font-size: 14px;\n font-family: inherit;\n border: 1px solid #ddd;\n border-radius: 6px;\n resize: vertical;\n min-height: 100px;\n transition: border-color 0.15s ease;\n}\n\n#pa-feedback-container .pa-feedback-textarea:focus {\n outline: none;\n border-color: ${tabColor};\n box-shadow: 0 0 0 3px ${hexToRgba(tabColor, 0.1)};\n}\n\n#pa-feedback-container .pa-feedback-textarea::placeholder {\n color: #999;\n}\n\n#pa-feedback-container .pa-feedback-counter {\n text-align: right;\n font-size: 12px;\n color: #888;\n}\n\n/* ============================================================================\n ERROR MESSAGE\n ============================================================================ */\n\n#pa-feedback-container .pa-feedback-error {\n background: #fee2e2;\n color: #dc2626;\n padding: 10px 14px;\n border-radius: 6px;\n font-size: 13px;\n}\n\n/* ============================================================================\n SUBMIT BUTTON\n ============================================================================ */\n\n#pa-feedback-container .pa-feedback-submit {\n width: 100%;\n padding: 12px 20px;\n font-size: 16px;\n font-weight: 600;\n color: #fff;\n background: ${tabColor};\n border: none;\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.15s ease;\n margin-top: auto;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n}\n\n#pa-feedback-container .pa-feedback-submit:hover:not(:disabled) {\n background: ${adjustColor(tabColor, -15)};\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);\n transform: translateY(-1px);\n}\n\n#pa-feedback-container .pa-feedback-submit:focus {\n outline: 2px solid ${tabColor};\n outline-offset: 2px;\n}\n\n#pa-feedback-container .pa-feedback-submit:disabled {\n opacity: 0.7;\n cursor: not-allowed;\n}\n\n/* ============================================================================\n SUCCESS MESSAGE\n ============================================================================ */\n\n#pa-feedback-container .pa-feedback-success {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px 20px;\n text-align: center;\n}\n\n#pa-feedback-container .pa-feedback-checkmark {\n width: 64px;\n height: 64px;\n background: #10b981;\n color: #fff;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 32px;\n margin-bottom: 20px;\n animation: pa-feedback-pop 0.3s ease;\n}\n\n@keyframes pa-feedback-pop {\n 0% { transform: scale(0); }\n 50% { transform: scale(1.2); }\n 100% { transform: scale(1); }\n}\n\n#pa-feedback-container .pa-feedback-success p {\n margin: 0;\n font-size: 18px;\n font-weight: 500;\n color: #333;\n}\n`;\n}\n\n/**\n * Adjust a hex color by a percentage (positive = lighter, negative = darker)\n */\nfunction adjustColor(hex: string, percent: number): string {\n // Remove # if present\n hex = hex.replace(/^#/, '');\n\n // Parse the color\n let r = parseInt(hex.substring(0, 2), 16);\n let g = parseInt(hex.substring(2, 4), 16);\n let b = parseInt(hex.substring(4, 6), 16);\n\n // Adjust\n r = Math.min(255, Math.max(0, r + (r * percent) / 100));\n g = Math.min(255, Math.max(0, g + (g * percent) / 100));\n b = Math.min(255, Math.max(0, b + (b * percent) / 100));\n\n // Convert back to hex\n return (\n '#' +\n Math.round(r).toString(16).padStart(2, '0') +\n Math.round(g).toString(16).padStart(2, '0') +\n Math.round(b).toString(16).padStart(2, '0')\n );\n}\n\n/**\n * Convert hex color to rgba\n */\nfunction hexToRgba(hex: string, alpha: number): string {\n hex = hex.replace(/^#/, '');\n const r = parseInt(hex.substring(0, 2), 16);\n const g = parseInt(hex.substring(2, 4), 16);\n const b = parseInt(hex.substring(4, 6), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n}\n","/**\n * @patch-adams/plugin-feedback\n *\n * Feedback widget plugin for Patch-Adams.\n * Injects a customizable feedback form into patched Rise courses.\n *\n * Features:\n * - Star rating\n * - Issue type/subtype selection\n * - Comment text area\n * - Multi-language support (en/fr)\n * - Customizable appearance\n * - Automatic metadata collection\n * - Configurable API endpoint\n */\n\nimport type { PatchAdamsPlugin } from '@patch-adams/core';\nimport { FeedbackConfigSchema, type FeedbackConfig } from './config.js';\nimport { generateFeedbackWidget } from './widget.js';\nimport { generateFeedbackStyles } from './styles.js';\n\n/**\n * Feedback plugin for Patch-Adams\n */\nexport const feedbackPlugin: PatchAdamsPlugin<FeedbackConfig> = {\n name: 'feedback',\n version: '1.0.0',\n description: 'Inject a feedback form widget into Rise courses for collecting user feedback',\n configSchema: FeedbackConfigSchema,\n\n generateCss(config: FeedbackConfig) {\n return {\n after: generateFeedbackStyles(config),\n };\n },\n\n generateJs(config: FeedbackConfig) {\n return {\n after: generateFeedbackWidget(config),\n };\n },\n};\n\n// Default export for convenience\nexport default feedbackPlugin;\n\n// Re-export types and utilities\nexport {\n FeedbackConfigSchema,\n type FeedbackConfig,\n type IssueType,\n type IssueSubtype,\n type MetadataOptions,\n DEFAULT_ISSUE_TYPES,\n} from './config.js';\n\nexport { generateFeedbackWidget } from './widget.js';\nexport { generateFeedbackStyles } from './styles.js';\nexport { getTranslations, translations, type Translations } from './i18n/index.js';\n"]}
package/dist/index.d.cts CHANGED
@@ -296,7 +296,11 @@ interface Translations {
296
296
  subtypeLabel: string;
297
297
  selectSubtype: string;
298
298
  commentLabel: string;
299
+ commentLabelPositive: string;
300
+ commentLabelNegative: string;
299
301
  commentPlaceholder: string;
302
+ commentPlaceholderPositive: string;
303
+ commentPlaceholderNegative: string;
300
304
  submit: string;
301
305
  submitting: string;
302
306
  thankYou: string;
package/dist/index.d.ts CHANGED
@@ -296,7 +296,11 @@ interface Translations {
296
296
  subtypeLabel: string;
297
297
  selectSubtype: string;
298
298
  commentLabel: string;
299
+ commentLabelPositive: string;
300
+ commentLabelNegative: string;
299
301
  commentPlaceholder: string;
302
+ commentPlaceholderPositive: string;
303
+ commentPlaceholderNegative: string;
300
304
  submit: string;
301
305
  submitting: string;
302
306
  thankYou: string;
package/dist/index.js CHANGED
@@ -118,15 +118,19 @@ var FeedbackConfigSchema = z.object({
118
118
 
119
119
  // src/i18n/en.json
120
120
  var en_default = {
121
- title: "Send Feedback",
122
- ratingLabel: "How would you rate this content?",
123
- issueTypeLabel: "What type of feedback?",
124
- selectIssueType: "Select type...",
125
- subtypeLabel: "More specifically:",
121
+ title: "Give Feedback",
122
+ ratingLabel: "How would you rate this course?",
123
+ issueTypeLabel: "What type of issue are you experiencing?",
124
+ selectIssueType: "Select an issue type...",
125
+ subtypeLabel: "Please specify:",
126
126
  selectSubtype: "Select...",
127
- commentLabel: "Your feedback",
128
- commentPlaceholder: "Please describe your feedback in detail...",
129
- submit: "Send Feedback",
127
+ commentLabel: "Tell us more:",
128
+ commentLabelPositive: "What did you like?",
129
+ commentLabelNegative: "What should we improve?",
130
+ commentPlaceholder: "Share your feedback...",
131
+ commentPlaceholderPositive: "Tell us what worked well...",
132
+ commentPlaceholderNegative: "Describe the issue or what could be improved...",
133
+ submit: "Submit",
130
134
  submitting: "Sending...",
131
135
  thankYou: "Thank you for your feedback!",
132
136
  errorSubmitting: "Failed to send feedback. Please try again.",
@@ -136,17 +140,21 @@ var en_default = {
136
140
 
137
141
  // src/i18n/fr.json
138
142
  var fr_default = {
139
- title: "Envoyer un commentaire",
140
- ratingLabel: "Comment \xE9valuez-vous ce contenu?",
141
- issueTypeLabel: "Type de commentaire",
142
- selectIssueType: "S\xE9lectionner...",
143
- subtypeLabel: "Plus pr\xE9cis\xE9ment:",
144
- selectSubtype: "S\xE9lectionner...",
145
- commentLabel: "Votre commentaire",
146
- commentPlaceholder: "Veuillez d\xE9crire votre commentaire en d\xE9tail...",
147
- submit: "Envoyer",
143
+ title: "Donner votre avis",
144
+ ratingLabel: "Comment \xE9valuez-vous ce cours?",
145
+ issueTypeLabel: "Quel type de probl\xE8me rencontrez-vous?",
146
+ selectIssueType: "S\xE9lectionnez un type de probl\xE8me...",
147
+ subtypeLabel: "Veuillez pr\xE9ciser:",
148
+ selectSubtype: "S\xE9lectionnez...",
149
+ commentLabel: "Dites-nous en plus:",
150
+ commentLabelPositive: "Qu'avez-vous aim\xE9?",
151
+ commentLabelNegative: "Que devrions-nous am\xE9liorer?",
152
+ commentPlaceholder: "Partagez vos commentaires...",
153
+ commentPlaceholderPositive: "Dites-nous ce qui a bien fonctionn\xE9...",
154
+ commentPlaceholderNegative: "D\xE9crivez le probl\xE8me ou ce qui pourrait \xEAtre am\xE9lior\xE9...",
155
+ submit: "Soumettre",
148
156
  submitting: "Envoi en cours...",
149
- thankYou: "Merci pour votre commentaire!",
157
+ thankYou: "Merci pour vos commentaires!",
150
158
  errorSubmitting: "\xC9chec de l'envoi. Veuillez r\xE9essayer.",
151
159
  errorRequired: "Veuillez remplir les champs obligatoires.",
152
160
  characterCount: "{current}/{max}"
@@ -259,9 +267,9 @@ function generateFeedbackWidget(config) {
259
267
  html += '</div>';
260
268
  ` : ""}
261
269
 
262
- // Issue Type (if enabled)
270
+ // Issue Type (if enabled) - hidden by default, shown when rating <= 3
263
271
  ${config.showIssueType ? `
264
- html += '<div class="pa-feedback-field">';
272
+ html += '<div id="pa-feedback-issue-type-field" class="pa-feedback-field pa-feedback-hidden">';
265
273
  html += '<label class="pa-feedback-label" for="pa-feedback-issue-type">' + FB.translations.issueTypeLabel + '${config.issueTypeRequired ? ' <span class="pa-feedback-required">*</span>' : ""}</label>';
266
274
  html += '<select id="pa-feedback-issue-type" class="pa-feedback-select">';
267
275
  html += '<option value="">' + FB.translations.selectIssueType + '</option>';
@@ -283,8 +291,8 @@ function generateFeedbackWidget(config) {
283
291
  // Comment (if enabled)
284
292
  ${config.showComment ? `
285
293
  html += '<div class="pa-feedback-field">';
286
- html += '<label class="pa-feedback-label" for="pa-feedback-comment">' + FB.translations.commentLabel + '${config.commentRequired ? ' <span class="pa-feedback-required">*</span>' : ""}</label>';
287
- html += '<textarea id="pa-feedback-comment" class="pa-feedback-textarea" maxlength="${config.commentMaxLength}" placeholder="${config.commentPlaceholder || ""}" rows="4"></textarea>';
294
+ html += '<label id="pa-feedback-comment-label" class="pa-feedback-label" for="pa-feedback-comment">' + FB.translations.commentLabel + '${config.commentRequired ? ' <span class="pa-feedback-required">*</span>' : ""}</label>';
295
+ html += '<textarea id="pa-feedback-comment" class="pa-feedback-textarea" maxlength="${config.commentMaxLength}" placeholder="' + FB.translations.commentPlaceholder + '" rows="4"></textarea>';
288
296
  html += '<div class="pa-feedback-counter"><span id="pa-feedback-char-count">0</span>/${config.commentMaxLength}</div>';
289
297
  html += '</div>';
290
298
  ` : ""}
@@ -343,6 +351,7 @@ function generateFeedbackWidget(config) {
343
351
  star.addEventListener('click', function() {
344
352
  FB.rating = index + 1;
345
353
  updateStars();
354
+ handleRatingChange();
346
355
  });
347
356
  star.addEventListener('mouseenter', function() {
348
357
  highlightStars(index + 1);
@@ -405,6 +414,43 @@ function generateFeedbackWidget(config) {
405
414
  star.classList.toggle('pa-feedback-star-hover', isFilled && count > FB.rating);
406
415
  });
407
416
  }
417
+
418
+ function handleRatingChange() {
419
+ ${config.showIssueType ? `
420
+ var issueTypeField = document.getElementById('pa-feedback-issue-type-field');
421
+ var subtypeField = document.getElementById('pa-feedback-subtype-field');
422
+ var issueTypeSelect = document.getElementById('pa-feedback-issue-type');
423
+ var subtypeSelect = document.getElementById('pa-feedback-subtype');
424
+ ` : ""}
425
+
426
+ ${config.showComment ? `
427
+ var commentLabel = document.getElementById('pa-feedback-comment-label');
428
+ var commentTextarea = document.getElementById('pa-feedback-comment');
429
+ ` : ""}
430
+
431
+ if (FB.rating <= 3) {
432
+ // Negative/neutral feedback path - show issue type
433
+ ${config.showIssueType ? `
434
+ issueTypeField.classList.remove('pa-feedback-hidden');
435
+ ` : ""}
436
+ ${config.showComment ? `
437
+ commentLabel.innerHTML = FB.translations.commentLabelNegative + '${config.commentRequired ? ' <span class="pa-feedback-required">*</span>' : ""}';
438
+ commentTextarea.placeholder = FB.translations.commentPlaceholderNegative;
439
+ ` : ""}
440
+ } else {
441
+ // Positive feedback path - hide issue type
442
+ ${config.showIssueType ? `
443
+ issueTypeField.classList.add('pa-feedback-hidden');
444
+ subtypeField.classList.add('pa-feedback-hidden');
445
+ issueTypeSelect.value = '';
446
+ subtypeSelect.value = '';
447
+ ` : ""}
448
+ ${config.showComment ? `
449
+ commentLabel.innerHTML = FB.translations.commentLabelPositive + '${config.commentRequired ? ' <span class="pa-feedback-required">*</span>' : ""}';
450
+ commentTextarea.placeholder = FB.translations.commentPlaceholderPositive;
451
+ ` : ""}
452
+ }
453
+ }
408
454
  ` : ""}
409
455
 
410
456
  ${config.showIssueType ? `
@@ -453,7 +499,8 @@ function generateFeedbackWidget(config) {
453
499
  ` : ""}
454
500
 
455
501
  ${config.showIssueType && config.issueTypeRequired ? `
456
- if (!document.getElementById('pa-feedback-issue-type').value) {
502
+ // Issue type is only required when rating <= 3
503
+ if (FB.rating <= 3 && !document.getElementById('pa-feedback-issue-type').value) {
457
504
  errors.push('Issue type is required');
458
505
  }
459
506
  ` : ""}
@@ -486,9 +533,17 @@ function generateFeedbackWidget(config) {
486
533
  function collectMetadata() {
487
534
  var meta = {};
488
535
  var cfg = FB.config.includeMetadata;
536
+ var htmlEl = document.documentElement;
489
537
 
538
+ // Get courseId from HTML data attribute (the manifest course ID)
539
+ // This is the unique identifier for this specific patched package
540
+ var courseId = htmlEl.getAttribute('data-pa-course-id');
541
+ if (courseId) {
542
+ meta.courseId = courseId;
543
+ }
544
+
545
+ // Get course title from LRS bridge if available
490
546
  if (cfg.courseId && window.pa_patcher && window.pa_patcher.lrs && window.pa_patcher.lrs.courseInfo) {
491
- meta.courseId = window.pa_patcher.lrs.courseInfo.id;
492
547
  meta.courseTitle = window.pa_patcher.lrs.courseInfo.title;
493
548
  }
494
549
 
@@ -552,8 +607,17 @@ function generateFeedbackWidget(config) {
552
607
 
553
608
  FB.rating = 0;
554
609
  ${config.showRating ? "updateStars();" : ""}
555
- ${config.showComment ? "updateCharCount();" : ""}
610
+ ${config.showComment ? `
611
+ updateCharCount();
612
+ // Reset comment label and placeholder to default
613
+ var commentLabel = document.getElementById('pa-feedback-comment-label');
614
+ var commentTextarea = document.getElementById('pa-feedback-comment');
615
+ commentLabel.innerHTML = FB.translations.commentLabel + '${config.commentRequired ? ' <span class="pa-feedback-required">*</span>' : ""}';
616
+ commentTextarea.placeholder = FB.translations.commentPlaceholder;
617
+ ` : ""}
556
618
  ${config.showIssueType ? `
619
+ // Hide issue type fields on reset
620
+ document.getElementById('pa-feedback-issue-type-field').classList.add('pa-feedback-hidden');
557
621
  document.getElementById('pa-feedback-subtype-field').classList.add('pa-feedback-hidden');
558
622
  ` : ""}
559
623
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/config.ts","../src/i18n/en.json","../src/i18n/fr.json","../src/i18n/index.ts","../src/widget.ts","../src/styles.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { z } from 'zod';\n\n/**\n * Issue subtype definition\n */\nexport const IssueSubtypeSchema = z.object({\n /** Unique identifier for the subtype */\n id: z.string().min(1),\n /** Display label for the subtype */\n label: z.string().min(1),\n});\n\n/**\n * Issue type definition with optional subtypes\n */\nexport const IssueTypeSchema = z.object({\n /** Unique identifier for the issue type */\n id: z.string().min(1),\n /** Display label for the issue type */\n label: z.string().min(1),\n /** Optional subtypes that appear when this type is selected */\n subtypes: z.array(IssueSubtypeSchema).optional(),\n});\n\n/**\n * Metadata options - what to include with feedback submissions\n */\nexport const MetadataOptionsSchema = z.object({\n /** Include the course ID from LRS bridge (if available) */\n courseId: z.boolean().default(true),\n /** Include the current lesson/page ID (URL hash or pathname) */\n lessonId: z.boolean().default(true),\n /** Include the browser user agent */\n userAgent: z.boolean().default(true),\n /** Include timestamp of submission */\n timestamp: z.boolean().default(true),\n /** Include the full current URL */\n url: z.boolean().default(true),\n});\n\n/**\n * Default issue types if none are provided\n */\nexport const DEFAULT_ISSUE_TYPES: z.infer<typeof IssueTypeSchema>[] = [\n {\n id: 'content',\n label: 'Content Issue',\n subtypes: [\n { id: 'typo', label: 'Typo / Grammar' },\n { id: 'incorrect', label: 'Incorrect Information' },\n { id: 'unclear', label: 'Unclear / Confusing' },\n { id: 'outdated', label: 'Outdated Content' },\n ],\n },\n {\n id: 'technical',\n label: 'Technical Issue',\n subtypes: [\n { id: 'audio', label: 'Audio Problem' },\n { id: 'video', label: 'Video Problem' },\n { id: 'navigation', label: 'Navigation Issue' },\n { id: 'display', label: 'Display / Layout Issue' },\n ],\n },\n {\n id: 'suggestion',\n label: 'Suggestion',\n },\n {\n id: 'other',\n label: 'Other',\n },\n];\n\n/**\n * Full feedback plugin configuration schema\n */\nexport const FeedbackConfigSchema = z.object({\n /** Whether the feedback plugin is enabled */\n enabled: z.boolean().default(true),\n\n // === Endpoint Configuration ===\n\n /** API endpoint URL for submitting feedback (optional - if not set, logs to console) */\n endpoint: z.string().url().optional(),\n\n /** HTTP method for the endpoint (default: POST) */\n method: z.enum(['POST', 'PUT']).default('POST'),\n\n /** Additional headers to send with the request */\n headers: z.record(z.string()).optional(),\n\n // === Appearance ===\n\n /** Position of the feedback tab (left or right side of screen) */\n position: z.enum(['left', 'right']).default('right'),\n\n /** Text displayed on the feedback tab */\n tabText: z.string().default('Feedback'),\n\n /** Background color of the feedback tab */\n tabColor: z.string().default('#da291c'),\n\n /** Text color of the feedback tab */\n tabTextColor: z.string().default('#ffffff'),\n\n /** Z-index for the feedback widget (default: 9999) */\n zIndex: z.number().default(9999),\n\n // === Form Fields ===\n\n /** Show star rating field */\n showRating: z.boolean().default(true),\n\n /** Whether rating is required to submit */\n ratingRequired: z.boolean().default(false),\n\n /** Number of stars in the rating (default: 5) */\n ratingStars: z.number().min(3).max(10).default(5),\n\n /** Show issue type dropdown */\n showIssueType: z.boolean().default(true),\n\n /** Whether issue type is required to submit */\n issueTypeRequired: z.boolean().default(false),\n\n /** Available issue types (uses defaults if not provided) */\n issueTypes: z.array(IssueTypeSchema).optional(),\n\n /** Show comment textarea */\n showComment: z.boolean().default(true),\n\n /** Whether comment is required to submit */\n commentRequired: z.boolean().default(false),\n\n /** Maximum length for comments (default: 500) */\n commentMaxLength: z.number().min(50).max(5000).default(500),\n\n /** Placeholder text for comment field (uses translation if not set) */\n commentPlaceholder: z.string().optional(),\n\n // === Language / i18n ===\n\n /** Locale for UI text (en or fr) */\n locale: z.enum(['en', 'fr']).default('en'),\n\n /** Custom translations (overrides built-in translations) */\n translations: z.record(z.string()).optional(),\n\n // === Metadata ===\n\n /** What metadata to include with feedback submissions */\n includeMetadata: MetadataOptionsSchema.default({}),\n\n // === Behavior ===\n\n /** Auto-close modal after successful submission (default: true) */\n autoClose: z.boolean().default(true),\n\n /** Delay in ms before auto-closing after success (default: 2000) */\n autoCloseDelay: z.number().min(500).max(10000).default(2000),\n\n /** Enable debug logging (default: false) */\n debug: z.boolean().default(false),\n});\n\nexport type FeedbackConfig = z.infer<typeof FeedbackConfigSchema>;\nexport type IssueType = z.infer<typeof IssueTypeSchema>;\nexport type IssueSubtype = z.infer<typeof IssueSubtypeSchema>;\nexport type MetadataOptions = z.infer<typeof MetadataOptionsSchema>;\n","{\n \"title\": \"Send Feedback\",\n \"ratingLabel\": \"How would you rate this content?\",\n \"issueTypeLabel\": \"What type of feedback?\",\n \"selectIssueType\": \"Select type...\",\n \"subtypeLabel\": \"More specifically:\",\n \"selectSubtype\": \"Select...\",\n \"commentLabel\": \"Your feedback\",\n \"commentPlaceholder\": \"Please describe your feedback in detail...\",\n \"submit\": \"Send Feedback\",\n \"submitting\": \"Sending...\",\n \"thankYou\": \"Thank you for your feedback!\",\n \"errorSubmitting\": \"Failed to send feedback. Please try again.\",\n \"errorRequired\": \"Please fill in the required fields.\",\n \"characterCount\": \"{current}/{max}\"\n}\n","{\n \"title\": \"Envoyer un commentaire\",\n \"ratingLabel\": \"Comment évaluez-vous ce contenu?\",\n \"issueTypeLabel\": \"Type de commentaire\",\n \"selectIssueType\": \"Sélectionner...\",\n \"subtypeLabel\": \"Plus précisément:\",\n \"selectSubtype\": \"Sélectionner...\",\n \"commentLabel\": \"Votre commentaire\",\n \"commentPlaceholder\": \"Veuillez décrire votre commentaire en détail...\",\n \"submit\": \"Envoyer\",\n \"submitting\": \"Envoi en cours...\",\n \"thankYou\": \"Merci pour votre commentaire!\",\n \"errorSubmitting\": \"Échec de l'envoi. Veuillez réessayer.\",\n \"errorRequired\": \"Veuillez remplir les champs obligatoires.\",\n \"characterCount\": \"{current}/{max}\"\n}\n","import en from './en.json';\nimport fr from './fr.json';\n\nexport interface Translations {\n title: string;\n ratingLabel: string;\n issueTypeLabel: string;\n selectIssueType: string;\n subtypeLabel: string;\n selectSubtype: string;\n commentLabel: string;\n commentPlaceholder: string;\n submit: string;\n submitting: string;\n thankYou: string;\n errorSubmitting: string;\n errorRequired: string;\n characterCount: string;\n}\n\nexport const translations: Record<string, Translations> = {\n en: en as Translations,\n fr: fr as Translations,\n};\n\nexport function getTranslations(locale: string, overrides?: Record<string, string>): Translations {\n const base = translations[locale] || translations.en;\n if (overrides) {\n return { ...base, ...overrides } as Translations;\n }\n return base;\n}\n\nexport { en, fr };\n","import type { FeedbackConfig, IssueType } from './config.js';\nimport { DEFAULT_ISSUE_TYPES } from './config.js';\nimport { getTranslations } from './i18n/index.js';\n\n/**\n * Generate the feedback widget JavaScript code.\n * This produces a self-contained IIFE that creates and manages the feedback widget.\n */\nexport function generateFeedbackWidget(config: FeedbackConfig): string {\n const t = getTranslations(config.locale, config.translations);\n const issueTypes = config.issueTypes || DEFAULT_ISSUE_TYPES;\n\n return `\n(function() {\n 'use strict';\n\n // ============================================================================\n // FEEDBACK WIDGET - Patch-Adams Plugin v1.0.0\n // ============================================================================\n\n var FEEDBACK = window.pa_patcher = window.pa_patcher || {};\n FEEDBACK.feedback = {\n version: '1.0.0',\n isOpen: false,\n rating: 0,\n config: ${JSON.stringify({\n endpoint: config.endpoint,\n method: config.method,\n headers: config.headers,\n position: config.position,\n showRating: config.showRating,\n ratingRequired: config.ratingRequired,\n ratingStars: config.ratingStars,\n showIssueType: config.showIssueType,\n issueTypeRequired: config.issueTypeRequired,\n showComment: config.showComment,\n commentRequired: config.commentRequired,\n commentMaxLength: config.commentMaxLength,\n autoClose: config.autoClose,\n autoCloseDelay: config.autoCloseDelay,\n debug: config.debug,\n includeMetadata: config.includeMetadata,\n })},\n translations: ${JSON.stringify(t)},\n issueTypes: ${JSON.stringify(issueTypes)},\n };\n\n var FB = FEEDBACK.feedback;\n\n function log() {\n if (FB.config.debug) {\n console.log.apply(console, ['[PA-Feedback]'].concat(Array.prototype.slice.call(arguments)));\n }\n }\n\n // ============================================================================\n // DOM CREATION\n // ============================================================================\n\n function createWidget() {\n var container = document.createElement('div');\n container.id = 'pa-feedback-container';\n container.className = 'pa-feedback-${config.position}';\n container.innerHTML = buildWidgetHtml();\n document.body.appendChild(container);\n setupEventListeners();\n log('Widget created');\n }\n\n function buildWidgetHtml() {\n var html = '';\n\n // Tab button\n html += '<button id=\"pa-feedback-tab\" class=\"pa-feedback-tab\" aria-label=\"' + FB.translations.title + '\">';\n html += '<span class=\"pa-feedback-tab-text\">${config.tabText}</span>';\n html += '</button>';\n\n // Modal\n html += '<div id=\"pa-feedback-modal\" class=\"pa-feedback-modal pa-feedback-hidden\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"pa-feedback-title\">';\n html += '<div class=\"pa-feedback-content\">';\n\n // Header\n html += '<div class=\"pa-feedback-header\">';\n html += '<h3 id=\"pa-feedback-title\">' + FB.translations.title + '</h3>';\n html += '<button id=\"pa-feedback-close\" class=\"pa-feedback-close\" aria-label=\"Close\">&times;</button>';\n html += '</div>';\n\n // Form\n html += '<form id=\"pa-feedback-form\">';\n\n // Rating (if enabled)\n ${config.showRating ? `\n html += '<div class=\"pa-feedback-field\">';\n html += '<label class=\"pa-feedback-label\">' + FB.translations.ratingLabel + '${config.ratingRequired ? ' <span class=\"pa-feedback-required\">*</span>' : ''}</label>';\n html += '<div class=\"pa-feedback-stars\" role=\"radiogroup\" aria-label=\"Rating\">';\n for (var i = 1; i <= ${config.ratingStars}; i++) {\n html += '<button type=\"button\" class=\"pa-feedback-star\" data-value=\"' + i + '\" role=\"radio\" aria-checked=\"false\" aria-label=\"' + i + ' star\">&#9734;</button>';\n }\n html += '</div>';\n html += '</div>';\n ` : ''}\n\n // Issue Type (if enabled)\n ${config.showIssueType ? `\n html += '<div class=\"pa-feedback-field\">';\n html += '<label class=\"pa-feedback-label\" for=\"pa-feedback-issue-type\">' + FB.translations.issueTypeLabel + '${config.issueTypeRequired ? ' <span class=\"pa-feedback-required\">*</span>' : ''}</label>';\n html += '<select id=\"pa-feedback-issue-type\" class=\"pa-feedback-select\">';\n html += '<option value=\"\">' + FB.translations.selectIssueType + '</option>';\n FB.issueTypes.forEach(function(type) {\n html += '<option value=\"' + type.id + '\">' + type.label + '</option>';\n });\n html += '</select>';\n html += '</div>';\n\n // Subtype container (hidden by default)\n html += '<div id=\"pa-feedback-subtype-field\" class=\"pa-feedback-field pa-feedback-hidden\">';\n html += '<label class=\"pa-feedback-label\" for=\"pa-feedback-subtype\">' + FB.translations.subtypeLabel + '</label>';\n html += '<select id=\"pa-feedback-subtype\" class=\"pa-feedback-select\">';\n html += '<option value=\"\">' + FB.translations.selectSubtype + '</option>';\n html += '</select>';\n html += '</div>';\n ` : ''}\n\n // Comment (if enabled)\n ${config.showComment ? `\n html += '<div class=\"pa-feedback-field\">';\n html += '<label class=\"pa-feedback-label\" for=\"pa-feedback-comment\">' + FB.translations.commentLabel + '${config.commentRequired ? ' <span class=\"pa-feedback-required\">*</span>' : ''}</label>';\n html += '<textarea id=\"pa-feedback-comment\" class=\"pa-feedback-textarea\" maxlength=\"${config.commentMaxLength}\" placeholder=\"${config.commentPlaceholder || ''}\" rows=\"4\"></textarea>';\n html += '<div class=\"pa-feedback-counter\"><span id=\"pa-feedback-char-count\">0</span>/${config.commentMaxLength}</div>';\n html += '</div>';\n ` : ''}\n\n // Error message area\n html += '<div id=\"pa-feedback-error\" class=\"pa-feedback-error pa-feedback-hidden\"></div>';\n\n // Submit button\n html += '<button type=\"submit\" id=\"pa-feedback-submit\" class=\"pa-feedback-submit\">';\n html += '<span class=\"pa-feedback-submit-text\">' + FB.translations.submit + '</span>';\n html += '<span class=\"pa-feedback-submit-loading pa-feedback-hidden\">' + FB.translations.submitting + '</span>';\n html += '</button>';\n\n html += '</form>';\n\n // Success message\n html += '<div id=\"pa-feedback-success\" class=\"pa-feedback-success pa-feedback-hidden\">';\n html += '<div class=\"pa-feedback-checkmark\">&#10003;</div>';\n html += '<p>' + FB.translations.thankYou + '</p>';\n html += '</div>';\n\n html += '</div>'; // content\n html += '</div>'; // modal\n\n return html;\n }\n\n // ============================================================================\n // EVENT HANDLERS\n // ============================================================================\n\n function setupEventListeners() {\n // Tab click\n document.getElementById('pa-feedback-tab').addEventListener('click', toggleModal);\n\n // Close button\n document.getElementById('pa-feedback-close').addEventListener('click', closeModal);\n\n // Form submit\n document.getElementById('pa-feedback-form').addEventListener('submit', handleSubmit);\n\n // Click outside to close\n document.getElementById('pa-feedback-modal').addEventListener('click', function(e) {\n if (e.target === this) closeModal();\n });\n\n // Escape key to close\n document.addEventListener('keydown', function(e) {\n if (e.key === 'Escape' && FB.isOpen) closeModal();\n });\n\n ${config.showRating ? `\n // Star rating\n var stars = document.querySelectorAll('.pa-feedback-star');\n stars.forEach(function(star, index) {\n star.addEventListener('click', function() {\n FB.rating = index + 1;\n updateStars();\n });\n star.addEventListener('mouseenter', function() {\n highlightStars(index + 1);\n });\n });\n document.querySelector('.pa-feedback-stars').addEventListener('mouseleave', function() {\n highlightStars(FB.rating);\n });\n ` : ''}\n\n ${config.showIssueType ? `\n // Issue type change\n document.getElementById('pa-feedback-issue-type').addEventListener('change', updateSubtypes);\n ` : ''}\n\n ${config.showComment ? `\n // Character counter\n document.getElementById('pa-feedback-comment').addEventListener('input', updateCharCount);\n ` : ''}\n }\n\n function toggleModal() {\n if (FB.isOpen) {\n closeModal();\n } else {\n openModal();\n }\n }\n\n function openModal() {\n FB.isOpen = true;\n document.getElementById('pa-feedback-modal').classList.remove('pa-feedback-hidden');\n document.getElementById('pa-feedback-tab').classList.add('pa-feedback-tab-active');\n log('Modal opened');\n }\n\n function closeModal() {\n FB.isOpen = false;\n document.getElementById('pa-feedback-modal').classList.add('pa-feedback-hidden');\n document.getElementById('pa-feedback-tab').classList.remove('pa-feedback-tab-active');\n log('Modal closed');\n }\n\n ${config.showRating ? `\n function updateStars() {\n var stars = document.querySelectorAll('.pa-feedback-star');\n stars.forEach(function(star, index) {\n var isFilled = index < FB.rating;\n star.innerHTML = isFilled ? '&#9733;' : '&#9734;';\n star.classList.toggle('pa-feedback-star-filled', isFilled);\n star.setAttribute('aria-checked', isFilled ? 'true' : 'false');\n });\n }\n\n function highlightStars(count) {\n var stars = document.querySelectorAll('.pa-feedback-star');\n stars.forEach(function(star, index) {\n var isFilled = index < count;\n star.innerHTML = isFilled ? '&#9733;' : '&#9734;';\n star.classList.toggle('pa-feedback-star-hover', isFilled && count > FB.rating);\n });\n }\n ` : ''}\n\n ${config.showIssueType ? `\n function updateSubtypes() {\n var typeSelect = document.getElementById('pa-feedback-issue-type');\n var subtypeField = document.getElementById('pa-feedback-subtype-field');\n var subtypeSelect = document.getElementById('pa-feedback-subtype');\n var selectedType = typeSelect.value;\n\n // Find the selected issue type\n var issueType = FB.issueTypes.find(function(t) { return t.id === selectedType; });\n\n if (issueType && issueType.subtypes && issueType.subtypes.length > 0) {\n // Populate subtypes\n subtypeSelect.innerHTML = '<option value=\"\">' + FB.translations.selectSubtype + '</option>';\n issueType.subtypes.forEach(function(subtype) {\n subtypeSelect.innerHTML += '<option value=\"' + subtype.id + '\">' + subtype.label + '</option>';\n });\n subtypeField.classList.remove('pa-feedback-hidden');\n } else {\n subtypeField.classList.add('pa-feedback-hidden');\n subtypeSelect.value = '';\n }\n }\n ` : ''}\n\n ${config.showComment ? `\n function updateCharCount() {\n var textarea = document.getElementById('pa-feedback-comment');\n var counter = document.getElementById('pa-feedback-char-count');\n counter.textContent = textarea.value.length;\n }\n ` : ''}\n\n // ============================================================================\n // FORM SUBMISSION\n // ============================================================================\n\n function validateForm() {\n var errors = [];\n\n ${config.showRating && config.ratingRequired ? `\n if (FB.rating === 0) {\n errors.push('Rating is required');\n }\n ` : ''}\n\n ${config.showIssueType && config.issueTypeRequired ? `\n if (!document.getElementById('pa-feedback-issue-type').value) {\n errors.push('Issue type is required');\n }\n ` : ''}\n\n ${config.showComment && config.commentRequired ? `\n if (!document.getElementById('pa-feedback-comment').value.trim()) {\n errors.push('Comment is required');\n }\n ` : ''}\n\n return errors;\n }\n\n function collectFormData() {\n var data = {};\n\n ${config.showRating ? `data.rating = FB.rating;` : ''}\n ${config.showIssueType ? `\n data.issueType = document.getElementById('pa-feedback-issue-type').value || null;\n data.issueSubtype = document.getElementById('pa-feedback-subtype').value || null;\n ` : ''}\n ${config.showComment ? `data.comment = document.getElementById('pa-feedback-comment').value.trim();` : ''}\n\n // Add metadata\n data.metadata = collectMetadata();\n\n return data;\n }\n\n function collectMetadata() {\n var meta = {};\n var cfg = FB.config.includeMetadata;\n\n if (cfg.courseId && window.pa_patcher && window.pa_patcher.lrs && window.pa_patcher.lrs.courseInfo) {\n meta.courseId = window.pa_patcher.lrs.courseInfo.id;\n meta.courseTitle = window.pa_patcher.lrs.courseInfo.title;\n }\n\n if (cfg.lessonId) {\n meta.lessonId = window.location.hash || window.location.pathname;\n }\n\n if (cfg.userAgent) {\n meta.userAgent = navigator.userAgent;\n }\n\n if (cfg.timestamp) {\n meta.timestamp = new Date().toISOString();\n }\n\n if (cfg.url) {\n meta.url = window.location.href;\n }\n\n return meta;\n }\n\n function showError(message) {\n var errorEl = document.getElementById('pa-feedback-error');\n errorEl.textContent = message;\n errorEl.classList.remove('pa-feedback-hidden');\n }\n\n function hideError() {\n document.getElementById('pa-feedback-error').classList.add('pa-feedback-hidden');\n }\n\n function setSubmitting(isSubmitting) {\n var submitBtn = document.getElementById('pa-feedback-submit');\n var textEl = submitBtn.querySelector('.pa-feedback-submit-text');\n var loadingEl = submitBtn.querySelector('.pa-feedback-submit-loading');\n\n submitBtn.disabled = isSubmitting;\n textEl.classList.toggle('pa-feedback-hidden', isSubmitting);\n loadingEl.classList.toggle('pa-feedback-hidden', !isSubmitting);\n }\n\n function showSuccess() {\n document.getElementById('pa-feedback-form').classList.add('pa-feedback-hidden');\n document.getElementById('pa-feedback-success').classList.remove('pa-feedback-hidden');\n\n if (FB.config.autoClose) {\n setTimeout(function() {\n closeModal();\n resetForm();\n }, FB.config.autoCloseDelay);\n }\n }\n\n function resetForm() {\n var form = document.getElementById('pa-feedback-form');\n form.reset();\n form.classList.remove('pa-feedback-hidden');\n document.getElementById('pa-feedback-success').classList.add('pa-feedback-hidden');\n hideError();\n\n FB.rating = 0;\n ${config.showRating ? 'updateStars();' : ''}\n ${config.showComment ? 'updateCharCount();' : ''}\n ${config.showIssueType ? `\n document.getElementById('pa-feedback-subtype-field').classList.add('pa-feedback-hidden');\n ` : ''}\n }\n\n async function handleSubmit(e) {\n e.preventDefault();\n hideError();\n\n // Validate\n var errors = validateForm();\n if (errors.length > 0) {\n showError(FB.translations.errorRequired);\n return;\n }\n\n // Collect data\n var data = collectFormData();\n log('Submitting:', data);\n\n // Submit\n setSubmitting(true);\n\n try {\n ${config.endpoint ? `\n var response = await fetch('${config.endpoint}', {\n method: '${config.method}',\n headers: Object.assign({\n 'Content-Type': 'application/json',\n }, FB.config.headers || {}),\n body: JSON.stringify(data),\n });\n\n if (!response.ok) {\n throw new Error('HTTP ' + response.status);\n }\n\n log('Submitted successfully');\n ` : `\n // No endpoint configured - just log\n console.log('[PA-Feedback] Feedback submitted:', data);\n log('No endpoint configured, feedback logged to console');\n `}\n\n showSuccess();\n } catch (err) {\n log('Submit error:', err);\n showError(FB.translations.errorSubmitting);\n } finally {\n setSubmitting(false);\n }\n }\n\n // ============================================================================\n // INITIALIZATION\n // ============================================================================\n\n function init() {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', createWidget);\n } else {\n createWidget();\n }\n log('Initialized');\n }\n\n init();\n\n})();\n`;\n}\n","import type { FeedbackConfig } from './config.js';\n\n/**\n * Generate the CSS styles for the feedback widget.\n * Styles are scoped with #pa-feedback-container to avoid conflicts.\n */\nexport function generateFeedbackStyles(config: FeedbackConfig): string {\n const position = config.position;\n const tabColor = config.tabColor;\n const tabTextColor = config.tabTextColor;\n const zIndex = config.zIndex;\n\n // Pre-compute position-dependent values\n const isRight = position === 'right';\n const tabTransform = isRight ? '-90deg' : '90deg';\n const tabTransformOrigin = isRight ? 'right center' : 'left center';\n const tabOffset = isRight ? 'right: 20px; left: auto;' : 'left: 20px; right: auto;';\n const tabBorderRadius = isRight ? '8px 8px 0 0' : '0 0 8px 8px';\n const modalPosition = isRight ? 'right: 0; left: auto;' : 'left: 0; right: auto;';\n const modalJustify = isRight ? 'flex-end' : 'flex-start';\n const contentShadow = isRight ? '-4px 0 20px rgba(0, 0, 0, 0.15)' : '4px 0 20px rgba(0, 0, 0, 0.15)';\n const slideFrom = isRight ? '100%' : '-100%';\n\n return `\n/* ============================================================================\n FEEDBACK WIDGET STYLES - Patch-Adams Plugin v1.0.0\n ============================================================================ */\n\n#pa-feedback-container {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;\n font-size: 14px;\n line-height: 1.5;\n color: #333;\n box-sizing: border-box;\n}\n\n#pa-feedback-container *,\n#pa-feedback-container *::before,\n#pa-feedback-container *::after {\n box-sizing: inherit;\n}\n\n/* Hidden utility class */\n#pa-feedback-container .pa-feedback-hidden {\n display: none !important;\n}\n\n/* ============================================================================\n TAB BUTTON\n ============================================================================ */\n\n#pa-feedback-tab {\n position: fixed;\n top: 50%;\n ${tabOffset}\n transform: translateY(-50%) rotate(${tabTransform});\n transform-origin: ${tabTransformOrigin};\n z-index: ${zIndex};\n background: ${tabColor};\n color: ${tabTextColor};\n border: none;\n padding: 10px 20px;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n border-radius: ${tabBorderRadius};\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n transition: all 0.2s ease;\n white-space: nowrap;\n}\n\n#pa-feedback-tab:hover {\n background: ${adjustColor(tabColor, -15)};\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);\n}\n\n#pa-feedback-tab:focus {\n outline: 2px solid ${tabColor};\n outline-offset: 2px;\n}\n\n#pa-feedback-tab.pa-feedback-tab-active {\n background: ${adjustColor(tabColor, -20)};\n}\n\n/* ============================================================================\n MODAL\n ============================================================================ */\n\n#pa-feedback-modal {\n position: fixed;\n top: 0;\n ${modalPosition}\n width: 360px;\n height: 100%;\n z-index: ${zIndex + 1};\n background: rgba(0, 0, 0, 0.3);\n display: flex;\n justify-content: ${modalJustify};\n animation: pa-feedback-fade-in 0.2s ease;\n}\n\n@keyframes pa-feedback-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n#pa-feedback-container .pa-feedback-content {\n width: 100%;\n max-width: 360px;\n height: 100%;\n background: #fff;\n box-shadow: ${contentShadow};\n display: flex;\n flex-direction: column;\n animation: pa-feedback-slide-in 0.2s ease;\n}\n\n@keyframes pa-feedback-slide-in {\n from {\n transform: translateX(${slideFrom});\n }\n to {\n transform: translateX(0);\n }\n}\n\n/* ============================================================================\n HEADER\n ============================================================================ */\n\n#pa-feedback-container .pa-feedback-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 16px 20px;\n border-bottom: 1px solid #e5e5e5;\n background: #fafafa;\n}\n\n#pa-feedback-container .pa-feedback-header h3 {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: #222;\n}\n\n#pa-feedback-container .pa-feedback-close {\n background: none;\n border: none;\n font-size: 24px;\n color: #666;\n cursor: pointer;\n padding: 4px 8px;\n line-height: 1;\n border-radius: 4px;\n transition: all 0.15s ease;\n}\n\n#pa-feedback-container .pa-feedback-close:hover {\n background: #e5e5e5;\n color: #333;\n}\n\n/* ============================================================================\n FORM\n ============================================================================ */\n\n#pa-feedback-form {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n display: flex;\n flex-direction: column;\n gap: 20px;\n}\n\n#pa-feedback-container .pa-feedback-field {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n#pa-feedback-container .pa-feedback-label {\n font-weight: 500;\n color: #444;\n font-size: 14px;\n}\n\n#pa-feedback-container .pa-feedback-required {\n color: #dc3545;\n}\n\n/* ============================================================================\n STAR RATING\n ============================================================================ */\n\n#pa-feedback-container .pa-feedback-stars {\n display: flex;\n gap: 4px;\n}\n\n#pa-feedback-container .pa-feedback-star {\n background: none;\n border: none;\n font-size: 28px;\n color: #ccc;\n cursor: pointer;\n padding: 4px;\n line-height: 1;\n transition: all 0.15s ease;\n}\n\n#pa-feedback-container .pa-feedback-star:hover {\n transform: scale(1.1);\n}\n\n#pa-feedback-container .pa-feedback-star-filled,\n#pa-feedback-container .pa-feedback-star-hover {\n color: #ffc107;\n}\n\n/* ============================================================================\n SELECT & TEXTAREA\n ============================================================================ */\n\n#pa-feedback-container .pa-feedback-select {\n width: 100%;\n padding: 10px 12px;\n font-size: 14px;\n border: 1px solid #ddd;\n border-radius: 6px;\n background: #fff;\n color: #333;\n cursor: pointer;\n transition: border-color 0.15s ease;\n}\n\n#pa-feedback-container .pa-feedback-select:focus {\n outline: none;\n border-color: ${tabColor};\n box-shadow: 0 0 0 3px ${hexToRgba(tabColor, 0.1)};\n}\n\n#pa-feedback-container .pa-feedback-textarea {\n width: 100%;\n padding: 12px;\n font-size: 14px;\n font-family: inherit;\n border: 1px solid #ddd;\n border-radius: 6px;\n resize: vertical;\n min-height: 100px;\n transition: border-color 0.15s ease;\n}\n\n#pa-feedback-container .pa-feedback-textarea:focus {\n outline: none;\n border-color: ${tabColor};\n box-shadow: 0 0 0 3px ${hexToRgba(tabColor, 0.1)};\n}\n\n#pa-feedback-container .pa-feedback-textarea::placeholder {\n color: #999;\n}\n\n#pa-feedback-container .pa-feedback-counter {\n text-align: right;\n font-size: 12px;\n color: #888;\n}\n\n/* ============================================================================\n ERROR MESSAGE\n ============================================================================ */\n\n#pa-feedback-container .pa-feedback-error {\n background: #fee2e2;\n color: #dc2626;\n padding: 10px 14px;\n border-radius: 6px;\n font-size: 13px;\n}\n\n/* ============================================================================\n SUBMIT BUTTON\n ============================================================================ */\n\n#pa-feedback-container .pa-feedback-submit {\n width: 100%;\n padding: 12px 20px;\n font-size: 16px;\n font-weight: 600;\n color: #fff;\n background: ${tabColor};\n border: none;\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.15s ease;\n margin-top: auto;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n}\n\n#pa-feedback-container .pa-feedback-submit:hover:not(:disabled) {\n background: ${adjustColor(tabColor, -15)};\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);\n transform: translateY(-1px);\n}\n\n#pa-feedback-container .pa-feedback-submit:focus {\n outline: 2px solid ${tabColor};\n outline-offset: 2px;\n}\n\n#pa-feedback-container .pa-feedback-submit:disabled {\n opacity: 0.7;\n cursor: not-allowed;\n}\n\n/* ============================================================================\n SUCCESS MESSAGE\n ============================================================================ */\n\n#pa-feedback-container .pa-feedback-success {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px 20px;\n text-align: center;\n}\n\n#pa-feedback-container .pa-feedback-checkmark {\n width: 64px;\n height: 64px;\n background: #10b981;\n color: #fff;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 32px;\n margin-bottom: 20px;\n animation: pa-feedback-pop 0.3s ease;\n}\n\n@keyframes pa-feedback-pop {\n 0% { transform: scale(0); }\n 50% { transform: scale(1.2); }\n 100% { transform: scale(1); }\n}\n\n#pa-feedback-container .pa-feedback-success p {\n margin: 0;\n font-size: 18px;\n font-weight: 500;\n color: #333;\n}\n`;\n}\n\n/**\n * Adjust a hex color by a percentage (positive = lighter, negative = darker)\n */\nfunction adjustColor(hex: string, percent: number): string {\n // Remove # if present\n hex = hex.replace(/^#/, '');\n\n // Parse the color\n let r = parseInt(hex.substring(0, 2), 16);\n let g = parseInt(hex.substring(2, 4), 16);\n let b = parseInt(hex.substring(4, 6), 16);\n\n // Adjust\n r = Math.min(255, Math.max(0, r + (r * percent) / 100));\n g = Math.min(255, Math.max(0, g + (g * percent) / 100));\n b = Math.min(255, Math.max(0, b + (b * percent) / 100));\n\n // Convert back to hex\n return (\n '#' +\n Math.round(r).toString(16).padStart(2, '0') +\n Math.round(g).toString(16).padStart(2, '0') +\n Math.round(b).toString(16).padStart(2, '0')\n );\n}\n\n/**\n * Convert hex color to rgba\n */\nfunction hexToRgba(hex: string, alpha: number): string {\n hex = hex.replace(/^#/, '');\n const r = parseInt(hex.substring(0, 2), 16);\n const g = parseInt(hex.substring(2, 4), 16);\n const b = parseInt(hex.substring(4, 6), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n}\n","/**\n * @patch-adams/plugin-feedback\n *\n * Feedback widget plugin for Patch-Adams.\n * Injects a customizable feedback form into patched Rise courses.\n *\n * Features:\n * - Star rating\n * - Issue type/subtype selection\n * - Comment text area\n * - Multi-language support (en/fr)\n * - Customizable appearance\n * - Automatic metadata collection\n * - Configurable API endpoint\n */\n\nimport type { PatchAdamsPlugin } from '@patch-adams/core';\nimport { FeedbackConfigSchema, type FeedbackConfig } from './config.js';\nimport { generateFeedbackWidget } from './widget.js';\nimport { generateFeedbackStyles } from './styles.js';\n\n/**\n * Feedback plugin for Patch-Adams\n */\nexport const feedbackPlugin: PatchAdamsPlugin<FeedbackConfig> = {\n name: 'feedback',\n version: '1.0.0',\n description: 'Inject a feedback form widget into Rise courses for collecting user feedback',\n configSchema: FeedbackConfigSchema,\n\n generateCss(config: FeedbackConfig) {\n return {\n after: generateFeedbackStyles(config),\n };\n },\n\n generateJs(config: FeedbackConfig) {\n return {\n after: generateFeedbackWidget(config),\n };\n },\n};\n\n// Default export for convenience\nexport default feedbackPlugin;\n\n// Re-export types and utilities\nexport {\n FeedbackConfigSchema,\n type FeedbackConfig,\n type IssueType,\n type IssueSubtype,\n type MetadataOptions,\n DEFAULT_ISSUE_TYPES,\n} from './config.js';\n\nexport { generateFeedbackWidget } from './widget.js';\nexport { generateFeedbackStyles } from './styles.js';\nexport { getTranslations, translations, type Translations } from './i18n/index.js';\n"]}
1
+ {"version":3,"sources":["../src/config.ts","../src/i18n/en.json","../src/i18n/fr.json","../src/i18n/index.ts","../src/widget.ts","../src/styles.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { z } from 'zod';\n\n/**\n * Issue subtype definition\n */\nexport const IssueSubtypeSchema = z.object({\n /** Unique identifier for the subtype */\n id: z.string().min(1),\n /** Display label for the subtype */\n label: z.string().min(1),\n});\n\n/**\n * Issue type definition with optional subtypes\n */\nexport const IssueTypeSchema = z.object({\n /** Unique identifier for the issue type */\n id: z.string().min(1),\n /** Display label for the issue type */\n label: z.string().min(1),\n /** Optional subtypes that appear when this type is selected */\n subtypes: z.array(IssueSubtypeSchema).optional(),\n});\n\n/**\n * Metadata options - what to include with feedback submissions\n */\nexport const MetadataOptionsSchema = z.object({\n /** Include the course ID from LRS bridge (if available) */\n courseId: z.boolean().default(true),\n /** Include the current lesson/page ID (URL hash or pathname) */\n lessonId: z.boolean().default(true),\n /** Include the browser user agent */\n userAgent: z.boolean().default(true),\n /** Include timestamp of submission */\n timestamp: z.boolean().default(true),\n /** Include the full current URL */\n url: z.boolean().default(true),\n});\n\n/**\n * Default issue types if none are provided\n */\nexport const DEFAULT_ISSUE_TYPES: z.infer<typeof IssueTypeSchema>[] = [\n {\n id: 'content',\n label: 'Content Issue',\n subtypes: [\n { id: 'typo', label: 'Typo / Grammar' },\n { id: 'incorrect', label: 'Incorrect Information' },\n { id: 'unclear', label: 'Unclear / Confusing' },\n { id: 'outdated', label: 'Outdated Content' },\n ],\n },\n {\n id: 'technical',\n label: 'Technical Issue',\n subtypes: [\n { id: 'audio', label: 'Audio Problem' },\n { id: 'video', label: 'Video Problem' },\n { id: 'navigation', label: 'Navigation Issue' },\n { id: 'display', label: 'Display / Layout Issue' },\n ],\n },\n {\n id: 'suggestion',\n label: 'Suggestion',\n },\n {\n id: 'other',\n label: 'Other',\n },\n];\n\n/**\n * Full feedback plugin configuration schema\n */\nexport const FeedbackConfigSchema = z.object({\n /** Whether the feedback plugin is enabled */\n enabled: z.boolean().default(true),\n\n // === Endpoint Configuration ===\n\n /** API endpoint URL for submitting feedback (optional - if not set, logs to console) */\n endpoint: z.string().url().optional(),\n\n /** HTTP method for the endpoint (default: POST) */\n method: z.enum(['POST', 'PUT']).default('POST'),\n\n /** Additional headers to send with the request */\n headers: z.record(z.string()).optional(),\n\n // === Appearance ===\n\n /** Position of the feedback tab (left or right side of screen) */\n position: z.enum(['left', 'right']).default('right'),\n\n /** Text displayed on the feedback tab */\n tabText: z.string().default('Feedback'),\n\n /** Background color of the feedback tab */\n tabColor: z.string().default('#da291c'),\n\n /** Text color of the feedback tab */\n tabTextColor: z.string().default('#ffffff'),\n\n /** Z-index for the feedback widget (default: 9999) */\n zIndex: z.number().default(9999),\n\n // === Form Fields ===\n\n /** Show star rating field */\n showRating: z.boolean().default(true),\n\n /** Whether rating is required to submit */\n ratingRequired: z.boolean().default(false),\n\n /** Number of stars in the rating (default: 5) */\n ratingStars: z.number().min(3).max(10).default(5),\n\n /** Show issue type dropdown */\n showIssueType: z.boolean().default(true),\n\n /** Whether issue type is required to submit */\n issueTypeRequired: z.boolean().default(false),\n\n /** Available issue types (uses defaults if not provided) */\n issueTypes: z.array(IssueTypeSchema).optional(),\n\n /** Show comment textarea */\n showComment: z.boolean().default(true),\n\n /** Whether comment is required to submit */\n commentRequired: z.boolean().default(false),\n\n /** Maximum length for comments (default: 500) */\n commentMaxLength: z.number().min(50).max(5000).default(500),\n\n /** Placeholder text for comment field (uses translation if not set) */\n commentPlaceholder: z.string().optional(),\n\n // === Language / i18n ===\n\n /** Locale for UI text (en or fr) */\n locale: z.enum(['en', 'fr']).default('en'),\n\n /** Custom translations (overrides built-in translations) */\n translations: z.record(z.string()).optional(),\n\n // === Metadata ===\n\n /** What metadata to include with feedback submissions */\n includeMetadata: MetadataOptionsSchema.default({}),\n\n // === Behavior ===\n\n /** Auto-close modal after successful submission (default: true) */\n autoClose: z.boolean().default(true),\n\n /** Delay in ms before auto-closing after success (default: 2000) */\n autoCloseDelay: z.number().min(500).max(10000).default(2000),\n\n /** Enable debug logging (default: false) */\n debug: z.boolean().default(false),\n});\n\nexport type FeedbackConfig = z.infer<typeof FeedbackConfigSchema>;\nexport type IssueType = z.infer<typeof IssueTypeSchema>;\nexport type IssueSubtype = z.infer<typeof IssueSubtypeSchema>;\nexport type MetadataOptions = z.infer<typeof MetadataOptionsSchema>;\n","{\n \"title\": \"Give Feedback\",\n \"ratingLabel\": \"How would you rate this course?\",\n \"issueTypeLabel\": \"What type of issue are you experiencing?\",\n \"selectIssueType\": \"Select an issue type...\",\n \"subtypeLabel\": \"Please specify:\",\n \"selectSubtype\": \"Select...\",\n \"commentLabel\": \"Tell us more:\",\n \"commentLabelPositive\": \"What did you like?\",\n \"commentLabelNegative\": \"What should we improve?\",\n \"commentPlaceholder\": \"Share your feedback...\",\n \"commentPlaceholderPositive\": \"Tell us what worked well...\",\n \"commentPlaceholderNegative\": \"Describe the issue or what could be improved...\",\n \"submit\": \"Submit\",\n \"submitting\": \"Sending...\",\n \"thankYou\": \"Thank you for your feedback!\",\n \"errorSubmitting\": \"Failed to send feedback. Please try again.\",\n \"errorRequired\": \"Please fill in the required fields.\",\n \"characterCount\": \"{current}/{max}\"\n}\n","{\n \"title\": \"Donner votre avis\",\n \"ratingLabel\": \"Comment évaluez-vous ce cours?\",\n \"issueTypeLabel\": \"Quel type de problème rencontrez-vous?\",\n \"selectIssueType\": \"Sélectionnez un type de problème...\",\n \"subtypeLabel\": \"Veuillez préciser:\",\n \"selectSubtype\": \"Sélectionnez...\",\n \"commentLabel\": \"Dites-nous en plus:\",\n \"commentLabelPositive\": \"Qu'avez-vous aimé?\",\n \"commentLabelNegative\": \"Que devrions-nous améliorer?\",\n \"commentPlaceholder\": \"Partagez vos commentaires...\",\n \"commentPlaceholderPositive\": \"Dites-nous ce qui a bien fonctionné...\",\n \"commentPlaceholderNegative\": \"Décrivez le problème ou ce qui pourrait être amélioré...\",\n \"submit\": \"Soumettre\",\n \"submitting\": \"Envoi en cours...\",\n \"thankYou\": \"Merci pour vos commentaires!\",\n \"errorSubmitting\": \"Échec de l'envoi. Veuillez réessayer.\",\n \"errorRequired\": \"Veuillez remplir les champs obligatoires.\",\n \"characterCount\": \"{current}/{max}\"\n}\n","import en from './en.json';\nimport fr from './fr.json';\n\nexport interface Translations {\n title: string;\n ratingLabel: string;\n issueTypeLabel: string;\n selectIssueType: string;\n subtypeLabel: string;\n selectSubtype: string;\n commentLabel: string;\n commentLabelPositive: string;\n commentLabelNegative: string;\n commentPlaceholder: string;\n commentPlaceholderPositive: string;\n commentPlaceholderNegative: string;\n submit: string;\n submitting: string;\n thankYou: string;\n errorSubmitting: string;\n errorRequired: string;\n characterCount: string;\n}\n\nexport const translations: Record<string, Translations> = {\n en: en as Translations,\n fr: fr as Translations,\n};\n\nexport function getTranslations(locale: string, overrides?: Record<string, string>): Translations {\n const base = translations[locale] || translations.en;\n if (overrides) {\n return { ...base, ...overrides } as Translations;\n }\n return base;\n}\n\nexport { en, fr };\n","import type { FeedbackConfig, IssueType } from './config.js';\nimport { DEFAULT_ISSUE_TYPES } from './config.js';\nimport { getTranslations } from './i18n/index.js';\n\n/**\n * Generate the feedback widget JavaScript code.\n * This produces a self-contained IIFE that creates and manages the feedback widget.\n */\nexport function generateFeedbackWidget(config: FeedbackConfig): string {\n const t = getTranslations(config.locale, config.translations);\n const issueTypes = config.issueTypes || DEFAULT_ISSUE_TYPES;\n\n return `\n(function() {\n 'use strict';\n\n // ============================================================================\n // FEEDBACK WIDGET - Patch-Adams Plugin v1.0.0\n // ============================================================================\n\n var FEEDBACK = window.pa_patcher = window.pa_patcher || {};\n FEEDBACK.feedback = {\n version: '1.0.0',\n isOpen: false,\n rating: 0,\n config: ${JSON.stringify({\n endpoint: config.endpoint,\n method: config.method,\n headers: config.headers,\n position: config.position,\n showRating: config.showRating,\n ratingRequired: config.ratingRequired,\n ratingStars: config.ratingStars,\n showIssueType: config.showIssueType,\n issueTypeRequired: config.issueTypeRequired,\n showComment: config.showComment,\n commentRequired: config.commentRequired,\n commentMaxLength: config.commentMaxLength,\n autoClose: config.autoClose,\n autoCloseDelay: config.autoCloseDelay,\n debug: config.debug,\n includeMetadata: config.includeMetadata,\n })},\n translations: ${JSON.stringify(t)},\n issueTypes: ${JSON.stringify(issueTypes)},\n };\n\n var FB = FEEDBACK.feedback;\n\n function log() {\n if (FB.config.debug) {\n console.log.apply(console, ['[PA-Feedback]'].concat(Array.prototype.slice.call(arguments)));\n }\n }\n\n // ============================================================================\n // DOM CREATION\n // ============================================================================\n\n function createWidget() {\n var container = document.createElement('div');\n container.id = 'pa-feedback-container';\n container.className = 'pa-feedback-${config.position}';\n container.innerHTML = buildWidgetHtml();\n document.body.appendChild(container);\n setupEventListeners();\n log('Widget created');\n }\n\n function buildWidgetHtml() {\n var html = '';\n\n // Tab button\n html += '<button id=\"pa-feedback-tab\" class=\"pa-feedback-tab\" aria-label=\"' + FB.translations.title + '\">';\n html += '<span class=\"pa-feedback-tab-text\">${config.tabText}</span>';\n html += '</button>';\n\n // Modal\n html += '<div id=\"pa-feedback-modal\" class=\"pa-feedback-modal pa-feedback-hidden\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"pa-feedback-title\">';\n html += '<div class=\"pa-feedback-content\">';\n\n // Header\n html += '<div class=\"pa-feedback-header\">';\n html += '<h3 id=\"pa-feedback-title\">' + FB.translations.title + '</h3>';\n html += '<button id=\"pa-feedback-close\" class=\"pa-feedback-close\" aria-label=\"Close\">&times;</button>';\n html += '</div>';\n\n // Form\n html += '<form id=\"pa-feedback-form\">';\n\n // Rating (if enabled)\n ${config.showRating ? `\n html += '<div class=\"pa-feedback-field\">';\n html += '<label class=\"pa-feedback-label\">' + FB.translations.ratingLabel + '${config.ratingRequired ? ' <span class=\"pa-feedback-required\">*</span>' : ''}</label>';\n html += '<div class=\"pa-feedback-stars\" role=\"radiogroup\" aria-label=\"Rating\">';\n for (var i = 1; i <= ${config.ratingStars}; i++) {\n html += '<button type=\"button\" class=\"pa-feedback-star\" data-value=\"' + i + '\" role=\"radio\" aria-checked=\"false\" aria-label=\"' + i + ' star\">&#9734;</button>';\n }\n html += '</div>';\n html += '</div>';\n ` : ''}\n\n // Issue Type (if enabled) - hidden by default, shown when rating <= 3\n ${config.showIssueType ? `\n html += '<div id=\"pa-feedback-issue-type-field\" class=\"pa-feedback-field pa-feedback-hidden\">';\n html += '<label class=\"pa-feedback-label\" for=\"pa-feedback-issue-type\">' + FB.translations.issueTypeLabel + '${config.issueTypeRequired ? ' <span class=\"pa-feedback-required\">*</span>' : ''}</label>';\n html += '<select id=\"pa-feedback-issue-type\" class=\"pa-feedback-select\">';\n html += '<option value=\"\">' + FB.translations.selectIssueType + '</option>';\n FB.issueTypes.forEach(function(type) {\n html += '<option value=\"' + type.id + '\">' + type.label + '</option>';\n });\n html += '</select>';\n html += '</div>';\n\n // Subtype container (hidden by default)\n html += '<div id=\"pa-feedback-subtype-field\" class=\"pa-feedback-field pa-feedback-hidden\">';\n html += '<label class=\"pa-feedback-label\" for=\"pa-feedback-subtype\">' + FB.translations.subtypeLabel + '</label>';\n html += '<select id=\"pa-feedback-subtype\" class=\"pa-feedback-select\">';\n html += '<option value=\"\">' + FB.translations.selectSubtype + '</option>';\n html += '</select>';\n html += '</div>';\n ` : ''}\n\n // Comment (if enabled)\n ${config.showComment ? `\n html += '<div class=\"pa-feedback-field\">';\n html += '<label id=\"pa-feedback-comment-label\" class=\"pa-feedback-label\" for=\"pa-feedback-comment\">' + FB.translations.commentLabel + '${config.commentRequired ? ' <span class=\"pa-feedback-required\">*</span>' : ''}</label>';\n html += '<textarea id=\"pa-feedback-comment\" class=\"pa-feedback-textarea\" maxlength=\"${config.commentMaxLength}\" placeholder=\"' + FB.translations.commentPlaceholder + '\" rows=\"4\"></textarea>';\n html += '<div class=\"pa-feedback-counter\"><span id=\"pa-feedback-char-count\">0</span>/${config.commentMaxLength}</div>';\n html += '</div>';\n ` : ''}\n\n // Error message area\n html += '<div id=\"pa-feedback-error\" class=\"pa-feedback-error pa-feedback-hidden\"></div>';\n\n // Submit button\n html += '<button type=\"submit\" id=\"pa-feedback-submit\" class=\"pa-feedback-submit\">';\n html += '<span class=\"pa-feedback-submit-text\">' + FB.translations.submit + '</span>';\n html += '<span class=\"pa-feedback-submit-loading pa-feedback-hidden\">' + FB.translations.submitting + '</span>';\n html += '</button>';\n\n html += '</form>';\n\n // Success message\n html += '<div id=\"pa-feedback-success\" class=\"pa-feedback-success pa-feedback-hidden\">';\n html += '<div class=\"pa-feedback-checkmark\">&#10003;</div>';\n html += '<p>' + FB.translations.thankYou + '</p>';\n html += '</div>';\n\n html += '</div>'; // content\n html += '</div>'; // modal\n\n return html;\n }\n\n // ============================================================================\n // EVENT HANDLERS\n // ============================================================================\n\n function setupEventListeners() {\n // Tab click\n document.getElementById('pa-feedback-tab').addEventListener('click', toggleModal);\n\n // Close button\n document.getElementById('pa-feedback-close').addEventListener('click', closeModal);\n\n // Form submit\n document.getElementById('pa-feedback-form').addEventListener('submit', handleSubmit);\n\n // Click outside to close\n document.getElementById('pa-feedback-modal').addEventListener('click', function(e) {\n if (e.target === this) closeModal();\n });\n\n // Escape key to close\n document.addEventListener('keydown', function(e) {\n if (e.key === 'Escape' && FB.isOpen) closeModal();\n });\n\n ${config.showRating ? `\n // Star rating\n var stars = document.querySelectorAll('.pa-feedback-star');\n stars.forEach(function(star, index) {\n star.addEventListener('click', function() {\n FB.rating = index + 1;\n updateStars();\n handleRatingChange();\n });\n star.addEventListener('mouseenter', function() {\n highlightStars(index + 1);\n });\n });\n document.querySelector('.pa-feedback-stars').addEventListener('mouseleave', function() {\n highlightStars(FB.rating);\n });\n ` : ''}\n\n ${config.showIssueType ? `\n // Issue type change\n document.getElementById('pa-feedback-issue-type').addEventListener('change', updateSubtypes);\n ` : ''}\n\n ${config.showComment ? `\n // Character counter\n document.getElementById('pa-feedback-comment').addEventListener('input', updateCharCount);\n ` : ''}\n }\n\n function toggleModal() {\n if (FB.isOpen) {\n closeModal();\n } else {\n openModal();\n }\n }\n\n function openModal() {\n FB.isOpen = true;\n document.getElementById('pa-feedback-modal').classList.remove('pa-feedback-hidden');\n document.getElementById('pa-feedback-tab').classList.add('pa-feedback-tab-active');\n log('Modal opened');\n }\n\n function closeModal() {\n FB.isOpen = false;\n document.getElementById('pa-feedback-modal').classList.add('pa-feedback-hidden');\n document.getElementById('pa-feedback-tab').classList.remove('pa-feedback-tab-active');\n log('Modal closed');\n }\n\n ${config.showRating ? `\n function updateStars() {\n var stars = document.querySelectorAll('.pa-feedback-star');\n stars.forEach(function(star, index) {\n var isFilled = index < FB.rating;\n star.innerHTML = isFilled ? '&#9733;' : '&#9734;';\n star.classList.toggle('pa-feedback-star-filled', isFilled);\n star.setAttribute('aria-checked', isFilled ? 'true' : 'false');\n });\n }\n\n function highlightStars(count) {\n var stars = document.querySelectorAll('.pa-feedback-star');\n stars.forEach(function(star, index) {\n var isFilled = index < count;\n star.innerHTML = isFilled ? '&#9733;' : '&#9734;';\n star.classList.toggle('pa-feedback-star-hover', isFilled && count > FB.rating);\n });\n }\n\n function handleRatingChange() {\n ${config.showIssueType ? `\n var issueTypeField = document.getElementById('pa-feedback-issue-type-field');\n var subtypeField = document.getElementById('pa-feedback-subtype-field');\n var issueTypeSelect = document.getElementById('pa-feedback-issue-type');\n var subtypeSelect = document.getElementById('pa-feedback-subtype');\n ` : ''}\n\n ${config.showComment ? `\n var commentLabel = document.getElementById('pa-feedback-comment-label');\n var commentTextarea = document.getElementById('pa-feedback-comment');\n ` : ''}\n\n if (FB.rating <= 3) {\n // Negative/neutral feedback path - show issue type\n ${config.showIssueType ? `\n issueTypeField.classList.remove('pa-feedback-hidden');\n ` : ''}\n ${config.showComment ? `\n commentLabel.innerHTML = FB.translations.commentLabelNegative + '${config.commentRequired ? ' <span class=\"pa-feedback-required\">*</span>' : ''}';\n commentTextarea.placeholder = FB.translations.commentPlaceholderNegative;\n ` : ''}\n } else {\n // Positive feedback path - hide issue type\n ${config.showIssueType ? `\n issueTypeField.classList.add('pa-feedback-hidden');\n subtypeField.classList.add('pa-feedback-hidden');\n issueTypeSelect.value = '';\n subtypeSelect.value = '';\n ` : ''}\n ${config.showComment ? `\n commentLabel.innerHTML = FB.translations.commentLabelPositive + '${config.commentRequired ? ' <span class=\"pa-feedback-required\">*</span>' : ''}';\n commentTextarea.placeholder = FB.translations.commentPlaceholderPositive;\n ` : ''}\n }\n }\n ` : ''}\n\n ${config.showIssueType ? `\n function updateSubtypes() {\n var typeSelect = document.getElementById('pa-feedback-issue-type');\n var subtypeField = document.getElementById('pa-feedback-subtype-field');\n var subtypeSelect = document.getElementById('pa-feedback-subtype');\n var selectedType = typeSelect.value;\n\n // Find the selected issue type\n var issueType = FB.issueTypes.find(function(t) { return t.id === selectedType; });\n\n if (issueType && issueType.subtypes && issueType.subtypes.length > 0) {\n // Populate subtypes\n subtypeSelect.innerHTML = '<option value=\"\">' + FB.translations.selectSubtype + '</option>';\n issueType.subtypes.forEach(function(subtype) {\n subtypeSelect.innerHTML += '<option value=\"' + subtype.id + '\">' + subtype.label + '</option>';\n });\n subtypeField.classList.remove('pa-feedback-hidden');\n } else {\n subtypeField.classList.add('pa-feedback-hidden');\n subtypeSelect.value = '';\n }\n }\n ` : ''}\n\n ${config.showComment ? `\n function updateCharCount() {\n var textarea = document.getElementById('pa-feedback-comment');\n var counter = document.getElementById('pa-feedback-char-count');\n counter.textContent = textarea.value.length;\n }\n ` : ''}\n\n // ============================================================================\n // FORM SUBMISSION\n // ============================================================================\n\n function validateForm() {\n var errors = [];\n\n ${config.showRating && config.ratingRequired ? `\n if (FB.rating === 0) {\n errors.push('Rating is required');\n }\n ` : ''}\n\n ${config.showIssueType && config.issueTypeRequired ? `\n // Issue type is only required when rating <= 3\n if (FB.rating <= 3 && !document.getElementById('pa-feedback-issue-type').value) {\n errors.push('Issue type is required');\n }\n ` : ''}\n\n ${config.showComment && config.commentRequired ? `\n if (!document.getElementById('pa-feedback-comment').value.trim()) {\n errors.push('Comment is required');\n }\n ` : ''}\n\n return errors;\n }\n\n function collectFormData() {\n var data = {};\n\n ${config.showRating ? `data.rating = FB.rating;` : ''}\n ${config.showIssueType ? `\n data.issueType = document.getElementById('pa-feedback-issue-type').value || null;\n data.issueSubtype = document.getElementById('pa-feedback-subtype').value || null;\n ` : ''}\n ${config.showComment ? `data.comment = document.getElementById('pa-feedback-comment').value.trim();` : ''}\n\n // Add metadata\n data.metadata = collectMetadata();\n\n return data;\n }\n\n function collectMetadata() {\n var meta = {};\n var cfg = FB.config.includeMetadata;\n var htmlEl = document.documentElement;\n\n // Get courseId from HTML data attribute (the manifest course ID)\n // This is the unique identifier for this specific patched package\n var courseId = htmlEl.getAttribute('data-pa-course-id');\n if (courseId) {\n meta.courseId = courseId;\n }\n\n // Get course title from LRS bridge if available\n if (cfg.courseId && window.pa_patcher && window.pa_patcher.lrs && window.pa_patcher.lrs.courseInfo) {\n meta.courseTitle = window.pa_patcher.lrs.courseInfo.title;\n }\n\n if (cfg.lessonId) {\n meta.lessonId = window.location.hash || window.location.pathname;\n }\n\n if (cfg.userAgent) {\n meta.userAgent = navigator.userAgent;\n }\n\n if (cfg.timestamp) {\n meta.timestamp = new Date().toISOString();\n }\n\n if (cfg.url) {\n meta.url = window.location.href;\n }\n\n return meta;\n }\n\n function showError(message) {\n var errorEl = document.getElementById('pa-feedback-error');\n errorEl.textContent = message;\n errorEl.classList.remove('pa-feedback-hidden');\n }\n\n function hideError() {\n document.getElementById('pa-feedback-error').classList.add('pa-feedback-hidden');\n }\n\n function setSubmitting(isSubmitting) {\n var submitBtn = document.getElementById('pa-feedback-submit');\n var textEl = submitBtn.querySelector('.pa-feedback-submit-text');\n var loadingEl = submitBtn.querySelector('.pa-feedback-submit-loading');\n\n submitBtn.disabled = isSubmitting;\n textEl.classList.toggle('pa-feedback-hidden', isSubmitting);\n loadingEl.classList.toggle('pa-feedback-hidden', !isSubmitting);\n }\n\n function showSuccess() {\n document.getElementById('pa-feedback-form').classList.add('pa-feedback-hidden');\n document.getElementById('pa-feedback-success').classList.remove('pa-feedback-hidden');\n\n if (FB.config.autoClose) {\n setTimeout(function() {\n closeModal();\n resetForm();\n }, FB.config.autoCloseDelay);\n }\n }\n\n function resetForm() {\n var form = document.getElementById('pa-feedback-form');\n form.reset();\n form.classList.remove('pa-feedback-hidden');\n document.getElementById('pa-feedback-success').classList.add('pa-feedback-hidden');\n hideError();\n\n FB.rating = 0;\n ${config.showRating ? 'updateStars();' : ''}\n ${config.showComment ? `\n updateCharCount();\n // Reset comment label and placeholder to default\n var commentLabel = document.getElementById('pa-feedback-comment-label');\n var commentTextarea = document.getElementById('pa-feedback-comment');\n commentLabel.innerHTML = FB.translations.commentLabel + '${config.commentRequired ? ' <span class=\"pa-feedback-required\">*</span>' : ''}';\n commentTextarea.placeholder = FB.translations.commentPlaceholder;\n ` : ''}\n ${config.showIssueType ? `\n // Hide issue type fields on reset\n document.getElementById('pa-feedback-issue-type-field').classList.add('pa-feedback-hidden');\n document.getElementById('pa-feedback-subtype-field').classList.add('pa-feedback-hidden');\n ` : ''}\n }\n\n async function handleSubmit(e) {\n e.preventDefault();\n hideError();\n\n // Validate\n var errors = validateForm();\n if (errors.length > 0) {\n showError(FB.translations.errorRequired);\n return;\n }\n\n // Collect data\n var data = collectFormData();\n log('Submitting:', data);\n\n // Submit\n setSubmitting(true);\n\n try {\n ${config.endpoint ? `\n var response = await fetch('${config.endpoint}', {\n method: '${config.method}',\n headers: Object.assign({\n 'Content-Type': 'application/json',\n }, FB.config.headers || {}),\n body: JSON.stringify(data),\n });\n\n if (!response.ok) {\n throw new Error('HTTP ' + response.status);\n }\n\n log('Submitted successfully');\n ` : `\n // No endpoint configured - just log\n console.log('[PA-Feedback] Feedback submitted:', data);\n log('No endpoint configured, feedback logged to console');\n `}\n\n showSuccess();\n } catch (err) {\n log('Submit error:', err);\n showError(FB.translations.errorSubmitting);\n } finally {\n setSubmitting(false);\n }\n }\n\n // ============================================================================\n // INITIALIZATION\n // ============================================================================\n\n function init() {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', createWidget);\n } else {\n createWidget();\n }\n log('Initialized');\n }\n\n init();\n\n})();\n`;\n}\n","import type { FeedbackConfig } from './config.js';\n\n/**\n * Generate the CSS styles for the feedback widget.\n * Styles are scoped with #pa-feedback-container to avoid conflicts.\n */\nexport function generateFeedbackStyles(config: FeedbackConfig): string {\n const position = config.position;\n const tabColor = config.tabColor;\n const tabTextColor = config.tabTextColor;\n const zIndex = config.zIndex;\n\n // Pre-compute position-dependent values\n const isRight = position === 'right';\n const tabTransform = isRight ? '-90deg' : '90deg';\n const tabTransformOrigin = isRight ? 'right center' : 'left center';\n const tabOffset = isRight ? 'right: 20px; left: auto;' : 'left: 20px; right: auto;';\n const tabBorderRadius = isRight ? '8px 8px 0 0' : '0 0 8px 8px';\n const modalPosition = isRight ? 'right: 0; left: auto;' : 'left: 0; right: auto;';\n const modalJustify = isRight ? 'flex-end' : 'flex-start';\n const contentShadow = isRight ? '-4px 0 20px rgba(0, 0, 0, 0.15)' : '4px 0 20px rgba(0, 0, 0, 0.15)';\n const slideFrom = isRight ? '100%' : '-100%';\n\n return `\n/* ============================================================================\n FEEDBACK WIDGET STYLES - Patch-Adams Plugin v1.0.0\n ============================================================================ */\n\n#pa-feedback-container {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;\n font-size: 14px;\n line-height: 1.5;\n color: #333;\n box-sizing: border-box;\n}\n\n#pa-feedback-container *,\n#pa-feedback-container *::before,\n#pa-feedback-container *::after {\n box-sizing: inherit;\n}\n\n/* Hidden utility class */\n#pa-feedback-container .pa-feedback-hidden {\n display: none !important;\n}\n\n/* ============================================================================\n TAB BUTTON\n ============================================================================ */\n\n#pa-feedback-tab {\n position: fixed;\n top: 50%;\n ${tabOffset}\n transform: translateY(-50%) rotate(${tabTransform});\n transform-origin: ${tabTransformOrigin};\n z-index: ${zIndex};\n background: ${tabColor};\n color: ${tabTextColor};\n border: none;\n padding: 10px 20px;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n border-radius: ${tabBorderRadius};\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n transition: all 0.2s ease;\n white-space: nowrap;\n}\n\n#pa-feedback-tab:hover {\n background: ${adjustColor(tabColor, -15)};\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);\n}\n\n#pa-feedback-tab:focus {\n outline: 2px solid ${tabColor};\n outline-offset: 2px;\n}\n\n#pa-feedback-tab.pa-feedback-tab-active {\n background: ${adjustColor(tabColor, -20)};\n}\n\n/* ============================================================================\n MODAL\n ============================================================================ */\n\n#pa-feedback-modal {\n position: fixed;\n top: 0;\n ${modalPosition}\n width: 360px;\n height: 100%;\n z-index: ${zIndex + 1};\n background: rgba(0, 0, 0, 0.3);\n display: flex;\n justify-content: ${modalJustify};\n animation: pa-feedback-fade-in 0.2s ease;\n}\n\n@keyframes pa-feedback-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n#pa-feedback-container .pa-feedback-content {\n width: 100%;\n max-width: 360px;\n height: 100%;\n background: #fff;\n box-shadow: ${contentShadow};\n display: flex;\n flex-direction: column;\n animation: pa-feedback-slide-in 0.2s ease;\n}\n\n@keyframes pa-feedback-slide-in {\n from {\n transform: translateX(${slideFrom});\n }\n to {\n transform: translateX(0);\n }\n}\n\n/* ============================================================================\n HEADER\n ============================================================================ */\n\n#pa-feedback-container .pa-feedback-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 16px 20px;\n border-bottom: 1px solid #e5e5e5;\n background: #fafafa;\n}\n\n#pa-feedback-container .pa-feedback-header h3 {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: #222;\n}\n\n#pa-feedback-container .pa-feedback-close {\n background: none;\n border: none;\n font-size: 24px;\n color: #666;\n cursor: pointer;\n padding: 4px 8px;\n line-height: 1;\n border-radius: 4px;\n transition: all 0.15s ease;\n}\n\n#pa-feedback-container .pa-feedback-close:hover {\n background: #e5e5e5;\n color: #333;\n}\n\n/* ============================================================================\n FORM\n ============================================================================ */\n\n#pa-feedback-form {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n display: flex;\n flex-direction: column;\n gap: 20px;\n}\n\n#pa-feedback-container .pa-feedback-field {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n#pa-feedback-container .pa-feedback-label {\n font-weight: 500;\n color: #444;\n font-size: 14px;\n}\n\n#pa-feedback-container .pa-feedback-required {\n color: #dc3545;\n}\n\n/* ============================================================================\n STAR RATING\n ============================================================================ */\n\n#pa-feedback-container .pa-feedback-stars {\n display: flex;\n gap: 4px;\n}\n\n#pa-feedback-container .pa-feedback-star {\n background: none;\n border: none;\n font-size: 28px;\n color: #ccc;\n cursor: pointer;\n padding: 4px;\n line-height: 1;\n transition: all 0.15s ease;\n}\n\n#pa-feedback-container .pa-feedback-star:hover {\n transform: scale(1.1);\n}\n\n#pa-feedback-container .pa-feedback-star-filled,\n#pa-feedback-container .pa-feedback-star-hover {\n color: #ffc107;\n}\n\n/* ============================================================================\n SELECT & TEXTAREA\n ============================================================================ */\n\n#pa-feedback-container .pa-feedback-select {\n width: 100%;\n padding: 10px 12px;\n font-size: 14px;\n border: 1px solid #ddd;\n border-radius: 6px;\n background: #fff;\n color: #333;\n cursor: pointer;\n transition: border-color 0.15s ease;\n}\n\n#pa-feedback-container .pa-feedback-select:focus {\n outline: none;\n border-color: ${tabColor};\n box-shadow: 0 0 0 3px ${hexToRgba(tabColor, 0.1)};\n}\n\n#pa-feedback-container .pa-feedback-textarea {\n width: 100%;\n padding: 12px;\n font-size: 14px;\n font-family: inherit;\n border: 1px solid #ddd;\n border-radius: 6px;\n resize: vertical;\n min-height: 100px;\n transition: border-color 0.15s ease;\n}\n\n#pa-feedback-container .pa-feedback-textarea:focus {\n outline: none;\n border-color: ${tabColor};\n box-shadow: 0 0 0 3px ${hexToRgba(tabColor, 0.1)};\n}\n\n#pa-feedback-container .pa-feedback-textarea::placeholder {\n color: #999;\n}\n\n#pa-feedback-container .pa-feedback-counter {\n text-align: right;\n font-size: 12px;\n color: #888;\n}\n\n/* ============================================================================\n ERROR MESSAGE\n ============================================================================ */\n\n#pa-feedback-container .pa-feedback-error {\n background: #fee2e2;\n color: #dc2626;\n padding: 10px 14px;\n border-radius: 6px;\n font-size: 13px;\n}\n\n/* ============================================================================\n SUBMIT BUTTON\n ============================================================================ */\n\n#pa-feedback-container .pa-feedback-submit {\n width: 100%;\n padding: 12px 20px;\n font-size: 16px;\n font-weight: 600;\n color: #fff;\n background: ${tabColor};\n border: none;\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.15s ease;\n margin-top: auto;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n}\n\n#pa-feedback-container .pa-feedback-submit:hover:not(:disabled) {\n background: ${adjustColor(tabColor, -15)};\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);\n transform: translateY(-1px);\n}\n\n#pa-feedback-container .pa-feedback-submit:focus {\n outline: 2px solid ${tabColor};\n outline-offset: 2px;\n}\n\n#pa-feedback-container .pa-feedback-submit:disabled {\n opacity: 0.7;\n cursor: not-allowed;\n}\n\n/* ============================================================================\n SUCCESS MESSAGE\n ============================================================================ */\n\n#pa-feedback-container .pa-feedback-success {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px 20px;\n text-align: center;\n}\n\n#pa-feedback-container .pa-feedback-checkmark {\n width: 64px;\n height: 64px;\n background: #10b981;\n color: #fff;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 32px;\n margin-bottom: 20px;\n animation: pa-feedback-pop 0.3s ease;\n}\n\n@keyframes pa-feedback-pop {\n 0% { transform: scale(0); }\n 50% { transform: scale(1.2); }\n 100% { transform: scale(1); }\n}\n\n#pa-feedback-container .pa-feedback-success p {\n margin: 0;\n font-size: 18px;\n font-weight: 500;\n color: #333;\n}\n`;\n}\n\n/**\n * Adjust a hex color by a percentage (positive = lighter, negative = darker)\n */\nfunction adjustColor(hex: string, percent: number): string {\n // Remove # if present\n hex = hex.replace(/^#/, '');\n\n // Parse the color\n let r = parseInt(hex.substring(0, 2), 16);\n let g = parseInt(hex.substring(2, 4), 16);\n let b = parseInt(hex.substring(4, 6), 16);\n\n // Adjust\n r = Math.min(255, Math.max(0, r + (r * percent) / 100));\n g = Math.min(255, Math.max(0, g + (g * percent) / 100));\n b = Math.min(255, Math.max(0, b + (b * percent) / 100));\n\n // Convert back to hex\n return (\n '#' +\n Math.round(r).toString(16).padStart(2, '0') +\n Math.round(g).toString(16).padStart(2, '0') +\n Math.round(b).toString(16).padStart(2, '0')\n );\n}\n\n/**\n * Convert hex color to rgba\n */\nfunction hexToRgba(hex: string, alpha: number): string {\n hex = hex.replace(/^#/, '');\n const r = parseInt(hex.substring(0, 2), 16);\n const g = parseInt(hex.substring(2, 4), 16);\n const b = parseInt(hex.substring(4, 6), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n}\n","/**\n * @patch-adams/plugin-feedback\n *\n * Feedback widget plugin for Patch-Adams.\n * Injects a customizable feedback form into patched Rise courses.\n *\n * Features:\n * - Star rating\n * - Issue type/subtype selection\n * - Comment text area\n * - Multi-language support (en/fr)\n * - Customizable appearance\n * - Automatic metadata collection\n * - Configurable API endpoint\n */\n\nimport type { PatchAdamsPlugin } from '@patch-adams/core';\nimport { FeedbackConfigSchema, type FeedbackConfig } from './config.js';\nimport { generateFeedbackWidget } from './widget.js';\nimport { generateFeedbackStyles } from './styles.js';\n\n/**\n * Feedback plugin for Patch-Adams\n */\nexport const feedbackPlugin: PatchAdamsPlugin<FeedbackConfig> = {\n name: 'feedback',\n version: '1.0.0',\n description: 'Inject a feedback form widget into Rise courses for collecting user feedback',\n configSchema: FeedbackConfigSchema,\n\n generateCss(config: FeedbackConfig) {\n return {\n after: generateFeedbackStyles(config),\n };\n },\n\n generateJs(config: FeedbackConfig) {\n return {\n after: generateFeedbackWidget(config),\n };\n },\n};\n\n// Default export for convenience\nexport default feedbackPlugin;\n\n// Re-export types and utilities\nexport {\n FeedbackConfigSchema,\n type FeedbackConfig,\n type IssueType,\n type IssueSubtype,\n type MetadataOptions,\n DEFAULT_ISSUE_TYPES,\n} from './config.js';\n\nexport { generateFeedbackWidget } from './widget.js';\nexport { generateFeedbackStyles } from './styles.js';\nexport { getTranslations, translations, type Translations } from './i18n/index.js';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patch-adams/plugin-feedback",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Feedback widget plugin for Patch-Adams - inject a feedback form into Rise courses",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",