---
name: gov-platform-module
description: >
  إنشاء modules جديدة للمنصة الموحدة الحكومية (المنصة الموحدة) باستخدام React frontend و .NET 8 backend و SQL Server.
  استخدم هذا الـ skill عند: إنشاء module جديد، إضافة صفحة CRUD، بناء feature جديدة في المنصة،
  إنشاء شاشة إدارية، بناء تقارير، أو أي عمل يتعلق بالمنصة الموحدة الحكومية.
  يشمل دعم كامل للغة العربية RTL، نظام الصلاحيات، والتكامل مع الأنظمة الحكومية.
---

# Gov Platform Module Generator
# مولّد وحدات المنصة الموحدة

ينشئ modules كاملة للمنصة الموحدة الحكومية مع الالتزام بالمعايير والأنماط المعتمدة.

## Architecture Overview

المنصة تتبع Clean Architecture:

```
Solution/
├── src/
│   ├── API/                          # .NET 8 Web API
│   │   ├── Controllers/
│   │   ├── Filters/
│   │   └── Middleware/
│   ├── Application/                  # Business Logic (CQRS)
│   │   ├── Features/{ModuleName}/
│   │   │   ├── Commands/
│   │   │   ├── Queries/
│   │   │   ├── DTOs/
│   │   │   └── Validators/
│   │   ├── Common/
│   │   └── Interfaces/
│   ├── Domain/                       # Entities & Value Objects
│   │   ├── Entities/
│   │   ├── Enums/
│   │   └── ValueObjects/
│   └── Infrastructure/               # Data Access & External
│       ├── Persistence/
│       │   ├── Configurations/
│       │   └── Repositories/
│       └── Services/
├── client/                           # React Frontend
│   └── src/
│       ├── modules/{moduleName}/
│       │   ├── pages/
│       │   ├── components/
│       │   ├── hooks/
│       │   ├── services/
│       │   ├── types/
│       │   └── index.ts
│       ├── shared/
│       └── layouts/
└── database/
    └── migrations/
```

## Module Generation Workflow

When creating a new module, follow these steps in order:

### Step 1: Domain Layer
Create the entity with Arabic display names and audit fields:

```csharp
// Domain/Entities/{EntityName}.cs
public class EntityName : BaseAuditableEntity
{
    public int Id { get; set; }
    
    [Display(Name = "الاسم بالعربي")]
    [Required(ErrorMessage = "حقل مطلوب")]
    public string NameAr { get; set; } = string.Empty;
    
    [Display(Name = "الاسم بالإنجليزي")]
    public string? NameEn { get; set; }
    
    public bool IsActive { get; set; } = true;
    public int? CreatedBy { get; set; }
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
    public int? UpdatedBy { get; set; }
    public DateTime? UpdatedAt { get; set; }
}
```

### Step 2: Application Layer (CQRS Pattern)
Generate Commands and Queries using MediatR:

```csharp
// Application/Features/{Module}/Commands/Create{Entity}Command.cs
public record Create{Entity}Command : IRequest<Result<int>>
{
    public string NameAr { get; init; } = string.Empty;
    public string? NameEn { get; init; }
}

public class Create{Entity}CommandHandler 
    : IRequestHandler<Create{Entity}Command, Result<int>>
{
    private readonly IApplicationDbContext _context;
    private readonly ICurrentUserService _currentUser;

    public async Task<Result<int>> Handle(
        Create{Entity}Command request, CancellationToken ct)
    {
        var entity = new Entity
        {
            NameAr = request.NameAr,
            NameEn = request.NameEn,
            CreatedBy = _currentUser.UserId
        };
        
        _context.Set<Entity>().Add(entity);
        await _context.SaveChangesAsync(ct);
        return Result<int>.Success(entity.Id);
    }
}
```

Always include FluentValidation:
```csharp
public class Create{Entity}CommandValidator 
    : AbstractValidator<Create{Entity}Command>
{
    public Create{Entity}CommandValidator()
    {
        RuleFor(x => x.NameAr)
            .NotEmpty().WithMessage("الاسم بالعربي مطلوب")
            .MaximumLength(200).WithMessage("الحد الأقصى 200 حرف");
    }
}
```

### Step 3: API Controller
```csharp
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class {Module}Controller : BaseApiController
{
    [HttpGet]
    [Permission("{Module}.View")]
    public async Task<ActionResult<PaginatedList<{Entity}Dto>>> GetAll(
        [FromQuery] Get{Entity}ListQuery query)
        => Ok(await Mediator.Send(query));

    [HttpGet("{id}")]
    [Permission("{Module}.View")]
    public async Task<ActionResult<{Entity}Dto>> GetById(int id)
        => Ok(await Mediator.Send(new Get{Entity}ByIdQuery(id)));

    [HttpPost]
    [Permission("{Module}.Create")]
    public async Task<ActionResult<int>> Create(Create{Entity}Command command)
        => Ok(await Mediator.Send(command));

    [HttpPut("{id}")]
    [Permission("{Module}.Edit")]
    public async Task<ActionResult> Update(int id, Update{Entity}Command command)
    {
        command = command with { Id = id };
        await Mediator.Send(command);
        return NoContent();
    }

    [HttpDelete("{id}")]
    [Permission("{Module}.Delete")]
    public async Task<ActionResult> Delete(int id)
    {
        await Mediator.Send(new Delete{Entity}Command(id));
        return NoContent();
    }
}
```

### Step 4: React Frontend Module

```typescript
// modules/{moduleName}/types/index.ts
export interface {Entity} {
  id: number;
  nameAr: string;
  nameEn?: string;
  isActive: boolean;
  createdAt: string;
  createdBy?: number;
}

export interface {Entity}FormData {
  nameAr: string;
  nameEn?: string;
  isActive: boolean;
}
```

```typescript
// modules/{moduleName}/services/{entity}Service.ts
import { apiClient } from '@/shared/api';

export const {entity}Service = {
  getAll: (params?: PaginationParams) =>
    apiClient.get<PaginatedResult<{Entity}>>(`/api/{module}`, { params }),
  
  getById: (id: number) =>
    apiClient.get<{Entity}>(`/api/{module}/${id}`),
  
  create: (data: {Entity}FormData) =>
    apiClient.post<number>(`/api/{module}`, data),
  
  update: (id: number, data: {Entity}FormData) =>
    apiClient.put(`/api/{module}/${id}`, data),
  
  delete: (id: number) =>
    apiClient.delete(`/api/{module}/${id}`),
};
```

```tsx
// modules/{moduleName}/pages/{Entity}ListPage.tsx
// Always use RTL layout with Arabic-first labels
const {Entity}ListPage: React.FC = () => {
  const { data, isLoading } = useQuery(
    ['{entity}List', pagination],
    () => {entity}Service.getAll(pagination)
  );

  const columns: ColumnDef<{Entity}>[] = [
    { header: 'الاسم', accessorKey: 'nameAr' },
    { header: 'Name', accessorKey: 'nameEn' },
    { header: 'الحالة', accessorKey: 'isActive',
      cell: ({ value }) => value ? 'فعال' : 'غير فعال' },
    { header: 'الإجراءات', /* action buttons */ },
  ];

  return (
    <PageContainer title="إدارة {EntityArabicName}">
      <DataTable
        columns={columns}
        data={data?.items ?? []}
        pagination={data?.pagination}
        isLoading={isLoading}
        onAdd={() => navigate('create')}
        addPermission="{Module}.Create"
      />
    </PageContainer>
  );
};
```

### Step 5: SQL Migration
```sql
CREATE TABLE [{Schema}].[{TableName}] (
    [Id] INT IDENTITY(1,1) PRIMARY KEY,
    [NameAr] NVARCHAR(200) NOT NULL,
    [NameEn] NVARCHAR(200) NULL,
    [IsActive] BIT NOT NULL DEFAULT 1,
    [CreatedBy] INT NULL,
    [CreatedAt] DATETIME2 NOT NULL DEFAULT GETUTCDATE(),
    [UpdatedBy] INT NULL,
    [UpdatedAt] DATETIME2 NULL
);

-- Add to permissions
INSERT INTO [Security].[Permissions] ([Code], [NameAr], [NameEn], [ModuleId])
VALUES 
    ('{Module}.View', 'عرض {EntityAr}', 'View {Entity}', @ModuleId),
    ('{Module}.Create', 'إضافة {EntityAr}', 'Create {Entity}', @ModuleId),
    ('{Module}.Edit', 'تعديل {EntityAr}', 'Edit {Entity}', @ModuleId),
    ('{Module}.Delete', 'حذف {EntityAr}', 'Delete {Entity}', @ModuleId);
```

## Standards & Conventions

- All entities must have `NameAr` (required) and `NameEn` (optional)
- All validation messages in Arabic
- Use `Result<T>` pattern for command responses
- Use `PaginatedList<T>` for list queries
- Frontend: Arabic labels first, English secondary
- Permissions follow `{Module}.{Action}` pattern
- Audit fields (`CreatedBy`, `CreatedAt`, `UpdatedBy`, `UpdatedAt`) on every entity
- Use `ICurrentUserService` for tracking user actions
- Soft delete preferred over hard delete (`IsDeleted` flag)

## Reference Files
- Read `references/permissions-matrix.md` for the full permissions model
- Read `references/api-conventions.md` for API response standards
