diff --git a/Governance/README.md b/Governance/README.md index 9ed9b34c..4fc1fba0 100644 --- a/Governance/README.md +++ b/Governance/README.md @@ -26,7 +26,7 @@ documents are added and refined over time (tracked as issues): - **Decision-making** — consensus and voting rules, the RFC/proposal process, and decision records. - **Community & conduct** — code of conduct, enforcement, moderation, and conflict - resolution. + resolution, including [communication norms](policies/COMMUNICATION_NORMS.md). - **Contribution governance** — review policy, triage, labels, and roadmap governance. - **Security & disclosure** — vulnerability reporting, embargo, and advisory processes. - **Releases & change** — versioning, release cadence, deprecation, and change policy. diff --git a/Governance/policies/COMMUNICATION_NORMS.md b/Governance/policies/COMMUNICATION_NORMS.md new file mode 100644 index 00000000..273885e2 --- /dev/null +++ b/Governance/policies/COMMUNICATION_NORMS.md @@ -0,0 +1,70 @@ +# Community Communication Norms + +**Version:** 1.0 + +## Purpose and Scope + +These norms set expectations for communication among TeachLink Web contributors, +maintainers, and community members. They apply to project-related communication +in the official channels below. They complement the project's conduct and +security policies; they do not replace them. + +## Official Channels + +- **GitHub Issues** are for actionable bug reports, feature requests, and + project questions that need tracking. +- **GitHub Discussions**, when enabled for this repository, are for open-ended + questions, ideas, and community conversation. If Discussions are unavailable, + use an appropriate issue. +- **Pull requests** are for proposing code or documentation changes and keeping + review feedback and decisions with the proposed change. +- **Security reports** must use the repository's private vulnerability + reporting channel when available. Otherwise, contact a maintainer privately. + Do not publish suspected vulnerabilities, exploit details, credentials, or + personal data in an issue, discussion, or pull request. + +Project decisions and actionable outcomes should be recorded in the relevant +repository issue, discussion, or pull request so they remain accessible and +searchable. Do not share private contact details or sensitive information in +public channels. + +## Tone and Etiquette + +- Be respectful, inclusive, and professional. Critique proposals and work, not + the people behind them. +- Be clear and constructive: provide relevant context, describe the impact, and + suggest a next step when practical. +- Assume good faith, while listening carefully when someone explains that a + message or action caused harm. Correct misunderstandings without personal + attacks or escalation. +- Keep discussion focused and accessible. Avoid repeated messages, personal + call-outs, and cross-posting the same request to pressure a faster response. +- Harassment, discrimination, threats, insults, and deliberate disruption are + not acceptable. Moderation and conduct concerns should be raised privately + with a maintainer rather than argued out in a public thread. + +## Response Expectations + +TeachLink Web is maintained across time zones and availability varies. The +following are targets, not guarantees or on-call commitments: + +- A maintainer should aim to acknowledge a new issue or discussion within three + business days. +- A pull request should aim to receive an initial review or status update within + five business days. +- If a response is delayed, contributors should add one concise follow-up to the + original thread after five business days. Avoid repeated pings; maintainers + will update the thread when they can. +- Security reports should be acknowledged promptly through the private channel, + but are handled under the security response process rather than these public + response targets. + +Business-day targets do not require anyone to respond outside their normal +availability. If a request is time-sensitive, explain the reason and impact in +the original thread without implying guaranteed priority. + +## Ownership and Changes + +Maintainers own this policy. Changes are proposed through a pull request that +touches only the `Governance/` folder, in accordance with the governance +contribution rules. \ No newline at end of file diff --git a/package.json b/package.json index 4f688132..880aec8c 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "recharts": "^2.15.4", "socket.io": "^4.8.3", "socket.io-client": "^4.8.3", + "sonner": "^2.0.8", "tailwind-merge": "^2.6.0", "uuid": "^14.0.1", "video.js": "^8.23.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ca5ffc75..a30bf8ca 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -163,6 +163,9 @@ importers: socket.io-client: specifier: ^4.8.3 version: 4.8.3 + sonner: + specifier: ^2.0.8 + version: 2.0.8(@types/react@18.3.29)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) tailwind-merge: specifier: ^2.6.0 version: 2.6.1 @@ -6890,6 +6893,16 @@ packages: sonic-boom@4.2.1: resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} + sonner@2.0.8: + resolution: {integrity: sha512-UM/ByIoFra8yzV75n1o0Puu0bw5U/9UNnDacrJNspekBewIfsQ3D6ez1nvlWpt7aTsO6rujQtifBpycwIivqlg==} + peerDependencies: + '@types/react': ^18.0.0 || ^19.0.0 + react: ^18.3.1 + react-dom: ^18.3.1 + peerDependenciesMeta: + '@types/react': + optional: true + source-list-map@2.0.1: resolution: {integrity: sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==} @@ -15970,6 +15983,13 @@ snapshots: dependencies: atomic-sleep: 1.0.0 + sonner@2.0.8(@types/react@18.3.29)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.29 + source-list-map@2.0.1: {} source-map-js@1.2.1: {} diff --git a/src/components/layout/HeaderComponent.tsx b/src/components/layout/HeaderComponent.tsx index b9bdea10..e9fea3e8 100644 --- a/src/components/layout/HeaderComponent.tsx +++ b/src/components/layout/HeaderComponent.tsx @@ -2,7 +2,7 @@ import { useEffect, useState, useMemo, startTransition } from 'react'; import Link from 'next/link'; -import { toast } from 'sonner'; +import { toast } from 'react-hot-toast'; import { useWallet } from '@/hooks/useWallet'; import { IGrantContext } from '@/types/grants'; import { createLogger } from '@/lib/logging'; diff --git a/src/components/layout/__tests__/HeaderComponent.test.tsx b/src/components/layout/__tests__/HeaderComponent.test.tsx index 840762b6..8bdbb7fa 100644 --- a/src/components/layout/__tests__/HeaderComponent.test.tsx +++ b/src/components/layout/__tests__/HeaderComponent.test.tsx @@ -57,7 +57,7 @@ describe('HeaderComponent — Grant Management Pipeline Verification', () => { const dropdownButton = await screen.findByText('1 Scope Grant Authorized'); fireEvent.click(dropdownButton); - expect(screen.getByText('BUDGET MANAGEMENT')).toBeInTheDocument(); + expect(screen.getByText('budget management')).toBeInTheDocument(); // Mock successful revocation endpoint cycle (global.fetch as any).mockResolvedValueOnce({ ok: true }); @@ -66,7 +66,7 @@ describe('HeaderComponent — Grant Management Pipeline Verification', () => { fireEvent.click(revokeButton); await waitFor(() => { - expect(screen.queryByText('BUDGET MANAGEMENT')).not.toBeInTheDocument(); + expect(screen.queryByText('budget management')).not.toBeInTheDocument(); }); }); }); diff --git a/src/hooks/useWallet.ts b/src/hooks/useWallet.ts new file mode 100644 index 00000000..3b381bcb --- /dev/null +++ b/src/hooks/useWallet.ts @@ -0,0 +1,11 @@ +import { useWallet as useWalletContext } from '@/providers/WalletProvider'; + +export function useWallet() { + const wallet = useWalletContext(); + + return { + ...wallet, + connected: wallet.isConnected, + publicKey: wallet.address, + }; +} \ No newline at end of file diff --git a/src/providers/WalletProvider.tsx b/src/providers/WalletProvider.tsx index be133c9c..bb924201 100644 --- a/src/providers/WalletProvider.tsx +++ b/src/providers/WalletProvider.tsx @@ -72,10 +72,10 @@ export function WalletProvider({ children }: WalletProviderProps) { network: validateWalletEnv().network, })); - const connect = useCallback(async () => { + const connect = useCallback(async (): Promise => { setState((prev) => ({ ...prev, isConnecting: true, error: null })); - return walletConnectionQueue.enqueue(async () => { + await walletConnectionQueue.enqueue(async () => { try { if (typeof window === 'undefined') { throw new Error('Window not available');