# Frontend-Backend Integration

## Overview

This document describes how frontend applications communicate with the Building Management System backend.

## Communication Architecture

```
┌─────────────────────────────────────────────────────────────────────────────┐
│                          Frontend Application                                │
│                     (Web App / Mobile App / External)                        │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  ┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐         │
│  │   HTTP Client   │    │   WebSocket     │    │   State Store   │         │
│  │    (Axios)      │    │   (Pusher)      │    │   (Pinia/Redux) │         │
│  └────────┬────────┘    └────────┬────────┘    └─────────────────┘         │
│           │                      │                                          │
└───────────┼──────────────────────┼──────────────────────────────────────────┘
            │                      │
            │    ┌─────────────────┘
            │    │
            ▼    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                           API Gateway                                        │
│                   (Laravel + Sanctum + Pusher)                               │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  ┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐         │
│  │   REST API      │    │   Broadcasting  │    │   File Storage  │         │
│  │  /api/v1/*      │    │   (WebSocket)   │    │   /storage/*    │         │
│  └─────────────────┘    └─────────────────┘    └─────────────────┘         │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘
```

## HTTP Communication

### API Client Setup (Axios)

```javascript
// api/client.js
import axios from 'axios';

const apiClient = axios.create({
    baseURL: process.env.VUE_APP_API_URL || 'http://127.0.0.1:8001/api/v1',
    headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
    },
});

// Request interceptor for auth token
apiClient.interceptors.request.use((config) => {
    const token = localStorage.getItem('auth_token');
    if (token) {
        config.headers.Authorization = `Bearer ${token}`;
    }

    // Add language header
    config.headers['Accept-Language'] = localStorage.getItem('locale') || 'en';

    // Add branch header if set
    const branchId = localStorage.getItem('branch_id');
    if (branchId) {
        config.headers['X-Branch-ID'] = branchId;
    }

    return config;
});

// Response interceptor for error handling
apiClient.interceptors.response.use(
    (response) => response,
    (error) => {
        if (error.response?.status === 401) {
            // Handle unauthorized - redirect to login
            localStorage.removeItem('auth_token');
            window.location.href = '/login';
        }
        return Promise.reject(error);
    }
);

export default apiClient;
```

### API Service Example

```javascript
// api/services/userService.js
import apiClient from '../client';

export const userService = {
    async getAll(params = {}) {
        const response = await apiClient.get('/users', { params });
        return response.data;
    },

    async getById(id) {
        const response = await apiClient.get(`/users/${id}`);
        return response.data;
    },

    async create(userData) {
        const response = await apiClient.post('/users', userData);
        return response.data;
    },

    async update(id, userData) {
        const response = await apiClient.put(`/users/${id}`, userData);
        return response.data;
    },

    async delete(id) {
        await apiClient.delete(`/users/${id}`);
    }
};
```

### Authentication Flow

```
┌──────────────┐                      ┌──────────────┐
│   Frontend   │                      │   Backend    │
└──────┬───────┘                      └──────┬───────┘
       │                                     │
       │  POST /auth/login                   │
       │  {email, password}                  │
       │────────────────────────────────────▶│
       │                                     │
       │                                     │ Validate credentials
       │                                     │ Generate Sanctum token
       │                                     │
       │  {token, user}                      │
       │◀────────────────────────────────────│
       │                                     │
       │  Store token in localStorage        │
       │                                     │
       │  GET /users (with Bearer token)     │
       │────────────────────────────────────▶│
       │                                     │
       │  {users: [...]}                     │
       │◀────────────────────────────────────│
       │                                     │
```

### Login Implementation

```javascript
// api/services/authService.js
import apiClient from '../client';

export const authService = {
    async login(email, password) {
        const response = await apiClient.post('/auth/login', {
            email,
            password
        });

        const { token, user } = response.data.data;

        // Store token
        localStorage.setItem('auth_token', token);
        localStorage.setItem('user', JSON.stringify(user));

        return user;
    },

    async logout() {
        try {
            await apiClient.post('/auth/logout');
        } finally {
            localStorage.removeItem('auth_token');
            localStorage.removeItem('user');
        }
    },

    async getCurrentUser() {
        const response = await apiClient.get('/auth/me');
        return response.data.data;
    },

    isAuthenticated() {
        return !!localStorage.getItem('auth_token');
    }
};
```

## Real-Time Communication

### Pusher Configuration

**Backend (.env)**
```env
BROADCAST_DRIVER=pusher
PUSHER_APP_ID=your_app_id
PUSHER_APP_KEY=your_app_key
PUSHER_APP_SECRET=your_app_secret
PUSHER_APP_CLUSTER=ap2
```

**Frontend Setup**
```javascript
// plugins/pusher.js
import Pusher from 'pusher-js';
import Echo from 'laravel-echo';

window.Pusher = Pusher;

const echo = new Echo({
    broadcaster: 'pusher',
    key: process.env.VUE_APP_PUSHER_KEY,
    cluster: process.env.VUE_APP_PUSHER_CLUSTER,
    forceTLS: true,
    authorizer: (channel) => ({
        authorize: (socketId, callback) => {
            apiClient.post('/broadcasting/auth', {
                socket_id: socketId,
                channel_name: channel.name
            })
            .then(response => callback(null, response.data))
            .catch(error => callback(error));
        }
    })
});

export default echo;
```

### Subscribing to Channels

```javascript
// components/NotificationListener.vue
import echo from '@/plugins/pusher';

export default {
    mounted() {
        // Public channel
        echo.channel('notifications')
            .listen('NewNotification', (event) => {
                this.handleNotification(event);
            });

        // Private channel (authenticated)
        echo.private(`users.${this.userId}`)
            .listen('OrderUpdated', (event) => {
                this.handleOrderUpdate(event);
            });

        // Presence channel (shows online users)
        echo.join(`project.${this.projectId}`)
            .here((users) => {
                this.onlineUsers = users;
            })
            .joining((user) => {
                this.onlineUsers.push(user);
            })
            .leaving((user) => {
                this.onlineUsers = this.onlineUsers.filter(u => u.id !== user.id);
            });
    },

    beforeUnmount() {
        echo.leave('notifications');
        echo.leave(`users.${this.userId}`);
        echo.leave(`project.${this.projectId}`);
    }
};
```

### Backend Broadcasting

```php
// Events/OrderUpdated.php
class OrderUpdated implements ShouldBroadcast
{
    public function __construct(
        public Order $order
    ) {}

    public function broadcastOn(): array
    {
        return [
            new PrivateChannel('users.' . $this->order->user_id),
        ];
    }

    public function broadcastWith(): array
    {
        return [
            'order_id' => $this->order->id,
            'status' => $this->order->status,
            'updated_at' => $this->order->updated_at->toISOString(),
        ];
    }
}
```

## File Uploads

### Single File Upload

```javascript
// Frontend
async uploadFile(file) {
    const formData = new FormData();
    formData.append('file', file);
    formData.append('type', 'document');

    const response = await apiClient.post('/files/upload', formData, {
        headers: {
            'Content-Type': 'multipart/form-data'
        },
        onUploadProgress: (progressEvent) => {
            const progress = Math.round(
                (progressEvent.loaded * 100) / progressEvent.total
            );
            this.uploadProgress = progress;
        }
    });

    return response.data.data.url;
}
```

### Backend File Handling

```php
// FileController.php
public function upload(Request $request): JsonResponse
{
    $request->validate([
        'file' => 'required|file|max:10240', // 10MB
        'type' => 'required|string'
    ]);

    $path = $request->file('file')->store(
        'uploads/' . $request->type,
        'public'
    );

    return response()->json([
        'success' => true,
        'data' => [
            'url' => Storage::url($path),
            'path' => $path
        ]
    ]);
}
```

## Error Handling

### Frontend Error Handler

```javascript
// utils/errorHandler.js
export function handleApiError(error) {
    if (error.response) {
        const { status, data } = error.response;

        switch (status) {
            case 422:
                // Validation errors
                return {
                    type: 'validation',
                    message: data.message,
                    errors: data.errors
                };

            case 401:
                // Unauthorized
                return {
                    type: 'auth',
                    message: 'Please log in to continue'
                };

            case 403:
                // Forbidden
                return {
                    type: 'permission',
                    message: 'You do not have permission for this action'
                };

            case 404:
                // Not found
                return {
                    type: 'not_found',
                    message: 'The requested resource was not found'
                };

            default:
                // Server error
                return {
                    type: 'server',
                    message: 'An unexpected error occurred'
                };
        }
    }

    // Network error
    return {
        type: 'network',
        message: 'Unable to connect to server'
    };
}
```

### Form Validation Display

```vue
<!-- components/FormErrors.vue -->
<template>
    <div v-if="errors" class="alert alert-danger">
        <ul>
            <li v-for="(messages, field) in errors" :key="field">
                {{ messages[0] }}
            </li>
        </ul>
    </div>
</template>

<script>
export default {
    props: {
        errors: {
            type: Object,
            default: null
        }
    }
};
</script>
```

## State Management Integration

### Pinia Store Example

```javascript
// stores/users.js
import { defineStore } from 'pinia';
import { userService } from '@/api/services/userService';

export const useUserStore = defineStore('users', {
    state: () => ({
        users: [],
        currentUser: null,
        loading: false,
        error: null,
        pagination: {
            currentPage: 1,
            lastPage: 1,
            total: 0
        }
    }),

    actions: {
        async fetchUsers(params = {}) {
            this.loading = true;
            this.error = null;

            try {
                const response = await userService.getAll(params);
                this.users = response.data;
                this.pagination = {
                    currentPage: response.meta.current_page,
                    lastPage: response.meta.last_page,
                    total: response.meta.total
                };
            } catch (error) {
                this.error = handleApiError(error);
            } finally {
                this.loading = false;
            }
        },

        async createUser(userData) {
            this.loading = true;

            try {
                const response = await userService.create(userData);
                this.users.unshift(response.data);
                return response.data;
            } catch (error) {
                this.error = handleApiError(error);
                throw error;
            } finally {
                this.loading = false;
            }
        }
    }
});
```

## Environment Configuration

### Frontend Environment Variables

```env
# .env.development
VUE_APP_API_URL=http://127.0.0.1:8001/api/v1
VUE_APP_PUSHER_KEY=your_dev_key
VUE_APP_PUSHER_CLUSTER=ap2

# .env.production
VUE_APP_API_URL=https://api.example.com/api/v1
VUE_APP_PUSHER_KEY=your_prod_key
VUE_APP_PUSHER_CLUSTER=ap2
```

### CORS Configuration

**Backend (config/cors.php)**
```php
return [
    'paths' => ['api/*', 'sanctum/csrf-cookie', 'broadcasting/auth'],

    'allowed_methods' => ['*'],

    'allowed_origins' => [
        'http://localhost:3000',
        'https://app.example.com',
    ],

    'allowed_headers' => ['*'],

    'exposed_headers' => [
        'X-RateLimit-Limit',
        'X-RateLimit-Remaining',
    ],

    'max_age' => 0,

    'supports_credentials' => true,
];
```

## Integration Testing

### API Testing with Frontend

```javascript
// tests/integration/userApi.test.js
import { userService } from '@/api/services/userService';
import { setupServer } from 'msw/node';
import { rest } from 'msw';

const server = setupServer(
    rest.get('/api/v1/users', (req, res, ctx) => {
        return res(ctx.json({
            success: true,
            data: [
                { id: 1, name: 'John Doe' }
            ]
        }));
    })
);

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

test('fetches users successfully', async () => {
    const result = await userService.getAll();
    expect(result.data).toHaveLength(1);
    expect(result.data[0].name).toBe('John Doe');
});
```
