diff --git a/apps/strapi/config/plugins.ts b/apps/strapi/config/plugins.ts index e110c27..f55325d 100644 --- a/apps/strapi/config/plugins.ts +++ b/apps/strapi/config/plugins.ts @@ -108,27 +108,6 @@ export default ({ env }) => { ? "blog-posts-production" : "blog-posts-testing", }, - feature: { - indexName: env.bool("MEILISEARCH_PRODUCTION", false) - ? "features-production" - : "features-testing", - entriesQuery: { - populate: { - icon: true, - feature_tag: { fields: ["title"] }, - }, - }, - transformEntry({ entry }) { - return { - ...entry, - feature_tag: entry.feature_tag?.title ?? null, - } - }, - settings: { - filterableAttributes: ["feature_tag"], - searchableAttributes: ["title", "description"], - }, - }, }, }, diff --git a/apps/strapi/database/migrations/2026.05.11T11:29.seed-features.js b/apps/strapi/database/migrations/2026.05.11T11:29.seed-features.js deleted file mode 100644 index ee4bb24..0000000 --- a/apps/strapi/database/migrations/2026.05.11T11:29.seed-features.js +++ /dev/null @@ -1,252 +0,0 @@ -/* eslint-disable no-console */ -/** - * Seed features from database/migrations/features.json into the `features` collection type. - * Each card -> Feature row; `label` resolves to a feature_tag relation. - * - * Step 1: extract unique labels, insert draft + published feature_tag rows. - * Step 2: insert draft + published feature rows (with `url`), wire each to its - * tag via features_feature_tag_lnk (draft<->draft, published<->published). - * - * Icons are attached in a separate follow-up migration so the icon source can - * vary per environment without touching this seed. - */ - -"use strict" - -const crypto = require("node:crypto") -const fs = require("node:fs") -const path = require("node:path") - -const FEATURES_JSON = path.join(__dirname, "features.json") -const FEATURE_TAGS_TABLE = "feature_tags" -const FEATURES_TABLE = "features" -const FEATURES_TAG_LNK_TABLE = "features_feature_tag_lnk" - -function loadCards() { - if (!fs.existsSync(FEATURES_JSON)) { - console.log(`[seed-features] ${FEATURES_JSON} not found, skipping`) - - return null - } - - const { cards } = JSON.parse(fs.readFileSync(FEATURES_JSON, "utf8")) - - if (!Array.isArray(cards)) { - console.log(`[seed-features] no cards array in features.json, skipping`) - - return null - } - - return cards -} - -async function loadTagState(knex, titles) { - const rows = await knex(FEATURE_TAGS_TABLE) - .whereIn("title", titles) - .select("title", "document_id", "published_at") - - const map = new Map() - for (const row of rows) { - const s = map.get(row.title) ?? { - documentId: row.document_id, - hasDraft: false, - hasPublished: false, - } - if (row.published_at === null) s.hasDraft = true - else s.hasPublished = true - map.set(row.title, s) - } - - return map -} - -async function seedTags(knex, labels, now) { - const stateByTitle = await loadTagState(knex, labels) - let inserted = 0 - - for (const title of labels) { - const state = stateByTitle.get(title) ?? { - documentId: crypto.randomUUID(), - hasDraft: false, - hasPublished: false, - } - - if (state.hasDraft && state.hasPublished) { - console.log(`[seed-features] tag complete, skipping: ${title}`) - continue - } - - const base = { - document_id: state.documentId, - title, - created_at: now, - updated_at: now, - locale: "en", - } - - if (!state.hasDraft) { - await knex(FEATURE_TAGS_TABLE).insert({ ...base, published_at: null }) - inserted += 1 - console.log(`[seed-features] inserted draft tag: ${title}`) - } - if (!state.hasPublished) { - await knex(FEATURE_TAGS_TABLE).insert({ ...base, published_at: now }) - inserted += 1 - console.log(`[seed-features] inserted published tag: ${title}`) - } - } - - console.log(`[seed-features] feature-tag rows inserted: ${inserted}`) -} - -async function loadTagIdsByLabel(knex, labels) { - const rows = await knex(FEATURE_TAGS_TABLE) - .whereIn("title", labels) - .select("id", "title", "published_at") - - const map = new Map() - for (const row of rows) { - const entry = map.get(row.title) ?? { draftId: null, publishedId: null } - if (row.published_at === null) entry.draftId = row.id - else entry.publishedId = row.id - map.set(row.title, entry) - } - - return map -} - -async function loadFeatureState(knex, titles) { - const rows = await knex(FEATURES_TABLE) - .whereIn("title", titles) - .select("id", "title", "document_id", "published_at") - - const map = new Map() - for (const row of rows) { - const s = map.get(row.title) ?? { - documentId: row.document_id, - draftId: null, - publishedId: null, - } - if (row.published_at === null) s.draftId = row.id - else s.publishedId = row.id - map.set(row.title, s) - } - - return map -} - -async function insertFeatureRow(knex, base, publishedAt) { - const [r] = await knex(FEATURES_TABLE) - .insert({ ...base, published_at: publishedAt }) - .returning("id") - - return typeof r === "object" ? r.id : r -} - -async function linkFeatureTag(knex, featureId, tagId) { - if (!tagId) return false - await knex(FEATURES_TAG_LNK_TABLE).insert({ - feature_id: featureId, - feature_tag_id: tagId, - feature_ord: 1, - }) - - return true -} - -async function seedFeatureCard(knex, card, ctx) { - const { title, description, label, url } = card - const { now, tagIdsByLabel, featureStateByTitle, counters } = ctx - - if (!title) { - console.log(`[seed-features] card missing title, skipping`) - - return - } - - const tagIds = tagIdsByLabel.get(label) - if (!tagIds) { - console.log( - `[seed-features] no tag for label "${label}", skipping feature: ${title}` - ) - - return - } - - const state = featureStateByTitle.get(title) ?? { - documentId: crypto.randomUUID(), - draftId: null, - publishedId: null, - } - - if (state.draftId && state.publishedId) { - console.log(`[seed-features] feature complete, skipping: ${title}`) - - return - } - - const base = { - document_id: state.documentId, - title, - description: description ?? null, - url: url ?? null, - created_at: now, - updated_at: now, - locale: "en", - } - - if (!state.draftId) { - const id = await insertFeatureRow(knex, base, null) - counters.features += 1 - if (await linkFeatureTag(knex, id, tagIds.draftId)) counters.links += 1 - } - - if (!state.publishedId) { - const id = await insertFeatureRow(knex, base, now) - counters.features += 1 - if (await linkFeatureTag(knex, id, tagIds.publishedId)) counters.links += 1 - } - - console.log(`[seed-features] inserted feature: ${title} (${label})`) -} - -async function seedFeatures(knex, cards, labels, now) { - const tagIdsByLabel = await loadTagIdsByLabel(knex, labels) - const featureStateByTitle = await loadFeatureState( - knex, - cards.map((c) => c.title) - ) - - const counters = { features: 0, links: 0 } - const ctx = { now, tagIdsByLabel, featureStateByTitle, counters } - - for (const card of cards) { - await seedFeatureCard(knex, card, ctx) - } - - console.log( - `[seed-features] features inserted: ${counters.features}, links: ${counters.links}` - ) -} - -module.exports = { - async up(knex) { - const cards = loadCards() - if (!cards) return - - console.log(`[seed-features] loaded ${cards.length} cards`) - - const uniqueLabels = [...new Set(cards.map((c) => c.label).filter(Boolean))] - console.log( - `[seed-features] ${uniqueLabels.length} unique labels: ${uniqueLabels.join(", ")}` - ) - - const now = new Date() - await seedTags(knex, uniqueLabels, now) - await seedFeatures(knex, cards, uniqueLabels, now) - }, - - async down() { - throw new Error("Irreversible: feature seed migration has no down step") - }, -} diff --git a/apps/strapi/database/migrations/2026.05.11T11:30.seed-feature-icons.js b/apps/strapi/database/migrations/2026.05.11T11:30.seed-feature-icons.js deleted file mode 100644 index 46e3e06..0000000 --- a/apps/strapi/database/migrations/2026.05.11T11:30.seed-feature-icons.js +++ /dev/null @@ -1,82 +0,0 @@ -/* eslint-disable no-console */ -/** - * Attach a placeholder icon to every feature row (draft + published). - * - * Looked up by `hash` in the `files` table — the upload-plugin hash is stable - * per environment. Override `PLACEHOLDER_ICON_HASH` (or the env var) before - * running this migration in a new environment where the icon was uploaded with - * a different filename. - * - * Idempotent: skips features that already have an icon morph row. - */ - -"use strict" - -const FILES_TABLE = "files" -const FILES_MORPH_TABLE = "files_related_mph" -const FEATURES_TABLE = "features" -const FEATURE_UID = "api::feature.feature" - -const PLACEHOLDER_ICON_HASH = "Layout_2d5ef707ac" - -async function loadPlaceholderIconId(knex) { - const row = await knex(FILES_TABLE) - .where({ hash: PLACEHOLDER_ICON_HASH }) - .first("id") - - if (!row) { - console.log( - `[seed-feature-icons] placeholder icon (hash ${PLACEHOLDER_ICON_HASH}) not found in ${FILES_TABLE}, skipping` - ) - - return null - } - - return row.id -} - -async function loadFeaturesMissingIcon(knex) { - return knex(FEATURES_TABLE) - .leftJoin(FILES_MORPH_TABLE, function joinIcon() { - this.on(`${FILES_MORPH_TABLE}.related_id`, "=", `${FEATURES_TABLE}.id`) - .andOnVal(`${FILES_MORPH_TABLE}.related_type`, "=", FEATURE_UID) - .andOnVal(`${FILES_MORPH_TABLE}.field`, "=", "icon") - }) - .whereNull(`${FILES_MORPH_TABLE}.id`) - .select(`${FEATURES_TABLE}.id`) -} - -module.exports = { - async up(knex) { - const iconFileId = await loadPlaceholderIconId(knex) - if (!iconFileId) return - - const missing = await loadFeaturesMissingIcon(knex) - - if (missing.length === 0) { - console.log(`[seed-feature-icons] every feature already has an icon`) - - return - } - - const rows = missing.map((f) => ({ - file_id: iconFileId, - related_id: f.id, - related_type: FEATURE_UID, - field: "icon", - order: 1, - })) - - await knex(FILES_MORPH_TABLE).insert(rows) - - console.log( - `[seed-feature-icons] attached placeholder icon to ${rows.length} feature row(s)` - ) - }, - - async down() { - throw new Error( - "Irreversible: placeholder icon morph rows are intentionally left in place" - ) - }, -} diff --git a/apps/strapi/database/migrations/features.json b/apps/strapi/database/migrations/features.json deleted file mode 100644 index 38bac1b..0000000 --- a/apps/strapi/database/migrations/features.json +++ /dev/null @@ -1,238 +0,0 @@ -{ - "cards": [ - { - "label": "Content Management", - "title": "Internationalization (i18n)", - "description": "Manage and publish content in multiple locales, allowing for localized content strategies and broader audience engagement.", - "url": "https://strapi.io/features/internationalization" - }, - { - "label": "Content Management", - "title": "Blocks Editor", - "description": "Drag and drop rich text in a user-friendly WYSIWYG environment, making content creation seamless and straightforward.", - "url": "https://docs.strapi.io/cms/features/content-type-builder" - }, - { - "label": "Content Management", - "title": "Dynamic Zones", - "description": "Editors adjust layouts with dynamic components for flexible page design.", - "url": "https://strapi.io/features/dynamic-zone" - }, - { - "label": "Content Management", - "title": "Content History", - "description": "No more headaches over lost edits or accidental changes. Keep your workflow smooth and stress-free with Content History.", - "url": "https://strapi.io/features/content-history" - }, - { - "label": "Content Management", - "title": "Live Preview", - "description": "Live preview changes before publishing to enable collaboration and confident content validation.", - "url": "https://strapi.io/features/live-preview" - }, - { - "label": "Customization", - "title": "Conditional Fields", - "description": "Give editors a smarter, more intuitive content-editing experience by showing only the fields that matter.", - "url": "https://strapi.io/features/conditional-fields" - }, - { - "label": "Create APIs", - "title": "Content GraphQL API", - "description": "The GraphQL API allows performing queries and mutations to interact with the content-types through Strapi's GraphQL plugin.", - "url": "https://strapi.io/features/customizable-api" - }, - { - "label": "Security", - "title": "API Tokens", - "description": "Manage end-user access to your API securely with customizable tokens, ensuring that data exposure is controlled and intentional.", - "url": "https://docs.strapi.io/user-docs/settings/API-tokens" - }, - { - "label": "Security", - "title": "TypeScript Support", - "description": "Improve coding practices and reduce errors with full TypeScript support, ensuring a more secure and maintainable codebase.", - "url": "https://docs.strapi.io/dev-docs/typescript/development" - }, - { - "label": "Security", - "title": "Audit Logs", - "description": "Keep a detailed record of every action taken within your CMS, allowing for better security monitoring and compliance.", - "url": "https://strapi.io/features/audit-logs" - }, - { - "label": "Security", - "title": "RBAC", - "description": "Control access and permissions within your CMS with advanced role-based access control, enhancing security and compliance.", - "url": "https://strapi.io/features/custom-roles-and-permissions" - }, - { - "label": "Security", - "title": "Single Sign-On (SSO)", - "description": "Simplify user authentication with Single Sign-On from major third-party providers, streamlining access while maintaining security.", - "url": "https://strapi.io/features/single-sign-on-sso" - }, - { - "label": "Hosting", - "title": "Upload Providers", - "description": "Use your preferred CDN for faster content delivery, optimizing user experience across different geographies.", - "url": "https://docs.strapi.io/cloud/advanced/upload" - }, - { - "label": "Hosting", - "title": "PostgreSQL Database", - "description": "Integrate smoothly with your favorite SQL databases, customizing data storage and retrieval to fit your needs.", - "url": "https://docs.strapi.io/cloud/advanced/database" - }, - { - "label": "Hosting", - "title": "Cloud CLI", - "description": "Deploy your Strapi projects directly from the terminal, making the process straightforward and efficient.", - "url": "https://docs.strapi.io/cloud/cli/cloud-cli" - }, - { - "label": "Hosting", - "title": "GitLab Integration", - "description": "Deploy from your GitLab repository, maintaining workflow efficiency and consistency.", - "url": "https://docs.strapi.io/cloud/account/account-settings" - }, - { - "label": "Create APIs", - "title": "Content REST API", - "description": "Integrate dynamic content delivery with a standard RESTful approach, making it easy to fetch and display content as needed.", - "url": "https://strapi.io/features/customizable-api" - }, - { - "label": "Hosting", - "title": "GitHub Integration", - "description": "Deploy code directly from your GitHub repository, facilitating continuous integration and faster updates.", - "url": "https://docs.strapi.io/cloud/account/account-settings" - }, - { - "label": "Hosting", - "title": "Built-in Email Provider", - "description": "Send emails directly through a built-in provider, streamlining communication and marketing efforts.", - "url": "https://docs.strapi.io/cloud/advanced/email" - }, - { - "label": "Hosting", - "title": "Built-in CDN", - "description": "Serve content efficiently with a built-in CDN, reducing load times and improving accessibility.", - "url": "https://docs.strapi.io/cloud/getting-started/caching" - }, - { - "label": "Hosting", - "title": "Backups", - "description": "Secure your CMS data with automatic backups, protecting against data loss and ensuring business continuity.", - "url": "https://docs.strapi.io/cloud/projects/settings" - }, - { - "label": "Hosting", - "title": "Strapi Cloud", - "description": "Host your CMS with our dedicated cloud service, designed for performance and ease of use.", - "url": "https://strapi.io/cloud" - }, - { - "label": "Customization", - "title": "Widget API", - "description": "Build your own widgets to surface the metrics, links, and insights you care about most. Add charts, content summaries, custom workflows, and more, right on your homepage.", - "url": "https://docs.strapi.io/cms/admin-panel-customization/homepage" - }, - { - "label": "Customization", - "title": "Plugin SDK", - "description": "The Plugin SDK is set of commands provided to create a plugin from scratch, link it to an existing project, and publish it.", - "url": "https://docs.strapi.io/dev-docs/plugins/development/plugin-sdk" - }, - { - "label": "Customization", - "title": "Plugin API", - "description": "Extend your CMS with custom plugins, creating tailored solutions for unique project needs.", - "url": "https://docs.strapi.io/dev-docs/plugins" - }, - { - "label": "Customization", - "title": "Design System", - "description": "The Strapi Design System is a collection of standards, foundations, components & hooks to make contributions and plugins more efficient and cohesive.", - "url": "https://design-system.strapi.io/?path=%2Fdocs%2Fgetting-started-welcome--docs" - }, - { - "label": "Customization", - "title": "Document Service API", - "description": "The Document Service API is built on top of the Query Engine API and used to perform CRUD (create, retrieve, update, and delete) operations on documents.", - "url": "https://docs.strapi.io/dev-docs/api/document-service" - }, - { - "label": "Create APIs", - "title": "Relations", - "description": "Design a custom content architecture that links your data in meaningful ways, organizing data in a way that makes sense for your application and your team.", - "url": "https://strapi.io/features/relations" - }, - { - "label": "Customization", - "title": "Custom Layout", - "description": "Personalize the content editing experience with custom layouts, enhancing usability and editor satisfaction.", - "url": "https://docs.strapi.io/dev-docs/plugins/admin-panel-api" - }, - { - "label": "Customization", - "title": "Webhooks", - "description": "Trigger actions automatically in your CMS or other integrated systems, improving operational efficiency.", - "url": "https://docs.strapi.io/dev-docs/backend-customization/webhooks" - }, - { - "label": "Customization", - "title": "Custom Fields", - "description": "Add any type of fields to your project, customizing data structures to suit your specific requirements.", - "url": "https://docs.strapi.io/dev-docs/custom-fields" - }, - { - "label": "Customization", - "title": "Marketplace", - "description": "Benefit from additional features developed by the community, enhancing your CMS with new functionalities.", - "url": "https://market.strapi.io/" - }, - { - "label": "Collaboration", - "title": "RBAC for admins", - "description": "Control access and permissions within your CMS with advanced role-based access control, enhancing security and compliance.", - "url": "https://docs.strapi.io/dev-docs/configurations/guides/rbac" - }, - { - "label": "Collaboration", - "title": "RBAC for end-users", - "description": "Manage the permissions of end-users who consume the content that is created and managed with a Strapi application and displayed on front-end applications.", - "url": "https://docs.strapi.io/dev-docs/configurations/guides/rbac" - }, - { - "label": "Collaboration", - "title": "Review Workflows", - "description": "Set up predefined approval processes that ensure every piece of content meets your standards before it's published.", - "url": "https://strapi.io/features/review-workflow" - }, - { - "label": "Collaboration", - "title": "Releases", - "description": "Group and manage the publication of your content, allowing for better control over when and how content goes live.", - "url": "https://strapi.io/features/releases" - }, - { - "label": "Content Management", - "title": "Cron Jobs", - "description": "Automate publishing and other repetitive tasks, optimizing workflow efficiency and ensuring timely content updates.", - "url": "https://docs.strapi.io/dev-docs/configurations/cron" - }, - { - "label": "Content Management", - "title": "Media Library", - "description": "Efficiently organize, store, and access media files, streamlining content creation and management processes.", - "url": "https://strapi.io/features/media-library" - }, - { - "label": "Create APIs", - "title": "Content-Type Builder", - "description": "Create and manage content models through a user-friendly interface, simplifying the development process.", - "url": "https://strapi.io/features/content-types-builder" - } - ] -} diff --git a/apps/strapi/src/api/feature-tag/content-types/feature-tag/schema.json b/apps/strapi/src/api/feature-tag/content-types/feature-tag/schema.json deleted file mode 100644 index 4941efe..0000000 --- a/apps/strapi/src/api/feature-tag/content-types/feature-tag/schema.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "kind": "collectionType", - "collectionName": "feature_tags", - "info": { - "singularName": "feature-tag", - "pluralName": "feature-tags", - "displayName": "Feature tag" - }, - "options": { - "draftAndPublish": true - }, - "pluginOptions": {}, - "attributes": { - "title": { - "type": "string" - }, - "features": { - "type": "relation", - "relation": "oneToMany", - "target": "api::feature.feature", - "mappedBy": "feature_tag" - } - } -} diff --git a/apps/strapi/src/api/feature-tag/controllers/feature-tag.ts b/apps/strapi/src/api/feature-tag/controllers/feature-tag.ts deleted file mode 100644 index 0ade4b5..0000000 --- a/apps/strapi/src/api/feature-tag/controllers/feature-tag.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * feature-tag controller - */ - -import { factories } from "@strapi/strapi" - -export default factories.createCoreController("api::feature-tag.feature-tag") diff --git a/apps/strapi/src/api/feature-tag/routes/feature-tag.ts b/apps/strapi/src/api/feature-tag/routes/feature-tag.ts deleted file mode 100644 index 1dc0522..0000000 --- a/apps/strapi/src/api/feature-tag/routes/feature-tag.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * feature-tag router - */ - -import { factories } from "@strapi/strapi" - -export default factories.createCoreRouter("api::feature-tag.feature-tag") diff --git a/apps/strapi/src/api/feature-tag/services/feature-tag.ts b/apps/strapi/src/api/feature-tag/services/feature-tag.ts deleted file mode 100644 index c0f58a6..0000000 --- a/apps/strapi/src/api/feature-tag/services/feature-tag.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * feature-tag service - */ - -import { factories } from "@strapi/strapi" - -export default factories.createCoreService("api::feature-tag.feature-tag") diff --git a/apps/strapi/src/api/feature/content-types/feature/schema.json b/apps/strapi/src/api/feature/content-types/feature/schema.json deleted file mode 100644 index 0b38748..0000000 --- a/apps/strapi/src/api/feature/content-types/feature/schema.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "kind": "collectionType", - "collectionName": "features", - "info": { - "singularName": "feature", - "pluralName": "features", - "displayName": "Feature" - }, - "options": { - "draftAndPublish": true - }, - "pluginOptions": {}, - "attributes": { - "title": { - "type": "string" - }, - "description": { - "type": "text" - }, - "icon": { - "type": "media", - "multiple": false, - "allowedTypes": [ - "images" - ] - }, - "feature_tag": { - "type": "relation", - "relation": "manyToOne", - "target": "api::feature-tag.feature-tag", - "inversedBy": "features" - }, - "url": { - "type": "string" - } - } -} diff --git a/apps/strapi/src/api/feature/controllers/feature.ts b/apps/strapi/src/api/feature/controllers/feature.ts deleted file mode 100644 index 2c95e09..0000000 --- a/apps/strapi/src/api/feature/controllers/feature.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * feature controller - */ - -import { factories } from "@strapi/strapi" - -export default factories.createCoreController("api::feature.feature") diff --git a/apps/strapi/src/api/feature/routes/feature.ts b/apps/strapi/src/api/feature/routes/feature.ts deleted file mode 100644 index 55af06a..0000000 --- a/apps/strapi/src/api/feature/routes/feature.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * feature router - */ - -import { factories } from "@strapi/strapi" - -export default factories.createCoreRouter("api::feature.feature") diff --git a/apps/strapi/src/api/feature/services/feature.ts b/apps/strapi/src/api/feature/services/feature.ts deleted file mode 100644 index 18df8b5..0000000 --- a/apps/strapi/src/api/feature/services/feature.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * feature service - */ - -import { factories } from "@strapi/strapi" - -export default factories.createCoreService("api::feature.feature") diff --git a/apps/strapi/types/generated/contentTypes.d.ts b/apps/strapi/types/generated/contentTypes.d.ts index 7d32888..d5740d4 100644 --- a/apps/strapi/types/generated/contentTypes.d.ts +++ b/apps/strapi/types/generated/contentTypes.d.ts @@ -951,70 +951,6 @@ export interface ApiCountryCountry extends Struct.CollectionTypeSchema { } } -export interface ApiFeatureTagFeatureTag extends Struct.CollectionTypeSchema { - collectionName: "feature_tags" - info: { - displayName: "Feature tag" - pluralName: "feature-tags" - singularName: "feature-tag" - } - options: { - draftAndPublish: true - } - attributes: { - createdAt: Schema.Attribute.DateTime - createdBy: Schema.Attribute.Relation<"oneToOne", "admin::user"> & - Schema.Attribute.Private - features: Schema.Attribute.Relation<"oneToMany", "api::feature.feature"> - locale: Schema.Attribute.String & Schema.Attribute.Private - localizations: Schema.Attribute.Relation< - "oneToMany", - "api::feature-tag.feature-tag" - > & - Schema.Attribute.Private - publishedAt: Schema.Attribute.DateTime - title: Schema.Attribute.String - updatedAt: Schema.Attribute.DateTime - updatedBy: Schema.Attribute.Relation<"oneToOne", "admin::user"> & - Schema.Attribute.Private - } -} - -export interface ApiFeatureFeature extends Struct.CollectionTypeSchema { - collectionName: "features" - info: { - displayName: "Feature" - pluralName: "features" - singularName: "feature" - } - options: { - draftAndPublish: true - } - attributes: { - createdAt: Schema.Attribute.DateTime - createdBy: Schema.Attribute.Relation<"oneToOne", "admin::user"> & - Schema.Attribute.Private - description: Schema.Attribute.Text - feature_tag: Schema.Attribute.Relation< - "manyToOne", - "api::feature-tag.feature-tag" - > - icon: Schema.Attribute.Media<"images"> - locale: Schema.Attribute.String & Schema.Attribute.Private - localizations: Schema.Attribute.Relation< - "oneToMany", - "api::feature.feature" - > & - Schema.Attribute.Private - publishedAt: Schema.Attribute.DateTime - title: Schema.Attribute.String - updatedAt: Schema.Attribute.DateTime - updatedBy: Schema.Attribute.Relation<"oneToOne", "admin::user"> & - Schema.Attribute.Private - url: Schema.Attribute.String - } -} - export interface ApiFooterFooter extends Struct.SingleTypeSchema { collectionName: "footers" info: { @@ -2197,8 +2133,6 @@ declare module "@strapi/strapi" { "api::cms-comparison.cms-comparison": ApiCmsComparisonCmsComparison "api::cms.cms": ApiCmsCms "api::country.country": ApiCountryCountry - "api::feature-tag.feature-tag": ApiFeatureTagFeatureTag - "api::feature.feature": ApiFeatureFeature "api::footer.footer": ApiFooterFooter "api::global.global": ApiGlobalGlobal "api::header.header": ApiHeaderHeader