diff --git a/app/admin-shell/scripts/admin-shell-overrides.js b/app/admin-shell/scripts/admin-shell-overrides.js
index 1e69e401d..2a9e4886d 100644
--- a/app/admin-shell/scripts/admin-shell-overrides.js
+++ b/app/admin-shell/scripts/admin-shell-overrides.js
@@ -81,7 +81,8 @@ module.exports = {
'pats',
'featureFlags',
'petoberfest',
- 'channels'
+ 'channels',
+ 'channel-collections'
],
//
@@ -163,7 +164,8 @@ module.exports = {
pats: 'pt',
featureFlags: 'ffl',
petoberfest: 'pb',
- channels: 'ch'
+ channels: 'ch',
+ 'channel-collections': 'cc'
},
//
diff --git a/app/admin-shell/webapp/controller/Shell.controller.js b/app/admin-shell/webapp/controller/Shell.controller.js
index f3a17963a..076810d26 100644
--- a/app/admin-shell/webapp/controller/Shell.controller.js
+++ b/app/admin-shell/webapp/controller/Shell.controller.js
@@ -61,7 +61,8 @@ sap.ui.define([
petoberfest: "petoberfest",
petoberfestContests: "petoberfestContests",
topicClusters: "topicClusters",
- channels: "channels"
+ channels: "channels",
+ channelCollections: "channelCollections"
};
var NAV_KEY_TO_TITLE = {
@@ -119,7 +120,8 @@ sap.ui.define([
petoberfest: "Pet Photo Moderation",
petoberfestContests: "Petoberfest Contests",
topicClusters: "Topic Clusters",
- channels: "Channels"
+ channels: "Channels",
+ channelCollections: "Channel Collections"
};
return Controller.extend("sap.tutorials.admin.shell.controller.Shell", {
diff --git a/app/admin-shell/webapp/manifest.json b/app/admin-shell/webapp/manifest.json
index 121be54d5..a2153203a 100644
--- a/app/admin-shell/webapp/manifest.json
+++ b/app/admin-shell/webapp/manifest.json
@@ -94,7 +94,8 @@
"sap.tutorials.admin.pats": "./components/pats",
"sap.tutorials.admin.featureFlags": "./components/featureFlags",
"sap.tutorials.admin.petoberfest": "./components/petoberfest",
- "sap.tutorials.admin.channels": "./components/channels"
+ "sap.tutorials.admin.channels": "./components/channels",
+ "sap.tutorials.admin.channelCollections": "./components/channel-collections"
},
"models": {
"admin": {
@@ -386,6 +387,12 @@
"settings": {},
"componentData": {},
"lazy": true
+ },
+ "channelCollectionsComponent": {
+ "name": "sap.tutorials.admin.channelCollections",
+ "settings": {},
+ "componentData": {},
+ "lazy": true
}
},
"services": {
@@ -906,6 +913,16 @@
}
]
},
+ {
+ "name": "channelCollections",
+ "pattern": "channel-collections",
+ "target": [
+ {
+ "name": "channelCollectionsTarget",
+ "prefix": "cc"
+ }
+ ]
+ },
{
"name": "feedbackDashboard",
"pattern": "feedback/dashboard",
@@ -1256,6 +1273,13 @@
"viewLevel": 1,
"prefix": "ch"
},
+ "channelCollectionsTarget": {
+ "type": "Component",
+ "usage": "channelCollectionsComponent",
+ "id": "channelCollectionsTarget",
+ "viewLevel": 1,
+ "prefix": "cc"
+ },
"feedbackDashboardTarget": {
"viewName": "TutorialFeedbackDashboard",
"viewLevel": 1
diff --git a/app/admin-shell/webapp/model/navigation.json b/app/admin-shell/webapp/model/navigation.json
index 80b83f456..ab7794964 100644
--- a/app/admin-shell/webapp/model/navigation.json
+++ b/app/admin-shell/webapp/model/navigation.json
@@ -23,6 +23,7 @@
{ "key": "advocates", "title": "Advocates" },
{ "key": "alerts", "title": "Alerts" },
{ "key": "channels", "title": "Channels" },
+ { "key": "channelCollections", "title": "Channel Collections" },
{ "key": "operations", "title": "Featured Tasks" }
]
},
diff --git a/app/admin/channel-collections/package.json b/app/admin/channel-collections/package.json
new file mode 100644
index 000000000..fa4f066f7
--- /dev/null
+++ b/app/admin/channel-collections/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "channel-collections-admin",
+ "version": "0.0.1",
+ "private": true,
+ "description": "Channel Collections - Admin Fiori Elements",
+ "sapux": true,
+ "scripts": {
+ "start": "fiori run --open index.html",
+ "build": "ui5 build --clean-dest"
+ },
+ "devDependencies": {
+ "@sap/ux-specification": "latest",
+ "@ui5/cli": "^4.0.0"
+ }
+}
diff --git a/app/admin/channel-collections/ui5.yaml b/app/admin/channel-collections/ui5.yaml
new file mode 100644
index 000000000..437804d82
--- /dev/null
+++ b/app/admin/channel-collections/ui5.yaml
@@ -0,0 +1,12 @@
+specVersion: "4.0"
+metadata:
+ name: sap.tutorials.admin.channelCollections
+type: application
+framework:
+ name: SAPUI5
+ version: "1.136.0"
+ libraries:
+ - name: sap.m
+ - name: sap.ui.core
+ - name: sap.ushell
+ - name: sap.fe.templates
diff --git a/app/admin/channel-collections/webapp/Component.js b/app/admin/channel-collections/webapp/Component.js
new file mode 100644
index 000000000..2565f7d12
--- /dev/null
+++ b/app/admin/channel-collections/webapp/Component.js
@@ -0,0 +1,4 @@
+sap.ui.define(["sap/fe/core/AppComponent"], function (AppComponent) {
+ "use strict";
+ return AppComponent.extend("sap.tutorials.admin.channelCollections.Component", { metadata: { manifest: "json" } });
+});
diff --git a/app/admin/channel-collections/webapp/i18n/i18n.properties b/app/admin/channel-collections/webapp/i18n/i18n.properties
new file mode 100644
index 000000000..6dd7090b9
--- /dev/null
+++ b/app/admin/channel-collections/webapp/i18n/i18n.properties
@@ -0,0 +1,2 @@
+appTitle=Channel Collections
+appDescription=Manage editorial channel collections
diff --git a/app/admin/channel-collections/webapp/manifest.json b/app/admin/channel-collections/webapp/manifest.json
new file mode 100644
index 000000000..650cc0603
--- /dev/null
+++ b/app/admin/channel-collections/webapp/manifest.json
@@ -0,0 +1,79 @@
+{
+ "_version": "1.65.0",
+ "sap.app": {
+ "id": "sap.tutorials.admin.channelCollections",
+ "type": "application",
+ "title": "{{appTitle}}",
+ "description": "{{appDescription}}",
+ "applicationVersion": { "version": "0.0.1" },
+ "i18n": "i18n/i18n.properties",
+ "dataSources": {
+ "mainService": {
+ "uri": "/admin/",
+ "type": "OData",
+ "settings": { "odataVersion": "4.0" }
+ }
+ },
+ "crossNavigation": {
+ "inbounds": {
+ "ChannelCollections-manage": {
+ "semanticObject": "ChannelCollections",
+ "action": "manage",
+ "title": "{{appTitle}}",
+ "signature": { "parameters": {}, "additionalParameters": "allowed" }
+ }
+ }
+ }
+ },
+ "sap.ui5": {
+ "dependencies": {
+ "minUI5Version": "1.136.0",
+ "libs": { "sap.fe.templates": {} }
+ },
+ "models": {
+ "": {
+ "dataSource": "mainService",
+ "preload": true,
+ "settings": {
+ "synchronizationMode": "None",
+ "operationMode": "Server",
+ "autoExpandSelect": true,
+ "earlyRequests": true
+ }
+ },
+ "i18n": {
+ "type": "sap.ui.model.resource.ResourceModel",
+ "settings": { "bundleName": "sap.tutorials.admin.channelCollections.i18n.i18n" }
+ }
+ },
+ "routing": {
+ "routes": [
+ { "name": "ChannelCollectionsList", "pattern": ":?query:", "target": "ChannelCollectionsList" },
+ { "name": "ChannelCollectionsObject", "pattern": "ChannelCollections({key}):?query:", "target": "ChannelCollectionsObject" }
+ ],
+ "targets": {
+ "ChannelCollectionsList": {
+ "type": "Component",
+ "id": "ChannelCollectionsList",
+ "name": "sap.fe.templates.ListReport",
+ "options": {
+ "settings": {
+ "contextPath": "/ChannelCollections",
+ "initialLoad": "Enabled"
+ }
+ }
+ },
+ "ChannelCollectionsObject": {
+ "type": "Component",
+ "id": "ChannelCollectionsObject",
+ "name": "sap.fe.templates.ObjectPage",
+ "options": {
+ "settings": {
+ "contextPath": "/ChannelCollections"
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/db/channels.cds b/db/channels.cds
index 094737819..1cd35f62f 100644
--- a/db/channels.cds
+++ b/db/channels.cds
@@ -1,6 +1,7 @@
namespace com.sap.developers.ims;
using { managed, cuid } from '@sap/cds/common';
+using { com.sap.developers.ims.AuthoringStatus } from './homepage';
type ChannelOwnerType : String enum {
SAP_Official; SAP_Developer_Advocate; SAP_Executive;
@@ -41,3 +42,21 @@ entity Channels : cuid, managed {
linkStatusOverride : String(20);
lastChecked : Timestamp;
}
+
+// --- P2: editorial clusters (LLM-drafted, human-reviewed) --------------------
+entity ChannelCollections : cuid, managed {
+ slug : String(80) @mandatory;
+ title : String(140) @mandatory;
+ intro : String(1200);
+ sortOrder : Integer default 100;
+ isPublished : Boolean default false;
+ authoringStatus : AuthoringStatus default 'BLANK' @assert.range;
+ items : Composition of many ChannelCollectionItems on items.collection = $self;
+}
+
+entity ChannelCollectionItems : cuid {
+ collection : Association to ChannelCollections;
+ channel : Association to Channels;
+ sortOrder : Integer default 100;
+ blurb : String(280);
+}
diff --git a/db/last-dev/csn.json b/db/last-dev/csn.json
index 1539cd15b..2a40ac451 100644
--- a/db/last-dev/csn.json
+++ b/db/last-dev/csn.json
@@ -2241,6 +2241,111 @@
},
"@cds.persistence.name": "COM_SAP_DEVELOPERS_IMS_CHANNELS"
},
+ "com.sap.developers.ims.ChannelCollections": {
+ "kind": "entity",
+ "@cds.persistence.journal": true,
+ "elements": {
+ "ID": {
+ "key": true,
+ "type": "cds.String",
+ "length": 36,
+ "@cds.persistence.name": "ID"
+ },
+ "createdAt": {
+ "type": "cds.Timestamp",
+ "@cds.persistence.name": "CREATEDAT"
+ },
+ "createdBy": {
+ "type": "cds.String",
+ "length": 255,
+ "@cds.persistence.name": "CREATEDBY"
+ },
+ "modifiedAt": {
+ "type": "cds.Timestamp",
+ "@cds.persistence.name": "MODIFIEDAT"
+ },
+ "modifiedBy": {
+ "type": "cds.String",
+ "length": 255,
+ "@cds.persistence.name": "MODIFIEDBY"
+ },
+ "slug": {
+ "type": "cds.String",
+ "length": 80,
+ "@cds.persistence.name": "SLUG"
+ },
+ "title": {
+ "type": "cds.String",
+ "length": 140,
+ "@cds.persistence.name": "TITLE"
+ },
+ "intro": {
+ "type": "cds.String",
+ "length": 1200,
+ "@cds.persistence.name": "INTRO"
+ },
+ "sortOrder": {
+ "type": "cds.Integer",
+ "default": {
+ "val": 100
+ },
+ "@cds.persistence.name": "SORTORDER"
+ },
+ "isPublished": {
+ "type": "cds.Boolean",
+ "default": {
+ "val": false
+ },
+ "@cds.persistence.name": "ISPUBLISHED"
+ },
+ "authoringStatus": {
+ "type": "cds.String",
+ "default": {
+ "val": "BLANK"
+ },
+ "length": 5000,
+ "@cds.persistence.name": "AUTHORINGSTATUS"
+ }
+ },
+ "@cds.persistence.name": "COM_SAP_DEVELOPERS_IMS_CHANNELCOLLECTIONS"
+ },
+ "com.sap.developers.ims.ChannelCollectionItems": {
+ "kind": "entity",
+ "@cds.persistence.journal": true,
+ "elements": {
+ "ID": {
+ "key": true,
+ "type": "cds.String",
+ "length": 36,
+ "@cds.persistence.name": "ID"
+ },
+ "collection_ID": {
+ "type": "cds.String",
+ "length": 36,
+ "@odata.foreignKey4": "collection",
+ "@cds.persistence.name": "COLLECTION_ID"
+ },
+ "channel_ID": {
+ "type": "cds.String",
+ "length": 36,
+ "@odata.foreignKey4": "channel",
+ "@cds.persistence.name": "CHANNEL_ID"
+ },
+ "sortOrder": {
+ "type": "cds.Integer",
+ "default": {
+ "val": 100
+ },
+ "@cds.persistence.name": "SORTORDER"
+ },
+ "blurb": {
+ "type": "cds.String",
+ "length": 280,
+ "@cds.persistence.name": "BLURB"
+ }
+ },
+ "@cds.persistence.name": "COM_SAP_DEVELOPERS_IMS_CHANNELCOLLECTIONITEMS"
+ },
"com.sap.developers.ims.FailedEmails": {
"kind": "entity",
"@cds.persistence.journal": true,
diff --git a/db/persistence.cds b/db/persistence.cds
index 0d2e2fc98..f8a154c34 100644
--- a/db/persistence.cds
+++ b/db/persistence.cds
@@ -52,3 +52,5 @@ annotate ims.MetricSnapshots with @cds.persistence.journal;
annotate ims.PublishTimings with @cds.persistence.journal;
annotate ims.TutorialValidationRules with @cds.persistence.journal;
annotate ims.Channels with @cds.persistence.journal;
+annotate ims.ChannelCollections with @cds.persistence.journal;
+annotate ims.ChannelCollectionItems with @cds.persistence.journal;
diff --git a/db/src/com.sap.developers.ims.ChannelCollectionItems.hdbmigrationtable b/db/src/com.sap.developers.ims.ChannelCollectionItems.hdbmigrationtable
new file mode 100644
index 000000000..81d728f79
--- /dev/null
+++ b/db/src/com.sap.developers.ims.ChannelCollectionItems.hdbmigrationtable
@@ -0,0 +1,9 @@
+== version=1
+COLUMN TABLE com_sap_developers_ims_ChannelCollectionItems (
+ ID NVARCHAR(36) NOT NULL,
+ collection_ID NVARCHAR(36),
+ channel_ID NVARCHAR(36),
+ sortOrder INTEGER DEFAULT 100,
+ blurb NVARCHAR(280),
+ PRIMARY KEY(ID)
+)
diff --git a/db/src/com.sap.developers.ims.ChannelCollections.hdbmigrationtable b/db/src/com.sap.developers.ims.ChannelCollections.hdbmigrationtable
new file mode 100644
index 000000000..31181dd09
--- /dev/null
+++ b/db/src/com.sap.developers.ims.ChannelCollections.hdbmigrationtable
@@ -0,0 +1,15 @@
+== version=1
+COLUMN TABLE com_sap_developers_ims_ChannelCollections (
+ ID NVARCHAR(36) NOT NULL,
+ createdAt TIMESTAMP,
+ createdBy NVARCHAR(255),
+ modifiedAt TIMESTAMP,
+ modifiedBy NVARCHAR(255),
+ slug NVARCHAR(80),
+ title NVARCHAR(140),
+ intro NVARCHAR(1200),
+ sortOrder INTEGER DEFAULT 100,
+ isPublished BOOLEAN DEFAULT FALSE,
+ authoringStatus NVARCHAR(5000) DEFAULT 'BLANK',
+ PRIMARY KEY(ID)
+)
diff --git a/hugo-apps/src/channels-directory/ChannelsDirectory.vue b/hugo-apps/src/channels-directory/ChannelsDirectory.vue
index 5ed895b2e..fe35c9706 100644
--- a/hugo-apps/src/channels-directory/ChannelsDirectory.vue
+++ b/hugo-apps/src/channels-directory/ChannelsDirectory.vue
@@ -1,8 +1,9 @@
+
+
+ {{ col.title }}
+ {{ col.intro }}
+
+
+