Skip to main content

Directory Overview

The Zarna frontend follows a structured, modular organization:
zarna-frontend/
├── src/                      # Source code
│   ├── components/           # React components
│   │   ├── ui/              # shadcn/ui components (auto-generated)
│   │   ├── layout/          # Layout components
│   │   ├── features/        # Feature-specific components
│   │   └── generated/       # Generated components
│   │
│   ├── context/             # React Context providers
│   │   ├── AuthContext.tsx
│   │   └── CompanyContext.tsx
│   │
│   ├── hooks/               # Custom React hooks
│   │   ├── use-mobile.ts
│   │   ├── use-toast.ts
│   │   └── use-auth.ts
│   │
│   ├── lib/                 # Utility functions
│   │   └── utils.ts
│   │
│   ├── services/            # API integration
│   │   └── api.ts
│   │
│   ├── utils/               # Helper functions
│   │
│   ├── App.tsx              # Main application component
│   ├── main.tsx             # Application entry point
│   └── index.css            # Global styles

├── public/                   # Static assets
│   ├── images/
│   ├── fonts/
│   └── icons/

├── components.json           # shadcn/ui configuration
├── tsconfig.json            # TypeScript configuration
├── vite.config.ts           # Vite configuration
├── tailwind.config.js       # Tailwind CSS configuration
├── postcss.config.mjs       # PostCSS configuration
├── package.json             # Dependencies and scripts
├── .eslintrc.json          # ESLint configuration
├── .prettierrc             # Prettier configuration
└── README.md               # Project documentation

Directory Guidelines

src/components/

Component organization by purpose:

ui/ - shadcn/ui Components

🚨 DO NOT MANUALLY EDIT - Generated by shadcn CLI
components/ui/
├── button.tsx               # Button component
├── card.tsx                 # Card component
├── dialog.tsx               # Dialog/modal component
├── dropdown-menu.tsx        # Dropdown menu component
├── form.tsx                 # Form component
├── input.tsx                # Input component
├── select.tsx               # Select component
├── table.tsx                # Table component
├── toast.tsx                # Toast notification component
└── ...                      # 40+ more components
Adding components:
npx shadcn@latest add button
npx shadcn@latest add dialog

layout/ - Layout Components

Reusable layout components:
components/layout/
├── Layout.tsx               # Main layout wrapper
├── Header.tsx               # Application header
├── Sidebar.tsx              # Navigation sidebar
├── Footer.tsx               # Application footer
└── Container.tsx            # Content container

features/ - Feature Components

Domain-specific components organized by feature:
components/features/
├── auth/
│   ├── LoginForm.tsx
│   ├── RegisterForm.tsx
│   └── ResetPasswordForm.tsx

├── companies/
│   ├── CompanyCard.tsx
│   ├── CompanyList.tsx
│   ├── CompanyForm.tsx
│   └── CompanyDetails.tsx

├── deals/
│   ├── DealPipeline.tsx
│   ├── DealCard.tsx
│   └── DealForm.tsx

└── files/
    ├── FileUploader.tsx
    ├── FileViewer.tsx
    └── PDFViewer.tsx

src/context/

React Context for global state:
// AuthContext.tsx
import { createContext, useContext, useState } from 'react'

interface AuthContextType {
  user: User | null
  login: (credentials: Credentials) => Promise<void>
  logout: () => void
  isAuthenticated: boolean
}

const AuthContext = createContext<AuthContextType | undefined>(undefined)

export function AuthProvider({ children }) {
  // Context implementation
}

export function useAuth() {
  const context = useContext(AuthContext)
  if (!context) throw new Error('useAuth must be used within AuthProvider')
  return context
}
Current Contexts:
  • AuthContext - Authentication state
  • CompanyContext - Current company selection

src/hooks/

Custom React hooks for reusable logic:
hooks/
├── use-auth.ts              # Authentication hook
├── use-mobile.ts            # Mobile detection hook
├── use-toast.ts             # Toast notifications hook
├── use-debounce.ts          # Debounce hook
├── use-local-storage.ts     # LocalStorage hook
└── use-api.ts               # API call hook
Example hook:
// hooks/use-mobile.ts
import { useEffect, useState } from "react"

const MOBILE_BREAKPOINT = 768

export function useMobile() {
  const [isMobile, setIsMobile] = useState<boolean>(false)

  useEffect(() => {
    const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
    const onChange = () => setIsMobile(mql.matches)

    mql.addEventListener("change", onChange)
    setIsMobile(mql.matches)

    return () => mql.removeEventListener("change", onChange)
  }, [])

  return isMobile
}

src/lib/

Pure utility functions (no React dependencies):
// lib/utils.ts
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

export function formatCurrency(amount: number): string {
  return new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: 'USD'
  }).format(amount)
}

export function truncate(str: string, length: number): string {
  return str.length > length ? str.substring(0, length) + '...' : str
}

src/services/

API integration and external service calls:
// services/api.ts
const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000'

export async function fetchCompanies() {
  const response = await fetch(`${API_BASE_URL}/api/companies`)
  return response.json()
}

export async function createCompany(data: CompanyCreate) {
  const response = await fetch(`${API_BASE_URL}/api/companies`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(data)
  })
  return response.json()
}

public/

Static assets served as-is:
public/
├── images/
│   ├── logo.svg
│   ├── hero.png
│   └── avatars/

├── fonts/
│   └── custom-font.woff2

└── icons/
    ├── favicon.ico
    └── manifest.json
Usage:
<img src="/images/logo.svg" alt="Logo" />

File Naming Conventions

Components

  • PascalCase: ComponentName.tsx
  • Examples: Button.tsx, UserProfile.tsx, DealPipeline.tsx

Hooks

  • kebab-case: use-hook-name.ts
  • Examples: use-auth.ts, use-mobile.ts, use-debounce.ts

Utilities

  • kebab-case: util-name.ts
  • Examples: format-date.ts, api-client.ts, validation.ts

Context

  • PascalCase: ContextName.tsx
  • Examples: AuthContext.tsx, ThemeContext.tsx

Types

  • PascalCase: TypeName.ts or inline in component files
  • Examples: types.ts, api-types.ts

Import Organization

Order imports for consistency:
// 1. External dependencies
import * as React from "react"
import { useState, useEffect } from "react"
import { useNavigate } from "react-router-dom"

// 2. Internal components
import { Button } from "@/components/ui/button"
import { Card } from "@/components/ui/card"

// 3. Hooks
import { useAuth } from "@/hooks/use-auth"
import { useMobile } from "@/hooks/use-mobile"

// 4. Utils
import { cn } from "@/lib/utils"
import { formatDate } from "@/utils/format"

// 5. Types
import type { User, Company } from "@/types"

// 6. Styles (if any)
import "./styles.css"

Path Aliases

Configured in tsconfig.json:
{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}
Usage:
// ✅ Good - using alias
import { Button } from "@/components/ui/button"
import { useAuth } from "@/hooks/use-auth"

// ❌ Bad - relative imports
import { Button } from "../../../components/ui/button"

Configuration Files

tsconfig.json

TypeScript configuration with strict mode:
{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

vite.config.ts

Vite build configuration:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src')
    }
  },
  server: {
    port: 3000,
    proxy: {
      '/api': {
        target: 'http://localhost:8000',
        changeOrigin: true
      }
    }
  }
})

tailwind.config.js

Tailwind CSS configuration:
/** @type {import('tailwindcss').Config} */
export default {
  darkMode: ["class"],
  content: [
    "./index.html",
    "./src/**/*.{ts,tsx}"
  ],
  theme: {
    extend: {
      colors: {
        border: "hsl(var(--border))",
        background: "hsl(var(--background))",
        foreground: "hsl(var(--foreground))",
        // ... theme colors
      }
    }
  },
  plugins: [require("tailwindcss-animate")]
}

components.json

shadcn/ui configuration:
{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "new-york",
  "rsc": false,
  "tsx": true,
  "tailwind": {
    "config": "tailwind.config.js",
    "css": "src/index.css",
    "baseColor": "slate",
    "cssVariables": true
  },
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils",
    "ui": "@/components/ui",
    "lib": "@/lib",
    "hooks": "@/hooks"
  }
}

Best Practices

Component Organization

Each component should have a single responsibility. If a component does too much, split it.
Group related components by feature rather than type.
Move common logic to custom hooks or utility functions.

Naming Conventions

  • Be descriptive: UserProfileCard > Card
  • Be consistent: Always use PascalCase for components
  • Prefix hooks: Always start with use (e.g., useAuth)
  • Suffix contexts: End with Context (e.g., AuthContext)

Next Steps

Components Guide

Learn component patterns and architecture

Theme System

Understand the color system and theming

Best Practices

Follow coding standards

Tech Stack

Explore all technologies