From 86086f88330ade0cbe42ba6dd5ccd9de09b48288 Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 26 Feb 2026 08:19:48 +0530 Subject: [PATCH 1/5] Add Decap CMS admin setup for local content editing - Add static/admin/index.html and config.yml for Decap CMS - Configure collections for team, alumni, work, events, job openings, values, and project partners - Enable local_backend for local development via decap-server proxy - Add react-is dependency required by styled-components --- package-lock.json | 78 ++++----- package.json | 1 + static/admin/config.yml | 359 ++++++++++++++++++++++++++++++++++++++++ static/admin/index.html | 11 ++ 4 files changed, 412 insertions(+), 37 deletions(-) create mode 100644 static/admin/config.yml create mode 100644 static/admin/index.html diff --git a/package-lock.json b/package-lock.json index 3d0ce98b8..bbbfdc084 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "react-dom": "^16.12.0", "react-helmet": "^5.2.1", "react-icons": "^4.1.0", + "react-is": "^19.2.4", "react-multi-carousel": "^2.6.2", "react-reveal": "^1.2.2", "react-select": "^5.7.4", @@ -45,6 +46,7 @@ "version": "1.2.6", "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -2409,6 +2411,7 @@ "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.1.1", @@ -2803,6 +2806,7 @@ "version": "0.5.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.0", "debug": "^4.1.1", @@ -2815,7 +2819,8 @@ "node_modules/@humanwhocodes/object-schema": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true }, "node_modules/@iarna/toml": { "version": "2.2.5", @@ -4322,6 +4327,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, "engines": { "node": ">=6" } @@ -4779,6 +4785,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, "engines": { "node": ">=8" } @@ -9274,6 +9281,7 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" @@ -9539,6 +9547,7 @@ "version": "7.32.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "dev": true, "dependencies": { "@babel/code-frame": "7.12.11", "@eslint/eslintrc": "^0.4.3", @@ -9860,6 +9869,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, "engines": { "node": ">=10" } @@ -9868,6 +9878,7 @@ "version": "7.3.1", "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, "dependencies": { "acorn": "^7.4.0", "acorn-jsx": "^5.3.1", @@ -9881,6 +9892,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, "engines": { "node": ">=4" } @@ -10536,6 +10548,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, "dependencies": { "flat-cache": "^3.0.4" }, @@ -10753,6 +10766,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", @@ -10765,7 +10779,8 @@ "node_modules/flatted": { "version": "3.2.9", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==" + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true }, "node_modules/flush-write-stream": { "version": "1.1.1", @@ -13495,6 +13510,7 @@ "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, "dependencies": { "type-fest": "^0.20.2" }, @@ -13509,6 +13525,7 @@ "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, "engines": { "node": ">=10" }, @@ -16309,6 +16326,7 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -16519,7 +16537,8 @@ "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true }, "node_modules/lodash.template": { "version": "4.5.0", @@ -16541,7 +16560,8 @@ "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==" + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true }, "node_modules/lodash.uniq": { "version": "4.5.0", @@ -19021,6 +19041,7 @@ "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, "dependencies": { "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", @@ -20988,6 +21009,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, "engines": { "node": ">= 0.8.0" } @@ -21654,10 +21676,9 @@ } }, "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "peer": true + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.4.tgz", + "integrity": "sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==" }, "node_modules/react-lifecycles-compat": { "version": "3.0.4", @@ -22772,6 +22793,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -23991,6 +24013,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", @@ -24007,6 +24030,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "engines": { "node": ">=8" } @@ -25281,6 +25305,7 @@ "version": "6.8.1", "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", + "dev": true, "dependencies": { "ajv": "^8.0.1", "lodash.truncate": "^4.4.2", @@ -25296,6 +25321,7 @@ "version": "8.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -25310,12 +25336,14 @@ "node_modules/table/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, "node_modules/table/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "engines": { "node": ">=8" } @@ -25323,12 +25351,14 @@ "node_modules/table/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true }, "node_modules/table/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -25900,6 +25930,7 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, "dependencies": { "prelude-ls": "^1.2.1" }, @@ -25907,20 +25938,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "license": "(MIT OR CC0-1.0)", - "optional": true, - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -26012,19 +26029,6 @@ "is-typedarray": "^1.0.0" } }, - "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", diff --git a/package.json b/package.json index ef2a6f316..db762ee45 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "react-dom": "^16.12.0", "react-helmet": "^5.2.1", "react-icons": "^4.1.0", + "react-is": "^19.2.4", "react-multi-carousel": "^2.6.2", "react-reveal": "^1.2.2", "react-select": "^5.7.4", diff --git a/static/admin/config.yml b/static/admin/config.yml new file mode 100644 index 000000000..da46a6ac6 --- /dev/null +++ b/static/admin/config.yml @@ -0,0 +1,359 @@ +backend: + name: github + repo: CivicDataLab/civicdatalab.github.io + branch: main + auth_type: pkce + app_id: YOUR_GITHUB_OAUTH_APP_CLIENT_ID # Replace this after OAuth setup + +local_backend: true # Remove this line before deploying to production + +media_folder: "static/images/uploads" +public_folder: "/images/uploads" + +slug: + encoding: "ascii" + clean_accents: true + +collections: + + # ─── TEAM MEMBERS ─────────────────────────────────────────────────────────── + - label: "Team Members" + name: "team" + folder: "content/team" + path: "{{slug}}/index" + create: true + format: frontmatter + extension: md + identifier_field: name + summary: "{{name}} — {{role}}" + fields: + - { label: "Full Name", name: "name", widget: "string" } + - { label: "Role / Title", name: "role", widget: "string" } + - { label: "Description", name: "description", widget: "text", required: false } + - { label: "Quote", name: "quote", widget: "string", required: false } + - { label: "Profile Image", name: "image", widget: "image", required: false } + - { label: "GitHub URL", name: "github", widget: "string", required: false } + - { label: "Twitter URL", name: "twitter", widget: "string", required: false } + - { label: "LinkedIn URL", name: "linkedin", widget: "string", required: false } + - { label: "Medium URL", name: "medium", widget: "string", required: false } + - { label: "Accent Color", name: "accentcolor", widget: "string", required: false } + - { label: "Sectors", name: "sectors", widget: "string", required: false, hint: "Comma-separated. e.g. Public Finance, Climate Action" } + - { label: "Projects", name: "projects", widget: "string", required: false, hint: "Comma-separated project names" } + - { label: "Bio (body text)", name: "body", widget: "markdown", required: false } + + # ─── ALUMNI ───────────────────────────────────────────────────────────────── + - label: "Alumni" + name: "alumnus" + folder: "content/alumnus" + path: "{{slug}}/index" + create: true + format: frontmatter + extension: md + identifier_field: name + summary: "{{name}} — {{role}}" + fields: + - { label: "Full Name", name: "name", widget: "string" } + - { label: "Role / Title", name: "role", widget: "string", required: false } + - { label: "Description", name: "description", widget: "text", required: false } + - { label: "Quote", name: "quote", widget: "string", required: false } + - { label: "Profile Image", name: "image", widget: "image", required: false } + - { label: "GitHub URL", name: "github", widget: "string", required: false } + - { label: "Twitter URL", name: "twitter", widget: "string", required: false } + - { label: "LinkedIn URL", name: "linkedin", widget: "string", required: false } + - { label: "Medium URL", name: "medium", widget: "string", required: false } + - { label: "Accent Color", name: "accentcolor", widget: "string", required: false } + - { label: "Sectors", name: "sectors", widget: "string", required: false } + - { label: "Projects", name: "projects", widget: "string", required: false } + - { label: "Bio (body text)", name: "body", widget: "markdown", required: false } + + # ─── PROJECT PARTNERS ─────────────────────────────────────────────────────── + - label: "Project Partners" + name: "projectpart" + folder: "content/projectpart" + path: "{{slug}}/index" + create: true + format: frontmatter + extension: md + identifier_field: name + summary: "{{name}}" + fields: + - { label: "Organisation Name", name: "name", widget: "string" } + - { label: "Short Name / Acronym", name: "short", widget: "string", required: false } + - { label: "Logo", name: "logo", widget: "image", required: false } + - { label: "Testimonial", name: "testimonial", widget: "text", required: false } + - { label: "Website", name: "website", widget: "string", required: false } + - { label: "Sectors", name: "sectors", widget: "string", required: false } + - { label: "Projects", name: "projects", widget: "string", required: false } + + # ─── VALUES ───────────────────────────────────────────────────────────────── + - label: "Values" + name: "values" + folder: "content/values" + path: "{{slug}}/index" + create: true + format: frontmatter + extension: md + identifier_field: title + summary: "{{number}}. {{title}}" + fields: + - { label: "Number", name: "number", widget: "string", required: false, hint: "e.g. 01, 02 ..." } + - { label: "Title", name: "title", widget: "string" } + - { label: "Image", name: "image", widget: "image", required: false } + - { label: "Description", name: "body", widget: "markdown", required: false } + + # ─── JOB OPENINGS ─────────────────────────────────────────────────────────── + - label: "Job Openings" + name: "openings" + folder: "content/openings" + create: true + format: frontmatter + extension: md + identifier_field: title + summary: "{{title}}" + fields: + - { label: "Job Title", name: "title", widget: "string" } + - { label: "Application URL", name: "url", widget: "string" } + + # ─── WORK: PUBLIC FINANCE ──────────────────────────────────────────────────── + - label: "Work: Public Finance" + name: "work_publicfinance" + folder: "content/work/publicfinance" + path: "{{slug}}/index" + create: true + format: frontmatter + extension: md + identifier_field: name + summary: "{{name}}" + filter: { field: "type", value: "project" } + fields: + - { label: "Project Name", name: "name", widget: "string" } + - { label: "Summary", name: "summary", widget: "text", required: false } + - { label: "Context", name: "context", widget: "text", required: false } + - { label: "Solution", name: "solution", widget: "text", required: false } + - { label: "Image", name: "image", widget: "image", required: false } + - { label: "Website URL", name: "url", widget: "string", required: false } + - { label: "Twitter", name: "twitter", widget: "string", required: false } + - { label: "GitHub", name: "github", widget: "string", required: false } + - { label: "Facebook", name: "facebook", widget: "string", required: false } + - { label: "LinkedIn", name: "linkedin", widget: "string", required: false } + - { label: "Sector", name: "sector", widget: "hidden", default: "Public Finance" } + - { label: "Type", name: "type", widget: "hidden", default: "project" } + - label: "Resources" + name: "resources" + widget: "list" + required: false + fields: + - { label: "Title", name: "title", widget: "string" } + - { label: "Link", name: "link", widget: "string" } + - { label: "Type", name: "type", widget: "select", options: ["Blog", "Report", "Dashboard", "Video", "Forum", "Tutorial", "Doc"] } + + # ─── WORK: CLIMATE ACTION ──────────────────────────────────────────────────── + - label: "Work: Climate Action" + name: "work_climateaction" + folder: "content/work/climateaction" + path: "{{slug}}/index" + create: true + format: frontmatter + extension: md + identifier_field: name + summary: "{{name}}" + filter: { field: "type", value: "project" } + fields: + - { label: "Project Name", name: "name", widget: "string" } + - { label: "Summary", name: "summary", widget: "text", required: false } + - { label: "Context", name: "context", widget: "text", required: false } + - { label: "Solution", name: "solution", widget: "text", required: false } + - { label: "Image", name: "image", widget: "image", required: false } + - { label: "Website URL", name: "url", widget: "string", required: false } + - { label: "Twitter", name: "twitter", widget: "string", required: false } + - { label: "GitHub", name: "github", widget: "string", required: false } + - { label: "Sector", name: "sector", widget: "hidden", default: "Climate Action" } + - { label: "Type", name: "type", widget: "hidden", default: "project" } + - label: "Resources" + name: "resources" + widget: "list" + required: false + fields: + - { label: "Title", name: "title", widget: "string" } + - { label: "Link", name: "link", widget: "string" } + - { label: "Type", name: "type", widget: "select", options: ["Blog", "Report", "Dashboard", "Video", "Forum", "Tutorial", "Doc"] } + + # ─── WORK: LAW & JUSTICE ───────────────────────────────────────────────────── + - label: "Work: Law & Justice" + name: "work_lawandjustice" + folder: "content/work/lawandjustice" + path: "{{slug}}/index" + create: true + format: frontmatter + extension: md + identifier_field: name + summary: "{{name}}" + filter: { field: "type", value: "project" } + fields: + - { label: "Project Name", name: "name", widget: "string" } + - { label: "Summary", name: "summary", widget: "text", required: false } + - { label: "Context", name: "context", widget: "text", required: false } + - { label: "Solution", name: "solution", widget: "text", required: false } + - { label: "Image", name: "image", widget: "image", required: false } + - { label: "Website URL", name: "url", widget: "string", required: false } + - { label: "Twitter", name: "twitter", widget: "string", required: false } + - { label: "GitHub", name: "github", widget: "string", required: false } + - { label: "Sector", name: "sector", widget: "hidden", default: "Law & Justice" } + - { label: "Type", name: "type", widget: "hidden", default: "project" } + - label: "Resources" + name: "resources" + widget: "list" + required: false + fields: + - { label: "Title", name: "title", widget: "string" } + - { label: "Link", name: "link", widget: "string" } + - { label: "Type", name: "type", widget: "select", options: ["Blog", "Report", "Dashboard", "Video", "Forum", "Tutorial", "Doc"] } + + # ─── WORK: OPEN CONTRACTING ────────────────────────────────────────────────── + - label: "Work: Open Contracting" + name: "work_opencontracting" + folder: "content/work/opencontracting" + path: "{{slug}}/index" + create: true + format: frontmatter + extension: md + identifier_field: name + summary: "{{name}}" + filter: { field: "type", value: "project" } + fields: + - { label: "Project Name", name: "name", widget: "string" } + - { label: "Summary", name: "summary", widget: "text", required: false } + - { label: "Context", name: "context", widget: "text", required: false } + - { label: "Solution", name: "solution", widget: "text", required: false } + - { label: "Image", name: "image", widget: "image", required: false } + - { label: "Website URL", name: "url", widget: "string", required: false } + - { label: "Twitter", name: "twitter", widget: "string", required: false } + - { label: "GitHub", name: "github", widget: "string", required: false } + - { label: "Sector", name: "sector", widget: "hidden", default: "Open Contracting" } + - { label: "Type", name: "type", widget: "hidden", default: "project" } + - label: "Resources" + name: "resources" + widget: "list" + required: false + fields: + - { label: "Title", name: "title", widget: "string" } + - { label: "Link", name: "link", widget: "string" } + - { label: "Type", name: "type", widget: "select", options: ["Blog", "Report", "Dashboard", "Video", "Forum", "Tutorial", "Doc"] } + + # ─── WORK: DIGITAL PUBLIC GOODS ────────────────────────────────────────────── + - label: "Work: Digital Public Goods" + name: "work_dpg" + folder: "content/work/dpg" + path: "{{slug}}/index" + create: true + format: frontmatter + extension: md + identifier_field: name + summary: "{{name}}" + filter: { field: "type", value: "project" } + fields: + - { label: "Project Name", name: "name", widget: "string" } + - { label: "Summary", name: "summary", widget: "text", required: false } + - { label: "Context", name: "context", widget: "text", required: false } + - { label: "Solution", name: "solution", widget: "text", required: false } + - { label: "Image", name: "image", widget: "image", required: false } + - { label: "Website URL", name: "url", widget: "string", required: false } + - { label: "GitHub", name: "github", widget: "string", required: false } + - { label: "Sector", name: "sector", widget: "hidden", default: "Digital Public Goods" } + - { label: "Type", name: "type", widget: "hidden", default: "project" } + - label: "Resources" + name: "resources" + widget: "list" + required: false + fields: + - { label: "Title", name: "title", widget: "string" } + - { label: "Link", name: "link", widget: "string" } + - { label: "Type", name: "type", widget: "select", options: ["Blog", "Report", "Dashboard", "Video", "Forum", "Tutorial", "Doc"] } + + # ─── WORK: URBAN DEVELOPMENT ───────────────────────────────────────────────── + - label: "Work: Urban Development" + name: "work_urbandevelopment" + folder: "content/work/urbandevelopment" + path: "{{slug}}/index" + create: true + format: frontmatter + extension: md + identifier_field: name + summary: "{{name}}" + filter: { field: "type", value: "project" } + fields: + - { label: "Project Name", name: "name", widget: "string" } + - { label: "Summary", name: "summary", widget: "text", required: false } + - { label: "Context", name: "context", widget: "text", required: false } + - { label: "Solution", name: "solution", widget: "text", required: false } + - { label: "Image", name: "image", widget: "image", required: false } + - { label: "Website URL", name: "url", widget: "string", required: false } + - { label: "GitHub", name: "github", widget: "string", required: false } + - { label: "Sector", name: "sector", widget: "hidden", default: "Urban Development" } + - { label: "Type", name: "type", widget: "hidden", default: "project" } + - label: "Resources" + name: "resources" + widget: "list" + required: false + fields: + - { label: "Title", name: "title", widget: "string" } + - { label: "Link", name: "link", widget: "string" } + - { label: "Type", name: "type", widget: "select", options: ["Blog", "Report", "Dashboard", "Video", "Forum", "Tutorial", "Doc"] } + + # ─── EVENTS: DATA DIALOGUES ────────────────────────────────────────────────── + - label: "Events: Data Dialogues" + name: "events_datadialogues" + folder: "content/events/data-dialogues" + path: "{{slug}}/index" + create: true + format: frontmatter + extension: md + identifier_field: name + summary: "{{name}}" + filter: { field: "type", value: "eventdetail" } + fields: + - { label: "Event Name", name: "name", widget: "string" } + - { label: "Summary", name: "summary", widget: "text", required: false } + - { label: "Context", name: "context", widget: "text", required: false } + - { label: "Event Type", name: "eventtype", widget: "hidden", default: "Data Dialogues" } + - { label: "Banner Image", name: "image", widget: "image", required: false } + - { label: "Report URL", name: "url", widget: "string", required: false } + - { label: "YouTube URL", name: "youtube", widget: "string", required: false } + - { label: "Type", name: "type", widget: "hidden", default: "eventdetail" } + - label: "Resources" + name: "resources" + widget: "list" + required: false + fields: + - { label: "Title", name: "title", widget: "string" } + - { label: "Link", name: "link", widget: "string" } + - { label: "Type", name: "type", widget: "select", options: ["Doc", "Video", "Blog", "Report"] } + + # ─── EVENTS: CLIMATE DIALOGUES ─────────────────────────────────────────────── + - label: "Events: Climate Dialogues" + name: "events_climatedialogues" + folder: "content/events/climate-dialogues" + path: "{{slug}}/index" + create: true + format: frontmatter + extension: md + identifier_field: name + summary: "{{name}}" + filter: { field: "type", value: "eventdetail" } + fields: + - { label: "Event Name", name: "name", widget: "string" } + - { label: "Summary", name: "summary", widget: "text", required: false } + - { label: "Context", name: "context", widget: "text", required: false } + - { label: "Event Type", name: "eventtype", widget: "hidden", default: "Climate Dialogues" } + - { label: "Banner Image", name: "image", widget: "image", required: false } + - { label: "Report URL", name: "url", widget: "string", required: false } + - { label: "YouTube URL", name: "youtube", widget: "string", required: false } + - { label: "Type", name: "type", widget: "hidden", default: "eventdetail" } + - label: "Resources" + name: "resources" + widget: "list" + required: false + fields: + - { label: "Title", name: "title", widget: "string" } + - { label: "Link", name: "link", widget: "string" } + - { label: "Type", name: "type", widget: "select", options: ["Doc", "Video", "Blog", "Report"] } diff --git a/static/admin/index.html b/static/admin/index.html new file mode 100644 index 000000000..dbfb8de7c --- /dev/null +++ b/static/admin/index.html @@ -0,0 +1,11 @@ + + + + + + CivicDataLab | Content Manager + + + + + From 46fccd729629ac86955317fc3ae09698e95f1d33 Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 26 Feb 2026 08:33:06 +0530 Subject: [PATCH 2/5] Prepare Decap CMS and CI for production deployment - Remove local_backend from config.yml (dev-only setting) - Fix CI workflow to use --legacy-peer-deps for npm install --- .github/workflows/gh-pages.yml | 2 +- static/admin/config.yml | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 808ff4bd7..ce846c56b 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -30,7 +30,7 @@ jobs: # ${{ runner.os }}-build-${{ env.cache-name }}- # ${{ runner.os }}-build- # ${{ runner.os }}- - - run: npm install + - run: npm install --legacy-peer-deps - run: npm run build - name: deploy uses: peaceiris/actions-gh-pages@v3 diff --git a/static/admin/config.yml b/static/admin/config.yml index da46a6ac6..e5080f2fa 100644 --- a/static/admin/config.yml +++ b/static/admin/config.yml @@ -3,9 +3,7 @@ backend: repo: CivicDataLab/civicdatalab.github.io branch: main auth_type: pkce - app_id: YOUR_GITHUB_OAUTH_APP_CLIENT_ID # Replace this after OAuth setup - -local_backend: true # Remove this line before deploying to production + app_id: YOUR_GITHUB_OAUTH_APP_CLIENT_ID media_folder: "static/images/uploads" public_folder: "/images/uploads" From 21f531ef4fe227fe8bbf9a5cc65328d8c64ab265 Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 26 Feb 2026 08:46:07 +0530 Subject: [PATCH 3/5] Update README with CMS setup and local dev instructions --- README.md | 49 ++++++++++++++++++++++++++++++++++++++--- static/admin/config.yml | 2 +- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0f7973004..e13d1d8a0 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,52 @@ We are a research lab working with the goal of using data, tech, design and soci Prerequisites: NodeJS must be installed on your machine. 1. Clone this repo. -2. Run `npm start` command inside the root folder of the project. -3. To build the website, run `npm run build`. -4. To run the local build, run the following command: `npm run serve`. +2. Run `npm install --legacy-peer-deps` to install dependencies. +3. Run `npm start` to start the development server at `http://localhost:8000`. +4. To build the website, run `npm run build`. +5. To run the local build, run the following command: `npm run serve`. + +## Content Management (Decap CMS) + +The website uses [Decap CMS](https://decapcms.org/) for managing content through a UI without editing markdown files directly. + +### Running the CMS locally + +1. Start the Gatsby dev server: `npm start` +2. In a separate terminal, start the Decap proxy server: `npx decap-server` +3. Open `http://localhost:8000/admin/index.html` in your browser +4. Click **Login** — no GitHub auth needed locally, it connects directly to your local files + +### Accessing the CMS in production + +Visit `https://civicdatalab.in/admin/` and log in with your GitHub account. + +> You must have **Write** access to the `CivicDataLab/civicdatalab.github.io` repo to save content. + +### Giving someone CMS access + +1. Go to the repo on GitHub → **Settings → Collaborators and teams** +2. Add the person's GitHub account with **Write** access +3. They can now log into the CMS using their GitHub account + +### Setting up the GitHub OAuth App (one-time, for production) + +1. Go to **GitHub → Settings → Developer settings → OAuth Apps → New OAuth App** +2. Set **Homepage URL** to `https://civicdatalab.in` +3. Set **Authorization callback URL** to `https://civicdatalab.in/admin/` +4. Copy the **Client ID** and update `app_id` in `static/admin/config.yml` + +### CMS collections available + +| Collection | Content folder | +|---|---| +| Team Members | `content/team/` | +| Alumni | `content/alumnus/` | +| Work (by sector) | `content/work/` | +| Events | `content/events/` | +| Job Openings | `content/openings/` | +| Values | `content/values/` | +| Project Partners | `content/projectpart/` | ## Wiki You can find guides on how to add/update project and bandhu level info [here](https://github.com/CivicDataLab/civicdatalab.github.io/wiki). diff --git a/static/admin/config.yml b/static/admin/config.yml index e5080f2fa..4cdee7afc 100644 --- a/static/admin/config.yml +++ b/static/admin/config.yml @@ -3,7 +3,7 @@ backend: repo: CivicDataLab/civicdatalab.github.io branch: main auth_type: pkce - app_id: YOUR_GITHUB_OAUTH_APP_CLIENT_ID + app_id: YOUR_GITHUB_OAUTH_APP_CLIENT_ID # TODO: Create a GitHub OAuth App and replace this before merging to main media_folder: "static/images/uploads" public_folder: "/images/uploads" From 4804607be93aad3eb0c461885304f79572fa8b3c Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 26 Feb 2026 08:57:39 +0530 Subject: [PATCH 4/5] Fix react-is version to match React 16 and revert unnecessary CI change --- .github/workflows/gh-pages.yml | 2 +- package-lock.json | 23 ++++------------------- package.json | 2 +- 3 files changed, 6 insertions(+), 21 deletions(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index ce846c56b..808ff4bd7 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -30,7 +30,7 @@ jobs: # ${{ runner.os }}-build-${{ env.cache-name }}- # ${{ runner.os }}-build- # ${{ runner.os }}- - - run: npm install --legacy-peer-deps + - run: npm install - run: npm run build - name: deploy uses: peaceiris/actions-gh-pages@v3 diff --git a/package-lock.json b/package-lock.json index bbbfdc084..771cd78d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,7 @@ "react-dom": "^16.12.0", "react-helmet": "^5.2.1", "react-icons": "^4.1.0", - "react-is": "^19.2.4", + "react-is": "^16.13.1", "react-multi-carousel": "^2.6.2", "react-reveal": "^1.2.2", "react-select": "^5.7.4", @@ -14242,11 +14242,6 @@ "react-is": "^16.7.0" } }, - "node_modules/hoist-non-react-statics/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, "node_modules/homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", @@ -21070,11 +21065,6 @@ "node": ">= 8.3" } }, - "node_modules/pretty-format/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, "node_modules/probe-image-size": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/probe-image-size/-/probe-image-size-6.0.0.tgz", @@ -21133,11 +21123,6 @@ "react-is": "^16.13.1" } }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, "node_modules/proper-lockfile": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", @@ -21676,9 +21661,9 @@ } }, "node_modules/react-is": { - "version": "19.2.4", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.4.tgz", - "integrity": "sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==" + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/react-lifecycles-compat": { "version": "3.0.4", diff --git a/package.json b/package.json index db762ee45..660539d09 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "react-dom": "^16.12.0", "react-helmet": "^5.2.1", "react-icons": "^4.1.0", - "react-is": "^19.2.4", + "react-is": "^16.13.1", "react-multi-carousel": "^2.6.2", "react-reveal": "^1.2.2", "react-select": "^5.7.4", From 490dab336aec7fb75f3ed8351c57ee21b2def508 Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 26 Feb 2026 13:54:30 +0530 Subject: [PATCH 5/5] Customise Decap CMS admin portal branding - Add branded splash screen with CDL logo that fades out on load - Replace Decap login logo with CDL logo via logo_url in config.yml - Hide Decap footer SVG attribution using MutationObserver - Document admin portal branding in README --- README.md | 9 +++++++++ static/admin/config.yml | 2 ++ static/admin/index.html | 44 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/README.md b/README.md index e13d1d8a0..c12e343ca 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,15 @@ Visit `https://civicdatalab.in/admin/` and log in with your GitHub account. 3. Set **Authorization callback URL** to `https://civicdatalab.in/admin/` 4. Copy the **Client ID** and update `app_id` in `static/admin/config.yml` +### Admin portal branding + +The CMS login page (`/admin/`) has been customised: +- **Splash screen** — a full-page branded loading screen with the CivicDataLab logo (`/static/cdl_logo.png`) is shown while the CMS loads, then fades out. +- **Login page logo** — the default Decap logo is replaced with the CivicDataLab logo via the `logo_url` field in `static/admin/config.yml`. +- **Decap footer** — the Decap attribution SVG at the bottom of the login page is hidden via a `MutationObserver` in `static/admin/index.html`. + +To update the logo, replace `/static/cdl_logo.png` and update the `logo_url` in `static/admin/config.yml` if the path changes. + ### CMS collections available | Collection | Content folder | diff --git a/static/admin/config.yml b/static/admin/config.yml index 4cdee7afc..cbbe8e7c5 100644 --- a/static/admin/config.yml +++ b/static/admin/config.yml @@ -5,6 +5,8 @@ backend: auth_type: pkce app_id: YOUR_GITHUB_OAUTH_APP_CLIENT_ID # TODO: Create a GitHub OAuth App and replace this before merging to main +logo_url: /cdl_logo.png + media_folder: "static/images/uploads" public_folder: "/images/uploads" diff --git a/static/admin/index.html b/static/admin/index.html index dbfb8de7c..57a93f4ad 100644 --- a/static/admin/index.html +++ b/static/admin/index.html @@ -5,7 +5,51 @@ CivicDataLab | Content Manager + +
+ CivicDataLab +

Loading Content Manager...

+
+