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
|
# 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
|
## Tech Stack
|
||||||
|
|
||||||
| Technology | Purpose |
|
| Technology | Purpose | Why Chosen |
|
||||||
|------------|---------|
|
|------------|---------|------------|
|
||||||
| React 18 | Frontend framework |
|
| React 18 | Frontend framework | Component-based, large ecosystem |
|
||||||
| Vite 5 | Build tool & dev server |
|
| Vite 5 | Build tool & dev server | Lightning fast HMR, optimized builds |
|
||||||
| Tailwind CSS 3 | Styling |
|
| Tailwind CSS 3 | Styling | Utility-first, highly customizable |
|
||||||
| Appwrite | Backend, database & storage |
|
| Appwrite | Backend, database & storage | Open-source, self-hostable, generous free tier |
|
||||||
| Lucide React | Icons |
|
| Lucide React | Icons | Consistent, lightweight, 1000+ icons |
|
||||||
|
|
||||||
## Key Features
|
## Key Features
|
||||||
|
|
||||||
<CardGroup cols={2}>
|
<CardGroup cols={2}>
|
||||||
<Card title="Blog System" icon="newspaper">
|
<Card title="Blog System" icon="newspaper" href="/features/blog-system">
|
||||||
Full-featured blog with categories, multiple authors, and rich text editing
|
Full-featured blog with categories, multiple authors, rich text editing, and SEO-friendly URLs
|
||||||
</Card>
|
</Card>
|
||||||
<Card title="Project Gallery" icon="folder">
|
<Card title="Project Gallery" icon="folder" href="/features/project-gallery">
|
||||||
Showcase projects with custom URLs, galleries, and detailed descriptions
|
Showcase projects with custom URLs, image galleries, and detailed descriptions
|
||||||
</Card>
|
</Card>
|
||||||
<Card title="Shortlink System" icon="link">
|
<Card title="Shortlink System" icon="link" href="/features/shortlink-system">
|
||||||
URL shortener with analytics, custom domains, and preview images
|
URL shortener with click analytics, geographic tracking, and custom domain support
|
||||||
</Card>
|
</Card>
|
||||||
<Card title="Shop & Orders" icon="shopping-cart">
|
<Card title="Shop & Orders" icon="shopping-cart" href="/features/shop-system">
|
||||||
E-commerce with PayPal integration, order tracking, and secure file delivery
|
E-commerce with PayPal integration, order tracking, coupons, and secure file delivery
|
||||||
</Card>
|
</Card>
|
||||||
<Card title="Font Management" icon="font">
|
<Card title="Font Management" icon="font" href="/features/font-management">
|
||||||
70+ Google Fonts plus custom font uploads with Bangla support
|
70+ Google Fonts plus custom font uploads with full Bangla language support
|
||||||
</Card>
|
</Card>
|
||||||
<Card title="Rich Text Editor" icon="pen">
|
<Card title="Rich Text Editor" icon="pen" href="/features/rich-text-editor">
|
||||||
Advanced editor with embeds, photo grids, tables, and full formatting
|
Advanced editor with embeds, photo grids, tables, and complete formatting options
|
||||||
</Card>
|
</Card>
|
||||||
</CardGroup>
|
</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
|
## Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
src/
|
portfolio/
|
||||||
├── components/ # Reusable UI components
|
├── src/
|
||||||
├── pages/
|
│ ├── components/ # Reusable UI components
|
||||||
│ ├── Admin/ # Admin dashboard pages
|
│ │ ├── Header/
|
||||||
│ └── ... # Public pages
|
│ │ ├── Footer/
|
||||||
├── lib/ # Appwrite services & utilities
|
│ │ ├── RichTextEditor/
|
||||||
└── utils/ # Helper functions
|
│ │ └── ...
|
||||||
|
│ ├── 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 Links
|
||||||
|
|
||||||
- [Quick Start Guide](/quick-start) - Get up and running in 10 minutes
|
<CardGroup cols={2}>
|
||||||
- [Appwrite Setup](/appwrite/overview) - Configure your backend
|
<Card title="Quick Start" icon="play" href="/quick-start">
|
||||||
- [Deployment Guide](/deployment) - Deploy to Vercel
|
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": [
|
"navigation": [
|
||||||
{
|
{
|
||||||
"group": "Getting Started",
|
"group": "Getting Started",
|
||||||
"pages": ["introduction", "quick-start", "deployment"]
|
"pages": ["introduction", "quick-start", "deployment", "troubleshooting"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"group": "Appwrite Setup",
|
"group": "Appwrite Setup",
|
||||||
@ -48,7 +48,8 @@
|
|||||||
"features/shortlink-system",
|
"features/shortlink-system",
|
||||||
"features/shop-system",
|
"features/shop-system",
|
||||||
"features/font-management",
|
"features/font-management",
|
||||||
"features/rich-text-editor"
|
"features/rich-text-editor",
|
||||||
|
"features/photo-grids"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -58,6 +59,14 @@
|
|||||||
"admin/content-management",
|
"admin/content-management",
|
||||||
"admin/settings"
|
"admin/settings"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "Developer Guide",
|
||||||
|
"pages": [
|
||||||
|
"developer/architecture",
|
||||||
|
"developer/api-reference",
|
||||||
|
"developer/customization"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"footerSocials": {
|
"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
|
# Quick Start Guide
|
||||||
|
|
||||||
|
Get your portfolio website up and running quickly with this step-by-step guide.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
- Node.js 18+ installed
|
Before starting, make sure you have:
|
||||||
- Appwrite account ([cloud.appwrite.io](https://cloud.appwrite.io))
|
|
||||||
- Git installed
|
| 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
|
## Installation
|
||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
<Step title="Clone & Install">
|
<Step title="Clone the Repository">
|
||||||
```bash
|
```bash
|
||||||
git clone <your-repo-url>
|
git clone <your-repo-url>
|
||||||
cd portfolio
|
cd portfolio
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Install Dependencies">
|
||||||
|
```bash
|
||||||
npm install
|
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>
|
||||||
|
|
||||||
<Step title="Configure Environment">
|
<Step title="Configure Environment">
|
||||||
Create a `.env` file in the root directory:
|
Create a `.env` file in the root directory:
|
||||||
|
|
||||||
```env
|
```env
|
||||||
|
# Appwrite Configuration
|
||||||
VITE_APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
|
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_DATABASE_ID=portfolio_db
|
||||||
VITE_APPWRITE_STORAGE_ID=reactbucket
|
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>
|
<Note>
|
||||||
Get these values from your Appwrite Console after creating a project.
|
This allows your local development server to communicate with Appwrite.
|
||||||
</Note>
|
</Note>
|
||||||
</Step>
|
</Step>
|
||||||
|
|
||||||
@ -43,47 +80,133 @@ description: "Get your portfolio website running in 10 minutes"
|
|||||||
```
|
```
|
||||||
|
|
||||||
Open [http://localhost:5173](http://localhost:5173) in your browser.
|
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>
|
</Step>
|
||||||
</Steps>
|
</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
|
### Option A: Quick Setup (Recommended for Testing)
|
||||||
2. Sign up with your email
|
|
||||||
3. Verify your email in Appwrite Console
|
|
||||||
|
|
||||||
### 2. Set Up Database
|
Create the minimum required collections:
|
||||||
|
|
||||||
Follow the [Appwrite Setup Guide](/appwrite/overview) to create:
|
1. Go to Appwrite Console → **Databases**
|
||||||
- Required collections (blogs, projects, products, etc.)
|
2. Click **Create Database** → ID: `portfolio_db`
|
||||||
- Storage buckets for images
|
3. Create these collections with basic attributes:
|
||||||
- Proper permissions
|
|
||||||
|
|
||||||
### 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 |
|
See [Database Setup](/appwrite/database-setup) for complete schema.
|
||||||
|---------|-----------|-------------|
|
|
||||||
| Header | `/admin/header` | Name, roles, description |
|
### Option B: Full Setup
|
||||||
| About | `/admin/about` | Bio, skills, experience |
|
|
||||||
| Projects | `/admin/projects` | Portfolio projects |
|
Follow the complete [Appwrite Setup Guide](/appwrite/overview) to create all collections and storage buckets.
|
||||||
| Blog | `/admin/blogs` | Blog posts |
|
|
||||||
|
## 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
|
## Build for Production
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# Build optimized production bundle
|
||||||
npm run build
|
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
|
## Next Steps
|
||||||
|
|
||||||
<CardGroup cols={2}>
|
<CardGroup cols={2}>
|
||||||
<Card title="Appwrite Setup" icon="database" href="/appwrite/overview">
|
<Card title="Complete Appwrite Setup" icon="database" href="/appwrite/overview">
|
||||||
Complete database and storage configuration
|
Set up all collections and storage buckets
|
||||||
</Card>
|
</Card>
|
||||||
<Card title="Deployment" icon="rocket" href="/deployment">
|
<Card title="Deploy to Vercel" icon="rocket" href="/deployment">
|
||||||
Deploy to Vercel or other platforms
|
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>
|
</Card>
|
||||||
</CardGroup>
|
</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