Enhanced documentation with troubleshooting, developer guides, and more details
This commit is contained in:
parent
3b4298f9ed
commit
30faf64256
380
developer/api-reference.mdx
Normal file
380
developer/api-reference.mdx
Normal file
@ -0,0 +1,380 @@
|
||||
---
|
||||
title: "API Reference"
|
||||
description: "Service functions and Appwrite API usage"
|
||||
---
|
||||
|
||||
# API Reference
|
||||
|
||||
Reference for service functions and Appwrite operations.
|
||||
|
||||
## Appwrite Client Setup
|
||||
|
||||
```javascript
|
||||
import { Client, Databases, Storage, Account, ID, Query } from 'appwrite';
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint(import.meta.env.VITE_APPWRITE_ENDPOINT)
|
||||
.setProject(import.meta.env.VITE_APPWRITE_PROJECT_ID);
|
||||
|
||||
export const databases = new Databases(client);
|
||||
export const storage = new Storage(client);
|
||||
export const account = new Account(client);
|
||||
export { ID, Query };
|
||||
```
|
||||
|
||||
## Database Operations
|
||||
|
||||
### List Documents
|
||||
|
||||
```javascript
|
||||
import { databases, Query } from './appwrite';
|
||||
|
||||
// Basic list
|
||||
const blogs = await databases.listDocuments(
|
||||
'portfolio_db',
|
||||
'blogs'
|
||||
);
|
||||
|
||||
// With queries
|
||||
const featuredBlogs = await databases.listDocuments(
|
||||
'portfolio_db',
|
||||
'blogs',
|
||||
[
|
||||
Query.equal('featured', true),
|
||||
Query.orderDesc('publishDate'),
|
||||
Query.limit(10)
|
||||
]
|
||||
);
|
||||
```
|
||||
|
||||
### Get Single Document
|
||||
|
||||
```javascript
|
||||
const blog = await databases.getDocument(
|
||||
'portfolio_db',
|
||||
'blogs',
|
||||
'document_id'
|
||||
);
|
||||
```
|
||||
|
||||
### Create Document
|
||||
|
||||
```javascript
|
||||
import { ID } from 'appwrite';
|
||||
|
||||
const newBlog = await databases.createDocument(
|
||||
'portfolio_db',
|
||||
'blogs',
|
||||
ID.unique(), // Auto-generate ID
|
||||
{
|
||||
title: 'My Blog Post',
|
||||
category: 'Technology',
|
||||
description: 'A great post...',
|
||||
// ... other fields
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Update Document
|
||||
|
||||
```javascript
|
||||
const updated = await databases.updateDocument(
|
||||
'portfolio_db',
|
||||
'blogs',
|
||||
'document_id',
|
||||
{
|
||||
title: 'Updated Title',
|
||||
// Only include fields to update
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Delete Document
|
||||
|
||||
```javascript
|
||||
await databases.deleteDocument(
|
||||
'portfolio_db',
|
||||
'blogs',
|
||||
'document_id'
|
||||
);
|
||||
```
|
||||
|
||||
## Query Methods
|
||||
|
||||
| Method | Description | Example |
|
||||
|--------|-------------|---------|
|
||||
| `Query.equal(attr, value)` | Exact match | `Query.equal('category', 'Tech')` |
|
||||
| `Query.notEqual(attr, value)` | Not equal | `Query.notEqual('status', 'draft')` |
|
||||
| `Query.lessThan(attr, value)` | Less than | `Query.lessThan('price', 100)` |
|
||||
| `Query.greaterThan(attr, value)` | Greater than | `Query.greaterThan('likes', 10)` |
|
||||
| `Query.search(attr, value)` | Full-text search | `Query.search('title', 'react')` |
|
||||
| `Query.orderAsc(attr)` | Sort ascending | `Query.orderAsc('createdAt')` |
|
||||
| `Query.orderDesc(attr)` | Sort descending | `Query.orderDesc('publishDate')` |
|
||||
| `Query.limit(n)` | Limit results | `Query.limit(10)` |
|
||||
| `Query.offset(n)` | Skip results | `Query.offset(20)` |
|
||||
| `Query.contains(attr, value)` | Array contains | `Query.contains('tags', 'react')` |
|
||||
|
||||
### Complex Queries
|
||||
|
||||
```javascript
|
||||
// Pagination
|
||||
const page2 = await databases.listDocuments(
|
||||
'portfolio_db',
|
||||
'blogs',
|
||||
[
|
||||
Query.orderDesc('publishDate'),
|
||||
Query.limit(10),
|
||||
Query.offset(10) // Skip first 10
|
||||
]
|
||||
);
|
||||
|
||||
// Multiple conditions
|
||||
const filtered = await databases.listDocuments(
|
||||
'portfolio_db',
|
||||
'products',
|
||||
[
|
||||
Query.equal('category', 'Electronics'),
|
||||
Query.equal('onSale', true),
|
||||
Query.lessThan('price', 500),
|
||||
Query.orderAsc('price')
|
||||
]
|
||||
);
|
||||
```
|
||||
|
||||
## Storage Operations
|
||||
|
||||
### Upload File
|
||||
|
||||
```javascript
|
||||
import { storage, ID } from './appwrite';
|
||||
|
||||
const file = await storage.createFile(
|
||||
'blog_images', // Bucket ID
|
||||
ID.unique(), // File ID
|
||||
document.getElementById('fileInput').files[0]
|
||||
);
|
||||
|
||||
// Get file URL
|
||||
const fileUrl = storage.getFileView('blog_images', file.$id);
|
||||
```
|
||||
|
||||
### Get File URL
|
||||
|
||||
```javascript
|
||||
// View URL (for images)
|
||||
const viewUrl = storage.getFileView('bucket_id', 'file_id');
|
||||
|
||||
// Download URL
|
||||
const downloadUrl = storage.getFileDownload('bucket_id', 'file_id');
|
||||
|
||||
// Preview URL (with transformations)
|
||||
const previewUrl = storage.getFilePreview(
|
||||
'bucket_id',
|
||||
'file_id',
|
||||
400, // width
|
||||
300, // height
|
||||
'center', // gravity
|
||||
90 // quality
|
||||
);
|
||||
```
|
||||
|
||||
### Delete File
|
||||
|
||||
```javascript
|
||||
await storage.deleteFile('bucket_id', 'file_id');
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
### Login
|
||||
|
||||
```javascript
|
||||
import { account } from './appwrite';
|
||||
|
||||
const session = await account.createEmailPasswordSession(
|
||||
'user@example.com',
|
||||
'password'
|
||||
);
|
||||
```
|
||||
|
||||
### Get Current User
|
||||
|
||||
```javascript
|
||||
try {
|
||||
const user = await account.get();
|
||||
console.log('Logged in as:', user.email);
|
||||
} catch (error) {
|
||||
console.log('Not logged in');
|
||||
}
|
||||
```
|
||||
|
||||
### Logout
|
||||
|
||||
```javascript
|
||||
await account.deleteSession('current');
|
||||
```
|
||||
|
||||
### Create Account
|
||||
|
||||
```javascript
|
||||
const user = await account.create(
|
||||
ID.unique(),
|
||||
'user@example.com',
|
||||
'password',
|
||||
'User Name' // Optional
|
||||
);
|
||||
```
|
||||
|
||||
## Service Examples
|
||||
|
||||
### Blog Service
|
||||
|
||||
```javascript
|
||||
// src/lib/blogService.js
|
||||
export const blogService = {
|
||||
async listAll() {
|
||||
return await databases.listDocuments(
|
||||
DATABASE_ID,
|
||||
'blogs',
|
||||
[Query.orderDesc('publishDate')]
|
||||
);
|
||||
},
|
||||
|
||||
async listFeatured() {
|
||||
return await databases.listDocuments(
|
||||
DATABASE_ID,
|
||||
'blogs',
|
||||
[
|
||||
Query.equal('featured', true),
|
||||
Query.orderDesc('publishDate'),
|
||||
Query.limit(6)
|
||||
]
|
||||
);
|
||||
},
|
||||
|
||||
async getBySlug(slug) {
|
||||
const result = await databases.listDocuments(
|
||||
DATABASE_ID,
|
||||
'blogs',
|
||||
[Query.equal('customSlug', slug)]
|
||||
);
|
||||
return result.documents[0] || null;
|
||||
},
|
||||
|
||||
async listByCategory(category) {
|
||||
return await databases.listDocuments(
|
||||
DATABASE_ID,
|
||||
'blogs',
|
||||
[
|
||||
Query.equal('category', category),
|
||||
Query.orderDesc('publishDate')
|
||||
]
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Shortlink Service
|
||||
|
||||
```javascript
|
||||
// src/lib/shortlinkService.js
|
||||
export const shortlinkService = {
|
||||
async getByPath(path) {
|
||||
const result = await databases.listDocuments(
|
||||
DATABASE_ID,
|
||||
'shortlinks',
|
||||
[
|
||||
Query.equal('customPath', path.toLowerCase()),
|
||||
Query.equal('isActive', true)
|
||||
]
|
||||
);
|
||||
return result.documents[0] || null;
|
||||
},
|
||||
|
||||
async incrementClicks(id, currentCount) {
|
||||
return await databases.updateDocument(
|
||||
DATABASE_ID,
|
||||
'shortlinks',
|
||||
id,
|
||||
{ clickCount: currentCount + 1 }
|
||||
);
|
||||
},
|
||||
|
||||
async recordAnalytics(shortlinkId, data) {
|
||||
return await databases.createDocument(
|
||||
DATABASE_ID,
|
||||
'shortlink_analytics',
|
||||
ID.unique(),
|
||||
{
|
||||
shortlinkId,
|
||||
timestamp: new Date().toISOString(),
|
||||
...data
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Image Upload Helper
|
||||
|
||||
```javascript
|
||||
// src/lib/imageService.js
|
||||
export const imageService = {
|
||||
async upload(bucketId, file) {
|
||||
const uploaded = await storage.createFile(
|
||||
bucketId,
|
||||
ID.unique(),
|
||||
file
|
||||
);
|
||||
|
||||
const url = storage.getFileView(bucketId, uploaded.$id);
|
||||
|
||||
return {
|
||||
id: uploaded.$id,
|
||||
url: url.href
|
||||
};
|
||||
},
|
||||
|
||||
async delete(bucketId, fileId) {
|
||||
await storage.deleteFile(bucketId, fileId);
|
||||
},
|
||||
|
||||
getPreviewUrl(bucketId, fileId, width = 400, height = 300) {
|
||||
return storage.getFilePreview(
|
||||
bucketId,
|
||||
fileId,
|
||||
width,
|
||||
height
|
||||
).href;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```javascript
|
||||
try {
|
||||
const result = await databases.listDocuments(...);
|
||||
} catch (error) {
|
||||
if (error.code === 404) {
|
||||
console.log('Collection not found');
|
||||
} else if (error.code === 401) {
|
||||
console.log('Not authorized');
|
||||
} else if (error.code === 403) {
|
||||
console.log('Permission denied');
|
||||
} else {
|
||||
console.log('Error:', error.message);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Common Error Codes
|
||||
|
||||
| Code | Meaning | Solution |
|
||||
|------|---------|----------|
|
||||
| 401 | Unauthorized | Check authentication |
|
||||
| 403 | Forbidden | Check permissions |
|
||||
| 404 | Not Found | Check collection/document ID |
|
||||
| 409 | Conflict | Duplicate unique value |
|
||||
| 413 | Payload Too Large | Reduce file/data size |
|
||||
| 429 | Rate Limited | Slow down requests |
|
||||
310
developer/architecture.mdx
Normal file
310
developer/architecture.mdx
Normal file
@ -0,0 +1,310 @@
|
||||
---
|
||||
title: "Architecture"
|
||||
description: "Technical architecture and project structure overview"
|
||||
---
|
||||
|
||||
# Project Architecture
|
||||
|
||||
Understanding the technical structure of the portfolio website.
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Layer | Technology | Purpose |
|
||||
|-------|------------|---------|
|
||||
| Frontend | React 18 | UI components and state management |
|
||||
| Build Tool | Vite 5 | Fast development and optimized builds |
|
||||
| Styling | Tailwind CSS 3 | Utility-first CSS framework |
|
||||
| Backend | Appwrite | Database, storage, authentication |
|
||||
| Icons | Lucide React | Consistent icon library |
|
||||
| Deployment | Vercel | Hosting and CI/CD |
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
portfolio/
|
||||
├── public/ # Static assets
|
||||
├── src/
|
||||
│ ├── components/ # Reusable UI components
|
||||
│ │ ├── Header/
|
||||
│ │ ├── Footer/
|
||||
│ │ ├── FontSelector/
|
||||
│ │ ├── RichTextEditor/
|
||||
│ │ ├── PhotoGrid/
|
||||
│ │ └── ...
|
||||
│ ├── pages/
|
||||
│ │ ├── Admin/ # Admin dashboard pages
|
||||
│ │ │ ├── BlogManagement/
|
||||
│ │ │ ├── ProjectManagement/
|
||||
│ │ │ ├── ShortlinkManagement/
|
||||
│ │ │ ├── ShopManagement/
|
||||
│ │ │ └── Settings/
|
||||
│ │ ├── HomePage.jsx
|
||||
│ │ ├── BlogPage.jsx
|
||||
│ │ ├── ProjectPage.jsx
|
||||
│ │ ├── ShopPage.jsx
|
||||
│ │ └── ...
|
||||
│ ├── lib/ # Services and utilities
|
||||
│ │ ├── appwrite.js # Appwrite client config
|
||||
│ │ ├── shortlinkService.js
|
||||
│ │ ├── analyticsService.js
|
||||
│ │ └── ...
|
||||
│ ├── utils/ # Helper functions
|
||||
│ │ ├── googleFonts.js
|
||||
│ │ ├── fontLoader.js
|
||||
│ │ └── ...
|
||||
│ ├── context/ # React context providers
|
||||
│ │ ├── AuthContext.jsx
|
||||
│ │ ├── CartContext.jsx
|
||||
│ │ └── ...
|
||||
│ ├── App.jsx # Main app component
|
||||
│ ├── main.jsx # Entry point
|
||||
│ └── index.css # Global styles
|
||||
├── docs/ # Documentation (separate repo)
|
||||
├── .env # Environment variables
|
||||
├── vite.config.js # Vite configuration
|
||||
├── tailwind.config.js # Tailwind configuration
|
||||
└── package.json
|
||||
```
|
||||
|
||||
## Data Flow
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||
│ Browser │────▶│ React │────▶│ Appwrite │
|
||||
│ (User) │◀────│ App │◀────│ Backend │
|
||||
└─────────────┘ └─────────────┘ └─────────────┘
|
||||
│
|
||||
┌──────┴──────┐
|
||||
│ │
|
||||
┌─────▼─────┐ ┌─────▼─────┐
|
||||
│ Components│ │ Context │
|
||||
│ (UI) │ │ (State) │
|
||||
└───────────┘ └───────────┘
|
||||
```
|
||||
|
||||
## Key Components
|
||||
|
||||
### Appwrite Client (`src/lib/appwrite.js`)
|
||||
|
||||
Central configuration for Appwrite SDK:
|
||||
|
||||
```javascript
|
||||
import { Client, Databases, Storage, Account } from 'appwrite';
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint(import.meta.env.VITE_APPWRITE_ENDPOINT)
|
||||
.setProject(import.meta.env.VITE_APPWRITE_PROJECT_ID);
|
||||
|
||||
export const databases = new Databases(client);
|
||||
export const storage = new Storage(client);
|
||||
export const account = new Account(client);
|
||||
```
|
||||
|
||||
### Database Service Pattern
|
||||
|
||||
Each feature has a dedicated service file:
|
||||
|
||||
```javascript
|
||||
// src/lib/blogService.js
|
||||
import { databases, ID, Query } from './appwrite';
|
||||
|
||||
const DATABASE_ID = import.meta.env.VITE_APPWRITE_DATABASE_ID;
|
||||
const COLLECTION_ID = 'blogs';
|
||||
|
||||
export const blogService = {
|
||||
async list(queries = []) {
|
||||
return await databases.listDocuments(
|
||||
DATABASE_ID,
|
||||
COLLECTION_ID,
|
||||
queries
|
||||
);
|
||||
},
|
||||
|
||||
async get(id) {
|
||||
return await databases.getDocument(
|
||||
DATABASE_ID,
|
||||
COLLECTION_ID,
|
||||
id
|
||||
);
|
||||
},
|
||||
|
||||
async create(data) {
|
||||
return await databases.createDocument(
|
||||
DATABASE_ID,
|
||||
COLLECTION_ID,
|
||||
ID.unique(),
|
||||
data
|
||||
);
|
||||
},
|
||||
|
||||
async update(id, data) {
|
||||
return await databases.updateDocument(
|
||||
DATABASE_ID,
|
||||
COLLECTION_ID,
|
||||
id,
|
||||
data
|
||||
);
|
||||
},
|
||||
|
||||
async delete(id) {
|
||||
return await databases.deleteDocument(
|
||||
DATABASE_ID,
|
||||
COLLECTION_ID,
|
||||
id
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Context Providers
|
||||
|
||||
Global state management using React Context:
|
||||
|
||||
```javascript
|
||||
// src/context/AuthContext.jsx
|
||||
import { createContext, useContext, useState, useEffect } from 'react';
|
||||
import { account } from '../lib/appwrite';
|
||||
|
||||
const AuthContext = createContext();
|
||||
|
||||
export function AuthProvider({ children }) {
|
||||
const [user, setUser] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
checkUser();
|
||||
}, []);
|
||||
|
||||
async function checkUser() {
|
||||
try {
|
||||
const session = await account.get();
|
||||
setUser(session);
|
||||
} catch {
|
||||
setUser(null);
|
||||
}
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={{ user, loading, checkUser }}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const useAuth = () => useContext(AuthContext);
|
||||
```
|
||||
|
||||
## Routing Structure
|
||||
|
||||
```javascript
|
||||
// src/App.jsx
|
||||
<Routes>
|
||||
{/* Public Routes */}
|
||||
<Route path="/" element={<HomePage />} />
|
||||
<Route path="/blogs" element={<BlogsPage />} />
|
||||
<Route path="/blog/:slug" element={<BlogPage />} />
|
||||
<Route path="/projects" element={<ProjectsPage />} />
|
||||
<Route path="/project/:slug" element={<ProjectPage />} />
|
||||
<Route path="/shop" element={<ShopPage />} />
|
||||
|
||||
{/* Shortlink Redirect */}
|
||||
<Route path="/:path" element={<ShortlinkRedirect />} />
|
||||
|
||||
{/* Admin Routes (Protected) */}
|
||||
<Route path="/admin" element={<AdminLayout />}>
|
||||
<Route index element={<Dashboard />} />
|
||||
<Route path="blogs" element={<BlogManagement />} />
|
||||
<Route path="projects" element={<ProjectManagement />} />
|
||||
<Route path="shortlinks" element={<ShortlinkManagement />} />
|
||||
{/* ... more admin routes */}
|
||||
</Route>
|
||||
</Routes>
|
||||
```
|
||||
|
||||
## State Management
|
||||
|
||||
| State Type | Solution | Use Case |
|
||||
|------------|----------|----------|
|
||||
| Server State | Direct Appwrite calls | Database operations |
|
||||
| Auth State | AuthContext | User session |
|
||||
| Cart State | CartContext + localStorage | Shopping cart |
|
||||
| UI State | useState/useReducer | Component-level state |
|
||||
|
||||
## Performance Optimizations
|
||||
|
||||
### Code Splitting
|
||||
|
||||
```javascript
|
||||
// Lazy load admin pages
|
||||
const BlogManagement = lazy(() => import('./pages/Admin/BlogManagement'));
|
||||
const ProjectManagement = lazy(() => import('./pages/Admin/ProjectManagement'));
|
||||
```
|
||||
|
||||
### Image Optimization
|
||||
|
||||
- Lazy loading for images below the fold
|
||||
- WebP format support
|
||||
- Responsive image sizes
|
||||
|
||||
### Font Loading
|
||||
|
||||
```javascript
|
||||
// Dynamic Google Font loading
|
||||
const loadGoogleFont = (fontFamily) => {
|
||||
const link = document.createElement('link');
|
||||
link.href = `https://fonts.googleapis.com/css2?family=${fontFamily}&display=swap`;
|
||||
link.rel = 'stylesheet';
|
||||
document.head.appendChild(link);
|
||||
};
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
| Concern | Solution |
|
||||
|---------|----------|
|
||||
| Authentication | Appwrite session management |
|
||||
| Authorization | Collection-level permissions |
|
||||
| XSS Prevention | DOMPurify for rich text |
|
||||
| CORS | Appwrite platform configuration |
|
||||
| Secrets | Environment variables (never in code) |
|
||||
|
||||
## Build Configuration
|
||||
|
||||
### Vite Config (`vite.config.js`)
|
||||
|
||||
```javascript
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
build: {
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: {
|
||||
vendor: ['react', 'react-dom', 'react-router-dom'],
|
||||
appwrite: ['appwrite'],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Tailwind Config (`tailwind.config.js`)
|
||||
|
||||
```javascript
|
||||
export default {
|
||||
content: ['./index.html', './src/**/*.{js,jsx}'],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: '#1E8479',
|
||||
secondary: '#105652',
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: []
|
||||
};
|
||||
```
|
||||
389
developer/customization.mdx
Normal file
389
developer/customization.mdx
Normal file
@ -0,0 +1,389 @@
|
||||
---
|
||||
title: "Customization"
|
||||
description: "How to customize and extend the portfolio website"
|
||||
---
|
||||
|
||||
# Customization Guide
|
||||
|
||||
Learn how to customize colors, fonts, components, and add new features.
|
||||
|
||||
## Theme Customization
|
||||
|
||||
### Colors
|
||||
|
||||
Edit `tailwind.config.js` to change the color scheme:
|
||||
|
||||
```javascript
|
||||
// tailwind.config.js
|
||||
export default {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
DEFAULT: '#1E8479',
|
||||
light: '#07C983',
|
||||
dark: '#105652',
|
||||
},
|
||||
secondary: '#F59E0B',
|
||||
accent: '#8B5CF6',
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Using Custom Colors
|
||||
|
||||
```jsx
|
||||
// In components
|
||||
<button className="bg-primary hover:bg-primary-dark text-white">
|
||||
Click Me
|
||||
</button>
|
||||
|
||||
<div className="text-primary-light border-primary">
|
||||
Content
|
||||
</div>
|
||||
```
|
||||
|
||||
### CSS Variables
|
||||
|
||||
For dynamic theming, use CSS variables in `src/index.css`:
|
||||
|
||||
```css
|
||||
:root {
|
||||
--color-primary: #1E8479;
|
||||
--color-primary-light: #07C983;
|
||||
--color-primary-dark: #105652;
|
||||
--color-background: #ffffff;
|
||||
--color-text: #1f2937;
|
||||
}
|
||||
|
||||
/* Dark mode */
|
||||
.dark {
|
||||
--color-background: #1f2937;
|
||||
--color-text: #f9fafb;
|
||||
}
|
||||
```
|
||||
|
||||
## Adding New Pages
|
||||
|
||||
### Public Page
|
||||
|
||||
<Steps>
|
||||
<Step title="Create Component">
|
||||
Create `src/pages/NewPage.jsx`:
|
||||
|
||||
```jsx
|
||||
export default function NewPage() {
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<h1 className="text-3xl font-bold">New Page</h1>
|
||||
{/* Your content */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add Route">
|
||||
In `src/App.jsx`:
|
||||
|
||||
```jsx
|
||||
import NewPage from './pages/NewPage';
|
||||
|
||||
// In Routes
|
||||
<Route path="/new-page" element={<NewPage />} />
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add to Navigation">
|
||||
Update menu settings in admin or edit `Header.jsx`
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Admin Page
|
||||
|
||||
<Steps>
|
||||
<Step title="Create Component">
|
||||
Create `src/pages/Admin/NewFeature/NewFeature.jsx`:
|
||||
|
||||
```jsx
|
||||
export default function NewFeature() {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1 className="text-2xl font-bold mb-6">New Feature</h1>
|
||||
{/* Admin content */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add Route">
|
||||
In `src/App.jsx` under admin routes:
|
||||
|
||||
```jsx
|
||||
<Route path="new-feature" element={<NewFeature />} />
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add to Sidebar">
|
||||
Edit `src/pages/Admin/AdminLayout.jsx` to add menu item
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Adding New Collections
|
||||
|
||||
### 1. Create in Appwrite
|
||||
|
||||
1. Go to Appwrite Console → Databases → portfolio_db
|
||||
2. Create new collection with attributes
|
||||
3. Set permissions
|
||||
|
||||
### 2. Create Service
|
||||
|
||||
```javascript
|
||||
// src/lib/newFeatureService.js
|
||||
import { databases, ID, Query } from './appwrite';
|
||||
|
||||
const DATABASE_ID = import.meta.env.VITE_APPWRITE_DATABASE_ID;
|
||||
const COLLECTION_ID = 'new_collection';
|
||||
|
||||
export const newFeatureService = {
|
||||
async list() {
|
||||
return await databases.listDocuments(DATABASE_ID, COLLECTION_ID);
|
||||
},
|
||||
|
||||
async get(id) {
|
||||
return await databases.getDocument(DATABASE_ID, COLLECTION_ID, id);
|
||||
},
|
||||
|
||||
async create(data) {
|
||||
return await databases.createDocument(
|
||||
DATABASE_ID,
|
||||
COLLECTION_ID,
|
||||
ID.unique(),
|
||||
data
|
||||
);
|
||||
},
|
||||
|
||||
async update(id, data) {
|
||||
return await databases.updateDocument(
|
||||
DATABASE_ID,
|
||||
COLLECTION_ID,
|
||||
id,
|
||||
data
|
||||
);
|
||||
},
|
||||
|
||||
async delete(id) {
|
||||
return await databases.deleteDocument(DATABASE_ID, COLLECTION_ID, id);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 3. Create Admin UI
|
||||
|
||||
Use existing admin pages as templates. Key patterns:
|
||||
|
||||
```jsx
|
||||
// List view with CRUD
|
||||
const [items, setItems] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
loadItems();
|
||||
}, []);
|
||||
|
||||
async function loadItems() {
|
||||
setLoading(true);
|
||||
const result = await newFeatureService.list();
|
||||
setItems(result.documents);
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
async function handleDelete(id) {
|
||||
if (confirm('Delete this item?')) {
|
||||
await newFeatureService.delete(id);
|
||||
loadItems();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Adding Font Selector to New Pages
|
||||
|
||||
```jsx
|
||||
import FontSelector from '../../../components/FontSelector/FontSelector';
|
||||
|
||||
const [titleFont, setTitleFont] = useState("'Open Sans', sans-serif");
|
||||
|
||||
// In your form
|
||||
<FontSelector
|
||||
value={titleFont}
|
||||
onChange={setTitleFont}
|
||||
label="Title Font"
|
||||
previewText="Preview Text Here"
|
||||
/>
|
||||
```
|
||||
|
||||
## Customizing Rich Text Editor
|
||||
|
||||
### Add New Toolbar Button
|
||||
|
||||
Edit `src/components/RichTextEditor/RichTextEditor.jsx`:
|
||||
|
||||
```jsx
|
||||
// Add to toolbar
|
||||
<button
|
||||
onClick={() => handleCustomAction()}
|
||||
className="p-2 hover:bg-gray-100 rounded"
|
||||
title="Custom Action"
|
||||
>
|
||||
<CustomIcon className="w-4 h-4" />
|
||||
</button>
|
||||
|
||||
// Handler function
|
||||
const handleCustomAction = () => {
|
||||
document.execCommand('customCommand', false, 'value');
|
||||
// Or manipulate selection directly
|
||||
};
|
||||
```
|
||||
|
||||
### Add New Embed Type
|
||||
|
||||
```jsx
|
||||
// In embed handler
|
||||
const supportedEmbeds = {
|
||||
youtube: /youtube\.com|youtu\.be/,
|
||||
vimeo: /vimeo\.com/,
|
||||
// Add new type
|
||||
spotify: /spotify\.com/,
|
||||
};
|
||||
```
|
||||
|
||||
## Custom Components
|
||||
|
||||
### Reusable Card Component
|
||||
|
||||
```jsx
|
||||
// src/components/Card/Card.jsx
|
||||
export default function Card({
|
||||
title,
|
||||
description,
|
||||
image,
|
||||
onClick,
|
||||
className = ''
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={`bg-white rounded-lg shadow-md overflow-hidden
|
||||
hover:shadow-lg transition-shadow cursor-pointer ${className}`}
|
||||
onClick={onClick}
|
||||
>
|
||||
{image && (
|
||||
<img src={image} alt={title} className="w-full h-48 object-cover" />
|
||||
)}
|
||||
<div className="p-4">
|
||||
<h3 className="font-bold text-lg mb-2">{title}</h3>
|
||||
<p className="text-gray-600 text-sm">{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Loading Spinner
|
||||
|
||||
```jsx
|
||||
// src/components/Spinner/Spinner.jsx
|
||||
export default function Spinner({ size = 'md' }) {
|
||||
const sizes = {
|
||||
sm: 'w-4 h-4',
|
||||
md: 'w-8 h-8',
|
||||
lg: 'w-12 h-12'
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`${sizes[size]} animate-spin rounded-full
|
||||
border-2 border-gray-300 border-t-primary`} />
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Environment-Specific Config
|
||||
|
||||
```javascript
|
||||
// src/config.js
|
||||
const config = {
|
||||
development: {
|
||||
apiUrl: 'http://localhost:5173',
|
||||
debug: true,
|
||||
},
|
||||
production: {
|
||||
apiUrl: 'https://yourdomain.com',
|
||||
debug: false,
|
||||
}
|
||||
};
|
||||
|
||||
export default config[import.meta.env.MODE] || config.development;
|
||||
```
|
||||
|
||||
## Adding Analytics
|
||||
|
||||
### Google Analytics
|
||||
|
||||
```html
|
||||
<!-- index.html -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=GA_ID"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', 'GA_ID');
|
||||
</script>
|
||||
```
|
||||
|
||||
### Track Page Views
|
||||
|
||||
```jsx
|
||||
// src/App.jsx
|
||||
import { useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
function App() {
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
// Track page view
|
||||
if (window.gtag) {
|
||||
window.gtag('event', 'page_view', {
|
||||
page_path: location.pathname,
|
||||
});
|
||||
}
|
||||
}, [location]);
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
<Tip>
|
||||
Keep components small and focused. If a component exceeds 200 lines, consider splitting it.
|
||||
</Tip>
|
||||
|
||||
<Tip>
|
||||
Use TypeScript for better type safety and IDE support in larger projects.
|
||||
</Tip>
|
||||
|
||||
<Tip>
|
||||
Create a `constants.js` file for magic strings and configuration values.
|
||||
</Tip>
|
||||
|
||||
<Warning>
|
||||
Never hardcode API keys or secrets. Always use environment variables.
|
||||
</Warning>
|
||||
|
||||
<Warning>
|
||||
Test thoroughly after customizations, especially database schema changes.
|
||||
</Warning>
|
||||
240
features/photo-grids.mdx
Normal file
240
features/photo-grids.mdx
Normal file
@ -0,0 +1,240 @@
|
||||
---
|
||||
title: "Photo Grids"
|
||||
description: "Create beautiful image layouts with 24 pre-designed templates"
|
||||
---
|
||||
|
||||
# Photo Grids
|
||||
|
||||
Insert stunning image galleries with professional layouts directly in your content.
|
||||
|
||||
## Overview
|
||||
|
||||
Photo grids allow you to display multiple images in visually appealing arrangements. Choose from 24 pre-designed layouts to showcase your work.
|
||||
|
||||
## Inserting a Photo Grid
|
||||
|
||||
<Steps>
|
||||
<Step title="Open Editor">
|
||||
Go to any content editor (Blog, Project, Product)
|
||||
</Step>
|
||||
<Step title="Click Grid Button">
|
||||
Click the **Grid** icon (Grid3x3) in the toolbar
|
||||
</Step>
|
||||
<Step title="Choose Layout">
|
||||
Browse 24 layout options and click your preferred one
|
||||
</Step>
|
||||
<Step title="Add Images">
|
||||
For each cell:
|
||||
1. Click the empty cell
|
||||
2. Choose **URL** or **Upload**
|
||||
3. Add your image
|
||||
4. Click **Add Image**
|
||||
</Step>
|
||||
<Step title="Insert">
|
||||
Click **Insert Grid** when all cells are filled
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Available Layouts
|
||||
|
||||
### Simple Grids (1-5)
|
||||
|
||||
| Layout | Description | Images |
|
||||
|--------|-------------|--------|
|
||||
| 2 Columns | Side by side | 2 |
|
||||
| 3 Columns | Three in a row | 3 |
|
||||
| 4 Columns | Four in a row | 4 |
|
||||
| 2×2 Grid | Square grid | 4 |
|
||||
| 3×2 Grid | Two rows of three | 6 |
|
||||
|
||||
**Best for:** Simple galleries, before/after comparisons, product showcases
|
||||
|
||||
### Asymmetric Layouts (6-10)
|
||||
|
||||
| Layout | Description | Images |
|
||||
|--------|-------------|--------|
|
||||
| Large Left + 2 Right | Featured image on left | 3 |
|
||||
| Large Right + 2 Left | Featured image on right | 3 |
|
||||
| 2 Top + 3 Bottom | Wide top, small bottom | 5 |
|
||||
| 3 Top + 2 Bottom | Small top, wide bottom | 5 |
|
||||
| Large Center + 4 Corners | Center focus | 5 |
|
||||
|
||||
**Best for:** Highlighting a main image with supporting images
|
||||
|
||||
### Creative Layouts (11-15)
|
||||
|
||||
| Layout | Description | Images |
|
||||
|--------|-------------|--------|
|
||||
| Masonry 1 | Tall images on sides | 4-6 |
|
||||
| Masonry 2 | Alternating heights | 4-6 |
|
||||
| Featured + 3 | One large, three small | 4 |
|
||||
| Split + 4 | Two large top, 4 small bottom | 6 |
|
||||
| Showcase | Alternating sizes | 5 |
|
||||
|
||||
**Best for:** Portfolio showcases, creative presentations
|
||||
|
||||
### Gallery Layouts (16-20)
|
||||
|
||||
| Layout | Description | Images |
|
||||
|--------|-------------|--------|
|
||||
| Gallery 1 | Tall left, wide right | 4 |
|
||||
| Gallery 2 | Large square with small images | 5 |
|
||||
| Portfolio 1 | Wide top, tall right | 4 |
|
||||
| Portfolio 2 | Mixed sizes | 5 |
|
||||
| Magazine | Editorial style | 5 |
|
||||
|
||||
**Best for:** Professional portfolios, editorial content
|
||||
|
||||
### Special Layouts (21-24)
|
||||
|
||||
| Layout | Description | Images |
|
||||
|--------|-------------|--------|
|
||||
| Hero + Grid | Hero image with thumbnails | 5 |
|
||||
| Spotlight | Wide feature with small images | 4 |
|
||||
| Collage 1 | Artistic arrangement | 5 |
|
||||
| Collage 2 | Creative mixed layout | 6 |
|
||||
|
||||
**Best for:** Hero sections, artistic presentations
|
||||
|
||||
## Editing Photo Grids
|
||||
|
||||
### Change Images
|
||||
|
||||
1. Hover over the grid in the editor
|
||||
2. Click **Settings** icon (gear)
|
||||
3. Click on the image you want to change
|
||||
4. Upload or enter new image URL
|
||||
5. Click **Update Grid**
|
||||
|
||||
### Change Layout
|
||||
|
||||
1. Click **Settings** on the grid
|
||||
2. Click **Change Layout**
|
||||
3. Select new layout
|
||||
4. Rearrange images if needed
|
||||
5. Click **Update Grid**
|
||||
|
||||
### Delete Grid
|
||||
|
||||
1. Hover over the grid
|
||||
2. Click **Delete** icon (trash)
|
||||
3. Confirm deletion
|
||||
|
||||
## Image Recommendations
|
||||
|
||||
### Optimal Sizes
|
||||
|
||||
| Use Case | Recommended Size |
|
||||
|----------|------------------|
|
||||
| Thumbnail cells | 400×400px |
|
||||
| Wide cells | 800×400px |
|
||||
| Tall cells | 400×800px |
|
||||
| Hero/Large cells | 1200×800px |
|
||||
|
||||
### File Formats
|
||||
|
||||
| Format | Best For |
|
||||
|--------|----------|
|
||||
| JPG | Photos, complex images |
|
||||
| PNG | Graphics, transparency |
|
||||
| WebP | Best compression (modern browsers) |
|
||||
|
||||
### Tips for Best Results
|
||||
|
||||
<Tip>
|
||||
Use images with similar color tones for a cohesive look.
|
||||
</Tip>
|
||||
|
||||
<Tip>
|
||||
Keep file sizes under 500KB per image for faster loading.
|
||||
</Tip>
|
||||
|
||||
<Tip>
|
||||
Use consistent aspect ratios within the same grid when possible.
|
||||
</Tip>
|
||||
|
||||
<Warning>
|
||||
Very large images (>2MB) will slow down page loading. Compress before uploading.
|
||||
</Warning>
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Portfolio Showcase
|
||||
|
||||
Use **Gallery** or **Portfolio** layouts to display your best work with a featured piece.
|
||||
|
||||
```
|
||||
┌─────────────────┬─────────┐
|
||||
│ │ 2 │
|
||||
│ 1 ├─────────┤
|
||||
│ (Featured) │ 3 │
|
||||
│ ├─────────┤
|
||||
│ │ 4 │
|
||||
└─────────────────┴─────────┘
|
||||
```
|
||||
|
||||
### Product Gallery
|
||||
|
||||
Use **2×2 Grid** or **3 Columns** for product photos from different angles.
|
||||
|
||||
```
|
||||
┌─────────┬─────────┬─────────┐
|
||||
│ 1 │ 2 │ 3 │
|
||||
└─────────┴─────────┴─────────┘
|
||||
```
|
||||
|
||||
### Before/After
|
||||
|
||||
Use **2 Columns** for side-by-side comparisons.
|
||||
|
||||
```
|
||||
┌─────────────┬─────────────┐
|
||||
│ Before │ After │
|
||||
└─────────────┴─────────────┘
|
||||
```
|
||||
|
||||
### Photo Essay
|
||||
|
||||
Use **Magazine** or **Collage** layouts for storytelling.
|
||||
|
||||
```
|
||||
┌─────────────────────────────┐
|
||||
│ Hero │
|
||||
├─────────┬─────────┬─────────┤
|
||||
│ 2 │ 3 │ 4 │
|
||||
└─────────┴─────────┴─────────┘
|
||||
```
|
||||
|
||||
## Responsive Behavior
|
||||
|
||||
All photo grids are automatically responsive:
|
||||
|
||||
| Screen Size | Behavior |
|
||||
|-------------|----------|
|
||||
| Desktop | Full grid layout |
|
||||
| Tablet | Adjusted spacing, some stacking |
|
||||
| Mobile | Stacked or simplified layout |
|
||||
|
||||
<Note>
|
||||
Complex layouts may simplify on mobile devices for better viewing experience.
|
||||
</Note>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Grid Not Appearing
|
||||
|
||||
1. Ensure you clicked **Insert Grid** after adding images
|
||||
2. Check that all cells have images
|
||||
3. Refresh the editor
|
||||
|
||||
### Images Not Loading
|
||||
|
||||
1. Verify image URLs are correct and accessible
|
||||
2. Check file upload completed successfully
|
||||
3. Ensure images are publicly accessible
|
||||
|
||||
### Layout Looks Wrong
|
||||
|
||||
1. Check image aspect ratios match the cell shapes
|
||||
2. Try a different layout better suited to your images
|
||||
3. Ensure all cells are filled
|
||||
126
introduction.mdx
126
introduction.mdx
@ -5,55 +5,121 @@ description: "A modern portfolio website built with React, Vite, Tailwind CSS, a
|
||||
|
||||
# Portfolio Website
|
||||
|
||||
A comprehensive, feature-rich portfolio website with an admin dashboard for complete content management.
|
||||
A comprehensive, feature-rich portfolio website with an admin dashboard for complete content management. Built for developers, designers, and creators who want full control over their online presence.
|
||||
|
||||
## Why This Portfolio?
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="No Coding Required" icon="wand-magic-sparkles">
|
||||
Manage all content through an intuitive admin panel
|
||||
</Card>
|
||||
<Card title="Fully Customizable" icon="palette">
|
||||
Change fonts, colors, and layouts without touching code
|
||||
</Card>
|
||||
<Card title="Production Ready" icon="rocket">
|
||||
Optimized for performance, SEO, and accessibility
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Technology | Purpose |
|
||||
|------------|---------|
|
||||
| React 18 | Frontend framework |
|
||||
| Vite 5 | Build tool & dev server |
|
||||
| Tailwind CSS 3 | Styling |
|
||||
| Appwrite | Backend, database & storage |
|
||||
| Lucide React | Icons |
|
||||
| Technology | Purpose | Why Chosen |
|
||||
|------------|---------|------------|
|
||||
| React 18 | Frontend framework | Component-based, large ecosystem |
|
||||
| Vite 5 | Build tool & dev server | Lightning fast HMR, optimized builds |
|
||||
| Tailwind CSS 3 | Styling | Utility-first, highly customizable |
|
||||
| Appwrite | Backend, database & storage | Open-source, self-hostable, generous free tier |
|
||||
| Lucide React | Icons | Consistent, lightweight, 1000+ icons |
|
||||
|
||||
## Key Features
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Blog System" icon="newspaper">
|
||||
Full-featured blog with categories, multiple authors, and rich text editing
|
||||
<Card title="Blog System" icon="newspaper" href="/features/blog-system">
|
||||
Full-featured blog with categories, multiple authors, rich text editing, and SEO-friendly URLs
|
||||
</Card>
|
||||
<Card title="Project Gallery" icon="folder">
|
||||
Showcase projects with custom URLs, galleries, and detailed descriptions
|
||||
<Card title="Project Gallery" icon="folder" href="/features/project-gallery">
|
||||
Showcase projects with custom URLs, image galleries, and detailed descriptions
|
||||
</Card>
|
||||
<Card title="Shortlink System" icon="link">
|
||||
URL shortener with analytics, custom domains, and preview images
|
||||
<Card title="Shortlink System" icon="link" href="/features/shortlink-system">
|
||||
URL shortener with click analytics, geographic tracking, and custom domain support
|
||||
</Card>
|
||||
<Card title="Shop & Orders" icon="shopping-cart">
|
||||
E-commerce with PayPal integration, order tracking, and secure file delivery
|
||||
<Card title="Shop & Orders" icon="shopping-cart" href="/features/shop-system">
|
||||
E-commerce with PayPal integration, order tracking, coupons, and secure file delivery
|
||||
</Card>
|
||||
<Card title="Font Management" icon="font">
|
||||
70+ Google Fonts plus custom font uploads with Bangla support
|
||||
<Card title="Font Management" icon="font" href="/features/font-management">
|
||||
70+ Google Fonts plus custom font uploads with full Bangla language support
|
||||
</Card>
|
||||
<Card title="Rich Text Editor" icon="pen">
|
||||
Advanced editor with embeds, photo grids, tables, and full formatting
|
||||
<Card title="Rich Text Editor" icon="pen" href="/features/rich-text-editor">
|
||||
Advanced editor with embeds, photo grids, tables, and complete formatting options
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## What's Included
|
||||
|
||||
### Public Pages
|
||||
- **Homepage** - Hero section, featured content, about preview
|
||||
- **Blog** - List view, single post, category filtering
|
||||
- **Projects** - Gallery view, detailed project pages
|
||||
- **Shop** - Product catalog, cart, checkout
|
||||
- **Contact** - Contact form with validation
|
||||
|
||||
### Admin Dashboard
|
||||
- **Content Management** - Header, about, experience, FAQ
|
||||
- **Blog Management** - Posts, categories, authors
|
||||
- **Project Management** - Projects, categories, galleries
|
||||
- **Shop Management** - Products, orders, coupons
|
||||
- **Shortlinks** - URL shortener, analytics, custom domains
|
||||
- **Settings** - Fonts, menu, social links
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
src/
|
||||
├── components/ # Reusable UI components
|
||||
├── pages/
|
||||
│ ├── Admin/ # Admin dashboard pages
|
||||
│ └── ... # Public pages
|
||||
├── lib/ # Appwrite services & utilities
|
||||
└── utils/ # Helper functions
|
||||
portfolio/
|
||||
├── src/
|
||||
│ ├── components/ # Reusable UI components
|
||||
│ │ ├── Header/
|
||||
│ │ ├── Footer/
|
||||
│ │ ├── RichTextEditor/
|
||||
│ │ └── ...
|
||||
│ ├── pages/
|
||||
│ │ ├── Admin/ # Admin dashboard (protected)
|
||||
│ │ ├── HomePage.jsx
|
||||
│ │ ├── BlogPage.jsx
|
||||
│ │ └── ...
|
||||
│ ├── lib/ # Appwrite services
|
||||
│ ├── utils/ # Helper functions
|
||||
│ └── context/ # React context providers
|
||||
├── public/ # Static assets
|
||||
├── docs/ # Documentation
|
||||
└── .env # Environment variables
|
||||
```
|
||||
|
||||
## Quick Links
|
||||
|
||||
- [Quick Start Guide](/quick-start) - Get up and running in 10 minutes
|
||||
- [Appwrite Setup](/appwrite/overview) - Configure your backend
|
||||
- [Deployment Guide](/deployment) - Deploy to Vercel
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Quick Start" icon="play" href="/quick-start">
|
||||
Get up and running in 10 minutes
|
||||
</Card>
|
||||
<Card title="Appwrite Setup" icon="database" href="/appwrite/overview">
|
||||
Configure your backend services
|
||||
</Card>
|
||||
<Card title="Deployment" icon="cloud-arrow-up" href="/deployment">
|
||||
Deploy to Vercel or other platforms
|
||||
</Card>
|
||||
<Card title="Troubleshooting" icon="wrench" href="/troubleshooting">
|
||||
Common issues and solutions
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Requirements
|
||||
|
||||
Before you begin, ensure you have:
|
||||
|
||||
- **Node.js 18+** - [Download](https://nodejs.org/)
|
||||
- **npm or yarn** - Comes with Node.js
|
||||
- **Appwrite Account** - [Sign up free](https://cloud.appwrite.io)
|
||||
- **Git** - [Download](https://git-scm.com/)
|
||||
|
||||
<Note>
|
||||
Appwrite Cloud offers a generous free tier that's sufficient for most portfolio sites. You can also self-host Appwrite if preferred.
|
||||
</Note>
|
||||
|
||||
13
mint.json
13
mint.json
@ -29,7 +29,7 @@
|
||||
"navigation": [
|
||||
{
|
||||
"group": "Getting Started",
|
||||
"pages": ["introduction", "quick-start", "deployment"]
|
||||
"pages": ["introduction", "quick-start", "deployment", "troubleshooting"]
|
||||
},
|
||||
{
|
||||
"group": "Appwrite Setup",
|
||||
@ -48,7 +48,8 @@
|
||||
"features/shortlink-system",
|
||||
"features/shop-system",
|
||||
"features/font-management",
|
||||
"features/rich-text-editor"
|
||||
"features/rich-text-editor",
|
||||
"features/photo-grids"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -58,6 +59,14 @@
|
||||
"admin/content-management",
|
||||
"admin/settings"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Developer Guide",
|
||||
"pages": [
|
||||
"developer/architecture",
|
||||
"developer/api-reference",
|
||||
"developer/customization"
|
||||
]
|
||||
}
|
||||
],
|
||||
"footerSocials": {
|
||||
|
||||
179
quick-start.mdx
179
quick-start.mdx
@ -5,35 +5,72 @@ description: "Get your portfolio website running in 10 minutes"
|
||||
|
||||
# Quick Start Guide
|
||||
|
||||
Get your portfolio website up and running quickly with this step-by-step guide.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Node.js 18+ installed
|
||||
- Appwrite account ([cloud.appwrite.io](https://cloud.appwrite.io))
|
||||
- Git installed
|
||||
Before starting, make sure you have:
|
||||
|
||||
| Requirement | Version | Check Command |
|
||||
|-------------|---------|---------------|
|
||||
| Node.js | 18+ | `node --version` |
|
||||
| npm | 9+ | `npm --version` |
|
||||
| Git | Any | `git --version` |
|
||||
|
||||
You'll also need an [Appwrite Cloud](https://cloud.appwrite.io) account (free).
|
||||
|
||||
## Installation
|
||||
|
||||
<Steps>
|
||||
<Step title="Clone & Install">
|
||||
<Step title="Clone the Repository">
|
||||
```bash
|
||||
git clone <your-repo-url>
|
||||
cd portfolio
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Install Dependencies">
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
This installs all required packages including React, Vite, Tailwind, and Appwrite SDK.
|
||||
</Step>
|
||||
|
||||
<Step title="Create Appwrite Project">
|
||||
1. Go to [cloud.appwrite.io](https://cloud.appwrite.io)
|
||||
2. Click **Create Project**
|
||||
3. Name it (e.g., "Portfolio")
|
||||
4. Copy your **Project ID**
|
||||
</Step>
|
||||
|
||||
<Step title="Configure Environment">
|
||||
Create a `.env` file in the root directory:
|
||||
|
||||
```env
|
||||
# Appwrite Configuration
|
||||
VITE_APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
|
||||
VITE_APPWRITE_PROJECT_ID=your_project_id
|
||||
VITE_APPWRITE_PROJECT_ID=your_project_id_here
|
||||
VITE_APPWRITE_DATABASE_ID=portfolio_db
|
||||
VITE_APPWRITE_STORAGE_ID=reactbucket
|
||||
|
||||
# Optional: PayPal (for shop)
|
||||
VITE_PAYPAL_CLIENT_ID=your_paypal_client_id
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Never commit `.env` to version control. It's already in `.gitignore`.
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="Add Platform in Appwrite">
|
||||
1. In Appwrite Console, go to **Settings** → **Platforms**
|
||||
2. Click **Add Platform** → **Web**
|
||||
3. Enter hostname: `localhost`
|
||||
4. Click **Create**
|
||||
|
||||
<Note>
|
||||
Get these values from your Appwrite Console after creating a project.
|
||||
This allows your local development server to communicate with Appwrite.
|
||||
</Note>
|
||||
</Step>
|
||||
|
||||
@ -43,47 +80,133 @@ description: "Get your portfolio website running in 10 minutes"
|
||||
```
|
||||
|
||||
Open [http://localhost:5173](http://localhost:5173) in your browser.
|
||||
|
||||
You should see the portfolio homepage (with placeholder content until you set up the database).
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## First-Time Setup
|
||||
## Database Setup
|
||||
|
||||
### 1. Create Admin Account
|
||||
Your site needs database collections to store content. You have two options:
|
||||
|
||||
1. Go to `/admin` in your browser
|
||||
2. Sign up with your email
|
||||
3. Verify your email in Appwrite Console
|
||||
### Option A: Quick Setup (Recommended for Testing)
|
||||
|
||||
### 2. Set Up Database
|
||||
Create the minimum required collections:
|
||||
|
||||
Follow the [Appwrite Setup Guide](/appwrite/overview) to create:
|
||||
- Required collections (blogs, projects, products, etc.)
|
||||
- Storage buckets for images
|
||||
- Proper permissions
|
||||
1. Go to Appwrite Console → **Databases**
|
||||
2. Click **Create Database** → ID: `portfolio_db`
|
||||
3. Create these collections with basic attributes:
|
||||
|
||||
### 3. Add Your First Content
|
||||
| Collection | Required Attributes |
|
||||
|------------|---------------------|
|
||||
| `header_section` | `name` (string), `roles` (string[]), `description` (string) |
|
||||
| `menubar_settings` | `showBlog` (boolean), `showProjects` (boolean) |
|
||||
|
||||
| Section | Admin URL | What to Add |
|
||||
|---------|-----------|-------------|
|
||||
| Header | `/admin/header` | Name, roles, description |
|
||||
| About | `/admin/about` | Bio, skills, experience |
|
||||
| Projects | `/admin/projects` | Portfolio projects |
|
||||
| Blog | `/admin/blogs` | Blog posts |
|
||||
See [Database Setup](/appwrite/database-setup) for complete schema.
|
||||
|
||||
### Option B: Full Setup
|
||||
|
||||
Follow the complete [Appwrite Setup Guide](/appwrite/overview) to create all collections and storage buckets.
|
||||
|
||||
## First-Time Admin Setup
|
||||
|
||||
<Steps>
|
||||
<Step title="Access Admin Panel">
|
||||
Go to [http://localhost:5173/admin](http://localhost:5173/admin)
|
||||
</Step>
|
||||
|
||||
<Step title="Create Account">
|
||||
Click **Sign Up** and create an account with your email.
|
||||
</Step>
|
||||
|
||||
<Step title="Verify Email (Optional)">
|
||||
If email verification is enabled in Appwrite, check your inbox and verify.
|
||||
</Step>
|
||||
|
||||
<Step title="Add Content">
|
||||
Start adding your content:
|
||||
|
||||
| Section | What to Add |
|
||||
|---------|-------------|
|
||||
| Header | Your name, job titles, intro |
|
||||
| About | Bio, skills, profile image |
|
||||
| Projects | Your portfolio work |
|
||||
| Blog | Articles and posts |
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Quick Content Checklist
|
||||
|
||||
After setup, add content in this order:
|
||||
|
||||
<Steps>
|
||||
<Step title="Header Section">
|
||||
`/admin/header` - Your name, rotating roles, description
|
||||
</Step>
|
||||
<Step title="About Section">
|
||||
`/admin/about` - Bio, skills, profile image
|
||||
</Step>
|
||||
<Step title="Project Categories">
|
||||
`/admin/project-categories` - Create 2-3 categories
|
||||
</Step>
|
||||
<Step title="First Project">
|
||||
`/admin/projects` - Add your best project
|
||||
</Step>
|
||||
<Step title="Blog Category">
|
||||
`/admin/blog-categories` - Create a category
|
||||
</Step>
|
||||
<Step title="First Blog Post">
|
||||
`/admin/blogs` - Write your first post
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Build for Production
|
||||
|
||||
```bash
|
||||
# Build optimized production bundle
|
||||
npm run build
|
||||
npm run preview # Test production build locally
|
||||
|
||||
# Preview production build locally
|
||||
npm run preview
|
||||
```
|
||||
|
||||
The build output is in the `dist/` folder, ready for deployment.
|
||||
|
||||
## Common Issues
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Site shows empty/no data">
|
||||
- Check `.env` file exists with correct values
|
||||
- Verify Appwrite project ID is correct
|
||||
- Ensure `localhost` is added to Appwrite platforms
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="CORS error in console">
|
||||
Add `localhost` to Appwrite Console → Settings → Platforms
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="npm install fails">
|
||||
- Delete `node_modules` and `package-lock.json`
|
||||
- Run `npm install` again
|
||||
- Ensure Node.js 18+ is installed
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
See [Troubleshooting](/troubleshooting) for more solutions.
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Appwrite Setup" icon="database" href="/appwrite/overview">
|
||||
Complete database and storage configuration
|
||||
<Card title="Complete Appwrite Setup" icon="database" href="/appwrite/overview">
|
||||
Set up all collections and storage buckets
|
||||
</Card>
|
||||
<Card title="Deployment" icon="rocket" href="/deployment">
|
||||
Deploy to Vercel or other platforms
|
||||
<Card title="Deploy to Vercel" icon="rocket" href="/deployment">
|
||||
Make your portfolio live
|
||||
</Card>
|
||||
<Card title="Customize Theme" icon="palette" href="/developer/customization">
|
||||
Change colors, fonts, and styles
|
||||
</Card>
|
||||
<Card title="Explore Features" icon="sparkles" href="/features/blog-system">
|
||||
Learn about all available features
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
281
troubleshooting.mdx
Normal file
281
troubleshooting.mdx
Normal file
@ -0,0 +1,281 @@
|
||||
---
|
||||
title: "Troubleshooting"
|
||||
description: "Common issues and solutions for your portfolio website"
|
||||
---
|
||||
|
||||
# Troubleshooting Guide
|
||||
|
||||
Quick solutions to common problems you might encounter.
|
||||
|
||||
## Database & Backend Issues
|
||||
|
||||
### Data Not Loading
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Site shows empty/basic mode">
|
||||
**Cause:** Environment variables not configured or Appwrite connection failed.
|
||||
|
||||
**Solution:**
|
||||
1. Check `.env` file exists with correct values
|
||||
2. Verify Appwrite project ID is correct
|
||||
3. Check browser console for errors
|
||||
4. Ensure Appwrite platform includes your domain
|
||||
|
||||
```env
|
||||
VITE_APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
|
||||
VITE_APPWRITE_PROJECT_ID=your_project_id
|
||||
VITE_APPWRITE_DATABASE_ID=portfolio_db
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="CORS Error in Console">
|
||||
**Error:** `Access to XMLHttpRequest blocked by CORS policy`
|
||||
|
||||
**Solution:**
|
||||
1. Go to Appwrite Console → Settings → Platforms
|
||||
2. Add your domain:
|
||||
- `localhost` (for development)
|
||||
- `your-site.vercel.app` (for production)
|
||||
- `yourdomain.com` (custom domain)
|
||||
3. Wait 1-2 minutes for changes to propagate
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Collection Not Found (404)">
|
||||
**Error:** `Collection or database not found`
|
||||
|
||||
**Solution:**
|
||||
1. Verify collection exists in Appwrite Console
|
||||
2. Check collection ID matches exactly (case-sensitive)
|
||||
3. Ensure database ID is correct in `.env`
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Permission Denied (401/403)">
|
||||
**Error:** `User is not authorized`
|
||||
|
||||
**Solution:**
|
||||
1. Check collection permissions in Appwrite
|
||||
2. For public content: Add "Any" with Read permission
|
||||
3. For admin content: Add "Users" with all permissions
|
||||
4. See [Permissions Guide](/appwrite/permissions)
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Image & Storage Issues
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Images Not Uploading">
|
||||
**Possible Causes:**
|
||||
- File too large (check bucket max size)
|
||||
- Wrong file format
|
||||
- Bucket permissions incorrect
|
||||
|
||||
**Solution:**
|
||||
1. Check file size (usually max 10MB)
|
||||
2. Use supported formats: jpg, png, gif, webp
|
||||
3. Verify bucket has "Users" create permission
|
||||
4. Check browser console for specific error
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Images Not Displaying">
|
||||
**Possible Causes:**
|
||||
- Bucket doesn't have public read access
|
||||
- Image URL is incorrect
|
||||
- Image was deleted
|
||||
|
||||
**Solution:**
|
||||
1. Check bucket has "Any" read permission
|
||||
2. Test image URL directly in browser
|
||||
3. Re-upload the image if needed
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Font Upload Fails">
|
||||
**Solution:**
|
||||
1. Ensure file is `.ttf`, `.otf`, or `.woff` format
|
||||
2. Check file size < 5MB
|
||||
3. Verify `custom-fonts` bucket exists
|
||||
4. Check bucket permissions
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Deployment Issues
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Vercel Build Fails">
|
||||
**Common Causes:**
|
||||
- Missing dependencies
|
||||
- Environment variables not set
|
||||
- Syntax errors in code
|
||||
|
||||
**Solution:**
|
||||
1. Check Vercel build logs for specific error
|
||||
2. Run `npm run build` locally to test
|
||||
3. Ensure all env variables are set in Vercel
|
||||
4. Check for TypeScript/ESLint errors
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="404 on Page Refresh">
|
||||
**Cause:** SPA routing not configured
|
||||
|
||||
**Solution:** Add `vercel.json` to project root:
|
||||
```json
|
||||
{
|
||||
"rewrites": [{ "source": "/(.*)", "destination": "/" }]
|
||||
}
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Environment Variables Not Working">
|
||||
**Solution:**
|
||||
1. Variables must start with `VITE_` prefix
|
||||
2. Redeploy after adding variables
|
||||
3. Check variables are set for correct environment (Production/Preview)
|
||||
4. Clear browser cache after redeploying
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Admin Panel Issues
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Can't Login to Admin">
|
||||
**Solution:**
|
||||
1. Verify Appwrite account exists
|
||||
2. Check email is verified in Appwrite
|
||||
3. Clear browser cookies and try again
|
||||
4. Check Appwrite Console for user status
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Changes Not Saving">
|
||||
**Possible Causes:**
|
||||
- Network error
|
||||
- Permission denied
|
||||
- Validation error
|
||||
|
||||
**Solution:**
|
||||
1. Check browser console for errors
|
||||
2. Verify you're logged in
|
||||
3. Check all required fields are filled
|
||||
4. Try refreshing and re-saving
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Rich Text Editor Not Working">
|
||||
**Solution:**
|
||||
1. Clear browser cache
|
||||
2. Try a different browser
|
||||
3. Check for JavaScript errors in console
|
||||
4. Ensure all dependencies are installed
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Shortlink Issues
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Shortlink Not Redirecting">
|
||||
**Solution:**
|
||||
1. Check shortlink is marked "Active"
|
||||
2. Verify path doesn't have typos
|
||||
3. Clear browser cache
|
||||
4. Check if path conflicts with existing routes
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Custom Domain Not Verifying">
|
||||
**Solution:**
|
||||
1. Wait 10-15 minutes for DNS propagation
|
||||
2. Verify DNS records are correct:
|
||||
- CNAME pointing to your domain
|
||||
- TXT record with verification token
|
||||
3. Check DNS with online tools (dnschecker.org)
|
||||
4. Ensure Cloudflare proxy is enabled (if using)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Analytics Not Recording">
|
||||
**Solution:**
|
||||
1. Check `shortlink_analytics` collection exists
|
||||
2. Verify collection permissions
|
||||
3. Test with incognito/private browsing
|
||||
4. Check browser console for errors
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Shop & Payment Issues
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="PayPal Not Loading">
|
||||
**Solution:**
|
||||
1. Verify `VITE_PAYPAL_CLIENT_ID` is set
|
||||
2. Check PayPal Client ID is correct
|
||||
3. Ensure PayPal app is enabled
|
||||
4. Test with sandbox credentials first
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Order Not Creating">
|
||||
**Solution:**
|
||||
1. Check `orders` collection exists with all attributes
|
||||
2. Verify collection permissions
|
||||
3. Check browser console for validation errors
|
||||
4. Ensure all required fields are provided
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Download Links Not Working">
|
||||
**Solution:**
|
||||
1. Check `order-files` bucket exists
|
||||
2. Verify user is logged in
|
||||
3. Ensure user email matches order email
|
||||
4. Check if download token expired (15 min limit)
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Performance Issues
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Site Loading Slowly">
|
||||
**Solutions:**
|
||||
1. Optimize images before uploading (compress, resize)
|
||||
2. Reduce number of embeds per page
|
||||
3. Use lazy loading for images
|
||||
4. Check network tab for slow requests
|
||||
5. Consider CDN for static assets
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Admin Panel Laggy">
|
||||
**Solutions:**
|
||||
1. Clear browser cache
|
||||
2. Close unused browser tabs
|
||||
3. Check for memory leaks in console
|
||||
4. Reduce number of items per page in lists
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Quick Diagnostic Steps
|
||||
|
||||
When something isn't working:
|
||||
|
||||
<Steps>
|
||||
<Step title="Check Browser Console">
|
||||
Press `F12` → Console tab. Look for red error messages.
|
||||
</Step>
|
||||
<Step title="Check Network Tab">
|
||||
Press `F12` → Network tab. Look for failed requests (red).
|
||||
</Step>
|
||||
<Step title="Verify Environment">
|
||||
Ensure `.env` file exists and has correct values.
|
||||
</Step>
|
||||
<Step title="Check Appwrite Console">
|
||||
Verify collections, buckets, and permissions are set up.
|
||||
</Step>
|
||||
<Step title="Clear Cache">
|
||||
Hard refresh: `Ctrl+Shift+R` (Windows) or `Cmd+Shift+R` (Mac)
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Getting Help
|
||||
|
||||
If you're still stuck:
|
||||
|
||||
1. **Check the specific feature documentation** for detailed setup steps
|
||||
2. **Review Appwrite Console** for database/storage issues
|
||||
3. **Check Vercel logs** for deployment issues
|
||||
4. **Search error messages** online for common solutions
|
||||
|
||||
<Note>
|
||||
Most issues are caused by missing environment variables, incorrect permissions, or CORS configuration. Always check these first!
|
||||
</Note>
|
||||
Loading…
Reference in New Issue
Block a user