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 @@