8 Commits

18 changed files with 586 additions and 375 deletions

View File

@@ -1,5 +1,15 @@
# Changelog
## v0.1.5
[compare changes](https://gitea.websimple.com/templates/wp-headless/compare/v0.1.4...v0.1.5)
### 🩹 Fixes
- Auth server utils upgrade to latest nuxt-graphql (fd61895)
- Immutable extractNodes (baa3061)
- Type issue with NodePage (3b706c0)
## v0.1.4
[compare changes](https://gitea.websimple.com/templates/wp-headless/compare/v0.1.2...v0.1.4)

View File

@@ -1,3 +1,12 @@
# Moonshine
Headless WordPress theme based on Nuxt.
Thème WordPress en headless basé sur Nuxt.
## Varaibles d'environnement
| Nom | Description | Exemple | Requise |
|-----|-------------|---------|---------|
| `NUXT_SESSION_PASSWORD` | Clé secrète pour l'authentification | `date \| md5sum` | ✅ |
| `NUXT_WP_URL` | URL du backend WordPress | https://wp.exemple.com | ✅ |
| `NUXT_SITE_URL` | URL du frontend Nuxt | https://www.example.com | |
| `NUXT_SITE_ENV` | Environnement | staging \| production | |

View File

@@ -0,0 +1,60 @@
{
"key": "group_options_site",
"title": "Options - Site",
"fields": [
{
"key": "field_697220310aaaf",
"label": "Email",
"name": "email",
"aria-label": "",
"type": "email",
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"allow_in_bindings": 0,
"placeholder": "",
"prepend": "",
"append": "",
"show_in_graphql": 1,
"graphql_description": "",
"graphql_field_name": "email",
"graphql_non_null": 1
}
],
"location": [
[
{
"param": "options_page",
"operator": "==",
"value": "site-options"
}
]
],
"menu_order": 0,
"position": "normal",
"style": "seamless",
"label_placement": "top",
"instruction_placement": "label",
"hide_on_screen": "",
"active": true,
"description": "",
"show_in_rest": 0,
"display_title": "",
"acfe_autosync": [
"json"
],
"acfe_form": 0,
"show_in_graphql": 1,
"graphql_field_name": "GroupSite",
"map_graphql_types_from_location_rules": 0,
"graphql_types": "",
"acfe_meta": "",
"acfe_note": "",
"modified": 1769087407
}

View File

@@ -0,0 +1,25 @@
{
"key": "ui_options_page_site",
"title": "Options du site",
"active": true,
"menu_order": 0,
"page_title": "Options du site",
"menu_slug": "site-options",
"parent_slug": "options-general.php",
"advanced_configuration": 1,
"icon_url": "",
"menu_title": "",
"position": "",
"redirect": false,
"description": "",
"menu_icon": [],
"update_button": "Mise à jour",
"updated_message": "Options mises à jours",
"capability": "edit_posts",
"data_storage": "options",
"post_id": "",
"autoload": 0,
"show_in_graphql": 1,
"graphql_type_name": "OptionsSite",
"modified": 1769086997
}

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import type { BuilderSectionsFragment } from "#graphql/fragments";
import type { BuilderSectionsFragment } from "#graphql/operations";
const props = defineProps<BuilderSectionsFragment>();
const sections = computed(() => {

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import type { LayoutContainedFragment } from "#graphql/fragments";
import type { LayoutContainedFragment } from "#graphql/operations";
import { tv, type VariantProps } from "tailwind-variants";
const props = defineProps<LayoutContainedFragment>();

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import type { NodePageFragment } from "#graphql/fragments";
import type { NodePageFragment } from "#graphql/operations";
defineProps<NodePageFragment>();
</script>
@@ -9,6 +9,6 @@ defineProps<NodePageFragment>();
<h1 v-if="!isFrontPage" class="font-bold text-4xl">
{{ title }}
</h1>
<BuilderSections v-bind="groupPostPage" />
<BuilderSections :sections="groupPostPage?.sections || []" />
</div>
</template>

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import type { SectionTextBlockFragment } from "#graphql/fragments";
import type { SectionTextBlockFragment } from "#graphql/operations";
defineProps<SectionTextBlockFragment>();
</script>

View File

@@ -13,7 +13,7 @@ login( input: { provider: PASSWORD, credentials: { username: $username, password
authToken
refreshToken
user {
...AuthUser
... AuthUser
}
}
}

View File

@@ -1,6 +1,10 @@
fragment GeneralSettings on GeneralSettings {
title
description
}
query GeneralSettings {
generalSettings {
title
description
... GeneralSettings
}
}

View File

@@ -0,0 +1,11 @@
fragment SiteOptions on GroupSite_Fields {
email
}
query OptionsSite {
optionsSite {
groupSite {
... SiteOptions
}
}
}

View File

@@ -4,4 +4,5 @@
require_once __DIR__ . '/includes/core/theme-setup.php';
// Vendors
require_once __DIR__ . '/includes/vendors/acf.php';
require_once __DIR__ . '/includes/vendors/tinymce.php';

View File

@@ -0,0 +1,15 @@
<?php
// Disable ACF / ACFE modules
add_filter( 'acf/settings/enable_post_types', '__return_false' );
add_action( 'acf/init', 'moonshine_acf_init' );
function moonshine_acf_init() {
acf_update_setting( 'acfe/modules/block_types', false );
acf_update_setting( 'acfe/modules/categories', false );
acf_update_setting( 'acfe/modules/forms', false );
acf_update_setting( 'acfe/modules/options', false );
acf_update_setting( 'acfe/modules/options_pages', false );
acf_update_setting( 'acfe/modules/post_types', false );
acf_update_setting( 'acfe/modules/taxonomies', false );
acf_update_setting( 'acfe/modules/templates', false );
}

View File

@@ -1,7 +1,7 @@
{
"name": "@lewebsimple/moonshine",
"description": "Headless WordPress theme based on Nuxt.",
"version": "0.1.4",
"version": "0.1.5",
"type": "module",
"private": true,
"scripts": {
@@ -17,7 +17,7 @@
},
"dependencies": {
"@iconify-json/lucide": "^1.2.86",
"@lewebsimple/nuxt-graphql": "^0.5.2",
"@lewebsimple/nuxt-graphql": "^0.5.11",
"@nuxt/ui": "4.3.0",
"@nuxtjs/seo": "^3.3.0",
"jwt-decode": "^4.0.0",

File diff suppressed because it is too large Load Diff

View File

@@ -51,6 +51,21 @@ interface AcfFieldGroupFields {
fieldGroupName: String @deprecated(reason: "Use __typename instead")
}
"""Options Page registered by ACF"""
interface AcfOptionsPage implements Node {
"""The globally unique ID for the object"""
id: ID!
""""""
menuTitle: String
""""""
pageTitle: String
""""""
parentId: String
}
"""The Headless Login authentication data."""
type AuthenticationData {
"""A new authentication token to use in future requests."""
@@ -3480,6 +3495,32 @@ interface GroupPostPage_Fields implements AcfFieldGroup & AcfFieldGroupFields &
sections: [GroupAbstractBuilderSections_Layout]
}
"""
The &quot;GroupSite&quot; Field Group. Added to the Schema by &quot;WPGraphQL for ACF&quot;.
"""
type GroupSite implements AcfFieldGroup & AcfFieldGroupFields & GroupSite_Fields {
"""
Field of the &quot;email&quot; Field Type added to the schema as part of the &quot;GroupSite&quot; Field Group
"""
email: String!
"""The name of the field group"""
fieldGroupName: String @deprecated(reason: "Use __typename instead")
}
"""
Interface representing fields of the ACF &quot;GroupSite&quot; Field Group
"""
interface GroupSite_Fields implements AcfFieldGroup & AcfFieldGroupFields {
"""
Field of the &quot;email&quot; Field Type added to the schema as part of the &quot;GroupSite&quot; Field Group
"""
email: String!
"""The name of the field group"""
fieldGroupName: String @deprecated(reason: "Use __typename instead")
}
"""
Content that can be organized in a parent-child structure. Provides fields for navigating up and down the hierarchy and maintaining structured relationships.
"""
@@ -5926,6 +5967,23 @@ interface OneToOneConnection implements Edge {
node: Node!
}
type OptionsSite implements AcfOptionsPage & Node & WithAcfGroupSite {
"""Fields of the GroupSite ACF Field Group"""
groupSite: GroupSite
"""The globally unique ID for the object"""
id: ID!
""""""
menuTitle: String
""""""
pageTitle: String
""""""
parentId: String
}
"""
Sort direction for ordered results. Determines whether items are returned in ascending or descending order.
"""
@@ -8436,7 +8494,7 @@ interface Previewable {
}
"""The root entry point into the Graph"""
type Query {
type Query implements WithAcfOptionsPageOptionsSite {
"""Entry point to get all settings for the site"""
allSettings: Settings
@@ -8721,6 +8779,9 @@ type Query {
uri: String!
): UniformResourceIdentifiable
""""""
optionsSite: OptionsSite
"""An object of the page Type. """
page(
"""
@@ -14080,6 +14141,20 @@ interface WithAcfGroupPostPage {
groupPostPage: GroupPostPage
}
"""
Provides access to fields of the &quot;GroupSite&quot; ACF Field Group via the &quot;groupSite&quot; field
"""
interface WithAcfGroupSite {
"""Fields of the GroupSite ACF Field Group"""
groupSite: GroupSite
}
"""Access point for the &quot;OptionsSite&quot; ACF Options Page"""
interface WithAcfOptionsPageOptionsSite {
""""""
optionsSite: OptionsSite
}
"""The writing setting type"""
type WritingSettings {
"""Catégorie darticle par défaut."""

View File

@@ -2,15 +2,15 @@ import type { H3Event } from "h3";
import { GraphQLClient } from "graphql-request";
import { jwtDecode } from "jwt-decode";
import type { User } from "#auth-utils";
import type { AuthUserFragment } from "#graphql/fragments";
import { AuthRefreshTokenDocument, type AuthLoginResult } from "#graphql/operations";
import type { AuthUserFragment, AuthLoginMutationResult } from "#graphql/operations";
import { AuthRefreshTokenDocument } from "#graphql/operations";
// Handle login result and store user session
export async function handleLogin(event: H3Event, loginData: AuthLoginResult) {
if (!loginData?.login) {
export async function handleLogin(event: H3Event, loginResult: AuthLoginMutationResult) {
if (!loginResult?.login) {
return false;
}
const { user, authToken, refreshToken } = loginData.login;
const { user, authToken, refreshToken } = loginResult.login;
if (!user || !authToken || !refreshToken) {
return false;
}

View File

@@ -1,4 +1,4 @@
// Helper: Extracts nodes from a GraphQL connection object, returning an empty array if nodes are absent.
export function extractNodes<T>(connection: { nodes?: T[] } | null | undefined): T[] {
return connection?.nodes || [] as T[];
export function extractNodes<T>(connection: { nodes?: readonly T[] } | null | undefined): readonly T[] {
return connection?.nodes ?? [];
}