https://github.com/profullstack/coldfollow-web
https://github.com/profullstack/coldfollow-web
Last synced: 22 days ago
JSON representation
- Host: GitHub
- URL: https://github.com/profullstack/coldfollow-web
- Owner: profullstack
- Created: 2025-05-04T13:22:31.000Z (about 1 year ago)
- Default Branch: master
- Last Pushed: 2025-07-01T00:22:26.000Z (12 months ago)
- Last Synced: 2025-12-27T00:52:39.882Z (6 months ago)
- Language: JavaScript
- Size: 2.35 MB
- Stars: 1
- Watchers: 3
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README-adding-routes.md
Awesome Lists containing this project
README
# Adding a New Route/Page/Component
This guide explains how to add a new route, page, or component to the application using our router setup.
## Overview
Our application uses a Single Page Application (SPA) router with helper utilities that simplify adding and managing routes. The router handles page transitions, authentication checks, and subscription requirements with minimal code.
## Step-by-Step Guide
### 1. Create the HTML View File
First, create an HTML file for your new page in the `/views` directory:
```html
My New Feature
My New Feature
This is my new feature page.
Submit
```
### 2. Create an Initializer Function (Optional)
If your page needs JavaScript initialization (event listeners, data loading, etc.), create an initializer function in `page-initializers.js`:
```javascript
/**
* Initialize the new feature page
*/
export function initMyNewFeaturePage() {
console.log('Initializing my new feature page');
// Get elements
const form = document.getElementById('my-feature-form');
if (!form) {
console.error('My feature form not found');
return;
}
// Add event listeners
form.addEventListener('submit', async (e) => {
e.preventDefault();
// Form submission logic
console.log('Form submitted');
// Get form data
const formData = new FormData(form);
const formDataObj = Object.fromEntries(formData.entries());
// Process the form data
console.log('Form data:', formDataObj);
// Additional logic...
});
// Other initialization code...
}
```
Don't forget to export the initializer function at the top of the file:
```javascript
export {
// ... existing exports
initMyNewFeaturePage
} from './page-initializers.js';
```
### 3. Add the Route to the Router
#### Approach 1: Add Directly to router.js
Open `router.js` and add your new route to the `createRoutes` call in the `defineRoutes` function:
```javascript
// In router.js
export function defineRoutes(router) {
console.log('Defining routes...');
// Define routes using the createRoutes helper
const routes = createRoutes({
// Existing routes...
'/': '/views/home.html',
// Add your new route
'/my-new-feature': {
viewPath: '/views/my-new-feature.html',
afterRender: initMyNewFeaturePage, // Optional
requireAuth: true // If the route requires authentication
}
});
// Rest of the function...
}
```
#### Approach 2: Create a Separate Routes File
For better organization in larger applications, you can create a separate file for related routes:
```javascript
// feature-routes.js
import { createRoutes } from './route-helpers.js';
import { initMyNewFeaturePage } from './page-initializers.js';
export const featureRoutes = createRoutes({
'/my-new-feature': {
viewPath: '/views/my-new-feature.html',
afterRender: initMyNewFeaturePage,
requireAuth: true
},
'/my-new-feature/settings': {
viewPath: '/views/my-new-feature-settings.html',
requireAuth: true
}
});
```
Then import and merge these routes in `router.js`:
```javascript
// In router.js
import { featureRoutes } from './feature-routes.js';
export function defineRoutes(router) {
console.log('Defining routes...');
// Define core routes
const coreRoutes = createRoutes({
// Existing routes...
});
// Merge all routes
const routes = {
...coreRoutes,
...featureRoutes
// Add more route collections as needed
};
// Register routes with the router
router.registerRoutes(routes);
// Rest of the function...
}
```
## Route Configuration Options
When adding a route, you can use these options:
| Option | Type | Description |
|--------|------|-------------|
| `viewPath` | String | Path to the HTML view file |
| `afterRender` | Function | Function to run after the page is rendered |
| `beforeEnter` | Function | Function to run before entering the route |
| `requireAuth` | Boolean | Whether the route requires authentication |
| `requireSubscription` | Boolean | Whether the route requires an active subscription |
## Examples
### Basic Route
```javascript
'/about': '/views/about.html'
```
### Route with Initialization
```javascript
'/profile': {
viewPath: '/views/profile.html',
afterRender: initProfilePage
}
```
### Protected Route (Requires Authentication)
```javascript
'/dashboard': {
viewPath: '/views/dashboard.html',
requireAuth: true
}
```
### Premium Route (Requires Subscription)
```javascript
'/premium-feature': {
viewPath: '/views/premium-feature.html',
requireSubscription: true
}
```
### Route with Custom Guard
```javascript
'/admin': {
viewPath: '/views/admin.html',
beforeEnter: (to, from, next) => {
const userRole = localStorage.getItem('user_role');
if (userRole === 'admin') {
next();
} else {
next('/access-denied');
}
}
}
```
## Testing Your New Route
After adding the route, you can test it by navigating to the URL in your browser:
```
http://localhost:3000/my-new-feature
```
The router will handle loading the HTML, running the initializer function, and checking authentication if required.
## Best Practices
1. **Organize Related Routes**: Keep related routes together, either in the same section of `router.js` or in a separate routes file.
2. **Use Descriptive Route Names**: Choose route paths that clearly describe the feature or page.
3. **Follow Naming Conventions**: Use consistent naming for HTML files, initializer functions, and route paths.
4. **Handle Authentication Properly**: Use the `requireAuth` option for protected routes instead of implementing custom checks.
5. **Add Internationalization**: Use `data-i18n` attributes for text that needs to be translated.
6. **Clean Up Event Listeners**: If your initializer adds event listeners, make sure to remove them when the page is unloaded to prevent memory leaks.
7. **Test Thoroughly**: Test your new route with different scenarios (logged in, logged out, with/without subscription) to ensure it behaves correctly.
## Using the Generator Script
The generator script provides tools for creating various components for both client-side and server-side development.
### Prerequisites
Make sure the script is executable:
```bash
chmod +x bin/generator.js
```
### Command Structure
The generator now uses a category-based command structure:
```bash
./bin/generator.js [options]
```
Categories:
- `client`: Client-side generators
- `server`: Server-side generators
### Client-Side Commands
#### Generate a Client Route
```bash
./bin/generator.js client route --route="/my-feature" --name="My Feature" [--auth] [--subscription]
```
Options:
- `--route`: The route path (required, e.g., "/my-feature")
- `--name`: The feature name (required, e.g., "My Feature")
- `--auth`: Add this flag if the route requires authentication
- `--subscription`: Add this flag if the route requires an active subscription
Example:
```bash
./bin/generator.js client route --route="/contact-us" --name="Contact Us"
```
This will:
1. Create a new HTML view file at `public/views/contact-us.html`
2. Add an initializer function `initContactUsPage` to `public/js/page-initializers.js`
3. Add the route to `public/js/router.js`
### Server-Side Commands
#### Generate a Server Route
```bash
./bin/generator.js server route --path="/api/v1/users" --controller="UserController" --method="get"
```
Options:
- `--path`: The API path (required, e.g., "/api/v1/users")
- `--controller`: The controller name (required, e.g., "UserController")
- `--method`: The HTTP method (required, e.g., "get", "post", "put", "delete", "patch")
Example:
```bash
./bin/generator.js server route --path="/api/v1/users" --controller="User" --method="get"
```
This will add a new route to the appropriate route file in the `src/routes` directory.
#### Generate a Database Migration
```bash
./bin/generator.js server migration --name="add_user_fields"
```
Options:
- `--name`: The migration name (required, e.g., "add_user_fields")
Example:
```bash
./bin/generator.js server migration --name="add_user_profile_fields"
```
This will create a timestamped SQL migration file in the `supabase/migrations` directory. The migration file includes clearly separated sections for "up" migrations (changes to apply) and "down" migrations (how to revert the changes).
#### Generate a Controller
```bash
./bin/generator.js server controller --name="UserController"
```
Options:
- `--name`: The controller name (required, e.g., "UserController" or "User")
Example:
```bash
./bin/generator.js server controller --name="User"
```
This will create a new controller file with standard CRUD methods in the `src/controllers` directory.
### Backward Compatibility
For backward compatibility, the old command format is still supported but will show a deprecation warning:
```bash
./bin/generator.js route --route="/my-feature" --name="My Feature" # Deprecated
```
### Getting Help
For general help:
```bash
./bin/generator.js --help
```
For command-specific help:
```bash
./bin/generator.js client route --help
./bin/generator.js server route --help
./bin/generator.js server migration --help
./bin/generator.js server controller --help
```
After running the generator, you can customize the generated files to fit your specific requirements.
## Template-Based Generation
The generator script uses a template-based approach for creating files. Templates are stored in the `templates` directory and are organized by category and type:
```
templates/
├── client/
│ └── route/
│ ├── view.html.template
│ ├── view.js.template
│ └── initializer.js.template
└── server/
├── controller.js.template
├── route.js.template
└── migration.sql.template
```
### Benefits of Template-Based Generation
1. **Separation of Concerns**: Templates are separate from the generator code, making maintenance easier
2. **Consistency**: All generated files follow the same structure and style
3. **Customization**: Templates can be modified without changing the generator code
4. **Flexibility**: New templates can be added for additional file types
### Customizing Templates
If you need to modify the structure or content of generated files, you can edit the templates directly. Templates use placeholders like `{{kebabCase}}`, `{{featureName}}`, and `{{controllerName}}` that are replaced with actual values during generation.
For example, to change the structure of generated HTML views, edit `templates/client/route/view.html.template`.