Page not found
+The requested page could not be found.
+diff --git a/STRAPI_PREVIEW_SETUP.md b/STRAPI_PREVIEW_SETUP.md
new file mode 100644
index 0000000..0b72d4d
--- /dev/null
+++ b/STRAPI_PREVIEW_SETUP.md
@@ -0,0 +1,193 @@
+# Strapi Preview Implementation for Vike App
+
+This document explains how to set up and use the Strapi Preview feature with this Vike application.
+
+## Features Implemented
+
+✅ **Basic Preview** - Preview draft and published content from Strapi admin
+✅ **Dynamic Routes** - Support for articles, pages, and other content types
+✅ **Preview Banner** - Visual indicator when in preview mode
+✅ **Live Preview Ready** - Communication setup for real-time content updates
+✅ **Security** - CSP headers for safe iframe embedding
+✅ **Cache Control** - Preview content bypasses caching
+
+## Setup Instructions
+
+### 1. Frontend Configuration (Already Done)
+
+The Vike app is now configured with:
+
+- **Environment Variables** (`.env`):
+ ```bash
+ VITE_CLIENT_URL=https://takerofnotes.com
+ VITE_PREVIEW_SECRET=preview-secret-key-change-this-in-production
+ ```
+
+- **Preview Route**: `/pages/preview/+data.js` and `/pages/preview/+Page.vue`
+- **Enhanced Strapi Client**: Preview-aware data fetching
+- **UI Components**: Preview banner and Live Preview communication
+- **CSP Headers**: Allow embedding from Strapi admin
+
+### 2. Strapi Configuration (To Do)
+
+1. **Copy the admin configuration** from `strapi-config-example.js` to your Strapi project at `config/admin.js`
+
+2. **Add environment variables** to your Strapi `.env`:
+ ```bash
+ CLIENT_URL=https://takerofnotes.com
+ PREVIEW_SECRET=preview-secret-key-change-this-in-production
+ ```
+
+3. **Update the preview secret** in both frontend and Strapi to match
+
+### 3. Content Type Setup
+
+The implementation supports these content type patterns:
+
+- **Global** (`api::global.global`) → `/` (homepage)
+- **Pages** (`api::page.page`) → `/{slug}`
+- **Articles** (`api::article.article`) → `/articles/{slug}`
+
+Add more content types by:
+1. Updating `getPreviewPathname()` in the Strapi config
+2. Creating corresponding route files in `/pages/`
+
+## How It Works
+
+### Preview Flow
+
+1. **Strapi Admin** → User clicks "Open Preview" on content
+2. **Preview Handler** → Generates preview URL with secret and parameters
+3. **Preview API** → Validates secret and redirects to content page with preview flags
+4. **Content Page** → Detects preview mode and fetches draft content
+5. **Preview Banner** → Shows preview status with exit option
+
+### URL Structure
+
+Preview URLs follow this pattern:
+```
+/preview?secret=xxx&path=/articles/my-article&status=draft&documentId=123&uid=api::article.article
+```
+
+Which redirects to:
+```
+/articles/my-article?preview=true&status=draft&documentId=123&uid=api::article.article
+```
+
+## File Structure
+
+```
+├── pages/
+│ ├── preview/ # Preview route handler
+│ │ ├── +Page.vue
+│ │ └── +data.js
+│ ├── @slug/ # Dynamic pages route
+│ │ ├── +Page.vue
+│ │ └── +data.js
+│ ├── articles/@slug/ # Dynamic articles route
+│ │ ├── +Page.vue
+│ │ └── +data.js
+│ ├── index/+data.js # Homepage with preview support
+│ └── +Layout.vue # Updated with preview components
+├── components/
+│ ├── PreviewBanner.vue # Preview mode indicator
+│ └── LivePreview.vue # Live Preview communication
+├── libs/
+│ └── strapi.js # Enhanced with preview support
+└── strapi-config-example.js # Strapi admin configuration
+```
+
+## Adding New Content Types
+
+To add preview support for a new content type:
+
+1. **Update Strapi Config** (`config/admin.js`):
+ ```js
+ case "api::my-content.my-content":
+ return `/my-content/${slug}`;
+ ```
+
+2. **Create Vike Route**:
+ ```
+ pages/my-content/@slug/
+ ├── +Page.vue
+ └── +data.js
+ ```
+
+3. **Implement Data Fetching**:
+ ```js
+ import { getBySlug, getByDocumentId } from '@/libs/strapi'
+
+ export const data = async (pageContext) => {
+ // Extract preview parameters
+ const isPreview = searchParams.preview === 'true'
+ const status = searchParams.status || 'published'
+
+ // Fetch with preview support
+ const response = await getBySlug('my-content', slug, {
+ isPreview,
+ status
+ })
+
+ return { content: response.data, isPreview, status }
+ }
+ ```
+
+## Security Notes
+
+- **Preview Secret**: Change the default secret in production
+- **CSP Headers**: Only allow embedding from your Strapi domain
+- **Environment Variables**: Use `VITE_` prefix for client-side access
+- **Validation**: Preview API validates secret before redirecting
+
+## Testing
+
+### Test Preview Mode
+
+1. Add `?preview=true&status=draft` to any URL
+2. Should see orange preview banner
+3. Content should be fetched with draft status
+
+### Test Preview Route
+
+Visit: `/preview?secret=your-secret&path=/&status=draft`
+Should redirect to: `/?preview=true&status=draft`
+
+## Live Preview (Strapi Growth/Enterprise)
+
+The implementation includes Live Preview communication:
+
+- **Real-time Updates**: Content changes in Strapi trigger page refresh
+- **Interactive Editing**: Double-click content to edit (with proper source maps)
+- **Source Maps**: Enable with `strapi-encode-source-maps` header
+
+## Troubleshooting
+
+### Preview Not Working
+
+- Check environment variables match between frontend and Strapi
+- Verify CSP headers allow iframe embedding
+- Ensure Strapi admin config is properly deployed
+
+### Content Not Loading
+
+- Check Strapi API authentication
+- Verify content type exists and has proper permissions
+- Check browser network tab for failed requests
+
+### Preview Banner Not Showing
+
+- Verify URL contains `preview=true` parameter
+- Check component import in Layout
+- Ensure data is being passed correctly
+
+## Development vs Production
+
+### Development
+- Use `http://localhost:3000` as CLIENT_URL
+- Preview works locally during development
+
+### Production
+- Update CLIENT_URL to your production domain
+- Ensure HTTPS for iframe embedding
+- Update preview secret for security
\ No newline at end of file
diff --git a/components/LivePreview.vue b/components/LivePreview.vue
new file mode 100644
index 0000000..9031bfb
--- /dev/null
+++ b/components/LivePreview.vue
@@ -0,0 +1,64 @@
+
+
+
+
+
+
diff --git a/components/PreviewBanner.vue b/components/PreviewBanner.vue
new file mode 100644
index 0000000..bd0c3aa
--- /dev/null
+++ b/components/PreviewBanner.vue
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
diff --git a/libs/strapi.js b/libs/strapi.js
index db5e1aa..f80e63f 100644
--- a/libs/strapi.js
+++ b/libs/strapi.js
@@ -5,7 +5,105 @@ export const strapiClient = strapi({
auth: import.meta.env.VITE_STRAPI_API_TOKEN,
})
-export const getGlobal = async () => {
- const globalManager = strapiClient.single('global')
- return await globalManager.find()
+/**
+ * Enhanced fetch function with preview support
+ * @param {string} contentType - The Strapi content type (e.g., 'global', 'articles')
+ * @param {object} options - Fetch options including preview settings
+ * @param {boolean} options.isPreview - Whether this is a preview request
+ * @param {string} options.status - Content status ('draft' or 'published')
+ * @param {boolean} options.isSingle - Whether this is a single type or collection
+ * @param {object} options.params - Additional query parameters
+ * @returns {Promise} Strapi response
+ */
+export const fetchWithPreview = async (contentType, options = {}) => {
+ const {
+ isPreview = false,
+ status = 'published',
+ isSingle = false,
+ params = {},
+ ...otherOptions
+ } = options
+
+ const queryParams = { ...params }
+ const headers = {}
+
+ // Add status parameter for draft content in preview mode
+ if (isPreview && status) {
+ queryParams.status = status
+ }
+
+ // Enable content source maps for Live Preview
+ if (isPreview) {
+ headers['strapi-encode-source-maps'] = 'true'
+ }
+
+ // Disable caching for preview requests
+ if (isPreview) {
+ headers['Cache-Control'] = 'no-cache'
+ }
+
+ try {
+ if (isSingle) {
+ const manager = strapiClient.single(contentType)
+ return await manager.find(queryParams, { headers, ...otherOptions })
+ } else {
+ const manager = strapiClient.collection(contentType)
+ return await manager.find(queryParams, { headers, ...otherOptions })
+ }
+ } catch (error) {
+ console.error(`Error fetching ${contentType}:`, error)
+ throw error
+ }
+}
+
+/**
+ * Fetch global content with preview support
+ */
+export const getGlobal = async (previewOptions = {}) => {
+ return await fetchWithPreview('global', {
+ isSingle: true,
+ ...previewOptions,
+ })
+}
+
+/**
+ * Fetch a collection item by slug with preview support
+ */
+export const getBySlug = async (contentType, slug, previewOptions = {}) => {
+ return await fetchWithPreview(contentType, {
+ isSingle: false,
+ params: {
+ filters: { slug: { $eq: slug } },
+ populate: '*',
+ },
+ ...previewOptions,
+ })
+}
+
+/**
+ * Fetch a single item by document ID (useful for preview)
+ */
+export const getByDocumentId = async (
+ contentType,
+ documentId,
+ previewOptions = {},
+) => {
+ const { isSingle = false } = previewOptions
+
+ if (isSingle) {
+ return await fetchWithPreview(contentType, {
+ isSingle: true,
+ params: { documentId },
+ ...previewOptions,
+ })
+ } else {
+ return await fetchWithPreview(contentType, {
+ isSingle: false,
+ params: {
+ filters: { documentId: { $eq: documentId } },
+ populate: '*',
+ },
+ ...previewOptions,
+ })
+ }
}
diff --git a/pages/+Head.vue b/pages/+Head.vue
index 9c6fa37..e3f6dcc 100644
--- a/pages/+Head.vue
+++ b/pages/+Head.vue
@@ -1,6 +1,11 @@
+
+
diff --git a/pages/+Layout.vue b/pages/+Layout.vue
index 1f730e7..250c771 100644
--- a/pages/+Layout.vue
+++ b/pages/+Layout.vue
@@ -1,8 +1,14 @@
The requested page could not be found. The requested article could not be found. Error: {{ error }} PageContext keys: {{ pageContext.join(', ') }} Redirecting to preview... If you are not redirected automatically, click here. Processing preview request...{{ page.title }}
+ Page not found
+ {{ article.title }}
+
+ Article not found
+ Preview Handler
+
+ Debug Information
+ {{ JSON.stringify(urlParsed, null, 2) }}
+
This should redirect to the homepage with preview parameters:
+ + Test Valid Preview + +This should show an unauthorized page:
+ + Test Invalid Preview + +This should show the homepage with preview banner:
+ + Test Preview Mode + +This should test dynamic route preview:
+ + Test Article Preview + +For reference, your Strapi config should generate URLs like this:
+https://takerofnotes.com/preview?secret=preview-secret-key-change-this-in-production&path=/articles/my-article&status=draft&documentId=123&uid=api::article.article
+ Make sure these are set in your .env file:
VITE_CLIENT_URL=https://takerofnotes.com +VITE_PREVIEW_SECRET=preview-secret-key-change-this-in-production+ +
And in your Strapi .env file:
CLIENT_URL=https://takerofnotes.com +PREVIEW_SECRET=preview-secret-key-change-this-in-production+