From 4ae75c04156c30d3f3fd7456ec307a1dec1a9bd8 Mon Sep 17 00:00:00 2001 From: Taki Koutsomitis Date: Wed, 7 Oct 2026 09:41:57 -0400 Subject: [PATCH 1/2] Replace .context and .cursor rules with AGENTS.md The rule files were stale: they described Jest and Cypress testing, Node 20, an external_references directory, and dumps of backend tables and endpoints. AGENTS.md covers the checks, dependency gotchas, and conventions the code follows today. CLAUDE.md imports it so Claude Code loads the same file, matching researchhub-backend. Also removes three Cursor debug logs that had been committed. --- .context/api-integration.md | 92 --- .cursor/debug-40f948.log | 99 --- .cursor/debug-4ff2e7.log | 14 - .cursor/debug-a5032c.log | 140 ----- .cursor/rules/api-endpoints.mdc | 566 ------------------ .cursor/rules/component-patterns.mdc | 184 ------ .cursor/rules/database-schema.mdc | 452 -------------- .cursor/rules/database-tables.mdc | 554 ----------------- .cursor/rules/design-system.mdc | 236 -------- .cursor/rules/hooks-architecture.mdc | 216 ------- .../rules/mobile-optimization-strategy.mdc | 268 --------- .cursor/rules/performance-optimization.mdc | 244 -------- .cursor/rules/project-structure.mdc | 70 --- .cursor/rules/senior-eng-review.mdc | 374 ------------ .cursor/rules/services-architecture.mdc | 227 ------- .../rules/state-management-architecture.mdc | 391 ------------ .cursor/rules/testing-standards.mdc | 320 ---------- .cursor/rules/types-architecture.mdc | 304 ---------- .cursor/rules/typescript-standards.mdc | 181 ------ AGENTS.md | 35 ++ CLAUDE.md | 1 + 21 files changed, 36 insertions(+), 4932 deletions(-) delete mode 100644 .context/api-integration.md delete mode 100644 .cursor/debug-40f948.log delete mode 100644 .cursor/debug-4ff2e7.log delete mode 100644 .cursor/debug-a5032c.log delete mode 100644 .cursor/rules/api-endpoints.mdc delete mode 100644 .cursor/rules/component-patterns.mdc delete mode 100644 .cursor/rules/database-schema.mdc delete mode 100644 .cursor/rules/database-tables.mdc delete mode 100644 .cursor/rules/design-system.mdc delete mode 100644 .cursor/rules/hooks-architecture.mdc delete mode 100644 .cursor/rules/mobile-optimization-strategy.mdc delete mode 100644 .cursor/rules/performance-optimization.mdc delete mode 100644 .cursor/rules/project-structure.mdc delete mode 100644 .cursor/rules/senior-eng-review.mdc delete mode 100644 .cursor/rules/services-architecture.mdc delete mode 100644 .cursor/rules/state-management-architecture.mdc delete mode 100644 .cursor/rules/testing-standards.mdc delete mode 100644 .cursor/rules/types-architecture.mdc delete mode 100644 .cursor/rules/typescript-standards.mdc create mode 100644 AGENTS.md create mode 100644 CLAUDE.md diff --git a/.context/api-integration.md b/.context/api-integration.md deleted file mode 100644 index 0b1440179..000000000 --- a/.context/api-integration.md +++ /dev/null @@ -1,92 +0,0 @@ -# API Integration Context - -About ResearchHub (the platform we are building): - -- ResearchHub is a web app that intends to incentivize scientists with ResearchCoin (ERC20 token) when they engage in "good" science practices (e.g. peer review, publishing openly, etc.) -- ResearchHub has a strong social network aspect, where users can follow each other and see each other's activity as wee as discover new content -- ResearchHub is built on Django (backend) and Next.js (frontend). - -Project goals: - -- Migrate from old Next.js app to new version when improved UI/UX and new components/features -- Maintain connection to existing Django backend - -## Reference Paths - -All paths are relative to the root of the project. - -- Publicly facing pages: `app/` -- UI components: `components/ui` -- Django API: `external_references/django-app` -- Old Next.js app (we are migrating from this): `external_references/old-nextjs-app` -- Django API endpoints: `external_references/django-app/src/urls.py` -- Modals should be added in `components/modals` -- Services should be added in `services` - -## Auth - -- Next.js app is using Auth via next-auth -- Django API does not use JWT but it does return a token in the response of the login endpoint -- app/api/auth/[...nextauth]/route.ts is the file that handles the authentication - -## Services - -- Services are located in `services` directory -- Services are used to interact with the Django API -- DTO transformer functions are located in `services/types` directory and are used to transform response data from the Django API into app-usable types. - -## Layout and conventions - -- `app/layouts/PageLayout.tsx` is the main layout file for the app -- `app/layouts/TopBar.tsx` is the top bar component for the app -- Every new page should be wrapped with `app/layouts/PageLayout.tsx` and have a three column layout. Initialize `RightSidebar` to be empty. - -## Contexts - -- Contexts including data providers should be located in `contexts` directory - -## Other - -- Utility functions are located in `utils` directory -- Date functions are located in `utils/date.ts` - -## Main Feed - -- `components/Feed/Feed.tsx` is the main feed component -- Feed item types are fetched from the /feed endpoint -- actions are: publish, repost, post, contribute -- types are: grant, paper, review, comment, contribution - -## Directives - -- Use next/Link component whenever rendering a component that redirects to another page -- Use `components/ui/Tabs` component whenever rendering a horizontal navigation -- Use `components/ui/Button` component whenever rendering a button. do not use ` - ); - }; - ``` - -2. **Variants with class-variance-authority**: - - Use `class-variance-authority` (cva) for components with multiple variants - - Define all variants in a single `cva` call - - Reference the variant styles in the component - - ```tsx - const buttonVariants = cva( - 'rounded-md font-medium transition-colors', - { - variants: { - variant: { - default: 'bg-primary-600 text-white hover:bg-primary-700', - secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300', - }, - size: { - sm: 'px-2 py-1 text-sm', - md: 'px-4 py-2', - lg: 'px-6 py-3 text-lg', - }, - }, - defaultVariants: { - variant: 'default', - size: 'md', - }, - } - ); - ``` - -## State Management - -1. **Local State**: - - Use `useState` for component-specific state - - Use `useReducer` for complex state logic - -2. **Shared State**: - - Use React Context for state that needs to be shared across multiple components - - Create dedicated context providers in the `contexts/` directory - -3. **Derived State**: - - Use `useMemo` for expensive computations - - Use `useCallback` for functions that are passed as props to prevent unnecessary re-renders - -## Component Composition - -1. **Component Props**: - - Accept a `className` prop for styling customization - - Use the spread operator for passing additional props - - ```tsx - const Button = ({ className, children, ...props }: ButtonProps) => { - return ( - - ); - }; - ``` - -2. **Composition Patterns**: - - Use the children prop for component composition - - Use render props for complex rendering logic - - Use compound components for related UI elements - -## Performance Optimization - -1. **Memoization**: - - Use `React.memo` for pure functional components that render often - - Use `useMemo` for expensive calculations - - Use `useCallback` for event handlers that are passed to child components - -2. **Code Splitting**: - - Use dynamic imports for large components - - Use Next.js's built-in code splitting features - -3. **Rendering Optimization**: - - Avoid unnecessary re-renders by using proper dependency arrays in hooks - - Implement virtualization for long lists with many items - -These patterns ensure consistent, maintainable, and performant components throughout the ResearchHub application. \ No newline at end of file diff --git a/.cursor/rules/database-schema.mdc b/.cursor/rules/database-schema.mdc deleted file mode 100644 index 25322324d..000000000 --- a/.cursor/rules/database-schema.mdc +++ /dev/null @@ -1,452 +0,0 @@ ---- -description: This document provides architectural and relationship information about the ResearchHub PostgreSQL database, complementing the detailed table specifications in `database-tables.cursor-rules`. -globs: -alwaysApply: false ---- -# ResearchHub Database Architecture (Cursor Rule) - -This document provides architectural and relationship information about the ResearchHub PostgreSQL database, complementing the detailed table specifications in `database-tables.cursor-rules`. - -## System Architecture Overview - -ResearchHub's database design implements a domain-driven approach centered around research content, with several key architectural patterns: - -1. **Unified Document Model**: Content (papers, posts, notes) is abstracted through a unified document system -2. **Polymorphic Relationships**: Generic relations support flexible content relationships -3. **Reputation Economy**: A complete transaction system for academic reputation -4. **Hub-based Organization**: Content categorization through research fields -5. **User Identity System**: Separates user accounts from author profiles -6. **Nonprofit Integration**: Supports fundraising for nonprofit organizations through the Endaoment service - -## Entity Relationship Diagram (Conceptual) - -``` -[USER SYSTEM] [CONTENT SYSTEM] [ORGANIZATION SYSTEM] -+----+ +---+ +----+ -| user_user | <----> | paper_paper | <----> | hub_hub | -+----+ +---+ +----+ - | | | - v v v -+----+ +---+ +----+ -| user_author | <----> | discussion_thread | <----> | topic_topic | -+----+ +---+ +----+ - | | | - v v | -+----+ +---+ | -| user_action | <----> | reputation_score | <----+ -+----+ +---+ - -[NONPROFIT SYSTEM] -+---------------+ +---------------+ -| nonprofit_org | <-------------> | purchase_fundraise | -+---------------+ +---------------+ - | | - v v -+------------------------+ -| nonprofit_fundraise_link | -+------------------------+ -``` - -## Domain Models - -### Content Domain - -The content model centers around unified documents that can represent different types of content: - -1. **Papers** (`paper_paper`): Research papers, which can be: - - Imported from external sources with DOIs - - Uploaded directly by users - - Created as preprints within the system - -2. **Discussions** (`discussion_thread`): Conversations that can be: - - Attached to papers - - Created as standalone discussions - - Function as comments on other content - -3. **Posts** (`researchhub_document_researchhubpost`): User-generated content that can be: - - Research summaries - - Commentary - - Blog-style posts - -All content connects through the `researchhub_document_researchhubunifieddocument` table, which establishes a polymorphic pattern allowing different content types to be treated uniformly in many contexts. - -### User Domain - -The user model separates identity from academic profile: - -1. **User Accounts** (`user_user`): Authentication and platform identity -2. **Author Profiles** (`user_author`): Academic identity and credentials -3. **Verification** (`user_userverification`): Processes for validating academic credentials - -This separation enables: -- One user to manage multiple author profiles -- Author identity verification without affecting user account access -- Academic metrics separate from platform activity metrics - -### Reputation Domain - -The reputation system implements a complete economic model: - -1. **Scoring** (`reputation_score`): Point values representing academic impact -2. **Transactions** (`reputation_distribution`): Movement of reputation between users -3. **Incentives** (`reputation_bounty`): Rewards for valuable contributions -4. **Escrow** (`reputation_escrow`): Holding system for reputation in process - -### Organization Domain - -Content is organized through multiple taxonomies: - -1. **Hubs** (`hub_hub`): Primary research field categorization -2. **Topics** (`topic_topic`): Finer-grained subject matter classification -3. **Concepts** (`researchhub_document_unifieddocumentconcepts`): Specific concepts within content - -## Key Relationships - -### Content to Users - -Content links to users through multiple relationship types: - -1. **Authorship**: `paper_authorship` connects papers to authors -2. **Creation**: Most content has a `created_by_id` reference to `user_user` -3. **Interaction**: `discussion_vote`, `user_action` track user interactions with content - -### Content Organization - -Content is organized through: - -1. **Hub Assignment**: `researchhub_document_researchhubunifieddocument_hubs` junction table -2. **Topic Association**: `topic_unifieddocumenttopics` relevancy-scored connections -3. **Hierarchy**: Topics belong to subfields, which belong to fields - -### Polymorphic Relationships - -The system uses Django's ContentType framework for polymorphic relationships: - -1. **Content Type ID**: References the `django_content_type` table to identify model types -2. **Object ID**: References the primary key of the specific instance -3. Together they create a "generic foreign key" pattern used in: - - `user_action` - - `discussion_vote` - - `reputation_distribution` - - `reputation_bounty` - -## Core Workflows - -### Nonprofit Fundraising Workflow - -``` - ┌─── Search for Nonprofit - │ -User → Create Fundraise → Select Nonprofit → Link to Document → Process Donations - │ - └─── Distribute Funds -``` - -Implementation path: -1. Search for nonprofit via Endaoment API -2. Create or select a nonprofit entry in `nonprofit_org` -3. Create a fundraise in `purchase_fundraise` -4. Link them through `nonprofit_fundraise_link` -5. Process donations and distribute funds - -### Paper Publication Workflow - -``` - ┌─── Assign Topics - │ -User → Upload Paper → Create Unified Doc → Assign Hubs → Link Authors → Add Metadata - │ - └─── Calculate Score -``` - -Implementation path: -1. Insert into `paper_paper` -2. Create entry in `researchhub_document_researchhubunifieddocument` -3. Link to hubs via `researchhub_document_researchhubunifieddocument_hubs` -4. Create entries in `paper_authorship` -5. Assign topics via `topic_unifieddocumenttopics` -6. Update metrics (score, views, etc.) - -### Discussion Creation Workflow - -``` - ┌─── Award Reputation - │ -User → Create Thread → Link to Paper/Post → Update Feeds → Update Metrics - │ - └─── Notify Subscribers -``` - -Implementation path: -1. Insert into `discussion_thread` -2. Link to paper or post through `paper_id` or generic relation -3. Create feed entries in `feed_feedentry` -4. Award reputation through `reputation_distribution` -5. Update content metrics (discussion_count, etc.) - -### Reputation Transaction Workflow - -``` - ┌─── Update Sender Balance - │ -User → Vote/Reward → Create Distribution → Update Recipient Score → Record History - │ - └─── Update Content Score -``` - -Implementation path: -1. Create entry in triggering table (e.g., `discussion_vote`) -2. Create distribution in `reputation_distribution` -3. Update scores in `reputation_score` -4. Record history in `reputation_scorechange` -5. Update content metrics (score, etc.) - -## Query Optimization Guidelines - -### Nonprofit Query Patterns - -When performing nonprofit-related queries: - -1. **Search via External API**: - ```sql - -- Avoid directly querying the database for nonprofits not yet in the system - -- Instead, use the Endaoment API for initial searches - -- Then store results in nonprofit_org for future reference - ``` - -2. **Fundraise Relationships**: - ```sql - -- Good: Efficient join through the link table - SELECT n.name, n.ein, f.goal_amount - FROM nonprofit_org n - JOIN nonprofit_fundraise_link nfl ON n.id = nfl.nonprofit_id - JOIN purchase_fundraise f ON nfl.fundraise_id = f.id - WHERE f.unified_document_id = 123 - ``` - -### Indexing Strategy - -The database employs strategic indexing to optimize performance: - -1. **Compound Indexes**: - - `(content_type_id, object_id)` for polymorphic lookups - - `(author_id, hub_id)` for reputation queries - - `(unified_document_id, is_public)` for content filtering - -2. **Text Search Indexes**: - - `paper_paper.title` and `paper_paper.abstract` for paper search - - `discussion_thread.plain_text` for content search - -3. **Foreign Key Indexes**: - - All foreign key columns are indexed - - Junction tables have indexes on both sides of relationships - -### Query Optimization Patterns - -When writing queries, follow these patterns for optimal performance: - -1. **Use Unified Document IDs**: - ```sql - -- Good: Query through unified document - SELECT * FROM paper_paper WHERE unified_document_id IN - (SELECT id FROM researchhub_document_researchhubunifieddocument WHERE score > 100) - - -- Avoid: Multiple separate queries - SELECT * FROM paper_paper WHERE score > 100 - UNION - SELECT * FROM researchhub_document_researchhubpost WHERE score > 100 - ``` - -2. **Filter Early in Joins**: - ```sql - -- Good: Filter before joining - SELECT p.* FROM paper_paper p - JOIN (SELECT unified_document_id FROM researchhub_document_researchhubunifieddocument_hubs - WHERE hub_id = 123) h ON p.unified_document_id = h.unified_document_id - - -- Avoid: Filter after joining multiple tables - SELECT p.* FROM paper_paper p - JOIN researchhub_document_researchhubunifieddocument_hubs uh ON p.unified_document_id = uh.unified_document_id - JOIN hub_hub h ON uh.hub_id = h.id - WHERE h.id = 123 - ``` - -3. **Use Content Type Lookups Efficiently**: - ```sql - -- Good: Pre-resolve content type - SELECT * FROM discussion_vote - WHERE content_type_id = (SELECT id FROM django_content_type - WHERE app_label = 'discussion' AND model = 'thread') - AND object_id = 123 - - -- Avoid: Multiple joins for simple lookups - SELECT * FROM discussion_vote v - JOIN django_content_type ct ON v.content_type_id = ct.id - WHERE ct.app_label = 'discussion' AND ct.model = 'thread' - AND v.object_id = 123 - ``` - -## Transaction Management - -The database requires careful transaction management in several areas: - -### Reputation Transactions - -Reputation transactions should follow ACID principles: - -1. **Atomicity**: Use database transactions to ensure distributions and score updates happen together -2. **Consistency**: Verify reputation totals match distributions -3. **Isolation**: Lock reputation records during updates to prevent race conditions -4. **Durability**: Ensure score changes are committed before notifying users - -Example transaction: -```sql -BEGIN; --- Create distribution -INSERT INTO reputation_distribution (sender_id, recipient_id, amount, ...) -VALUES (1, 2, 10, ...); - --- Update recipient score -UPDATE reputation_score -SET score = score + 10 -WHERE author_id = 2 AND hub_id = 3; - --- Record history -INSERT INTO reputation_scorechange (score_id, score_change, ...) -VALUES (123, 10, ...); -COMMIT; -``` - -### Content Relationship Integrity - -When creating or modifying content relationships: - -1. Create the primary content entity first -2. Establish the unified document connection -3. Create dependent relationships within the same transaction -4. Update denormalized counters and metrics - -## Data Migration Considerations - -When migrating or evolving the schema: - -1. **Denormalized Fields**: Several tables contain denormalized count fields that must be updated when related data changes -2. **Polymorphic Relationships**: Changes to model names require updates to `django_content_type` references -3. **Score Recalculation**: Reputation scores may need recalculation after structural changes - -## Common Query Examples - -### Paper Lookup with Related Information - -Find a paper with all related metadata, including authors, hubs, and metrics: - -```sql -SELECT - p.id, p.title, p.doi, p.abstract, - p.score, p.citations, p.views, p.downloads, - STRING_AGG(DISTINCT a.raw_author_name, ', ') AS authors, - STRING_AGG(DISTINCT h.name, ', ') AS hubs, - COUNT(DISTINCT dt.id) AS thread_count -FROM - paper_paper p - LEFT JOIN paper_authorship a ON p.id = a.paper_id - LEFT JOIN researchhub_document_researchhubunifieddocument_hubs rdh - ON p.unified_document_id = rdh.researchhubunifieddocument_id - LEFT JOIN hub_hub h ON rdh.hub_id = h.id - LEFT JOIN discussion_thread dt ON dt.paper_id = p.id -WHERE - p.doi = '10.1234/example.doi' - OR p.id = 12345 -GROUP BY - p.id; -``` - -### Finding Active Contributors - -Identify the most active contributors in a specific research field: - -```sql -SELECT - u.id, u.username, - ua.first_name, ua.last_name, - COUNT(DISTINCT p.id) AS paper_count, - COUNT(DISTINCT dt.id) AS discussion_count, - SUM(rs.score) AS total_reputation -FROM - user_user u - JOIN user_author ua ON u.id = ua.user_id - LEFT JOIN paper_authorship pa ON ua.id = pa.author_id - LEFT JOIN paper_paper p ON pa.paper_id = p.id - LEFT JOIN discussion_thread dt ON dt.created_by_id = u.id - LEFT JOIN reputation_score rs ON ua.id = rs.author_id -WHERE - rs.hub_id IN ( - SELECT id FROM hub_hub WHERE name LIKE '%Neuroscience%' - ) - AND dt.created_date >= NOW() - INTERVAL '6 months' -GROUP BY - u.id, ua.id -ORDER BY - total_reputation DESC -LIMIT 20; -``` - -### Content Recommendation Query - -Find relevant papers for a user based on their interests and activity: - -```sql -WITH user_hubs AS ( - SELECT DISTINCT h.id - FROM hub_hub h - JOIN researchhub_document_researchhubunifieddocument_hubs hd ON h.id = hd.hub_id - JOIN paper_paper p ON p.unified_document_id = hd.researchhubunifieddocument_id - JOIN paper_authorship pa ON p.id = pa.paper_id - WHERE pa.author_id = 123 - - UNION - - SELECT h.id - FROM hub_hub h - JOIN user_follow uf ON h.id = uf.object_id - WHERE uf.user_id = 456 - AND uf.content_type_id = (SELECT id FROM django_content_type WHERE app_label = 'hub' AND model = 'hub') -), -user_topics AS ( - SELECT DISTINCT t.id - FROM topic_topic t - JOIN topic_unifieddocumenttopics tud ON t.id = tud.topic_id - JOIN paper_paper p ON p.unified_document_id = tud.unified_document_id - JOIN discussion_thread dt ON dt.paper_id = p.id - WHERE dt.created_by_id = 456 -) -SELECT - p.id, p.title, p.doi, - p.score * 0.5 + p.citations * 0.3 + p.downloads * 0.2 AS relevance_score -FROM - paper_paper p - JOIN researchhub_document_researchhubunifieddocument_hubs hd - ON p.unified_document_id = hd.researchhubunifieddocument_id - JOIN topic_unifieddocumenttopics tud ON p.unified_document_id = tud.unified_document_id -WHERE - hd.hub_id IN (SELECT id FROM user_hubs) - AND tud.topic_id IN (SELECT id FROM user_topics) - AND p.is_public = TRUE - AND p.is_removed = FALSE - AND p.created_date >= NOW() - INTERVAL '1 year' -ORDER BY - relevance_score DESC -LIMIT 10; -``` - -## Schema Evolution Best Practices - -When modifying the ResearchHub database schema, follow these guidelines: - -1. **Backward Compatibility**: Keep existing relationships functional during transitions -2. **Transaction Safety**: Ensure migrations can be rolled back cleanly -3. **Data Validation**: Verify data integrity before and after migrations -4. **Performance Impact**: Consider query patterns when adding or removing indexes -5. **Polymorphic Relations**: Update content types appropriately when modifying models - -For detailed table information, refer to `database-tables.cursor-rules`. diff --git a/.cursor/rules/database-tables.mdc b/.cursor/rules/database-tables.mdc deleted file mode 100644 index 5b84510f6..000000000 --- a/.cursor/rules/database-tables.mdc +++ /dev/null @@ -1,554 +0,0 @@ ---- -description: This document provides a comprehensive reference of table compositions in the ResearchHub database. -globs: -alwaysApply: false ---- -# ResearchHub Database Tables (Cursor Rule) - -This document provides a comprehensive reference of table compositions in the ResearchHub database. - -## User Management - -### nonprofit_org - -**Description**: Nonprofit organizations that can receive donations. - -| Column | Type | Description | -| ----- | --- | ---- | -| id | integer | Primary key | -| name | character varying(255) | Name of the nonprofit | -| ein | character varying(20) | Employer Identification Number | -| endaoment_org_id | character varying(100) | Unique ID in Endaoment system | -| base_wallet_address | character varying(42) | Blockchain wallet address | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | - -### nonprofit_fundraise_link - -**Description**: Join table connecting nonprofits and fundraising campaigns. - -| Column | Type | Description | -| ----- | ----- | --- | -| id | integer | Primary key | -| nonprofit_id | integer | Foreign key to nonprofit_org | -| fundraise_id | integer | Foreign key to purchase_fundraise | -| note | text | Notes about this specific link | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | - -### user_user - -**Description**: Core user account table. - -| Column | Type | Description | -| -------------- | -------------------------- | ------------------------------------ | -| id | integer | Primary key | -| username | character varying | Unique username | -| email | character varying | User's email address | -| password | character varying | Hashed password | -| first_name | character varying | User's first name | -| last_name | character varying | User's last name | -| is_active | boolean | Whether the account is active | -| date_joined | timestamp with time zone | When the user joined | -| reputation | integer | User's reputation score | -| is_staff | boolean | Whether user has staff privileges | -| is_superuser | boolean | Whether user has superuser privileges| - -### user_author - -**Description**: Author profile information linked to users. - -| Column | Type | Description | -| ------------------ | ---------------------- | ----------------------------------- | -| id | integer | Primary key | -| first_name | character varying | Author's first name | -| last_name | character varying | Author's last name | -| user_id | integer | Foreign key to user_user | -| orcid | character varying | ORCID identifier | -| scopus_author_id | character varying | Scopus author identifier | -| google_scholar_id | character varying | Google Scholar identifier | -| twitter | character varying | Twitter handle | -| linkedin | character varying | LinkedIn profile URL | -| website | character varying | Personal website URL | - -### user_userverification - -**Description**: User verification information. - -| Column | Type | Description | -| -------------- | -------------------------- | ------------------------------------ | -| id | integer | Primary key | -| first_name | text | User's first name | -| last_name | text | User's last name | -| status | text | Verification status | -| verified_by | text | Verification method | -| external_id | text | External identifier | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | -| user_id | integer | Foreign key to user_user | - -### user_follow - -**Description**: User following relationships. - -| Column | Type | Description | -| -------------- | -------------------------- | ------------------------------------ | -| id | integer | Primary key | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | -| object_id | integer | ID of followed object | -| content_type_id| integer | Type of followed content | -| user_id | integer | User who follows | - -### user_action - -**Description**: User actions on the platform. - -| Column | Type | Description | -| -------------- | -------------------------- | ------------------------------------ | -| id | integer | Primary key | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | -| object_id | integer | ID of action object | -| content_type_id| integer | Type of content | -| user_id | integer | User who performed action | -| read_date | timestamp with time zone | When action was read | -| display | boolean | Whether to display action | -| is_removed | boolean | Whether action is removed | - -## Paper Resources - -### paper_paper - -**Description**: Research paper metadata. - -| Column | Type | Description | -| -------------------- | ------------------------ | ---------------------------------- | -| id | integer | Primary key | -| title | character varying(1024) | Paper title | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | -| paper_publish_date | date | Publication date | -| doi | character varying(255) | Digital Object Identifier | -| url | character varying(1024) | URL to paper | -| uploaded_by_id | integer | User who uploaded the paper | -| file | character varying(512) | File path | -| abstract | text | Paper abstract | -| is_public | boolean | Whether paper is public | -| is_removed | boolean | Whether paper has been removed | -| slug | character varying(1024) | URL-friendly slug | -| paper_type | character varying(32) | Type of paper | -| score | integer | Aggregated score | -| citations | integer | Citation count | -| downloads | integer | Download count | -| views | integer | View count | -| unified_document_id | integer | Related unified document | -| is_open_access | boolean | Whether paper is open access | - -### paper_authorship - -**Description**: Paper author relationships. - -| Column | Type | Description | -| ---------------- | ------------------------ | ---------------------------------- | -| id | integer | Primary key | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | -| author_position | character varying(10) | Position in author list | -| is_corresponding | boolean | Whether author is corresponding | -| raw_author_name | character varying(255) | Raw author name from source | -| author_id | integer | Foreign key to user_author | -| paper_id | integer | Foreign key to paper_paper | -| email | character varying(255) | Author's email | -| department | character varying(255) | Author's department | - -## Discussion Resources - -### discussion_thread - -**Description**: Discussion threads associated with papers. - -| Column | Type | Description | -| -------------------- | ------------------------ | ---------------------------------- | -| id | integer | Primary key | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | -| is_public | boolean | Whether thread is public | -| is_removed | boolean | Whether thread has been removed | -| text | jsonb | Text content (rich format) | -| title | character varying(255) | Thread title | -| created_by_id | integer | User who created the thread | -| paper_id | integer | Related paper | -| was_edited | boolean | Whether thread was edited | -| plain_text | text | Plain text version | -| source | character varying(32) | Source of the thread | -| score | integer | Thread score | -| discussion_post_type | character varying(16) | Type of discussion post | -| is_accepted_answer | boolean | Whether marked as accepted answer | - -## Content Types - -### django_content_type - -**Description**: Maps content types to their IDs. - -| Column | Type | Description | -| ----------- | --------------------- | ----------------------------------- | -| id | integer | Primary key | -| app_label | character varying(100)| Application label | -| model | character varying(100)| Model name | - -## Feed System - -### feed_feedentry - -**Description**: Feed entries for user activity. - -| Column | Type | Description | -| -------------------- | ------------------------ | ---------------------------------- | -| id | bigint | Primary key | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | -| object_id | integer | ID of feed object | -| parent_object_id | integer | ID of parent object | -| action | text | Action performed | -| action_date | timestamp with time zone | When action was performed | -| content_type_id | integer | Type of content | -| parent_content_type_id| integer | Type of parent content | -| user_id | integer | User associated with feed entry | - -## Notes System - -### note_note - -**Description**: Notes created by users. - -| Column | Type | Description | -| -------------------- | ------------------------ | ---------------------------------- | -| id | integer | Primary key | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | -| created_by_id | integer | User who created the note | -| latest_version_id | integer | Latest version of note content | -| organization_id | integer | Associated organization | -| unified_document_id | integer | Related unified document | -| title | text | Note title | - -### note_notecontent - -**Description**: Content of notes. - -| Column | Type | Description | -| ----------- | ---------------------- | ----------------------------------- | -| id | integer | Primary key | -| created_date| timestamp with time zone| Creation timestamp | -| src | character varying(512) | Source of content | -| plain_text | text | Plain text version | -| note_id | integer | Related note | -| json | jsonb | JSON content | - -## Purchase System - -### purchase_balance - -**Description**: User balances. - -| Column | Type | Description | -| -------------- | -------------------------- | ------------------------------------ | -| id | integer | Primary key | -| object_id | integer | Associated object ID | -| amount | character varying(255) | Balance amount | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | -| content_type_id| integer | Type of content | -| user_id | integer | Associated user | -| testnet_amount | character varying(255) | Testnet balance amount | - -### purchase_fundraise - -**Description**: Fundraising campaigns. - -| Column | Type | Description | -| -------------- | -------------------------- | ------------------------------------ | -| id | integer | Primary key | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | -| status | character varying(32) | Fundraise status | -| goal_amount | numeric | Goal amount | -| goal_currency | character varying(16) | Currency of goal | -| start_date | timestamp with time zone | Start date | -| end_date | timestamp with time zone | End date | -| created_by_id | integer | User who created fundraise | -| escrow_id | integer | Associated escrow | -| unified_document_id | integer | Related unified document | - -### purchase_purchase - -**Description**: User purchases. - -| Column | Type | Description | -| -------------- | -------------------------- | ------------------------------------ | -| id | integer | Primary key | -| object_id | integer | Associated object ID | -| purchase_type | character varying(32) | Type of purchase | -| amount | character varying(255) | Purchase amount | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | -| content_type_id| integer | Type of content | -| user_id | integer | User who made purchase | -| purchase_hash | character varying(32) | Purchase hash | -| purchase_method| character varying(16) | Purchase method | -| transaction_hash| character varying(255) | Blockchain transaction hash | -| boost_time | double precision | Boost time | -| paid_date | timestamp with time zone | Payment date | -| paid_status | character varying(255) | Payment status | -| group_id | integer | Group ID for batch purchases | - -## Reputation System - -### reputation_score - -**Description**: User reputation scores. - -| Column | Type | Description | -| -------------- | -------------------------- | ------------------------------------ | -| id | integer | Primary key | -| score | integer | Reputation score value | -| author_id | integer | Related author | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | -| hub_id | integer | Related hub | - -### reputation_scorechange - -**Description**: Changes to reputation scores. - -| Column | Type | Description | -| -------------------- | ------------------------ | ---------------------------------- | -| id | integer | Primary key | -| updated_date | timestamp with time zone | Last update timestamp | -| algorithm_version | integer | Algorithm version | -| score_after_change | integer | Score after change | -| score_change | integer | Amount of change | -| raw_value_change | integer | Raw value change | -| changed_object_id | integer | Changed object ID | -| changed_object_field | character varying(100) | Field that changed | -| variable_counts | jsonb | Variable counts | -| created_date | timestamp with time zone | Creation timestamp | -| algorithm_variables_id| integer | Algorithm variables | -| changed_content_type_id| integer | Changed content type | -| score_id | integer | Related score | - -### reputation_distribution - -**Description**: Reputation distribution transactions. - -| Column | Type | Description | -| ---------------- | ------------------------ | ------------------------------------- | -| id | integer | Primary key | -| amount | integer | Amount distributed | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | -| sender_id | integer | User sending reputation | -| recipient_id | integer | User receiving reputation | -| content_type_id | integer | Type of content | -| object_id | integer | ID of related object | -| distribution_type| character varying | Type of distribution | - -### reputation_bounty - -**Description**: Bounties for content contributions. - -| Column | Type | Description | -| -------------- | -------------------------- | ------------------------------------ | -| id | integer | Primary key | -| amount | integer | Bounty amount | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | -| creator_id | integer | User who created bounty | -| content_type_id| integer | Type of content | -| object_id | integer | ID of related object | -| expires_date | timestamp with time zone | Expiration date | -| status | character varying | Bounty status | - -### reputation_escrow - -**Description**: Escrow for reputation transactions. - -| Column | Type | Description | -| -------------- | -------------------------- | ------------------------------------ | -| id | integer | Primary key | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | -| hold_type | character varying(16) | Type of hold | -| amount_holding | numeric | Amount in escrow | -| object_id | integer | Associated object ID | -| status | character varying(16) | Escrow status | -| content_type_id| integer | Type of content | -| created_by_id | integer | User who created escrow | -| bounty_fee_id | integer | Associated bounty fee | -| amount_paid | numeric | Amount paid out | - -### reputation_escrowrecipients - -**Description**: Recipients of escrow payouts. - -| Column | Type | Description | -| -------------- | -------------------------- | ------------------------------------ | -| id | integer | Primary key | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | -| amount | numeric | Amount to receive | -| escrow_id | integer | Associated escrow | -| user_id | integer | User receiving payout | - -### reputation_withdrawal - -**Description**: Withdrawal of reputation tokens. - -| Column | Type | Description | -| ---------------- | ------------------------ | ------------------------------------- | -| id | integer | Primary key | -| from_address | character varying(255) | From address | -| to_address | character varying(255) | To address | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | -| paid_date | timestamp with time zone | Payment date | -| transaction_hash | character varying(255) | Transaction hash | -| user_id | integer | Associated user | -| is_removed | boolean | Whether withdrawal is removed | -| is_removed_date | timestamp with time zone | When withdrawal was removed | -| paid_status | character varying(255) | Payment status | -| token_address | character varying(255) | Token address | -| amount | character varying(255) | Withdrawal amount | -| fee | character varying(255) | Withdrawal fee | -| is_public | boolean | Whether withdrawal is public | -| network | character varying(10) | Blockchain network | - -## Document System - -### researchhub_document_researchhubunifieddocument - -**Description**: Unified document model combining different content types. - -| Column | Type | Description | -| ------------------ | ------------------------ | ------------------------------------ | -| id | integer | Primary key | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | -| document_type | character varying(32) | Type of document | -| score | integer | Document score | -| is_removed | boolean | Whether document is removed | -| published_date | timestamp with time zone | Publication date | -| is_public | boolean | Whether document is public | -| hot_score | integer | Hot score for trending | -| document_filter_id | integer | Associated document filter | -| is_removed_date | timestamp with time zone | When document was removed | - -### researchhub_document_researchhubpost - -**Description**: Posts in ResearchHub. - -| Column | Type | Description | -| ------------------ | ------------------------ | ------------------------------------ | -| id | integer | Primary key | -| version_number | integer | Version number | -| prev_version_id | integer | Previous version ID | -| unified_document_id| integer | Related unified document | -| created_date | timestamp with time zone | Creation timestamp | -| discussion_src | character varying(512) | Discussion source | -| editor_type | character varying(32) | Type of editor | -| eln_src | character varying(512) | ELN source | -| renderable_text | text | Renderable text content | -| updated_date | timestamp with time zone | Last update timestamp | -| title | text | Post title | -| document_type | character varying(32) | Type of document | -| created_by_id | integer | User who created post | -| preview_img | character varying(200) | Preview image | -| discussion_count | integer | Count of discussions | -| slug | character varying(1024) | URL-friendly slug | -| note_id | integer | Associated note | -| doi | character varying(255) | Digital Object Identifier | -| bounty_type | character varying(64) | Type of bounty | -| score | integer | Post score | - -### researchhub_document_unifieddocumentconcepts - -**Description**: Concepts associated with unified documents. - -| Column | Type | Description | -| ------------------ | ------------------------ | ------------------------------------ | -| id | integer | Primary key | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | -| relevancy_score | double precision | Relevancy score | -| level | integer | Concept level | -| concept_id | integer | Associated concept | -| unified_document_id| integer | Related unified document | - -## Reviews - -### review_review - -**Description**: Reviews of content. - -| Column | Type | Description | -| ------------------ | ------------------------ | ------------------------------------ | -| id | integer | Primary key | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | -| score | double precision | Review score | -| created_by_id | integer | User who created review | -| unified_document_id| integer | Related unified document | -| is_removed | boolean | Whether review is removed | -| is_public | boolean | Whether review is public | -| is_removed_date | timestamp with time zone | When review was removed | -| content_type_id | integer | Type of content | -| object_id | integer | ID of reviewed object | - -## Topics - -### topic_topic - -**Description**: Research topics. - -| Column | Type | Description | -| --------------------- | ------------------------ | ------------------------------------ | -| id | bigint | Primary key | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | -| openalex_id | character varying(255) | OpenAlex ID | -| display_name | text | Display name | -| works_count | integer | Count of works | -| cited_by_count | integer | Count of citations | -| keywords | ARRAY | Keywords array | -| openalex_updated_date | timestamp with time zone | OpenAlex update timestamp | -| subfield_id | bigint | Associated subfield | -| openalex_created_date | timestamp with time zone | OpenAlex creation timestamp | - -### topic_subfield - -**Description**: Research subfields. - -| Column | Type | Description | -| --------------------- | ------------------------ | ------------------------------------ | -| id | bigint | Primary key | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | -| openalex_id | character varying(255) | OpenAlex ID | -| display_name | text | Display name | -| field_id | bigint | Associated field | - -### topic_unifieddocumenttopics - -**Description**: Association between documents and topics. - -| Column | Type | Description | -| ------------------ | ------------------------ | ------------------------------------ | -| id | bigint | Primary key | -| created_date | timestamp with time zone | Creation timestamp | -| updated_date | timestamp with time zone | Last update timestamp | -| relevancy_score | double precision | Relevancy score | -| topic_id | bigint | Associated topic | -| unified_document_id| integer | Related unified document | -| is_primary | boolean | Whether topic is primary | diff --git a/.cursor/rules/design-system.mdc b/.cursor/rules/design-system.mdc deleted file mode 100644 index c09f8a2d9..000000000 --- a/.cursor/rules/design-system.mdc +++ /dev/null @@ -1,236 +0,0 @@ ---- -description: -globs: -alwaysApply: false ---- - # ResearchHub Design System - -This document outlines the design system and styling guidelines used in the ResearchHub codebase. - -## Styling Approach - -ResearchHub uses Tailwind CSS as the primary styling solution, complemented by CSS modules for complex components when necessary. - -1. **Tailwind CSS**: - - Use Tailwind utility classes for most styling needs - - Follow the "utility-first" approach - - Use Tailwind's built-in responsive classes for responsive design - -2. **CSS Modules**: - - Use CSS modules for complex components that require custom animations or styles that are difficult to achieve with Tailwind - - Name CSS module files with the `.module.css` extension - -3. **Styling Utilities**: - - Use the `cn()` utility function for conditional class name composition - - Use `class-variance-authority` (cva) for component variants - -## Color System - -1. **Color Palette**: - - Use the color tokens defined in the Tailwind configuration - - Primary colors: Shades of indigo and blue - - Secondary colors: Gray scale and orange/yellow for accent - - Semantic colors: Success (green), warning (yellow), error (red), info (blue) - - ```tsx - // Use semantic color tokens -
Primary button
-
Secondary button
-
Error state
- ``` - -2. **Color Usage**: - - Use primary colors for primary actions and branding - - Use gray scale for UI structure, backgrounds, and secondary elements - - Use semantic colors consistently for their respective meanings - - Maintain appropriate contrast ratios for accessibility (4.5:1 minimum) - -## Typography - -1. **Font Family**: - - Primary font: Inter (sans-serif) - - Heading font: Cal Sans for titles and large headings - - Monospace font: For code blocks and technical content - -2. **Font Sizes**: - - Follow the Tailwind CSS font size scale - - Use relative units (rem) for font sizes to support user preferences - - ```tsx -

Page Title

-

Section Heading

-

Body text

- Caption text - ``` - -3. **Font Weights**: - - Regular (400) for body text - - Medium (500) for semi-emphasis - - Semibold (600) for headings and emphasis - - Bold (700) for strong emphasis - -4. **Line Heights**: - - Use appropriate line heights for readability - - Body text: `leading-normal` (1.5) - - Headings: `leading-tight` (1.25) - - Single-line elements: `leading-none` (1) - -## Spacing - -1. **Spacing Scale**: - - Follow the Tailwind CSS spacing scale - - Use consistent spacing for margins and padding - - Use the spacing scale for gaps, grid gaps, and other spacing properties - - ```tsx -
-
-

Section Title

-

Content

-
-
- ``` - -2. **Layout Spacing**: - - Page margins: px-4 sm:px-6 md:px-8 lg:px-12 - - Section spacing: my-8 md:my-12 lg:my-16 - - Component spacing: p-4 for cards, p-2 for smaller components - -## Components - -1. **Base Components**: - - Use the components in the `components/ui` directory as the foundation - - Common components include Button, Input, Card, Badge, and more - - All components are built with accessibility in mind - -2. **Component Variants**: - - Components have consistent variants using `class-variance-authority` - - Common variants include size, color, and emphasis level - - ```tsx - // Button with variants - - - ``` - -3. **Component Composition**: - - Build complex UI by composing smaller components - - Use the children prop for flexible content - - Use render props for complex rendering logic - -## Icons - -1. **Icon System**: - - Use Lucide icons as the primary icon set - - Use Font Awesome for additional icons when necessary - - Keep icons consistent in style and usage - - ```tsx - import { User, Settings, Bell } from 'lucide-react'; - - - ``` - -2. **Icon Sizing**: - - Use consistent sizing: sm (16px), md (20px), lg (24px) - - Adjust stroke width for better visibility at different sizes - - Maintain proper alignment with text using flex layout - -## Responsive Design - -1. **Breakpoints**: - - Follow Tailwind's breakpoint system: - - sm: 640px - - md: 768px - - lg: 1024px - - xl: 1280px - - 2xl: 1536px - - custom: wide (1200px), 3xl (1600px) - -2. **Mobile-first Approach**: - - Start with mobile layouts and progressively enhance for larger screens - - Use responsive utilities to adapt layout, typography, and spacing - - ```tsx -
- {/* Content */} -
- ``` - -3. **Responsive Components**: - - Design components to adapt to different screen sizes - - Use responsive variants for component properties - - Test all components across different breakpoints - -## Accessibility - -1. **Color Contrast**: - - Maintain a minimum contrast ratio of 4.5:1 for normal text - - Maintain a minimum contrast ratio of 3:1 for large text and UI components - - Test color combinations with accessibility tools - -2. **Keyboard Navigation**: - - Ensure all interactive elements are keyboard accessible - - Implement proper focus states with visible focus indicators - - Maintain a logical tab order - -3. **Screen Readers**: - - Add appropriate ARIA attributes when necessary - - Use semantic HTML elements when possible - - Provide alt text for images and aria-labels for interactive elements - -4. **Reduced Motion**: - - Respect user preferences for reduced motion - - Provide alternatives to motion-heavy interactions - - ```tsx -
- {/* Content */} -
- ``` - -## Dark Mode - -1. **Color Adaptation**: - - Use Tailwind's dark mode utilities to adapt colors - - Keep proper contrast in both light and dark modes - - Test all components in both modes - - ```tsx -
- {/* Content */} -
- ``` - -2. **Dark Mode Strategy**: - - Implement dark mode based on user preference - - Allow user toggling with persistent preference - - Ensure smooth transitions between modes - -## Animation and Interaction - -1. **Transitions**: - - Use subtle transitions for state changes - - Keep transitions short (150-300ms) - - Use appropriate easing functions - - ```tsx - - ``` - -2. **Hover and Focus States**: - - Provide clear visual feedback for hover and focus states - - Keep hover effects subtle but noticeable - - Ensure focus states meet accessibility standards - -3. **Loading States**: - - Show appropriate loading indicators for async operations - - Use skeleton loaders for content loading - - Maintain layout stability during loading states - -These design system guidelines ensure a consistent, accessible, and visually cohesive user experience throughout the ResearchHub application. \ No newline at end of file diff --git a/.cursor/rules/hooks-architecture.mdc b/.cursor/rules/hooks-architecture.mdc deleted file mode 100644 index 01732633b..000000000 --- a/.cursor/rules/hooks-architecture.mdc +++ /dev/null @@ -1,216 +0,0 @@ ---- -description: This document outlines the architectural patterns and best practices for custom React hooks in the ResearchHub codebase. -globs: -alwaysApply: false ---- - # ResearchHub Hooks Architecture - -This document outlines the architectural patterns and best practices for custom React hooks in the ResearchHub codebase. - -## Hook Structure - -1. **Naming Convention**: - - Prefix all hooks with `use` (e.g., `useVote`, `useFeed`, `useNote`) - - Use camelCase for hook names - - Name hooks based on their primary functionality or domain - -2. **Client Components**: - - Add the 'use client' directive at the top of client hooks - - Hooks that use browser-specific APIs should always have 'use client' - - ```typescript - 'use client'; - - import { useState } from 'react'; - - export const useFeature = () => { - // Hook implementation - }; - ``` - -3. **Hook Return Types**: - - For simple hooks, return an object with named properties - - For hooks with actions, use tuple pattern: `[state, action]` - - Explicitly define return types with TypeScript interfaces - - ```typescript - // Object return pattern - interface UseFeatureReturn { - data: Data[]; - isLoading: boolean; - error: Error | null; - refresh: () => void; - } - - // Tuple return pattern - type UseFeatureState = { data: Data | null; isLoading: boolean; error: Error | null }; - type UseFeatureAction = (params: ActionParams) => Promise; - type UseFeatureReturn = [UseFeatureState, UseFeatureAction]; - ``` - -## State Management - -1. **State Organization**: - - Group related state variables in a state object - - Manage loading, error, and data states consistently - - Use descriptive state variable names - - ```typescript - const useFeature = () => { - const [data, setData] = useState(null); - const [isLoading, setIsLoading] = useState(false); - const [error, setError] = useState(null); - - // Hook implementation - - return { data, isLoading, error }; - }; - ``` - -2. **Error Handling**: - - Always include error state in hooks that make API calls - - Parse and transform API errors to meaningful user-facing messages - - Implement consistent error handling patterns across hooks - - ```typescript - try { - const response = await ServiceName.method(params); - setData(response); - } catch (err) { - const { data = {} } = err instanceof ApiError ? JSON.parse(err.message) : {}; - const errorMsg = data?.detail || 'Default error message'; - setError(errorMsg); - } finally { - setIsLoading(false); - } - ``` - -## Hook Composition - -1. **Separation of Concerns**: - - Create single-purpose hooks that handle one specific feature - - Compose multiple hooks together for complex functionality - - Keep each hook focused on a specific domain - - ```typescript - // In a component: - const { data: user } = useUser(userId); - const { posts, isLoading: postsLoading } = usePosts(userId); - const { comments, isLoading: commentsLoading } = useComments(userId); - ``` - -2. **Data Fetching Patterns**: - - Initialize data fetching in `useEffect` - - Provide refetch/refresh functions for manual updates - - Handle loading and error states consistently - - ```typescript - const [data, setData] = useState([]); - const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(null); - - useEffect(() => { - fetchData(); - }, [dependencies]); - - const fetchData = async () => { - setIsLoading(true); - setError(null); - try { - const result = await ServiceName.getData(params); - setData(result); - } catch (error) { - setError(error); - } finally { - setIsLoading(false); - } - }; - ``` - -3. **Pagination and Infinite Scrolling**: - - Implement consistent pagination patterns - - Track page number, hasMore flag, and loading states - - Append to existing data rather than replacing on loadMore - - ```typescript - const loadMore = async () => { - if (!hasMore || isLoading) return; - - try { - const nextPage = page + 1; - const result = await Service.getData({ page: nextPage, ...otherParams }); - setData((prev) => [...prev, ...result.items]); - setHasMore(result.hasMore); - setPage(nextPage); - } catch (error) { - console.error('Error loading more:', error); - } - }; - ``` - -## Performance Optimization - -1. **Don't memoize**: - - Properly manage dependency arrays to prevent unnecessary re-renders - -2. **Debouncing and Throttling**: - - Debounce input handlers and search functions - - Implement throttling for scroll events and frequent updates - - Clean up debounced/throttled functions in `useEffect` return - - ```typescript - const debouncedSave = useRef( - debounce((content: string) => { - saveToServer(content); - }, 500) - ).current; - - useEffect(() => { - return () => { - debouncedSave.cancel(); - }; - }, [debouncedSave]); - ``` - -## API Integration - -1. **Service Integration**: - - Use service classes for API calls (never make direct fetch calls in hooks) - - Abstract API interaction details away from component logic - - Transform API responses to match the expected UI data structure - - ```typescript - // Good - const data = await NoteService.getNote(noteId); - - // Avoid - const data = await fetch(`/api/notes/${noteId}`).then(res => res.json()); - ``` - -2. **Optimistic Updates**: - - Implement optimistic updates for better user experience - - Store previous state for rollback in case of errors - - Update UI immediately, then confirm with API response - - ```typescript - const addItem = async (newItem: Item) => { - // Optimistically update UI - const previousItems = [...items]; - setItems((prev) => [...prev, { ...newItem, id: 'temp-id' }]); - - try { - // Actual API call - const savedItem = await ItemService.addItem(newItem); - // Update with real data from server - setItems((prev) => prev.map(item => - item.id === 'temp-id' ? savedItem : item - )); - } catch (error) { - // Rollback on error - setItems(previousItems); - setError(error); - } - }; - ``` - -These patterns ensure consistent, maintainable, and performant hooks throughout the ResearchHub application. \ No newline at end of file diff --git a/.cursor/rules/mobile-optimization-strategy.mdc b/.cursor/rules/mobile-optimization-strategy.mdc deleted file mode 100644 index a20f84025..000000000 --- a/.cursor/rules/mobile-optimization-strategy.mdc +++ /dev/null @@ -1,268 +0,0 @@ ---- -description: When applying buttons or styles that depend on screen size, or mobile, or having issues with staging compared to local -globs: -alwaysApply: false ---- -# Mobile Optimization Strategy - -This document outlines the mobile optimization strategy developed for ResearchHub landing page components to address environment-specific responsive layout issues. - -## Problem Context - -### Root Issue Identified -- **Problem**: Responsive Tailwind classes (`sm:`, `md:`, etc.) weren't compiling properly on staging despite JavaScript correctly detecting screen sizes -- **Symptoms**: - - Buttons stacked vertically and went full-width on staging - - Same code displayed correctly on localhost - - Debug tools showed correct screen size detection but wrong visual rendering -- **Root Cause**: Environment-specific CSS compilation/loading issue affecting responsive class generation - -## Core Solution Strategy - -Replace responsive breakpoints with environment-agnostic approaches that work consistently across all deployment environments. - -### 1. Button Standardization -```tsx -// ❌ Avoid: Custom responsive button implementations - -

Description

- -
- -

Description

-
- -``` - -### Single Button Layout -```tsx -// For features with single action -
- -

Description

-
-``` - -### Benefits Grid Layout -```tsx -// Replace responsive grids with flex-wrap -
- {benefits.map((benefit, index) => ( -
-
- {benefit} -
- ))} -
-``` - -### Navigation Tabs -```tsx -// Environment-agnostic tab navigation -
- {features.map((feature, index) => ( - - ))} -
-``` - -## Mobile-Specific Optimizations - -### Icon Layout Prevention -```tsx -// ❌ Avoid: Horizontal layouts that can distort icons on mobile -
- -
Content
-
- -// ✅ Preferred: Vertical layouts for mobile-friendly icons -
-
- -
-
Content
-
-``` - -### Compact Data Display -```tsx -// For price/data displays that need to fit on mobile -
-
-
- Label -
-
$0.33
-
- -
- -
-
- Change -
-
+12.5%
-
-
-``` - -### Text Optimization for Mobile -- Shorten navigation labels: "Align Incentives" → "Incentives" -- Reduce header sizes: `text-5xl` → `text-4xl` for better vertical fit -- Remove redundant text: "USD" when "$" symbol is present - -## Technical Principles - -### 1. Environment Independence -- Avoid any CSS that could behave differently between localhost and staging -- Use compilation-independent approaches -- Test across multiple environments before deployment - -### 2. Natural Responsiveness -- Leverage intrinsic sizing (`min-w-`, `max-w-`, `flex-1`) -- Use flex-wrapping instead of explicit breakpoints -- Allow content to determine layout naturally - -### 3. Component Consistency -- Utilize existing design system components (Button, SpotlightCard) -- Maintain consistent spacing and typography scales -- Follow established component patterns - -### 4. Progressive Enhancement -- Design mobile-first with natural scaling up -- Ensure core functionality works without responsive classes -- Add enhancements that don't break base experience - -## Testing Strategy - -### Environment Testing -1. Test on localhost with various screen sizes -2. Deploy to staging and verify identical behavior -3. Use browser dev tools to simulate different devices -4. Test with network throttling to catch loading issues - -### Debug Techniques -```tsx -// Screen size detection for debugging -const [screenWidth, setScreenWidth] = useState(0); - -useEffect(() => { - const updateWidth = () => setScreenWidth(window.innerWidth); - updateWidth(); - window.addEventListener('resize', updateWidth); - return () => window.removeEventListener('resize', updateWidth); -}, []); - -// Visual breakpoint indicator -
- {screenWidth}px -
-``` - -## Common Anti-Patterns to Avoid - -### ❌ Responsive Class Dependencies -```tsx -// These can fail in certain build environments -className="w-full sm:w-auto" -className="grid grid-cols-1 md:grid-cols-2" -className="text-lg sm:text-xl md:text-2xl" -``` - -### ❌ Complex Breakpoint Logic -```tsx -// Overly complex responsive behavior -className="flex flex-col sm:flex-row md:flex-col lg:flex-row" -``` - -### ❌ Environment-Specific Assumptions -```tsx -// Assuming responsive classes will always compile correctly -const isMobile = useMediaQuery('(max-width: 768px)'); // JS works -// But corresponding CSS classes might not compile on staging -``` - -## Success Metrics - -- **Consistency**: Identical behavior across localhost and staging -- **Mobile Performance**: Natural stacking and appropriate sizing on mobile devices -- **Maintainability**: Reduced complexity in responsive logic -- **Reliability**: No environment-specific layout failures - -## Implementation Checklist - -- [ ] Replace custom buttons with Button component -- [ ] Convert responsive grids to flex-wrap layouts -- [ ] Remove responsive typography classes -- [ ] Test across multiple environments -- [ ] Verify mobile layout behavior -- [ ] Ensure natural content wrapping -- [ ] Validate icon and image sizing -- [ ] Check navigation usability on mobile -- [ ] Test loading states and transitions -- [ ] Verify accessibility across screen sizes - -This strategy ensures robust, environment-agnostic mobile optimization that works consistently across all deployment environments while maintaining excellent user experience on mobile devices. diff --git a/.cursor/rules/performance-optimization.mdc b/.cursor/rules/performance-optimization.mdc deleted file mode 100644 index 4996136e7..000000000 --- a/.cursor/rules/performance-optimization.mdc +++ /dev/null @@ -1,244 +0,0 @@ ---- -description: This document outlines the performance optimization strategies used in the ResearchHub codebase. -globs: -alwaysApply: false ---- - # ResearchHub Performance Optimization - -This document outlines the performance optimization strategies used in the ResearchHub codebase. - -## React Optimization - - -2. **Hook Optimization**: - - Use `useCallback()` for functions passed as props - - Always specify dependencies correctly in dependency arrays - - ```tsx - - // Event handler passed as prop - const handleEdit = useCallback((id: string) => { - // Edit logic - }, [/* dependencies */]); - ``` - -3. **State Management**: - - Split large state objects into smaller, more focused pieces - - Use context selectors to prevent unnecessary re-renders - - Consider using libraries like Zustand or Jotai for complex state management - -## Next.js Optimization - -1. **Image Optimization**: - - Use Next.js `Image` component for all images - - Specify width and height to prevent layout shifts - - Use appropriate loading strategies (`lazy` for below-the-fold content) - - ```tsx - import Image from 'next/image'; - - // Good usage - User profile - ``` - -2. **Code Splitting**: - - Use dynamic imports for large components and libraries - - Implement page-level code splitting with Next.js's built-in features - - ```tsx - // Dynamic import for a large component - const HeavyChart = dynamic(() => import('@/components/HeavyChart'), { - loading: () => , - ssr: false, // If not needed on the server - }); - ``` - -3. **Server Components**: - - Use React Server Components for data fetching and rendering static content - - Keep client components small and focused on interactive elements - -4. **Font Optimization**: - - Use Next.js font optimization to reduce CLS - - Preload critical fonts - - ```tsx - import { Inter } from 'next/font/google'; - - const inter = Inter({ - subsets: ['latin'], - display: 'swap', - }); - ``` - -## Rendering Optimization - -1. **List Virtualization**: - - Implement virtualization for long lists (>50 items) - - Consider using libraries like `react-window` or `react-virtualized` - - ```tsx - import { FixedSizeList } from 'react-window'; - - const VirtualizedList = ({ items }) => ( - - {({ index, style }) => ( -
- {items[index].name} -
- )} -
- ); - ``` - -2. **Debouncing and Throttling**: - - Debounce search inputs and form fields that trigger API calls - - Throttle scroll and resize event handlers - - ```tsx - import { useState, useEffect } from 'react'; - import { debounce } from 'lodash-es'; - - const SearchInput = () => { - const [value, setValue] = useState(''); - - useEffect(() => { - const handleSearch = debounce((term: string) => { - // Perform search - }, 300); - - if (value) handleSearch(value); - - return () => { - handleSearch.cancel(); - }; - }, [value]); - - return ( - setValue(e.target.value)} - /> - ); - }; - ``` - -3. **Lazy Loading**: - - Implement lazy loading for below-the-fold content - - Use intersection observer for efficient detection - - ```tsx - import { useEffect, useRef, useState } from 'react'; - - const LazyLoadedComponent = () => { - const [isVisible, setIsVisible] = useState(false); - const ref = useRef(null); - - useEffect(() => { - const observer = new IntersectionObserver( - ([entry]) => { - if (entry.isIntersecting) { - setIsVisible(true); - observer.disconnect(); - } - }, - { rootMargin: '100px' } - ); - - if (ref.current) { - observer.observe(ref.current); - } - - return () => { - observer.disconnect(); - }; - }, []); - - return ( -
- {isVisible ? : } -
- ); - }; - ``` - -## Network Optimization - - -1. **Optimistic Updates**: - - Implement optimistic UI updates for better user experience - - Always handle failure cases gracefully - - ```tsx - import { useMutation, useQueryClient } from '@tanstack/react-query'; - - const UpdateButton = ({ user }) => { - const queryClient = useQueryClient(); - - const mutation = useMutation({ - mutationFn: updateUser, - onMutate: async (newUser) => { - await queryClient.cancelQueries(['user', user.id]); - const previousUser = queryClient.getQueryData(['user', user.id]); - queryClient.setQueryData(['user', user.id], newUser); - return { previousUser }; - }, - onError: (err, newUser, context) => { - queryClient.setQueryData( - ['user', user.id], - context.previousUser - ); - }, - onSettled: () => { - queryClient.invalidateQueries(['user', user.id]); - }, - }); - - return ( - - ); - }; - ``` - -3. **API Response Size**: - - Request only necessary data from APIs - - Implement pagination for large datasets - -## Bundle Optimization - -1. **Tree Shaking**: - - Import only what you need from libraries - - Use selective imports for large libraries - - ```tsx - // Good - only imports what's needed - import { Button } from '@/components/ui'; - import { formatDate } from '@/utils/date'; - - // Bad - imports the entire library - import * as UI from '@/components/ui'; - import * as Utils from '@/utils'; - ``` - -2. **Bundle Analysis**: - - Regularly analyze bundle size with tools like `@next/bundle-analyzer` - - Optimize or replace large dependencies - -3. **Code Splitting**: - - Split code at the route level - - Use dynamic imports for features not needed on initial load - -These performance optimization guidelines ensure that the ResearchHub application provides a smooth and responsive user experience. \ No newline at end of file diff --git a/.cursor/rules/project-structure.mdc b/.cursor/rules/project-structure.mdc deleted file mode 100644 index 21417b532..000000000 --- a/.cursor/rules/project-structure.mdc +++ /dev/null @@ -1,70 +0,0 @@ ---- -description: -globs: -alwaysApply: false ---- - # ResearchHub Project Structure Guidelines - -This document outlines the structure and organization of the ResearchHub codebase to help ensure consistency and maintainability. - -## Directory Structure - -The project follows a standard Next.js 15 application structure with TypeScript: - -- `app/` - Contains the App Router and related components (Next.js 15 page structure) -- `pages/` - Contains the legacy Pages Router components -- `components/` - Contains reusable UI components - - `ui/` - Contains base UI components (buttons, inputs, etc.) - - `[Feature]/` - Feature-specific components (organized by domain) -- `hooks/` - Custom React hooks -- `utils/` - Helper functions and utilities -- `services/` - API service functions -- `contexts/` - React context providers -- `public/` - Static assets -- `store/` - State management -- `types/` - TypeScript type definitions -- `constants/` - Application constants and configuration - -## Naming Conventions - -Follow these naming conventions throughout the codebase: - -1. **Files and Directories**: - - React components: PascalCase (e.g., `Button.tsx`, `UserProfile.tsx`) - - Utility files: camelCase (e.g., `formatDate.ts`, `apiHelpers.ts`) - - Test files: Same name as the file being tested with `.test` or `.spec` (e.g., `Button.test.tsx`) - -2. **Component Organization**: - - Group related components in feature-specific directories - - Place shared/generic UI components in the `components/ui/` directory - - Create index files for easier imports - -## Import Order - -Maintain a consistent import order in all files: - -1. React and Next.js imports -2. Third-party library imports -3. Component imports -4. Hook imports -5. Utility/helper imports -6. Type imports -7. Asset imports (styles, images, etc.) - -## Code Organization - -- Keep files reasonably sized (aim for <300 lines where possible) -- Extract reusable logic into custom hooks -- Extract complex or reusable UI elements into dedicated components - -## Path Aliases - -Use the configured path aliases to maintain clean imports: - -- `@/components` - For component imports -- `@/hooks` - For hook imports -- `@/utils` - For utility imports -- `@/types` - For type imports -- etc. - -This structure ensures code is organized logically and consistently throughout the project. \ No newline at end of file diff --git a/.cursor/rules/senior-eng-review.mdc b/.cursor/rules/senior-eng-review.mdc deleted file mode 100644 index d8c9dac2b..000000000 --- a/.cursor/rules/senior-eng-review.mdc +++ /dev/null @@ -1,374 +0,0 @@ ---- -description: This is a document that provides key PR reviewing as if they were a senior engineer -globs: ---- -# Key Learnings from Senior Engineer Code Review - -1. **Leverage React Providers for Global State** - - - Using context providers (like the ExchangeRateContext) is an effective way to share data application-wide. - - Encapsulating fetch logic within these providers or in dedicated hooks can simplify the rest of the codebase and improve maintainability. - -2. **Separate Concerns Between Services and View Layer** - - - Keep the "services" layer focused on fetching and basic data transformations. - - Avoid placing view-specific formatting (e.g., icon lookup or UI-ready strings) inside services—this leads to entanglement and potential reusability issues. - - Move highly UI-related transforms (for example, mapping distribution types to icons) into a dedicated "lib" or the "view layer" (e.g., inside component utilities or dedicated functions). - -3. **Use Dedicated Transformers Carefully** - - - Keep data transformations minimal and "model-oriented" in services, returning clean, typed data. - - Perform purely presentational transformations (e.g., formatting numeric values or currency strings) within the view layer to reduce the likelihood of unnecessary re-fetching or tight coupling. - -4. **Consider Creating Custom Hooks for Data Fetching** - - - Custom hooks (like "useUserBalance") can abstract away internal fetch logic and state management, similar to how "useSession" works in Next.js. - - This pattern improves readability, makes the code more composable, and centralizes asynchronous logic behind a well-defined interface. - -5. **Watch Out for Unnecessary Re-Renders and Side Effects** - - - Multiple data retrieval triggers can cause performance issues or data inconsistencies if not handled carefully. - - Use checks to ensure that data is only fetched when needed. Employ memoization or context effectively to prevent repetitive fetch calls. - -6. **Embrace a Layered Architecture** - - - A "service" layer for API interactions. - - A "types/model" layer to define and represent data structures. - - A "view/presentation" layer (React components) that handles all UI and formatting. - - This structure is analogous to classic architectural patterns (e.g., "MVC") and ensures a more scalable and maintainable application over time. - -7. **Balance Raw Data vs. Derived Data** - - - Keep a reference to the raw data (unformatted, untransformed) for potential debugging and logic needs. - - Derive only what's necessary for display in the UI to avoid confusion or data loss. - -8. **Consistent, Incremental Refinement** - - - Small refactors—such as relocating formatting functions and hooking into contexts—can make a significant difference in clarity and scalability. - - Regular feedback loops and code reviews help solidify best practices and ensure the application remains well-structured as it grows. - -9. **Minimal Client-Side Caching and Centralized Data** - - - Use React Context for global or shared data rather than implementing multiple fetch calls or storing redundant data in each component. - - Avoid heavy local caching or memoization beyond what React already provides; excessive caching can introduce staleness bugs and complicate the code. - - Avoid React Query. - - Fetch data once (for instance, in a provider) and rely on that single source of truth across your application. - - Keep caching boundaries small: if data is needed by multiple components, lifting it into Context makes sense; otherwise, store it locally. - - Be mindful of Next.js server/client boundaries; avoid turning everything into a client-side component, as it diminishes server-rendering advantages. - -10. **Keep File and Folder Names Consistent** - - - Use a clear and consistent naming scheme for files and folders. For example, if you have a component for an item in a list, call it `TransactionListItem.tsx`, not `TransactionItem.tsx`. - - For directories, use lowercase (e.g., `services`, `skeletons`) and consider capitalization for React component files. - -11. **Use Descriptive Names That Reflect Purpose** - - - Choose intuitive front-end labels that reflect the actual functionality; avoid ambiguous terms. - - If you have a button that exports data, label it "Export CSV" or "Export," rather than something vague like "export by year." - -12. **DTO vs. "Plain" Types** - - - In service files, use type names like `SomethingApiResponse` or `SomethingDto` to indicate it's a direct mapping from an API payload. - - For app-wide types (after you've transformed them), use straightforward names like `Transaction` or `Notification` that don't reference the API layer. - -13. **Keep Function Names Action-Oriented** - - - Use verbs and descriptive phrases, like `markAllAsRead()` instead of `mark()`. - - For fetching or transforming data, name them accordingly, e.g. `getTransactions()` or `transformTransaction()`. - -14. **Use Short but Unambiguous Events or Handlers** - - - Handlers should be clear about which user action they handle, such as `onClickDeposit` or `handleDeposit`. - - Keep them concise: `onClose`, `onClick`, `handleSubmit`, etc. - -15. **Simplify Type Organization and API Integration** - - - Avoid using `.dto` files for API types unless specifically needed (e.g., authentication). - - Maintain two primary files for each domain: - 1. A service file (e.g., `services/organization.service.ts`) handling API calls - 2. A types file (e.g., `types/organization.ts`) containing domain types and transformers - - Use `any` type for raw API responses in service files since we don't control the API - - Define clean, transformed types in the types directory that match our application's needs - - Implement defensive transformers with null checks (e.g., `response.items || []`) - - Keep service methods focused on API calls and basic error handling - - Return transformed, application-specific types from service methods - - Consider adding specific error classes for each service domain - -16. **State Management Best Practices** - - - Be cautious with object dependencies in useEffect hooks - - Remember that object reassignment (even with same values) triggers effects due to reference changes - - Use additional state variables to prevent unnecessary re-fetching when dealing with session-like objects - - Consider implementing fetch flags or other mechanisms to prevent duplicate API calls - -17. **Type Naming Conventions** - - - Avoid prefixes like "Formatted" in type names - - Use clear, domain-focused names (e.g., `Organization` instead of `FormattedOrganization`) - - Keep type definitions close to their domain context - - Use transformer functions to convert API data to application types - -18. **Error Handling Philosophy** - - - Let errors bubble up naturally unless there's a specific need to handle them - - Remove unnecessary try-catch blocks that just re-throw errors - - Fail fast when data is invalid rather than trying to recover silently - - Use try-catch only when: - 1. You can meaningfully recover from the error - 2. You need to transform the error type - 3. The API doesn't return data in the expected format - - For API interactions: - 1. Always use try-catch to transform technical errors into domain-specific ones - 2. Ensure errors are followed by user-facing alerts (even if implementation is deferred) - 3. Keep error messages user-friendly but specific enough for debugging - -19. **Debugging and Logging** - - - Avoid committing console.log statements to production code - - Use proper error tracking and monitoring tools instead - - If logging is needed, implement structured logging with proper levels - - Consider adding debug flags for development-only logging - -20. **Component Naming Best Practices** - - - Name components based on their primary function, not their location or styling - - Use action-oriented names for interactive components (e.g., `OrganizationSwitcher` instead of `OrganizationHeader`) - - Consider the component's role in the larger application context - - Examples: - - ✅ `OrganizationSwitcher` (describes action/purpose) - - ❌ `OrganizationHeader` (describes location/styling) - - ✅ `NoteList` (describes content type) - - ❌ `SidebarContent` (too generic/location-based) - -21. **Avoid Premature Library Adoption** - - - Carefully evaluate third-party libraries before adoption, even popular ones like React Query. - - Consider future architecture plans (e.g., potential GraphQL migration) when making library choices - - Prefer built-in React patterns (contexts, custom hooks) for data fetching until complexity justifies additional abstractions - - Development-mode double rendering (from React Strict Mode) is expected; validate actual issues in staging/production before optimizing - - -22. **URL as Source of Truth** - - - Use URL parameters as the primary source of truth for route-dependent state - - Instead of managing parallel state that needs to stay in sync with the URL: - ```typescript - // ❌ Don't sync state manually - const [currentOrg, setCurrentOrg] = useState(null); - useEffect(() => { - setCurrentOrg(findOrgBySlug(params.orgSlug)); - }, [params.orgSlug]); - - // ✅ Derive state from URL directly - const params = useParams(); - const currentOrg = organizations.find(o => o.slug === params.orgSlug); - ``` - - Let the URL drive state changes rather than managing duplicate state - - Avoid storing URL-derived data in local state unless absolutely necessary - -23. **Provider-Based State Management** - - - Design providers to be self-contained and autonomous: - ```typescript - // ❌ Don't expose state management methods - const OrgContext = { - selectedOrg, - setSelectedOrg, - syncWithUrl, // Avoid external sync methods - }; - - // ✅ Handle state management internally - const OrgContext = { - selectedOrg, // Only expose read-only state - organizations, - isLoading, - }; - ``` - - Providers should manage their own state synchronization - - Components should not need to know how provider state is managed - - Minimize the API surface of providers to prevent misuse - -24. **Component State Dependencies** - - - Be explicit about component state dependencies: - ```typescript - // ❌ Don't depend on multiple sources of truth - if (isLoadingNotes || !note || orgSlug !== selectedOrg?.slug) { - return ; - } - - // ✅ Use derived state from a single source - const isReady = selectedOrg?.slug === orgSlug && !isLoading && note; - if (!isReady) return ; - ``` - - Avoid components having to reconcile state from multiple sources - - Let providers handle state reconciliation - - Components should consume ready-to-use state - -25. **Effect Dependencies in Providers** - - - Keep effect dependencies minimal and stable: - ```typescript - // ❌ Don't include unnecessary dependencies - useEffect(() => { - syncOrganization(selectedOrg, currentSlug); - }, [selectedOrg, organizations, currentSlug]); // Too many deps - - // ✅ Use minimal, stable dependencies - useEffect(() => { - if (!organizations.length || !currentSlug) return; - const org = organizations.find(o => o.slug === currentSlug); - setSelectedOrg(org); - }, [currentSlug, organizations]); // Only essential deps - ``` - - Providers should have clear, focused effects - - Each effect should have a single responsibility - - Use early returns to prevent unnecessary state updates - -26. **Navigation and State Updates** - - - Separate navigation from state management: - ```typescript - // ❌ Don't couple navigation with state updates - const handleOrgSelect = async (org) => { - setSelectedOrg(org); - await router.push(`/org/${org.slug}`); - }; - - // ✅ Handle navigation only, let providers manage state - const handleOrgSelect = (org) => { - router.push(`/org/${org.slug}`); - }; - ``` - - Let URL changes drive state updates through providers - - Components should focus on navigation and UI interactions - - Avoid mixing navigation logic with state management - - These patterns help prevent: - - Duplicate API calls from multiple state syncs - - Race conditions in state updates - - Unnecessary component re-renders - - Complex state management in components - - -27. **Smart Orchestration Layer** - - - Use a smart orchestration layer between services and view components: - ```typescript - // ❌ Don't fetch data directly in view components - const NoteEditor = () => { - const { note } = useNote(noteId); - return ; - }; - - // ✅ Use a smart orchestrator to manage data flow - const NotePage = () => { - const { notes } = useOrganizationNotes(); - const initialNote = notes.find(n => n.id === noteId); - const [shouldFetchContent, setShouldFetchContent] = useState(false); - - // Control when to fetch full content - useEffect(() => { - if (initialNote) setShouldFetchContent(true); - }, [initialNote?.id]); - - return ; - }; - ``` - - Orchestration layer responsibilities: - 1. Coordinate data dependencies - 2. Control fetch timing - 3. Handle loading states - 4. Transform data for view layer - - Benefits: - - Prevents duplicate API calls - - Provides better loading UX - - Keeps view components simple - - Makes data flow explicit - -28. **Progressive Data Loading** - - - Load data in stages based on what the user needs immediately: - ```typescript - // ❌ Don't fetch everything upfront - const { fullNote } = useNote(noteId); - - // ✅ Load progressively - const { metadata } = useNotesList(); // Quick list view - const { content } = useNoteContent(shouldLoad ? id : null); // Full content when needed - ``` - - Implementation patterns: - 1. Start with minimal metadata - 2. Show immediate UI feedback - 3. Load full content when needed - 4. Update UI progressively - - Key principles: - - Show something to users as quickly as possible - - Only fetch what's needed when it's needed - - Use loading states to maintain UI responsiveness - - Let the orchestration layer control the loading sequence - -29. **Data Flow Control** - - - Manage data flow through clear boundaries: - ```typescript - // ❌ Don't mix concerns - const Component = () => { - const [data, setData] = useState(null); - useEffect(() => { - fetchData().then(setData); - }, [id]); - }; - - // ✅ Separate concerns with clear boundaries - // Service layer - Data fetching - const useNoteService = (id) => { - return NoteService.getNote(id); - }; - - // Orchestration layer - Flow control - const NotePage = () => { - const { initialNote } = useNotesList(); - const { fullNote } = useNoteContent(whenReady ? id : null); - }; - - // View layer - Display only - const NoteEditor = ({ content, isLoading }) => { - return ; - }; - ``` - - Key principles: - - Services handle raw data fetching - - Orchestration controls data flow - - Views handle presentation only - - Each layer has a single responsibility - - 30. **URL-Driven Navigation Patterns** - - - Handle navigation state changes in a predictable sequence: - ```typescript - // ❌ Don't mix navigation and data dependencies - const handleSwitch = async (item) => { - setSelected(item); - if (data.length) { - await router.push(`/${item.slug}/${data[0].id}`); - } else { - await router.push(`/${item.slug}`); - } - }; - - // ✅ Use conditional navigation with clear fallbacks - const handleSwitch = async (item) => { - const targetPath = data.length > 0 - ? `/${item.slug}/${data[0].id}` - : `/${item.slug}`; - await router.push(targetPath); - }; - ``` - - Key principles: - - Let URL changes trigger state updates, not the other way around - - Handle navigation edge cases (e.g., missing data) with clear fallbacks - - Keep navigation logic separate from data fetching - - Use the URL to drive the initial state of the application - - diff --git a/.cursor/rules/services-architecture.mdc b/.cursor/rules/services-architecture.mdc deleted file mode 100644 index 76cc32445..000000000 --- a/.cursor/rules/services-architecture.mdc +++ /dev/null @@ -1,227 +0,0 @@ ---- -description: This document outlines the architectural patterns and best practices for services in the ResearchHub codebase. -globs: -alwaysApply: false ---- - # ResearchHub Services Architecture - -This document outlines the architectural patterns and best practices for services in the ResearchHub codebase. - -## Service Structure - -1. **Class-based Organization**: - - Organize services as static TypeScript classes - - Group related API endpoints in domain-specific services - - Name services with the `Service` suffix (e.g., `NoteService`, `ReactionService`) - - ```typescript - export class FeatureService { - private static readonly BASE_PATH = '/api'; - - static async getItem(id: string): Promise { - // Implementation - } - - static async createItem(params: CreateItemParams): Promise { - // Implementation - } - } - ``` - -2. **API Client Pattern**: - - Use the central `ApiClient` for all HTTP requests - - Abstract HTTP methods (GET, POST, PATCH, DELETE) behind ApiClient methods - - Handle authentication, headers, and base URL configuration in ApiClient - - ```typescript - static async getItem(id: string): Promise { - const response = await ApiClient.get(`${this.BASE_PATH}/items/${id}`); - return transformItem(response); - } - ``` - -3. **Method Naming**: - - Use descriptive, action-based names for service methods - - Follow CRUD naming conventions: get, create, update, delete - - Use domain-specific verbs for specialized actions (e.g., `publish`, `flag`, `vote`) - -## Type Definitions - -1. **Request/Response Types**: - - Define clear interface types for API request parameters - - Name request parameter interfaces with `Params` suffix (e.g., `CreateNoteParams`) - - Use descriptive interface names that indicate the action - - ```typescript - export interface CreateItemParams { - title: string; - description: string; - categoryId: number; - tags?: string[]; - } - ``` - -2. **API Path Variables**: - - Store API path variables as private static readonly class properties - - Use uppercase for constant path variables - - Keep path construction logic inside service methods - - ```typescript - private static readonly BASE_PATH = '/api'; - private static readonly ITEMS_PATH = `${this.BASE_PATH}/items`; - ``` - -## Error Handling - -1. **Service-specific Errors**: - - Define service-specific error classes - - Extend the base Error class with additional properties - - Provide meaningful error messages and error codes - - ```typescript - export class ServiceNameError extends Error { - constructor( - message: string, - public readonly code?: string - ) { - super(message); - this.name = 'ServiceNameError'; - } - } - ``` - -2. **Error Propagation**: - - Catch and transform HTTP errors within service methods - - Provide consistent error formats for consumers - - Include relevant error details and status codes - - ```typescript - try { - return await ApiClient.post( - `${this.BASE_PATH}/items`, - params - ); - } catch (error) { - if (error instanceof ApiError) { - throw new ServiceNameError( - `Failed to create item: ${error.message}`, - error.status.toString() - ); - } - throw new ServiceNameError('An unexpected error occurred'); - } - ``` - -## Data Transformation - -1. **Transformation Pattern**: - - Transform API responses to match client-side data models - - Use transformer functions to convert snake_case to camelCase - - Ensure all transformed data includes proper TypeScript types - - ```typescript - static async getItem(id: string): Promise { - const response = await ApiClient.get(`${this.BASE_PATH}/items/${id}`); - return transformItem(response); - } - ``` - -2. **Transformer Organization**: - - Keep transformer functions with relevant type definitions - - Maintain consistent transformation patterns across services - - Preserve raw response data for debugging purposes - -## Request Parameters - -1. **Parameter Formatting**: - - Accept camelCase parameters in service methods - - Convert camelCase to snake_case for API requests when needed - - Handle URL parameter encoding for GET requests - - ```typescript - static async getItems(params: GetItemsParams): Promise { - const queryParams = new URLSearchParams(); - - if (params.categoryId) { - queryParams.append('category_id', params.categoryId.toString()); - } - - if (params.sortBy) { - queryParams.append('sort_by', params.sortBy); - } - - const url = `${this.ITEMS_PATH}?${queryParams.toString()}`; - const response = await ApiClient.get(url); - return transformItemsResponse(response); - } - ``` - -2. **Optional Parameters**: - - Use optional parameters with sensible defaults - - Validate required parameters before making API calls - - Provide clear error messages for missing required parameters - - ```typescript - static async searchItems(query: string, options: SearchOptions = {}): Promise { - if (!query) { - throw new ServiceNameError('Search query is required'); - } - - const queryParams = new URLSearchParams({ - q: query, - page: (options.page || 1).toString(), - limit: (options.limit || 20).toString(), - }); - - // Implementation - } - ``` - -## Documentation - -1. **JSDoc Comments**: - - Document all public service methods with JSDoc - - Include parameter descriptions, return types, and thrown exceptions - - Add examples for complex methods - - ```typescript - /** - * Fetches a specific item by ID - * @param id - The unique identifier of the item to fetch - * @returns A promise that resolves to the item - * @throws {ServiceNameError} When the request fails or the item doesn't exist - * @example - * const item = await FeatureService.getItem('123'); - */ - static async getItem(id: string): Promise { - // Implementation - } - ``` - -2. **Method Descriptions**: - - Describe the purpose of each method - - Document any side effects or special behaviors - - Include any authorization requirements - -## Service Composition - -1. **Service Boundaries**: - - Keep services focused on specific domains - - Avoid cross-service dependencies where possible - - Use composition when services need to work together - - ```typescript - // Good: Services focused on specific domains - await UserService.getProfile(userId); - await ContentService.getUserContent(userId); - - // Avoid: Services directly depending on each other - // Prefer composition at the hook or component level - ``` - -2. **File Organization**: - - One service class per file - - Group related interfaces and types with the service - - Export types needed by consumers - -These patterns ensure consistent, maintainable, and reliable services throughout the ResearchHub application. \ No newline at end of file diff --git a/.cursor/rules/state-management-architecture.mdc b/.cursor/rules/state-management-architecture.mdc deleted file mode 100644 index 61fb54b50..000000000 --- a/.cursor/rules/state-management-architecture.mdc +++ /dev/null @@ -1,391 +0,0 @@ ---- -description: This document outlines the architectural patterns and best practices for state management in the ResearchHub codebase. -globs: -alwaysApply: false ---- - # ResearchHub State Management Architecture - -This document outlines the architectural patterns and best practices for state management in the ResearchHub codebase. - -## State Management Hierarchy - -1. **Tiered State Strategy**: - - **Local Component State**: For UI-specific, ephemeral state - - **Custom Hooks**: For reusable, domain-specific state logic - - **Context Providers**: For shared, application-wide state - - **URL/Router State**: For navigation and shareable state - - ```typescript - // Local component state - const [isOpen, setIsOpen] = useState(false); - - // Custom hook for domain logic - const { data, isLoading, error } = useDocument(documentId); - - // Global context - const { notificationData, unreadCount } = useNotifications(); - - // Router state - const { params } = useParams(); - ``` - -## React Context - -1. **When to Use Context**: - - Use for state that needs to be accessed by multiple components across different parts of the component tree - - Appropriate for authentication, theme, notifications, and user preferences - - Not recommended for state that changes frequently (can cause re-renders) - - Avoid nesting too many contexts to prevent "wrapper hell" - - ```typescript - // Good use case for context: authentication state - const { status, data: session } = useSession(); - const { showAuthModal } = useAuthModalContext(); - - // Good use case for context: notifications - const { unreadCount } = useNotifications(); - ``` - -2. **Context Structure**: - - Create a dedicated file for each context (`contexts/FeatureContext.tsx`) - - Define a clear interface for the context value - - Include both state and actions that modify that state - - Provide meaningful default values - - ```typescript - interface FeatureContextType { - data: DataType[]; - isLoading: boolean; - error: Error | null; - actions: { - fetchData: () => Promise; - updateItem: (id: string, data: Partial) => Promise; - deleteItem: (id: string) => Promise; - }; - } - - const FeatureContext = createContext({ - data: [], - isLoading: false, - error: null, - actions: { - fetchData: async () => {}, - updateItem: async () => {}, - deleteItem: async () => {}, - }, - }); - ``` - -## Context Providers - -1. **Provider Implementation**: - - Use the 'use client' directive for client-side contexts - - Implement useState or useReducer for state management - - Use useCallback for functions passed to children - - Handle loading and error states consistently - - ```typescript - 'use client'; - - export function FeatureProvider({ children }: { children: ReactNode }) { - const [data, setData] = useState([]); - const [isLoading, setIsLoading] = useState(false); - const [error, setError] = useState(null); - - const fetchData = useCallback(async () => { - setIsLoading(true); - setError(null); - try { - const result = await FeatureService.getData(); - setData(result); - } catch (err) { - setError(err instanceof Error ? err : new Error('Failed to fetch data')); - } finally { - setIsLoading(false); - } - }, []); - - // Additional action functions using useCallback - - const value = { - data, - isLoading, - error, - actions: { - fetchData, - // Other actions - }, - }; - - return ( - - {children} - - ); - } - ``` - -2. **Context Consumer Hooks**: - - Create a custom hook for accessing each context - - Include type checking and error handling - - Use descriptive names for hooks (`useFeatureContext`) - - ```typescript - export function useFeatureContext() { - const context = useContext(FeatureContext); - if (!context) { - throw new Error('useFeatureContext must be used within a FeatureProvider'); - } - return context; - } - ``` - -## State Modeling - -1. **State Object Patterns**: - - Group related state in a single object - - Include loading, error, and data states for async operations - - Define clear interfaces for state objects - - ```typescript - interface FeatureState { - data: DataType[]; - isLoading: boolean; - error: Error | null; - page: number; - hasMore: boolean; - } - - const [state, setState] = useState({ - data: [], - isLoading: false, - error: null, - page: 1, - hasMore: true, - }); - - // Update state immutably - setState((prev) => ({ - ...prev, - data: [...prev.data, newItem], - })); - ``` - -2. **State Updates**: - - Use functional updates for state that depends on previous state - - Maintain immutability in all state updates - - Group related state updates where possible - - ```typescript - // Prefer this functional update pattern - setData((prevData) => [...prevData, newItem]); - - // For multiple related state updates - const handleSuccess = (result) => { - setData(result.data); - setHasMore(result.hasMore); - setPage(result.page); - setError(null); - setIsLoading(false); - }; - ``` - -## Server State Integration - -1. **Data Fetching Pattern**: - - Separate service layer for API calls - - Custom hooks for specific data fetching operations - - Consistent loading, error, and data state management - - ```typescript - export const useDocument = (documentId: string) => { - const [data, setData] = useState(null); - const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(null); - - const fetchDocument = useCallback(async () => { - if (!documentId) return; - - setIsLoading(true); - setError(null); - try { - const result = await DocumentService.getDocument(documentId); - setData(result); - } catch (err) { - setError(err instanceof Error ? err : new Error('Failed to fetch document')); - } finally { - setIsLoading(false); - } - }, [documentId]); - - useEffect(() => { - fetchDocument(); - }, [fetchDocument]); - - return { data, isLoading, error, refetch: fetchDocument }; - }; - ``` - -2. **Pagination and Infinite Scrolling**: - - Maintain page number or cursor in state - - Track loading state for initial load vs. loading more - - Append to existing data instead of replacing - - ```typescript - const loadMore = async () => { - if (!hasMore || isLoading) return; - - setIsLoadingMore(true); - try { - const nextPage = page + 1; - const result = await Service.getData({ page: nextPage, ...otherParams }); - setData((prev) => [...prev, ...result.items]); - setHasMore(result.hasMore); - setPage(nextPage); - } catch (error) { - console.error('Error loading more items:', error); - } finally { - setIsLoadingMore(false); - } - }; - ``` - -## Optimizing Context Performance - -1. **Context Splitting**: - - Split large contexts into smaller, more focused ones - - Separate frequently changing state from stable state - - Group related state and functions together - - ```typescript - // Instead of one large context - const AppContext = createContext({/* too many properties */}); - - // Split into focused contexts - const UserContext = createContext({/* user-related state */}); - const PreferencesContext = createContext({/* user preferences */}); - const NotificationsContext = createContext({/* notifications state */}); - ``` - -2. **Memoization**: - - Use React.memo for context provider components when absolutely needed - - Memoize context values with useMemo when absolutely needed - - Use useCallback for functions in context value when absolutely needed - - ```typescript - export function FeatureProvider({ children }: { children: ReactNode }) { - // State declarations - - // Memoize expensive functions - const processData = useCallback((data) => { - // Process data - }, [dependencies]); - - // Memoize the context value - const value = useMemo(() => ({ - data, - isLoading, - error, - processData, - }), [data, isLoading, error, processData]); - - return ( - - {children} - - ); - } - ``` - -## State Selectors - -1. **Selector Patterns**: - - Create custom hooks that select specific pieces of context - - Avoid re-renders by consuming only needed context parts - - Use memoization for derived state when absolutely needed - - ```typescript - // Main context hook - export const useFeatureContext = () => useContext(FeatureContext); - - // Selector hooks - export const useFeatureData = () => { - const { data } = useFeatureContext(); - return data; - }; - - export const useFeatureStatus = () => { - const { isLoading, error } = useFeatureContext(); - return { isLoading, error }; - }; - - export const useFeatureActions = () => { - const { actions } = useFeatureContext(); - return actions; - }; - ``` - -2. **Derived State**: - - Use useMemo for computed values based on state when absolutely needed - - Keep computations close to where the state is used - - Memoize selectors that perform expensive calculations - - ```typescript - const useFilteredItems = (filter) => { - const { data } = useFeatureContext(); - - // Memoized derived state - const filteredItems = useMemo(() => { - return data.filter(item => item.category === filter); - }, [data, filter]); - - return filteredItems; - }; - ``` - -## Local vs. Global State Guidelines - -1. **When to Use Local State**: - - UI state (modal open/closed, tabs, accordions) - - Form input values during editing - - Temporary visual states (hover, focus) - - Component-specific loading and error states - - ```typescript - // Good local state examples - const [isOpen, setIsOpen] = useState(false); - const [activeTab, setActiveTab] = useState('details'); - const [formValues, setFormValues] = useState({ name: '', email: '' }); - ``` - -2. **When to Use Context (Global State)**: - - User authentication and profile - - Theme and preferences - - Notifications - - Shopping cart or multi-step wizards - - Feature flags and configuration - - ```typescript - // Examples using context appropriately - const { user, status } = useAuthContext(); - const { theme, toggleTheme } = useThemeContext(); - const { notifications, markAsRead } = useNotifications(); - ``` - -3. **When to Use URL State**: - - Current view or page - - Filters and sorting options - - Search queries - - Detail item IDs - - Anything that should be shareable or bookmarkable - - ```typescript - // URL state examples - const router = useRouter(); - const { tab, sort, filter } = useSearchParams(); - - // Update URL state - router.push(`/dashboard?tab=${newTab}&sort=${sort}`); - ``` - -These patterns ensure consistent, maintainable, and performant state management throughout the ResearchHub application. \ No newline at end of file diff --git a/.cursor/rules/testing-standards.mdc b/.cursor/rules/testing-standards.mdc deleted file mode 100644 index 4d6e9dab5..000000000 --- a/.cursor/rules/testing-standards.mdc +++ /dev/null @@ -1,320 +0,0 @@ ---- -description: -globs: -alwaysApply: false ---- - # ResearchHub Testing Standards - -This document outlines the testing standards and best practices used in the ResearchHub codebase. - -## Testing Framework - -ResearchHub uses the following testing stack: - -1. **Jest** - For test running and assertions -2. **React Testing Library** - For testing React components -3. **MSW (Mock Service Worker)** - For mocking API requests -4. **Cypress** - For end-to-end testing - -## Test Types - -1. **Unit Tests**: - - Test individual functions and components in isolation - - Mock all external dependencies - - Focus on testing the logic rather than implementation details - -2. **Integration Tests**: - - Test multiple components or functions working together - - Test interactions between components - - Test API calls with mocked responses using MSW - -3. **End-to-End Tests**: - - Test entire workflows from the user's perspective - - Use Cypress to interact with the application - - Focus on critical user paths - -## Test File Organization - -1. **File Naming**: - - Unit and integration tests: `ComponentName.test.tsx` or `functionName.test.ts` - - End-to-end tests: `feature-name.spec.ts` - -2. **File Location**: - - Co-locate unit and integration tests with the code they're testing - - Place end-to-end tests in a dedicated `cypress/integration` directory - - ``` - components/ - ├── Button/ - │ ├── Button.tsx - │ └── Button.test.tsx - utils/ - ├── formatDate.ts - └── formatDate.test.ts - ``` - -## Unit Testing Standards - -1. **Component Testing**: - - Focus on testing behavior, not implementation details - - Test user interactions (clicks, input changes, etc.) - - Test rendering logic and conditional rendering - - Test accessibility features - - ```tsx - import { render, screen, fireEvent } from '@testing-library/react'; - import { Button } from './Button'; - - describe('Button', () => { - it('renders with correct text', () => { - render(); - expect(screen.getByRole('button', { name: /click me/i })).toBeInTheDocument(); - }); - - it('calls onClick when clicked', () => { - const handleClick = jest.fn(); - render(); - fireEvent.click(screen.getByRole('button')); - expect(handleClick).toHaveBeenCalledTimes(1); - }); - - it('is disabled when disabled prop is true', () => { - render(); - expect(screen.getByRole('button')).toBeDisabled(); - }); - }); - ``` - -2. **Utility Function Testing**: - - Test all edge cases - - Test with a variety of inputs - - Test error handling - - ```tsx - import { formatCurrency } from './formatCurrency'; - - describe('formatCurrency', () => { - it('formats positive numbers correctly', () => { - expect(formatCurrency(1000)).toBe('$1,000.00'); - expect(formatCurrency(1000.5)).toBe('$1,000.50'); - }); - - it('formats negative numbers correctly', () => { - expect(formatCurrency(-1000)).toBe('-$1,000.00'); - }); - - it('handles zero correctly', () => { - expect(formatCurrency(0)).toBe('$0.00'); - }); - - it('throws an error for non-numeric input', () => { - expect(() => formatCurrency('abc' as any)).toThrow(); - }); - }); - ``` - -3. **Hook Testing**: - - Use `renderHook` from `@testing-library/react-hooks` - - Test initial values, updates, and edge cases - - ```tsx - import { renderHook, act } from '@testing-library/react-hooks'; - import { useCounter } from './useCounter'; - - describe('useCounter', () => { - it('initializes with default value', () => { - const { result } = renderHook(() => useCounter()); - expect(result.current.count).toBe(0); - }); - - it('initializes with provided value', () => { - const { result } = renderHook(() => useCounter(10)); - expect(result.current.count).toBe(10); - }); - - it('increments the counter', () => { - const { result } = renderHook(() => useCounter()); - act(() => { - result.current.increment(); - }); - expect(result.current.count).toBe(1); - }); - }); - ``` - -## Integration Testing Standards - -1. **Component Integration**: - - Test groups of components working together - - Test state changes and their effects on the UI - - Use context providers when necessary - - ```tsx - import { render, screen, fireEvent } from '@testing-library/react'; - import { TodoList } from './TodoList'; - import { TodoProvider } from '@/contexts/TodoContext'; - - describe('TodoList', () => { - it('adds a new todo when form is submitted', () => { - render( - - - - ); - - // Fill in the form - fireEvent.change(screen.getByLabelText(/new todo/i), { - target: { value: 'Test todo' }, - }); - - // Submit the form - fireEvent.click(screen.getByRole('button', { name: /add/i })); - - // Check that the todo was added - expect(screen.getByText('Test todo')).toBeInTheDocument(); - }); - }); - ``` - -2. **API Mocking**: - - Use MSW to mock API requests - - Test success and error scenarios - - Test loading states - - ```tsx - import { rest } from 'msw'; - import { setupServer } from 'msw/node'; - import { render, screen, waitFor } from '@testing-library/react'; - import { UserProfile } from './UserProfile'; - - const server = setupServer( - rest.get('/api/user/:id', (req, res, ctx) => { - return res( - ctx.json({ - id: '123', - name: 'John Doe', - email: 'john@example.com', - }) - ); - }) - ); - - beforeAll(() => server.listen()); - afterEach(() => server.resetHandlers()); - afterAll(() => server.close()); - - describe('UserProfile', () => { - it('renders user data after loading', async () => { - render(); - - // Initially shows loading state - expect(screen.getByText(/loading/i)).toBeInTheDocument(); - - // After loading, shows user data - await waitFor(() => { - expect(screen.getByText('John Doe')).toBeInTheDocument(); - expect(screen.getByText('john@example.com')).toBeInTheDocument(); - }); - }); - - it('handles error state', async () => { - server.use( - rest.get('/api/user/:id', (req, res, ctx) => { - return res(ctx.status(500)); - }) - ); - - render(); - - await waitFor(() => { - expect(screen.getByText(/error/i)).toBeInTheDocument(); - }); - }); - }); - ``` - -## End-to-End Testing Standards - -1. **Test Coverage**: - - Test critical user flows - - Test authentication and authorization - - Test form submissions and validations - - ```tsx - describe('Authentication', () => { - beforeEach(() => { - cy.visit('/login'); - }); - - it('allows users to log in', () => { - cy.intercept('POST', '/api/login', { - body: { - token: 'fake-token', - user: { id: '123', name: 'Test User' }, - }, - }).as('loginRequest'); - - cy.get('input[name="email"]').type('user@example.com'); - cy.get('input[name="password"]').type('password123'); - cy.get('button[type="submit"]').click(); - - cy.wait('@loginRequest'); - cy.url().should('include', '/dashboard'); - cy.contains('Welcome, Test User'); - }); - - it('shows error messages for invalid login', () => { - cy.intercept('POST', '/api/login', { - statusCode: 401, - body: { message: 'Invalid credentials' }, - }).as('loginRequest'); - - cy.get('input[name="email"]').type('user@example.com'); - cy.get('input[name="password"]').type('wrong-password'); - cy.get('button[type="submit"]').click(); - - cy.wait('@loginRequest'); - cy.contains('Invalid credentials'); - cy.url().should('include', '/login'); - }); - }); - ``` - -2. **Test Isolation**: - - Reset state between tests - - Use fixtures for test data - - Clean up after tests - -## Test Coverage Requirements - -1. **Coverage Targets**: - - Unit tests: 85% code coverage - - Integration tests: Critical user flows - - End-to-end tests: Core user journeys - -2. **Critical Areas**: - - Authentication and authorization - - Form validation and submission - - Data fetching and error handling - - Complex business logic - -## Testing Best Practices - -1. **General Best Practices**: - - Write tests before or alongside code (TDD where appropriate) - - Keep tests simple and focused - - Don't test implementation details - - Test behavior, not functions - -2. **Maintainability**: - - Use descriptive test names - - Organize tests logically - - Extract common test setup into helper functions - - Keep tests independent of each other - -3. **Performance**: - - Keep unit tests fast - - Run slow tests (integration, e2e) in CI only - - Use mocks for external services - -These testing standards ensure code quality, prevent regressions, and provide confidence when refactoring or adding new features to the ResearchHub application. \ No newline at end of file diff --git a/.cursor/rules/types-architecture.mdc b/.cursor/rules/types-architecture.mdc deleted file mode 100644 index b79d6d141..000000000 --- a/.cursor/rules/types-architecture.mdc +++ /dev/null @@ -1,304 +0,0 @@ ---- -description: This document outlines the architectural patterns and best practices for TypeScript types in the ResearchHub codebase. -globs: -alwaysApply: false ---- - # ResearchHub Types Architecture - -This document outlines the architectural patterns and best practices for TypeScript types in the ResearchHub codebase. - -## Type Organization - -1. **Domain-specific Files**: - - Organize types by domain in dedicated files (e.g., `feed.ts`, `note.ts`) - - Group related interfaces, types, and enums together - - Export all types needed by consumers - - ```typescript - // feed.ts - export interface FeedEntry { /* ... */ } - export type FeedActionType = 'upvote' | 'comment' | 'create'; - export enum ContentType { /* ... */ } - ``` - -2. **Naming Conventions**: - - Use PascalCase for interfaces, types, and enums - - Use descriptive names that convey purpose and structure - - Add appropriate suffixes to indicate usage (e.g., `Response`, `Params`) - - ```typescript - // Good naming examples - export interface NoteApiResponse { /* ... */ } - export type ContentType = 'paper' | 'post' | 'bounty'; - export enum FundingRequestStatus { /* ... */ } - ``` - -## Interface Structure - -1. **API Response Interfaces**: - - Define interfaces for all API responses - - Include API-specific naming (e.g., snake_case properties) - - Use the `ApiResponse` suffix for clarity - - ```typescript - export interface NoteApiResponse { - id: string; - title: string; - content: string; - created_by: string; - created_at: string; - updated_at: string; - // Other API properties - } - ``` - -2. **Application Model Interfaces**: - - Create client-side model interfaces with camelCase properties - - Include all properties needed by UI components - - Add optional properties for partial data scenarios - - ```typescript - export interface Note { - id: string; - title: string; - content: string; - createdBy: string; - createdAt: Date; - updatedAt: Date; - isEditable?: boolean; - // Other client-side properties - } - ``` - -3. **Extension Patterns**: - - Use interface extension to build upon base interfaces - - Create specialized interfaces for specific use cases - - Avoid deep inheritance hierarchies (prefer composition) - - ```typescript - export interface BaseNote { - id: string; - title: string; - } - - export interface NoteWithContent extends BaseNote { - content: string; - collaborators: User[]; - } - - export interface NoteListItem extends BaseNote { - excerpt: string; - updatedAt: Date; - } - ``` - -## Type Definitions - -1. **Union Types**: - - Use union types for values with a finite set of options - - Define clear, specific union types for related values - - Add documentation for non-obvious union types - - ```typescript - // Simple union of string literals - export type ContentType = 'paper' | 'post' | 'bounty' | 'funding_request'; - - // Union of interfaces - export type Content = Paper | Post | Bounty | FundingRequest; - ``` - -2. **Enums**: - - Use enums for fixed sets of related values - - Prefer string enums for better debugging and serialization - - Document enum values with JSDoc comments - - ```typescript - /** - * Status of a funding request - */ - export enum FundingRequestStatus { - DRAFT = 'draft', - SUBMITTED = 'submitted', - APPROVED = 'approved', - REJECTED = 'rejected', - FUNDED = 'funded', - COMPLETED = 'completed', - } - ``` - -3. **Literal Types**: - - Use literal types for specific, known values - - Combine literals into union types for better type checking - - Use const assertions for object literals when appropriate - - ```typescript - // Literal type with union - type ButtonSize = 'sm' | 'md' | 'lg' | 'xl'; - - // Const assertion for object literal - const ROUTES = { - HOME: '/', - FEED: '/feed', - PROFILE: '/profile', - } as const; - - type Route = typeof ROUTES[keyof typeof ROUTES]; - ``` - -## Type Transformations - -1. **Transformer Pattern**: - - Use transformer functions to convert between API and client models - - Maintain consistent transformation patterns across the application - - Preserve raw data for debugging when needed - - ```typescript - // Using the transformer utility - import { createTransformer } from '../types/transformer'; - - export const transformNote = createTransformer((data) => ({ - id: data.id, - title: data.title, - content: data.content, - createdBy: data.created_by, - createdAt: new Date(data.created_at), - updatedAt: new Date(data.updated_at), - // Transform other properties - })); - ``` - -2. **Partial Types**: - - Use `Partial` for optional update operations - - Define specific input types for partial updates - - Document which fields are required vs. optional - - ```typescript - export interface UpdateNoteParams { - id: string; // Required - title?: string; // Optional update fields - content?: string; - collaborators?: string[]; - } - - // Or using utility types - export type UpdateNoteParams = { - id: string; // Required - } & Partial>; - ``` - -## Type Utilities - -1. **Pick and Omit**: - - Use `Pick` to create a type with a subset of properties - - Use `Omit` to create a type without specific properties - - Combine utility types for complex type transformations - - ```typescript - // Creating a type with only specific properties - type NotePreview = Pick; - - // Creating a type without specific properties - type NoteForCreation = Omit; - ``` - -2. **Type Guards**: - - Implement type guards to narrow types - - Use descriptive function names for type guards - - Return type predicates for better type inference - - ```typescript - // Type guard for Content union type - export function isPaper(content: Content): content is Paper { - return content.type === 'paper'; - } - - // Usage example - if (isPaper(content)) { - // Content is now narrowed to Paper type - console.log(content.doi); // TypeScript knows this property exists - } - ``` - -## Documentation - -1. **JSDoc Comments**: - - Document complex types with JSDoc comments - - Include descriptions for non-obvious properties - - Add examples for complex or unusual types - - ```typescript - /** - * Represents a feed entry in the application - * @property id - Unique identifier for the entry - * @property content - The content object (Paper, Post, etc.) - * @property contentType - The type of content - * @property actions - List of actions performed on this content - */ - export interface FeedEntry { - id: string; - content: Content; - contentType: ContentType; - actions: FeedAction[]; - // Other properties - } - ``` - -2. **Inline Comments**: - - Add inline comments for complex type expressions - - Document the purpose of utility type transformations - - Explain non-obvious type constraints - - ```typescript - // Maps API response fields to client model fields - type UserModelMapping = { - [K in keyof UserApiResponse as CamelCase]: UserApiResponse[K]; - }; - - // Creates a discriminated union based on content type - type ContentByType = { - [K in ContentType]: { type: K } & ContentTypeMapping[K]; - }[ContentType]; - ``` - -## Type Safety - -1. **Strict Type Checking**: - - Enable strict mode in TypeScript configuration - - Avoid use of `any` type - - Use `unknown` instead of `any` for values of uncertain type - - ```typescript - // Avoid this - function processData(data: any): any { - return data.value; - } - - // Prefer this - function processData(data: unknown): number { - if (typeof data === 'object' && data !== null && 'value' in data) { - return typeof data.value === 'number' ? data.value : 0; - } - return 0; - } - ``` - -2. **Nullability**: - - Use optional properties (`?`) instead of union with `null` - - Handle null and undefined values consistently - - Use non-null assertion (`!`) sparingly and only when certain - - ```typescript - // Prefer optional properties - interface User { - id: string; - name: string; - bio?: string; // Better than bio: string | null - } - - // Handle nullability carefully - function getUserName(user?: User): string { - return user?.name ?? 'Unknown User'; - } - ``` - -These patterns ensure consistent, maintainable, and type-safe code throughout the ResearchHub application. \ No newline at end of file diff --git a/.cursor/rules/typescript-standards.mdc b/.cursor/rules/typescript-standards.mdc deleted file mode 100644 index 407f0a1cc..000000000 --- a/.cursor/rules/typescript-standards.mdc +++ /dev/null @@ -1,181 +0,0 @@ ---- -description: -globs: -alwaysApply: false ---- - # ResearchHub TypeScript Standards - -This document outlines the TypeScript standards and conventions used in the ResearchHub codebase. - -## Type Definitions - -1. **Interfaces vs Types**: - - Use `interface` for object shapes that may be extended - - Use `type` for unions, primitives, and complex types that won't be extended - - Prefer interfaces for public API definitions - - ```typescript - // Use interface for object shapes that might be extended - interface UserProfile { - id: string; - name: string; - email: string; - } - - // Use type for unions and more complex types - type Status = 'idle' | 'loading' | 'success' | 'error'; - type ButtonVariant = 'primary' | 'secondary' | 'ghost'; - ``` - -2. **Naming Conventions**: - - Use PascalCase for type names and interfaces - - Use descriptive names that indicate the purpose - - Append `Props` to component prop interfaces - - Prefix enum names with 'E' (e.g., `EButtonSize`) - -3. **Organization**: - - Place shared types in the `types/` directory - - Keep component-specific types in the component file - - Group related types together - -## Type Safety - -1. **Strict Type Checking**: - - Enable strict mode in `tsconfig.json` - - Avoid using `any` type when possible - - Use specific types instead of generic `object` type - -2. **Null and Undefined**: - - Use optional chaining (`?.`) for potentially undefined properties - - Use nullish coalescing (`??`) for default values - - Be explicit about nullable types with union types - - ```typescript - function getUser(id?: string): User | null { - if (!id) return null; - // ... - } - - // Handling nullable values - const username = user?.name ?? 'Guest'; - ``` - -3. **Type Assertions**: - - Use type assertions (`as Type`) sparingly - - Prefer type guards to narrow types safely - - Document why type assertions are necessary when used - -4. **Type Guards**: - - Use type predicates for custom type guards - - ```typescript - function isUser(obj: any): obj is User { - return obj && typeof obj === 'object' && 'id' in obj && 'name' in obj; - } - ``` - -## Generic Types - -1. **Using Generics**: - - Use generics to create reusable utility types - - Use descriptive type parameter names (T, U, etc. are acceptable for simple cases) - - Constrain generics when possible using extends - - ```typescript - // Generic function with constraint - function getProperty(obj: T, key: K): T[K] { - return obj[key]; - } - ``` - -2. **Common Generic Patterns**: - - Use `React.FC` for functional components (optional, as explicit return types are preferred) - - Use built-in generic types like `Partial`, `Pick`, and `Omit` - -## API Types - -1. **Request/Response Types**: - - Define specific types for API requests and responses - - Use descriptive names that indicate the purpose - - Keep related request/response types together - - ```typescript - interface GetUserRequest { - userId: string; - } - - interface GetUserResponse { - user: User; - lastLogin: string; - } - ``` - -2. **Data Transformations**: - - Define transformation functions between API and UI models - - Document any assumptions or edge cases - -## Event Handlers - -1. **Event Types**: - - Use specific event types from React - - Define clear callback types for custom events - - ```typescript - // React event handler - const handleClick = (event: React.MouseEvent) => { - // ... - }; - - // Custom event handler - interface TabChangeEvent { - previousIndex: number; - newIndex: number; - } - - type TabChangeHandler = (event: TabChangeEvent) => void; - ``` - -## Error Handling - -1. **Error Types**: - - Define custom error types with meaningful properties - - Use union types to represent different error states - - ```typescript - type APIError = { - code: string; - message: string; - status: number; - }; - - type LoadingState = - | { status: 'idle' } - | { status: 'loading' } - | { status: 'success'; data: T } - | { status: 'error'; error: APIError }; - ``` - -## Documentation - -1. **JSDoc Comments**: - - Use JSDoc comments for functions, interfaces, and complex types - - Document parameters, return types, and exceptions - - Include examples for complex functions - - ```typescript - /** - * Fetches a user by ID from the API - * @param id - The unique identifier of the user - * @returns A promise that resolves to the user or null if not found - * @throws {APIError} When the API request fails - * @example - * ``` - * const user = await fetchUser('123'); - * ``` - */ - async function fetchUser(id: string): Promise { - // ... - } - ``` - -These TypeScript standards ensure code quality, readability, and maintainability throughout the ResearchHub application. \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..11aee83b8 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,35 @@ +# ResearchHub Web + +Next.js frontend for ResearchHub. All data comes from the Django API in +`researchhub-backend`, reached through `NEXT_PUBLIC_API_URL`. + +## Checks +- Run `npm run lint` and `npm run type-check` before finishing. +- The pre-commit hook runs Prettier, ESLint, and a full type-check, so a commit + fails on a type error anywhere in the repo. +- The Playwright smoke tests in `smoke/` are the only test suite. They run + against a running environment (see `README.md`). + +## Dependencies +- `@tiptap-pro/*` and Font Awesome Pro install from private registries. Their + tokens live in a gitignored `.npmrc` at the repo root, so a fresh clone or git + worktree needs a copy of it before `npm install` works. +- Change dependencies with `npx -y npm@10.7.0 install`. Other npm versions + rewrite `package-lock.json` metadata. + +## Conventions +- Data flows component → hook → service → `ApiClient` (`services/client.ts`), + which attaches the auth token and throws `ApiError`. Services are + `services/*.service.ts` classes of static methods. +- The API returns snake_case. Each domain file in `types/` defines the camelCase + model and a `transform*` function built with `createTransformer` + (`types/transformer.ts`), which keeps the original payload on `.raw`. Services + return transformed models. +- Keep services and transformers to data; display formatting belongs in + components. +- `app/layouts/PageLayout.tsx` is the standard page shell. +- Build UI from `components/ui/` (`Button`, `Tabs`, `BaseModal`, ...) and merge + class names with `cn()` from `utils/styles.ts`. Modals go in + `components/modals/`. +- Icons come from `lucide-react` by default. +- Import with the `@/` alias. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..43c994c2d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md From 09d085f3f209dee135a13e23add41a5cd44f9717 Mon Sep 17 00:00:00 2001 From: Taki Koutsomitis Date: Wed, 7 Oct 2026 09:50:01 -0400 Subject: [PATCH 2/2] Tell agents to add tests only when asked --- AGENTS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AGENTS.md b/AGENTS.md index 11aee83b8..ff9077953 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -9,6 +9,7 @@ Next.js frontend for ResearchHub. All data comes from the Django API in fails on a type error anywhere in the repo. - The Playwright smoke tests in `smoke/` are the only test suite. They run against a running environment (see `README.md`). +- Add tests only when the user asks for them. ## Dependencies - `@tiptap-pro/*` and Font Awesome Pro install from private registries. Their