@jhits/plugin-blog 0.0.1 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +43 -40
- package/src/index.server.ts +2 -0
- package/src/utils/index.ts +1 -2
- package/src/api/README.md +0 -224
- package/src/hooks/README.md +0 -91
- package/src/utils/README.md +0 -75
- package/src/views/README.md +0 -82
package/package.json
CHANGED
|
@@ -1,57 +1,60 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jhits/plugin-blog",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Professional blog management system for the JHITS ecosystem",
|
|
5
5
|
"publishConfig": {
|
|
6
|
-
|
|
6
|
+
"access": "public"
|
|
7
7
|
},
|
|
8
8
|
"main": "./src/index.tsx",
|
|
9
9
|
"types": "./src/index.tsx",
|
|
10
10
|
"exports": {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./src/index.tsx",
|
|
13
|
+
"default": "./src/index.tsx"
|
|
14
|
+
},
|
|
15
|
+
"./server": {
|
|
16
|
+
"types": "./src/index.server.ts",
|
|
17
|
+
"default": "./src/index.server.ts"
|
|
18
|
+
}
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
"@jhits/plugin-core": "^0.0.1",
|
|
22
|
+
"@jhits/plugin-content": "^0.0.1",
|
|
23
|
+
"@jhits/plugin-images": "^0.0.1",
|
|
24
|
+
"bcrypt": "^6.0.0",
|
|
25
|
+
"framer-motion": "^12.23.26",
|
|
26
|
+
"lucide-react": "^0.562.0",
|
|
27
|
+
"mongodb": "^7.0.0",
|
|
28
|
+
"next-auth": "^4.24.13"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
"next": ">=15.0.0",
|
|
32
|
+
"next-intl": ">=4.0.0",
|
|
33
|
+
"next-themes": ">=0.4.0",
|
|
34
|
+
"react": ">=18.0.0",
|
|
35
|
+
"react-dom": ">=18.0.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
38
|
+
"@tailwindcss/postcss": "^4",
|
|
39
|
+
"@types/bcrypt": "^6.0.0",
|
|
40
|
+
"@types/node": "^20.19.27",
|
|
41
|
+
"@types/react": "^19",
|
|
42
|
+
"@types/react-dom": "^19",
|
|
43
|
+
"eslint": "^9",
|
|
44
|
+
"eslint-config-next": "16.1.1",
|
|
45
|
+
"next": "16.1.1",
|
|
46
|
+
"next-intl": "4.6.1",
|
|
47
|
+
"next-themes": "0.4.6",
|
|
48
|
+
"react": "19.2.3",
|
|
49
|
+
"react-dom": "19.2.3",
|
|
50
|
+
"tailwindcss": "^4",
|
|
51
|
+
"typescript": "^5"
|
|
52
52
|
},
|
|
53
53
|
"files": [
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
"src/**/*.{ts,tsx,json}",
|
|
55
|
+
"!src/**/*.md",
|
|
56
|
+
"!src/**/README.md",
|
|
57
|
+
"!README.md",
|
|
58
|
+
"package.json"
|
|
56
59
|
]
|
|
57
|
-
|
|
60
|
+
}
|
package/src/index.server.ts
CHANGED
package/src/utils/index.ts
CHANGED
package/src/api/README.md
DELETED
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
# Blog API
|
|
2
|
-
|
|
3
|
-
RESTful API handlers for blog posts, included in the `@jhits/plugin-blog` package.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
The API handlers are automatically available when you install `@jhits/plugin-blog`.
|
|
8
|
-
|
|
9
|
-
## Setup
|
|
10
|
-
|
|
11
|
-
### 1. Create API Route in Your App
|
|
12
|
-
|
|
13
|
-
Create API routes in your Next.js app to mount the blog API handlers:
|
|
14
|
-
|
|
15
|
-
**`src/app/api/blogs/route.ts`** - List and create blogs:
|
|
16
|
-
```typescript
|
|
17
|
-
import { NextRequest } from 'next/server';
|
|
18
|
-
import { GET, POST, createBlogApiConfig } from '@jhits/plugin-blog/api';
|
|
19
|
-
import clientPromise from '@/lib/mongodb';
|
|
20
|
-
import { cookies } from 'next/headers';
|
|
21
|
-
import jwt from 'jsonwebtoken';
|
|
22
|
-
|
|
23
|
-
const JWT_SECRET = process.env.JWT_SECRET || 'secret';
|
|
24
|
-
|
|
25
|
-
async function getUserId(req: NextRequest): Promise<string | null> {
|
|
26
|
-
try {
|
|
27
|
-
const cookieStore = await cookies();
|
|
28
|
-
const token = cookieStore.get('auth_token')?.value;
|
|
29
|
-
if (!token) return null;
|
|
30
|
-
const decoded = jwt.verify(token, JWT_SECRET) as { id: string };
|
|
31
|
-
return decoded.id;
|
|
32
|
-
} catch {
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const config = createBlogApiConfig({
|
|
38
|
-
getDb: async () => clientPromise,
|
|
39
|
-
getUserId,
|
|
40
|
-
collectionName: 'blogs', // optional, defaults to 'blogs'
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
export async function GET(req: NextRequest) {
|
|
44
|
-
return GET(req, undefined, config);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export async function POST(req: NextRequest) {
|
|
48
|
-
return POST(req, undefined, config);
|
|
49
|
-
}
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
**`src/app/api/blogs/[slug]/route.ts`** - Get, update, and delete by slug:
|
|
53
|
-
```typescript
|
|
54
|
-
import { NextRequest } from 'next/server';
|
|
55
|
-
import { GET, PUT, DELETE, createBlogApiConfig } from '@jhits/plugin-blog/api';
|
|
56
|
-
import clientPromise from '@/lib/mongodb';
|
|
57
|
-
import { cookies } from 'next/headers';
|
|
58
|
-
import jwt from 'jsonwebtoken';
|
|
59
|
-
|
|
60
|
-
const JWT_SECRET = process.env.JWT_SECRET || 'secret';
|
|
61
|
-
|
|
62
|
-
async function getUserId(req: NextRequest): Promise<string | null> {
|
|
63
|
-
try {
|
|
64
|
-
const cookieStore = await cookies();
|
|
65
|
-
const token = cookieStore.get('auth_token')?.value;
|
|
66
|
-
if (!token) return null;
|
|
67
|
-
const decoded = jwt.verify(token, JWT_SECRET) as { id: string };
|
|
68
|
-
return decoded.id;
|
|
69
|
-
} catch {
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const config = createBlogApiConfig({
|
|
75
|
-
getDb: async () => clientPromise,
|
|
76
|
-
getUserId,
|
|
77
|
-
collectionName: 'blogs',
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
export async function GET(
|
|
81
|
-
req: NextRequest,
|
|
82
|
-
{ params }: { params: Promise<{ slug: string }> }
|
|
83
|
-
) {
|
|
84
|
-
return GET(req, { params }, config);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export async function PUT(
|
|
88
|
-
req: NextRequest,
|
|
89
|
-
{ params }: { params: Promise<{ slug: string }> }
|
|
90
|
-
) {
|
|
91
|
-
return PUT(req, { params }, config);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export async function DELETE(
|
|
95
|
-
req: NextRequest,
|
|
96
|
-
{ params }: { params: Promise<{ slug: string }> }
|
|
97
|
-
) {
|
|
98
|
-
return DELETE(req, { params }, config);
|
|
99
|
-
}
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
## API Endpoints
|
|
103
|
-
|
|
104
|
-
### GET /api/blogs
|
|
105
|
-
List all blog posts.
|
|
106
|
-
|
|
107
|
-
**Query Parameters:**
|
|
108
|
-
- `limit` (number, default: 10) - Number of posts to return
|
|
109
|
-
- `skip` (number, default: 0) - Number of posts to skip
|
|
110
|
-
- `status` (string, optional) - Filter by status (`published`, `concept`, `draft`)
|
|
111
|
-
- `admin` (boolean, default: false) - If true, returns all posts for authenticated user (including drafts)
|
|
112
|
-
|
|
113
|
-
**Response:**
|
|
114
|
-
```json
|
|
115
|
-
{
|
|
116
|
-
"blogs": [...],
|
|
117
|
-
"total": 100
|
|
118
|
-
}
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
### POST /api/blogs
|
|
122
|
-
Create a new blog post.
|
|
123
|
-
|
|
124
|
-
**Request Body:**
|
|
125
|
-
```json
|
|
126
|
-
{
|
|
127
|
-
"title": "My Blog Post",
|
|
128
|
-
"summary": "A brief summary",
|
|
129
|
-
"contentBlocks": [...],
|
|
130
|
-
"image": {
|
|
131
|
-
"src": "/uploads/image.jpg",
|
|
132
|
-
"alt": "Image alt text",
|
|
133
|
-
"brightness": 100,
|
|
134
|
-
"blur": 0
|
|
135
|
-
},
|
|
136
|
-
"categoryTags": {
|
|
137
|
-
"category": "Technology",
|
|
138
|
-
"tags": ["web", "development"]
|
|
139
|
-
},
|
|
140
|
-
"publicationData": {
|
|
141
|
-
"status": "concept" | "published",
|
|
142
|
-
"date": "2024-01-01T00:00:00.000Z"
|
|
143
|
-
},
|
|
144
|
-
"seo": {
|
|
145
|
-
"title": "SEO Title",
|
|
146
|
-
"description": "SEO Description",
|
|
147
|
-
"keywords": ["keyword1", "keyword2"],
|
|
148
|
-
"ogImage": "/uploads/og-image.jpg",
|
|
149
|
-
"canonicalUrl": "https://example.com/blog/my-post"
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
**Response:**
|
|
155
|
-
```json
|
|
156
|
-
{
|
|
157
|
-
"message": "Draft saved successfully",
|
|
158
|
-
"blogId": "...",
|
|
159
|
-
"slug": "my-blog-post-draft-1234"
|
|
160
|
-
}
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
### GET /api/blogs/[slug]
|
|
164
|
-
Get a single blog post by slug.
|
|
165
|
-
|
|
166
|
-
**Response:**
|
|
167
|
-
```json
|
|
168
|
-
{
|
|
169
|
-
"_id": "...",
|
|
170
|
-
"title": "My Blog Post",
|
|
171
|
-
"slug": "my-blog-post",
|
|
172
|
-
"contentBlocks": [...],
|
|
173
|
-
...
|
|
174
|
-
}
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
### PUT /api/blogs/[slug]
|
|
178
|
-
Update a blog post by slug.
|
|
179
|
-
|
|
180
|
-
**Request Body:** Same as POST /api/blogs
|
|
181
|
-
|
|
182
|
-
**Response:**
|
|
183
|
-
```json
|
|
184
|
-
{
|
|
185
|
-
"message": "Blog updated successfully",
|
|
186
|
-
"slug": "updated-slug"
|
|
187
|
-
}
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
### DELETE /api/blogs/[slug]
|
|
191
|
-
Delete a blog post by slug.
|
|
192
|
-
|
|
193
|
-
**Response:**
|
|
194
|
-
```json
|
|
195
|
-
{
|
|
196
|
-
"message": "Blog deleted successfully"
|
|
197
|
-
}
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
## Security
|
|
201
|
-
|
|
202
|
-
- All endpoints (except public GET) require authentication
|
|
203
|
-
- Users can only access/modify their own posts
|
|
204
|
-
- Published posts are visible to everyone
|
|
205
|
-
- Drafts/concepts are only visible to the author
|
|
206
|
-
|
|
207
|
-
## Error Responses
|
|
208
|
-
|
|
209
|
-
All endpoints return standard HTTP status codes:
|
|
210
|
-
- `200` - Success
|
|
211
|
-
- `400` - Bad Request (validation errors)
|
|
212
|
-
- `401` - Unauthorized (not authenticated)
|
|
213
|
-
- `403` - Forbidden (not authorized)
|
|
214
|
-
- `404` - Not Found
|
|
215
|
-
- `500` - Internal Server Error
|
|
216
|
-
|
|
217
|
-
Error response format:
|
|
218
|
-
```json
|
|
219
|
-
{
|
|
220
|
-
"error": "Error message",
|
|
221
|
-
"detail": "Detailed error information"
|
|
222
|
-
}
|
|
223
|
-
```
|
|
224
|
-
|
package/src/hooks/README.md
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
# Blog Plugin Hooks
|
|
2
|
-
|
|
3
|
-
React hooks for fetching blog data in client applications.
|
|
4
|
-
|
|
5
|
-
## useBlogs
|
|
6
|
-
|
|
7
|
-
Fetch a list of blog posts with automatic loading and error handling.
|
|
8
|
-
|
|
9
|
-
### Usage
|
|
10
|
-
|
|
11
|
-
```tsx
|
|
12
|
-
import { useBlogs } from '@jhits/plugin-blog';
|
|
13
|
-
|
|
14
|
-
function MyComponent() {
|
|
15
|
-
const { blogs, loading, error, total, refetch } = useBlogs({
|
|
16
|
-
limit: 10,
|
|
17
|
-
skip: 0,
|
|
18
|
-
status: 'published',
|
|
19
|
-
admin: false,
|
|
20
|
-
apiBaseUrl: '/api/blogs',
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
if (loading) return <div>Loading...</div>;
|
|
24
|
-
if (error) return <div>Error: {error}</div>;
|
|
25
|
-
|
|
26
|
-
return (
|
|
27
|
-
<div>
|
|
28
|
-
{blogs.map(blog => (
|
|
29
|
-
<div key={blog.id}>
|
|
30
|
-
<h2>{blog.title}</h2>
|
|
31
|
-
<p>{blog.excerpt}</p>
|
|
32
|
-
</div>
|
|
33
|
-
))}
|
|
34
|
-
</div>
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### Options
|
|
40
|
-
|
|
41
|
-
- `limit` (number, default: 10): Maximum number of posts to fetch
|
|
42
|
-
- `skip` (number, default: 0): Number of posts to skip (for pagination)
|
|
43
|
-
- `status` (string, optional): Filter by status ('published', 'draft', 'concept')
|
|
44
|
-
- `admin` (boolean, default: false): Whether to fetch all posts for admin (includes drafts)
|
|
45
|
-
- `apiBaseUrl` (string, default: '/api/blogs'): API base URL
|
|
46
|
-
|
|
47
|
-
### Returns
|
|
48
|
-
|
|
49
|
-
- `blogs` (PostListItem[]): Array of blog posts
|
|
50
|
-
- `loading` (boolean): Whether data is currently loading
|
|
51
|
-
- `error` (string | null): Error message if fetch failed
|
|
52
|
-
- `total` (number): Total number of posts available
|
|
53
|
-
- `refetch` (function): Function to manually refetch blogs
|
|
54
|
-
|
|
55
|
-
## useBlog
|
|
56
|
-
|
|
57
|
-
Fetch a single blog post by slug.
|
|
58
|
-
|
|
59
|
-
### Usage
|
|
60
|
-
|
|
61
|
-
```tsx
|
|
62
|
-
import { useBlog } from '@jhits/plugin-blog';
|
|
63
|
-
|
|
64
|
-
function BlogPost({ slug }: { slug: string }) {
|
|
65
|
-
const { blog, loading, error, refetch } = useBlog({ slug });
|
|
66
|
-
|
|
67
|
-
if (loading) return <div>Loading...</div>;
|
|
68
|
-
if (error) return <div>Error: {error}</div>;
|
|
69
|
-
if (!blog) return <div>Post not found</div>;
|
|
70
|
-
|
|
71
|
-
return (
|
|
72
|
-
<article>
|
|
73
|
-
<h1>{blog.title}</h1>
|
|
74
|
-
<div>{/* Render blog content */}</div>
|
|
75
|
-
</article>
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
### Options
|
|
81
|
-
|
|
82
|
-
- `slug` (string, required): Blog post slug
|
|
83
|
-
- `apiBaseUrl` (string, default: '/api/blogs'): API base URL
|
|
84
|
-
|
|
85
|
-
### Returns
|
|
86
|
-
|
|
87
|
-
- `blog` (BlogPost | null): Blog post data
|
|
88
|
-
- `loading` (boolean): Whether data is currently loading
|
|
89
|
-
- `error` (string | null): Error message if fetch failed
|
|
90
|
-
- `refetch` (function): Function to manually refetch the blog post
|
|
91
|
-
|
package/src/utils/README.md
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
# Blog Plugin Client Utilities
|
|
2
|
-
|
|
3
|
-
Helper functions for fetching blog data in client applications (non-React).
|
|
4
|
-
|
|
5
|
-
## fetchBlogs
|
|
6
|
-
|
|
7
|
-
Fetch blog posts from the API (returns a Promise).
|
|
8
|
-
|
|
9
|
-
### Usage
|
|
10
|
-
|
|
11
|
-
```ts
|
|
12
|
-
import { fetchBlogs } from '@jhits/plugin-blog';
|
|
13
|
-
|
|
14
|
-
async function loadBlogs() {
|
|
15
|
-
try {
|
|
16
|
-
const { blogs, total } = await fetchBlogs({
|
|
17
|
-
limit: 10,
|
|
18
|
-
skip: 0,
|
|
19
|
-
status: 'published',
|
|
20
|
-
admin: false,
|
|
21
|
-
apiBaseUrl: '/api/blogs',
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
console.log(`Found ${total} posts`);
|
|
25
|
-
blogs.forEach(blog => {
|
|
26
|
-
console.log(blog.title);
|
|
27
|
-
});
|
|
28
|
-
} catch (error) {
|
|
29
|
-
console.error('Failed to fetch blogs:', error);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
### Options
|
|
35
|
-
|
|
36
|
-
- `limit` (number, default: 10): Maximum number of posts to fetch
|
|
37
|
-
- `skip` (number, default: 0): Number of posts to skip (for pagination)
|
|
38
|
-
- `status` (string, optional): Filter by status ('published', 'draft', 'concept')
|
|
39
|
-
- `admin` (boolean, default: false): Whether to fetch all posts for admin (includes drafts)
|
|
40
|
-
- `apiBaseUrl` (string, default: '/api/blogs'): API base URL
|
|
41
|
-
|
|
42
|
-
### Returns
|
|
43
|
-
|
|
44
|
-
Promise resolving to:
|
|
45
|
-
- `blogs` (PostListItem[]): Array of blog posts
|
|
46
|
-
- `total` (number): Total number of posts available
|
|
47
|
-
|
|
48
|
-
## fetchBlog
|
|
49
|
-
|
|
50
|
-
Fetch a single blog post by slug (returns a Promise).
|
|
51
|
-
|
|
52
|
-
### Usage
|
|
53
|
-
|
|
54
|
-
```ts
|
|
55
|
-
import { fetchBlog } from '@jhits/plugin-blog';
|
|
56
|
-
|
|
57
|
-
async function loadBlogPost(slug: string) {
|
|
58
|
-
try {
|
|
59
|
-
const blog = await fetchBlog({ slug });
|
|
60
|
-
console.log(blog.title);
|
|
61
|
-
} catch (error) {
|
|
62
|
-
console.error('Failed to fetch blog:', error);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### Options
|
|
68
|
-
|
|
69
|
-
- `slug` (string, required): Blog post slug
|
|
70
|
-
- `apiBaseUrl` (string, default: '/api/blogs'): API base URL
|
|
71
|
-
|
|
72
|
-
### Returns
|
|
73
|
-
|
|
74
|
-
Promise resolving to a `BlogPost` object.
|
|
75
|
-
|
package/src/views/README.md
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
# Blog Plugin Views
|
|
2
|
-
|
|
3
|
-
## Directory Structure
|
|
4
|
-
|
|
5
|
-
```
|
|
6
|
-
src/views/
|
|
7
|
-
├── PostManager/
|
|
8
|
-
│ ├── PostManagerView.tsx # Professional table layout for post management
|
|
9
|
-
│ └── index.ts
|
|
10
|
-
├── CanvasEditor/
|
|
11
|
-
│ ├── CanvasEditorView.tsx # Drag-and-drop block editor workspace
|
|
12
|
-
│ └── index.ts
|
|
13
|
-
├── SlugSEO/
|
|
14
|
-
│ ├── SlugSEOManagerView.tsx # URL slug and SEO metadata management
|
|
15
|
-
│ └── index.ts
|
|
16
|
-
└── Preview/
|
|
17
|
-
├── PreviewBridgeView.tsx # Live preview of blog posts
|
|
18
|
-
└── index.ts
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
## Design System Compliance
|
|
22
|
-
|
|
23
|
-
All views follow the dashboard's earth-tone design system:
|
|
24
|
-
|
|
25
|
-
- **Colors**: Neutral palette (neutral-100 to neutral-900), primary (violet-700)
|
|
26
|
-
- **Typography**:
|
|
27
|
-
- Headings: `font-black uppercase tracking-tighter`
|
|
28
|
-
- Labels: `text-[10px] font-black uppercase tracking-widest`
|
|
29
|
-
- Body: `font-sans`
|
|
30
|
-
- **Layout**:
|
|
31
|
-
- Containers: `rounded-[2.5rem]`
|
|
32
|
-
- Padding: `p-8`
|
|
33
|
-
- Backgrounds: `bg-white dark:bg-neutral-900`
|
|
34
|
-
- **Buttons**: `rounded-full`, uppercase, tracking-widest
|
|
35
|
-
- **Borders**: `border-neutral-300 dark:border-neutral-700`
|
|
36
|
-
|
|
37
|
-
## PostManagerView
|
|
38
|
-
|
|
39
|
-
Professional table layout with:
|
|
40
|
-
- Search and filter functionality
|
|
41
|
-
- Status badges (published, draft, scheduled, archived)
|
|
42
|
-
- Empty state with "Create Your First Post" CTA
|
|
43
|
-
- Action buttons (Edit, Preview, Delete)
|
|
44
|
-
- Mock data for development
|
|
45
|
-
|
|
46
|
-
## CanvasEditorView
|
|
47
|
-
|
|
48
|
-
Block editor workspace with:
|
|
49
|
-
- Toolbar with Back, Preview, and Save buttons
|
|
50
|
-
- Title input field
|
|
51
|
-
- Block canvas area
|
|
52
|
-
- Empty state for adding first block
|
|
53
|
-
- Integrated with EditorProvider for state management
|
|
54
|
-
|
|
55
|
-
## SlugSEOManagerView
|
|
56
|
-
|
|
57
|
-
SEO management interface with:
|
|
58
|
-
- URL slug editor with collision detection placeholder
|
|
59
|
-
- Google-style search result preview
|
|
60
|
-
- Meta description editor with character counter
|
|
61
|
-
|
|
62
|
-
## PreviewBridgeView
|
|
63
|
-
|
|
64
|
-
Live preview interface with:
|
|
65
|
-
- Header with Refresh and Open in New Tab buttons
|
|
66
|
-
- Preview container (ready for iframe or side-by-side panel)
|
|
67
|
-
- Placeholder for preview rendering
|
|
68
|
-
|
|
69
|
-
## Usage
|
|
70
|
-
|
|
71
|
-
Views are imported and used in the main `BlogPlugin` router:
|
|
72
|
-
|
|
73
|
-
```typescript
|
|
74
|
-
import { PostManagerView } from './views/PostManager';
|
|
75
|
-
import { CanvasEditorView } from './views/CanvasEditor';
|
|
76
|
-
// ... etc
|
|
77
|
-
|
|
78
|
-
// In router switch statement:
|
|
79
|
-
case 'posts':
|
|
80
|
-
return <PostManagerView siteId={siteId} locale={locale} />;
|
|
81
|
-
```
|
|
82
|
-
|