freemium-ui 2.0.133 → 2.0.134

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.
Files changed (36) hide show
  1. package/.github/actionlint.yaml +6 -0
  2. package/.github/pr_validation_guide.md +305 -0
  3. package/.github/pull_request_template.md +189 -0
  4. package/.github/workflows/pr-quality.yml +114 -0
  5. package/.github/workflows/pr-template-validation.yml +367 -0
  6. package/.github/workflows/sonarqube-main.yml +106 -0
  7. package/.nvmrc +1 -0
  8. package/README.md +9 -0
  9. package/catalog-info.yaml +33 -0
  10. package/docs/adrs/index.md +11 -0
  11. package/docs/architecture/code-structure.md +109 -0
  12. package/docs/architecture/deep-dive.md +193 -0
  13. package/docs/getting-started.md +103 -0
  14. package/docs/index.md +35 -0
  15. package/docs/operations/alerts-configuration.md +11 -0
  16. package/docs/operations/how-to-debug.md +43 -0
  17. package/docs/operations/monitoring.md +16 -0
  18. package/docs/operations/recent-issues.md +14 -0
  19. package/docs/operations/sop.md +25 -0
  20. package/docs/operations/support.md +15 -0
  21. package/lib/README.md +9 -0
  22. package/lib/index.cjs.js +1041 -1021
  23. package/lib/index.d.ts +2 -0
  24. package/lib/index.js +315 -294
  25. package/lib/package.json +1 -1
  26. package/mkdocs.yml +38 -0
  27. package/package.json +3 -1
  28. package/sonar-project.properties +25 -0
  29. package/tech-docs/docs/architecture/components.md +49 -0
  30. package/tech-docs/docs/architecture/index.md +54 -0
  31. package/tech-docs/docs/development/build-publish.md +42 -0
  32. package/tech-docs/docs/development/storybook.md +37 -0
  33. package/tech-docs/docs/getting-started.md +67 -0
  34. package/tech-docs/docs/index.md +47 -0
  35. package/tech-docs/docs/repositories/index.md +22 -0
  36. package/tech-docs/mkdocs.yml +28 -0
@@ -0,0 +1,367 @@
1
+ name: PR Template Validation
2
+
3
+ on:
4
+ pull_request:
5
+ types: [opened, edited, synchronize]
6
+
7
+ permissions:
8
+ pull-requests: write
9
+ issues: write
10
+ contents: read
11
+
12
+ jobs:
13
+ validate-template:
14
+ runs-on: ['self-hosted', 'Linux']
15
+ name: Validate PR Template Sections
16
+
17
+ steps:
18
+ - name: Check if PR is from a bot
19
+ id: bot-check
20
+ uses: actions/github-script@v7
21
+ with:
22
+ script: |
23
+ const pr = context.payload.pull_request;
24
+ const author = pr.user.login;
25
+ const authorType = pr.user.type;
26
+
27
+ console.log('=== Bot Detection ===');
28
+ console.log(`PR Author: ${author}`);
29
+ console.log(`Author Type: ${authorType}`);
30
+
31
+ const botUsers = [
32
+ 'dependabot[bot]',
33
+ 'dependabot-preview[bot]',
34
+ 'renovate[bot]',
35
+ 'github-actions[bot]',
36
+ 'mergify[bot]',
37
+ 'snyk-bot'
38
+ ];
39
+
40
+ const isBot =
41
+ botUsers.includes(author) ||
42
+ author.endsWith('[bot]') ||
43
+ authorType === 'Bot';
44
+
45
+ const isDependencyPR =
46
+ pr.title.toLowerCase().includes('bump') ||
47
+ pr.title.toLowerCase().includes('update dependencies') ||
48
+ pr.title.toLowerCase().includes('chore(deps)');
49
+
50
+ const shouldSkip = isBot || isDependencyPR;
51
+
52
+ console.log(`Is Bot: ${isBot}`);
53
+ console.log(`Is Dependency PR: ${isDependencyPR}`);
54
+ console.log(`Should Skip Validation: ${shouldSkip}`);
55
+
56
+ if (shouldSkip) {
57
+ console.log('⏭️ Skipping PR template validation for automated PR');
58
+
59
+ const botSkipMarker = '<!-- pr-bot-skip-marker -->';
60
+ const comments = await github.paginate(github.rest.issues.listComments, {
61
+ owner: context.repo.owner,
62
+ repo: context.repo.repo,
63
+ issue_number: context.issue.number,
64
+ per_page: 100
65
+ });
66
+
67
+ const existingComment = comments.find(c => c.body.includes(botSkipMarker));
68
+ if (!existingComment) {
69
+ await github.rest.issues.createComment({
70
+ owner: context.repo.owner,
71
+ repo: context.repo.repo,
72
+ issue_number: context.issue.number,
73
+ body: `${botSkipMarker}\n🤖 **Automated PR Detected** - Skipping PR template validation.\n\nThis PR appears to be from a bot or automated process. Template validation is not required.`
74
+ });
75
+ console.log('Created bot skip comment');
76
+ } else {
77
+ console.log('Bot skip comment already exists, skipping duplicate');
78
+ }
79
+ }
80
+
81
+ return shouldSkip;
82
+
83
+ - name: Validate PR Template Completion
84
+ if: steps.bot-check.outputs.result != 'true'
85
+ uses: actions/github-script@v7
86
+ with:
87
+ github-token: ${{ secrets.RUNWAYCI_GITHUB_PAT_TOKEN }}
88
+ script: |
89
+ const prBody = context.payload.pull_request.body || '';
90
+ const prTitle = context.payload.pull_request.title || '';
91
+ const errors = [];
92
+ const warnings = [];
93
+
94
+ console.log('=== Starting PR Template Validation ===');
95
+ console.log(`PR #${context.issue.number}: ${prTitle}`);
96
+
97
+ // 0. Validate PR Title (must match commit message format)
98
+ console.log('Checking PR Title format...');
99
+ const validTypes = ['feat', 'fix', 'chore', 'docs', 'refactor', 'test', 'perf', 'ci', 'build', 'style', 'revert'];
100
+ const titleRegex = /^(feat|fix|chore|docs|refactor|test|perf|ci|build|style|revert):\s*\[(MP-\d+|FS-\d+|FSURVEY-\d+|DEVREL-\d+|PLAT-\d+|MISC|Docs)\]\s+\S.*$/;
101
+ const hasTitleFormat = titleRegex.test(prTitle);
102
+
103
+ if (!hasTitleFormat) {
104
+ errors.push(`**PR Title**: Invalid format. Required format: \`type: [TICKET-ID] description\`
105
+
106
+ **Valid types**: ${validTypes.join(', ')}
107
+ **Valid tickets**: MP-xxxx, FS-xxxx, FSURVEY-xxxx, DEVREL-xxxx, PLAT-xxxx, MISC, Docs
108
+
109
+ **Examples**:
110
+ - ✅ \`feat: [MP-12345] Add new Dropdown variant\`
111
+ - ✅ \`feat: [FS-1000] Add Tooltip component\`
112
+ - ✅ \`feat: [FSURVEY-2024] Add survey builder enhancement\`
113
+ - ✅ \`fix: [MISC] Fix Modal focus trap\`
114
+ - ✅ \`chore: [DEVREL-4720] Update dependencies\`
115
+ - ❌ \`Add Dropdown variant\` (missing type and ticket)
116
+ - ❌ \`[MP-12345] Add feature\` (missing type prefix)
117
+
118
+ **Your title**: \`${prTitle}\``);
119
+ } else {
120
+ const titleMatch = prTitle.match(titleRegex);
121
+ console.log(`✓ PR Title format valid: ${titleMatch[1]} - ${titleMatch[2]}`);
122
+ }
123
+
124
+ // 1. Validate Type of Change (at least one checkbox selected)
125
+ console.log('Checking Type of Change...');
126
+ const typeRegex = /## Type of Change[\s\S]*?(?=##|$)/;
127
+ const typeSection = prBody.match(typeRegex)?.[0] || '';
128
+ const hasTypeSelected = /- \[x\]/i.test(typeSection);
129
+
130
+ if (!hasTypeSelected) {
131
+ errors.push('**Type of Change**: No type selected. Please check at least one box (Feature, Bugfix, Refactor, etc.).');
132
+ } else {
133
+ console.log('✓ Type of Change selected');
134
+ }
135
+
136
+ // 2. Validate Ticket Reference (must have valid ticket ID)
137
+ console.log('Checking Ticket Reference...');
138
+ const ticketRegex = /-\s*\*\*Ticket ID\*\*:\s*\[(MP-\d+|FS-\d+|FSURVEY-\d+|DEVREL-\d+|PLAT-\d+|MISC|Docs)\]/;
139
+ const hasTicketId = ticketRegex.test(prBody);
140
+
141
+ if (!hasTicketId) {
142
+ errors.push('**Ticket Reference**: Missing or invalid ticket ID. Required format: `[MP-xxxx]`, `[FS-xxxx]`, `[FSURVEY-xxxx]`, `[DEVREL-xxxx]`, `[PLAT-xxxx]`, `[MISC]`, or `[Docs]`');
143
+ } else {
144
+ const ticketMatch = prBody.match(ticketRegex);
145
+ console.log(`✓ Ticket ID found: ${ticketMatch[1]}`);
146
+ }
147
+
148
+ // 3. Validate Changes Made (minimum 3 bullet points with content)
149
+ console.log('Checking Changes Made...');
150
+ const changesRegex = /## Changes Made[\s\S]*?(?=##|$)/;
151
+ const changesSection = prBody.match(changesRegex)?.[0] || '';
152
+ const changesBullets = (changesSection.match(/^-\s+.+$/gm) || [])
153
+ .map(line => line.replace(/^-\s+/, '').trim())
154
+ .filter(content =>
155
+ content.length > 0 &&
156
+ !/^Change\s+\d+:?\s*$/i.test(content)
157
+ );
158
+
159
+ if (changesBullets.length < 3) {
160
+ errors.push(`**Changes Made**: Insufficient items listed (found ${changesBullets.length}). Please provide at least 3 bullet points describing your changes.`);
161
+ } else {
162
+ console.log(`✓ Changes Made: ${changesBullets.length} item(s) listed (minimum 3 met)`);
163
+ }
164
+
165
+ // 4. Validate Affected Areas (at least one checkbox selected)
166
+ console.log('Checking Affected Areas...');
167
+ const affectedRegex = /## Affected Areas[\s\S]*?(?=##|$)/;
168
+ const affectedSection = prBody.match(affectedRegex)?.[0] || '';
169
+ const hasAreaSelected = /- \[x\]/i.test(affectedSection);
170
+
171
+ if (!hasAreaSelected) {
172
+ errors.push('**Affected Areas**: No area selected. Please check at least one affected area (Components, Styles/Themes, Stories, etc.).');
173
+ } else {
174
+ console.log('✓ Affected Areas selected');
175
+ }
176
+
177
+ // 5. Validate Breaking Changes (must select exactly one option)
178
+ console.log('Checking Breaking Changes...');
179
+ const breakingRegex = /## Breaking Changes[\s\S]*?(?=##|$)/;
180
+ const breakingSection = prBody.match(breakingRegex)?.[0] || '';
181
+ const breakingSelections = breakingSection.match(
182
+ /^- \[[xX]\]\s+(Yes,?\s+this PR introduces breaking changes|No breaking changes.*)$/gim
183
+ ) || [];
184
+ const hasBreakingSelection = breakingSelections.length === 1;
185
+
186
+ if (!hasBreakingSelection) {
187
+ errors.push('**Breaking Changes**: Select exactly one option (Yes or No).');
188
+ } else {
189
+ console.log('✓ Breaking Changes option selected');
190
+ }
191
+
192
+ // 6. If breaking changes = yes, require description
193
+ const hasBreakingYes = /^-\s*\[[xX]\]\s+Yes,?\s+this PR introduces breaking changes/im.test(breakingSection);
194
+ if (hasBreakingYes) {
195
+ console.log('Breaking changes detected, checking for description...');
196
+ const descriptionPart = breakingSection
197
+ .replace(/^## Breaking Changes.*$/m, '')
198
+ .replace(/^- \[[ xX]\]\s+Yes,?\s+this PR introduces breaking changes.*$/m, '')
199
+ .replace(/^- \[[ xX]\]\s+No breaking changes.*$/m, '')
200
+ .replace(/<!---[\s\S]*?-->/g, '')
201
+ .trim();
202
+ const hasDescription = descriptionPart.length > 50;
203
+
204
+ if (!hasDescription) {
205
+ errors.push('**Breaking Changes**: You selected "Yes" but did not describe the breaking changes. Please provide details and migration steps.');
206
+ } else {
207
+ console.log('✓ Breaking changes description provided');
208
+ }
209
+ }
210
+
211
+ // 7. Validate Testing section (test steps required)
212
+ console.log('Checking Testing section...');
213
+ const testingRegex = /## How Has This Been Tested\?[\s\S]*?(?=##|$)/;
214
+ const testingSection = prBody.match(testingRegex)?.[0] || '';
215
+ const testStepsContent = testingSection
216
+ .replace(/\*\*Test Steps:\*\*/g, '')
217
+ .replace(/<!---[\s\S]*?-->/g, '')
218
+ .trim();
219
+ const numberedSteps = testStepsContent.match(/^\d+\.\s+.+$/gm) || [];
220
+ const hasTestSteps = numberedSteps.some(
221
+ step => !/^\d+\.\s+Step\s+\d+\s*$/i.test(step.trim())
222
+ );
223
+
224
+ if (!hasTestSteps) {
225
+ warnings.push('**Testing**: Test steps are missing or contain only placeholders. Please describe how you tested your changes (steps 1, 2, 3...).');
226
+ } else {
227
+ console.log('✓ Test steps provided');
228
+ }
229
+
230
+ // 8. Validate SDET Guidance (must be filled)
231
+ console.log('Checking SDET Guidance...');
232
+ const sdetRegex = /## What should the SDET know\?[\s\S]*?(?=##|$)/;
233
+ const sdetSection = prBody.match(sdetRegex)?.[0] || '';
234
+ const sdetContent = sdetSection
235
+ .split('## What should the SDET know?')[1] || '';
236
+ const sdetCleanContent = sdetContent
237
+ .replace(/<!---[\s\S]*?-->/g, '')
238
+ .replace(/^-\s+Test\s+scenario\s+\d+:?\s*$/gim, '')
239
+ .replace(/^-\s+Edge\s+case(\s+to\s+check)?:?\s*$/gim, '')
240
+ .replace(/^-\s+Focus\s+area:?\s*$/gim, '')
241
+ .trim();
242
+ const hasSdetGuidance = sdetCleanContent.length > 30;
243
+
244
+ if (!hasSdetGuidance) {
245
+ warnings.push('**SDET Guidance**: Please provide testing guidance for the QA team (test scenarios, edge cases, focus areas).');
246
+ } else {
247
+ console.log('✓ SDET guidance provided');
248
+ }
249
+
250
+ // 9. Check Screenshots for UI changes
251
+ console.log('Checking for UI changes...');
252
+ const files = await github.paginate(github.rest.pulls.listFiles, {
253
+ owner: context.repo.owner,
254
+ repo: context.repo.repo,
255
+ pull_number: context.issue.number,
256
+ per_page: 100
257
+ });
258
+
259
+ const uiFiles = files.filter(file =>
260
+ file.filename.endsWith('.tsx') &&
261
+ (
262
+ file.filename.startsWith('src/components/') ||
263
+ file.filename.startsWith('src/stories/')
264
+ )
265
+ );
266
+
267
+ if (uiFiles.length > 0) {
268
+ console.log(`UI changes detected in ${uiFiles.length} files`);
269
+ const screenshotsRegex = /## Screenshots\/Videos[\s\S]*?(?=##|$)/;
270
+ const screenshotsSection = prBody.match(screenshotsRegex)?.[0] || '';
271
+ const hasScreenshots = /!\[.*?\]\(.*?\)|https?:\/\/\S+\.(png|jpg|jpeg|gif|mp4|webm|mov)(?:\?\S+)?|https?:\/\/github\.com\/user-attachments\/assets\/[^\s)]+/i.test(screenshotsSection);
272
+
273
+ if (!hasScreenshots) {
274
+ warnings.push(`**Screenshots/Videos**: UI changes detected in ${uiFiles.length} file(s) but no screenshots provided. Please consider adding before/after screenshots or a Storybook link.`);
275
+ } else {
276
+ console.log('✓ Screenshots provided for UI changes');
277
+ }
278
+ } else {
279
+ console.log('No UI changes detected');
280
+ }
281
+
282
+ // Generate validation report
283
+ console.log(`\nValidation Results: ${errors.length} errors, ${warnings.length} warnings`);
284
+
285
+ const docsRef = context.payload.pull_request.head.sha;
286
+ const repoUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}`;
287
+ const guideUrl = `${repoUrl}/blob/${docsRef}/.github/pr_validation_guide.md`;
288
+ const templateUrl = `${repoUrl}/blob/${docsRef}/.github/pull_request_template.md`;
289
+
290
+ const reportMarker = '<!-- pr-template-validation-report -->';
291
+ let comment = `${reportMarker}\n## 🔍 PR Template Validation Report\n\n`;
292
+
293
+ if (errors.length > 0) {
294
+ comment += '### ❌ Required Sections Missing\n\n';
295
+ comment += 'The following **required sections** must be completed:\n\n';
296
+ comment += errors.map(e => `- ${e}`).join('\n');
297
+ comment += '\n\n**⚠️ Action Required**: Please edit your PR description to include all required information.\n\n';
298
+ comment += `📖 **Verify against the rules:** [PR Validation Guide](${guideUrl})\n\n`;
299
+ comment += '---\n\n';
300
+ }
301
+
302
+ if (warnings.length > 0) {
303
+ comment += '### ⚠️ Recommended Improvements\n\n';
304
+ comment += 'Please consider adding:\n\n';
305
+ comment += warnings.map(w => `- ${w}`).join('\n');
306
+ comment += '\n\n---\n\n';
307
+ }
308
+
309
+ if (errors.length === 0 && warnings.length === 0) {
310
+ comment = `${reportMarker}\n## ✅ PR Template Complete\n\n`;
311
+ comment += 'All required sections are properly filled. Great job! 🎉\n';
312
+ }
313
+
314
+ // Add helpful footer
315
+ comment += '\n---\n\n';
316
+ comment += '<details>\n';
317
+ comment += '<summary>📋 PR Template Requirements</summary>\n\n';
318
+ comment += '**Required Sections:**\n';
319
+ comment += '- Type of Change (select at least one)\n';
320
+ comment += '- Ticket Reference (valid ticket ID)\n';
321
+ comment += '- Changes Made (minimum 3 items)\n';
322
+ comment += '- Affected Areas (select at least one)\n';
323
+ comment += '- Breaking Changes (select Yes/No)\n\n';
324
+ comment += '**Recommended Sections:**\n';
325
+ comment += '- Testing (test steps)\n';
326
+ comment += '- SDET Guidance (QA testing guidance)\n';
327
+ comment += '- Screenshots (if UI changes)\n\n';
328
+ comment += `📖 [PR Validation Guide](${guideUrl}) — full rules, examples, and troubleshooting\n`;
329
+ comment += `📝 [Pull Request Template](${templateUrl}) — the template structure\n`;
330
+ comment += '</details>\n';
331
+
332
+ // Post or update comment
333
+ const comments = await github.paginate(github.rest.issues.listComments, {
334
+ owner: context.repo.owner,
335
+ repo: context.repo.repo,
336
+ issue_number: context.issue.number,
337
+ per_page: 100
338
+ });
339
+
340
+ const botComment = comments.find(c =>
341
+ c.body.includes(reportMarker)
342
+ );
343
+
344
+ if (botComment) {
345
+ await github.rest.issues.updateComment({
346
+ owner: context.repo.owner,
347
+ repo: context.repo.repo,
348
+ comment_id: botComment.id,
349
+ body: comment
350
+ });
351
+ console.log('Updated existing validation comment');
352
+ } else {
353
+ await github.rest.issues.createComment({
354
+ owner: context.repo.owner,
355
+ repo: context.repo.repo,
356
+ issue_number: context.issue.number,
357
+ body: comment
358
+ });
359
+ console.log('Created new validation comment');
360
+ }
361
+
362
+ // Fail the check if there are errors (blocks merge with branch protection)
363
+ if (errors.length > 0) {
364
+ core.setFailed(`PR template validation failed: ${errors.length} required section(s) missing or incomplete`);
365
+ } else {
366
+ console.log('✅ PR template validation passed!');
367
+ }
@@ -0,0 +1,106 @@
1
+ name: SonarQube Main Branch
2
+
3
+ # Analyses the main branch after every merge, per the Platforms CX guidance
4
+ # (https://confluence.freshworks.com/spaces/SHAR/pages/525401715/Sonarqube+integration+Frontend).
5
+ #
6
+ # This is what produces the overall project dashboard: total bugs, code smells,
7
+ # vulnerabilities, security hotspots, duplication and coverage across the whole
8
+ # codebase. Pull request analyses only report on changed code and never populate
9
+ # a branch, so without this workflow the project's main branch stays empty.
10
+ #
11
+ # It also gives the New Code baseline that PR analyses diff against.
12
+
13
+ on:
14
+ push:
15
+ branches:
16
+ - main
17
+ # Allows a one-off baseline run without waiting for a merge.
18
+ workflow_dispatch:
19
+
20
+ concurrency:
21
+ group: sonarqube-main-${{ github.ref }}
22
+ cancel-in-progress: true
23
+
24
+ permissions:
25
+ contents: read
26
+
27
+ jobs:
28
+ analysis:
29
+ name: Analyse main branch
30
+ runs-on: fw-shared
31
+ timeout-minutes: 30
32
+
33
+ steps:
34
+ - name: Checkout
35
+ uses: actions/checkout@v4
36
+ with:
37
+ # SonarQube needs full history for blame attribution and new-code detection.
38
+ fetch-depth: 0
39
+ # Do not leave the GITHUB_TOKEN in the workspace git config. The SonarQube
40
+ # scan action runs in a container with the workspace mounted, and no step
41
+ # here performs a git network operation that would need the credential.
42
+ persist-credentials: false
43
+
44
+ # Node 20 LTS — align with org runner or bump when standard changes
45
+ - name: Setup Node
46
+ uses: actions/setup-node@v4
47
+ with:
48
+ node-version: '20'
49
+ cache: 'npm'
50
+
51
+ # Configures npm to fetch @freshworks scoped packages from GitHub Packages —
52
+ # needed here because this package depends on @freshworks/dew-styles.
53
+ - name: Configure npm for GitHub Packages
54
+ run: |
55
+ echo "@freshdesk:registry=https://npm.pkg.github.com/" >> .npmrc
56
+ echo "@freshworks:registry=https://npm.pkg.github.com/" >> .npmrc
57
+ echo "@freshworksinc:registry=https://npm.pkg.github.com/" >> .npmrc
58
+ echo "//npm.pkg.github.com/:_authToken=${{ secrets.GH_AUTH_TOKEN }}" >> .npmrc
59
+
60
+ # --ignore-scripts: same GH_AUTH_TOKEN-in-.npmrc exposure as the PR
61
+ # workflow, even though this one only ever runs already-merged main
62
+ # code. No dependency's install script is needed to build or test here.
63
+ - name: Install dependencies
64
+ run: npm ci --ignore-scripts
65
+
66
+ # The step above appends a literal GH_AUTH_TOKEN to the workspace .npmrc.
67
+ # Remove it so the token does not persist into later steps — the SonarQube
68
+ # scan action runs in a container with the workspace mounted. No step after
69
+ # this one installs packages. The tracked .npmrc in this repo is empty, so
70
+ # there's nothing to restore — just delete the appended file.
71
+ - name: Remove npm auth token from workspace
72
+ if: ${{ always() }}
73
+ run: git checkout -- .npmrc || rm -f .npmrc
74
+
75
+ - name: Test coverage
76
+ timeout-minutes: 15
77
+ env:
78
+ CI: true
79
+ run: npm run test:coverage
80
+
81
+ # Reads sonar-project.properties for scope and exclusions. No pull request
82
+ # parameters here — that is what makes this a branch analysis rather than a
83
+ # PR analysis, and therefore what populates the project dashboard.
84
+ #
85
+ # continue-on-error: the SonarQube instance is at its server-wide LOC
86
+ # licence limit, and branch analyses of net-new code are rejected with
87
+ # errorType LICENSING_LOC. Remove this once the limit is raised.
88
+ - name: SonarQube analysis
89
+ id: sonar
90
+ continue-on-error: true
91
+ # Pinned to v8.2.1 rather than the mutable @master ref — this action
92
+ # runs with SONAR_TOKEN and the workspace mounted.
93
+ uses: sonarsource/sonarqube-scan-action@22918119ff8e1ca75a623e15c8296b6ea4fbe28f # v8.2.1
94
+ env:
95
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
96
+ SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
97
+
98
+ # continue-on-error makes the job conclusion "success" regardless, which
99
+ # would hide auth, host, scanner and configuration failures.
100
+ # Caveat: this only catches scanner-side failures. A server-side rejection
101
+ # (e.g. LICENSING_LOC) still exits 0 — verify via the SonarQube task API:
102
+ # /api/ce/component?component=<projectKey>
103
+ - name: Flag SonarQube analysis failure
104
+ if: steps.sonar.outcome != 'success'
105
+ run: |
106
+ echo "::warning title=SonarQube main-branch analysis did not complete::Step outcome was '${{ steps.sonar.outcome }}'. The project dashboard may be stale — see the SonarQube analysis step log."
package/.nvmrc ADDED
@@ -0,0 +1 @@
1
+ 18
package/README.md CHANGED
@@ -85,3 +85,12 @@ Publish the package to npm
85
85
  ```
86
86
  npm publish
87
87
  ```
88
+
89
+ ## AIPDLC (SurveyServ)
90
+
91
+ This repo is onboarded to AIPDLC (Freshworks AI Product Development Lifecycle) for the SurveyServ team.
92
+
93
+ - Run AIPDLC phases with the SurveyServ scope, e.g. `/fw-system-design --scope=bu/surveyserv`.
94
+ - `/fw-init` generates a local `.aipdlc/workspace.json` pointer; it is **gitignored** (per-developer, per-epic state) — run `/fw-init` locally to (re)create it.
95
+ - Team-wide AIPDLC customization (dev/deploy overlays, rules) lives in `freshworks/ai-builder-artifacts` under `bu/platform/modules/surveyserv/`.
96
+ - This package pins **Node 18** (see `.nvmrc`; Storybook 8 requires Node ≥18) — run `nvm use` before installing.
@@ -0,0 +1,33 @@
1
+ # Prism (Backstage) software catalog descriptor.
2
+ # Registers this repo as the shared UI component library of the SurveyServ
3
+ # system. The `platforms/surveyserv` System entity itself is declared in the
4
+ # freshdesk/surveyserv repo (see PR #5448) — not redeclared here to avoid a
5
+ # duplicate-entity conflict in the catalog.
6
+ # Schema: `get_catalog_schema` in the Prism MCP server is the source of truth.
7
+ apiVersion: backstage.io/v1alpha1
8
+ kind: Component
9
+ metadata:
10
+ name: freemium-ui
11
+ namespace: platforms
12
+ title: Freemium UI
13
+ description: >-
14
+ Lightweight, Styled-Components-based React UI component library shared
15
+ across SurveyServ/Freemium frontends. Published as an npm package.
16
+ annotations:
17
+ github.com/project-slug: freshdesk/freemium-ui
18
+ backstage.io/techdocs-ref: dir:./tech-docs
19
+ tags:
20
+ - react
21
+ - library
22
+ - typescript
23
+ - ui-components
24
+ - styled-components
25
+ labels:
26
+ exposure: internal
27
+ language: TypeScript
28
+ orgVersion: v1alpha1
29
+ spec:
30
+ type: library
31
+ lifecycle: production
32
+ owner: platforms/plat-maise-surveyserv
33
+ system: platforms/surveyserv
@@ -0,0 +1,11 @@
1
+ # ADRs
2
+
3
+ No architecture decision records exist in this repository (no `adrs/` source files documenting an actual decision, and no dedicated ADR pages found in Confluence for `freemium-ui` specifically — this page and the empty `adrs/` scaffold directory are onboarding artifacts, not ADRs themselves). The `adrs/` directory at the repo root is scaffolding for future ADR source files — currently empty.
4
+
5
+ Key architectural decisions that _have_ been made but are only recorded implicitly in code/config (candidates for a retroactive ADR if the team wants one):
6
+
7
+ - Externalizing only `react`/`react-dom` at build time and bundling every other dependency (`rollup.config.ts`'s `external` array) — see [Deep Dive → Dependencies & coupling](../architecture/deep-dive.md#3-dependencies--coupling).
8
+ - Adopting Radix UI primitives + a shared shadow-DOM-aware portal pattern (`usePortalContainer`/`useShadowDomScrollFix`) for every overlay component — see [Deep Dive → Architecture & control flow](../architecture/deep-dive.md#1-architecture--control-flow).
9
+ - Adding a third `dew`/`DewTheme` theme, driven by CSS variables from `@freshworks/dew-styles`, alongside the original `light`/`dark` themes — see the Confluence "Reskinning Project - Freshworks Dew 2.0 Design System" page and [Deep Dive → Idiomatic vernacular](../architecture/deep-dive.md#4-idiomatic-vernacular).
10
+
11
+ No ADRs recorded yet.