Skip to content

Fixed SSG pnpm generate errors #21

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,9 @@ TODO

# dist folder
dist/

# Directus Docker data directories
test-website/simple-cms/directus/database
test-website/simple-cms/directus/extensions
test-website/simple-cms/directus/templates
test-website/simple-cms/directus/uploads
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22
19 changes: 14 additions & 5 deletions test-website/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,20 @@ This test website is based on the Simple CMS Starter Template of [@directus-labs
## Setup Instructions

While you can also set up the Visual Editing test website with a Directus instance running in a
[Docker](https://directus.io/docs/getting-started/create-a-project#docker-installation) container, this guide describes
setting up a development environment using the official Directus repository.
[Docker](https://directus.io/docs/getting-started/create-a-project#docker-installation) container, this guide describes setting up a development environment using the official Directus repository.

### Set up your Directus Dev Instance
### Set up Directus using docker compose
1. Create the folders needed for the Docker container:
```sh
cd ./test-website/simple-cms/directus
mkdir database/ extensions/ templates/ uploads/
```
2. Run `docker compose up` to start the Directus container.
3. Login to Directus Studio [http://localhost:8080](http://localhost:8080) with the admin email and password ing `./simple-cms/directus/docker-compose.yml`, create a token for your user and have it ready to add to your `.env` file.

> Note the file [`/simple-cms/directus/docker-compose.yml`](./simple-cms/directus/docker-compose.yml) contains the configuration described below.

### Set up your Directus Dev Instance (if you are not using Docker)

1. Clone the official [Directus GitHub repository](https://github.com/directus/directus) and make sure you have the
dependencies installed (`pnpm i`) and build everything (`pnpm build`)!
Expand Down Expand Up @@ -70,8 +80,7 @@ setting up a development environment using the official Directus repository.
cd test-website/template && npm run setup-directus
```

On Windows, use the following command and make sure to replace `<directus-url>` with your Directus URL and
`<your-token>` with the token you generated earlier.
If the command above fails, or on Windows, use the following command and make sure to replace `<directus-url>` with your Directus URL and `<your-token>` with the token you generated earlier.

```sh
cd test-website/template && npx directus-template-cli@latest apply -p --directusUrl=<directus-url> --templateLocation=. --templateType=local --directusToken=<your-token>
Expand Down
34 changes: 34 additions & 0 deletions test-website/simple-cms/directus/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
services:
directus:
image: directus/directus:latest
ports:
- 8080:8055
volumes:
- ./database:/directus/database
- ./uploads:/directus/uploads
- ./extensions:/directus/extensions
- ./templates:/directus/templates
environment:
SECRET: "replace-with-secure-random-value"
ADMIN_EMAIL: "[email protected]"
ADMIN_PASSWORD: "d1r3ctu5"

DB_CLIENT: "sqlite3"
DB_FILENAME: "/directus/database/data.db"
WEBSOCKETS_ENABLED: "true"

# Required for Visual Editor
CONTENT_SECURITY_POLICY_DIRECTIVES__FRAME_SRC: "http://localhost:*"
CACHE_ENABLED: true
CACHE_AUTO_PURGE: true
CACHE_TTL: 1d
CORS_ENABLED: true
CORS_ORIGIN: http://localhost:3000

# Make sure to set this in production
# (see https://docs.directus.io/self-hosted/config-options#general)
# PUBLIC_URL: "https://directus.example.com"

# Environment variables can also be defined in a file (for example `.env`):
# env_file:
# - .env
1 change: 1 addition & 0 deletions test-website/simple-cms/nuxt/.nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22
5 changes: 2 additions & 3 deletions test-website/simple-cms/nuxt/app/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ useHead({
</script>

<template>
<div>
<NuxtLayout />
</div>
<NuxtRouteAnnouncer />
<NuxtLayout />
</template>
15 changes: 9 additions & 6 deletions test-website/simple-cms/nuxt/app/components/base/BaseBlock.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
<script setup lang="ts">
import { computed } from 'vue';
import Hero from '~/components/block/Hero.vue';
import RichText from '~/components/block/RichText.vue';
import Gallery from '~/components/block/Gallery.vue';
import Pricing from '~/components/block/Pricing.vue';
import Posts from '~/components/block/Posts.vue';
import Form from '~/components/block/FormBlock.vue';
import { defineAsyncComponent } from 'vue';

// import using `defineAsyncComponent` to load components only when needed
const Hero = defineAsyncComponent(() => import('~/components/block/Hero.vue'));
const RichText = defineAsyncComponent(() => import('~/components/block/RichText.vue'));
const Gallery = defineAsyncComponent(() => import('~/components/block/Gallery.vue'));
const Pricing = defineAsyncComponent(() => import('~/components/block/Pricing.vue'));
const Posts = defineAsyncComponent(() => import('~/components/block/Posts.vue'));
const Form = defineAsyncComponent(() => import('~/components/block/FormBlock.vue'));

interface BaseBlockProps {
block: {
Expand Down
63 changes: 58 additions & 5 deletions test-website/simple-cms/nuxt/app/components/base/SearchModel.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
<script setup lang="ts">
import { ref, onMounted, watch, nextTick } from 'vue';
import { useRouter } from 'vue-router';
import { useNuxtApp } from '#app';
import { useDebounceFn } from '@vueuse/core';
import { Search } from 'lucide-vue-next';
import { readItems } from '@directus/sdk';
import type { Page, Post } from '~~/shared/types/schema'; // Assuming types are here

type SearchResult = {
id: string;
Expand All @@ -18,6 +21,7 @@ const results = ref<SearchResult[]>([]);
const loading = ref(false);
const searched = ref(false);
const router = useRouter();
const { $directus } = useNuxtApp();

const fetchResults = async (search: string) => {
if (search.length < 3) {
Expand All @@ -30,13 +34,62 @@ const fetchResults = async (search: string) => {
searched.value = true;

try {
const data = await $fetch<SearchResult[]>('/api/search', {
params: { search },
});
// Explicitly type the results from Promise.all
const [pages, posts] = (await Promise.all([
$directus.request(
readItems('pages', {
filter: {
_or: [
{ title: { _contains: search } },
{ description: { _contains: search } },
{ permalink: { _contains: search } },
],
},
fields: ['id', 'title', 'description', 'permalink'],
}),
),
$directus.request(
readItems('posts', {
filter: {
_and: [
{ status: { _eq: 'published' } }, // Ensure only published posts are searched client-side
{
_or: [
{ title: { _contains: search } },
{ description: { _contains: search } },
{ slug: { _contains: search } },
{ content: { _contains: search } },
],
},
],
},
fields: ['id', 'title', 'description', 'slug', 'content', 'status'],
}),
),
])) as [Page[], Post[]]; // Assert the types here

results.value = [...data];
const combinedResults: SearchResult[] = [
...pages.map((page) => ({
id: page.id,
title: page.title ?? '',
description: page.description ?? '',
type: 'Page',
link: `/${page.permalink?.replace(/^\/+/, '') ?? ''}`,
content: '', // Pages don't have searchable content field here
})),
...posts.map((post) => ({
id: post.id,
title: post.title ?? '',
description: post.description ?? '',
type: 'Post',
link: `/blog/${post.slug ?? ''}`,
content: post.content ?? '',
})),
];

results.value = combinedResults;
await nextTick();
} catch {
} catch (error) {
results.value = [];
} finally {
loading.value = false;
Expand Down
40 changes: 34 additions & 6 deletions test-website/simple-cms/nuxt/app/components/block/Posts.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<script setup lang="ts">
import { ref, computed } from 'vue';
import { useRoute, useRouter, useAsyncData } from 'nuxt/app';
import { useRoute, useRouter, useAsyncData, useNuxtApp } from 'nuxt/app';
import { ChevronsLeft, ChevronsRight, ChevronLeft, ChevronRight } from 'lucide-vue-next';
import { apply, setAttr } from '@directus/visual-editing';
import { readItems } from '@directus/sdk';

interface Post {
id: string;
Expand All @@ -25,22 +26,49 @@ const props = defineProps<PostsProps>();

const route = useRoute();
const router = useRouter();
const { $directus } = useNuxtApp();

const perPage = props.data.limit || 6;
const currentPage = ref(Number(route.query.page) || 1);

const { data: totalPagesData, refresh: totalPagesRefresh } = await useAsyncData('posts-total-pages', () =>
$fetch<{ total: number }>('/api/posts/count'),
);
const { data: totalPagesData, refresh: totalPagesRefresh } = await useAsyncData('posts-total-pages', async () => {
try {
const response = await $directus.request(
readItems('posts', {
aggregate: { count: '*' },
filter: { status: { _eq: 'published' } },
}),
);
return { total: Number(response[0]?.count) || 0 };
} catch (err) {
// console.error('Failed to fetch total post count:', err);
throw createError({ statusCode: 500, message: 'Failed to fetch total post count' + err });
}
});
const totalPages = computed(() => Math.ceil((totalPagesData.value?.total || 0) / perPage));

const {
data: paginatedPosts,
error: fetchError,
refresh: postsRefresh,
} = await useAsyncData(
} = await useAsyncData<Post[]>(
'paginated-posts',
() => $fetch<Post[]>('/api/posts', { query: { page: currentPage.value, limit: perPage } }),
async () => {
try {
return await $directus.request<Post[]>(
readItems('posts', {
limit: perPage,
page: currentPage.value,
sort: ['-published_at'],
fields: ['id', 'title', 'description', 'slug', 'image'],
filter: { status: { _eq: 'published' } },
}),
);
} catch (err) {
// console.error('Failed to fetch paginated posts:', err);
throw createError({ statusCode: 500, message: 'Failed to fetch paginated posts' + err });
}
},
{ watch: [currentPage] },
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
<script setup lang="ts">
import { useField } from 'vee-validate';
import Input from '~/components/ui/input/Input.vue';
import { Textarea } from '~/components/ui/textarea';
import CheckboxField from './fields/CheckboxField.vue';
import CheckboxGroupField from './fields/CheckboxGroupField.vue';
import RadioGroupField from './fields/RadioGroupField.vue';
import SelectField from './fields/SelectField.vue';
import FileUploadField from './fields/FileUploadField.vue';
import { Info } from 'lucide-vue-next';
import type { FormField } from '../../../shared/types/schema';

const Input = defineAsyncComponent(() => import('~/components/ui/input/Input.vue'));
const Textarea = defineAsyncComponent(() => import('~/components/ui/textarea/Textarea.vue'));
const CheckboxField = defineAsyncComponent(() => import('./fields/CheckboxField.vue'));
const CheckboxGroupField = defineAsyncComponent(() => import('./fields/CheckboxGroupField.vue'));
const RadioGroupField = defineAsyncComponent(() => import('./fields/RadioGroupField.vue'));
const SelectField = defineAsyncComponent(() => import('./fields/SelectField.vue'));
const FileUploadField = defineAsyncComponent(() => import('./fields/FileUploadField.vue'));

const props = defineProps<{ field: FormField }>();
const { value, errorMessage } = useField(props.field.name ?? '');

Expand Down
Loading