fix: add missing readBooks translations
Some checks failed
Dev Deployment (Zero Downtime) / deploy-dev (push) Has been cancelled
Some checks failed
Dev Deployment (Zero Downtime) / deploy-dev (push) Has been cancelled
This commit is contained in:
@@ -20,6 +20,8 @@ export type ProjectDetailData = {
|
|||||||
date: string;
|
date: string;
|
||||||
github?: string | null;
|
github?: string | null;
|
||||||
live?: string | null;
|
live?: string | null;
|
||||||
|
button_live_label?: string | null;
|
||||||
|
button_github_label?: string | null;
|
||||||
imageUrl?: string | null;
|
imageUrl?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -205,7 +207,7 @@ export default function ProjectDetailClient({
|
|||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="flex items-center justify-between w-full px-4 py-3 bg-stone-900 text-stone-50 rounded-xl font-medium hover:bg-stone-800 hover:scale-[1.02] transition-all shadow-md group"
|
className="flex items-center justify-between w-full px-4 py-3 bg-stone-900 text-stone-50 rounded-xl font-medium hover:bg-stone-800 hover:scale-[1.02] transition-all shadow-md group"
|
||||||
>
|
>
|
||||||
<span>{tDetail("liveDemo")}</span>
|
<span>{project.button_live_label || tDetail("liveDemo")}</span>
|
||||||
<ExternalLink size={18} className="group-hover:translate-x-1 transition-transform" />
|
<ExternalLink size={18} className="group-hover:translate-x-1 transition-transform" />
|
||||||
</a>
|
</a>
|
||||||
) : (
|
) : (
|
||||||
@@ -221,7 +223,7 @@ export default function ProjectDetailClient({
|
|||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="flex items-center justify-between w-full px-4 py-3 bg-white border border-stone-200 text-stone-700 rounded-xl font-medium hover:bg-stone-50 hover:text-stone-900 hover:border-stone-300 transition-all shadow-sm group"
|
className="flex items-center justify-between w-full px-4 py-3 bg-white border border-stone-200 text-stone-700 rounded-xl font-medium hover:bg-stone-50 hover:text-stone-900 hover:border-stone-300 transition-all shadow-sm group"
|
||||||
>
|
>
|
||||||
<span>{tDetail("viewSource")}</span>
|
<span>{project.button_github_label || tDetail("viewSource")}</span>
|
||||||
<GithubIcon size={18} className="group-hover:rotate-12 transition-transform" />
|
<GithubIcon size={18} className="group-hover:rotate-12 transition-transform" />
|
||||||
</a>
|
</a>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -453,8 +453,11 @@ export async function getBookReviews(locale: string): Promise<BookReview[] | nul
|
|||||||
book_image
|
book_image
|
||||||
rating
|
rating
|
||||||
finished_at
|
finished_at
|
||||||
translations(filter: { languages_code: { code: { _eq: "${directusLocale}" } } }) {
|
translations {
|
||||||
review
|
review
|
||||||
|
languages_code {
|
||||||
|
code
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -471,16 +474,23 @@ export async function getBookReviews(locale: string): Promise<BookReview[] | nul
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return reviews.map((item: any) => ({
|
return reviews.map((item: any) => {
|
||||||
id: item.id,
|
// Filter die passende Übersetzung im Code
|
||||||
hardcover_id: item.hardcover_id || undefined,
|
const translation = item.translations?.find(
|
||||||
book_title: item.book_title,
|
(t: any) => t.languages_code?.code === directusLocale
|
||||||
book_author: item.book_author,
|
) || item.translations?.[0]; // Fallback auf die erste Übersetzung falls locale nicht passt
|
||||||
book_image: item.book_image || undefined,
|
|
||||||
rating: typeof item.rating === 'number' ? item.rating : parseInt(item.rating) || 0,
|
return {
|
||||||
review: item.translations?.[0]?.review || undefined,
|
id: item.id,
|
||||||
finished_at: item.finished_at || undefined,
|
hardcover_id: item.hardcover_id || undefined,
|
||||||
}));
|
book_title: item.book_title,
|
||||||
|
book_author: item.book_author,
|
||||||
|
book_image: item.book_image || undefined,
|
||||||
|
rating: typeof item.rating === 'number' ? item.rating : parseInt(item.rating) || 0,
|
||||||
|
review: translation?.review || undefined,
|
||||||
|
finished_at: item.finished_at || undefined,
|
||||||
|
};
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Failed to fetch book reviews (${locale}):`, error);
|
console.error(`Failed to fetch book reviews (${locale}):`, error);
|
||||||
return null;
|
return null;
|
||||||
@@ -503,6 +513,8 @@ export interface Project {
|
|||||||
future_improvements?: string;
|
future_improvements?: string;
|
||||||
github_url?: string;
|
github_url?: string;
|
||||||
live_url?: string;
|
live_url?: string;
|
||||||
|
button_live_label?: string;
|
||||||
|
button_github_label?: string;
|
||||||
image_url?: string;
|
image_url?: string;
|
||||||
demo_video_url?: string;
|
demo_video_url?: string;
|
||||||
performance_metrics?: string;
|
performance_metrics?: string;
|
||||||
@@ -592,6 +604,8 @@ export async function getProjects(
|
|||||||
content
|
content
|
||||||
meta_description
|
meta_description
|
||||||
keywords
|
keywords
|
||||||
|
button_live_label
|
||||||
|
button_github_label
|
||||||
languages_code { code }
|
languages_code { code }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -644,6 +658,8 @@ export async function getProjects(
|
|||||||
future_improvements: proj.future_improvements,
|
future_improvements: proj.future_improvements,
|
||||||
github_url: proj.github,
|
github_url: proj.github,
|
||||||
live_url: proj.live,
|
live_url: proj.live,
|
||||||
|
button_live_label: trans.button_live_label,
|
||||||
|
button_github_label: trans.button_github_label,
|
||||||
image_url: proj.image_url,
|
image_url: proj.image_url,
|
||||||
demo_video_url: proj.demo_video,
|
demo_video_url: proj.demo_video,
|
||||||
performance_metrics: proj.performance_metrics,
|
performance_metrics: proj.performance_metrics,
|
||||||
@@ -703,6 +719,8 @@ export async function getProjectBySlug(
|
|||||||
content
|
content
|
||||||
meta_description
|
meta_description
|
||||||
keywords
|
keywords
|
||||||
|
button_live_label
|
||||||
|
button_github_label
|
||||||
languages_code { code }
|
languages_code { code }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -755,6 +773,8 @@ export async function getProjectBySlug(
|
|||||||
future_improvements: proj.future_improvements,
|
future_improvements: proj.future_improvements,
|
||||||
github_url: proj.github,
|
github_url: proj.github,
|
||||||
live_url: proj.live,
|
live_url: proj.live,
|
||||||
|
button_live_label: trans.button_live_label,
|
||||||
|
button_github_label: trans.button_github_label,
|
||||||
image_url: proj.image_url,
|
image_url: proj.image_url,
|
||||||
demo_video_url: proj.demo_video,
|
demo_video_url: proj.demo_video,
|
||||||
screenshots: parseTags(proj.screenshots),
|
screenshots: parseTags(proj.screenshots),
|
||||||
|
|||||||
@@ -65,9 +65,9 @@
|
|||||||
"progress": "Fortschritt"
|
"progress": "Fortschritt"
|
||||||
},
|
},
|
||||||
"readBooks": {
|
"readBooks": {
|
||||||
"title": "Gelesen",
|
"title": "Gelesene Bücher",
|
||||||
"finishedAt": "Beendet",
|
"finishedAt": "Beendet am",
|
||||||
"showMore": "{count} weitere",
|
"showMore": "{count} weitere anzeigen",
|
||||||
"showLess": "Weniger anzeigen"
|
"showLess": "Weniger anzeigen"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
66
scripts/atomic-setup-book-reviews.js
Normal file
66
scripts/atomic-setup-book-reviews.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
require('dotenv').config();
|
||||||
|
const DIRECTUS_URL = process.env.DIRECTUS_URL || 'https://cms.dk0.dev';
|
||||||
|
const DIRECTUS_TOKEN = process.env.DIRECTUS_STATIC_TOKEN;
|
||||||
|
|
||||||
|
async function api(endpoint, method = 'POST', body = null) {
|
||||||
|
const res = await fetch(`${DIRECTUS_URL}/${endpoint}`, {
|
||||||
|
method,
|
||||||
|
headers: { 'Authorization': `Bearer ${DIRECTUS_TOKEN}`, 'Content-Type': 'application/json' },
|
||||||
|
body: body ? JSON.stringify(body) : null
|
||||||
|
});
|
||||||
|
return res.ok ? await res.json() : { error: true, status: res.status };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function atomicSetup() {
|
||||||
|
console.log('🚀 Starte atomares Setup...');
|
||||||
|
|
||||||
|
// 1. Die Haupt-Collection mit allen Feldern in EINEM Request
|
||||||
|
const setup = await api('collections', 'POST', {
|
||||||
|
collection: 'book_reviews',
|
||||||
|
schema: {},
|
||||||
|
meta: { icon: 'import_contacts', display_template: '{{book_title}}' },
|
||||||
|
fields: [
|
||||||
|
{ field: 'id', type: 'integer', schema: { is_primary_key: true, has_auto_increment: true } },
|
||||||
|
{ field: 'status', type: 'string', schema: { default_value: 'draft' }, meta: { interface: 'select-dropdown' } },
|
||||||
|
{ field: 'book_title', type: 'string', schema: {}, meta: { interface: 'input' } },
|
||||||
|
{ field: 'book_author', type: 'string', schema: {}, meta: { interface: 'input' } },
|
||||||
|
{ field: 'book_image', type: 'string', schema: {}, meta: { interface: 'input' } },
|
||||||
|
{ field: 'rating', type: 'integer', schema: {}, meta: { interface: 'rating' } },
|
||||||
|
{ field: 'hardcover_id', type: 'string', schema: { is_unique: true }, meta: { interface: 'input' } },
|
||||||
|
{ field: 'finished_at', type: 'date', schema: {}, meta: { interface: 'datetime' } }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (setup.error) { console.error('Fehler bei Haupt-Collection:', setup); return; }
|
||||||
|
console.log('✅ Haupt-Collection steht.');
|
||||||
|
|
||||||
|
// 2. Die Übersetzungs-Collection
|
||||||
|
await api('collections', 'POST', {
|
||||||
|
collection: 'book_reviews_translations',
|
||||||
|
schema: {},
|
||||||
|
meta: { hidden: true },
|
||||||
|
fields: [
|
||||||
|
{ field: 'id', type: 'integer', schema: { is_primary_key: true, has_auto_increment: true } },
|
||||||
|
{ field: 'book_reviews_id', type: 'integer', schema: {} },
|
||||||
|
{ field: 'languages_code', type: 'string', schema: {} },
|
||||||
|
{ field: 'review', type: 'text', schema: {}, meta: { interface: 'input-rich-text-html' } }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
console.log('✅ Übersetzungstabelle steht.');
|
||||||
|
|
||||||
|
// 3. Die Relationen (Der Kleber)
|
||||||
|
await api('relations', 'POST', { collection: 'book_reviews_translations', field: 'book_reviews_id', related_collection: 'book_reviews', meta: { one_field: 'translations' }, schema: { on_delete: 'CASCADE' } });
|
||||||
|
await api('relations', 'POST', { collection: 'book_reviews_translations', field: 'languages_code', related_collection: 'languages', schema: { on_delete: 'SET NULL' } });
|
||||||
|
|
||||||
|
// 4. Das Translations-Feld in der Haupt-Collection registrieren
|
||||||
|
await api('fields/book_reviews', 'POST', {
|
||||||
|
field: 'translations',
|
||||||
|
type: 'alias',
|
||||||
|
meta: { interface: 'translations', special: ['translations'], options: { languageField: 'languages_code' } }
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('✨ Alles fertig! Bitte lade Directus neu.');
|
||||||
|
}
|
||||||
|
|
||||||
|
atomicSetup().catch(console.error);
|
||||||
60
scripts/cleanup-directus-ui.js
Normal file
60
scripts/cleanup-directus-ui.js
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
require('dotenv').config();
|
||||||
|
const DIRECTUS_URL = process.env.DIRECTUS_URL || 'https://cms.dk0.dev';
|
||||||
|
const DIRECTUS_TOKEN = process.env.DIRECTUS_STATIC_TOKEN;
|
||||||
|
|
||||||
|
async function api(endpoint, method = 'GET', body = null) {
|
||||||
|
const url = `${DIRECTUS_URL}/${endpoint}`;
|
||||||
|
const options = {
|
||||||
|
method,
|
||||||
|
headers: { 'Authorization': `Bearer ${DIRECTUS_TOKEN}`, 'Content-Type': 'application/json' }
|
||||||
|
};
|
||||||
|
if (body) options.body = JSON.stringify(body);
|
||||||
|
const response = await fetch(url, options);
|
||||||
|
const data = await response.json();
|
||||||
|
return { ok: response.ok, data };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function finalCleanup() {
|
||||||
|
console.log('🧹 Räume Directus UI auf...');
|
||||||
|
|
||||||
|
// 1. Haupt-Collection konfigurieren
|
||||||
|
await api('collections/book_reviews', 'PATCH', {
|
||||||
|
meta: {
|
||||||
|
icon: 'import_contacts',
|
||||||
|
display_template: '{{book_title}}',
|
||||||
|
hidden: false,
|
||||||
|
group: null, // Aus Ordnern herausholen
|
||||||
|
singleton: false,
|
||||||
|
translations: [
|
||||||
|
{ language: 'de-DE', translation: 'Buch-Bewertungen' },
|
||||||
|
{ language: 'en-US', translation: 'Book Reviews' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. Übersetzungs-Tabelle verstecken (Wichtig für die Optik!)
|
||||||
|
await api('collections/book_reviews_translations', 'PATCH', {
|
||||||
|
meta: {
|
||||||
|
hidden: true,
|
||||||
|
group: 'book_reviews' // Technisch untergeordnet
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. Sicherstellen, dass das 'translations' Feld im CMS gut aussieht
|
||||||
|
await api('fields/book_reviews/translations', 'PATCH', {
|
||||||
|
meta: {
|
||||||
|
interface: 'translations',
|
||||||
|
display: 'translations',
|
||||||
|
options: {
|
||||||
|
languageField: 'languages_code',
|
||||||
|
userLanguage: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('✅ UI optimiert! Bitte lade Directus jetzt neu (Cmd+R / Strg+R).');
|
||||||
|
console.log('Du solltest jetzt links in der Navigation "Book Reviews" mit einem Buch-Icon sehen.');
|
||||||
|
}
|
||||||
|
|
||||||
|
finalCleanup().catch(console.error);
|
||||||
38
scripts/deep-fix-languages.js
Normal file
38
scripts/deep-fix-languages.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
require('dotenv').config();
|
||||||
|
const DIRECTUS_URL = process.env.DIRECTUS_URL || 'https://cms.dk0.dev';
|
||||||
|
const DIRECTUS_TOKEN = process.env.DIRECTUS_STATIC_TOKEN;
|
||||||
|
|
||||||
|
async function api(endpoint, method = 'GET', body = null) {
|
||||||
|
const url = `${DIRECTUS_URL}/${endpoint}`;
|
||||||
|
const options = {
|
||||||
|
method,
|
||||||
|
headers: { 'Authorization': `Bearer ${DIRECTUS_TOKEN}`, 'Content-Type': 'application/json' }
|
||||||
|
};
|
||||||
|
if (body) options.body = JSON.stringify(body);
|
||||||
|
const response = await fetch(url, options);
|
||||||
|
const data = await response.json().catch(() => ({}));
|
||||||
|
return { ok: response.ok, data };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deepFixLanguages() {
|
||||||
|
console.log('🏗 Starte Deep-Fix...');
|
||||||
|
const collections = ['projects', 'book_reviews', 'hobbies', 'tech_stack_categories', 'messages'];
|
||||||
|
for (const coll of collections) {
|
||||||
|
const transColl = coll + '_translations';
|
||||||
|
console.log('🛠 Fixe ' + transColl);
|
||||||
|
await api('relations/' + transColl + '/languages_code', 'DELETE').catch(() => {});
|
||||||
|
await api('relations', 'POST', {
|
||||||
|
collection: transColl,
|
||||||
|
field: 'languages_code',
|
||||||
|
related_collection: 'languages',
|
||||||
|
schema: {},
|
||||||
|
meta: { interface: 'select-dropdown', options: { template: '{{name}}' } }
|
||||||
|
});
|
||||||
|
await api('fields/' + transColl + '/languages_code', 'PATCH', {
|
||||||
|
meta: { interface: 'select-dropdown', display: 'raw', required: true }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log('✅ Fertig! Bitte lade Directus neu.');
|
||||||
|
}
|
||||||
|
deepFixLanguages().catch(console.error);
|
||||||
27
scripts/emergency-directus-fix.js
Normal file
27
scripts/emergency-directus-fix.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
require('dotenv').config();
|
||||||
|
const DIRECTUS_URL = process.env.DIRECTUS_URL || 'https://cms.dk0.dev';
|
||||||
|
const DIRECTUS_TOKEN = process.env.DIRECTUS_STATIC_TOKEN;
|
||||||
|
|
||||||
|
async function api(endpoint, method = 'GET', body = null) {
|
||||||
|
const url = `${DIRECTUS_URL}/${endpoint}`;
|
||||||
|
const options = {
|
||||||
|
method,
|
||||||
|
headers: { 'Authorization': `Bearer ${DIRECTUS_TOKEN}`, 'Content-Type': 'application/json' }
|
||||||
|
};
|
||||||
|
if (body) options.body = JSON.stringify(body);
|
||||||
|
const response = await fetch(url, options);
|
||||||
|
const data = await response.json().catch(() => ({}));
|
||||||
|
return { ok: response.ok, data };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function emergencyFix() {
|
||||||
|
console.log('Fixing...');
|
||||||
|
await api('collections/book_reviews', 'PATCH', { meta: { hidden: true } });
|
||||||
|
await api('collections/book_reviews', 'PATCH', { meta: { hidden: false, icon: 'book' } });
|
||||||
|
await api('fields/book_reviews/translations', 'PATCH', {
|
||||||
|
meta: { interface: 'translations', options: { languageField: 'languages_code' } }
|
||||||
|
});
|
||||||
|
console.log('Done. Please reload in Incognito.');
|
||||||
|
}
|
||||||
|
emergencyFix().catch(console.error);
|
||||||
45
scripts/final-directus-ui-fix.js
Normal file
45
scripts/final-directus-ui-fix.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
require('dotenv').config();
|
||||||
|
const DIRECTUS_URL = process.env.DIRECTUS_URL || 'https://cms.dk0.dev';
|
||||||
|
const DIRECTUS_TOKEN = process.env.DIRECTUS_STATIC_TOKEN;
|
||||||
|
|
||||||
|
async function api(endpoint, method = 'PATCH', body = null) {
|
||||||
|
const res = await fetch(`${DIRECTUS_URL}/${endpoint}`, {
|
||||||
|
method,
|
||||||
|
headers: { 'Authorization': `Bearer ${DIRECTUS_TOKEN}`, 'Content-Type': 'application/json' },
|
||||||
|
body: body ? JSON.stringify(body) : null
|
||||||
|
});
|
||||||
|
return res.ok ? await res.json() : { error: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function finalDirectusUiFix() {
|
||||||
|
console.log('🛠 Finaler UI-Fix für Status und Übersetzungen...');
|
||||||
|
|
||||||
|
// 1. Status-Dropdown Optionen hinzufügen
|
||||||
|
await api('fields/book_reviews/status', 'PATCH', {
|
||||||
|
meta: {
|
||||||
|
interface: 'select-dropdown',
|
||||||
|
options: {
|
||||||
|
choices: [
|
||||||
|
{ text: 'Draft', value: 'draft' },
|
||||||
|
{ text: 'Published', value: 'published' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. Translations Interface auf Tabs (translations) umstellen
|
||||||
|
await api('fields/book_reviews/translations', 'PATCH', {
|
||||||
|
meta: {
|
||||||
|
interface: 'translations',
|
||||||
|
special: ['translations'],
|
||||||
|
options: {
|
||||||
|
languageField: 'languages_code'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('✅ UI-Einstellungen korrigiert! Bitte lade Directus neu.');
|
||||||
|
}
|
||||||
|
|
||||||
|
finalDirectusUiFix().catch(console.error);
|
||||||
55
scripts/fix-auto-id.js
Normal file
55
scripts/fix-auto-id.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
require('dotenv').config();
|
||||||
|
const DIRECTUS_URL = process.env.DIRECTUS_URL || 'https://cms.dk0.dev';
|
||||||
|
const DIRECTUS_TOKEN = process.env.DIRECTUS_STATIC_TOKEN;
|
||||||
|
|
||||||
|
async function api(endpoint, method = 'PATCH', body = null) {
|
||||||
|
const res = await fetch(`${DIRECTUS_URL}/${endpoint}`, {
|
||||||
|
method,
|
||||||
|
headers: { 'Authorization': `Bearer ${DIRECTUS_TOKEN}`, 'Content-Type': 'application/json' },
|
||||||
|
body: body ? JSON.stringify(body) : null
|
||||||
|
});
|
||||||
|
const data = await res.json().catch(() => ({}));
|
||||||
|
return { ok: res.ok, data };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fixAutoId() {
|
||||||
|
console.log('🛠 Automatisierung der IDs für Übersetzungen...');
|
||||||
|
|
||||||
|
// 1. ID Feld in der Übersetzungstabelle konfigurieren
|
||||||
|
await api('fields/book_reviews_translations/id', 'PATCH', {
|
||||||
|
meta: {
|
||||||
|
hidden: true,
|
||||||
|
interface: 'input',
|
||||||
|
readonly: true,
|
||||||
|
special: null
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. Fremdschlüssel (book_reviews_id) verstecken, da Directus das intern regelt
|
||||||
|
await api('fields/book_reviews_translations/book_reviews_id', 'PATCH', {
|
||||||
|
meta: {
|
||||||
|
hidden: true,
|
||||||
|
interface: 'input',
|
||||||
|
readonly: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. Sprach-Code Feld konfigurieren
|
||||||
|
await api('fields/book_reviews_translations/languages_code', 'PATCH', {
|
||||||
|
meta: {
|
||||||
|
interface: 'select-dropdown',
|
||||||
|
width: 'half',
|
||||||
|
options: {
|
||||||
|
choices: [
|
||||||
|
{ text: 'Deutsch', value: 'de-DE' },
|
||||||
|
{ text: 'English', value: 'en-US' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('✅ Fertig! Die IDs werden nun automatisch im Hintergrund verwaltet.');
|
||||||
|
}
|
||||||
|
|
||||||
|
fixAutoId().catch(console.error);
|
||||||
64
scripts/fix-book-reviews-ui.js
Normal file
64
scripts/fix-book-reviews-ui.js
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
require('dotenv').config();
|
||||||
|
const DIRECTUS_URL = process.env.DIRECTUS_URL || 'https://cms.dk0.dev';
|
||||||
|
const DIRECTUS_TOKEN = process.env.DIRECTUS_STATIC_TOKEN;
|
||||||
|
|
||||||
|
async function api(endpoint, method = 'GET', body = null) {
|
||||||
|
const url = `${DIRECTUS_URL}/${endpoint}`;
|
||||||
|
const options = {
|
||||||
|
method,
|
||||||
|
headers: { 'Authorization': `Bearer ${DIRECTUS_TOKEN}`, 'Content-Type': 'application/json' }
|
||||||
|
};
|
||||||
|
if (body) options.body = JSON.stringify(body);
|
||||||
|
const response = await fetch(url, options);
|
||||||
|
return response.ok ? await response.json() : { error: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fixUI() {
|
||||||
|
console.log('🔧 Repariere Directus UI für Book Reviews...');
|
||||||
|
|
||||||
|
// 1. Status Feld verschönern
|
||||||
|
await api('fields/book_reviews/status', 'PATCH', {
|
||||||
|
meta: {
|
||||||
|
interface: 'select-dropdown',
|
||||||
|
display: 'labels',
|
||||||
|
display_options: {
|
||||||
|
showAsDot: true,
|
||||||
|
choices: [
|
||||||
|
{ value: 'published', foreground: '#FFFFFF', background: '#00C897' },
|
||||||
|
{ value: 'draft', foreground: '#FFFFFF', background: '#666666' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
choices: [
|
||||||
|
{ text: 'Veröffentlicht', value: 'published' },
|
||||||
|
{ text: 'Entwurf', value: 'draft' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. Sprachen-Verknüpfung reparieren (WICHTIG für Tabs)
|
||||||
|
await api('relations', 'POST', {
|
||||||
|
collection: 'book_reviews_translations',
|
||||||
|
field: 'languages_code',
|
||||||
|
related_collection: 'languages',
|
||||||
|
meta: { interface: 'select-dropdown' }
|
||||||
|
}).catch(() => console.log('Relation existiert evtl. schon...'));
|
||||||
|
|
||||||
|
// 3. Übersetzungs-Interface aktivieren
|
||||||
|
await api('fields/book_reviews/translations', 'PATCH', {
|
||||||
|
meta: {
|
||||||
|
interface: 'translations',
|
||||||
|
display: 'translations',
|
||||||
|
options: {
|
||||||
|
languageField: 'languages_code',
|
||||||
|
userLanguage: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('✅ UI-Fix angewendet! Bitte lade Directus neu.');
|
||||||
|
}
|
||||||
|
|
||||||
|
fixUI().catch(console.error);
|
||||||
37
scripts/fix-directus-book-reviews.js
Normal file
37
scripts/fix-directus-book-reviews.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
require('dotenv').config();
|
||||||
|
const DIRECTUS_URL = process.env.DIRECTUS_URL || 'https://cms.dk0.dev';
|
||||||
|
const DIRECTUS_TOKEN = process.env.DIRECTUS_STATIC_TOKEN;
|
||||||
|
|
||||||
|
async function api(endpoint, method = 'GET', body = null) {
|
||||||
|
const url = `${DIRECTUS_URL}/${endpoint}`;
|
||||||
|
const options = {
|
||||||
|
method,
|
||||||
|
headers: { 'Authorization': `Bearer ${DIRECTUS_TOKEN}`, 'Content-Type': 'application/json' }
|
||||||
|
};
|
||||||
|
if (body) options.body = JSON.stringify(body);
|
||||||
|
const response = await fetch(url, options);
|
||||||
|
const data = await response.json();
|
||||||
|
return { ok: response.ok, data, status: response.status };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fix() {
|
||||||
|
console.log('🔧 Fixing Directus Book Reviews...');
|
||||||
|
await api('collections/book_reviews', 'PATCH', { meta: { icon: 'menu_book', display_template: '{{book_title}}', hidden: false } });
|
||||||
|
|
||||||
|
// Link to system languages
|
||||||
|
await api('relations', 'POST', {
|
||||||
|
collection: 'book_reviews_translations',
|
||||||
|
field: 'languages_code',
|
||||||
|
related_collection: 'languages',
|
||||||
|
meta: { interface: 'select-dropdown' }
|
||||||
|
});
|
||||||
|
|
||||||
|
// UI Improvements
|
||||||
|
await api('fields/book_reviews/status', 'PATCH', { meta: { interface: 'select-dropdown', display: 'labels' } });
|
||||||
|
await api('fields/book_reviews/rating', 'PATCH', { meta: { interface: 'rating', display: 'rating' } });
|
||||||
|
await api('fields/book_reviews_translations/review', 'PATCH', { meta: { interface: 'input-rich-text-html' } });
|
||||||
|
|
||||||
|
console.log('✅ Fix applied! Bitte lade Directus neu und setze die Permissions auf Public.');
|
||||||
|
}
|
||||||
|
fix().catch(console.error);
|
||||||
51
scripts/fix-relations-metadata.js
Normal file
51
scripts/fix-relations-metadata.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
require('dotenv').config();
|
||||||
|
const DIRECTUS_URL = process.env.DIRECTUS_URL || 'https://cms.dk0.dev';
|
||||||
|
const DIRECTUS_TOKEN = process.env.DIRECTUS_STATIC_TOKEN;
|
||||||
|
|
||||||
|
async function api(endpoint, method = 'GET', body = null) {
|
||||||
|
const url = `${DIRECTUS_URL}/${endpoint}`;
|
||||||
|
const options = {
|
||||||
|
method,
|
||||||
|
headers: { 'Authorization': `Bearer ${DIRECTUS_TOKEN}`, 'Content-Type': 'application/json' }
|
||||||
|
};
|
||||||
|
if (body) options.body = JSON.stringify(body);
|
||||||
|
const response = await fetch(url, options);
|
||||||
|
const data = await response.json().catch(() => ({}));
|
||||||
|
return { ok: response.ok, data };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fixRelationsMetadata() {
|
||||||
|
console.log('🔗 Fixe Sprach-Relationen Metadaten...');
|
||||||
|
|
||||||
|
const collections = ['projects', 'book_reviews', 'hobbies', 'tech_stack_categories', 'messages'];
|
||||||
|
|
||||||
|
for (const coll of collections) {
|
||||||
|
const transColl = `${coll}_translations`;
|
||||||
|
console.log(`🛠 Konfiguriere ${transColl}...`);
|
||||||
|
|
||||||
|
// Wir müssen die Relation von languages_code zur languages Tabelle
|
||||||
|
// für Directus "greifbar" machen.
|
||||||
|
await api(`relations/${transColl}/languages_code`, 'PATCH', {
|
||||||
|
meta: {
|
||||||
|
interface: 'select-dropdown',
|
||||||
|
display: 'raw'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// WICHTIG: Wir sagen dem Hauptfeld "translations" noch einmal
|
||||||
|
// ganz explizit, welches Feld in der Untertabelle für die Sprache zuständig ist.
|
||||||
|
await api(`fields/${coll}/translations`, 'PATCH', {
|
||||||
|
meta: {
|
||||||
|
interface: 'translations',
|
||||||
|
options: {
|
||||||
|
languageField: 'languages_code' // Der Name des Feldes in der *_translations Tabelle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('✅ Fertig! Bitte lade Directus neu.');
|
||||||
|
}
|
||||||
|
|
||||||
|
fixRelationsMetadata().catch(console.error);
|
||||||
54
scripts/fix-translation-interface.js
Normal file
54
scripts/fix-translation-interface.js
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
require('dotenv').config();
|
||||||
|
const DIRECTUS_URL = process.env.DIRECTUS_URL || 'https://cms.dk0.dev';
|
||||||
|
const DIRECTUS_TOKEN = process.env.DIRECTUS_STATIC_TOKEN;
|
||||||
|
|
||||||
|
async function api(endpoint, method = 'GET', body = null) {
|
||||||
|
const url = `${DIRECTUS_URL}/${endpoint}`;
|
||||||
|
const options = {
|
||||||
|
method,
|
||||||
|
headers: { 'Authorization': `Bearer ${DIRECTUS_TOKEN}`, 'Content-Type': 'application/json' }
|
||||||
|
};
|
||||||
|
if (body) options.body = JSON.stringify(body);
|
||||||
|
const response = await fetch(url, options);
|
||||||
|
const data = await response.json().catch(() => ({}));
|
||||||
|
return { ok: response.ok, data };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fixTranslationInterface() {
|
||||||
|
console.log('🛠 Erdenke das Translations-Interface neu...');
|
||||||
|
|
||||||
|
const collections = ['projects', 'book_reviews', 'hobbies', 'tech_stack_categories', 'messages'];
|
||||||
|
|
||||||
|
for (const coll of collections) {
|
||||||
|
console.log(`🔧 Fixe Interface für ${coll}...`);
|
||||||
|
|
||||||
|
// Wir überschreiben die Metadaten des Feldes "translations"
|
||||||
|
// WICHTIG: Wir setzen interface auf 'translations' und mappen das languageField
|
||||||
|
await api(`fields/${coll}/translations`, 'PATCH', {
|
||||||
|
meta: {
|
||||||
|
interface: 'translations',
|
||||||
|
display: 'translations',
|
||||||
|
special: ['translations'],
|
||||||
|
options: {
|
||||||
|
languageField: 'languages_code',
|
||||||
|
userLanguage: true,
|
||||||
|
defaultLanguage: 'de-DE'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wir stellen sicher, dass in der Untertabelle das Feld languages_code
|
||||||
|
// als 'languages' Typ erkannt wird
|
||||||
|
await api(`fields/${coll}_translations/languages_code`, 'PATCH', {
|
||||||
|
meta: {
|
||||||
|
interface: 'select-dropdown',
|
||||||
|
special: null // Kein spezielles Feld hier, nur ein normaler FK
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('✅ Übersetzung-Tabs sollten jetzt erscheinen! Bitte Directus hart neu laden.');
|
||||||
|
}
|
||||||
|
|
||||||
|
fixTranslationInterface().catch(console.error);
|
||||||
63
scripts/force-translations-plus.js
Normal file
63
scripts/force-translations-plus.js
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
require('dotenv').config();
|
||||||
|
const DIRECTUS_URL = process.env.DIRECTUS_URL || 'https://cms.dk0.dev';
|
||||||
|
const DIRECTUS_TOKEN = process.env.DIRECTUS_STATIC_TOKEN;
|
||||||
|
|
||||||
|
async function api(endpoint, method = 'GET', body = null) {
|
||||||
|
const url = `${DIRECTUS_URL}/${endpoint}`;
|
||||||
|
const options = {
|
||||||
|
method,
|
||||||
|
headers: { 'Authorization': `Bearer ${DIRECTUS_TOKEN}`, 'Content-Type': 'application/json' }
|
||||||
|
};
|
||||||
|
if (body) options.body = JSON.stringify(body);
|
||||||
|
const response = await fetch(url, options);
|
||||||
|
const data = await response.json().catch(() => ({}));
|
||||||
|
return { ok: response.ok, data };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function forceTranslationsPlusButton() {
|
||||||
|
console.log('🔨 Erzwinge "Plus"-Button für Übersetzungen...');
|
||||||
|
|
||||||
|
const coll = 'book_reviews';
|
||||||
|
const transColl = 'book_reviews_translations';
|
||||||
|
|
||||||
|
// 1. Das alte Alias-Feld löschen (falls es klemmt)
|
||||||
|
await api(`fields/${coll}/translations`, 'DELETE').catch(() => {});
|
||||||
|
|
||||||
|
// 2. Das Feld komplett neu anlegen als technisches Alias für die Relation
|
||||||
|
await api(`fields/${coll}`, 'POST', {
|
||||||
|
field: 'translations',
|
||||||
|
type: 'alias',
|
||||||
|
meta: {
|
||||||
|
interface: 'translations',
|
||||||
|
display: 'translations',
|
||||||
|
special: ['translations'],
|
||||||
|
options: {
|
||||||
|
languageField: 'languages_code',
|
||||||
|
userLanguage: true
|
||||||
|
},
|
||||||
|
width: 'full'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. Die Relation explizit als One-to-Many (O2M) registrieren
|
||||||
|
// Das ist der wichtigste Schritt für den Plus-Button!
|
||||||
|
await api('relations', 'POST', {
|
||||||
|
collection: transColl,
|
||||||
|
field: 'book_reviews_id',
|
||||||
|
related_collection: coll,
|
||||||
|
meta: {
|
||||||
|
one_field: 'translations',
|
||||||
|
junction_field: null,
|
||||||
|
one_deselect_action: 'delete'
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
on_delete: 'CASCADE'
|
||||||
|
}
|
||||||
|
}).catch(err => console.log('Relation existiert evtl. schon, überspringe...'));
|
||||||
|
|
||||||
|
console.log('✅ Fertig! Bitte lade Directus neu.');
|
||||||
|
console.log('Gehe in ein Buch -> Jetzt MUSS unten bei "Translations" ein Plus-Button oder "Create New" stehen.');
|
||||||
|
}
|
||||||
|
|
||||||
|
forceTranslationsPlusButton().catch(console.error);
|
||||||
46
scripts/global-cms-beauty-fix.js
Normal file
46
scripts/global-cms-beauty-fix.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
require('dotenv').config();
|
||||||
|
const DIRECTUS_URL = process.env.DIRECTUS_URL || 'https://cms.dk0.dev';
|
||||||
|
const DIRECTUS_TOKEN = process.env.DIRECTUS_STATIC_TOKEN;
|
||||||
|
|
||||||
|
async function api(endpoint, method = 'GET', body = null) {
|
||||||
|
const url = `${DIRECTUS_URL}/${endpoint}`;
|
||||||
|
const options = {
|
||||||
|
method,
|
||||||
|
headers: { 'Authorization': `Bearer ${DIRECTUS_TOKEN}`, 'Content-Type': 'application/json' }
|
||||||
|
};
|
||||||
|
if (body) options.body = JSON.stringify(body);
|
||||||
|
const response = await fetch(url, options);
|
||||||
|
const data = await response.json().catch(() => ({}));
|
||||||
|
return { ok: response.ok, data };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function globalBeautyFix() {
|
||||||
|
console.log('✨ Starte globale CMS-Verschönerung...');
|
||||||
|
await api('items/languages', 'POST', { code: 'de-DE', name: 'German' }).catch(() => {});
|
||||||
|
await api('items/languages', 'POST', { code: 'en-US', name: 'English' }).catch(() => {});
|
||||||
|
|
||||||
|
const collections = ['projects', 'book_reviews', 'hobbies', 'tech_stack_categories', 'messages'];
|
||||||
|
for (const coll of collections) {
|
||||||
|
console.log('📦 Optimiere ' + coll);
|
||||||
|
await api('collections/' + coll + '_translations', 'PATCH', { meta: { hidden: true } });
|
||||||
|
await api('fields/' + coll + '/translations', 'PATCH', {
|
||||||
|
meta: {
|
||||||
|
interface: 'translations',
|
||||||
|
display: 'translations',
|
||||||
|
width: 'full',
|
||||||
|
options: { languageField: 'languages_code', defaultLanguage: 'de-DE', userLanguage: true }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await api('relations', 'POST', {
|
||||||
|
collection: coll + '_translations',
|
||||||
|
field: 'languages_code',
|
||||||
|
related_collection: 'languages',
|
||||||
|
meta: { interface: 'select-dropdown' }
|
||||||
|
}).catch(() => {});
|
||||||
|
}
|
||||||
|
await api('fields/projects/tags', 'PATCH', { meta: { interface: 'tags' } });
|
||||||
|
await api('fields/projects/technologies', 'PATCH', { meta: { interface: 'tags' } });
|
||||||
|
console.log('✅ CMS ist jetzt aufgeräumt! Bitte Directus neu laden.');
|
||||||
|
}
|
||||||
|
globalBeautyFix().catch(console.error);
|
||||||
55
scripts/make-directus-editable.js
Normal file
55
scripts/make-directus-editable.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
require('dotenv').config();
|
||||||
|
const DIRECTUS_URL = process.env.DIRECTUS_URL || 'https://cms.dk0.dev';
|
||||||
|
const DIRECTUS_TOKEN = process.env.DIRECTUS_STATIC_TOKEN;
|
||||||
|
|
||||||
|
async function api(endpoint, method = 'GET', body = null) {
|
||||||
|
const url = `${DIRECTUS_URL}/${endpoint}`;
|
||||||
|
const options = {
|
||||||
|
method,
|
||||||
|
headers: { 'Authorization': `Bearer ${DIRECTUS_TOKEN}`, 'Content-Type': 'application/json' }
|
||||||
|
};
|
||||||
|
if (body) options.body = JSON.stringify(body);
|
||||||
|
const response = await fetch(url, options);
|
||||||
|
const data = await response.json();
|
||||||
|
return { ok: response.ok, data };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function makeEditable() {
|
||||||
|
console.log('🔓 Mache "Book Reviews" editierbar...');
|
||||||
|
|
||||||
|
// 1. Prüfen ob ID Feld existiert, sonst anlegen
|
||||||
|
console.log('1. Erstelle ID-Primärschlüssel...');
|
||||||
|
await api('fields/book_reviews', 'POST', {
|
||||||
|
field: 'id',
|
||||||
|
type: 'integer',
|
||||||
|
schema: {
|
||||||
|
is_primary_key: true,
|
||||||
|
has_auto_increment: true
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
hidden: true // Im Formular verstecken, da automatisch
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. Sicherstellen, dass alle Felder eine Interface-Zuweisung haben (wichtig für die Eingabe)
|
||||||
|
console.log('2. Konfiguriere Eingabe-Interfaces...');
|
||||||
|
const fieldUpdates = [
|
||||||
|
{ field: 'book_title', interface: 'input' },
|
||||||
|
{ field: 'book_author', interface: 'input' },
|
||||||
|
{ field: 'rating', interface: 'rating' },
|
||||||
|
{ field: 'status', interface: 'select-dropdown' },
|
||||||
|
{ field: 'finished_at', interface: 'datetime' }
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const f of fieldUpdates) {
|
||||||
|
await api(`fields/book_reviews/${f.field}`, 'PATCH', {
|
||||||
|
meta: { interface: f.interface, readonly: false }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('✅ Fertig! Bitte lade Directus neu.');
|
||||||
|
console.log('Solltest du immer noch nicht editieren können, musst du eventuell die Collection löschen und neu anlegen lassen, da die Datenbank-Struktur (ID) manchmal nicht nachträglich über die API geändert werden kann.');
|
||||||
|
}
|
||||||
|
|
||||||
|
makeEditable().catch(console.error);
|
||||||
46
scripts/master-setup-book-reviews.js
Normal file
46
scripts/master-setup-book-reviews.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
require('dotenv').config();
|
||||||
|
const DIRECTUS_URL = process.env.DIRECTUS_URL || 'https://cms.dk0.dev';
|
||||||
|
const DIRECTUS_TOKEN = process.env.DIRECTUS_STATIC_TOKEN;
|
||||||
|
|
||||||
|
async function api(endpoint, method = 'GET', body = null) {
|
||||||
|
const url = `${DIRECTUS_URL}/${endpoint}`;
|
||||||
|
const options = {
|
||||||
|
method,
|
||||||
|
headers: { 'Authorization': `Bearer ${DIRECTUS_TOKEN}`, 'Content-Type': 'application/json' }
|
||||||
|
};
|
||||||
|
if (body) options.body = JSON.stringify(body);
|
||||||
|
const response = await fetch(url, options);
|
||||||
|
const data = await response.json();
|
||||||
|
return { ok: response.ok, data };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function masterSetup() {
|
||||||
|
console.log('🚀 Starte Master-Setup...');
|
||||||
|
await api('collections', 'POST', { collection: 'book_reviews', meta: { icon: 'import_contacts', display_template: '{{book_title}}' } });
|
||||||
|
await api('fields/book_reviews', 'POST', { field: 'id', type: 'integer', schema: { is_primary_key: true, has_auto_increment: true }, meta: { hidden: true } });
|
||||||
|
|
||||||
|
const fields = [
|
||||||
|
{ field: 'status', type: 'string', meta: { interface: 'select-dropdown', options: { choices: [{text: 'Published', value: 'published'}, {text: 'Draft', value: 'draft'}] } }, schema: { default_value: 'draft' } },
|
||||||
|
{ field: 'book_title', type: 'string', meta: { interface: 'input' } },
|
||||||
|
{ field: 'book_author', type: 'string', meta: { interface: 'input' } },
|
||||||
|
{ field: 'book_image', type: 'string', meta: { interface: 'input' } },
|
||||||
|
{ field: 'rating', type: 'integer', meta: { interface: 'rating' } },
|
||||||
|
{ field: 'hardcover_id', type: 'string', meta: { interface: 'input' }, schema: { is_unique: true } },
|
||||||
|
{ field: 'finished_at', type: 'date', meta: { interface: 'datetime' } }
|
||||||
|
];
|
||||||
|
for (const f of fields) await api('fields/book_reviews', 'POST', f);
|
||||||
|
|
||||||
|
await api('collections', 'POST', { collection: 'book_reviews_translations', meta: { hidden: true } });
|
||||||
|
await api('fields/book_reviews_translations', 'POST', { field: 'id', type: 'integer', schema: { is_primary_key: true, has_auto_increment: true }, meta: { hidden: true } });
|
||||||
|
await api('fields/book_reviews_translations', 'POST', { field: 'book_reviews_id', type: 'integer' });
|
||||||
|
await api('fields/book_reviews_translations', 'POST', { field: 'languages_code', type: 'string' });
|
||||||
|
await api('fields/book_reviews_translations', 'POST', { field: 'review', type: 'text', meta: { interface: 'input-rich-text-html' } });
|
||||||
|
|
||||||
|
await api('relations', 'POST', { collection: 'book_reviews_translations', field: 'book_reviews_id', related_collection: 'book_reviews', meta: { one_field: 'translations' } });
|
||||||
|
await api('relations', 'POST', { collection: 'book_reviews_translations', field: 'languages_code', related_collection: 'languages' });
|
||||||
|
await api('fields/book_reviews', 'POST', { field: 'translations', type: 'alias', meta: { interface: 'translations', special: ['translations'] } });
|
||||||
|
|
||||||
|
console.log('✨ Setup abgeschlossen! Bitte lade Directus neu und setze die Public-Permissions.');
|
||||||
|
}
|
||||||
|
masterSetup().catch(console.error);
|
||||||
51
scripts/perfect-directus-structure.js
Normal file
51
scripts/perfect-directus-structure.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
require('dotenv').config();
|
||||||
|
const DIRECTUS_URL = process.env.DIRECTUS_URL || 'https://cms.dk0.dev';
|
||||||
|
const DIRECTUS_TOKEN = process.env.DIRECTUS_STATIC_TOKEN;
|
||||||
|
|
||||||
|
async function api(endpoint, method = 'GET', body = null) {
|
||||||
|
const url = `${DIRECTUS_URL}/${endpoint}`;
|
||||||
|
const options = {
|
||||||
|
method,
|
||||||
|
headers: { 'Authorization': `Bearer ${DIRECTUS_TOKEN}`, 'Content-Type': 'application/json' }
|
||||||
|
};
|
||||||
|
if (body) options.body = JSON.stringify(body);
|
||||||
|
const response = await fetch(url, options);
|
||||||
|
const data = await response.json();
|
||||||
|
return { ok: response.ok, data };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function perfectStructure() {
|
||||||
|
console.log('💎 Optimiere CMS-Struktur zur Perfektion...');
|
||||||
|
|
||||||
|
// 1. Projekt-Buttons individualisieren
|
||||||
|
console.log('1. Erweitere Projekte um individuelle Button-Labels...');
|
||||||
|
await api('fields/projects_translations', 'POST', { field: 'button_live_label', type: 'string', meta: { interface: 'input', options: { placeholder: 'z.B. Live Demo, App öffnen...' }, width: 'half' } });
|
||||||
|
await api('fields/projects_translations', 'POST', { field: 'button_github_label', type: 'string', meta: { interface: 'input', options: { placeholder: 'z.B. Source Code, GitHub...' }, width: 'half' } });
|
||||||
|
|
||||||
|
// 2. SEO für Inhaltsseiten
|
||||||
|
console.log('2. Füge SEO-Felder zu Content-Pages hinzu...');
|
||||||
|
await api('fields/content_pages', 'POST', { field: 'meta_description', type: 'string', meta: { interface: 'input' } });
|
||||||
|
await api('fields/content_pages', 'POST', { field: 'keywords', type: 'string', meta: { interface: 'input' } });
|
||||||
|
|
||||||
|
// 3. Die ultimative "Messages" Collection (für UI Strings)
|
||||||
|
console.log('3. Erstelle globale "Messages" Collection...');
|
||||||
|
await api('collections', 'POST', { collection: 'messages', schema: {}, meta: { icon: 'translate', display_template: '{{key}}' } });
|
||||||
|
await api('fields/messages', 'POST', { field: 'key', type: 'string', schema: { is_primary_key: true }, meta: { interface: 'input', options: { placeholder: 'home.hero.title' } } });
|
||||||
|
|
||||||
|
// Messages Translations
|
||||||
|
await api('collections', 'POST', { collection: 'messages_translations', schema: {}, meta: { hidden: true } });
|
||||||
|
await api('fields/messages_translations', 'POST', { field: 'id', type: 'integer', schema: { is_primary_key: true, has_auto_increment: true }, meta: { hidden: true } });
|
||||||
|
await api('fields/messages_translations', 'POST', { field: 'messages_id', type: 'string', schema: {} });
|
||||||
|
await api('fields/messages_translations', 'POST', { field: 'languages_code', type: 'string', schema: {} });
|
||||||
|
await api('fields/messages_translations', 'POST', { field: 'value', type: 'text', meta: { interface: 'input' } });
|
||||||
|
|
||||||
|
// Relationen für Messages
|
||||||
|
await api('relations', 'POST', { collection: 'messages_translations', field: 'messages_id', related_collection: 'messages', meta: { one_field: 'translations' } });
|
||||||
|
await api('relations', 'POST', { collection: 'messages_translations', field: 'languages_code', related_collection: 'languages' });
|
||||||
|
await api('fields/messages', 'POST', { field: 'translations', type: 'alias', meta: { interface: 'translations', special: ['translations'] } });
|
||||||
|
|
||||||
|
console.log('✨ CMS Struktur ist jetzt perfekt! Lade Directus neu.');
|
||||||
|
}
|
||||||
|
|
||||||
|
perfectStructure().catch(console.error);
|
||||||
37
scripts/set-public-permissions.js
Normal file
37
scripts/set-public-permissions.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
require('dotenv').config();
|
||||||
|
const DIRECTUS_URL = process.env.DIRECTUS_URL || 'https://cms.dk0.dev';
|
||||||
|
const DIRECTUS_TOKEN = process.env.DIRECTUS_STATIC_TOKEN;
|
||||||
|
|
||||||
|
async function setPublicPermissions() {
|
||||||
|
console.log('🔓 Setze Public-Berechtigungen für Book Reviews...');
|
||||||
|
|
||||||
|
// Wir holen die ID der Public Rolle
|
||||||
|
const rolesRes = await fetch(`${DIRECTUS_URL}/roles`, { headers: { 'Authorization': `Bearer ${DIRECTUS_TOKEN}` } });
|
||||||
|
const roles = await rolesRes.json();
|
||||||
|
const publicRole = roles.data.find(r => r.name.toLowerCase() === 'public');
|
||||||
|
|
||||||
|
if (!publicRole) return console.error('Public Rolle nicht gefunden.');
|
||||||
|
|
||||||
|
const collections = ['book_reviews', 'book_reviews_translations'];
|
||||||
|
|
||||||
|
for (const coll of collections) {
|
||||||
|
console.log(`- Erlaube Lesezugriff auf ${coll}`);
|
||||||
|
await fetch(`${DIRECTUS_URL}/permissions`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Authorization': `Bearer ${DIRECTUS_TOKEN}`, 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
role: publicRole.id,
|
||||||
|
collection: coll,
|
||||||
|
action: 'read',
|
||||||
|
permissions: {},
|
||||||
|
validation: null,
|
||||||
|
fields: ['*']
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('✅ Fertig! Die Website sollte die Daten jetzt lesen können.');
|
||||||
|
}
|
||||||
|
|
||||||
|
setPublicPermissions().catch(console.error);
|
||||||
46
scripts/simple-translation-fix.js
Normal file
46
scripts/simple-translation-fix.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
require('dotenv').config();
|
||||||
|
const DIRECTUS_URL = process.env.DIRECTUS_URL || 'https://cms.dk0.dev';
|
||||||
|
const DIRECTUS_TOKEN = process.env.DIRECTUS_STATIC_TOKEN;
|
||||||
|
|
||||||
|
async function api(endpoint, method = 'PATCH', body = null) {
|
||||||
|
const res = await fetch(`${DIRECTUS_URL}/${endpoint}`, {
|
||||||
|
method,
|
||||||
|
headers: { 'Authorization': `Bearer ${DIRECTUS_TOKEN}`, 'Content-Type': 'application/json' },
|
||||||
|
body: body ? JSON.stringify(body) : null
|
||||||
|
});
|
||||||
|
return res.ok ? await res.json() : { error: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function simpleFix() {
|
||||||
|
console.log('🛠 Vereinfache Übersetzungs-Interface...');
|
||||||
|
|
||||||
|
// 1. Das Hauptfeld "translations" konfigurieren
|
||||||
|
await api('fields/book_reviews/translations', 'PATCH', {
|
||||||
|
meta: {
|
||||||
|
interface: 'translations',
|
||||||
|
options: {
|
||||||
|
languageField: 'languages_code',
|
||||||
|
userLanguage: false, // Deaktivieren, um Fehler zu vermeiden
|
||||||
|
defaultLanguage: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. Das Sprachfeld in der Untertabelle konfigurieren
|
||||||
|
await api('fields/book_reviews_translations/languages_code', 'PATCH', {
|
||||||
|
meta: {
|
||||||
|
interface: 'select-dropdown',
|
||||||
|
options: {
|
||||||
|
choices: [
|
||||||
|
{ text: 'Deutsch', value: 'de-DE' },
|
||||||
|
{ text: 'English', value: 'en-US' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('✅ Fertig! Bitte lade Directus neu.');
|
||||||
|
}
|
||||||
|
|
||||||
|
simpleFix().catch(console.error);
|
||||||
66
scripts/ultra-setup-book-reviews.js
Normal file
66
scripts/ultra-setup-book-reviews.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
require('dotenv').config();
|
||||||
|
const DIRECTUS_URL = process.env.DIRECTUS_URL || 'https://cms.dk0.dev';
|
||||||
|
const DIRECTUS_TOKEN = process.env.DIRECTUS_STATIC_TOKEN;
|
||||||
|
|
||||||
|
async function api(endpoint, method = 'GET', body = null) {
|
||||||
|
const url = `${DIRECTUS_URL}/${endpoint}`;
|
||||||
|
const options = {
|
||||||
|
method,
|
||||||
|
headers: { 'Authorization': `Bearer ${DIRECTUS_TOKEN}`, 'Content-Type': 'application/json' }
|
||||||
|
};
|
||||||
|
if (body) options.body = JSON.stringify(body);
|
||||||
|
const response = await fetch(url, options);
|
||||||
|
const data = await response.json();
|
||||||
|
return { ok: response.ok, data };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ultraSetup() {
|
||||||
|
console.log('🚀 Starte Ultra-Setup für "Book Reviews"...');
|
||||||
|
|
||||||
|
// 1. Collection anlegen (mit Primärschlüssel-Definition im Schema!)
|
||||||
|
await api('collections', 'POST', {
|
||||||
|
collection: 'book_reviews',
|
||||||
|
schema: {},
|
||||||
|
meta: { icon: 'import_contacts', display_template: '{{book_title}}' }
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. Felder mit explizitem Schema (Datenbank-Spalten)
|
||||||
|
const fields = [
|
||||||
|
{ field: 'id', type: 'integer', schema: { is_primary_key: true, has_auto_increment: true }, meta: { hidden: true } },
|
||||||
|
{ field: 'status', type: 'string', schema: { default_value: 'draft' }, meta: { interface: 'select-dropdown', width: 'half' } },
|
||||||
|
{ field: 'book_title', type: 'string', schema: {}, meta: { interface: 'input', width: 'full' } },
|
||||||
|
{ field: 'book_author', type: 'string', schema: {}, meta: { interface: 'input', width: 'half' } },
|
||||||
|
{ field: 'rating', type: 'integer', schema: {}, meta: { interface: 'rating', width: 'half' } },
|
||||||
|
{ field: 'book_image', type: 'string', schema: {}, meta: { interface: 'input', width: 'full' } },
|
||||||
|
{ field: 'hardcover_id', type: 'string', schema: { is_unique: true }, meta: { interface: 'input', width: 'half' } },
|
||||||
|
{ field: 'finished_at', type: 'date', schema: {}, meta: { interface: 'datetime', width: 'half' } }
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const f of fields) {
|
||||||
|
await api('fields/book_reviews', 'POST', f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Übersetzungen
|
||||||
|
await api('collections', 'POST', { collection: 'book_reviews_translations', schema: {}, meta: { hidden: true } });
|
||||||
|
|
||||||
|
const transFields = [
|
||||||
|
{ field: 'id', type: 'integer', schema: { is_primary_key: true, has_auto_increment: true }, meta: { hidden: true } },
|
||||||
|
{ field: 'book_reviews_id', type: 'integer', schema: {}, meta: { hidden: true } },
|
||||||
|
{ field: 'languages_code', type: 'string', schema: {}, meta: { interface: 'select-dropdown' } },
|
||||||
|
{ field: 'review', type: 'text', schema: {}, meta: { interface: 'input-rich-text-html' } }
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const f of transFields) {
|
||||||
|
await api('fields/book_reviews_translations', 'POST', f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Relationen (Der Kleber)
|
||||||
|
await api('relations', 'POST', { collection: 'book_reviews_translations', field: 'book_reviews_id', related_collection: 'book_reviews', meta: { one_field: 'translations' }, schema: { on_delete: 'CASCADE' } });
|
||||||
|
await api('relations', 'POST', { collection: 'book_reviews_translations', field: 'languages_code', related_collection: 'languages', schema: { on_delete: 'SET NULL' } });
|
||||||
|
await api('fields/book_reviews', 'POST', { field: 'translations', type: 'alias', meta: { interface: 'translations', special: ['translations'] } });
|
||||||
|
|
||||||
|
console.log('✅ Ultra-Setup abgeschlossen! Bitte lade Directus neu.');
|
||||||
|
}
|
||||||
|
|
||||||
|
ultraSetup().catch(console.error);
|
||||||
Reference in New Issue
Block a user