@content-streamline/nextjs 0.0.2 → 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/README.md +205 -90
- package/dist/api/client.d.ts +2 -2
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +9 -4
- package/dist/components/LatestPosts.d.ts +53 -0
- package/dist/components/LatestPosts.d.ts.map +1 -0
- package/dist/components/LatestPosts.js +29 -0
- package/dist/components/PostsList.d.ts +15 -3
- package/dist/components/PostsList.d.ts.map +1 -1
- package/dist/components/PostsList.js +13 -7
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +14 -4
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -5,11 +5,12 @@ Next.js library for Content Streamline API with reusable, responsive components.
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- 🚀 **Easy Setup** - Configure via environment variables
|
|
8
|
-
- 📦 **Reusable Components** - Pre-built React components for posts
|
|
8
|
+
- 📦 **Reusable Components** - Pre-built React components for posts
|
|
9
9
|
- 💾 **Smart Caching** - Automatic file-based caching with refresh control
|
|
10
10
|
- 🎨 **Customizable** - Responsive components with configurable props
|
|
11
11
|
- 📱 **Mobile-Friendly** - Built-in responsive design
|
|
12
12
|
- 🔧 **TypeScript** - Full TypeScript support
|
|
13
|
+
- 🏷️ **Platform Filtering** - Filter posts by platform (blog, linkedin, x, etc.)
|
|
13
14
|
|
|
14
15
|
## Installation
|
|
15
16
|
|
|
@@ -48,21 +49,32 @@ import '@content-streamline/nextjs/components/styles.css';
|
|
|
48
49
|
|
|
49
50
|
### 3. Use in Server Components (App Router)
|
|
50
51
|
|
|
51
|
-
**Blog
|
|
52
|
+
**Blog Page with Featured Posts** (`app/blog/page.tsx`):
|
|
52
53
|
|
|
53
54
|
```tsx
|
|
54
55
|
import { getAllPosts } from '@content-streamline/nextjs/server';
|
|
55
|
-
import { PostsList } from '@content-streamline/nextjs/components';
|
|
56
|
+
import { LatestPosts, PostsList } from '@content-streamline/nextjs/components';
|
|
56
57
|
|
|
57
58
|
export const revalidate = 3600; // Revalidate every hour (ISR)
|
|
58
59
|
|
|
59
60
|
export default async function BlogPage() {
|
|
60
|
-
const posts = await getAllPosts();
|
|
61
|
+
const posts = await getAllPosts({ platform: 'blog' });
|
|
61
62
|
|
|
62
63
|
return (
|
|
63
64
|
<div>
|
|
64
65
|
<h1>Blog</h1>
|
|
65
|
-
|
|
66
|
+
|
|
67
|
+
{/* Featured section with latest 3 posts */}
|
|
68
|
+
<section>
|
|
69
|
+
<h2>Latest Posts</h2>
|
|
70
|
+
<LatestPosts posts={posts} count={3} baseUrl="/blog" />
|
|
71
|
+
</section>
|
|
72
|
+
|
|
73
|
+
{/* Remaining posts in vertical list */}
|
|
74
|
+
<section>
|
|
75
|
+
<h2>All Posts</h2>
|
|
76
|
+
<PostsList posts={posts} skipFirst={3} baseUrl="/blog" />
|
|
77
|
+
</section>
|
|
66
78
|
</div>
|
|
67
79
|
);
|
|
68
80
|
}
|
|
@@ -77,7 +89,7 @@ import { notFound } from 'next/navigation';
|
|
|
77
89
|
|
|
78
90
|
export async function generateStaticParams() {
|
|
79
91
|
const { getAllPosts } = await import('@content-streamline/nextjs/server');
|
|
80
|
-
const posts = await getAllPosts();
|
|
92
|
+
const posts = await getAllPosts({ platform: 'blog' });
|
|
81
93
|
|
|
82
94
|
return posts.flatMap(post =>
|
|
83
95
|
post.post_translations
|
|
@@ -94,7 +106,7 @@ export default async function PostPage({
|
|
|
94
106
|
}: {
|
|
95
107
|
params: { id: string; slug: string };
|
|
96
108
|
}) {
|
|
97
|
-
const post = await getPostBySlug(params.slug);
|
|
109
|
+
const post = await getPostBySlug(params.slug, { platform: 'blog' });
|
|
98
110
|
|
|
99
111
|
if (!post) {
|
|
100
112
|
notFound();
|
|
@@ -108,15 +120,16 @@ export default async function PostPage({
|
|
|
108
120
|
|
|
109
121
|
### 4. Use in Pages Router
|
|
110
122
|
|
|
111
|
-
**Blog
|
|
123
|
+
**Blog Page** (`pages/blog/index.tsx`):
|
|
112
124
|
|
|
113
125
|
```tsx
|
|
114
126
|
import { getAllPosts } from '@content-streamline/nextjs/server';
|
|
115
|
-
import { PostsList } from '@content-streamline/nextjs/components';
|
|
127
|
+
import { LatestPosts, PostsList } from '@content-streamline/nextjs/components';
|
|
116
128
|
import type { GetStaticProps } from 'next';
|
|
129
|
+
import type { Post } from '@content-streamline/nextjs';
|
|
117
130
|
|
|
118
131
|
export const getStaticProps: GetStaticProps = async () => {
|
|
119
|
-
const posts = await getAllPosts();
|
|
132
|
+
const posts = await getAllPosts({ platform: 'blog' });
|
|
120
133
|
|
|
121
134
|
return {
|
|
122
135
|
props: { posts },
|
|
@@ -128,7 +141,8 @@ export default function BlogPage({ posts }: { posts: Post[] }) {
|
|
|
128
141
|
return (
|
|
129
142
|
<div>
|
|
130
143
|
<h1>Blog</h1>
|
|
131
|
-
<
|
|
144
|
+
<LatestPosts posts={posts} count={3} baseUrl="/blog" />
|
|
145
|
+
<PostsList posts={posts} skipFirst={3} baseUrl="/blog" />
|
|
132
146
|
</div>
|
|
133
147
|
);
|
|
134
148
|
}
|
|
@@ -140,9 +154,10 @@ export default function BlogPage({ posts }: { posts: Post[] }) {
|
|
|
140
154
|
import { getPostBySlug, getAllPosts } from '@content-streamline/nextjs/server';
|
|
141
155
|
import { PostDetail } from '@content-streamline/nextjs/components';
|
|
142
156
|
import type { GetStaticProps, GetStaticPaths } from 'next';
|
|
157
|
+
import type { PostDetailResponse } from '@content-streamline/nextjs';
|
|
143
158
|
|
|
144
159
|
export const getStaticPaths: GetStaticPaths = async () => {
|
|
145
|
-
const posts = await getAllPosts();
|
|
160
|
+
const posts = await getAllPosts({ platform: 'blog' });
|
|
146
161
|
|
|
147
162
|
const paths = posts.flatMap(post =>
|
|
148
163
|
post.post_translations
|
|
@@ -159,7 +174,7 @@ export const getStaticPaths: GetStaticPaths = async () => {
|
|
|
159
174
|
};
|
|
160
175
|
|
|
161
176
|
export const getStaticProps: GetStaticProps = async ({ params }) => {
|
|
162
|
-
const post = await getPostBySlug(params!.slug as string);
|
|
177
|
+
const post = await getPostBySlug(params!.slug as string, { platform: 'blog' });
|
|
163
178
|
|
|
164
179
|
if (!post) {
|
|
165
180
|
return { notFound: true };
|
|
@@ -188,11 +203,14 @@ Get all posts with automatic caching.
|
|
|
188
203
|
import { getAllPosts } from '@content-streamline/nextjs/server';
|
|
189
204
|
|
|
190
205
|
const posts = await getAllPosts({
|
|
191
|
-
|
|
192
|
-
|
|
206
|
+
platform: 'blog', // Filter by platform (default: undefined = all)
|
|
207
|
+
maxCacheAge: 60, // Cache age in minutes (default: 60)
|
|
208
|
+
forceRefresh: false, // Force API fetch (default: false)
|
|
193
209
|
});
|
|
194
210
|
```
|
|
195
211
|
|
|
212
|
+
**Platform values**: `'blog'`, `'x'`, `'linkedin'`, `'facebook'`, `'instagram'`, `'other'`
|
|
213
|
+
|
|
196
214
|
#### `getPost(id, options?)`
|
|
197
215
|
|
|
198
216
|
Get a single post by ID.
|
|
@@ -218,6 +236,7 @@ import { getPostBySlug } from '@content-streamline/nextjs/server';
|
|
|
218
236
|
const post = await getPostBySlug('my-post-slug', {
|
|
219
237
|
language: 'en',
|
|
220
238
|
contentFormat: 'markdown',
|
|
239
|
+
platform: 'blog',
|
|
221
240
|
maxCacheAge: 60,
|
|
222
241
|
forceRefresh: false,
|
|
223
242
|
});
|
|
@@ -225,30 +244,66 @@ const post = await getPostBySlug('my-post-slug', {
|
|
|
225
244
|
|
|
226
245
|
### Components
|
|
227
246
|
|
|
247
|
+
#### `<LatestPosts />`
|
|
248
|
+
|
|
249
|
+
Display featured/hero posts with the first post highlighted. Perfect for homepage or blog landing sections.
|
|
250
|
+
|
|
251
|
+
```tsx
|
|
252
|
+
import { LatestPosts } from '@content-streamline/nextjs/components';
|
|
253
|
+
|
|
254
|
+
<LatestPosts
|
|
255
|
+
posts={posts}
|
|
256
|
+
count={3} // Number of posts to show (default: 3)
|
|
257
|
+
baseUrl="/blog" // Base URL for post links
|
|
258
|
+
platform="blog" // Filter by platform (default: 'blog')
|
|
259
|
+
language="en" // Language for translations (default: 'en')
|
|
260
|
+
className="custom-class" // Custom container class
|
|
261
|
+
cardClassName="custom" // Custom card class
|
|
262
|
+
showDate={true} // Show post date (default: true)
|
|
263
|
+
showExcerpt={true} // Show post excerpt (default: true)
|
|
264
|
+
showImages={true} // Show post images (default: true)
|
|
265
|
+
renderTitle={(post, translation) => <CustomTitle />} // Custom title renderer
|
|
266
|
+
renderExcerpt={(post, translation) => <CustomExcerpt />} // Custom excerpt renderer
|
|
267
|
+
/>
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
**Layout behavior**:
|
|
271
|
+
- First post is displayed as a featured card (larger, with image on left)
|
|
272
|
+
- Remaining posts are displayed in a responsive grid below
|
|
273
|
+
- Fully responsive - stacks vertically on mobile
|
|
274
|
+
|
|
228
275
|
#### `<PostsList />`
|
|
229
276
|
|
|
230
|
-
Display a list
|
|
277
|
+
Display posts in a vertical list with thumbnails on the left. Ideal for archive pages or "more posts" sections.
|
|
231
278
|
|
|
232
279
|
```tsx
|
|
233
280
|
import { PostsList } from '@content-streamline/nextjs/components';
|
|
234
281
|
|
|
235
282
|
<PostsList
|
|
236
283
|
posts={posts}
|
|
237
|
-
baseUrl="/blog"
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
284
|
+
baseUrl="/blog" // Base URL for post links
|
|
285
|
+
platform="blog" // Filter by platform (default: 'blog')
|
|
286
|
+
skipFirst={3} // Skip first N posts (default: 0)
|
|
287
|
+
language="en" // Language for translations (default: 'en')
|
|
288
|
+
className="custom-class" // Custom container class
|
|
289
|
+
itemClassName="custom" // Custom item class
|
|
290
|
+
showDate={true} // Show post date (default: true)
|
|
291
|
+
showExcerpt={true} // Show post excerpt (default: true)
|
|
292
|
+
showImages={true} // Show post images (default: true)
|
|
293
|
+
renderTitle={(post, translation) => <CustomTitle />} // Custom title renderer
|
|
294
|
+
renderExcerpt={(post, translation) => <CustomExcerpt />} // Custom excerpt renderer
|
|
246
295
|
/>
|
|
247
296
|
```
|
|
248
297
|
|
|
298
|
+
**Layout behavior**:
|
|
299
|
+
- Vertical list with image thumbnail on the left
|
|
300
|
+
- Title and excerpt on the right
|
|
301
|
+
- Compact design for browsing many posts
|
|
302
|
+
- `skipFirst` prop allows combining with `LatestPosts` to avoid duplicates
|
|
303
|
+
|
|
249
304
|
#### `<PostDetail />`
|
|
250
305
|
|
|
251
|
-
Display a single post.
|
|
306
|
+
Display a single post with full content.
|
|
252
307
|
|
|
253
308
|
```tsx
|
|
254
309
|
import { PostDetail } from '@content-streamline/nextjs/components';
|
|
@@ -256,18 +311,69 @@ import { PostDetail } from '@content-streamline/nextjs/components';
|
|
|
256
311
|
<PostDetail
|
|
257
312
|
post={post}
|
|
258
313
|
backUrl="/blog" // URL for back link
|
|
259
|
-
language="en" // Language for translation
|
|
314
|
+
language="en" // Language for translation (default: 'en')
|
|
260
315
|
className="custom-class" // Custom container class
|
|
261
|
-
showBackLink={true} // Show back link
|
|
262
|
-
showDate={true} // Show post date
|
|
263
|
-
showType={true}
|
|
264
|
-
showImage={true}
|
|
265
|
-
showGallery={true}
|
|
266
|
-
renderContent={(content) => <MarkdownRenderer content={content} />}
|
|
267
|
-
renderTitle={(post, translation) => <CustomTitle />}
|
|
316
|
+
showBackLink={true} // Show back link (default: true)
|
|
317
|
+
showDate={true} // Show post date (default: true)
|
|
318
|
+
showType={true} // Show post type badge (default: true)
|
|
319
|
+
showImage={true} // Show featured image (default: true)
|
|
320
|
+
showGallery={true} // Show gallery images (default: true)
|
|
321
|
+
renderContent={(content) => <MarkdownRenderer content={content} />} // Custom content renderer
|
|
322
|
+
renderTitle={(post, translation) => <CustomTitle />} // Custom title renderer
|
|
268
323
|
/>
|
|
269
324
|
```
|
|
270
325
|
|
|
326
|
+
## Common Patterns
|
|
327
|
+
|
|
328
|
+
### Featured Posts + Archive List
|
|
329
|
+
|
|
330
|
+
Combine `LatestPosts` and `PostsList` for a complete blog layout:
|
|
331
|
+
|
|
332
|
+
```tsx
|
|
333
|
+
import { getAllPosts } from '@content-streamline/nextjs/server';
|
|
334
|
+
import { LatestPosts, PostsList } from '@content-streamline/nextjs/components';
|
|
335
|
+
|
|
336
|
+
export default async function BlogPage() {
|
|
337
|
+
const posts = await getAllPosts({ platform: 'blog' });
|
|
338
|
+
|
|
339
|
+
return (
|
|
340
|
+
<main>
|
|
341
|
+
{/* Hero section with 3 featured posts */}
|
|
342
|
+
<section className="hero">
|
|
343
|
+
<LatestPosts posts={posts} count={3} baseUrl="/blog" />
|
|
344
|
+
</section>
|
|
345
|
+
|
|
346
|
+
{/* Archive list (skip the 3 featured posts) */}
|
|
347
|
+
<section className="archive">
|
|
348
|
+
<h2>More Articles</h2>
|
|
349
|
+
<PostsList posts={posts} skipFirst={3} baseUrl="/blog" />
|
|
350
|
+
</section>
|
|
351
|
+
</main>
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Multi-Platform Content
|
|
357
|
+
|
|
358
|
+
Display content from different platforms:
|
|
359
|
+
|
|
360
|
+
```tsx
|
|
361
|
+
// Blog posts
|
|
362
|
+
const blogPosts = await getAllPosts({ platform: 'blog' });
|
|
363
|
+
|
|
364
|
+
// LinkedIn posts
|
|
365
|
+
const linkedinPosts = await getAllPosts({ platform: 'linkedin' });
|
|
366
|
+
|
|
367
|
+
// All posts (no filter)
|
|
368
|
+
const allPosts = await getAllPosts();
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Homepage with Single Featured Post
|
|
372
|
+
|
|
373
|
+
```tsx
|
|
374
|
+
<LatestPosts posts={posts} count={1} baseUrl="/blog" />
|
|
375
|
+
```
|
|
376
|
+
|
|
271
377
|
## Customization
|
|
272
378
|
|
|
273
379
|
### Styling
|
|
@@ -275,15 +381,26 @@ import { PostDetail } from '@content-streamline/nextjs/components';
|
|
|
275
381
|
Components use CSS classes with the `content-streamline-` prefix. You can override styles:
|
|
276
382
|
|
|
277
383
|
```css
|
|
278
|
-
/* Override
|
|
279
|
-
.content-streamline-
|
|
280
|
-
border-radius:
|
|
281
|
-
box-shadow: 0
|
|
384
|
+
/* Override latest posts card */
|
|
385
|
+
.content-streamline-latest-card {
|
|
386
|
+
border-radius: 20px;
|
|
387
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/* Override featured card */
|
|
391
|
+
.content-streamline-latest-card--featured {
|
|
392
|
+
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/* Override list items */
|
|
396
|
+
.content-streamline-list-item {
|
|
397
|
+
border-left: 4px solid #3b82f6;
|
|
282
398
|
}
|
|
283
399
|
|
|
284
400
|
/* Override post title */
|
|
285
|
-
.content-streamline-
|
|
286
|
-
|
|
401
|
+
.content-streamline-latest-title a,
|
|
402
|
+
.content-streamline-list-title a {
|
|
403
|
+
color: #your-brand-color;
|
|
287
404
|
}
|
|
288
405
|
```
|
|
289
406
|
|
|
@@ -292,16 +409,17 @@ Components use CSS classes with the `content-streamline-` prefix. You can overri
|
|
|
292
409
|
Use render props for complete control:
|
|
293
410
|
|
|
294
411
|
```tsx
|
|
295
|
-
<
|
|
412
|
+
<LatestPosts
|
|
296
413
|
posts={posts}
|
|
297
414
|
renderTitle={(post, translation) => (
|
|
298
415
|
<h2 className="my-custom-title">
|
|
416
|
+
<span className="category">{post.content_type}</span>
|
|
299
417
|
{translation.title}
|
|
300
418
|
</h2>
|
|
301
419
|
)}
|
|
302
420
|
renderExcerpt={(post, translation) => (
|
|
303
421
|
<p className="my-custom-excerpt">
|
|
304
|
-
{translation.meta_description}
|
|
422
|
+
{translation.meta_description?.slice(0, 100)}...
|
|
305
423
|
</p>
|
|
306
424
|
)}
|
|
307
425
|
/>
|
|
@@ -341,60 +459,57 @@ const posts = await getAllPosts({ forceRefresh: true });
|
|
|
341
459
|
Full TypeScript support is included:
|
|
342
460
|
|
|
343
461
|
```tsx
|
|
344
|
-
import type {
|
|
462
|
+
import type {
|
|
463
|
+
Post,
|
|
464
|
+
PostDetailResponse,
|
|
465
|
+
PostsListProps,
|
|
466
|
+
LatestPostsProps,
|
|
467
|
+
PostDetailProps,
|
|
468
|
+
} from '@content-streamline/nextjs';
|
|
345
469
|
|
|
346
470
|
function MyComponent({ post }: { post: PostDetailResponse }) {
|
|
347
|
-
//
|
|
471
|
+
// Full type safety
|
|
348
472
|
}
|
|
349
473
|
```
|
|
350
474
|
|
|
351
|
-
##
|
|
352
|
-
|
|
353
|
-
###
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
return (
|
|
389
|
-
<PostDetail
|
|
390
|
-
post={post}
|
|
391
|
-
renderContent={(content) => <ReactMarkdown>{content}</ReactMarkdown>}
|
|
392
|
-
/>
|
|
393
|
-
);
|
|
394
|
-
}
|
|
395
|
-
```
|
|
475
|
+
## CSS Classes Reference
|
|
476
|
+
|
|
477
|
+
### LatestPosts
|
|
478
|
+
- `.content-streamline-latest-posts` - Container
|
|
479
|
+
- `.content-streamline-latest-card` - Individual card
|
|
480
|
+
- `.content-streamline-latest-card--featured` - First (featured) card
|
|
481
|
+
- `.content-streamline-latest-image` - Image container
|
|
482
|
+
- `.content-streamline-latest-content` - Content area
|
|
483
|
+
- `.content-streamline-latest-date` - Date element
|
|
484
|
+
- `.content-streamline-latest-title` - Title
|
|
485
|
+
- `.content-streamline-latest-excerpt` - Excerpt text
|
|
486
|
+
- `.content-streamline-latest-link` - "Read more" link
|
|
487
|
+
|
|
488
|
+
### PostsList
|
|
489
|
+
- `.content-streamline-list` - Container
|
|
490
|
+
- `.content-streamline-list-item` - Individual item
|
|
491
|
+
- `.content-streamline-list-image` - Image thumbnail
|
|
492
|
+
- `.content-streamline-list-content` - Content area
|
|
493
|
+
- `.content-streamline-list-date` - Date element
|
|
494
|
+
- `.content-streamline-list-title` - Title
|
|
495
|
+
- `.content-streamline-list-excerpt` - Excerpt text
|
|
496
|
+
|
|
497
|
+
### PostDetail
|
|
498
|
+
- `.content-streamline-post-detail` - Container
|
|
499
|
+
- `.content-streamline-back-link` - Back navigation link
|
|
500
|
+
- `.content-streamline-post-header` - Header section
|
|
501
|
+
- `.content-streamline-post-title` - Title
|
|
502
|
+
- `.content-streamline-post-meta` - Meta information
|
|
503
|
+
- `.content-streamline-post-date` - Date
|
|
504
|
+
- `.content-streamline-post-type` - Type badge
|
|
505
|
+
- `.content-streamline-post-featured-image` - Featured image
|
|
506
|
+
- `.content-streamline-post-content` - Content area
|
|
507
|
+
- `.content-streamline-post-gallery` - Image gallery
|
|
508
|
+
|
|
509
|
+
### Common
|
|
510
|
+
- `.content-streamline-empty` - Empty state message
|
|
511
|
+
- `.content-streamline-post-not-found` - Not found message
|
|
396
512
|
|
|
397
513
|
## License
|
|
398
514
|
|
|
399
515
|
MIT
|
|
400
|
-
|
package/dist/api/client.d.ts
CHANGED
|
@@ -10,11 +10,11 @@ declare class ContentStreamlineAPI {
|
|
|
10
10
|
/**
|
|
11
11
|
* List all posts with pagination
|
|
12
12
|
*/
|
|
13
|
-
listPosts(page?: number): Promise<PostsResponse>;
|
|
13
|
+
listPosts(page?: number, platform?: string): Promise<PostsResponse>;
|
|
14
14
|
/**
|
|
15
15
|
* Get all posts by fetching all pages
|
|
16
16
|
*/
|
|
17
|
-
getAllPosts(): Promise<Post[]>;
|
|
17
|
+
getAllPosts(platform?: string): Promise<Post[]>;
|
|
18
18
|
/**
|
|
19
19
|
* Get a single post by ID
|
|
20
20
|
*/
|
package/dist/api/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,IAAI,EACJ,kBAAkB,EAClB,aAAa,EACd,MAAM,mBAAmB,CAAC;AAE3B,cAAM,oBAAoB;IACxB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;YAKlC,KAAK;IAiCnB;;OAEG;IACG,SAAS,CAAC,IAAI,GAAE,MAAU,GAAG,OAAO,CAAC,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,IAAI,EACJ,kBAAkB,EAClB,aAAa,EACd,MAAM,mBAAmB,CAAC;AAE3B,cAAM,oBAAoB;IACxB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;YAKlC,KAAK;IAiCnB;;OAEG;IACG,SAAS,CAAC,IAAI,GAAE,MAAU,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAS5E;;OAEG;IACG,WAAW,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAgBrD;;OAEG;IACG,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,aAAa,GAAE,UAAU,GAAG,MAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAIxG;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,oBAAoB,CAE1F"}
|
package/dist/api/client.js
CHANGED
|
@@ -38,18 +38,23 @@ class ContentStreamlineAPI {
|
|
|
38
38
|
/**
|
|
39
39
|
* List all posts with pagination
|
|
40
40
|
*/
|
|
41
|
-
async listPosts(page = 1) {
|
|
42
|
-
|
|
41
|
+
async listPosts(page = 1, platform) {
|
|
42
|
+
const params = new URLSearchParams();
|
|
43
|
+
params.set('page', page.toString());
|
|
44
|
+
if (platform) {
|
|
45
|
+
params.set('platform', platform);
|
|
46
|
+
}
|
|
47
|
+
return this.fetch(`/public/v1/posts?${params.toString()}`);
|
|
43
48
|
}
|
|
44
49
|
/**
|
|
45
50
|
* Get all posts by fetching all pages
|
|
46
51
|
*/
|
|
47
|
-
async getAllPosts() {
|
|
52
|
+
async getAllPosts(platform) {
|
|
48
53
|
const allPosts = [];
|
|
49
54
|
let currentPage = 1;
|
|
50
55
|
let hasMore = true;
|
|
51
56
|
while (hasMore) {
|
|
52
|
-
const response = await this.listPosts(currentPage);
|
|
57
|
+
const response = await this.listPosts(currentPage, platform);
|
|
53
58
|
allPosts.push(...response.data);
|
|
54
59
|
hasMore = response.pagination.next_page !== null;
|
|
55
60
|
currentPage = response.pagination.next_page || currentPage + 1;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Post } from '../types/index.js';
|
|
3
|
+
export interface LatestPostsProps {
|
|
4
|
+
posts: Post[];
|
|
5
|
+
/**
|
|
6
|
+
* Number of posts to display (default: 3)
|
|
7
|
+
*/
|
|
8
|
+
count?: number;
|
|
9
|
+
/**
|
|
10
|
+
* Base URL for post links (e.g., '/blog' or '/posts')
|
|
11
|
+
* Post URL will be: {baseUrl}/{postId}/{slug}
|
|
12
|
+
*/
|
|
13
|
+
baseUrl?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Platform filter for posts (default: 'blog')
|
|
16
|
+
* Valid values: 'x', 'linkedin', 'blog', 'facebook', 'instagram', 'other'
|
|
17
|
+
*/
|
|
18
|
+
platform?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Language to use for post translations (default: 'en')
|
|
21
|
+
*/
|
|
22
|
+
language?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Custom className for the container
|
|
25
|
+
*/
|
|
26
|
+
className?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Custom className for post cards
|
|
29
|
+
*/
|
|
30
|
+
cardClassName?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Show post date (default: true)
|
|
33
|
+
*/
|
|
34
|
+
showDate?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Show post excerpt/description (default: true)
|
|
37
|
+
*/
|
|
38
|
+
showExcerpt?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Show post images (default: true)
|
|
41
|
+
*/
|
|
42
|
+
showImages?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Custom render function for post title
|
|
45
|
+
*/
|
|
46
|
+
renderTitle?: (post: Post, translation: Post['post_translations'][0]) => React.ReactNode;
|
|
47
|
+
/**
|
|
48
|
+
* Custom render function for post excerpt
|
|
49
|
+
*/
|
|
50
|
+
renderExcerpt?: (post: Post, translation: Post['post_translations'][0]) => React.ReactNode;
|
|
51
|
+
}
|
|
52
|
+
export declare function LatestPosts({ posts, count, baseUrl, platform, language, className, cardClassName, showDate, showExcerpt, showImages, renderTitle, renderExcerpt, }: LatestPostsProps): import("react/jsx-runtime").JSX.Element;
|
|
53
|
+
//# sourceMappingURL=LatestPosts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LatestPosts.d.ts","sourceRoot":"","sources":["../../src/components/LatestPosts.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAE9C,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,IAAI,EAAE,CAAC;IACd;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,SAAS,CAAC;IACzF;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,SAAS,CAAC;CAC5F;AAED,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,KAAS,EACT,OAAkB,EAClB,QAAiB,EACjB,QAAe,EACf,SAAc,EACd,aAAkB,EAClB,QAAe,EACf,WAAkB,EAClB,UAAiB,EACjB,WAAW,EACX,aAAa,GACd,EAAE,gBAAgB,2CAuFlB"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
export function LatestPosts({ posts, count = 3, baseUrl = '/posts', platform = 'blog', language = 'en', className = '', cardClassName = '', showDate = true, showExcerpt = true, showImages = true, renderTitle, renderExcerpt, }) {
|
|
4
|
+
// Filter posts by platform if specified
|
|
5
|
+
const filteredPosts = platform
|
|
6
|
+
? posts.filter(post => post.platform.toLowerCase() === platform.toLowerCase())
|
|
7
|
+
: posts;
|
|
8
|
+
// Take only the first N posts
|
|
9
|
+
const latestPosts = filteredPosts.slice(0, count);
|
|
10
|
+
if (latestPosts.length === 0) {
|
|
11
|
+
return (_jsx("div", { className: `content-streamline-empty ${className}`, children: _jsx("p", { children: "No posts found." }) }));
|
|
12
|
+
}
|
|
13
|
+
return (_jsx("div", { className: `content-streamline-latest-posts ${className}`, children: latestPosts.map((post, index) => {
|
|
14
|
+
const translation = post.post_translations.find((t) => t.language === language) ||
|
|
15
|
+
post.post_translations[0];
|
|
16
|
+
if (!translation || !translation.slug)
|
|
17
|
+
return null;
|
|
18
|
+
const featuredImage = post.images.find((img) => img.ordering === 0) || post.images[0];
|
|
19
|
+
const date = new Date(post.created_at);
|
|
20
|
+
const isFirst = index === 0;
|
|
21
|
+
return (_jsxs("article", { className: `content-streamline-latest-card ${isFirst ? 'content-streamline-latest-card--featured' : ''} ${cardClassName}`, children: [showImages && featuredImage && (_jsx("a", { href: `${baseUrl}/${post.id}/${translation.slug}`, className: "content-streamline-latest-image-link", children: _jsx("div", { className: "content-streamline-latest-image", children: _jsx("img", { src: featuredImage.url, alt: featuredImage.alt || translation.title, loading: isFirst ? 'eager' : 'lazy' }) }) })), _jsxs("div", { className: "content-streamline-latest-content", children: [showDate && (_jsx("time", { className: "content-streamline-latest-date", dateTime: post.created_at, children: date.toLocaleDateString('en-US', {
|
|
22
|
+
year: 'numeric',
|
|
23
|
+
month: 'long',
|
|
24
|
+
day: 'numeric',
|
|
25
|
+
}) })), _jsx("h2", { className: "content-streamline-latest-title", children: _jsx("a", { href: `${baseUrl}/${post.id}/${translation.slug}`, children: renderTitle ? renderTitle(post, translation) : translation.title }) }), showExcerpt && translation.meta_description && (_jsx("p", { className: "content-streamline-latest-excerpt", children: renderExcerpt
|
|
26
|
+
? renderExcerpt(post, translation)
|
|
27
|
+
: translation.meta_description })), _jsx("a", { href: `${baseUrl}/${post.id}/${translation.slug}`, className: "content-streamline-latest-link", children: "Read more \u2192" })] })] }, post.id));
|
|
28
|
+
}) }));
|
|
29
|
+
}
|
|
@@ -7,6 +7,18 @@ export interface PostsListProps {
|
|
|
7
7
|
* Post URL will be: {baseUrl}/{postId}/{slug}
|
|
8
8
|
*/
|
|
9
9
|
baseUrl?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Platform filter for posts (default: 'blog')
|
|
12
|
+
* Valid values: 'x', 'linkedin', 'blog', 'facebook', 'instagram', 'other'
|
|
13
|
+
* Filters the posts to only show those matching this platform.
|
|
14
|
+
* For best performance, also pass platform to getAllPosts() when fetching.
|
|
15
|
+
*/
|
|
16
|
+
platform?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Number of posts to skip from the beginning (default: 0)
|
|
19
|
+
* Useful when using LatestPosts component to display the first N posts separately.
|
|
20
|
+
*/
|
|
21
|
+
skipFirst?: number;
|
|
10
22
|
/**
|
|
11
23
|
* Language to use for post translations (default: 'en')
|
|
12
24
|
*/
|
|
@@ -16,9 +28,9 @@ export interface PostsListProps {
|
|
|
16
28
|
*/
|
|
17
29
|
className?: string;
|
|
18
30
|
/**
|
|
19
|
-
* Custom className for post
|
|
31
|
+
* Custom className for post items
|
|
20
32
|
*/
|
|
21
|
-
|
|
33
|
+
itemClassName?: string;
|
|
22
34
|
/**
|
|
23
35
|
* Show post date (default: true)
|
|
24
36
|
*/
|
|
@@ -40,5 +52,5 @@ export interface PostsListProps {
|
|
|
40
52
|
*/
|
|
41
53
|
renderExcerpt?: (post: Post, translation: Post['post_translations'][0]) => React.ReactNode;
|
|
42
54
|
}
|
|
43
|
-
export declare function PostsList({ posts, baseUrl, language, className,
|
|
55
|
+
export declare function PostsList({ posts, baseUrl, platform, skipFirst, language, className, itemClassName, showDate, showExcerpt, showImages, renderTitle, renderExcerpt, }: PostsListProps): import("react/jsx-runtime").JSX.Element;
|
|
44
56
|
//# sourceMappingURL=PostsList.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PostsList.d.ts","sourceRoot":"","sources":["../../src/components/PostsList.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAE9C,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,IAAI,EAAE,CAAC;IACd;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,SAAS,CAAC;IACzF;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,SAAS,CAAC;CAC5F;AAED,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,OAAkB,EAClB,QAAe,EACf,SAAc,EACd,aAAkB,EAClB,QAAe,EACf,WAAkB,EAClB,UAAiB,EACjB,WAAW,EACX,aAAa,GACd,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"PostsList.d.ts","sourceRoot":"","sources":["../../src/components/PostsList.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAE9C,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,IAAI,EAAE,CAAC;IACd;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,SAAS,CAAC;IACzF;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,SAAS,CAAC;CAC5F;AAED,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,OAAkB,EAClB,QAAiB,EACjB,SAAa,EACb,QAAe,EACf,SAAc,EACd,aAAkB,EAClB,QAAe,EACf,WAAkB,EAClB,UAAiB,EACjB,WAAW,EACX,aAAa,GACd,EAAE,cAAc,2CAgFhB"}
|
|
@@ -1,22 +1,28 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
export function PostsList({ posts, baseUrl = '/posts', language = 'en', className = '',
|
|
4
|
-
|
|
3
|
+
export function PostsList({ posts, baseUrl = '/posts', platform = 'blog', skipFirst = 0, language = 'en', className = '', itemClassName = '', showDate = true, showExcerpt = true, showImages = true, renderTitle, renderExcerpt, }) {
|
|
4
|
+
// Filter posts by platform if specified
|
|
5
|
+
const filteredPosts = platform
|
|
6
|
+
? posts.filter(post => post.platform.toLowerCase() === platform.toLowerCase())
|
|
7
|
+
: posts;
|
|
8
|
+
// Skip first N posts
|
|
9
|
+
const displayPosts = skipFirst > 0 ? filteredPosts.slice(skipFirst) : filteredPosts;
|
|
10
|
+
if (displayPosts.length === 0) {
|
|
5
11
|
return (_jsx("div", { className: `content-streamline-empty ${className}`, children: _jsx("p", { children: "No posts found." }) }));
|
|
6
12
|
}
|
|
7
|
-
return (_jsx("div", { className: `content-streamline-
|
|
13
|
+
return (_jsx("div", { className: `content-streamline-list ${className}`, children: displayPosts.map((post) => {
|
|
8
14
|
const translation = post.post_translations.find((t) => t.language === language) ||
|
|
9
15
|
post.post_translations[0];
|
|
10
16
|
if (!translation || !translation.slug)
|
|
11
17
|
return null;
|
|
12
18
|
const featuredImage = post.images.find((img) => img.ordering === 0) || post.images[0];
|
|
13
19
|
const date = new Date(post.created_at);
|
|
14
|
-
return (_jsxs("article", { className: `content-streamline-
|
|
20
|
+
return (_jsxs("article", { className: `content-streamline-list-item ${itemClassName}`, children: [showImages && featuredImage && (_jsx("a", { href: `${baseUrl}/${post.id}/${translation.slug}`, className: "content-streamline-list-image-link", children: _jsx("div", { className: "content-streamline-list-image", children: _jsx("img", { src: featuredImage.url, alt: featuredImage.alt || translation.title, loading: "lazy" }) }) })), _jsxs("div", { className: "content-streamline-list-content", children: [showDate && (_jsx("time", { className: "content-streamline-list-date", dateTime: post.created_at, children: date.toLocaleDateString('en-US', {
|
|
15
21
|
year: 'numeric',
|
|
16
|
-
month: '
|
|
22
|
+
month: 'short',
|
|
17
23
|
day: 'numeric',
|
|
18
|
-
}) })), _jsx("
|
|
24
|
+
}) })), _jsx("h3", { className: "content-streamline-list-title", children: _jsx("a", { href: `${baseUrl}/${post.id}/${translation.slug}`, children: renderTitle ? renderTitle(post, translation) : translation.title }) }), showExcerpt && translation.meta_description && (_jsx("p", { className: "content-streamline-list-excerpt", children: renderExcerpt
|
|
19
25
|
? renderExcerpt(post, translation)
|
|
20
|
-
: translation.meta_description }))
|
|
26
|
+
: translation.meta_description }))] })] }, post.id));
|
|
21
27
|
}) }));
|
|
22
28
|
}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export { PostsList } from './PostsList.js';
|
|
5
5
|
export { PostDetail } from './PostDetail.js';
|
|
6
|
+
export { LatestPosts } from './LatestPosts.js';
|
|
6
7
|
export type { PostsListProps } from './PostsList.js';
|
|
7
8
|
export type { PostDetailProps } from './PostDetail.js';
|
|
9
|
+
export type { LatestPostsProps } from './LatestPosts.js';
|
|
8
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/components/index.js
CHANGED
|
@@ -3,5 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export { PostsList } from './PostsList.js';
|
|
5
5
|
export { PostDetail } from './PostDetail.js';
|
|
6
|
+
export { LatestPosts } from './LatestPosts.js';
|
|
6
7
|
// Styles are available at '@content-streamline/nextjs/components/styles.css'
|
|
7
8
|
// Users should import them in their app: import '@content-streamline/nextjs/components/styles.css'
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export type { Post, PostDetailResponse, PostTranslation, PostImage, PostsResponse, Pagination, } from './types/index.js';
|
|
6
6
|
export { getAllPosts, getPost, getPostBySlug, } from './server/index.js';
|
|
7
|
-
export { PostsList, PostDetail, } from './components/index.js';
|
|
8
|
-
export type { PostsListProps, PostDetailProps, } from './components/index.js';
|
|
7
|
+
export { PostsList, PostDetail, LatestPosts, } from './components/index.js';
|
|
8
|
+
export type { PostsListProps, PostDetailProps, LatestPostsProps, } from './components/index.js';
|
|
9
9
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,YAAY,EACV,IAAI,EACJ,kBAAkB,EAClB,eAAe,EACf,SAAS,EACT,aAAa,EACb,UAAU,GACX,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,WAAW,EACX,OAAO,EACP,aAAa,GACd,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,SAAS,EACT,UAAU,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,YAAY,EACV,IAAI,EACJ,kBAAkB,EAClB,eAAe,EACf,SAAS,EACT,aAAa,EACb,UAAU,GACX,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,WAAW,EACX,OAAO,EACP,aAAa,GACd,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,SAAS,EACT,UAAU,EACV,WAAW,GACZ,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EACV,cAAc,EACd,eAAe,EACf,gBAAgB,GACjB,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/server/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type { Post, PostDetailResponse } from '../types/index.js';
|
|
|
10
10
|
export declare function getAllPosts(options?: {
|
|
11
11
|
maxCacheAge?: number;
|
|
12
12
|
forceRefresh?: boolean;
|
|
13
|
+
platform?: string;
|
|
13
14
|
}): Promise<Post[]>;
|
|
14
15
|
/**
|
|
15
16
|
* Get a single post by ID
|
|
@@ -30,6 +31,7 @@ export declare function getPostBySlug(slug: string, options?: {
|
|
|
30
31
|
contentFormat?: 'markdown' | 'html';
|
|
31
32
|
maxCacheAge?: number;
|
|
32
33
|
forceRefresh?: boolean;
|
|
34
|
+
platform?: string;
|
|
33
35
|
}): Promise<PostDetailResponse | null>;
|
|
34
36
|
export type { Post, PostDetailResponse, PostTranslation, PostImage, PostsResponse, Pagination, } from '../types/index.js';
|
|
35
37
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,OAAO,KAAK,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAkBlE;;;GAGG;AACH,wBAAsB,WAAW,CAAC,OAAO,CAAC,EAAE;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,OAAO,KAAK,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAkBlE;;;GAGG;AACH,wBAAsB,WAAW,CAAC,OAAO,CAAC,EAAE;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CA2ClB;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAC3B,EAAE,EAAE,MAAM,EACV,OAAO,CAAC,EAAE;IACR,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,GACA,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAiCpC;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;IACR,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GACA,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CA2BpC;AAGD,YAAY,EACV,IAAI,EACJ,kBAAkB,EAClB,eAAe,EACf,SAAS,EACT,aAAa,EACb,UAAU,GACX,MAAM,mBAAmB,CAAC"}
|
package/dist/server/index.js
CHANGED
|
@@ -20,21 +20,30 @@ function getAPIConfig() {
|
|
|
20
20
|
* Use this in Server Components or API Routes
|
|
21
21
|
*/
|
|
22
22
|
export async function getAllPosts(options) {
|
|
23
|
-
const { maxCacheAge =
|
|
23
|
+
const { maxCacheAge = 15, forceRefresh = false, platform } = options || {};
|
|
24
24
|
// Check cache freshness
|
|
25
25
|
if (!forceRefresh) {
|
|
26
26
|
const needsRefresh = await shouldRefreshCache(maxCacheAge);
|
|
27
27
|
if (!needsRefresh) {
|
|
28
28
|
const cached = await getAllCachedPosts();
|
|
29
29
|
if (cached.length > 0) {
|
|
30
|
-
|
|
30
|
+
// Filter cached posts by platform if specified
|
|
31
|
+
if (platform) {
|
|
32
|
+
const filtered = cached.filter(post => post.platform.toLowerCase() === platform.toLowerCase());
|
|
33
|
+
if (filtered.length > 0) {
|
|
34
|
+
return filtered;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
return cached;
|
|
39
|
+
}
|
|
31
40
|
}
|
|
32
41
|
}
|
|
33
42
|
}
|
|
34
43
|
// Fetch from API
|
|
35
44
|
const { baseUrl, accessToken } = getAPIConfig();
|
|
36
45
|
const api = createAPIClient(baseUrl, accessToken);
|
|
37
|
-
const posts = await api.getAllPosts();
|
|
46
|
+
const posts = await api.getAllPosts(platform);
|
|
38
47
|
// Cache all posts
|
|
39
48
|
for (const post of posts) {
|
|
40
49
|
try {
|
|
@@ -54,7 +63,7 @@ export async function getAllPosts(options) {
|
|
|
54
63
|
* Use this in Server Components or API Routes
|
|
55
64
|
*/
|
|
56
65
|
export async function getPost(id, options) {
|
|
57
|
-
const { language = 'en', contentFormat = 'markdown', maxCacheAge =
|
|
66
|
+
const { language = 'en', contentFormat = 'markdown', maxCacheAge = 15, forceRefresh = false, } = options || {};
|
|
58
67
|
// Try cache first
|
|
59
68
|
if (!forceRefresh) {
|
|
60
69
|
const needsRefresh = await shouldRefreshCache(maxCacheAge);
|
|
@@ -88,6 +97,7 @@ export async function getPostBySlug(slug, options) {
|
|
|
88
97
|
const posts = await getAllPosts({
|
|
89
98
|
maxCacheAge: options?.maxCacheAge,
|
|
90
99
|
forceRefresh: options?.forceRefresh,
|
|
100
|
+
platform: options?.platform,
|
|
91
101
|
});
|
|
92
102
|
const language = options?.language || 'en';
|
|
93
103
|
// Find post with matching slug
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@content-streamline/nextjs",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Next.js library for Content Streamline API with reusable components",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -49,4 +49,3 @@
|
|
|
49
49
|
"README.md"
|
|
50
50
|
]
|
|
51
51
|
}
|
|
52
|
-
|