Compare commits
2
Commits
82e4a0cbf0
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6a06ae1de | ||
|
|
7253dece8e |
+37
-423
@@ -1,443 +1,57 @@
|
||||
---
|
||||
name: doc-updater
|
||||
description: Documentation and codemap specialist. Use PROACTIVELY for updating codemaps and documentation. Runs /update-codemaps and /update-docs, generates docs/CODEMAPS/*, updates READMEs and guides.
|
||||
description: Documentation specialist. Use PROACTIVELY to keep MkDocs technical docs and READMEs aligned with the codebase. Technical docs live in docs/ + mkdocs.yml; user docs live in sibling trackgo-fe/apps/docs-dev.
|
||||
tools: Read, Write, Edit, Bash, Grep, Glob
|
||||
model: opus
|
||||
---
|
||||
|
||||
# Documentation & Codemap Specialist
|
||||
# Documentation Updater
|
||||
|
||||
You are a documentation specialist focused on keeping codemaps and documentation current with the codebase. Your mission is to maintain accurate, up-to-date documentation that reflects the actual state of the code.
|
||||
Keep technical documentation current with the TrackGo codebase. Follow `.cursor/rules/documentation.mdc` and `.cursor/rules/technical-docs.mdc`.
|
||||
|
||||
## Core Responsibilities
|
||||
## Documentation split
|
||||
|
||||
1. **Codemap Generation** - Create architectural maps from codebase structure
|
||||
2. **Documentation Updates** - Refresh READMEs and guides from code
|
||||
3. **AST Analysis** - Use TypeScript compiler API to understand structure
|
||||
4. **Dependency Mapping** - Track imports/exports across modules
|
||||
5. **Documentation Quality** - Ensure docs match reality
|
||||
| Kind | Location | Audience |
|
||||
| --- | --- | --- |
|
||||
| Technical | `docs/` + `mkdocs.yml` (this repo) | Developers / engineers |
|
||||
| User | sibling `trackgo-fe/apps/docs-dev/` | Customers / operators |
|
||||
|
||||
## Tools at Your Disposal
|
||||
Do **not** put customer how-tos in MkDocs. Do **not** treat `docs/CODEMAPS/*` as the default target unless those files already exist and the user asks for them.
|
||||
|
||||
### Analysis Tools
|
||||
- **ts-morph** - TypeScript AST analysis and manipulation
|
||||
- **TypeScript Compiler API** - Deep code structure analysis
|
||||
- **madge** - Dependency graph visualization
|
||||
- **jsdoc-to-markdown** - Generate docs from JSDoc comments
|
||||
Technical coverage under `docs/`:
|
||||
|
||||
### Analysis Commands
|
||||
```bash
|
||||
# Analyze TypeScript project structure
|
||||
npx ts-morph
|
||||
|
||||
# Generate dependency graph
|
||||
npx madge --image graph.svg src/
|
||||
|
||||
# Extract JSDoc comments
|
||||
npx jsdoc2md src/**/*.ts
|
||||
```text
|
||||
docs/
|
||||
backend/ # NestJS API — this repo
|
||||
frontend-web/ # trackgo-fe/apps/web
|
||||
frontend-landing/ # trackgo-fe/apps/landing
|
||||
mobile/ # trackgo_mobile
|
||||
api.md # HTTP API reference (keep)
|
||||
report-*.md # report docs (keep)
|
||||
```
|
||||
|
||||
## Codemap Generation Workflow
|
||||
## Source of truth
|
||||
|
||||
### 1. Repository Structure Analysis
|
||||
```
|
||||
a) Identify all workspaces/packages
|
||||
b) Map directory structure
|
||||
c) Find entry points (apps/*, packages/*, services/*)
|
||||
d) Detect framework patterns (NestJS, Node.js, etc.)
|
||||
```
|
||||
1. This repo: `src/app.module.ts`, `src/modules/`, `docs/api.md`, `package.json`, `.env` examples
|
||||
2. Sibling `trackgo-fe`: `apps/web` menus/modules, `apps/landing`
|
||||
3. Sibling `trackgo_mobile`: `lib/config/router.dart`, `lib/ui/features/`, `pubspec.yaml`
|
||||
|
||||
### 2. Module Analysis
|
||||
```
|
||||
For each module:
|
||||
- Extract exports (public API)
|
||||
- Map imports (dependencies)
|
||||
- Identify routes (API routes, pages)
|
||||
- Find database models (Drizzle schema)
|
||||
- Locate queue/worker modules
|
||||
```
|
||||
If a product brief disagrees with code, follow the code or label **Coming soon**. No emoji. Diagrams: PlantUML fenced blocks, not Mermaid.
|
||||
|
||||
### 3. Generate Codemaps
|
||||
```
|
||||
Structure:
|
||||
docs/CODEMAPS/
|
||||
├── INDEX.md # Overview of all areas
|
||||
├── frontend.md # Frontend structure
|
||||
├── backend.md # Backend/API structure
|
||||
├── database.md # Database schema
|
||||
├── integrations.md # External services
|
||||
└── workers.md # Background jobs
|
||||
```
|
||||
## Workflow
|
||||
|
||||
### 4. Codemap Format
|
||||
```markdown
|
||||
# [Area] Codemap
|
||||
1. Discover modules from `src/app.module.ts` (Auth, Users, Privileges, Configuration, Sales, Field, Reports)
|
||||
2. Cross-read sibling FE/mobile for frontend-web, frontend-landing, and mobile sections
|
||||
3. Update markdown under `docs/<area>/` and register pages in `mkdocs.yml` `nav`
|
||||
4. When touching `mkdocs.yml`, set `site_name` to TrackGo if it still says something else (e.g. Concourse CI)
|
||||
5. Keep existing `docs/api.md` and report pages accurate when APIs change
|
||||
6. Preview with the Docker TechDocs command in `docs/readme.md`
|
||||
7. Show a diff summary
|
||||
|
||||
**Last Updated:** YYYY-MM-DD
|
||||
**Entry Points:** list of main files
|
||||
## Quality
|
||||
|
||||
## Architecture
|
||||
|
||||
[ASCII diagram of component relationships]
|
||||
|
||||
## Key Modules
|
||||
|
||||
| Module | Purpose | Exports | Dependencies |
|
||||
|--------|---------|---------|--------------|
|
||||
| ... | ... | ... | ... |
|
||||
|
||||
## Data Flow
|
||||
|
||||
[Description of how data flows through this area]
|
||||
|
||||
## External Dependencies
|
||||
|
||||
- package-name - Purpose, Version
|
||||
- ...
|
||||
|
||||
## Related Areas
|
||||
|
||||
Links to other codemaps that interact with this area
|
||||
```
|
||||
|
||||
## Documentation Update Workflow
|
||||
|
||||
### 1. Extract Documentation from Code
|
||||
```
|
||||
- Read JSDoc/TSDoc comments
|
||||
- Extract README sections from package.json
|
||||
- Parse environment variables from .env.example
|
||||
- Collect API endpoint definitions
|
||||
```
|
||||
|
||||
### 2. Update Documentation Files
|
||||
```
|
||||
Files to update:
|
||||
- README.md - Project overview, setup instructions
|
||||
- docs/GUIDES/*.md - Feature guides, tutorials
|
||||
- package.json - Descriptions, scripts docs
|
||||
- API documentation - Endpoint specs
|
||||
```
|
||||
|
||||
### 3. Documentation Validation
|
||||
```
|
||||
- Verify all mentioned files exist
|
||||
- Check all links work
|
||||
- Ensure examples are runnable
|
||||
- Validate code snippets compile
|
||||
```
|
||||
|
||||
## Example Project-Specific Codemaps
|
||||
|
||||
### API Codemap (docs/CODEMAPS/api.md)
|
||||
```markdown
|
||||
# API Architecture
|
||||
|
||||
**Last Updated:** YYYY-MM-DD
|
||||
**Framework:** NestJS
|
||||
**Entry Point:** src/main.ts
|
||||
|
||||
## Structure
|
||||
|
||||
src/
|
||||
├── main.ts
|
||||
├── app.module.ts
|
||||
├── common/
|
||||
├── config/
|
||||
└── modules/
|
||||
├── auth/
|
||||
└── users/
|
||||
|
||||
## Data Flow
|
||||
|
||||
Client → Controller → Service → Drizzle Repository → PostgreSQL
|
||||
|
||||
## External Dependencies
|
||||
|
||||
- NestJS - Framework
|
||||
- Drizzle ORM - Database
|
||||
- PostgreSQL - Data store
|
||||
- Redis - Cache / queues
|
||||
```
|
||||
```
|
||||
|
||||
### Backend Codemap (docs/CODEMAPS/backend.md)
|
||||
```markdown
|
||||
# Backend Architecture
|
||||
|
||||
**Last Updated:** YYYY-MM-DD
|
||||
**Runtime:** NestJS
|
||||
**Entry Point:** src/main.ts
|
||||
|
||||
## API Routes
|
||||
|
||||
| Route | Method | Purpose |
|
||||
|-------|--------|---------|
|
||||
| /users | GET | List users |
|
||||
| /users | POST | Create user |
|
||||
| /auth/login | POST | Authenticate |
|
||||
|
||||
## Data Flow
|
||||
|
||||
Controller → Service → Drizzle Repository → PostgreSQL
|
||||
|
||||
## External Services
|
||||
|
||||
- PostgreSQL via Drizzle
|
||||
- Redis - cache / queues
|
||||
```
|
||||
|
||||
### Integrations Codemap (docs/CODEMAPS/integrations.md)
|
||||
```markdown
|
||||
# External Integrations
|
||||
|
||||
**Last Updated:** YYYY-MM-DD
|
||||
|
||||
## Authentication (JWT)
|
||||
- NestJS guards and strategies
|
||||
- Session / token management
|
||||
|
||||
## Database (PostgreSQL + Drizzle)
|
||||
- Schema in drizzle/schema.ts
|
||||
- Versioned SQL migrations
|
||||
- Least-privilege DB role
|
||||
|
||||
## Search (Redis + OpenAI)
|
||||
- Vector embeddings (text-embedding-ada-002)
|
||||
- Semantic search (KNN)
|
||||
- Fallback to substring search
|
||||
|
||||
## Blockchain (Solana)
|
||||
- Wallet integration
|
||||
- Transaction handling
|
||||
- Meteora CP-AMM SDK
|
||||
```
|
||||
|
||||
## README Update Template
|
||||
|
||||
When updating README.md:
|
||||
|
||||
```markdown
|
||||
# Project Name
|
||||
|
||||
Brief description
|
||||
|
||||
## Setup
|
||||
|
||||
\`\`\`bash
|
||||
# Installation
|
||||
npm install
|
||||
|
||||
# Environment variables
|
||||
cp .env.example .env.local
|
||||
# Fill in: OPENAI_API_KEY, REDIS_URL, etc.
|
||||
|
||||
# Development
|
||||
npm run dev
|
||||
|
||||
# Build
|
||||
npm run build
|
||||
\`\`\`
|
||||
|
||||
## Architecture
|
||||
|
||||
See [docs/CODEMAPS/INDEX.md](docs/CODEMAPS/INDEX.md) for detailed architecture.
|
||||
|
||||
### Key Directories
|
||||
|
||||
- `src/modules` - NestJS feature modules
|
||||
- `src/common` - Filters, guards, pipes, interceptors
|
||||
- `drizzle/` - Schema and migrations
|
||||
- `test/` - E2E tests
|
||||
- `src/lib` - Utility libraries and clients
|
||||
|
||||
## Features
|
||||
|
||||
- [Feature 1] - Description
|
||||
- [Feature 2] - Description
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Setup Guide](docs/GUIDES/setup.md)
|
||||
- [API Reference](docs/GUIDES/api.md)
|
||||
- [Architecture](docs/CODEMAPS/INDEX.md)
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
```
|
||||
|
||||
## Scripts to Power Documentation
|
||||
|
||||
### scripts/codemaps/generate.ts
|
||||
```typescript
|
||||
/**
|
||||
* Generate codemaps from repository structure
|
||||
* Usage: tsx scripts/codemaps/generate.ts
|
||||
*/
|
||||
|
||||
import { Project } from 'ts-morph'
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
|
||||
async function generateCodemaps() {
|
||||
const project = new Project({
|
||||
tsConfigFilePath: 'tsconfig.json',
|
||||
})
|
||||
|
||||
// 1. Discover all source files
|
||||
const sourceFiles = project.getSourceFiles('src/**/*.{ts,tsx}')
|
||||
|
||||
// 2. Build import/export graph
|
||||
const graph = buildDependencyGraph(sourceFiles)
|
||||
|
||||
// 3. Detect entrypoints (pages, API routes)
|
||||
const entrypoints = findEntrypoints(sourceFiles)
|
||||
|
||||
// 4. Generate codemaps
|
||||
await generateFrontendMap(graph, entrypoints)
|
||||
await generateBackendMap(graph, entrypoints)
|
||||
await generateIntegrationsMap(graph)
|
||||
|
||||
// 5. Generate index
|
||||
await generateIndex()
|
||||
}
|
||||
|
||||
function buildDependencyGraph(files: SourceFile[]) {
|
||||
// Map imports/exports between files
|
||||
// Return graph structure
|
||||
}
|
||||
|
||||
function findEntrypoints(files: SourceFile[]) {
|
||||
// Identify pages, API routes, entry files
|
||||
// Return list of entrypoints
|
||||
}
|
||||
```
|
||||
|
||||
### scripts/docs/update.ts
|
||||
```typescript
|
||||
/**
|
||||
* Update documentation from code
|
||||
* Usage: tsx scripts/docs/update.ts
|
||||
*/
|
||||
|
||||
import * as fs from 'fs'
|
||||
import { execSync } from 'child_process'
|
||||
|
||||
async function updateDocs() {
|
||||
// 1. Read codemaps
|
||||
const codemaps = readCodemaps()
|
||||
|
||||
// 2. Extract JSDoc/TSDoc
|
||||
const apiDocs = extractJSDoc('src/**/*.ts')
|
||||
|
||||
// 3. Update README.md
|
||||
await updateReadme(codemaps, apiDocs)
|
||||
|
||||
// 4. Update guides
|
||||
await updateGuides(codemaps)
|
||||
|
||||
// 5. Generate API reference
|
||||
await generateAPIReference(apiDocs)
|
||||
}
|
||||
|
||||
function extractJSDoc(pattern: string) {
|
||||
// Use jsdoc-to-markdown or similar
|
||||
// Extract documentation from source
|
||||
}
|
||||
```
|
||||
|
||||
## Pull Request Template
|
||||
|
||||
When opening PR with documentation updates:
|
||||
|
||||
```markdown
|
||||
## Docs: Update Codemaps and Documentation
|
||||
|
||||
### Summary
|
||||
Regenerated codemaps and updated documentation to reflect current codebase state.
|
||||
|
||||
### Changes
|
||||
- Updated docs/CODEMAPS/* from current code structure
|
||||
- Refreshed README.md with latest setup instructions
|
||||
- Updated docs/GUIDES/* with current API endpoints
|
||||
- Added X new modules to codemaps
|
||||
- Removed Y obsolete documentation sections
|
||||
|
||||
### Generated Files
|
||||
- docs/CODEMAPS/INDEX.md
|
||||
- docs/CODEMAPS/frontend.md
|
||||
- docs/CODEMAPS/backend.md
|
||||
- docs/CODEMAPS/integrations.md
|
||||
|
||||
### Verification
|
||||
- [x] All links in docs work
|
||||
- [x] Code examples are current
|
||||
- [x] Architecture diagrams match reality
|
||||
- [x] No obsolete references
|
||||
|
||||
### Impact
|
||||
🟢 LOW - Documentation only, no code changes
|
||||
|
||||
See docs/CODEMAPS/INDEX.md for complete architecture overview.
|
||||
```
|
||||
|
||||
## Maintenance Schedule
|
||||
|
||||
**Weekly:**
|
||||
- Check for new files in src/ not in codemaps
|
||||
- Verify README.md instructions work
|
||||
- Update package.json descriptions
|
||||
|
||||
**After Major Features:**
|
||||
- Regenerate all codemaps
|
||||
- Update architecture documentation
|
||||
- Refresh API reference
|
||||
- Update setup guides
|
||||
|
||||
**Before Releases:**
|
||||
- Comprehensive documentation audit
|
||||
- Verify all examples work
|
||||
- Check all external links
|
||||
- Update version references
|
||||
|
||||
## Quality Checklist
|
||||
|
||||
Before committing documentation:
|
||||
- [ ] Codemaps generated from actual code
|
||||
- [ ] All file paths verified to exist
|
||||
- [ ] Code examples compile/run
|
||||
- [ ] Links tested (internal and external)
|
||||
- [ ] Freshness timestamps updated
|
||||
- [ ] ASCII diagrams are clear
|
||||
- [ ] No obsolete references
|
||||
- [ ] Spelling/grammar checked
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Single Source of Truth** - Generate from code, don't manually write
|
||||
2. **Freshness Timestamps** - Always include last updated date
|
||||
3. **Token Efficiency** - Keep codemaps under 500 lines each
|
||||
4. **Clear Structure** - Use consistent markdown formatting
|
||||
5. **Actionable** - Include setup commands that actually work
|
||||
6. **Linked** - Cross-reference related documentation
|
||||
7. **Examples** - Show real working code snippets
|
||||
8. **Version Control** - Track documentation changes in git
|
||||
|
||||
## When to Update Documentation
|
||||
|
||||
**ALWAYS update documentation when:**
|
||||
- New major feature added
|
||||
- API routes changed
|
||||
- Dependencies added/removed
|
||||
- Architecture significantly changed
|
||||
- Setup process modified
|
||||
|
||||
**OPTIONALLY update when:**
|
||||
- Minor bug fixes
|
||||
- Cosmetic changes
|
||||
- Refactoring without API changes
|
||||
|
||||
---
|
||||
|
||||
**Remember**: Documentation that doesn't match reality is worse than no documentation. Always generate from source of truth (the actual code).
|
||||
- Every path mentioned must exist in the relevant repo
|
||||
- Commands must match each repo's package manager scripts
|
||||
- No user how-tos (menus, click-paths) in MkDocs — send those to `trackgo-fe/apps/docs-dev`
|
||||
- No emoji; PlantUML for architecture and sequence diagrams
|
||||
- Do not invent features or endpoints
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
description: TrackGo docs split — technical MkDocs here, user VitePress in trackgo-fe/apps/docs-dev
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# Documentation Split
|
||||
|
||||
TrackGo has two documentation kinds. Sibling repos: `trackgo-be`, `trackgo-fe`, `trackgo_mobile`.
|
||||
|
||||
| Kind | Audience | Location | Tooling |
|
||||
| --- | --- | --- | --- |
|
||||
| Technical | Developers / engineers | `docs/` + `mkdocs.yml` (this repo) | MkDocs / Backstage TechDocs |
|
||||
| User | Customers / operators | `trackgo-fe/apps/docs-dev/` | VitePress |
|
||||
|
||||
## Rules
|
||||
|
||||
- Technical: stack, architecture, how to run, how modules interact (backend, web, landing, mobile).
|
||||
- User: features and usage (web + mobile only). Never put user how-tos in MkDocs.
|
||||
- Never put tech stack / package architecture / Electron IPC in docs-dev (user docs only going forward).
|
||||
- No emoji in any documentation.
|
||||
- Diagrams and sequence diagrams: PlantUML fenced blocks (` ```plantuml `), not Mermaid.
|
||||
- Source of truth is the codebase. If a brief disagrees with code, follow the code or label **Coming soon**. Do not invent features.
|
||||
|
||||
Authoring details: `.cursor/rules/technical-docs.mdc` (when editing `docs/` or `mkdocs.yml`).
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
description: How to write TrackGo technical docs in MkDocs (docs/ + mkdocs.yml)
|
||||
globs: docs/**/*.md,mkdocs.yml
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Technical Documentation (MkDocs)
|
||||
|
||||
Audience: developers and engineers. Location: `docs/` + `mkdocs.yml`.
|
||||
|
||||
## Scope
|
||||
|
||||
Document tech stack, architecture, how to run, and how modules interact for:
|
||||
|
||||
| Area | Sources |
|
||||
| --- | --- |
|
||||
| Backend | this repo — `src/app.module.ts`, `src/modules/` |
|
||||
| Frontend web | sibling `trackgo-fe/apps/web` |
|
||||
| Frontend landing | sibling `trackgo-fe/apps/landing` (public SPA, not the ERP) |
|
||||
| Mobile | sibling `trackgo_mobile` |
|
||||
|
||||
Do **not** write customer how-tos here — those belong in `trackgo-fe/apps/docs-dev`.
|
||||
|
||||
## Layout
|
||||
|
||||
Prefer these folders under `docs/`:
|
||||
|
||||
```text
|
||||
docs/
|
||||
backend/
|
||||
frontend-web/
|
||||
frontend-landing/
|
||||
mobile/
|
||||
api.md # keep existing API reference
|
||||
report-*.md # keep existing report docs
|
||||
```
|
||||
|
||||
When adding pages:
|
||||
|
||||
1. Create the markdown under the matching folder.
|
||||
2. Register it in `mkdocs.yml` `nav`.
|
||||
3. Set `site_name` to TrackGo (it may still say `Concourse CI` — rename when you touch nav).
|
||||
|
||||
Preview: Docker TechDocs command in `docs/readme.md`.
|
||||
|
||||
## Ground in code
|
||||
|
||||
- Backend modules from `src/app.module.ts`: Auth, Users, Privileges, Configuration, Sales, Field, Reports.
|
||||
- Web menus / RBAC keys: `trackgo-fe/apps/web/src/apps/main/layouts/data/menu.data.ts`.
|
||||
- Mobile routes: `trackgo_mobile/lib/config/router.dart` (login, home, plan, customers, check-in, payment).
|
||||
- Cross-read sibling repos; do not invent features. Brief vs code → follow code or **Coming soon**.
|
||||
|
||||
## Style
|
||||
|
||||
- No emoji.
|
||||
- Diagrams / sequences: PlantUML only.
|
||||
|
||||
````markdown
|
||||
```plantuml
|
||||
@startuml
|
||||
Alice -> Bob: request
|
||||
Bob --> Alice: response
|
||||
@enduml
|
||||
```
|
||||
````
|
||||
|
||||
```text
|
||||
# BAD — user how-to in MkDocs
|
||||
docs/how-to-create-invoice.md
|
||||
|
||||
# BAD — Mermaid or emoji
|
||||
```mermaid
|
||||
```
|
||||
"🚀 Getting Started"
|
||||
|
||||
# GOOD
|
||||
docs/backend/architecture.md
|
||||
docs/frontend-web/modules.md
|
||||
```
|
||||
@@ -0,0 +1,8 @@
|
||||
## Docs
|
||||
|
||||
Using backstage with mkdocs.yml
|
||||
Preview :
|
||||
|
||||
```
|
||||
docker run --rm -w /content -v $(pwd):/content -p 8000:8000 -it harbor.eigen.co.id/eigen/techdocs:1.0.1 serve -a 0.0.0.0:8000
|
||||
```
|
||||
@@ -0,0 +1,8 @@
|
||||
site_name: 'Concourse CI'
|
||||
|
||||
nav:
|
||||
- Home: index.md
|
||||
- Android APK Build: android-apk-build.md
|
||||
|
||||
plugins:
|
||||
- techdocs-core
|
||||
@@ -1,4 +1,10 @@
|
||||
import { bigint, doublePrecision, index, pgTable, uuid } from 'drizzle-orm/pg-core';
|
||||
import {
|
||||
bigint,
|
||||
doublePrecision,
|
||||
index,
|
||||
pgTable,
|
||||
uuid,
|
||||
} from 'drizzle-orm/pg-core';
|
||||
import { employees } from './employees-table';
|
||||
|
||||
export const timelineFootprints = pgTable(
|
||||
|
||||
@@ -137,7 +137,9 @@ describe('AuthController', () => {
|
||||
).resolves.toMatchObject({
|
||||
privilege: { id: 'priv-1', code: 'ADMIN' },
|
||||
permissions: {
|
||||
'ADMIN.SETTINGS.USER.PRIVILEGES': expect.objectContaining({ view: true }),
|
||||
'ADMIN.SETTINGS.USER.PRIVILEGES': expect.objectContaining({
|
||||
view: true,
|
||||
}),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -34,7 +34,10 @@ describe('CyclesService', () => {
|
||||
const employeesService = { findById: jest.fn(), findByCode: jest.fn() };
|
||||
const branchesService = { findById: jest.fn(), findByCode: jest.fn() };
|
||||
const customersService = { findById: jest.fn(), findByCode: jest.fn() };
|
||||
const privilegesService = { checkPermission: jest.fn(), checkAnyPermission: jest.fn() };
|
||||
const privilegesService = {
|
||||
checkPermission: jest.fn(),
|
||||
checkAnyPermission: jest.fn(),
|
||||
};
|
||||
|
||||
const now = DateTime.fromUnixMs(1_700_000_000_000);
|
||||
const user: AuthUser = {
|
||||
|
||||
@@ -72,7 +72,10 @@ describe('PlansService', () => {
|
||||
markDraftsProcessed: jest.fn(),
|
||||
};
|
||||
const packingSlipsService = { findById: jest.fn() };
|
||||
const privilegesService = { checkPermission: jest.fn(), checkAnyPermission: jest.fn() };
|
||||
const privilegesService = {
|
||||
checkPermission: jest.fn(),
|
||||
checkAnyPermission: jest.fn(),
|
||||
};
|
||||
|
||||
const user: AuthUser = {
|
||||
id: 'user-1',
|
||||
|
||||
@@ -14,7 +14,9 @@ describe('TimelineService', () => {
|
||||
let footprintsRepository: jest.Mocked<
|
||||
Pick<TimelineFootprintsRepository, 'insertMany' | 'list'>
|
||||
>;
|
||||
let activitiesRepository: jest.Mocked<Pick<TimelineActivitiesRepository, 'list'>>;
|
||||
let activitiesRepository: jest.Mocked<
|
||||
Pick<TimelineActivitiesRepository, 'list'>
|
||||
>;
|
||||
let employeesService: jest.Mocked<Pick<EmployeesService, 'requireByUserId'>>;
|
||||
let companySettingsService: jest.Mocked<
|
||||
Pick<CompanySettingsService, 'requireTimelineConfig'>
|
||||
@@ -40,8 +42,14 @@ describe('TimelineService', () => {
|
||||
const moduleRef: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
TimelineService,
|
||||
{ provide: TimelineFootprintsRepository, useValue: footprintsRepository },
|
||||
{ provide: TimelineActivitiesRepository, useValue: activitiesRepository },
|
||||
{
|
||||
provide: TimelineFootprintsRepository,
|
||||
useValue: footprintsRepository,
|
||||
},
|
||||
{
|
||||
provide: TimelineActivitiesRepository,
|
||||
useValue: activitiesRepository,
|
||||
},
|
||||
{ provide: EmployeesService, useValue: employeesService },
|
||||
{ provide: CompanySettingsService, useValue: companySettingsService },
|
||||
],
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
Injectable,
|
||||
} from '@nestjs/common';
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
import {
|
||||
DEFAULT_RELATION_FIELDS,
|
||||
pickRelation,
|
||||
@@ -138,14 +135,17 @@ export class TimelineService {
|
||||
if (employeeId || footprints.length <= MAX_FOOTPRINTS_ALL_EMPLOYEES) {
|
||||
return [...footprints];
|
||||
}
|
||||
const stride = Math.ceil(
|
||||
footprints.length / MAX_FOOTPRINTS_ALL_EMPLOYEES,
|
||||
);
|
||||
const stride = Math.ceil(footprints.length / MAX_FOOTPRINTS_ALL_EMPLOYEES);
|
||||
return footprints.filter((_, index) => index % stride === 0);
|
||||
}
|
||||
|
||||
private assertCoordinates(latitude: number, longitude: number): void {
|
||||
if (latitude < -90 || latitude > 90 || longitude < -180 || longitude > 180) {
|
||||
if (
|
||||
latitude < -90 ||
|
||||
latitude > 90 ||
|
||||
longitude < -180 ||
|
||||
longitude > 180
|
||||
) {
|
||||
throw new BadRequestException('Invalid coordinates');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,10 +18,16 @@ describe('privilege-action', () => {
|
||||
|
||||
describe('privilege-key-code', () => {
|
||||
it('accepts 3- and 4-part dotted uppercase codes', () => {
|
||||
expect(isValidPrivilegeKeyCode('ADMIN.SETTINGS.USER.PRIVILEGES')).toBe(true);
|
||||
expect(isValidPrivilegeKeyCode('ADMIN.SALES.ACTIVITIES.INVOICE')).toBe(true);
|
||||
expect(isValidPrivilegeKeyCode('ADMIN.SETTINGS.USER.PRIVILEGES')).toBe(
|
||||
true,
|
||||
);
|
||||
expect(isValidPrivilegeKeyCode('ADMIN.SALES.ACTIVITIES.INVOICE')).toBe(
|
||||
true,
|
||||
);
|
||||
expect(isValidPrivilegeKeyCode('MOBILE.SALES.PLAN')).toBe(true);
|
||||
expect(assertPrivilegeKeyCode('MOBILE.SALES.PLAN')).toBe('MOBILE.SALES.PLAN');
|
||||
expect(assertPrivilegeKeyCode('MOBILE.SALES.PLAN')).toBe(
|
||||
'MOBILE.SALES.PLAN',
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects invalid codes', () => {
|
||||
|
||||
@@ -12,7 +12,9 @@ describe('privilege-key-code', () => {
|
||||
});
|
||||
|
||||
it('accepts 4-part keys', () => {
|
||||
expect(isValidPrivilegeKeyCode('MOBILE.SALES.PLAN.ATTENDANCE')).toBe(true);
|
||||
expect(isValidPrivilegeKeyCode('MOBILE.SALES.PLAN.ATTENDANCE')).toBe(
|
||||
true,
|
||||
);
|
||||
expect(isValidPrivilegeKeyCode('ADMIN.SALES.ACTIVITIES.PLAN')).toBe(true);
|
||||
});
|
||||
|
||||
@@ -33,7 +35,9 @@ describe('privilege-key-code', () => {
|
||||
|
||||
describe('assertPrivilegeKeyCode', () => {
|
||||
it('returns the code when valid', () => {
|
||||
expect(assertPrivilegeKeyCode('MOBILE.SALES.PLAN')).toBe('MOBILE.SALES.PLAN');
|
||||
expect(assertPrivilegeKeyCode('MOBILE.SALES.PLAN')).toBe(
|
||||
'MOBILE.SALES.PLAN',
|
||||
);
|
||||
});
|
||||
|
||||
it('throws when invalid', () => {
|
||||
|
||||
@@ -169,11 +169,7 @@ describe('PrivilegesService', () => {
|
||||
it('checkAnyPermission uses checkPermission for a single key', async () => {
|
||||
repository.checkPermission.mockResolvedValue(true);
|
||||
await expect(
|
||||
service.checkAnyPermission(
|
||||
'user-1',
|
||||
['MOBILE.SALES.PLAN'],
|
||||
'view',
|
||||
),
|
||||
service.checkAnyPermission('user-1', ['MOBILE.SALES.PLAN'], 'view'),
|
||||
).resolves.toBe(true);
|
||||
expect(repository.checkPermission).toHaveBeenCalledWith(
|
||||
'user-1',
|
||||
|
||||
@@ -21,7 +21,8 @@ import {
|
||||
} from './dto/packing-slip.dto';
|
||||
import { PackingSlipsService } from './packing-slips.service';
|
||||
|
||||
export const PACKING_SLIP_PRIVILEGE_KEY = 'ADMIN.LOGISTICS.ACTIVITIES.PACKING_SLIP';
|
||||
export const PACKING_SLIP_PRIVILEGE_KEY =
|
||||
'ADMIN.LOGISTICS.ACTIVITIES.PACKING_SLIP';
|
||||
|
||||
@ApiTags('packing-slips')
|
||||
@ApiBearerAuth(BEARER_AUTH_NAME)
|
||||
|
||||
@@ -118,9 +118,9 @@ describe('Company settings (e2e)', () => {
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect((updated.body as { gpsIntervalSeconds: number }).gpsIntervalSeconds).toBe(
|
||||
15,
|
||||
);
|
||||
expect(
|
||||
(updated.body as { gpsIntervalSeconds: number }).gpsIntervalSeconds,
|
||||
).toBe(15);
|
||||
expect(
|
||||
(updated.body as { checkoutWarningRadiusMeters: number })
|
||||
.checkoutWarningRadiusMeters,
|
||||
|
||||
@@ -202,7 +202,9 @@ describe('Privileges (e2e)', () => {
|
||||
expect(me.body.privilege).toMatchObject({
|
||||
id: adminPrivilegeId,
|
||||
});
|
||||
expect(me.body.permissions['ADMIN.SETTINGS.USER.PRIVILEGES'].view).toBe(true);
|
||||
expect(me.body.permissions['ADMIN.SETTINGS.USER.PRIVILEGES'].view).toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.patch(`/users/${otherUserId}/privilege`)
|
||||
|
||||
@@ -135,7 +135,8 @@ describe('Timeline (e2e)', () => {
|
||||
});
|
||||
|
||||
it('ingests footprints and returns them on admin timeline query', async () => {
|
||||
const recordedAt = DateTime.fromUnixMs(Date.now()).startOfDay().value + 3_600_000;
|
||||
const recordedAt =
|
||||
DateTime.fromUnixMs(Date.now()).startOfDay().value + 3_600_000;
|
||||
|
||||
const ingest = await request(app.getHttpServer())
|
||||
.post('/timeline/footprints')
|
||||
@@ -143,14 +144,21 @@ describe('Timeline (e2e)', () => {
|
||||
.send({
|
||||
points: [
|
||||
{ latitude: -6.2, longitude: 106.8, recordedAt },
|
||||
{ latitude: -6.201, longitude: 106.801, recordedAt: recordedAt + 5000 },
|
||||
{
|
||||
latitude: -6.201,
|
||||
longitude: 106.801,
|
||||
recordedAt: recordedAt + 5000,
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(201);
|
||||
|
||||
expect((ingest.body as { inserted: number }).inserted).toBe(2);
|
||||
|
||||
const today = DateTime.fromUnixMs(Date.now()).startOfDay().format().slice(0, 10);
|
||||
const today = DateTime.fromUnixMs(Date.now())
|
||||
.startOfDay()
|
||||
.format()
|
||||
.slice(0, 10);
|
||||
const day = await request(app.getHttpServer())
|
||||
.get('/timeline')
|
||||
.query({ date: today, employeeId })
|
||||
@@ -168,7 +176,10 @@ describe('Timeline (e2e)', () => {
|
||||
});
|
||||
|
||||
it('returns activities-only timeline for the current user', async () => {
|
||||
const today = DateTime.fromUnixMs(Date.now()).startOfDay().format().slice(0, 10);
|
||||
const today = DateTime.fromUnixMs(Date.now())
|
||||
.startOfDay()
|
||||
.format()
|
||||
.slice(0, 10);
|
||||
const me = await request(app.getHttpServer())
|
||||
.get('/timeline/me')
|
||||
.query({ date: today })
|
||||
|
||||
Reference in New Issue
Block a user