.. _frontend-development: Frontend Development Guide ========================== Welcome to the Buildly Frontend Development Guide. This comprehensive resource covers everything you need to build modern, responsive frontends that integrate seamlessly with Buildly Core. .. contents:: Table of Contents :local: :depth: 2 Overview -------- Buildly provides flexible frontend options to suit your development preferences and project requirements: - **React Template** - Modern React application with Material-UI - **Angular UI Library** - Comprehensive Angular component library - **Standalone Integration** - Connect any frontend framework to Buildly Core Technology Stack ---------------- **Supported Frontend Frameworks:** - React 18+ with TypeScript - Angular 12+ with TypeScript - Vue.js 3+ (community supported) - Vanilla JavaScript/TypeScript **UI Component Libraries:** - Material-UI (MUI) for React - Buildly UI Angular components - Custom design systems supported **State Management:** - React: Redux, Context API, Zustand - Angular: RxJS, NgRx - Buildly Core API integration patterns Quick Start ----------- React Template ~~~~~~~~~~~~~~ The Buildly React Template provides a complete starting point for React applications. **Installation:** .. code-block:: bash # Clone the React template git clone https://github.com/buildlyio/buildly-react-template.git my-app cd my-app # Install dependencies npm install # Configure environment cp .env.example .env # Edit .env with your Buildly Core API URL # Start development server npm start **Key Features:** - Pre-configured routing with React Router - Material-UI component integration - OAuth 2.0 authentication setup - API service layer for Buildly Core - Responsive layouts and themes - Form handling with validation - Internationalization (i18n) support Angular UI Library ~~~~~~~~~~~~~~~~~~ The Buildly Angular UI library provides reusable components and services. **Installation:** .. code-block:: bash # Create new Angular app ng new my-buildly-app cd my-buildly-app # Install Buildly UI library npm install @buildly/ui-angular # Import in your app module # See Angular integration guide below **Key Components:** - Data tables with sorting and filtering - Form components with validation - Navigation and layout components - Charts and data visualization - Authentication components Project Structure ----------------- React Template Structure ~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: text buildly-react-template/ ├── public/ │ ├── index.html │ └── assets/ ├── src/ │ ├── components/ # Reusable UI components │ │ ├── common/ │ │ ├── forms/ │ │ └── layouts/ │ ├── pages/ # Page-level components │ │ ├── Dashboard/ │ │ ├── Auth/ │ │ └── Profile/ │ ├── services/ # API services │ │ ├── api.js │ │ ├── auth.js │ │ └── buildly.js │ ├── store/ # State management │ │ ├── actions/ │ │ ├── reducers/ │ │ └── store.js │ ├── utils/ # Helper functions │ │ ├── validators.js │ │ └── formatters.js │ ├── routes/ # Route configuration │ ├── theme/ # Theme customization │ ├── App.js │ └── index.js ├── package.json ├── .env.example └── README.md **Recommended Practices:** - Keep components small and focused - Use functional components with hooks - Implement proper TypeScript types - Follow component naming conventions - Organize by feature when scaling Buildly Core Integration ------------------------- API Configuration ~~~~~~~~~~~~~~~~~ Configure your frontend to connect to Buildly Core API. **Environment Configuration:** .. code-block:: javascript // .env file REACT_APP_API_URL=https://api.yourdomain.com REACT_APP_OAUTH_CLIENT_ID=your-client-id REACT_APP_OAUTH_CLIENT_SECRET=your-client-secret **API Service Setup:** .. code-block:: javascript // src/services/buildly.js import axios from 'axios'; const API_URL = process.env.REACT_APP_API_URL; // Create axios instance with default config const buildlyAPI = axios.create({ baseURL: API_URL, timeout: 10000, headers: { 'Content-Type': 'application/json', } }); // Add authentication token to requests buildlyAPI.interceptors.request.use( (config) => { const token = localStorage.getItem('access_token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }, (error) => Promise.reject(error) ); // Handle token refresh on 401 buildlyAPI.interceptors.response.use( (response) => response, async (error) => { const originalRequest = error.config; if (error.response.status === 401 && !originalRequest._retry) { originalRequest._retry = true; try { const refreshToken = localStorage.getItem('refresh_token'); const response = await axios.post(`${API_URL}/oauth/token/`, { grant_type: 'refresh_token', refresh_token: refreshToken, client_id: process.env.REACT_APP_OAUTH_CLIENT_ID, }); localStorage.setItem('access_token', response.data.access_token); return buildlyAPI(originalRequest); } catch (refreshError) { // Redirect to login window.location.href = '/login'; return Promise.reject(refreshError); } } return Promise.reject(error); } ); export default buildlyAPI; Authentication Implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ OAuth 2.0 authentication with Buildly Core. **Login Flow:** .. code-block:: javascript // src/services/auth.js import buildlyAPI from './buildly'; export const authService = { async login(username, password) { try { const response = await buildlyAPI.post('/oauth/token/', { grant_type: 'password', username, password, client_id: process.env.REACT_APP_OAUTH_CLIENT_ID, client_secret: process.env.REACT_APP_OAUTH_CLIENT_SECRET, }); const { access_token, refresh_token, user } = response.data; // Store tokens localStorage.setItem('access_token', access_token); localStorage.setItem('refresh_token', refresh_token); localStorage.setItem('user', JSON.stringify(user)); return { success: true, user }; } catch (error) { return { success: false, error: error.response?.data?.error || 'Login failed' }; } }, async logout() { // Clear local storage localStorage.removeItem('access_token'); localStorage.removeItem('refresh_token'); localStorage.removeItem('user'); // Optional: Call server logout endpoint try { await buildlyAPI.post('/oauth/revoke-token/'); } catch (error) { console.error('Logout error:', error); } }, getCurrentUser() { const userStr = localStorage.getItem('user'); return userStr ? JSON.parse(userStr) : null; }, isAuthenticated() { return !!localStorage.getItem('access_token'); } }; **Protected Routes:** .. code-block:: javascript // src/components/ProtectedRoute.js import React from 'react'; import { Navigate } from 'react-router-dom'; import { authService } from '../services/auth'; const ProtectedRoute = ({ children }) => { if (!authService.isAuthenticated()) { return ; } return children; }; export default ProtectedRoute; State Management ---------------- Redux Setup (React) ~~~~~~~~~~~~~~~~~~~ **Store Configuration:** .. code-block:: javascript // src/store/store.js import { createStore, applyMiddleware, combineReducers } from 'redux'; import thunk from 'redux-thunk'; import { composeWithDevTools } from 'redux-devtools-extension'; // Import reducers import authReducer from './reducers/authReducer'; import dataReducer from './reducers/dataReducer'; import uiReducer from './reducers/uiReducer'; const rootReducer = combineReducers({ auth: authReducer, data: dataReducer, ui: uiReducer, }); const store = createStore( rootReducer, composeWithDevTools(applyMiddleware(thunk)) ); export default store; **Example Reducer:** .. code-block:: javascript // src/store/reducers/authReducer.js const initialState = { user: null, isAuthenticated: false, loading: false, error: null, }; export default function authReducer(state = initialState, action) { switch (action.type) { case 'LOGIN_REQUEST': return { ...state, loading: true, error: null }; case 'LOGIN_SUCCESS': return { ...state, loading: false, isAuthenticated: true, user: action.payload, error: null, }; case 'LOGIN_FAILURE': return { ...state, loading: false, isAuthenticated: false, error: action.payload, }; case 'LOGOUT': return initialState; default: return state; } } **Actions:** .. code-block:: javascript // src/store/actions/authActions.js import { authService } from '../../services/auth'; export const login = (username, password) => async (dispatch) => { dispatch({ type: 'LOGIN_REQUEST' }); const result = await authService.login(username, password); if (result.success) { dispatch({ type: 'LOGIN_SUCCESS', payload: result.user }); } else { dispatch({ type: 'LOGIN_FAILURE', payload: result.error }); } }; export const logout = () => async (dispatch) => { await authService.logout(); dispatch({ type: 'LOGOUT' }); }; UI Components ------------- Common Component Patterns ~~~~~~~~~~~~~~~~~~~~~~~~~~ **Data Table Component:** .. code-block:: jsx // src/components/common/DataTable.jsx import React, { useState } from 'react'; import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TablePagination, Paper, CircularProgress, } from '@mui/material'; const DataTable = ({ columns, data, loading = false, onRowClick, pagination = true }) => { const [page, setPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(10); const handleChangePage = (event, newPage) => { setPage(newPage); }; const handleChangeRowsPerPage = (event) => { setRowsPerPage(parseInt(event.target.value, 10)); setPage(0); }; if (loading) { return ( ); } const paginatedData = pagination ? data.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) : data; return ( {columns.map((column) => ( {column.label} ))} {paginatedData.map((row, index) => ( onRowClick && onRowClick(row)} sx={{ cursor: onRowClick ? 'pointer' : 'default' }} > {columns.map((column) => ( {column.render ? column.render(row[column.id], row) : row[column.id] } ))} ))}
{pagination && ( )}
); }; export default DataTable; **Form Component with Validation:** .. code-block:: jsx // src/components/common/Form.jsx import React from 'react'; import { useForm, Controller } from 'react-hook-form'; import { TextField, Button, Box } from '@mui/material'; const Form = ({ fields, onSubmit, submitText = 'Submit' }) => { const { control, handleSubmit, formState: { errors } } = useForm(); return ( {fields.map((field) => ( ( )} /> ))} ); }; export default Form; Styling and Theming ------------------- Material-UI Theme Customization ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Custom Theme:** .. code-block:: javascript // src/theme/theme.js import { createTheme } from '@mui/material/styles'; const theme = createTheme({ palette: { primary: { main: '#0C5595', light: '#4A8BC2', dark: '#083A68', }, secondary: { main: '#FF6B35', }, background: { default: '#F5F7FA', paper: '#FFFFFF', }, }, typography: { fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif', h1: { fontSize: '2.5rem', fontWeight: 600, }, h2: { fontSize: '2rem', fontWeight: 600, }, button: { textTransform: 'none', }, }, shape: { borderRadius: 8, }, components: { MuiButton: { styleOverrides: { root: { borderRadius: 8, padding: '10px 24px', }, }, }, MuiCard: { styleOverrides: { root: { boxShadow: '0 2px 8px rgba(0,0,0,0.1)', }, }, }, }, }); export default theme; **Apply Theme:** .. code-block:: jsx // src/App.js import React from 'react'; import { ThemeProvider } from '@mui/material/styles'; import CssBaseline from '@mui/material/CssBaseline'; import theme from './theme/theme'; import Routes from './routes'; function App() { return ( ); } export default App; Routing ------- React Router Setup ~~~~~~~~~~~~~~~~~~ **Route Configuration:** .. code-block:: jsx // src/routes/index.jsx import React from 'react'; import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'; import ProtectedRoute from '../components/ProtectedRoute'; // Pages import Login from '../pages/Auth/Login'; import Register from '../pages/Auth/Register'; import Dashboard from '../pages/Dashboard'; import Profile from '../pages/Profile'; import NotFound from '../pages/NotFound'; const AppRoutes = () => { return ( {/* Public Routes */} } /> } /> {/* Protected Routes */} } /> } /> {/* Redirects and 404 */} } /> } /> ); }; export default AppRoutes; Testing ------- Unit Testing ~~~~~~~~~~~~ **Component Testing with React Testing Library:** .. code-block:: javascript // src/components/common/__tests__/DataTable.test.js import React from 'react'; import { render, screen, fireEvent } from '@testing-library/react'; import DataTable from '../DataTable'; describe('DataTable', () => { const mockColumns = [ { id: 'name', label: 'Name' }, { id: 'email', label: 'Email' }, ]; const mockData = [ { name: 'John Doe', email: 'john@example.com' }, { name: 'Jane Smith', email: 'jane@example.com' }, ]; it('renders table with data', () => { render(); expect(screen.getByText('John Doe')).toBeInTheDocument(); expect(screen.getByText('jane@example.com')).toBeInTheDocument(); }); it('calls onRowClick when row is clicked', () => { const handleRowClick = jest.fn(); render( ); const firstRow = screen.getByText('John Doe').closest('tr'); fireEvent.click(firstRow); expect(handleRowClick).toHaveBeenCalledWith(mockData[0]); }); }); **API Service Testing:** .. code-block:: javascript // src/services/__tests__/auth.test.js import { authService } from '../auth'; import buildlyAPI from '../buildly'; jest.mock('../buildly'); describe('authService', () => { beforeEach(() => { localStorage.clear(); jest.clearAllMocks(); }); it('logs in successfully', async () => { const mockResponse = { data: { access_token: 'mock-token', refresh_token: 'mock-refresh', user: { id: 1, username: 'testuser' }, }, }; buildlyAPI.post.mockResolvedValue(mockResponse); const result = await authService.login('testuser', 'password'); expect(result.success).toBe(true); expect(localStorage.getItem('access_token')).toBe('mock-token'); }); }); Build and Deployment -------------------- Production Build ~~~~~~~~~~~~~~~~ **Build Configuration:** .. code-block:: bash # Build for production npm run build # Output is in build/ directory # - Minified and optimized # - Source maps for debugging # - Static assets with cache busting **Environment-Specific Builds:** .. code-block:: bash # Development npm run build:dev # Staging npm run build:staging # Production npm run build:prod **package.json scripts:** .. code-block:: json { "scripts": { "start": "react-scripts start", "build": "react-scripts build", "build:dev": "REACT_APP_ENV=development npm run build", "build:staging": "REACT_APP_ENV=staging npm run build", "build:prod": "REACT_APP_ENV=production npm run build", "test": "react-scripts test", "test:coverage": "react-scripts test --coverage --watchAll=false" } } Performance Optimization ------------------------ Code Splitting ~~~~~~~~~~~~~~ **Lazy Loading Routes:** .. code-block:: jsx import React, { lazy, Suspense } from 'react'; import { CircularProgress, Box } from '@mui/material'; // Lazy load pages const Dashboard = lazy(() => import('../pages/Dashboard')); const Profile = lazy(() => import('../pages/Profile')); const LoadingFallback = () => ( ); const AppRoutes = () => { return ( }> } /> } /> ); }; **Component Memoization:** .. code-block:: jsx import React, { memo, useMemo, useCallback } from 'react'; const ExpensiveComponent = memo(({ data, onItemClick }) => { // Component only re-renders when data or onItemClick changes const processedData = useMemo(() => { return data.map(item => ({ ...item, processed: heavyComputation(item) })); }, [data]); const handleClick = useCallback((item) => { onItemClick(item); }, [onItemClick]); return (
{processedData.map(item => (
handleClick(item)}> {item.name}
))}
); }); Best Practices -------------- Code Organization ~~~~~~~~~~~~~~~~~ ✅ **DO:** - Use functional components with hooks - Implement proper TypeScript types - Keep components under 250 lines - Use meaningful component names - Organize by feature for large apps - Implement proper error boundaries - Use environment variables for config ❌ **DON'T:** - Mix business logic with UI code - Use inline styles (use theme) - Commit API keys or secrets - Ignore PropTypes/TypeScript - Nest components too deeply - Fetch data in render methods Accessibility ~~~~~~~~~~~~~ - Use semantic HTML elements - Add ARIA labels where needed - Ensure keyboard navigation works - Maintain proper heading hierarchy - Provide text alternatives for images - Use sufficient color contrast - Test with screen readers Security ~~~~~~~~ - Sanitize user inputs - Implement CSRF protection - Use HTTPOnly cookies for tokens - Validate on both client and server - Keep dependencies updated - Implement rate limiting - Use Content Security Policy Next Steps ---------- **Further Reading:** - :doc:`/design/responsive` - Responsive design guidelines - :doc:`/backend/api-development` - API integration patterns - :doc:`/deployment/docker` - Deployment strategies **External Resources:** - `React Documentation `_ - `Material-UI Documentation `_ - `Buildly React Template Repository `_ **Video Resources:** - `Buildly YouTube Channel `_ - Frontend development tutorials and demos - `OpenBuild YouTube Channel `_ - Additional frontend development guides **Community:** - Join the Buildly Slack community - Contribute to GitHub repositories - Share your implementations .. note:: This guide is continuously updated. Check the GitHub repository for the latest examples and templates.