Contributing to Nixopus Frontend
This guide provides detailed instructions for contributing to the Nixopus frontend codebase.
Setup for Frontend Development
Prerequisites
- Node.js 18.0 or higher
- Yarn package manager
- A running instance of the Nixopus API (locally or remotely)
Environment Setup
bash# Clone the repository git clone https://github.com/raghavyuva/nixopus.git cd nixopus # Copy environment template cp view/.env.sample view/.env.local # Install dependencies cd view yarn install
Running the Development Server
bashcd view yarn dev
The development server will start on
http://localhost:3000
by default.
Project Structure
The frontend follows Next.js App Router structure with the following organization:
view/
├── app/ # Next.js App Router pages
│ ├── layout.tsx # Root layout
│ ├── page.tsx # Home page
│ ├── dashboard/ # Dashboard pages
│ ├── login/ # Authentication pages
│ ├── settings/ # Settings pages
│ └── ...
├── components/ # React components
│ ├── ui/ # UI components
│ ├── layout/ # Layout components
│ └── features/ # Feature-specific components
├── hooks/ # Custom React hooks
├── lib/ # Utility functions
├── redux/ # Redux store setup
└── types/ # TypeScript type definitions
Adding a New Feature
Create a New Branch
bashgit checkout -b feature/your-feature-name
Plan Your Implementation
Consider where your feature fits within the application:
- Is it a new page/route?
- Is it a component enhancement?
- Does it modify existing behavior?
Implementation Steps
For a New Page
Create a new directory in the appropriate place in the App Router structure:
view/app/your-feature/ ├── page.tsx # Main page component ├── layout.tsx # Optional layout └── components/ # Page-specific components
Example
page.tsx
:tsx'use client' import { useEffect, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import { fetchYourFeatureData } from '@/redux/your-feature/actions' import YourFeatureComponent from './components/YourFeatureComponent' export default function YourFeaturePage() { const dispatch = useDispatch() const { data, loading, error } = useSelector(state => state.yourFeature) useEffect(() => { dispatch(fetchYourFeatureData()) }, [dispatch]) if (loading) return <div>Loading...</div> if (error) return <div>Error: {error}</div> return ( <div className="container mx-auto py-8"> <h1 className="text-2xl font-bold mb-4">Your Feature</h1> <YourFeatureComponent data={data} /> </div> ) }
For a New Component
Create components in the appropriate directory:
tsx// view/components/features/your-feature/YourComponent.tsx import { useState } from 'react' import { Button } from '@/components/ui/button' interface YourComponentProps { title: string onAction: () => void } export default function YourComponent({ title, onAction }: YourComponentProps) { const [isActive, setIsActive] = useState(false) return ( <div className="border rounded-lg p-4"> <h2 className="text-xl font-semibold">{title}</h2> <p className="text-gray-600 my-2">Your component description</p> <Button variant={isActive ? 'default' : 'outline'} onClick={() => { setIsActive(!isActive) onAction() }} > {isActive ? 'Active' : 'Inactive'} </Button> </div> ) }
For Redux Integration
Create the necessary Redux files:
view/redux/your-feature/ ├── slice.ts # Redux Toolkit slice ├── actions.ts # Async actions └── selectors.ts # Selectors
To write down API calls, create a new file in the
services
directory using Redux Toolkit Query:Example:
tsx// view/redux/services/<featureFolder>/<featureName>Api.ts import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' import { baseQueryWithReauth } from '@/redux/base-query' import { <EntityType>, <CreateDto>, <UpdateDto> } from '@/redux/types/<featureFolder>' import { API_ENDPOINTS } from '@/redux/api-conf' export const <featureName>Api = createApi({ reducerPath: '<featureName>Api', baseQuery: baseQueryWithReauth, tagTypes: ['<TagType>'], endpoints: (builder) => ({ get<FeaturePascalPlural>: builder.query<<EntityType>[], void>({ query: () => ({ url: API_ENDPOINTS.<FEATURE_PLURAL>, method: 'GET' }), transformResponse: (res: { data: <EntityType>[] }) => res.data, providesTags: (result) => result ? [ ...result.map(({ id }) => ({ type: '<TagType>' as const, id })), { type: '<TagType>', id: 'LIST' }, ] : [{ type: '<TagType>', id: 'LIST' }], }), create<FeaturePascal>: builder.mutation<<EntityType>, <CreateDto>>({ query: (body) => ({ url: API_ENDPOINTS.ADD_<FEATURE_UPPER>, method: 'POST', body, }), invalidatesTags: [{ type: '<TagType>', id: 'LIST' }], }), update<FeaturePascal>: builder.mutation<<EntityType>, <UpdateDto>>({ query: (body) => ({ url: API_ENDPOINTS.UPDATE_<FEATURE_UPPER>, method: 'PUT', body, }), invalidatesTags: [{ type: '<TagType>', id: 'LIST' }], }), delete<FeaturePascal>: builder.mutation<void, string>({ query: (id) => ({ url: API_ENDPOINTS.DELETE_<FEATURE_UPPER>, method: 'DELETE', body: { id }, }), invalidatesTags: [{ type: '<TagType>', id: 'LIST' }], }), }), }) export const { useGet<FeaturePascalPlural>Query, useCreate<FeaturePascal>Mutation, useUpdate<FeaturePascal>Mutation, useDelete<FeaturePascal>Mutation, } = <featureName>Api
Add Tests
Create tests using Jest and React Testing Library:
tsx// view/components/features/your-feature/YourComponent.test.tsx import { render, screen, fireEvent } from '@testing-library/react' import YourComponent from './YourComponent' describe('YourComponent', () => { const mockOnAction = jest.fn() beforeEach(() => { jest.clearAllMocks() }) it('renders correctly with props', () => { render(<YourComponent title="Test Title" onAction={mockOnAction} />) expect(screen.getByText('Test Title')).toBeInTheDocument() expect(screen.getByText('Your component description')).toBeInTheDocument() expect(screen.getByRole('button')).toHaveTextContent('Inactive') }) it('calls onAction when button is clicked', () => { render(<YourComponent title="Test Title" onAction={mockOnAction} />) fireEvent.click(screen.getByRole('button')) expect(mockOnAction).toHaveBeenCalledTimes(1) expect(screen.getByRole('button')).toHaveTextContent('Active') }) })
UI Guidelines
Use Existing UI Components
Nixopus uses Shadcn UI components based on Radix UI for consistency. Explore the
components/ui
directory before creating new components.Follow Design System
- Use the project's color palette defined in
tailwind.config.js
- Follow spacing and typography guidelines
- Maintain responsive designs that work on all screen sizes
- Use the project's color palette defined in
Accessibility
- Use semantic HTML elements
- Add proper ARIA attributes when needed
- Ensure keyboard navigation works
- Maintain sufficient color contrast
Component Organization
- Break down large components into smaller, reusable pieces
- Use composition with children props when appropriate
- Follow the container/presentation component pattern
Code Style and Guidelines
TypeScript
- Use proper type definitions
- Avoid
any
types when possible - Create interfaces for component props
- Use type guards for conditional rendering
React Best Practices
- Use functional components with hooks
- Memoize expensive calculations with useMemo
- Optimize renders with useCallback for handler functions
- Use React.memo for pure components that render often
CSS and Styling
- Use Tailwind CSS for styling
- Follow the project's class naming conventions
- Group related classes for better readability
Code Formatting
The project uses ESLint and Prettier for code formatting:
bash# Check for lint errors yarn lint # Fix lint errors yarn lint:fix
Testing Your Changes
Run the Development Server
bashyarn dev
Run Tests
bashyarn test yarn test:watch # Watch mode
Build for Production
bashyarn build
Common Pitfalls
- Not updating types when modifying data structures
- Forgetting to handle loading and error states
- Not considering mobile responsiveness
- Creating duplicate functionality instead of reusing components
- Not testing edge cases and error scenarios
Submitting Your Contribution
Commit Changes
bashgit add . git commit -m "feat: add your feature"
Push and Create a Pull Request
bashgit push origin feature/your-feature-name
Follow the PR template and provide detailed information about your changes.
Need Help?
If you need assistance, feel free to:
- Create an issue on GitHub
- Reach out on the project's Discord channel
- Contact the maintainers directly
Thank you for contributing to Nixopus!