# Coding Standards

Conventions and rules for all code in the ArchiTools repository.

---

## Language

TypeScript in strict mode. The `tsconfig.json` must include:

```json
{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "forceConsistentCasingInFileNames": true
  }
}
```

**No `any`** unless there is a documented, unavoidable reason (e.g., third-party library with missing types). When `any` is truly necessary, use `// eslint-disable-next-line @typescript-eslint/no-explicit-any` with a comment explaining why.

Prefer `unknown` over `any` when the type is genuinely unknown. Narrow with type guards.

---

## File Naming

| Entity | Convention | Example |
|---|---|---|
| Files (components) | kebab-case | `signature-preview.tsx` |
| Files (hooks) | kebab-case with `use-` prefix | `use-signature-builder.ts` |
| Files (utils/services) | kebab-case | `sanitize-xml-name.ts` |
| Files (types) | `types.ts` within module | `types.ts` |
| Files (tests) | `*.test.ts` / `*.test.tsx` | `sanitize-xml-name.test.ts` |
| Component names | PascalCase | `SignaturePreview` |
| Hook names | camelCase with `use` prefix | `useSignatureBuilder` |
| Utility functions | camelCase | `sanitizeXmlName` |
| Constants | UPPER_SNAKE_CASE | `DEFAULT_NAMESPACE` |
| Type/Interface names | PascalCase | `SignatureConfig` |
| Enum values | PascalCase | `FieldVariant.Upper` |

---

## Component Patterns

### Functional Components Only

No class components. All components are functions.

```tsx
// Correct
export function SignaturePreview({ config }: SignaturePreviewProps) {
  return <div>...</div>;
}

// Wrong: arrow function export (less debuggable in stack traces)
export const SignaturePreview = ({ config }: SignaturePreviewProps) => { ... };

// Wrong: default export
export default function SignaturePreview() { ... }
```

Exception: arrow functions are acceptable for small inline components passed as props or used in `.map()`.

### Props Interface

Define the props interface directly above the component, in the same file. Suffix with `Props`.

```tsx
interface SignaturePreviewProps {
  config: SignatureConfig;
  className?: string;
  onExport?: () => void;
}

export function SignaturePreview({ config, className, onExport }: SignaturePreviewProps) {
  // ...
}
```

If the props interface is reused across multiple files, define it in the module's `types.ts` and import it.

### Named Exports

All exports are named. No default exports anywhere in the codebase.

Rationale: named exports enforce consistent import names, improve refactoring, and work better with tree-shaking.

```tsx
// Correct
export function SignaturePreview() { ... }
export function useSignatureBuilder() { ... }
export interface SignatureConfig { ... }

// Wrong
export default function SignaturePreview() { ... }
```

### Co-located Styles

All styling is done with Tailwind classes in JSX. No CSS modules, no styled-components, no separate `.css` files per component.

```tsx
// Correct
<div className="rounded-xl border bg-card p-6 shadow-sm">

// Wrong: external CSS file
import styles from './signature-preview.module.css';
<div className={styles.card}>
```

Global styles live only in `src/app/globals.css` (theme tokens, font imports, base resets).

### Conditional Classes

Use the `cn()` utility (from `src/shared/utils/cn.ts`, wrapping `clsx` + `tailwind-merge`):

```tsx
import { cn } from '@/shared/utils/cn';

<div className={cn(
  'rounded-xl border p-6',
  isActive && 'border-primary bg-primary/5',
  className
)} />
```

---

## Hook Patterns

### Naming

All hooks start with `use`. File names match: `use-signature-builder.ts` exports `useSignatureBuilder`.

### Return Type

Return a typed object, not a tuple/array.

```tsx
// Correct
export function useSignatureBuilder(config: SignatureConfig): SignatureBuilderState {
  // ...
  return { html, previewData, isGenerating };
}

// Wrong: tuple return (positional args are fragile)
export function useSignatureBuilder(config: SignatureConfig) {
  return [html, previewData, isGenerating];
}
```

Exception: simple two-value hooks that mirror `useState` semantics may return a tuple if the pattern is unambiguous: `const [value, setValue] = useSomeState()`.

### Single Responsibility

Each hook does one thing. If a hook grows beyond ~80 lines, consider splitting.

```
useSignatureConfig    -- manages form state
useSignatureBuilder   -- generates HTML from config
useSignatureExport    -- handles file download
```

Not:

```
useSignature          -- does everything
```

### Dependencies

Hooks that depend on external services (storage, API) receive them as parameters:

```tsx
export function useCategoryManager(storage: StorageAdapter): CategoryManagerState {
  // ...
}
```

This enables testing with mock storage.

---

## Service Patterns

Services are modules that encapsulate business logic outside of React's component lifecycle.

### Pure Functions Where Possible

```tsx
// Correct: pure function, easy to test
export function sanitizeXmlName(name: string): string | null {
  // ...
}

// Correct: pure function with explicit dependencies
export function generateXml(input: XmlGeneratorInput): XmlGeneratorResult {
  // ...
}
```

### Accept Dependencies via Parameters

```tsx
// Correct: zip library passed in (or imported at module level, testable via jest.mock)
export async function createZipArchive(
  files: Record<string, string>,
  zipFactory: () => JSZip = () => new JSZip()
): Promise<Blob> {
  const zip = zipFactory();
  // ...
}
```

### No Direct DOM Access

Services must never call `document.*`, `window.localStorage`, or any browser API directly. All browser interactions are mediated through hooks or adapters that are injected.

```tsx
// Wrong: service directly accessing DOM
export function downloadFile(content: string) {
  const a = document.createElement('a');  // NO
  // ...
}

// Correct: service returns data, hook handles DOM interaction
export function prepareDownload(content: string, filename: string): DownloadPayload {
  return { blob: new Blob([content]), filename };
}
```

---

## Import Ordering

Imports are grouped in this order, separated by blank lines:

```tsx
// 1. React
import { useState, useCallback } from 'react';

// 2. Next.js
import { useRouter } from 'next/navigation';

// 3. Third-party libraries
import JSZip from 'jszip';
import { z } from 'zod';

// 4. Core (@/core)
import { labels } from '@/core/i18n/labels';
import { useStorage } from '@/core/storage/use-storage';

// 5. Shared (@/shared)
import { Button } from '@/shared/components/ui/button';
import { cn } from '@/shared/utils/cn';

// 6. Module-level (@/modules)
import { useXmlGenerator } from '@/modules/xml-generator/hooks/use-xml-generator';

// 7. Relative imports (same module)
import { SignaturePreview } from './signature-preview';
import type { SignatureConfig } from '../types';
```

Enforce with ESLint `import/order` rule.

---

## Path Aliases

Configured in `tsconfig.json`:

```json
{
  "compilerOptions": {
    "paths": {
      "@/core/*": ["./src/core/*"],
      "@/shared/*": ["./src/shared/*"],
      "@/modules/*": ["./src/modules/*"],
      "@/config/*": ["./src/config/*"]
    }
  }
}
```

**Rules:**

- Always use path aliases for cross-boundary imports.
- Use relative imports only within the same module directory.
- Never use `../../../` chains that cross module boundaries.

```tsx
// Correct: cross-boundary via alias
import { labels } from '@/core/i18n/labels';

// Correct: within same module via relative
import { SignaturePreview } from './signature-preview';

// Wrong: relative path crossing module boundary
import { labels } from '../../../core/i18n/labels';
```

---

## No Barrel Re-exports from Module Boundaries

Do not create `index.ts` files that re-export everything from a module.

```
// WRONG: src/modules/xml-generator/index.ts
export * from './hooks/use-xml-generator';
export * from './hooks/use-category-manager';
export * from './utils/sanitize-xml-name';
export * from './types';
```

Rationale: barrel files break tree-shaking in Next.js, cause circular dependency issues, and make import costs invisible. Import directly from the specific file.

```tsx
// Correct: direct import
import { useXmlGenerator } from '@/modules/xml-generator/hooks/use-xml-generator';

// Wrong: barrel import
import { useXmlGenerator } from '@/modules/xml-generator';
```

Exception: `src/shared/components/ui/` may use barrel re-exports since shadcn/ui generates them and they are leaf components with no circular dependency risk.

---

## Type Conventions

### `interface` vs `type`

- Use `interface` for object shapes (props, configs, data models).
- Use `type` for unions, intersections, mapped types, and utility types.

```tsx
// Object shape: interface
interface SignatureConfig {
  prefix: string;
  name: string;
  colors: ColorMap;
}

// Union: type
type FieldVariant = 'base' | 'short' | 'upper' | 'lower' | 'initials' | 'first';

// Intersection: type
type SignatureWithMeta = SignatureConfig & { id: string; createdAt: string };

// Mapped type: type
type PartialSignature = Partial<SignatureConfig>;
```

### Naming Suffixes

| Suffix | Usage | Example |
|---|---|---|
| `Props` | Component props | `SignaturePreviewProps` |
| `State` | Hook return types | `SignatureBuilderState` |
| `Result` | Service return types | `XmlGeneratorResult` |
| `Config` | Configuration objects | `SignatureConfig` |
| `Input` | Service/function input parameters | `XmlGeneratorInput` |

### Enums

Prefer union types over TypeScript enums for simple value sets:

```tsx
// Preferred
type GeneratorMode = 'simple' | 'advanced';

// Acceptable for larger sets with associated logic
enum FieldVariant {
  Base = 'base',
  Short = 'short',
  Upper = 'upper',
  Lower = 'lower',
  Initials = 'initials',
  First = 'first',
}
```

---

## Error Handling

### Result Pattern for Services

Service functions that can fail return a discriminated union instead of throwing:

```tsx
type Result<T, E = Error> =
  | { success: true; data: T }
  | { success: false; error: E };

export function parseXmlConfig(raw: string): Result<XmlConfig> {
  try {
    const parsed = JSON.parse(raw);
    // validate...
    return { success: true, data: parsed };
  } catch (e) {
    return { success: false, error: new Error('Invalid configuration format') };
  }
}
```

### try/catch at Boundaries

Use `try/catch` only at the boundary between services and UI (in hooks or event handlers):

```tsx
export function useXmlExport() {
  const handleDownload = useCallback(async () => {
    try {
      const blob = await createZipArchive(files);
      triggerDownload(blob, filename);
    } catch (error) {
      toast.error(labels.common.exportFailed);
    }
  }, [files, filename]);

  return { handleDownload };
}
```

Do not scatter `try/catch` through utility functions. Let errors propagate to the boundary.

### Never Swallow Errors Silently

```tsx
// Wrong: silent failure
try { storage.set('key', data); } catch (e) {}

// Correct: at minimum, log
try {
  storage.set('key', data);
} catch (error) {
  console.error('Failed to persist data:', error);
}
```

---

## Comments

### When to Comment

- Non-obvious business logic (e.g., "POT = SuprafataConstruitaLaSol / SuprafataTeren per Romanian building code").
- Workarounds for known issues (link to issue/PR).
- Regex patterns (always explain what the regex matches).
- Magic numbers that cannot be replaced with named constants.

### When Not to Comment

- Self-explanatory code. If the function is `sanitizeXmlName`, do not add `/** Sanitizes an XML name */`.
- JSDoc on every function. Add JSDoc only on public API boundaries of shared utilities.
- TODO comments without an owner or issue reference.

### Comment Style

```tsx
// Single-line comments for inline explanations.

/**
 * Multi-line JSDoc only for shared utility public API.
 * Describe what the function does, not how.
 */
export function formatPhoneNumber(raw: string): PhoneFormatResult {
  // Romanian mobile numbers: 07xx xxx xxx -> +40 7xx xxx xxx
  // ...
}
```

---

## Romanian Text

All user-facing Romanian text lives exclusively in `src/core/i18n/labels.ts`.

**Components never contain inline Romanian strings:**

```tsx
// Wrong
<Button>Descarca HTML</Button>

// Correct
import { labels } from '@/core/i18n/labels';
<Button>{labels.signature.exportHtml}</Button>
```

**Exceptions:**

- Test files may contain Romanian strings as test fixtures.
- Data files (presets, defaults) may contain Romanian field names that are domain terms, not UI labels.

Romanian diacritics in label values: use them where correct (`Semnatura` vs `Semnatura` -- prefer with diacritics in label constants if the display context supports it). For technical identifiers (XML field names, CSS classes, file paths), never use diacritics.

---

## Git Conventions

### Commit Messages

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

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

[optional body]

[optional footer]
```

**Types:**

| Type | Usage |
|---|---|
| `feat` | New feature |
| `fix` | Bug fix |
| `refactor` | Code restructuring without behavior change |
| `docs` | Documentation only |
| `style` | Formatting, whitespace (not CSS) |
| `test` | Adding or fixing tests |
| `chore` | Build, tooling, dependencies |
| `perf` | Performance improvement |

**Scope:** module name or area (`signature`, `xml-generator`, `core`, `ui`, `config`).

**Examples:**

```
feat(signature): add multi-company support to signature builder
fix(xml-generator): handle duplicate field names in category
refactor(core): extract storage abstraction from localStorage calls
docs(guides): add HTML tool integration plan
chore(deps): upgrade shadcn/ui to 2.1.0
```

### Branching

- `main` -- production-ready code. Protected. No direct pushes.
- `feature/<description>` -- feature branches, branched from `main`.
- `fix/<description>` -- bugfix branches.
- `chore/<description>` -- maintenance branches.

### Pull Requests

- One logical change per PR.
- PR title follows conventional commit format.
- PR description includes: what changed, why, how to test.
- All CI checks must pass before merge.
- Squash merge to `main` (clean linear history).

---

## Anti-Patterns

The following patterns are explicitly prohibited in this codebase. Each entry includes the reason and the correct alternative.

### 1. `any` as escape hatch

```tsx
// Wrong
const data: any = fetchSomething();

// Correct
const data: unknown = fetchSomething();
if (isValidConfig(data)) { /* narrow type */ }
```

### 2. Default exports

```tsx
// Wrong
export default function Page() { ... }

// Correct (Next.js pages are the sole exception -- App Router requires default exports for page.tsx/layout.tsx)
// For page.tsx and layout.tsx ONLY, default export is acceptable because Next.js requires it.
export default function Page() { ... }

// Everything else: named exports
export function SignatureForm() { ... }
```

### 3. Direct DOM manipulation in components

```tsx
// Wrong
useEffect(() => {
  document.getElementById('preview')!.innerHTML = html;
}, [html]);

// Correct
return <div dangerouslySetInnerHTML={{ __html: html }} />;
// Or better: render structured JSX from data
```

### 4. Inline Romanian strings

```tsx
// Wrong
<Label>Nume si Prenume</Label>

// Correct
<Label>{labels.signature.fieldName}</Label>
```

### 5. God hooks

```tsx
// Wrong: hook that manages 15 pieces of state and 10 callbacks
export function useEverything() { ... }

// Correct: split by responsibility
export function useSignatureConfig() { ... }
export function useSignatureBuilder() { ... }
export function useSignatureExport() { ... }
```

### 6. Barrel re-exports at module boundary

```tsx
// Wrong: src/modules/xml-generator/index.ts
export * from './hooks/use-xml-generator';

// Correct: import directly from source file
import { useXmlGenerator } from '@/modules/xml-generator/hooks/use-xml-generator';
```

### 7. Business logic in components

```tsx
// Wrong: XML sanitization logic inline in JSX
function XmlForm() {
  const sanitize = (name: string) => {
    let n = name.replace(/\s+/g, '_');
    // 20 more lines of logic...
  };
}

// Correct: extract to utility, test separately
import { sanitizeXmlName } from '../utils/sanitize-xml-name';
```

### 8. Untyped hook returns

```tsx
// Wrong: caller has to guess the shape
export function useConfig() {
  return [config, setConfig, isLoading, error];
}

// Correct: explicit interface
interface ConfigState {
  config: Config;
  setConfig: (c: Config) => void;
  isLoading: boolean;
  error: Error | null;
}
export function useConfig(): ConfigState { ... }
```

### 9. `useEffect` for derived state

```tsx
// Wrong: effect to compute a value from props
const [fullName, setFullName] = useState('');
useEffect(() => {
  setFullName(`${prefix} ${name}`);
}, [prefix, name]);

// Correct: derive during render
const fullName = `${prefix} ${name}`;
// Or useMemo if computation is expensive
const fullName = useMemo(() => expensiveFormat(prefix, name), [prefix, name]);
```

### 10. Committing secrets or environment files

Never commit `.env`, `.env.local`, credentials, API keys, or tokens. The `.gitignore` must include these patterns. If a secret is accidentally committed, rotate it immediately -- do not just delete the file.
