From a66393c8c5cda8fb64b7917ce5a49913f2eb050e Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 16:10:32 +0100 Subject: [PATCH 01/23] Add aria-label to Twitter share button --- frontend/src/components/ShareModal.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/components/ShareModal.tsx b/frontend/src/components/ShareModal.tsx index 8271b934..c761378e 100644 --- a/frontend/src/components/ShareModal.tsx +++ b/frontend/src/components/ShareModal.tsx @@ -113,6 +113,7 @@ export function ShareModal({ )} From 9d8b7b8d80436b3602b3f01a74cbdc5f63dbbd09 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 16:16:58 +0100 Subject: [PATCH 07/23] Add aria-label to remove drawing button --- frontend/src/components/DrawingToolsPanel.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/components/DrawingToolsPanel.tsx b/frontend/src/components/DrawingToolsPanel.tsx index 2bac50ab..58961eb7 100644 --- a/frontend/src/components/DrawingToolsPanel.tsx +++ b/frontend/src/components/DrawingToolsPanel.tsx @@ -98,6 +98,7 @@ export function DrawingToolsPanel({ e.stopPropagation(); onRemoveTool(tool.id); }} + aria-label={`Remove ${tool.type} drawing`} > × From 65ec8cb264f291567dfe43f790b5c98fcf1ebed4 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 16:17:34 +0100 Subject: [PATCH 08/23] Add aria-labels to undo and redo buttons --- frontend/src/components/DrawingToolsPanel.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/DrawingToolsPanel.tsx b/frontend/src/components/DrawingToolsPanel.tsx index 58961eb7..11c02cc4 100644 --- a/frontend/src/components/DrawingToolsPanel.tsx +++ b/frontend/src/components/DrawingToolsPanel.tsx @@ -176,16 +176,16 @@ export function DrawingToolbar({ {isDrawing && (
- {onUndo && ( - )} {onRedo && ( - )} From 9f53de8cc71faffb9436d5ae7b10800f15cc488b Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 16:33:54 +0100 Subject: [PATCH 09/23] Add aria-label to close notifications button --- frontend/src/components/NotificationCenter.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/components/NotificationCenter.tsx b/frontend/src/components/NotificationCenter.tsx index d5b6fcb6..21c96c10 100644 --- a/frontend/src/components/NotificationCenter.tsx +++ b/frontend/src/components/NotificationCenter.tsx @@ -82,6 +82,7 @@ export default function NotificationCenter({ From d1b3436bbf094b2d855675e7df2a4d29306295c0 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 17:03:25 +0100 Subject: [PATCH 10/23] Add aria-labels and aria-pressed to filter buttons --- frontend/src/components/NotificationCenter.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frontend/src/components/NotificationCenter.tsx b/frontend/src/components/NotificationCenter.tsx index 21c96c10..3c9842b6 100644 --- a/frontend/src/components/NotificationCenter.tsx +++ b/frontend/src/components/NotificationCenter.tsx @@ -101,6 +101,8 @@ export default function NotificationCenter({ ? 'bg-blue-600 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200' }`} + aria-label="Show all notifications" + aria-pressed={filterStatus === 'all'} > All @@ -111,6 +113,8 @@ export default function NotificationCenter({ ? 'bg-blue-600 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200' }`} + aria-label="Show unread notifications" + aria-pressed={filterStatus === 'unread'} > Unread @@ -121,6 +125,8 @@ export default function NotificationCenter({ ? 'bg-blue-600 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200' }`} + aria-label="Show read notifications" + aria-pressed={filterStatus === 'read'} > Read From f0eff166fd5b8d44e90eeed75b4f63897e81393b Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 17:04:41 +0100 Subject: [PATCH 11/23] Add aria-label to close button in proposal modal --- frontend/src/components/CreateProposalModal.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/components/CreateProposalModal.tsx b/frontend/src/components/CreateProposalModal.tsx index bb2b9825..c17f67c7 100644 --- a/frontend/src/components/CreateProposalModal.tsx +++ b/frontend/src/components/CreateProposalModal.tsx @@ -148,6 +148,7 @@ export function CreateProposalModal({ cursor: 'pointer', padding: '4px', }} + aria-label="Close modal" > × From 2e01d2a256a3ab0ea6e3ac028bd573bb8f8ff2e1 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 17:05:21 +0100 Subject: [PATCH 12/23] Add aria-labels to add and remove liquidity buttons --- frontend/src/components/PoolPositionRow.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/components/PoolPositionRow.tsx b/frontend/src/components/PoolPositionRow.tsx index ed039776..44bf7747 100644 --- a/frontend/src/components/PoolPositionRow.tsx +++ b/frontend/src/components/PoolPositionRow.tsx @@ -85,6 +85,7 @@ export function PoolPositionRow({ onClick={onAddLiquidity} className="px-3 py-1.5 text-xs font-medium text-emerald-400 bg-emerald-500/10 rounded-lg hover:bg-emerald-500/20 transition-colors" title="Add liquidity" + aria-label="Add liquidity to pool" > + @@ -94,6 +95,7 @@ export function PoolPositionRow({ onClick={onRemoveLiquidity} className="px-3 py-1.5 text-xs font-medium text-red-400 bg-red-500/10 rounded-lg hover:bg-red-500/20 transition-colors" title="Remove liquidity" + aria-label="Remove liquidity from pool" > − From 02cc2a6b839da9bdfb2bc33a81d430c48b897afe Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 17:06:38 +0100 Subject: [PATCH 13/23] Add documentation for ARIA label improvements --- frontend/docs/ACCESSIBILITY_ARIA_LABELS.md | 95 ++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 frontend/docs/ACCESSIBILITY_ARIA_LABELS.md diff --git a/frontend/docs/ACCESSIBILITY_ARIA_LABELS.md b/frontend/docs/ACCESSIBILITY_ARIA_LABELS.md new file mode 100644 index 00000000..d020cf0e --- /dev/null +++ b/frontend/docs/ACCESSIBILITY_ARIA_LABELS.md @@ -0,0 +1,95 @@ +# ARIA Labels for Icon-Only Buttons + +## Overview + +This document outlines the accessibility improvements made to icon-only buttons throughout the application. All icon-only buttons now include descriptive `aria-label` attributes to ensure screen reader users can understand their purpose. + +## WCAG Compliance + +These changes address WCAG 2.1 Level A compliance, specifically: +- **1.1.1 Non-text Content**: All non-text content has a text alternative that serves the equivalent purpose +- **4.1.2 Name, Role, Value**: User interface components have names that can be programmatically determined + +## Components Updated + +### ShareModal.tsx +- Twitter share button: `aria-label="Share on Twitter"` +- Discord share button: `aria-label="Share on Discord"` +- Telegram share button: `aria-label="Share on Telegram"` +- Reddit share button: `aria-label="Share on Reddit"` + +### MobileBottomSheet.tsx +- Snap point buttons: `aria-label="Snap to position {n}"` with `aria-pressed` state +- Close button: Already had proper `aria-label="Close"` + +### DrawingToolsPanel.tsx +- Clear all button: `aria-label="Clear all drawings"` +- Remove drawing button: `aria-label="Remove {type} drawing"` +- Stop drawing button: `aria-label="Stop drawing"` +- Undo button: `aria-label="Undo last action"` +- Redo button: `aria-label="Redo last action"` + +### NotificationCenter.tsx +- Close button: `aria-label="Close notifications"` +- Filter buttons: + - All: `aria-label="Show all notifications"` with `aria-pressed` + - Unread: `aria-label="Show unread notifications"` with `aria-pressed` + - Read: `aria-label="Show read notifications"` with `aria-pressed` + +### CreateProposalModal.tsx +- Close button: `aria-label="Close modal"` + +### PoolPositionRow.tsx +- Add liquidity button: `aria-label="Add liquidity to pool"` +- Remove liquidity button: `aria-label="Remove liquidity from pool"` + +## Best Practices + +### When to Use aria-label + +Use `aria-label` when: +1. A button contains only an icon (no visible text) +2. A button contains only a symbol (×, +, −, etc.) +3. The visible text is not descriptive enough + +### When to Use aria-pressed + +Use `aria-pressed` for toggle buttons that have an on/off state: +```tsx + +``` + +### When to Use aria-hidden + +Use `aria-hidden="true"` on decorative icons within buttons that have text labels: +```tsx + +``` + +## Testing + +### Manual Testing +1. Use a screen reader (NVDA, JAWS, VoiceOver) to navigate the application +2. Tab through all interactive elements +3. Verify that each button announces its purpose clearly + +### Automated Testing +Run accessibility audits using: +- axe DevTools +- Lighthouse accessibility audit +- WAVE browser extension + +## Future Improvements + +1. Add keyboard shortcuts for common actions +2. Implement focus management for modals +3. Add live regions for dynamic content updates +4. Ensure all interactive elements have visible focus indicators From 90fc7a98f6ea573d80c4f44830782a8a9bcf6a60 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 17:07:15 +0100 Subject: [PATCH 14/23] Add accessibility tests for ARIA labels --- .../__tests__/accessibility.test.tsx | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 frontend/src/components/__tests__/accessibility.test.tsx diff --git a/frontend/src/components/__tests__/accessibility.test.tsx b/frontend/src/components/__tests__/accessibility.test.tsx new file mode 100644 index 00000000..4391bdc5 --- /dev/null +++ b/frontend/src/components/__tests__/accessibility.test.tsx @@ -0,0 +1,106 @@ +import { describe, it, expect } from 'vitest'; +import { render, screen } from '@testing-library/react'; +import { ShareModal } from '../ShareModal'; +import { MobileBottomSheet } from '../MobileBottomSheet'; +import { NotificationCenter } from '../NotificationCenter'; + +describe('Accessibility - ARIA Labels', () => { + describe('ShareModal', () => { + it('should have aria-label on close button', () => { + render( + {}} + marketId={1} + /> + ); + + const closeButton = screen.getByLabelText('Close'); + expect(closeButton).toBeInTheDocument(); + }); + + it('should have aria-labels on social share buttons', () => { + render( + {}} + marketId={1} + /> + ); + + expect(screen.getByLabelText('Share on Twitter')).toBeInTheDocument(); + expect(screen.getByLabelText('Share on Discord')).toBeInTheDocument(); + expect(screen.getByLabelText('Share on Telegram')).toBeInTheDocument(); + expect(screen.getByLabelText('Share on Reddit')).toBeInTheDocument(); + }); + }); + + describe('MobileBottomSheet', () => { + it('should have aria-label on close button', () => { + render( + {}} + title="Test Sheet" + > +
Content
+
+ ); + + const closeButton = screen.getByLabelText('Close'); + expect(closeButton).toBeInTheDocument(); + }); + + it('should have aria-labels on snap point buttons', () => { + render( + {}} + snapPoints={[0.5, 0.9]} + > +
Content
+
+ ); + + expect(screen.getByLabelText('Snap to position 1')).toBeInTheDocument(); + expect(screen.getByLabelText('Snap to position 2')).toBeInTheDocument(); + }); + }); + + describe('NotificationCenter', () => { + it('should have aria-label on close button', () => { + render( + {}} + /> + ); + + const closeButton = screen.getByLabelText('Close notifications'); + expect(closeButton).toBeInTheDocument(); + }); + + it('should have aria-labels on filter buttons', () => { + render( + + ); + + expect(screen.getByLabelText('Show all notifications')).toBeInTheDocument(); + expect(screen.getByLabelText('Show unread notifications')).toBeInTheDocument(); + expect(screen.getByLabelText('Show read notifications')).toBeInTheDocument(); + }); + + it('should have aria-pressed on active filter button', () => { + render( + + ); + + const allButton = screen.getByLabelText('Show all notifications'); + expect(allButton).toHaveAttribute('aria-pressed', 'true'); + }); + }); +}); From 07155cf3f756882ff4b350f314c2d9abd0b04d16 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 17:07:49 +0100 Subject: [PATCH 15/23] Update accessibility checklist with icon button fixes --- docs/ACCESSIBILITY_TESTING_CHECKLIST.md | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/docs/ACCESSIBILITY_TESTING_CHECKLIST.md b/docs/ACCESSIBILITY_TESTING_CHECKLIST.md index 3106f973..5bebd959 100644 --- a/docs/ACCESSIBILITY_TESTING_CHECKLIST.md +++ b/docs/ACCESSIBILITY_TESTING_CHECKLIST.md @@ -1,7 +1,26 @@ -# Accessibility Testing Checklist - Issue #160 +# Accessibility Testing Checklist ## Overview -Use this checklist to verify that aria-describedby attributes are working correctly across all form components. +This checklist covers accessibility testing for form components (Issue #160) and icon-only buttons (Issue #158). + +## Issue #158: Icon-Only Buttons ARIA Labels + +### Components Fixed +- [x] ShareModal - Social share buttons +- [x] MobileBottomSheet - Snap point buttons +- [x] DrawingToolsPanel - Tool action buttons +- [x] NotificationCenter - Filter and close buttons +- [x] CreateProposalModal - Close button +- [x] PoolPositionRow - Liquidity action buttons + +### Testing Requirements +- [ ] All icon-only buttons have descriptive aria-label +- [ ] Toggle buttons have aria-pressed state +- [ ] Decorative icons have aria-hidden="true" +- [ ] Screen readers announce button purpose clearly +- [ ] Button labels are concise and descriptive + +## Issue #160: Form Error Announcements ## General Testing From 4683822c944ebf4f456851bec83152275da3dc4f Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 23:40:10 +0100 Subject: [PATCH 16/23] chore: add required imports for contract read-only function calls - Import callReadOnlyFunction for querying contract state - Import cvToValue for parsing Clarity values - Support read-only operations on migration contract Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- frontend/src/services/MigrationService.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/frontend/src/services/MigrationService.ts b/frontend/src/services/MigrationService.ts index 04e5e5ce..cd5623a1 100644 --- a/frontend/src/services/MigrationService.ts +++ b/frontend/src/services/MigrationService.ts @@ -1,5 +1,13 @@ import { openContractCall } from '@stacks/connect'; -import { uintCV, stringUtf8CV, bufferCV, boolCV, PostConditionMode } from '@stacks/transactions'; +import { + uintCV, + stringUtf8CV, + bufferCV, + boolCV, + PostConditionMode, + callReadOnlyFunction, + cvToValue, +} from '@stacks/transactions'; export interface Migration { migrationId: number; From 6bb25a4e79aacdf2b24f3fd23577194f4ce754d6 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 23:40:16 +0100 Subject: [PATCH 17/23] refactor: add network parameter to MigrationService constructor - Support optional network configuration for contract operations - Enable flexibility in test and production environments - Store network for use in read-only function calls Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- frontend/src/services/MigrationService.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/services/MigrationService.ts b/frontend/src/services/MigrationService.ts index cd5623a1..32c6e1c0 100644 --- a/frontend/src/services/MigrationService.ts +++ b/frontend/src/services/MigrationService.ts @@ -26,9 +26,11 @@ export interface MigrationData { export class MigrationService { private migrationContract: { address: string; name: string }; + private network: any; - constructor(migrationContract: { address: string; name: string }) { + constructor(migrationContract: { address: string; name: string }, network?: any) { this.migrationContract = migrationContract; + this.network = network; } async registerMigration( From ff3c074eeefc4f1455dc190c2a1d58de8dbbe5d2 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 23:40:21 +0100 Subject: [PATCH 18/23] refactor: add helper method for contract address parsing - Extract base address from contract name format (address.contract-name) - Handle both qualified and unqualified contract addresses - Support flexibility in contract address specification Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- frontend/src/services/MigrationService.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frontend/src/services/MigrationService.ts b/frontend/src/services/MigrationService.ts index 32c6e1c0..5ab3fdd0 100644 --- a/frontend/src/services/MigrationService.ts +++ b/frontend/src/services/MigrationService.ts @@ -102,6 +102,13 @@ export class MigrationService { return 1; } + private extractAddress(addressInput: string): string { + if (addressInput.includes('.')) { + return addressInput.split('.')[0]; + } + return addressInput; + } + async getMigration(migrationId: number): Promise { return null; } From d47bf6219660c5ee98f6f9299cce5227c568e93c Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 23:40:27 +0100 Subject: [PATCH 19/23] feat: implement getCurrentVersion method with contract call - Query get-current-version read-only function from migration contract - Parse returned Clarity value and convert to number - Fallback to version 1 on error or invalid response - Add comprehensive error handling and logging Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- frontend/src/services/MigrationService.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/frontend/src/services/MigrationService.ts b/frontend/src/services/MigrationService.ts index 5ab3fdd0..8eba6fc9 100644 --- a/frontend/src/services/MigrationService.ts +++ b/frontend/src/services/MigrationService.ts @@ -99,7 +99,26 @@ export class MigrationService { } async getCurrentVersion(): Promise { - return 1; + try { + const contractAddress = this.extractAddress(this.migrationContract.address); + const response = await callReadOnlyFunction({ + contractAddress, + contractName: this.migrationContract.name, + functionName: 'get-current-version', + functionArgs: [], + network: this.network, + senderAddress: contractAddress, + }); + + if (response.ok && typeof response.value === 'object') { + const value = cvToValue(response.value); + return typeof value === 'number' ? value : 1; + } + return 1; + } catch (error) { + console.error('Failed to get current version:', error); + return 1; + } } private extractAddress(addressInput: string): string { From eda1f297830aa9177fc2fa24f975c4d922e25335 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 23:40:34 +0100 Subject: [PATCH 20/23] feat: implement getMigration method to query migration details - Call get-migration read-only function with migration ID parameter - Parse response and map contract fields to Migration interface - Handle optional fields (executedAt, executedBy) - Support snake_case contract field names - Return null if migration not found or on error Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- frontend/src/services/MigrationService.ts | 32 ++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/frontend/src/services/MigrationService.ts b/frontend/src/services/MigrationService.ts index 8eba6fc9..6704106b 100644 --- a/frontend/src/services/MigrationService.ts +++ b/frontend/src/services/MigrationService.ts @@ -129,7 +129,37 @@ export class MigrationService { } async getMigration(migrationId: number): Promise { - return null; + try { + const contractAddress = this.extractAddress(this.migrationContract.address); + const response = await callReadOnlyFunction({ + contractAddress, + contractName: this.migrationContract.name, + functionName: 'get-migration', + functionArgs: [uintCV(migrationId)], + network: this.network, + senderAddress: contractAddress, + }); + + if (response.ok && response.value) { + const result = cvToValue(response.value); + if (result && typeof result === 'object') { + const migration = result as any; + return { + migrationId, + version: migration.version || 0, + description: migration.description || '', + executed: migration.executed || false, + executedAt: migration['executed-at'] || undefined, + executedBy: migration['executed-by'] || undefined, + rollbackAvailable: migration['rollback-available'] || false, + }; + } + } + return null; + } catch (error) { + console.error(`Failed to get migration ${migrationId}:`, error); + return null; + } } async getMigrationData(migrationId: number): Promise { From 79fe6e1dd2d7900bca1ffebb0829094323e4527b Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 23:40:40 +0100 Subject: [PATCH 21/23] feat: implement getMigrationData method for data access - Query get-migration-data read-only function with migration ID - Extract data hash and size from contract response - Map contract response fields to MigrationData interface - Return empty Uint8Array and 0 for missing values - Return null if data not found or on error Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- frontend/src/services/MigrationService.ts | 27 ++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/frontend/src/services/MigrationService.ts b/frontend/src/services/MigrationService.ts index 6704106b..6b3d1a43 100644 --- a/frontend/src/services/MigrationService.ts +++ b/frontend/src/services/MigrationService.ts @@ -163,7 +163,32 @@ export class MigrationService { } async getMigrationData(migrationId: number): Promise { - return null; + try { + const contractAddress = this.extractAddress(this.migrationContract.address); + const response = await callReadOnlyFunction({ + contractAddress, + contractName: this.migrationContract.name, + functionName: 'get-migration-data', + functionArgs: [uintCV(migrationId)], + network: this.network, + senderAddress: contractAddress, + }); + + if (response.ok && response.value) { + const result = cvToValue(response.value); + if (result && typeof result === 'object') { + const data = result as any; + return { + dataHash: data['data-hash'] || new Uint8Array(), + dataSize: data['data-size'] || 0, + }; + } + } + return null; + } catch (error) { + console.error(`Failed to get migration data ${migrationId}:`, error); + return null; + } } async isMigrationExecuted(migrationId: number): Promise { From adb1e516d6242795ee6e9bd5a889088e367f9ad1 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 23:40:45 +0100 Subject: [PATCH 22/23] feat: implement isMigrationExecuted method for status checking - Query is-migration-executed read-only function - Check migration execution status on contract - Return boolean indicating if migration has been executed - Fallback to false on error or invalid response - Add error handling and logging for execution status checks Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- frontend/src/services/MigrationService.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/frontend/src/services/MigrationService.ts b/frontend/src/services/MigrationService.ts index 6b3d1a43..57ed908d 100644 --- a/frontend/src/services/MigrationService.ts +++ b/frontend/src/services/MigrationService.ts @@ -192,7 +192,26 @@ export class MigrationService { } async isMigrationExecuted(migrationId: number): Promise { - return false; + try { + const contractAddress = this.extractAddress(this.migrationContract.address); + const response = await callReadOnlyFunction({ + contractAddress, + contractName: this.migrationContract.name, + functionName: 'is-migration-executed', + functionArgs: [uintCV(migrationId)], + network: this.network, + senderAddress: contractAddress, + }); + + if (response.ok && typeof response.value === 'object') { + const value = cvToValue(response.value); + return value === true; + } + return false; + } catch (error) { + console.error(`Failed to check migration execution ${migrationId}:`, error); + return false; + } } async getMigrationCount(): Promise { From 4033e2bc67ab58822f128c7ff1c7335ac0c8bb00 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 23:40:51 +0100 Subject: [PATCH 23/23] feat: implement getMigrationCount method to query total migrations - Query get-migration-count read-only function - Return total number of migrations registered in contract - Parse Clarity integer response and convert to JavaScript number - Fallback to 0 on error or invalid response - Add error handling and logging Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- frontend/src/services/MigrationService.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/frontend/src/services/MigrationService.ts b/frontend/src/services/MigrationService.ts index 57ed908d..238f5839 100644 --- a/frontend/src/services/MigrationService.ts +++ b/frontend/src/services/MigrationService.ts @@ -215,6 +215,25 @@ export class MigrationService { } async getMigrationCount(): Promise { - return 0; + try { + const contractAddress = this.extractAddress(this.migrationContract.address); + const response = await callReadOnlyFunction({ + contractAddress, + contractName: this.migrationContract.name, + functionName: 'get-migration-count', + functionArgs: [], + network: this.network, + senderAddress: contractAddress, + }); + + if (response.ok && typeof response.value === 'object') { + const value = cvToValue(response.value); + return typeof value === 'number' ? value : 0; + } + return 0; + } catch (error) { + console.error('Failed to get migration count:', error); + return 0; + } } }