diff --git a/.github/workflows/on-demand-setup-teams-field-groups.yml b/.github/workflows/on-demand-setup-teams-field-groups.yml new file mode 100644 index 00000000000..482e2da9b3c --- /dev/null +++ b/.github/workflows/on-demand-setup-teams-field-groups.yml @@ -0,0 +1,41 @@ +name: Setup Teams Field Groups +on: + workflow_dispatch: + +jobs: + setup_teams_field_groups: + runs-on: ubuntu-latest + environment: Production + container: + image: ghcr.io/yldio/asap-hub/node-python-sq:7b77d99657ab3718ed548ba366ffbcbb15315fd8 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup + id: setup + uses: ./.github/actions/setup-environment + with: + environment-name: Production + - name: Setup Teams Field Groups + run: | + yarn workspace @asap-hub/contentful setup-teams-field-groups + env: + CONTENTFUL_MANAGEMENT_ACCESS_TOKEN: ${{ secrets.CONTENTFUL_MANAGEMENT_TOKEN }} + CONTENTFUL_SPACE_ID: ${{ steps.setup.outputs.crn-contentful-space-id }} + CONTENTFUL_ENV_ID: Production + + notify_failure: + runs-on: ubuntu-latest + needs: [setup_teams_field_groups] + if: ${{ failure() }} + steps: + - name: Checkout + uses: actions/checkout@v3 + - uses: ./.github/actions/slack/ + with: + message: Setup teams field groups failed + webhook: ${{ secrets.SLACK_WEBHOOK }} + status: failure diff --git a/.github/workflows/reusable-deployment.yml b/.github/workflows/reusable-deployment.yml index 431d1c8b532..dcf571bb2f0 100644 --- a/.github/workflows/reusable-deployment.yml +++ b/.github/workflows/reusable-deployment.yml @@ -392,6 +392,33 @@ jobs: CONTENTFUL_MANAGEMENT_ACCESS_TOKEN: ${{ secrets.CONTENTFUL_MANAGEMENT_TOKEN }} CONTENTFUL_SPACE_ID: ${{ steps.setup.outputs.gp2-contentful-space-id }} + crn-contentful-field-groups: + needs: [crn-contentful-migrate] + # Run after migrations to set up field groups (editorLayout) + if: ${{ (inputs.environment-name == 'Branch' && inputs.crn-contentful-env != 'Development' && inputs.crn-contentful-env !='Production') || inputs.environment-name != 'Branch' }} + runs-on: ubuntu-latest + environment: ${{ inputs.environment-name }} + container: + image: ghcr.io/yldio/asap-hub/node-python-sq:7b77d99657ab3718ed548ba366ffbcbb15315fd8 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup + id: setup + uses: ./.github/actions/setup-environment + with: + environment-name: ${{ inputs.environment-name }} + - name: Setup Teams Field Groups + run: | + yarn workspace @asap-hub/contentful setup-teams-field-groups + env: + CONTENTFUL_MANAGEMENT_ACCESS_TOKEN: ${{ secrets.CONTENTFUL_MANAGEMENT_TOKEN }} + CONTENTFUL_SPACE_ID: ${{ steps.setup.outputs.crn-contentful-space-id }} + CONTENTFUL_ENV_ID: ${{ inputs.crn-contentful-env }} + ses: needs: [crn-sls-deploy] runs-on: ubuntu-latest diff --git a/packages/contentful/package.json b/packages/contentful/package.json index 9bf9c776517..c2db5ac7523 100644 --- a/packages/contentful/package.json +++ b/packages/contentful/package.json @@ -16,6 +16,7 @@ "migrate-project-data": "ts-node ./scripts/migrate-project-data.ts", "migrate-preliminary-data-sharing": "ts-node ./scripts/migrate-preliminary-data-sharing.ts", "migrate-impact-categories": "ts-node ./scripts/migrate-impact-categories.ts", + "setup-teams-field-groups": "ts-node ./scripts/setup-teams-field-groups.ts", "transform-privacy-policy": "ts-node ./scripts/transform-privacy-policy.ts", "rollback-privacy-policy": "ts-node ./scripts/rollback-privacy-policy.ts", "watch:babel": "../../scripts/build-babel.sh watch", diff --git a/packages/contentful/scripts/setup-teams-field-groups.ts b/packages/contentful/scripts/setup-teams-field-groups.ts new file mode 100644 index 00000000000..2d4f1571e59 --- /dev/null +++ b/packages/contentful/scripts/setup-teams-field-groups.ts @@ -0,0 +1,119 @@ +import * as contentful from 'contentful-management'; + +const spaceId = process.env.CONTENTFUL_SPACE_ID!; +const contentfulManagementAccessToken = + process.env.CONTENTFUL_MANAGEMENT_ACCESS_TOKEN!; +const environmentId = process.env.CONTENTFUL_ENV_ID!; + +const CONTENT_TYPE_ID = 'teams'; +const RESOURCE_GROUP_ID = 'resourceGroup'; +const RESOURCE_GROUP_NAME = 'Resource Section'; + +const RESOURCE_FIELD_IDS = [ + 'resourceTitle', + 'resourceDescription', + 'resourceButtonCopy', + 'resourceContactEmail', + 'resourceLink', +]; + +const client = contentful.createClient({ + accessToken: contentfulManagementAccessToken, +}); + +async function setupFieldGroups() { + try { + console.log('šŸš€ Starting field group setup for teams content type...'); + console.log(` Space ID: ${spaceId}`); + console.log(` Environment: ${environmentId}`); + + // Get space and environment + const space = await client.getSpace(spaceId); + const environment = await space.getEnvironment(environmentId); + + // Get the current editor interface + console.log('\nšŸ“– Fetching current editor interface...'); + const editorInterface = + await environment.getEditorInterfaceForContentType(CONTENT_TYPE_ID); + + // Get all field IDs from the content type + const contentType = await environment.getContentType(CONTENT_TYPE_ID); + const allFieldIds = contentType.fields.map((field) => field.id); + + console.log(` Found ${allFieldIds.length} fields in content type`); + + // Create the editor layout with field groups + console.log(`\n✨ Creating field group: "${RESOURCE_GROUP_NAME}"`); + + // Get remaining fields (not in resource group) + const remainingFieldIds = allFieldIds.filter( + (fieldId) => !RESOURCE_FIELD_IDS.includes(fieldId), + ); + + // Build the editor layout with a single top-level tab containing a fieldset + // Structure: One tab with regular fields + a collapsible fieldset for resource fields + const editorLayout: any[] = [ + { + groupId: 'content', + name: 'Content', + items: [ + // Regular fields first + ...remainingFieldIds.map((fieldId) => ({ fieldId })), + // Resource fields as a nested fieldset group + { + groupId: RESOURCE_GROUP_ID, + name: RESOURCE_GROUP_NAME, + items: RESOURCE_FIELD_IDS.map((fieldId) => ({ fieldId })), + }, + ], + }, + ]; + + console.log( + ` ${remainingFieldIds.length} regular fields + "${RESOURCE_GROUP_NAME}" fieldset`, + ); + + // Update the editor interface + editorInterface.editorLayout = editorLayout; + + // groupControls defines how each group is rendered + // - Top level: topLevelTab + // - Nested groups: fieldset (collapsible section) + editorInterface.groupControls = [ + { + groupId: 'content', + widgetId: 'topLevelTab', + widgetNamespace: 'builtin', + }, + { + groupId: RESOURCE_GROUP_ID, + widgetId: 'fieldset', + widgetNamespace: 'builtin', + }, + ]; + + console.log('\nšŸ’¾ Saving editor interface...'); + await editorInterface.update(); + + // Re-publish content type to force UI refresh (workaround for Contentful bug) + console.log('\nšŸ”„ Re-publishing content type to refresh UI...'); + const publishedContentType = await contentType.publish(); + console.log(` Content type version: ${publishedContentType.sys.version}`); + + console.log('\nāœ… Field groups successfully configured!'); + console.log( + ` Resource fields (${RESOURCE_FIELD_IDS.length}) are now in "${RESOURCE_GROUP_NAME}" fieldset`, + ); + console.log( + '\nšŸŽ‰ Done! Check your Contentful web app to see the collapsible fieldset.', + ); + } catch (error) { + console.error('\nāŒ Error setting up field groups:', error); + if (error instanceof Error) { + console.error(' Message:', error.message); + } + process.exit(1); + } +} + +setupFieldGroups();