feat(i18n,drupal): consolidate locale codes and add Drupal infrastructure
Some checks failed
Deploy to Production / deploy (push) Failing after 33s

i18n changes:
- Remove qpv locale code, standardize on vp-VL for Viossa
- Remove wp-VL duplicate, keep locale structure clean
- Update langcode types in DrupalService from 'qpv' to 'vp-VL'

Drupal integration additions:
- Add PreviewDrupalService for viewing unpublished content
- Add authentication system with Pinia store and composables
- Add meta tag management system (SEO, OpenGraph, Twitter)
- Add route guards for protected admin routes
- Add login/preview/admin pages

Locale file changes:
- Deleted: qpv.ftl (duplicate of vp-VL.ftl)
- Kept: en_US.ftl, es_LA.ftl, vp_VL.ftl, wp_VL.ftl
This commit is contained in:
BarnacleBoy 2026-05-15 07:07:28 +00:00
parent 22636f025f
commit 90c7e0cddb
26 changed files with 4833 additions and 41 deletions

View file

@ -24,19 +24,21 @@ interface FetchOptions {
interface NodeListParams extends JsonApiParams {
contentType: ContentType;
langcode?: "en" | "qpv"; // Support English and Viossa translations
langcode?: "en" | "vp-VL"; // Support English and Viossa translations
includeUnpublished?: boolean; // New parameter for preview mode
}
interface NodeParams extends JsonApiParams {
id: string;
contentType: ContentType;
include?: string[];
langcode?: "en" | "qpv";
langcode?: "en" | "vp-VL";
includeUnpublished?: boolean; // New parameter for preview mode
}
interface TaxonomyParams {
vocabulary: Vocabulary;
langcode?: "en" | "qpv";
langcode?: "en" | "vp-VL";
}
/**
@ -50,6 +52,7 @@ interface TaxonomyParams {
*/
export class DrupalService {
private baseUrl: string;
private authHeaders: Record<string, string> = {};
constructor(options: FetchOptions = {}) {
// Use environment variable or default to proxy path
@ -68,10 +71,24 @@ export class DrupalService {
}
}
/**
* Set authentication headers for subsequent requests
*/
setAuthHeaders(headers: Record<string, string>) {
this.authHeaders = { ...headers };
}
/**
* Clear authentication headers
*/
clearAuthHeaders() {
this.authHeaders = {};
}
/**
* Build query string from JsonApiParams
*/
private buildQueryString(params: JsonApiParams = {}, langcode?: string): string {
private buildQueryString(params: JsonApiParams = {}, langcode?: string, includeUnpublished?: boolean): string {
const queryParts: string[] = [];
// Language filter for translated content
@ -79,6 +96,11 @@ export class DrupalService {
queryParts.push(`filter[langcode]=${langcode}`);
}
// Include unpublished content in preview mode
if (includeUnpublished) {
queryParts.push(`filter[status][condition][path]=status&filter[status][condition][operator]=IS NULL&filter[status][condition][value]=1`);
}
// Include relationships
if (params.include?.length) {
queryParts.push(`include=${params.include.join(",")}`);
@ -132,14 +154,18 @@ export class DrupalService {
return queryParts.length > 0 ? `?${queryParts.join("&")}` : "";
}
/**
/**
* Fetch wrapper with error handling
*/
private async fetchJson<T>(url: string): Promise<T> {
const headers: Record<string, string> = {
"Accept": "application/vnd.api+json",
...this.authHeaders,
};
const response = await fetch(url, {
headers: {
"Accept": "application/vnd.api+json",
},
headers,
credentials: 'include', // Include cookies for session auth
});
if (!response.ok) {
@ -150,7 +176,7 @@ export class DrupalService {
const errorJson = JSON.parse(errorBody);
errors = errorJson.errors || [];
} catch {
// NotJSON error
// Not JSON error
}
throw new DrupalApiError(
@ -169,8 +195,8 @@ export class DrupalService {
async getNodes<T extends DrupalNodeAttributes = DrupalNodeAttributes>(
params: NodeListParams
): Promise<JsonApiDocument<T>> {
const { contentType, langcode, ...queryParams } = params;
const queryString = this.buildQueryString(queryParams, langcode);
const { contentType, langcode, includeUnpublished, ...queryParams } = params;
const queryString = this.buildQueryString(queryParams, langcode, includeUnpublished);
const url = `${this.baseUrl}/node/${contentType}${queryString}`;
return this.fetchJson<JsonApiDocument<T>>(url);
@ -183,9 +209,9 @@ export class DrupalService {
async getNode<T extends DrupalNodeAttributes = DrupalNodeAttributes>(
params: NodeParams
): Promise<JsonApiDocument<T>> {
const { id, contentType, include, langcode } = params;
const { id, contentType, include, langcode, includeUnpublished } = params;
const queryParams: JsonApiParams = include ? { include } : {};
const queryString = this.buildQueryString(queryParams, langcode);
const queryString = this.buildQueryString(queryParams, langcode, includeUnpublished);
const url = `${this.baseUrl}/node/${contentType}/${id}${queryString}`;
return this.fetchJson<JsonApiDocument<T>>(url);