diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..e25469f --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,6 @@ +# These organizations support the ILN Frontend project +github: [Invoice-Liquidity-Network] + +# Drips Network Wave funding +custom: + - https://drips.network/waves diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 0000000..07154bd --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,67 @@ +# Automatic PR labeling based on changed file paths +# Labels are applied when a PR touches files in the specified paths + +ci: + - .github/workflows/**/* + - .github/labeler.yml + - .github/dependabot.yml + - .github/CODEOWNERS + - .husky/**/* + - .gitcliff.toml + - eslint.config.mjs + - prettierrc + - vitest.config.ts + - playwright.config.ts + - stryker.conf.json + - .lighthouserc.json + +documentation: + - docs/**/* + - README.md + - CONTRIBUTING.md + - DESIGN.md + - SECURITY.md + - CHANGELOG.md + - PR_NEW.md + +testing: + - __tests__/**/* + - __tests__/**/e2e/**/* + - e2e/**/* + - vitest.setup.ts + - vitest.shims.d.ts + +ui: + - src/components/**/* + - src/app/**/page.tsx + - src/app/**/layout.tsx + - styles/**/* + +hooks: + - src/hooks/**/* + - src/context/**/* + +contracts: + - src/lib/soroban.ts + - src/lib/horizon.ts + - src/lib/indexer-websocket.ts + - src/lib/contract/**/* + - src/lib/invoice-nft.ts + +api: + - app/api/**/* + +utils: + - src/utils/**/* + - src/lib/**/* # (excluding contract-specific files above) + +types: + - src/types/**/* + - src/constants.ts + +dependencies: + - package.json + - pnpm-lock.yaml + - tsconfig.json + - next.config.ts + - postcss.config.mjs diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml new file mode 100644 index 0000000..db13c22 --- /dev/null +++ b/.github/workflows/pr-labeler.yml @@ -0,0 +1,26 @@ +name: Pull Request Labeler + +on: + pull_request_target: + types: [opened, edited, synchronize, reopened] + +permissions: + contents: read + pull-requests: write + +jobs: + labeler: + name: Apply labels based on changed files + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Run Labeler + uses: actions/labeler@v5 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + configuration-path: .github/labeler.yml + sync-labels: true diff --git a/.github/workflows/stale-assignments.yml b/.github/workflows/stale-assignments.yml new file mode 100644 index 0000000..8585f14 --- /dev/null +++ b/.github/workflows/stale-assignments.yml @@ -0,0 +1,142 @@ +name: Stale Assignment Reclaimer + +on: + schedule: + - cron: '0 0 * * *' + workflow_dispatch: + inputs: + warning_days: + description: 'Days before the warning comment is posted' + required: false + default: '7' + reclaim_days: + description: 'Days before the assignee is removed' + required: false + default: '14' + +permissions: + issues: write + pull-requests: read + +env: + WARNING_DAYS: ${{ github.event.inputs.warning_days || vars.STALE_ASSIGNMENT_WARNING_DAYS || '7' }} + RECLAIM_DAYS: ${{ github.event.inputs.reclaim_days || vars.STALE_ASSIGNMENT_RECLAIM_DAYS || '14' }} + WARNING_LABEL: ${{ vars.STALE_ASSIGNMENT_WARNING_LABEL || 'stale-assignment-warning' }} + RECLAIM_LABEL: ${{ vars.STALE_ASSIGNMENT_RECLAIM_LABEL || 'assignment-reclaimed' }} + WARNING_MESSAGE: ${{ vars.STALE_ASSIGNMENT_WARNING_MESSAGE || '⚠️ **Stale Assignment Warning**\n\nThis issue has been assigned to @assignees for X days without linked PR activity. Please keep the assignment active by opening or updating a linked PR, or unassign yourself if you are no longer working on it.\n\nIf no activity is detected within Y days, the assignment will be automatically removed.' }} + RECLAIM_MESSAGE: ${{ vars.STALE_ASSIGNMENT_RECLAIM_MESSAGE || '🔄 **Assignment Reclaimed**\n\nThis issue had been assigned without linked PR activity for X days, so the assignment has been automatically removed. If you were still working on it, please re-assign yourself and keep the PR in motion.' }} + +jobs: + stale-assignments: + name: Reclaim abandoned issue assignments + runs-on: ubuntu-latest + steps: + - name: Reclaim stale assignments + uses: actions/github-script@v7 + with: + script: | + const owner = context.repo.owner; + const repo = context.repo.repo; + const now = Date.now(); + const warningDays = Number(process.env.WARNING_DAYS || '7'); + const reclaimDays = Number(process.env.RECLAIM_DAYS || '14'); + const warningLabel = process.env.WARNING_LABEL || 'stale-assignment-warning'; + const reclaimLabel = process.env.RECLAIM_LABEL || 'assignment-reclaimed'; + const warningTemplate = (process.env.WARNING_MESSAGE || '').trim(); + const reclaimTemplate = (process.env.RECLAIM_MESSAGE || '').trim(); + + const issues = await github.paginate(github.rest.issues.listForRepo, { + owner, + repo, + state: 'open', + assignee: '*', + per_page: 100 + }); + + const allPullRequests = await github.paginate(github.rest.pulls.list, { + owner, + repo, + state: 'all', + per_page: 100 + }); + + for (const issue of issues) { + const assignees = issue.assignees.map((assignee) => assignee.login); + if (assignees.length === 0) continue; + + const linkedPullRequests = allPullRequests.filter((pullRequest) => { + const bodyText = `${pullRequest.title ?? ''}\n${pullRequest.body ?? ''}`; + return bodyText.includes(`#${issue.number}`); + }); + + const lastLinkedActivity = linkedPullRequests.length + ? linkedPullRequests.reduce((latestTimestamp, pullRequest) => { + const updatedAt = new Date(pullRequest.updated_at ?? pullRequest.created_at ?? 0).getTime(); + return updatedAt > latestTimestamp ? updatedAt : latestTimestamp; + }, 0) + : 0; + + const lastActivityAt = Math.max( + new Date(issue.updated_at ?? issue.created_at ?? 0).getTime(), + lastLinkedActivity + ); + const daysSinceLastActivity = Math.floor((now - lastActivityAt) / (1000 * 60 * 60 * 24)); + + const hasWarningLabel = issue.labels.some((label) => label.name === warningLabel); + const hasReclaimLabel = issue.labels.some((label) => label.name === reclaimLabel); + + if (daysSinceLastActivity >= warningDays && !hasWarningLabel && !hasReclaimLabel) { + const warningBody = warningTemplate + .replace(/@assignees/g, assignees.map((login) => `@${login}`).join(', ')) + .replace(/X days/g, `${daysSinceLastActivity} days`) + .replace(/Y days/g, `${Math.max(reclaimDays - warningDays, 0)} days`); + + await github.rest.issues.createComment({ + owner, + repo, + issue_number: issue.number, + body: warningBody + }); + + await github.rest.issues.addLabels({ + owner, + repo, + issue_number: issue.number, + labels: [warningLabel] + }); + } + + if (daysSinceLastActivity >= reclaimDays && !hasReclaimLabel) { + const reclaimBody = reclaimTemplate + .replace(/@assignees/g, assignees.map((login) => `@${login}`).join(', ')) + .replace(/X days/g, `${daysSinceLastActivity} days`); + + await github.rest.issues.createComment({ + owner, + repo, + issue_number: issue.number, + body: reclaimBody + }); + + await github.rest.issues.removeAssignees({ + owner, + repo, + issue_number: issue.number, + assignees + }); + + await github.rest.issues.removeLabel({ + owner, + repo, + issue_number: issue.number, + name: warningLabel + }).catch(() => {}); + + await github.rest.issues.addLabels({ + owner, + repo, + issue_number: issue.number, + labels: [reclaimLabel] + }); + } + } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 57a72fb..463f20b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -240,6 +240,12 @@ This command generates: - Storybook file: `src/components/.stories.tsx` - Test stub file: `src/components/__tests__/.test.tsx` (or inside the target subdirectory) +### Issue and PR Assignment Policy + +Issues that are assigned to a contributor are expected to move forward promptly. If an issue remains assigned without a linked PR update for 7 days, the repository automation will post a reminder comment. If the issue still shows no linked PR activity after 14 days, the assignee is automatically removed so the issue can be claimed by someone else. + +The thresholds and message text are configurable through the workflow inputs and repository variables used by [.github/workflows/stale-assignments.yml](.github/workflows/stale-assignments.yml). Contributors should keep assignments current, open or update a linked PR early, and unassign themselves if they can no longer work on the issue. + ### Code Style and Formatting We use **ESLint** and **Prettier** to maintain consistent code quality. @@ -897,6 +903,44 @@ When you open a PR, GitHub will automatically suggest reviewers based on the fil If you become a regular contributor to a specific area of the codebase, you can request to be added as a code owner. Contact a maintainer to discuss this. +## Stale Assignment Policy + +To maintain effective Wave throughput and ensure issues don't get claimed and abandoned, this repository uses an automated stale assignment reclaimer. + +### How It Works + +The stale assignment bot runs daily and monitors assigned issues: + +1. **Warning Stage (7 days of inactivity)** + - If an issue has been assigned for 7+ days with no linked PR activity, a warning comment is added + - The issue is labeled with `stale-assignment-warning` + - The assignee is notified with instructions to either: + - Open a draft PR + - Comment with a progress update + - Unassign themselves if no longer working on it + +2. **Reclaim Stage (14 days of inactivity)** + - If no activity is detected for 14+ days, the assignment is automatically removed + - The issue is labeled with `assignment-reclaimed` + - Other contributors can then claim the issue + +### Configuration + +The timeout periods are configurable in `.github/workflows/stale-assignments.yml`: +- `WARNING_DAYS`: Days before warning comment (default: 7) +- `RECLAIM_DAYS`: Days before unassignment (default: 14) + +### For Contributors + +- **When claiming an issue**: Open a draft PR within 7 days to show active work +- **If you need more time**: Comment on the issue with a progress update to reset the timer +- **If you can't complete it**: Unassign yourself promptly so others can claim it +- **After reclamation**: If your assignment was reclaimed but you're still working on it, re-assign yourself and open a PR promptly + +### Exemptions + +Issues with linked open PRs are automatically exempt from the stale assignment check. + ## Code of Conduct Please be respectful and constructive in all interactions. We aim to create a welcoming environment for all contributors. diff --git a/README.md b/README.md index f042304..2c20f6c 100644 --- a/README.md +++ b/README.md @@ -256,6 +256,7 @@ The analytics view brings together yield, volume, and market trends for deeper p ## 🔗 Useful Links & Documentation +- **Documentation Index**: Browse all project documentation in [docs/README.md](docs/README.md). - **Getting Started Guide**: Refer to the [Quick Start](#-quick-start) section. - **Developer Quickstart**: Follow the full setup guide in [docs/developer-quickstart.md](docs/developer-quickstart.md). - **Troubleshooting Guide**: Consolidated list of local setup symptoms, Freighter connection, and database gotchas in [docs/troubleshooting.md](docs/troubleshooting.md). diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..774da36 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,37 @@ +# Documentation Index + +This directory contains the main contributor and operations documentation for the ILN Frontend project. Use this page as the starting point when you need context on architecture, testing, CI, accessibility, or implementation details. + +## Core Guides + +- **[architecture.md](architecture.md)** - Frontend architecture overview covering design decisions, folder structure, and major data-flow patterns. +- **[developer-quickstart.md](developer-quickstart.md)** - End-to-end setup guide from a fresh clone through local development and initial verification. +- **[testing.md](testing.md)** - Testing strategy and conventions for Vitest, Playwright, and other quality checks. +- **[ci-cd.md](ci-cd.md)** - GitHub Actions, deployment flow, and CI/CD runner guidance. + +## Quality and Performance + +- **[LIGHTHOUSE_CI.md](LIGHTHOUSE_CI.md)** - Lighthouse CI performance budgets, thresholds, and report review guidance. +- **[VISUAL_REGRESSION_WORKFLOW.md](VISUAL_REGRESSION_WORKFLOW.md)** - Chromatic visual regression workflow and approval process. +- **[accessibility-audit-toast-notifications.md](accessibility-audit-toast-notifications.md)** - Accessibility audit notes for toast and notification flows. +- **[accessibility-implementation-summary.md](accessibility-implementation-summary.md)** - Summary of accessibility implementation work and supporting details. +- **[screen-reader-testing-guide.md](screen-reader-testing-guide.md)** - Manual testing checklist for screen-reader and keyboard accessibility. + +## Product and Domain Documentation + +- **[api-routes.md](api-routes.md)** - Reference for the API routes that power the frontend experience. +- **[contract-fixtures.md](contract-fixtures.md)** - Fixture and integration-test context for Stellar contract interactions. +- **[error-codes.md](error-codes.md)** - Error code catalog and troubleshooting notes. +- **[feature-flags.md](feature-flags.md)** - Overview of feature flags and current rollout settings. +- **[i18n.md](i18n.md)** - Internationalization setup and translation workflow details. +- **[repo-size-audit.md](repo-size-audit.md)** - Repository size analysis and optimization recommendations. +- **[supabase-setup.md](supabase-setup.md)** - Supabase configuration and local setup notes. + +## Hooks and Examples + +- **[hooks/](hooks/)** - Detailed documentation for custom React hooks, including the authenticated-wallet hook reference. +- **[examples/](examples/)** - Example assets and snippets used across the documentation set. + +--- + +Keep this index updated whenever new documentation is added to the docs directory so contributors can find it quickly.