# Contributing to Platform Marketing Content (PMC)

Thank you for your interest in contributing to Platform Marketing Content! This document provides guidelines and standards for contributing to this project.

## Table of Contents

- [Prerequisites](#prerequisites)
- [Initial Setup](#initial-setup)
- [Project Structure](#project-structure)
- [Development Workflow](#development-workflow)
- [Code Conventions](#code-conventions)
- [Repository Pattern](#repository-pattern)
- [Testing Guidelines](#testing-guidelines)
- [Pull Request Process](#pull-request-process)
- [Database Migrations](#database-migrations)
- [API Documentation](#api-documentation)

---

## Prerequisites

Before you begin, ensure you have the following installed:

- **Node.js**: Version 20+ (LTS recommended)
- **npm**: Version 9+ (comes with Node.js)
- **PostgreSQL**: Version 16
- **Redis**: Version 7
- **Git**: Latest version
- **MinIO** (Optional): For local object storage testing
- **ComfyUI** (Optional): For AI image generation features

### Recommended Tools

- **Visual Studio Code** with extensions:
  - ESLint
  - Prettier
  - TypeScript and JavaScript Language Features
  - REST Client

---

## Initial Setup

### 1. Clone the Repository

```bash
git clone <repository-url>
cd platform_marketing_content
```

### 2. Install Dependencies

```bash
# Backend
cd apps/backend
npm install

# Frontend
cd ../frontend
npm install
```

### 3. Environment Configuration

#### Backend Setup

```bash
cd apps/backend
cp .env.example .env
```

Edit `.env` with your local configuration:

```env
# Database
DB_HOST=localhost
DB_PORT=5432
DB_NAME=pmc_dev
DB_USER=pmc_user
DB_PASSWORD=pmc_secret_2024

# Redis
REDIS_HOST=localhost
REDIS_PORT=6379

# JWT
JWT_SECRET=your-secret-key-here
JWT_EXPIRES_IN=1d

# Server
PORT=3111
NODE_ENV=development
```

#### Frontend Setup

```bash
cd apps/frontend
cp .env.example .env
```

Edit `.env`:

```env
VITE_API_URL=http://localhost:3111
VITE_WS_URL=ws://localhost:3111
```

### 4. Database Setup

Create the database:

```bash
# Connect to PostgreSQL
psql -U postgres

# Create user and database
CREATE USER pmc_user WITH PASSWORD 'pmc_secret_2024';
CREATE DATABASE pmc_dev OWNER pmc_user;
GRANT ALL PRIVILEGES ON DATABASE pmc_dev TO pmc_user;
\q
```

Run migrations:

```bash
cd apps/backend
npm run typeorm migration:run
```

### 5. Start Development Servers

```bash
# Backend (Terminal 1)
cd apps/backend
npm run start:dev

# Frontend (Terminal 2)
cd apps/frontend
npm run dev
```

Access the application:
- Frontend: http://localhost:3110
- Backend API: http://localhost:3111
- API Documentation: http://localhost:3111/api

---

## Project Structure

```
platform_marketing_content/
├── apps/
│   ├── backend/                 # NestJS Backend API
│   │   ├── src/
│   │   │   ├── common/          # Shared utilities, decorators, filters
│   │   │   ├── config/          # Configuration modules
│   │   │   ├── modules/         # Feature modules
│   │   │   │   ├── auth/        # Authentication & authorization
│   │   │   │   ├── tenants/     # Multi-tenancy
│   │   │   │   ├── projects/    # Content projects
│   │   │   │   ├── assets/      # Media asset management
│   │   │   │   └── crm/         # Clients, brands, products
│   │   │   ├── shared/          # Shared entities, services, repositories
│   │   │   │   ├── constants/   # Global constants
│   │   │   │   ├── dto/         # Data Transfer Objects
│   │   │   │   ├── entities/    # Shared TypeORM entities
│   │   │   │   ├── repositories/# Repository interfaces & factory
│   │   │   │   └── services/    # Shared services
│   │   │   ├── app.module.ts
│   │   │   └── main.ts
│   │   ├── test/                # E2E tests
│   │   └── package.json
│   │
│   └── frontend/                # React Frontend
│       ├── src/
│       │   ├── components/      # Reusable UI components
│       │   ├── pages/           # Page components
│       │   ├── hooks/           # Custom React hooks
│       │   ├── services/        # API service layer
│       │   └── utils/           # Utility functions
│       └── package.json
│
├── database/                    # Database schemas and migrations
├── docs/                        # Documentation
├── docker/                      # Docker configurations
├── nginx/                       # Nginx configurations
├── orchestration/               # Deployment guides
├── scripts/                     # Utility scripts
├── .env.ports                   # Port configuration registry
└── README.md
```

---

## Development Workflow

### Branch Naming

Follow these conventions:

- `feature/description` - New features
- `fix/description` - Bug fixes
- `refactor/description` - Code refactoring
- `docs/description` - Documentation updates
- `test/description` - Test additions/updates
- `chore/description` - Maintenance tasks

Example: `feature/add-project-templates`

### Commit Messages

Follow the [Conventional Commits](https://www.conventionalcommits.org/) specification:

```
<type>(<scope>): <subject>

[optional body]

[optional footer]
```

**Types:**
- `feat`: New feature
- `fix`: Bug fix
- `docs`: Documentation changes
- `style`: Code style changes (formatting, semicolons, etc.)
- `refactor`: Code refactoring
- `test`: Adding or updating tests
- `chore`: Maintenance tasks

**Examples:**

```bash
feat(projects): add project template system
fix(auth): resolve JWT token expiration issue
docs(api): update Swagger documentation for content endpoints
refactor(repositories): implement repository pattern for all services
test(crm): add unit tests for client service
```

---

## Code Conventions

### TypeScript Style Guide

#### General Rules

1. **Use TypeScript strict mode** - Already enabled in `tsconfig.json`
2. **Prefer interfaces over types** for object shapes
3. **Use explicit return types** for all functions
4. **Avoid `any` type** - Use `unknown` if type is truly unknown
5. **Use meaningful variable names** - Avoid abbreviations except for common ones (e.g., `id`, `dto`)

#### Formatting

The project uses **Prettier** and **ESLint** for code formatting:

```bash
# Format code
npm run format

# Lint code
npm run lint

# Type check
npm run typecheck
```

#### Naming Conventions

- **Classes**: PascalCase - `ProjectService`, `ClientController`
- **Interfaces**: PascalCase with `I` prefix - `IBaseRepository`, `IProjectService`
- **Types**: PascalCase - `ServiceContext`, `PaginationOptions`
- **Functions/Methods**: camelCase - `findById`, `createProject`
- **Variables**: camelCase - `projectData`, `userId`
- **Constants**: UPPER_SNAKE_CASE - `DEFAULT_PAGE_SIZE`, `MAX_FILE_SIZE`
- **Enums**: PascalCase (name) and UPPER_SNAKE_CASE (values)

```typescript
enum UserRole {
  SUPER_ADMIN = 'SUPER_ADMIN',
  TENANT_ADMIN = 'TENANT_ADMIN',
  USER = 'USER',
}
```

### NestJS Conventions

#### Module Structure

Each feature module should follow this structure:

```
module-name/
├── controllers/
│   └── module-name.controller.ts
├── services/
│   └── module-name.service.ts
├── repositories/
│   └── module-name.repository.ts
├── dto/
│   ├── create-module-name.dto.ts
│   └── update-module-name.dto.ts
├── entities/
│   └── module-name.entity.ts
└── module-name.module.ts
```

#### Dependency Injection

Use constructor injection for all dependencies:

```typescript
@Injectable()
export class ProjectService {
  constructor(
    private readonly projectRepository: ProjectRepository,
    private readonly logger: Logger,
  ) {}
}
```

---

## Repository Pattern

### Overview

This project implements the **Repository Pattern** to abstract data access logic and decouple business logic from database operations.

### Repository Interfaces

Located in `/apps/backend/src/shared/repositories/`:

- `IBaseRepository<T>` - Full CRUD operations
- `IReadOnlyRepository<T>` - Read-only operations
- `IWriteOnlyRepository<T>` - Write-only operations

### Using the Repository Pattern

#### 1. Create a Repository Interface (Optional)

For domain-specific operations, extend the base interface:

```typescript
// apps/backend/src/modules/projects/repositories/project.repository.interface.ts
import { IBaseRepository, ServiceContext } from '@shared/repositories';
import { Project } from '../entities/project.entity';

export interface IProjectRepository extends IBaseRepository<Project> {
  findByClientId(ctx: ServiceContext, clientId: string): Promise<Project[]>;
  findActiveProjects(ctx: ServiceContext): Promise<Project[]>;
}
```

#### 2. Implement the Repository

```typescript
// apps/backend/src/modules/projects/repositories/project.repository.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { IProjectRepository } from './project.repository.interface';
import { Project } from '../entities/project.entity';
import { ServiceContext } from '@shared/repositories';

@Injectable()
export class ProjectRepository implements IProjectRepository {
  constructor(
    @InjectRepository(Project)
    private readonly repository: Repository<Project>,
  ) {}

  async findById(ctx: ServiceContext, id: string): Promise<Project | null> {
    return this.repository.findOne({
      where: { id, tenantId: ctx.tenantId },
    });
  }

  async findByClientId(ctx: ServiceContext, clientId: string): Promise<Project[]> {
    return this.repository.find({
      where: { clientId, tenantId: ctx.tenantId },
    });
  }

  async findActiveProjects(ctx: ServiceContext): Promise<Project[]> {
    return this.repository.find({
      where: {
        tenantId: ctx.tenantId,
        isActive: true,
      },
    });
  }

  // Implement other IBaseRepository methods...
}
```

#### 3. Register in Factory (Optional)

For advanced dependency injection scenarios:

```typescript
// apps/backend/src/app.module.ts
import { RepositoryFactory } from '@shared/repositories';

@Module({
  // ...
})
export class AppModule implements OnModuleInit {
  constructor(private readonly projectRepository: ProjectRepository) {}

  onModuleInit() {
    const factory = RepositoryFactory.getInstance();
    factory.register('ProjectRepository', this.projectRepository);
  }
}
```

#### 4. Use in Services

```typescript
// apps/backend/src/modules/projects/services/project.service.ts
import { Injectable } from '@nestjs/common';
import { ProjectRepository } from '../repositories/project.repository';
import { ServiceContext } from '@shared/repositories';

@Injectable()
export class ProjectService {
  constructor(private readonly projectRepository: ProjectRepository) {}

  async getProjectById(ctx: ServiceContext, id: string) {
    return this.projectRepository.findById(ctx, id);
  }

  async getClientProjects(ctx: ServiceContext, clientId: string) {
    return this.projectRepository.findByClientId(ctx, clientId);
  }
}
```

### ServiceContext

All repository methods require a `ServiceContext` for multi-tenancy:

```typescript
export interface ServiceContext {
  tenantId: string;
  userId: string;
}
```

Extract from request in controllers:

```typescript
@Get(':id')
async getProject(
  @Param('id') id: string,
  @Req() req: Request,
) {
  const ctx: ServiceContext = {
    tenantId: req.user.tenantId,
    userId: req.user.id,
  };

  return this.projectService.getProjectById(ctx, id);
}
```

---

## Testing Guidelines

### Unit Tests

Location: `*.spec.ts` files next to source files

```typescript
// project.service.spec.ts
describe('ProjectService', () => {
  let service: ProjectService;
  let repository: MockType<ProjectRepository>;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        ProjectService,
        {
          provide: ProjectRepository,
          useFactory: mockRepository,
        },
      ],
    }).compile();

    service = module.get<ProjectService>(ProjectService);
    repository = module.get(ProjectRepository);
  });

  it('should find project by id', async () => {
    const mockProject = { id: '1', name: 'Test Project' };
    repository.findById.mockResolvedValue(mockProject);

    const ctx: ServiceContext = { tenantId: 't1', userId: 'u1' };
    const result = await service.getProjectById(ctx, '1');

    expect(result).toEqual(mockProject);
    expect(repository.findById).toHaveBeenCalledWith(ctx, '1');
  });
});
```

### E2E Tests

Location: `apps/backend/test/`

```bash
npm run test:e2e
```

### Running Tests

```bash
# Run all tests
npm test

# Run tests in watch mode
npm run test:watch

# Run tests with coverage
npm run test:cov

# Run specific test file
npm test -- project.service.spec.ts
```

### Test Coverage Requirements

- **Minimum coverage**: 80% for services and repositories
- **Controllers**: Test happy paths and error cases
- **Services**: Test all business logic
- **Repositories**: Test database queries

---

## Pull Request Process

### Before Submitting

1. **Update your branch** with the latest `main`:
   ```bash
   git checkout main
   git pull origin main
   git checkout your-branch
   git rebase main
   ```

2. **Run all checks**:
   ```bash
   npm run lint
   npm run typecheck
   npm test
   npm run build
   ```

3. **Update documentation** if needed

4. **Write/update tests** for new features

### PR Guidelines

1. **Title**: Use conventional commit format
   - Example: `feat(projects): add project template system`

2. **Description**: Include:
   - What changed and why
   - Related issue numbers (if applicable)
   - Screenshots (for UI changes)
   - Migration instructions (if applicable)

3. **Checklist**:
   - [ ] Code follows style guidelines
   - [ ] Tests added/updated and passing
   - [ ] Documentation updated
   - [ ] No console.log or debugging code
   - [ ] TypeScript types are properly defined
   - [ ] Database migrations created (if needed)

### Review Process

- **At least one approval** required
- **All CI checks** must pass
- **Resolve all comments** before merging
- Use **Squash and Merge** for feature branches

---

## Database Migrations

### Creating a Migration

```bash
cd apps/backend

# Generate migration based on entity changes
npm run typeorm migration:generate -- -n MigrationName

# Create empty migration
npm run typeorm migration:create -- -n MigrationName
```

### Running Migrations

```bash
# Run pending migrations
npm run typeorm migration:run

# Revert last migration
npm run typeorm migration:revert

# Show migration status
npm run typeorm migration:show
```

### Migration Best Practices

1. **Always test migrations** on a copy of production data
2. **Make migrations reversible** - Implement `down()` method
3. **Include data migrations** if schema changes affect existing data
4. **Use transactions** for data integrity
5. **Document breaking changes** in PR description

---

## API Documentation

### Swagger/OpenAPI

The API is documented using Swagger. Access at: `http://localhost:3111/api`

### Documenting Endpoints

Use NestJS decorators:

```typescript
@ApiTags('projects')
@ApiBearerAuth()
@Controller('projects')
export class ProjectController {
  @Post()
  @ApiOperation({ summary: 'Create a new project' })
  @ApiResponse({
    status: 201,
    description: 'Project created successfully',
    type: Project,
  })
  @ApiResponse({
    status: 400,
    description: 'Invalid input data',
  })
  async create(@Body() dto: CreateProjectDto) {
    // ...
  }
}
```

### DTOs

Document all DTOs:

```typescript
export class CreateProjectDto {
  @ApiProperty({
    description: 'Project name',
    example: 'Summer Campaign 2024',
  })
  @IsString()
  @MinLength(3)
  name: string;

  @ApiProperty({
    description: 'Project description',
    example: 'Marketing campaign for summer collection',
    required: false,
  })
  @IsOptional()
  @IsString()
  description?: string;
}
```

---

## Questions or Issues?

- **Bug Reports**: Create an issue with the `bug` label
- **Feature Requests**: Create an issue with the `enhancement` label
- **Questions**: Create an issue with the `question` label

Thank you for contributing to Platform Marketing Content!
