diff --git a/.github/workflows/civicsignalblog-deploy-prod.yml b/.github/workflows/civicsignalblog-deploy-prod.yml new file mode 100644 index 000000000..7583f6a9e --- /dev/null +++ b/.github/workflows/civicsignalblog-deploy-prod.yml @@ -0,0 +1,104 @@ +name: CivicSignal Blog | Deploy | PROD + +on: + push: + branches: [main] + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: "${{ github.workflow }} @ ${{ github.ref }}" + cancel-in-progress: true + +env: + DOKKU_REMOTE_BRANCH: "master" + DOKKU_REMOTE_URL: "ssh://dokku@ui-1.prod.codeforafrica.org/civicsignalblog-ui" + GIT_PUSH_FLAGS: "--force" + IMAGE_NAME: "codeforafrica/civicsignalblog-ui" + VERSION_FILE_NAME: "./apps/civisignalblog/package.json" + NEXT_PUBLIC_APP_URL: "https://blog.civicsignal.africa" + SENTRY_ENVIRONMENT: "production" + +jobs: + deploy: + runs-on: ${{ matrix.os }} + strategy: + matrix: + node-version: [20.16] + os: [ubuntu-latest] + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # Set up Node since it's required by version-check + # https://github.com/EndBug/version-check#github-workflow + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Check if version is bumped + id: version-check + uses: EndBug/version-check@v2 + with: + # Whether to search in every commit's diff. + # This is useful if you often do change the version without saying it + # in the commit message. If you always include the semver of the new + # version in your commit message when you bump versions then you can + # omit this. + diff-search: true + file-name: "${{ env.VERSION_FILE_NAME }}" + + - name: Set up Docker Buildx + if: steps.version-check.outputs.changed == 'true' + uses: docker/setup-buildx-action@v3 + + - name: Cache Docker layers + if: steps.version-check.outputs.changed == 'true' + uses: actions/cache@v4 + with: + key: ${{ runner.os }}-buildx-${{ github.sha }} + path: /tmp/.buildx-cache + restore-keys: | + ${{ runner.os }}-buildx- + + - name: Login to DockerHub + if: steps.version-check.outputs.changed == 'true' + uses: docker/login-action@v3 + with: + password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} + username: ${{ secrets.DOCKER_HUB_USERNAME }} + + - name: Build Docker image + if: steps.version-check.outputs.changed == 'true' + uses: docker/build-push-action@v5 + with: + build-args: | + MONGODB_URL=${{ secrets.CIVICSIGNALBLOG_MONGO_URL }} + NEXT_PUBLIC_APP_URL=${{ env.NEXT_PUBLIC_APP_URL }} + PAYLOAD_SECRET=${{ secrets.CIVICSIGNALBLOG_PAYLOAD_SECRET }} + SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ENVIRONMENT=${{ env.SENTRY_ENVIRONMENT }} + SENTRY_ORG=${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT=${{ secrets.CIVICSIGNALBLOG_SENTRY_PROJECT }} + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache-new + context: . + target: civicsignalblog-runner + push: true + tags: "${{ env.IMAGE_NAME }}:${{ steps.version-check.outputs.version }}" + + - name: Move cache + if: steps.version-check.outputs.changed == 'true' + run: | + rm -rf /tmp/.buildx-cache + mv /tmp/.buildx-cache-new /tmp/.buildx-cache + + - name: Push to Dokku + if: steps.version-check.outputs.changed == 'true' + uses: dokku/github-action@v1.4.0 + with: + deploy_docker_image: ${{ env.IMAGE_NAME }}:${{ steps.version-check.outputs.version }} + ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }} + git_remote_url: ${{ env.DOKKU_REMOTE_URL }} diff --git a/Dockerfile b/Dockerfile index 34cd9c740..834d89d47 100644 --- a/Dockerfile +++ b/Dockerfile @@ -320,27 +320,27 @@ CMD ["node", "apps/climatemappedafrica/server.js"] # ============================================================================ -# Code for Africa +# CivicSignal Blog # ============================================================================ # -# codeforafrica-desp: image with all codeforafrica dependencies +# civicsignalblog-desp: image with all codeforafrica dependencies # ------------------------------------------------------------- -FROM base-deps AS codeforafrica-deps +FROM base-deps AS civicsignalblog-deps # TODO(kilemensi): Figure out why this is needed COPY packages/commons-ui-testing-library/package.json ./packages/commons-ui-testing-library/package.json -COPY apps/codeforafrica/package.json ./apps/codeforafrica/package.json +COPY apps/civicsignalblog/package.json ./apps/civicsignalblog/package.json -RUN pnpm --filter "./apps/codeforafrica/" install --offline --frozen-lockfile +RUN pnpm --filter "./apps/civicsignalblog/" install --offline --frozen-lockfile # -# codeforafrica-builder: image that uses deps to build shippable output +# civicsignalblog-builder: image that uses deps to build shippable output # --------------------------------------------------------------------- -FROM base-builder AS codeforafrica-builder +FROM base-builder AS civicsignalblog-builder ARG NEXT_TELEMETRY_DISABLED \ # Next.js / Payload (build time) @@ -352,8 +352,8 @@ ARG NEXT_TELEMETRY_DISABLED \ # Payload (runtime) # TODO(koech): Standadise naming of Mongo DB URL. Our options: # - MONGODB_URL (codeforafrica) - # - MONGO_URL (charterafrica, roboshield) - MONGODB_URL \ + # - MONGO_URL (charterafrica, civicsignalblog, roboshield) + MONGO_URL \ PAYLOAD_SECRET \ # Sentry (build time) SENTRY_AUTH_TOKEN \ @@ -362,32 +362,32 @@ ARG NEXT_TELEMETRY_DISABLED \ SENTRY_PROJECT # This is in app-builder instead of base-builder just incase app-deps adds deps -COPY --from=codeforafrica-deps /workspace/node_modules ./node_modules +COPY --from=civicsignalblog-deps /workspace/node_modules ./node_modules -COPY --from=codeforafrica-deps /workspace/apps/codeforafrica/node_modules ./apps/codeforafrica/node_modules +COPY --from=civicsignalblog-deps /workspace/apps/civicsignalblog/node_modules ./apps/civicsignalblog/node_modules -COPY apps/codeforafrica ./apps/codeforafrica/ +COPY apps/civicsignalblog ./apps/civicsignalblog/ # When building Next.js app, Next.js needs to connect to local Payload ENV PAYLOAD_PUBLIC_APP_URL=http://localhost:3000 -RUN pnpm --filter "./apps/codeforafrica/" build-next +RUN pnpm --filter "./apps/civicsignalblog/" build-next # When building Payload app, Payload needs to have final app URL ENV PAYLOAD_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL} -RUN pnpm --filter "./apps/codeforafrica/" build-payload +RUN pnpm --filter "./apps/civicsignalblog/" build-payload # -# codeforafrica-runner: final deployable image +# civicsignalblog-runner: final deployable image # -------------------------------------------- -FROM base-runner AS codeforafrica-runner +FROM base-runner AS civicsignalblog-runner ARG NEXT_PUBLIC_APP_LOGO_URL \ PAYLOAD_CONFIG_PATH="dist/payload.config.js" \ PAYLOAD_PUBLIC_APP_URL # TODO(koech): Standadise naming of GA MEASUREMENT ID. Our options: -# - GA_MEASUREMENT_ID (charterafrica, codeforafrica) +# - GA_MEASUREMENT_ID (charterafrica, civicsignalblog, codeforafrica) # - GOOGLE_ANALYTICS_ID (pesayetu, roboshield, vpnmanager) # This is only needed at runtime ENV NEXT_PUBLIC_APP_LOGO_URL=${NEXT_PUBLIC_APP_LOGO_URL} \ @@ -396,32 +396,32 @@ ENV NEXT_PUBLIC_APP_LOGO_URL=${NEXT_PUBLIC_APP_LOGO_URL} \ RUN set -ex \ # Create nextjs cache dir w/ correct permissions - && mkdir -p ./apps/codeforafrica//.next \ - && chown nextjs:nodejs ./apps/codeforafrica/.next + && mkdir -p ./apps/civicsignalblog//.next \ + && chown nextjs:nodejs ./apps/civicsignalblog/.next # PNPM # symlink some dependencies -COPY --from=codeforafrica-builder --chown=nextjs:nodejs /workspace/node_modules ./node_modules +COPY --from=civicsignalblog-builder --chown=nextjs:nodejs /workspace/node_modules ./node_modules # Since we can't use output: "standalone", copy all app's dependencies -COPY --from=codeforafrica-builder --chown=nextjs:nodejs /workspace/apps/codeforafrica/node_modules ./apps/codeforafrica/node_modules -COPY --from=codeforafrica-builder --chown=nextjs:nodejs /workspace/apps/codeforafrica/next.config.js ./apps/codeforafrica/next.config.js -COPY --from=codeforafrica-builder --chown=nextjs:nodejs /workspace/apps/codeforafrica/.env ./apps/codeforafrica/.env -COPY --from=codeforafrica-builder --chown=nextjs:nodejs /workspace/apps/codeforafrica/migrations ./apps/codeforafrica/migrations +COPY --from=civicsignalblog-builder --chown=nextjs:nodejs /workspace/apps/civicsignalblog/node_modules ./apps/civicsignalblog/node_modules +COPY --from=civicsignalblog-builder --chown=nextjs:nodejs /workspace/apps/civicsignalblog/next.config.js ./apps/civicsignalblog/next.config.js +COPY --from=civicsignalblog-builder --chown=nextjs:nodejs /workspace/apps/civicsignalblog/.env ./apps/civicsignalblog/.env +COPY --from=civicsignalblog-builder --chown=nextjs:nodejs /workspace/apps/civicsignalblog/migrations ./apps/civicsignalblog/migrations # Next.js # Public assets -COPY --from=codeforafrica-builder --chown=nextjs:nodejs /workspace/apps/codeforafrica/public ./apps/codeforafrica/public +COPY --from=civicsignalblog-builder --chown=nextjs:nodejs /workspace/apps/civicsignalblog/public ./apps/civicsignalblog/public # Since we can't use output: "standalone", copy the whole app's .next folder # TODO(kilemensi): Figure out which files in .next folder are not needed -COPY --from=codeforafrica-builder --chown=nextjs:nodejs /workspace/apps/codeforafrica/.next ./apps/codeforafrica/.next +COPY --from=civicsignalblog-builder --chown=nextjs:nodejs /workspace/apps/civicsignalblog/.next ./apps/civicsignalblog/.next # Payload -COPY --from=codeforafrica-builder /workspace/apps/codeforafrica/dist ./apps/codeforafrica/dist -COPY --from=codeforafrica-builder /workspace/apps/codeforafrica/build ./apps/codeforafrica/build +COPY --from=civicsignalblog-builder /workspace/apps/civicsignalblog/dist ./apps/civicsignalblog/dist +COPY --from=civicsignalblog-builder /workspace/apps/civicsignalblog/build ./apps/civicsignalblog/build # Since we can't use output: "standalone", switch to specific app's folder -WORKDIR /workspace/apps/codeforafrica +WORKDIR /workspace/apps/civicsignalblog USER nextjs @@ -429,6 +429,116 @@ USER nextjs CMD ["node", "dist/server.js"] +# ============================================================================ +# Code for Africa +# ============================================================================ + +# +# codeforafrica-desp: image with all codeforafrica dependencies +# ------------------------------------------------------------- + + FROM base-deps AS codeforafrica-deps + + # TODO(kilemensi): Figure out why this is needed + COPY packages/commons-ui-testing-library/package.json ./packages/commons-ui-testing-library/package.json + + COPY apps/codeforafrica/package.json ./apps/codeforafrica/package.json + + RUN pnpm --filter "./apps/codeforafrica/" install --offline --frozen-lockfile + + # + # codeforafrica-builder: image that uses deps to build shippable output + # --------------------------------------------------------------------- + + FROM base-builder AS codeforafrica-builder + + ARG NEXT_TELEMETRY_DISABLED \ + # Next.js / Payload (build time) + PORT \ + # Next.js (runtime) + NEXT_PUBLIC_APP_NAME="Code for Africa" \ + NEXT_PUBLIC_APP_URL \ + NEXT_PUBLIC_SENTRY_DSN \ + # Payload (runtime) + # TODO(koech): Standadise naming of Mongo DB URL. Our options: + # - MONGODB_URL (codeforafrica) + # - MONGO_URL (charterafrica, roboshield) + MONGODB_URL \ + PAYLOAD_SECRET \ + # Sentry (build time) + SENTRY_AUTH_TOKEN \ + SENTRY_ENVIRONMENT \ + SENTRY_ORG \ + SENTRY_PROJECT + + # This is in app-builder instead of base-builder just incase app-deps adds deps + COPY --from=codeforafrica-deps /workspace/node_modules ./node_modules + + COPY --from=codeforafrica-deps /workspace/apps/codeforafrica/node_modules ./apps/codeforafrica/node_modules + + COPY apps/codeforafrica ./apps/codeforafrica/ + + # When building Next.js app, Next.js needs to connect to local Payload + ENV PAYLOAD_PUBLIC_APP_URL=http://localhost:3000 + RUN pnpm --filter "./apps/codeforafrica/" build-next + + # When building Payload app, Payload needs to have final app URL + ENV PAYLOAD_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL} + RUN pnpm --filter "./apps/codeforafrica/" build-payload + + # + # codeforafrica-runner: final deployable image + # -------------------------------------------- + + FROM base-runner AS codeforafrica-runner + + ARG NEXT_PUBLIC_APP_LOGO_URL \ + PAYLOAD_CONFIG_PATH="dist/payload.config.js" \ + PAYLOAD_PUBLIC_APP_URL + + # TODO(koech): Standadise naming of GA MEASUREMENT ID. Our options: + # - GA_MEASUREMENT_ID (charterafrica, codeforafrica) + # - GOOGLE_ANALYTICS_ID (pesayetu, roboshield, vpnmanager) + # This is only needed at runtime + ENV NEXT_PUBLIC_APP_LOGO_URL=${NEXT_PUBLIC_APP_LOGO_URL} \ + PAYLOAD_PUBLIC_APP_URL=${PAYLOAD_PUBLIC_APP_URL} \ + PAYLOAD_CONFIG_PATH=${PAYLOAD_CONFIG_PATH} + + RUN set -ex \ + # Create nextjs cache dir w/ correct permissions + && mkdir -p ./apps/codeforafrica//.next \ + && chown nextjs:nodejs ./apps/codeforafrica/.next + + # PNPM + # symlink some dependencies + COPY --from=codeforafrica-builder --chown=nextjs:nodejs /workspace/node_modules ./node_modules + + # Since we can't use output: "standalone", copy all app's dependencies + COPY --from=codeforafrica-builder --chown=nextjs:nodejs /workspace/apps/codeforafrica/node_modules ./apps/codeforafrica/node_modules + COPY --from=codeforafrica-builder --chown=nextjs:nodejs /workspace/apps/codeforafrica/next.config.js ./apps/codeforafrica/next.config.js + COPY --from=codeforafrica-builder --chown=nextjs:nodejs /workspace/apps/codeforafrica/.env ./apps/codeforafrica/.env + COPY --from=codeforafrica-builder --chown=nextjs:nodejs /workspace/apps/codeforafrica/migrations ./apps/codeforafrica/migrations + # Next.js + # Public assets + COPY --from=codeforafrica-builder --chown=nextjs:nodejs /workspace/apps/codeforafrica/public ./apps/codeforafrica/public + + # Since we can't use output: "standalone", copy the whole app's .next folder + # TODO(kilemensi): Figure out which files in .next folder are not needed + COPY --from=codeforafrica-builder --chown=nextjs:nodejs /workspace/apps/codeforafrica/.next ./apps/codeforafrica/.next + + # Payload + COPY --from=codeforafrica-builder /workspace/apps/codeforafrica/dist ./apps/codeforafrica/dist + COPY --from=codeforafrica-builder /workspace/apps/codeforafrica/build ./apps/codeforafrica/build + + # Since we can't use output: "standalone", switch to specific app's folder + WORKDIR /workspace/apps/codeforafrica + + USER nextjs + + # Custom server to run Payload and Next.js in the same app + CMD ["node", "dist/server.js"] + + # ============================================================================ # PesaYetu # ============================================================================ diff --git a/Makefile b/Makefile index f0bc56697..17e7a31f3 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,9 @@ COMPOSE_BUILD_ENV=BUILDKIT_PROGRESS=plain charterafrica: $(COMPOSE_BUILD_ENV) $(COMPOSE) --env-file apps/charterafrica/.env.local up charterafrica --build +civicsignalblog: + $(COMPOSE_BUILD_ENV) $(COMPOSE) --env-file apps/civicsignalblog/.env.local up civicsignalblog --build + codeforafrica: $(COMPOSE_BUILD_ENV) $(COMPOSE) --env-file apps/codeforafrica/.env.local up codeforafrica --build diff --git a/apps/civicsignalblog/.env b/apps/civicsignalblog/.env new file mode 100644 index 000000000..9c951a201 --- /dev/null +++ b/apps/civicsignalblog/.env @@ -0,0 +1,11 @@ +MIGRATIONS_DIR=./migrations +NEXT_PUBLIC_APP_DIRECTORY="apps/civicsignal/" +NEXT_PUBLIC_APP_NAME="Code for Africa" +NEXT_PUBLIC_APP_URL="https://blog.civicsignal.africa" +NEXT_PUBLIC_GOOGLE_ANALYTICS_ID="G-NXX5DBEHXC" +NEXT_PUBLIC_IMAGE_DOMAINS="" +NEXT_PUBLIC_IMAGE_UNOPTIMIZED="true" +NEXT_PUBLIC_VERCEL_URL=${VERCEL_URL} +SENTRY_ENVIRONMENT=local +SENTRY_ORG=code-for-africa +SENTRY_PROJECT=civicsignal diff --git a/apps/civicsignalblog/.eslintignore b/apps/civicsignalblog/.eslintignore new file mode 100644 index 000000000..19d6cb9ac --- /dev/null +++ b/apps/civicsignalblog/.eslintignore @@ -0,0 +1,36 @@ +# dependencies +node_modules +.pnp +.pnp.js +.pnpm-debug.log + +# typescript +dist/ + +# testing +coverage + +# next.js +.next/ +out/ + +# payload +build/ + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Vercel +.vercel +.now + +# turbo +.turbo +test-results/ +playwright-report/ diff --git a/apps/civicsignalblog/.eslintrc.js b/apps/civicsignalblog/.eslintrc.js new file mode 100644 index 000000000..468a5ff74 --- /dev/null +++ b/apps/civicsignalblog/.eslintrc.js @@ -0,0 +1,11 @@ +module.exports = { + root: true, + extends: ["eslint-config-commons-ui/next"], + settings: { + "import/resolver": { + webpack: { + config: "./eslint.webpack.config.js", + }, + }, + }, +}; diff --git a/apps/civicsignalblog/.lintstagedrc.js b/apps/civicsignalblog/.lintstagedrc.js new file mode 100644 index 000000000..bce8a3fb3 --- /dev/null +++ b/apps/civicsignalblog/.lintstagedrc.js @@ -0,0 +1 @@ +module.exports = require("eslint-config-commons-ui/.lintstagedrc"); diff --git a/apps/civicsignalblog/README.md b/apps/civicsignalblog/README.md new file mode 100644 index 000000000..a6438c90c --- /dev/null +++ b/apps/civicsignalblog/README.md @@ -0,0 +1,27 @@ +# CivicSignal Blog + +Temporary CivicSignal blog + +## Getting Started + +First create `.env.local` file in the root directory of the project. + +```bash +cp env.template .env.local +``` + +and modify the `.env.local` file according to your needs. + +#### Note + +The default `.env` file is for the 'Publicly' visible environment variables. + +Then run the development server: + +```bash +pnpm dev +``` + +## Debugging + +Chrome DevTools debugging is enabled by default for both client-side and server-side code. See Next.js [docs](https://nextjs.org/docs/advanced-features/debugging#debugging-with-chrome-devtools) for more information. diff --git a/apps/civicsignalblog/contrib/dokku/Dockerfile b/apps/civicsignalblog/contrib/dokku/Dockerfile new file mode 100644 index 000000000..8870a2734 --- /dev/null +++ b/apps/civicsignalblog/contrib/dokku/Dockerfile @@ -0,0 +1 @@ +FROM codeforafrica/codeforafrica-ui:0.1.2 diff --git a/apps/civicsignalblog/env.template b/apps/civicsignalblog/env.template new file mode 100644 index 000000000..ec6f77274 --- /dev/null +++ b/apps/civicsignalblog/env.template @@ -0,0 +1,27 @@ +NEXT_PUBLIC_IMAGE_DOMAINS= +NEXT_PUBLIC_IMAGE_UNOPTIMIZED="true|false" +NEXT_PUBLIC_APP_NAME="Blog | CivicSignal" +NEXT_PUBLIC_APP_URL="http://localhost:3000" + +# Google +# +GOOGLE_API_KEY= + +# PAYLOAD +PAYLOAD_PUBLIC_APP_URL=http://localhost:3010 +PAYLOAD_PUBLIC_PORT=3010 +PAYLOAD_SECRET=randomsecretkeythatyougenerateherethisisjustanexample +MONGO_URL=mongodb://root:rootpassword@localhost:27017/codeforafrica?authSource=admin + +# S3 +S3_BUCKET +S3_SECRET_ACCESS_KEY= +S3_ACCESS_KEY_ID= +S3_REGION + +# Sentry +NEXT_PUBLIC_SENTRY_DSN= +SENTRY_AUTH_TOKEN= +SENTRY_ENVIRONMENT= +SENTRY_ORG= +SENTRY_PROJECT= diff --git a/apps/civicsignalblog/eslint.webpack.config.js b/apps/civicsignalblog/eslint.webpack.config.js new file mode 100644 index 000000000..cb3271e24 --- /dev/null +++ b/apps/civicsignalblog/eslint.webpack.config.js @@ -0,0 +1,26 @@ +const path = require("path"); + +module.exports = { + module: { + rules: [ + { + test: /\.svg$/i, + type: "asset", + resourceQuery: /url/, // *.svg?url + }, + { + test: /\.svg$/i, + issuer: /\.[jt]sx?$/, + resourceQuery: { not: [/url/] }, // exclude react component if *.svg?url + use: ["@svgr/webpack"], + }, + ], + }, + resolve: { + alias: { + "@/civicsignalblog": path.resolve(__dirname, "src/"), + content: path.resolve(__dirname, "content/"), + }, + extensions: [".js"], + }, +}; diff --git a/apps/civicsignalblog/instrumentation.js b/apps/civicsignalblog/instrumentation.js new file mode 100644 index 000000000..9417cfcce --- /dev/null +++ b/apps/civicsignalblog/instrumentation.js @@ -0,0 +1,11 @@ +// Next.js requires this to be exported as register +// eslint-disable-next-line import/prefer-default-export +export async function register() { + if (process.env.NEXT_RUNTIME === "nodejs") { + await import("./sentry.server.config"); + } + + if (process.env.NEXT_RUNTIME === "edge") { + await import("./sentry.edge.config"); + } +} diff --git a/apps/civicsignalblog/jest.config.js b/apps/civicsignalblog/jest.config.js new file mode 100644 index 000000000..dea4ec013 --- /dev/null +++ b/apps/civicsignalblog/jest.config.js @@ -0,0 +1,16 @@ +const defaultConfig = require("jest-config-commons-ui/next"); + +const { moduleNameMapper } = defaultConfig; + +module.exports = { + ...defaultConfig, + moduleNameMapper: { + ...moduleNameMapper, + "^@/civicsignalblog/(.*)$": "/src/$1", + "^@/commons-ui/core/(.*)$": + "/../../packages/commons-ui-core/src/$1", + "^@/commons-ui/next/(.*)$": + "/../../packages/commons-ui-next/src/$1", + }, + transformIgnorePatterns: ["/node_modules/(?!camelcase-keys)"], +}; diff --git a/apps/civicsignalblog/jest.setup.js b/apps/civicsignalblog/jest.setup.js new file mode 100644 index 000000000..646b9d21c --- /dev/null +++ b/apps/civicsignalblog/jest.setup.js @@ -0,0 +1,31 @@ +/* eslint-env jest */ +global.TextEncoder = jest.fn().mockImplementation(() => ({ + encode: jest.fn(), + encodeInto: jest.fn(), +})); + +global.TextDecoder = jest.fn().mockImplementation(() => ({ + decode: jest.fn(), +})); + +global.TextEncoder = jest.fn().mockImplementation(() => ({ + encode: jest.fn(), + encodeInto: jest.fn(), +})); + +global.TextDecoder = jest.fn().mockImplementation(() => ({ + decode: jest.fn(), +})); + +process.env.NEXT_PUBLIC_APP_URL = "http://localhost:3000"; + +jest.mock("next/router", () => ({ + useRouter: jest.fn().mockImplementation(() => ({ + asPath: "", + isReady: true, + push: jest.fn(), + query: {}, + })), +})); + +module.exports = require("@commons-ui/testing-library/jest.setup"); diff --git a/apps/civicsignalblog/jsconfig.json b/apps/civicsignalblog/jsconfig.json new file mode 100644 index 000000000..4d7756fda --- /dev/null +++ b/apps/civicsignalblog/jsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/civicsignalblog/*": ["./src/*"], + "@/commons-ui/core/*": ["../../packages/commons-ui-core/src/*"], + "@/commons-ui/next/*": ["../../packages/commons-ui-next/src/*"] + } + }, + "exclude": ["node_modules"] +} diff --git a/apps/civicsignalblog/migrations/20240508_113513_versions_v1_v2.ts b/apps/civicsignalblog/migrations/20240508_113513_versions_v1_v2.ts new file mode 100644 index 000000000..df87bd2b5 --- /dev/null +++ b/apps/civicsignalblog/migrations/20240508_113513_versions_v1_v2.ts @@ -0,0 +1,103 @@ +import { MigrateUpArgs, MigrateDownArgs } from "@payloadcms/db-mongodb"; + +export async function up({ payload }: MigrateUpArgs): Promise { + async function migrateCollectionDocs(slug: string, docsAtATime = 100) { + const VersionsModel = payload.db.versions[slug]; + const remainingDocs = await VersionsModel.aggregate( + [ + // Sort so that newest are first + { + $sort: { + updatedAt: -1, + }, + }, + // Group by parent ID + // take the $first of each + { + $group: { + _id: "$parent", + _versionID: { $first: "$_id" }, + createdAt: { $first: "$createdAt" }, + latest: { $first: "$latest" }, + updatedAt: { $first: "$updatedAt" }, + version: { $first: "$version" }, + }, + }, + { + $match: { + latest: { $eq: null }, + }, + }, + { + $limit: docsAtATime, + }, + ], + { + allowDiskUse: true, + }, + ).exec(); + + if (!remainingDocs || remainingDocs.length === 0) { + const newVersions = await VersionsModel.find({ + latest: { + $eq: true, + }, + }); + + if (newVersions?.length) { + payload.logger.info( + `Migrated ${newVersions.length} documents in the "${slug}" versions collection.`, + ); + } + + return; + } + + const remainingDocIds = remainingDocs.map((doc) => doc._versionID); + + await VersionsModel.updateMany( + { + _id: { + $in: remainingDocIds, + }, + }, + { + latest: true, + }, + ); + + await migrateCollectionDocs(slug); + } + + // For each collection + await Promise.all( + payload.config.collections.map(async ({ slug, versions }) => { + if (versions?.drafts) { + return migrateCollectionDocs(slug); + } + }), + ); + + // For each global + await Promise.all( + payload.config.globals.map(async ({ slug, versions }) => { + if (versions) { + const VersionsModel = payload.db.versions[slug]; + + await VersionsModel.findOneAndUpdate( + {}, + { latest: true }, + { + sort: { updatedAt: -1 }, + }, + ).exec(); + + payload.logger.info(`Migrated the "${slug}" global.`); + } + }), + ); +} + +export async function down({ payload }: MigrateDownArgs): Promise { + // Migration code +} diff --git a/apps/civicsignalblog/next-env.d.ts b/apps/civicsignalblog/next-env.d.ts new file mode 100644 index 000000000..4f11a03dc --- /dev/null +++ b/apps/civicsignalblog/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/apps/civicsignalblog/next.config.js b/apps/civicsignalblog/next.config.js new file mode 100644 index 000000000..db92419f2 --- /dev/null +++ b/apps/civicsignalblog/next.config.js @@ -0,0 +1,50 @@ +const { withSentryConfig } = require("@sentry/nextjs"); + +const nextConfig = { + images: { + domains: process.env.NEXT_PUBLIC_IMAGE_DOMAINS?.split(",") + ?.map((d) => d.trim()) + ?.filter((d) => d), + unoptimized: + process.env.NEXT_PUBLIC_IMAGE_UNOPTIMIZED?.trim()?.toLowerCase() === + "true", + }, + modularizeImports: { + // NOTE: only transform @mui/material and not any of sub-modules e.g. @mui/material/styles. + "@mui/material^": { + transform: "@mui/material/{{member}}", + }, + }, + pageExtensions: ["page.js"], + reactStrictMode: true, + transpilePackages: ["@commons-ui/core", "@commons-ui/next"], + webpack: (config) => { + config.module.rules.push( + { + test: /\.svg$/i, + type: "asset", + resourceQuery: /url/, // *.svg?url + }, + { + test: /\.svg$/i, + issuer: /\.[jt]sx?$/, + resourceQuery: { not: [/url/] }, // exclude react component if *.svg?url + use: ["@svgr/webpack"], + }, + { + test: /\.md$/, + loader: "frontmatter-markdown-loader", + }, + ); + config.experiments = { ...config.experiments, topLevelAwait: true }; // eslint-disable-line no-param-reassign + return config; + }, +}; + +module.exports = withSentryConfig(nextConfig, { + silent: true, + hideSourceMaps: true, + org: process.env.SENTRY_ORG, + authToken: process.env.SENTRY_AUTH_TOKEN, + project: process.env.SENTRY_PROJECT, +}); diff --git a/apps/civicsignalblog/package.json b/apps/civicsignalblog/package.json new file mode 100644 index 000000000..8b7d30dab --- /dev/null +++ b/apps/civicsignalblog/package.json @@ -0,0 +1,104 @@ +{ + "name": "civicsignalblog", + "version": "0.1.2", + "private": true, + "author": "Code for Africa ", + "description": "This is the (temporary) CivicSignal blog", + "keywords": [ + "civicsignal", + "next", + "next.js", + "react", + "mui" + ], + "repository": { + "type": "git", + "url": "https://github.com/codeforafrica/ui.git", + "directory": "apps/civicsignalblog" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/codeforafrica/ui/issues" + }, + "scripts": { + "build-server": "tsc --project tsconfig.server.json", + "build-next": "NEXT_BUILD=true pnpm build-server && NEXT_BUILD=true PAYLOAD_CONFIG_PATH=${PAYLOAD_CONFIG_PATH:-dist/payload.config.js} node dist/server.js", + "build-payload": "payload build", + "start": "PAYLOAD_CONFIG_PATH=${PAYLOAD_CONFIG_PATH:-dist/payload.config.js} NODE_ENV=${NODE_ENV:-production} node dist/server.js", + "dev": "NODE_OPTIONS='--inspect' ts-node --project tsconfig.server.json server.ts", + "lint-check": "TIMING=1 eslint './'", + "lint": "TIMING=1 eslint --fix './'", + "jest": "jest", + "playwright": "npx playwright test", + "clean": "rm -rf .next .turbo build dist node_modules" + }, + "dependencies": { + "@aws-sdk/client-s3": "^3.622.0", + "@aws-sdk/lib-storage": "^3.622.0", + "@commons-ui/core": "workspace:*", + "@commons-ui/next": "workspace:*", + "@emotion/cache": "^11.13.1", + "@emotion/react": "^11.13.0", + "@emotion/server": "^11.11.0", + "@emotion/styled": "^11.13.0", + "@googlemaps/react-wrapper": "^1.1.35", + "@mui/material": "^5.16.6", + "@mui/utils": "^5.16.6", + "@next/env": "^14.2.5", + "@payloadcms/bundler-webpack": "^1.0.7", + "@payloadcms/db-mongodb": "^1.7.1", + "@payloadcms/plugin-cloud-storage": "^1.1.3", + "@payloadcms/plugin-nested-docs": "^1.0.12", + "@payloadcms/plugin-sentry": "^0.0.6", + "@payloadcms/plugin-seo": "^2.3.2", + "@payloadcms/richtext-slate": "^1.5.2", + "@sentry/nextjs": "^8.22.0", + "camelcase-keys": "^9.1.3", + "dotenv": "^16.4.5", + "express": "^4.19.2", + "fast-equals": "^5.0.1", + "gray-matter": "^4.0.3", + "js-yaml": "^4.1.0", + "jsdom": "^23.2.0", + "next": "^14.2.5", + "next-seo": "^6.5.0", + "nodemailer-sendgrid": "^1.0.3", + "payload": "^2.25.0", + "prop-types": "^15.8.1", + "qs": "^6.13.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-share": "^5.1.0", + "sharp": "^0.33.4", + "slate": "^0.103.0", + "swr": "^2.2.5" + }, + "devDependencies": { + "@babel/core": "^7.25.2", + "@babel/preset-react": "^7.24.7", + "@commons-ui/testing-library": "workspace:*", + "@playwright/test": "^1.45.3", + "@svgr/webpack": "^8.1.0", + "@swc/core": "^1.7.5", + "@types/express": "^4.17.21", + "@types/node": "^20.14.14", + "@types/react": "^18.3.3", + "babel-jest": "^29.7.0", + "eslint": "^8.57.0", + "eslint-config-commons-ui": "workspace:*", + "eslint-import-resolver-webpack": "^0.13.8", + "eslint-plugin-import": "^2.29.1", + "identity-obj-proxy": "^3.0.0", + "jest": "^29.7.0", + "jest-config-commons-ui": "workspace:*", + "playwright-config-commons-ui": "workspace:*", + "prettier": "^3.3.3", + "react-test-renderer": "^18.3.1", + "ts-node": "^10.9.2", + "typescript": "^5.5.4", + "webpack": "^5.93.0" + }, + "engines": { + "node": "20.x" + } +} diff --git a/apps/civicsignalblog/payload.config.ts b/apps/civicsignalblog/payload.config.ts new file mode 100644 index 000000000..3d2b683e4 --- /dev/null +++ b/apps/civicsignalblog/payload.config.ts @@ -0,0 +1,134 @@ +import path from "path"; + +import { buildConfig } from "payload/config"; +import { slateEditor } from "@payloadcms/richtext-slate"; +import { mongooseAdapter } from "@payloadcms/db-mongodb"; +import { webpackBundler } from "@payloadcms/bundler-webpack"; +import { CollectionConfig, GlobalConfig } from "payload/types"; +import { cloudStorage } from "@payloadcms/plugin-cloud-storage"; +import dotenv from "dotenv"; +import { sentry } from "@payloadcms/plugin-sentry"; +import seo from "@payloadcms/plugin-seo"; +import nestedDocs from "@payloadcms/plugin-nested-docs"; +import { s3Adapter } from "@payloadcms/plugin-cloud-storage/s3"; + +import Authors from "./src/payload/collections/Authors"; +import Media from "./src/payload/collections/Media"; +import Pages from "./src/payload/collections/Pages"; +import Posts from "./src/payload/collections/Posts"; +import Publication from "./src/payload/globals/Publication"; +import Site from "./src/payload/globals/Site"; +import Tags from "./src/payload/collections/Tags"; +import Users from "./src/payload/collections/Users"; +import { defaultLocale, locales } from "./src/payload/utils/locales"; + +dotenv.config(); +dotenv.config({ path: "./.env.local" }); + +const appURL = process?.env?.PAYLOAD_PUBLIC_APP_URL; + +const cors = + process?.env?.PAYLOAD_CORS?.split(",") + ?.map((d) => d.trim()) + ?.filter(Boolean) ?? []; + +const csrf = + process?.env?.PAYLOAD_CSRF?.split(",") + ?.map((d) => d.trim()) + ?.filter(Boolean) ?? []; + +const adapter = s3Adapter({ + config: { + region: process?.env?.S3_REGION, + credentials: { + accessKeyId: process?.env?.S3_ACCESS_KEY_ID, + secretAccessKey: process?.env?.S3_SECRET_ACCESS_KEY, + }, + }, + bucket: process?.env?.S3_BUCKET, +} as any); + +export default buildConfig({ + serverURL: appURL, + editor: slateEditor({}), + db: mongooseAdapter({ + url: process.env.MONGO_URL, + migrationDir: process.env.MIGRATIONS_DIR, + }), + collections: [ + Authors, + Media, + Pages, + Posts, + Tags, + Users, + ] as CollectionConfig[], + globals: [Publication, Site] as GlobalConfig[], + ...(locales?.length + ? { + localization: { + locales, + defaultLocale, + fallback: true, + }, + } + : undefined), + admin: { + css: path.resolve(__dirname, "./src/payload/admin/scss/custom.scss"), + user: Users.slug, + webpack: (config) => ({ + ...config, + resolve: { + ...config.resolve, + fallback: { + ...config?.resolve?.fallback, + fs: false, + os: false, + "process/browser": false, + }, + }, + }), + bundler: webpackBundler(), + }, + cors, + csrf, + i18n: { + fallbackLng: "en", // default + debug: false, // default + resources: { + en: { + "codeforafrica.validation": { + uniquePlatforms: "Please select a unique platform", + }, + }, + }, + }, + plugins: [ + cloudStorage({ + collections: { + media: { + adapter, + prefix: "media", + }, + }, + }), + sentry({ + dsn: process?.env?.NEXT_PUBLIC_SENTRY_DSN, + }), + seo({ + collections: ["pages", "posts"], + globals: [], + uploadsCollection: "media", + generateTitle: ({ doc }: any) => doc?.title?.value as string, + generateURL: ({ doc }: any) => + doc?.slug?.value ? `${appURL}/${doc.slug.value}` : undefined, + } as any), + nestedDocs({ + collections: ["pages"], + generateLabel: (_, doc) => doc.title as string, + generateURL: (docs) => + docs.reduce((url, doc) => `${url}/${doc.slug}`, ""), + }), + ] as any[], + telemetry: process?.env?.NODE_ENV !== "production", +}); diff --git a/apps/civicsignalblog/playwright.config.js b/apps/civicsignalblog/playwright.config.js new file mode 100644 index 000000000..f46c5bd3b --- /dev/null +++ b/apps/civicsignalblog/playwright.config.js @@ -0,0 +1,19 @@ +/* eslint-disable import/no-extraneous-dependencies */ +const defaultConfig = require("playwright-config-commons-ui"); + +const { use, webServer } = defaultConfig; +const PORT = 3002; +const config = { + ...defaultConfig, + use: { + ...use, + baseURL: `http://localhost:${PORT}/`, + }, + webServer: { + ...webServer, + command: `PORT=${PORT} node .next/standalone/apps/codeforafrica/server.js`, + port: PORT, + }, +}; + +module.exports = config; diff --git a/apps/civicsignalblog/public/android-chrome-192x192.png b/apps/civicsignalblog/public/android-chrome-192x192.png new file mode 100644 index 000000000..b6b1c195a Binary files /dev/null and b/apps/civicsignalblog/public/android-chrome-192x192.png differ diff --git a/apps/civicsignalblog/public/android-chrome-512x512.png b/apps/civicsignalblog/public/android-chrome-512x512.png new file mode 100644 index 000000000..994c5f68a Binary files /dev/null and b/apps/civicsignalblog/public/android-chrome-512x512.png differ diff --git a/apps/civicsignalblog/public/apple-touch-icon.png b/apps/civicsignalblog/public/apple-touch-icon.png new file mode 100644 index 000000000..bc086d518 Binary files /dev/null and b/apps/civicsignalblog/public/apple-touch-icon.png differ diff --git a/apps/civicsignalblog/public/browserconfig.xml b/apps/civicsignalblog/public/browserconfig.xml new file mode 100644 index 000000000..f9c2e67fe --- /dev/null +++ b/apps/civicsignalblog/public/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #2b5797 + + + diff --git a/apps/civicsignalblog/public/data/README.md b/apps/civicsignalblog/public/data/README.md new file mode 100644 index 000000000..669ed14f1 --- /dev/null +++ b/apps/civicsignalblog/public/data/README.md @@ -0,0 +1 @@ +# Data cache directory diff --git a/apps/civicsignalblog/public/favicon-16x16.png b/apps/civicsignalblog/public/favicon-16x16.png new file mode 100644 index 000000000..aeecce01f Binary files /dev/null and b/apps/civicsignalblog/public/favicon-16x16.png differ diff --git a/apps/civicsignalblog/public/favicon-32x32.png b/apps/civicsignalblog/public/favicon-32x32.png new file mode 100644 index 000000000..3dd2a39a8 Binary files /dev/null and b/apps/civicsignalblog/public/favicon-32x32.png differ diff --git a/apps/civicsignalblog/public/favicon.ico b/apps/civicsignalblog/public/favicon.ico new file mode 100644 index 000000000..886942077 Binary files /dev/null and b/apps/civicsignalblog/public/favicon.ico differ diff --git a/apps/civicsignalblog/public/fonts/merriweather-v28-latin-300.eot b/apps/civicsignalblog/public/fonts/merriweather-v28-latin-300.eot new file mode 100644 index 000000000..cd887e3dd Binary files /dev/null and b/apps/civicsignalblog/public/fonts/merriweather-v28-latin-300.eot differ diff --git a/apps/civicsignalblog/public/fonts/merriweather-v28-latin-300.svg b/apps/civicsignalblog/public/fonts/merriweather-v28-latin-300.svg new file mode 100644 index 000000000..78c478078 --- /dev/null +++ b/apps/civicsignalblog/public/fonts/merriweather-v28-latin-300.svg @@ -0,0 +1,374 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/civicsignalblog/public/fonts/merriweather-v28-latin-300.ttf b/apps/civicsignalblog/public/fonts/merriweather-v28-latin-300.ttf new file mode 100644 index 000000000..9f8309fca Binary files /dev/null and b/apps/civicsignalblog/public/fonts/merriweather-v28-latin-300.ttf differ diff --git a/apps/civicsignalblog/public/fonts/merriweather-v28-latin-300.woff b/apps/civicsignalblog/public/fonts/merriweather-v28-latin-300.woff new file mode 100644 index 000000000..d3300beeb Binary files /dev/null and b/apps/civicsignalblog/public/fonts/merriweather-v28-latin-300.woff differ diff --git a/apps/civicsignalblog/public/fonts/merriweather-v28-latin-300.woff2 b/apps/civicsignalblog/public/fonts/merriweather-v28-latin-300.woff2 new file mode 100644 index 000000000..a11988387 Binary files /dev/null and b/apps/civicsignalblog/public/fonts/merriweather-v28-latin-300.woff2 differ diff --git a/apps/civicsignalblog/public/fonts/merriweather-v28-latin-700.eot b/apps/civicsignalblog/public/fonts/merriweather-v28-latin-700.eot new file mode 100644 index 000000000..4a434ffba Binary files /dev/null and b/apps/civicsignalblog/public/fonts/merriweather-v28-latin-700.eot differ diff --git a/apps/civicsignalblog/public/fonts/merriweather-v28-latin-700.svg b/apps/civicsignalblog/public/fonts/merriweather-v28-latin-700.svg new file mode 100644 index 000000000..c0e3e8889 --- /dev/null +++ b/apps/civicsignalblog/public/fonts/merriweather-v28-latin-700.svg @@ -0,0 +1,375 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/civicsignalblog/public/fonts/merriweather-v28-latin-700.ttf b/apps/civicsignalblog/public/fonts/merriweather-v28-latin-700.ttf new file mode 100644 index 000000000..6d6065c96 Binary files /dev/null and b/apps/civicsignalblog/public/fonts/merriweather-v28-latin-700.ttf differ diff --git a/apps/civicsignalblog/public/fonts/merriweather-v28-latin-700.woff b/apps/civicsignalblog/public/fonts/merriweather-v28-latin-700.woff new file mode 100644 index 000000000..31cbeaeeb Binary files /dev/null and b/apps/civicsignalblog/public/fonts/merriweather-v28-latin-700.woff differ diff --git a/apps/civicsignalblog/public/fonts/merriweather-v28-latin-700.woff2 b/apps/civicsignalblog/public/fonts/merriweather-v28-latin-700.woff2 new file mode 100644 index 000000000..a6919a9da Binary files /dev/null and b/apps/civicsignalblog/public/fonts/merriweather-v28-latin-700.woff2 differ diff --git a/apps/civicsignalblog/public/fonts/open-sans-v28-latin-300.eot b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-300.eot new file mode 100644 index 000000000..5905323db Binary files /dev/null and b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-300.eot differ diff --git a/apps/civicsignalblog/public/fonts/open-sans-v28-latin-300.svg b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-300.svg new file mode 100644 index 000000000..3972e8496 --- /dev/null +++ b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-300.svg @@ -0,0 +1,346 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/civicsignalblog/public/fonts/open-sans-v28-latin-300.ttf b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-300.ttf new file mode 100644 index 000000000..bc6b1130b Binary files /dev/null and b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-300.ttf differ diff --git a/apps/civicsignalblog/public/fonts/open-sans-v28-latin-300.woff b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-300.woff new file mode 100644 index 000000000..a58383c7c Binary files /dev/null and b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-300.woff differ diff --git a/apps/civicsignalblog/public/fonts/open-sans-v28-latin-300.woff2 b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-300.woff2 new file mode 100644 index 000000000..b7c278af5 Binary files /dev/null and b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-300.woff2 differ diff --git a/apps/civicsignalblog/public/fonts/open-sans-v28-latin-600.eot b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-600.eot new file mode 100644 index 000000000..5176ca4d8 Binary files /dev/null and b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-600.eot differ diff --git a/apps/civicsignalblog/public/fonts/open-sans-v28-latin-600.svg b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-600.svg new file mode 100644 index 000000000..d75414d21 --- /dev/null +++ b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-600.svg @@ -0,0 +1,348 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/civicsignalblog/public/fonts/open-sans-v28-latin-600.ttf b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-600.ttf new file mode 100644 index 000000000..df48bc457 Binary files /dev/null and b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-600.ttf differ diff --git a/apps/civicsignalblog/public/fonts/open-sans-v28-latin-600.woff b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-600.woff new file mode 100644 index 000000000..6ad677a2f Binary files /dev/null and b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-600.woff differ diff --git a/apps/civicsignalblog/public/fonts/open-sans-v28-latin-600.woff2 b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-600.woff2 new file mode 100644 index 000000000..d3eaed36d Binary files /dev/null and b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-600.woff2 differ diff --git a/apps/civicsignalblog/public/fonts/open-sans-v28-latin-700.eot b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-700.eot new file mode 100644 index 000000000..4773a0e7d Binary files /dev/null and b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-700.eot differ diff --git a/apps/civicsignalblog/public/fonts/open-sans-v28-latin-700.svg b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-700.svg new file mode 100644 index 000000000..d6cd8baae --- /dev/null +++ b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-700.svg @@ -0,0 +1,349 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/civicsignalblog/public/fonts/open-sans-v28-latin-700.ttf b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-700.ttf new file mode 100644 index 000000000..599d9f70c Binary files /dev/null and b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-700.ttf differ diff --git a/apps/civicsignalblog/public/fonts/open-sans-v28-latin-700.woff b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-700.woff new file mode 100644 index 000000000..bb308f2f2 Binary files /dev/null and b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-700.woff differ diff --git a/apps/civicsignalblog/public/fonts/open-sans-v28-latin-700.woff2 b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-700.woff2 new file mode 100644 index 000000000..e86607039 Binary files /dev/null and b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-700.woff2 differ diff --git a/apps/civicsignalblog/public/fonts/open-sans-v28-latin-regular.eot b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-regular.eot new file mode 100644 index 000000000..db2317563 Binary files /dev/null and b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-regular.eot differ diff --git a/apps/civicsignalblog/public/fonts/open-sans-v28-latin-regular.svg b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-regular.svg new file mode 100644 index 000000000..f6bfcca54 --- /dev/null +++ b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-regular.svg @@ -0,0 +1,349 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/civicsignalblog/public/fonts/open-sans-v28-latin-regular.ttf b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-regular.ttf new file mode 100644 index 000000000..eb796aadf Binary files /dev/null and b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-regular.ttf differ diff --git a/apps/civicsignalblog/public/fonts/open-sans-v28-latin-regular.woff b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-regular.woff new file mode 100644 index 000000000..796d9f32d Binary files /dev/null and b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-regular.woff differ diff --git a/apps/civicsignalblog/public/fonts/open-sans-v28-latin-regular.woff2 b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-regular.woff2 new file mode 100644 index 000000000..6354811d5 Binary files /dev/null and b/apps/civicsignalblog/public/fonts/open-sans-v28-latin-regular.woff2 differ diff --git a/apps/civicsignalblog/public/icons/Type=facebook, Size=32, Color=White.svg b/apps/civicsignalblog/public/icons/Type=facebook, Size=32, Color=White.svg new file mode 100644 index 000000000..4a8e8c8a0 --- /dev/null +++ b/apps/civicsignalblog/public/icons/Type=facebook, Size=32, Color=White.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/civicsignalblog/public/icons/Type=github, Size=32, Color=White.svg b/apps/civicsignalblog/public/icons/Type=github, Size=32, Color=White.svg new file mode 100644 index 000000000..8feaa2e55 --- /dev/null +++ b/apps/civicsignalblog/public/icons/Type=github, Size=32, Color=White.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/civicsignalblog/public/icons/Type=instagram, Size=32, Color=White.svg b/apps/civicsignalblog/public/icons/Type=instagram, Size=32, Color=White.svg new file mode 100644 index 000000000..4a6508469 --- /dev/null +++ b/apps/civicsignalblog/public/icons/Type=instagram, Size=32, Color=White.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/civicsignalblog/public/icons/Type=linkedin, Size=32, Color=White.svg b/apps/civicsignalblog/public/icons/Type=linkedin, Size=32, Color=White.svg new file mode 100644 index 000000000..9848a0230 --- /dev/null +++ b/apps/civicsignalblog/public/icons/Type=linkedin, Size=32, Color=White.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/civicsignalblog/public/icons/Type=map-pin, Size=64, Color=Primary.svg b/apps/civicsignalblog/public/icons/Type=map-pin, Size=64, Color=Primary.svg new file mode 100644 index 000000000..7d40da98c --- /dev/null +++ b/apps/civicsignalblog/public/icons/Type=map-pin, Size=64, Color=Primary.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/civicsignalblog/public/icons/Type=slack, Size=32, Color=White.svg b/apps/civicsignalblog/public/icons/Type=slack, Size=32, Color=White.svg new file mode 100644 index 000000000..75e14a9ac --- /dev/null +++ b/apps/civicsignalblog/public/icons/Type=slack, Size=32, Color=White.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/civicsignalblog/public/icons/Type=twitter, Size=32, Color=White.svg b/apps/civicsignalblog/public/icons/Type=twitter, Size=32, Color=White.svg new file mode 100644 index 000000000..be940a322 --- /dev/null +++ b/apps/civicsignalblog/public/icons/Type=twitter, Size=32, Color=White.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/civicsignalblog/public/image.jpg b/apps/civicsignalblog/public/image.jpg new file mode 100644 index 000000000..68819be9c Binary files /dev/null and b/apps/civicsignalblog/public/image.jpg differ diff --git a/apps/civicsignalblog/public/images/cms/blocks/contact_form.jpg b/apps/civicsignalblog/public/images/cms/blocks/contact_form.jpg new file mode 100644 index 000000000..06aeb1bc6 Binary files /dev/null and b/apps/civicsignalblog/public/images/cms/blocks/contact_form.jpg differ diff --git a/apps/civicsignalblog/public/images/cms/blocks/custom_page_header.jpg b/apps/civicsignalblog/public/images/cms/blocks/custom_page_header.jpg new file mode 100644 index 000000000..078189342 Binary files /dev/null and b/apps/civicsignalblog/public/images/cms/blocks/custom_page_header.jpg differ diff --git a/apps/civicsignalblog/public/images/cms/blocks/error.png b/apps/civicsignalblog/public/images/cms/blocks/error.png new file mode 100644 index 000000000..dcd2e66e6 Binary files /dev/null and b/apps/civicsignalblog/public/images/cms/blocks/error.png differ diff --git a/apps/civicsignalblog/public/images/cms/blocks/featured_stories.jpg b/apps/civicsignalblog/public/images/cms/blocks/featured_stories.jpg new file mode 100644 index 000000000..33cc3e2a5 Binary files /dev/null and b/apps/civicsignalblog/public/images/cms/blocks/featured_stories.jpg differ diff --git a/apps/civicsignalblog/public/images/cms/blocks/get_in_touch.jpg b/apps/civicsignalblog/public/images/cms/blocks/get_in_touch.jpg new file mode 100644 index 000000000..ca1fb2b1e Binary files /dev/null and b/apps/civicsignalblog/public/images/cms/blocks/get_in_touch.jpg differ diff --git a/apps/civicsignalblog/public/images/cms/blocks/get_involved.jpg b/apps/civicsignalblog/public/images/cms/blocks/get_involved.jpg new file mode 100644 index 000000000..e0759fe80 Binary files /dev/null and b/apps/civicsignalblog/public/images/cms/blocks/get_involved.jpg differ diff --git a/apps/civicsignalblog/public/images/cms/blocks/guiding_principles.png b/apps/civicsignalblog/public/images/cms/blocks/guiding_principles.png new file mode 100644 index 000000000..1b5de838a Binary files /dev/null and b/apps/civicsignalblog/public/images/cms/blocks/guiding_principles.png differ diff --git a/apps/civicsignalblog/public/images/cms/blocks/hero.jpg b/apps/civicsignalblog/public/images/cms/blocks/hero.jpg new file mode 100644 index 000000000..95c346194 Binary files /dev/null and b/apps/civicsignalblog/public/images/cms/blocks/hero.jpg differ diff --git a/apps/civicsignalblog/public/images/cms/blocks/join_our_slack.jpg b/apps/civicsignalblog/public/images/cms/blocks/join_our_slack.jpg new file mode 100644 index 000000000..1d245879a Binary files /dev/null and b/apps/civicsignalblog/public/images/cms/blocks/join_our_slack.jpg differ diff --git a/apps/civicsignalblog/public/images/cms/blocks/longform.jpg b/apps/civicsignalblog/public/images/cms/blocks/longform.jpg new file mode 100644 index 000000000..a89295287 Binary files /dev/null and b/apps/civicsignalblog/public/images/cms/blocks/longform.jpg differ diff --git a/apps/civicsignalblog/public/images/cms/blocks/meet_our_team.jpg b/apps/civicsignalblog/public/images/cms/blocks/meet_our_team.jpg new file mode 100644 index 000000000..7cac36961 Binary files /dev/null and b/apps/civicsignalblog/public/images/cms/blocks/meet_our_team.jpg differ diff --git a/apps/civicsignalblog/public/images/cms/blocks/our-work-showcase.png b/apps/civicsignalblog/public/images/cms/blocks/our-work-showcase.png new file mode 100644 index 000000000..6382ba177 Binary files /dev/null and b/apps/civicsignalblog/public/images/cms/blocks/our-work-showcase.png differ diff --git a/apps/civicsignalblog/public/images/cms/blocks/our_impact.jpg b/apps/civicsignalblog/public/images/cms/blocks/our_impact.jpg new file mode 100644 index 000000000..b01b4a6c4 Binary files /dev/null and b/apps/civicsignalblog/public/images/cms/blocks/our_impact.jpg differ diff --git a/apps/civicsignalblog/public/images/cms/blocks/our_mission.jpg b/apps/civicsignalblog/public/images/cms/blocks/our_mission.jpg new file mode 100644 index 000000000..e6b4fc1b0 Binary files /dev/null and b/apps/civicsignalblog/public/images/cms/blocks/our_mission.jpg differ diff --git a/apps/civicsignalblog/public/images/cms/blocks/our_offices.png b/apps/civicsignalblog/public/images/cms/blocks/our_offices.png new file mode 100644 index 000000000..140ebec9b Binary files /dev/null and b/apps/civicsignalblog/public/images/cms/blocks/our_offices.png differ diff --git a/apps/civicsignalblog/public/images/cms/blocks/page_header.jpg b/apps/civicsignalblog/public/images/cms/blocks/page_header.jpg new file mode 100644 index 000000000..c6eba6b6c Binary files /dev/null and b/apps/civicsignalblog/public/images/cms/blocks/page_header.jpg differ diff --git a/apps/civicsignalblog/public/images/cms/blocks/partners.png b/apps/civicsignalblog/public/images/cms/blocks/partners.png new file mode 100644 index 000000000..7f87ee37e Binary files /dev/null and b/apps/civicsignalblog/public/images/cms/blocks/partners.png differ diff --git a/apps/civicsignalblog/public/images/cms/blocks/posts.jpg b/apps/civicsignalblog/public/images/cms/blocks/posts.jpg new file mode 100644 index 000000000..dce8f4dbf Binary files /dev/null and b/apps/civicsignalblog/public/images/cms/blocks/posts.jpg differ diff --git a/apps/civicsignalblog/public/images/cms/blocks/projects.png b/apps/civicsignalblog/public/images/cms/blocks/projects.png new file mode 100644 index 000000000..0f1c207a5 Binary files /dev/null and b/apps/civicsignalblog/public/images/cms/blocks/projects.png differ diff --git a/apps/civicsignalblog/public/images/cms/blocks/team.png b/apps/civicsignalblog/public/images/cms/blocks/team.png new file mode 100644 index 000000000..0eb5dcb16 Binary files /dev/null and b/apps/civicsignalblog/public/images/cms/blocks/team.png differ diff --git a/apps/civicsignalblog/public/images/type-facebook-size-32-color-white.svg b/apps/civicsignalblog/public/images/type-facebook-size-32-color-white.svg new file mode 100644 index 000000000..4a8e8c8a0 --- /dev/null +++ b/apps/civicsignalblog/public/images/type-facebook-size-32-color-white.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/civicsignalblog/public/images/type-github-size-32-color-white.svg b/apps/civicsignalblog/public/images/type-github-size-32-color-white.svg new file mode 100644 index 000000000..8feaa2e55 --- /dev/null +++ b/apps/civicsignalblog/public/images/type-github-size-32-color-white.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/civicsignalblog/public/images/type-instagram-size-32-color-white.svg b/apps/civicsignalblog/public/images/type-instagram-size-32-color-white.svg new file mode 100644 index 000000000..4a6508469 --- /dev/null +++ b/apps/civicsignalblog/public/images/type-instagram-size-32-color-white.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/civicsignalblog/public/images/type-linkedin-size-32-color-white.svg b/apps/civicsignalblog/public/images/type-linkedin-size-32-color-white.svg new file mode 100644 index 000000000..9848a0230 --- /dev/null +++ b/apps/civicsignalblog/public/images/type-linkedin-size-32-color-white.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/civicsignalblog/public/images/type-slack-size-32-color-white.svg b/apps/civicsignalblog/public/images/type-slack-size-32-color-white.svg new file mode 100644 index 000000000..75e14a9ac --- /dev/null +++ b/apps/civicsignalblog/public/images/type-slack-size-32-color-white.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/civicsignalblog/public/images/type-slack-size-64-color-currentcolor.svg b/apps/civicsignalblog/public/images/type-slack-size-64-color-currentcolor.svg new file mode 100644 index 000000000..d7e085fc5 --- /dev/null +++ b/apps/civicsignalblog/public/images/type-slack-size-64-color-currentcolor.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/civicsignalblog/public/images/type-twitter-size-32-color-white.svg b/apps/civicsignalblog/public/images/type-twitter-size-32-color-white.svg new file mode 100644 index 000000000..d8009fd1a --- /dev/null +++ b/apps/civicsignalblog/public/images/type-twitter-size-32-color-white.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/civicsignalblog/public/images/vector.png b/apps/civicsignalblog/public/images/vector.png new file mode 100644 index 000000000..a214feae0 Binary files /dev/null and b/apps/civicsignalblog/public/images/vector.png differ diff --git a/apps/civicsignalblog/public/mstile-150x150.png b/apps/civicsignalblog/public/mstile-150x150.png new file mode 100644 index 000000000..f47cd2c07 Binary files /dev/null and b/apps/civicsignalblog/public/mstile-150x150.png differ diff --git a/apps/civicsignalblog/public/safari-pinned-tab.svg b/apps/civicsignalblog/public/safari-pinned-tab.svg new file mode 100644 index 000000000..dcae051b6 --- /dev/null +++ b/apps/civicsignalblog/public/safari-pinned-tab.svg @@ -0,0 +1,46 @@ + + + + +Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + + + + + + diff --git a/apps/civicsignalblog/public/site.webmanifest b/apps/civicsignalblog/public/site.webmanifest new file mode 100644 index 000000000..96ca8fa01 --- /dev/null +++ b/apps/civicsignalblog/public/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "Code for Africa", + "short_name": "CfA", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/apps/civicsignalblog/sentry.client.config.js b/apps/civicsignalblog/sentry.client.config.js new file mode 100644 index 000000000..4648af1c2 --- /dev/null +++ b/apps/civicsignalblog/sentry.client.config.js @@ -0,0 +1,14 @@ +// This file configures the initialization of Sentry on the browser. +// The config you add here will be used whenever a page is visited. +// https://docs.sentry.io/platforms/javascript/guides/nextjs/ + +import * as Sentry from "@sentry/nextjs"; + +const SENTRY_DSN = process.env.NEXT_PUBLIC_SENTRY_DSN; + +Sentry.init({ + dsn: SENTRY_DSN, + // Note: if you want to override the automatic release value, do not set a + // `release` value here - use the environment variable `SENTRY_RELEASE`, so + // that it will also get attached to your source maps +}); diff --git a/apps/civicsignalblog/sentry.edge.config.js b/apps/civicsignalblog/sentry.edge.config.js new file mode 100644 index 000000000..cd9a6cfa8 --- /dev/null +++ b/apps/civicsignalblog/sentry.edge.config.js @@ -0,0 +1,15 @@ +// This file configures the initialization of Sentry for edge features (middleware, edge routes, and so on). +// The config you add here will be used whenever one of the edge features is loaded. +// Note that this config is unrelated to the Verel Edge Runtime and is also required when running locally. +// https://docs.sentry.io/platforms/javascript/guides/nextjs/ + +import * as Sentry from "@sentry/nextjs"; + +const SENTRY_DSN = process.env.NEXT_PUBLIC_SENTRY_DSN; + +Sentry.init({ + dsn: SENTRY_DSN, + // Note: if you want to override the automatic release value, do not set a + // `release` value here - use the environment variable `SENTRY_RELEASE`, so + // that it will also get attached to your source maps +}); diff --git a/apps/civicsignalblog/sentry.server.config.js b/apps/civicsignalblog/sentry.server.config.js new file mode 100644 index 000000000..a74911ba0 --- /dev/null +++ b/apps/civicsignalblog/sentry.server.config.js @@ -0,0 +1,14 @@ +// This file configures the initialization of Sentry on the server. +// The config you add here will be used whenever the server handles a request. +// https://docs.sentry.io/platforms/javascript/guides/nextjs/ + +import * as Sentry from "@sentry/nextjs"; + +const SENTRY_DSN = process.env.NEXT_PUBLIC_SENTRY_DSN; + +Sentry.init({ + dsn: SENTRY_DSN, + // Note: if you want to override the automatic release value, do not set a + // `release` value here - use the environment variable `SENTRY_RELEASE`, so + // that it will also get attached to your source maps +}); diff --git a/apps/civicsignalblog/server.ts b/apps/civicsignalblog/server.ts new file mode 100644 index 000000000..063368788 --- /dev/null +++ b/apps/civicsignalblog/server.ts @@ -0,0 +1,92 @@ +import path from "path"; +import { spawn } from "child_process"; +import express from "express"; +import next from "next"; +import nodemailerSendgrid from "nodemailer-sendgrid"; +import payload from "payload"; +import { Payload } from "payload/dist/payload"; +import { loadEnvConfig } from "@next/env"; + +const projectDir = process.cwd(); +loadEnvConfig(projectDir); + +const dev = process.env.NODE_ENV !== "production"; +const hostname = process.env.NEXT_HOSTNAME || "localhost"; +const port = parseInt(process.env.PORT || "3000", 10); +const sendGridAPIKey = process.env.SENDGRID_API_KEY; + +if (!process.env.NEXT_MANUAL_SIG_HANDLE) { + process.on("SIGTERM", () => process.exit(0)); + process.on("SIGINT", () => process.exit(0)); +} + +const app = express(); + +const start = async (): Promise => { + let localPayload: Payload; + try { + localPayload = await payload.init({ + ...(sendGridAPIKey + ? { + email: { + transportOptions: nodemailerSendgrid({ + apiKey: sendGridAPIKey, + }), + fromName: process.env.SENDGRID_FROM_NAME || "Code for Africa CMS", + fromAddress: + process.env.SENDGRID_FROM_EMAIL || "noreply@dodeforafrica.org", + }, + } + : undefined), + secret: process.env.PAYLOAD_SECRET, + express: app, + onInit: (initPayload) => { + initPayload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`); + }, + }); + } catch (e: any) { + console.error(e); + process.exit(); + } + + if (process.env.NEXT_BUILD) { + app.listen(port, async () => { + localPayload.logger.info("NextJS is now building..."); + const nextBuild = spawn( + "pnpm", + ["next", "build", path.resolve(projectDir)], + { + shell: true, + stdio: "inherit", + }, + ); + nextBuild.on("close", (code) => { + process.exit(code); + }); + nextBuild.on("error", (err) => { + localPayload.logger.error(err); + process.exit(1); + }); + }); + + return; + } + + const nextApp = next({ dev, hostname, port }); + const nextHandler = nextApp.getRequestHandler(); + nextApp.prepare().then(() => { + localPayload.logger.info("NextJS started"); + + app.get("*", (req: any, res: any) => nextHandler(req, res)); + app.post("*", (req: any, res: any) => nextHandler(req, res)); + app.put("*", (req: any, res: any) => nextHandler(req, res)); + + app.listen(port, async () => { + localPayload.logger.info( + `Next.js App URL: ${process.env.NEXT_PUBLIC_APP_URL}`, + ); + }); + }); +}; + +start(); diff --git a/apps/civicsignalblog/src/assets/icons/Type=arrow-left, Size=32, Color=CurrentColor.svg b/apps/civicsignalblog/src/assets/icons/Type=arrow-left, Size=32, Color=CurrentColor.svg new file mode 100644 index 000000000..11b2b3852 --- /dev/null +++ b/apps/civicsignalblog/src/assets/icons/Type=arrow-left, Size=32, Color=CurrentColor.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/civicsignalblog/src/assets/icons/Type=arrow-right, Size=32, Color=CurrentColor.svg b/apps/civicsignalblog/src/assets/icons/Type=arrow-right, Size=32, Color=CurrentColor.svg new file mode 100644 index 000000000..36522db93 --- /dev/null +++ b/apps/civicsignalblog/src/assets/icons/Type=arrow-right, Size=32, Color=CurrentColor.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/civicsignalblog/src/assets/icons/Type=external-link, Size=24, Color=White.svg b/apps/civicsignalblog/src/assets/icons/Type=external-link, Size=24, Color=White.svg new file mode 100644 index 000000000..82ef14a06 --- /dev/null +++ b/apps/civicsignalblog/src/assets/icons/Type=external-link, Size=24, Color=White.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/civicsignalblog/src/assets/icons/Type=facebook, Size=24, Color=CurrentColor.svg b/apps/civicsignalblog/src/assets/icons/Type=facebook, Size=24, Color=CurrentColor.svg new file mode 100644 index 000000000..3914b3321 --- /dev/null +++ b/apps/civicsignalblog/src/assets/icons/Type=facebook, Size=24, Color=CurrentColor.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/civicsignalblog/src/assets/icons/Type=github, Size=24, Color=CurrentColor.svg b/apps/civicsignalblog/src/assets/icons/Type=github, Size=24, Color=CurrentColor.svg new file mode 100644 index 000000000..33fc4f5fb --- /dev/null +++ b/apps/civicsignalblog/src/assets/icons/Type=github, Size=24, Color=CurrentColor.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/civicsignalblog/src/assets/icons/Type=instagram, Size=24, Color=CurrentColor.svg b/apps/civicsignalblog/src/assets/icons/Type=instagram, Size=24, Color=CurrentColor.svg new file mode 100644 index 000000000..2680211cd --- /dev/null +++ b/apps/civicsignalblog/src/assets/icons/Type=instagram, Size=24, Color=CurrentColor.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/apps/civicsignalblog/src/assets/icons/Type=linkedin, Size=24, Color=CurrentColor.svg b/apps/civicsignalblog/src/assets/icons/Type=linkedin, Size=24, Color=CurrentColor.svg new file mode 100644 index 000000000..18e5c230d --- /dev/null +++ b/apps/civicsignalblog/src/assets/icons/Type=linkedin, Size=24, Color=CurrentColor.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/civicsignalblog/src/assets/icons/Type=mail, Size=24, Color=CurrentColor.svg b/apps/civicsignalblog/src/assets/icons/Type=mail, Size=24, Color=CurrentColor.svg new file mode 100644 index 000000000..1bf0267b3 --- /dev/null +++ b/apps/civicsignalblog/src/assets/icons/Type=mail, Size=24, Color=CurrentColor.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/civicsignalblog/src/assets/icons/Type=search, Size=16, Color=CurrentColor.svg b/apps/civicsignalblog/src/assets/icons/Type=search, Size=16, Color=CurrentColor.svg new file mode 100644 index 000000000..bc015fcdc --- /dev/null +++ b/apps/civicsignalblog/src/assets/icons/Type=search, Size=16, Color=CurrentColor.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/civicsignalblog/src/assets/icons/Type=send, Size=16, Color=White.svg b/apps/civicsignalblog/src/assets/icons/Type=send, Size=16, Color=White.svg new file mode 100644 index 000000000..258d6d0b0 --- /dev/null +++ b/apps/civicsignalblog/src/assets/icons/Type=send, Size=16, Color=White.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/civicsignalblog/src/assets/icons/Type=slack, Size=24, Color=CurrentColor.svg b/apps/civicsignalblog/src/assets/icons/Type=slack, Size=24, Color=CurrentColor.svg new file mode 100644 index 000000000..2f69259e7 --- /dev/null +++ b/apps/civicsignalblog/src/assets/icons/Type=slack, Size=24, Color=CurrentColor.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/apps/civicsignalblog/src/assets/icons/Type=slack, Size=64, Color=CurrentColor.svg b/apps/civicsignalblog/src/assets/icons/Type=slack, Size=64, Color=CurrentColor.svg new file mode 100644 index 000000000..d7e085fc5 --- /dev/null +++ b/apps/civicsignalblog/src/assets/icons/Type=slack, Size=64, Color=CurrentColor.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/civicsignalblog/src/assets/icons/Type=twitter, Size=24, Color=CurrentColor.svg b/apps/civicsignalblog/src/assets/icons/Type=twitter, Size=24, Color=CurrentColor.svg new file mode 100644 index 000000000..c56259497 --- /dev/null +++ b/apps/civicsignalblog/src/assets/icons/Type=twitter, Size=24, Color=CurrentColor.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/civicsignalblog/src/assets/icons/Type=x, Size=24, Color=CurrentColor.svg b/apps/civicsignalblog/src/assets/icons/Type=x, Size=24, Color=CurrentColor.svg new file mode 100644 index 000000000..93fbad2f7 --- /dev/null +++ b/apps/civicsignalblog/src/assets/icons/Type=x, Size=24, Color=CurrentColor.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/civicsignalblog/src/assets/images/1920x668px bg - 2 2.png b/apps/civicsignalblog/src/assets/images/1920x668px bg - 2 2.png new file mode 100644 index 000000000..39c60da93 Binary files /dev/null and b/apps/civicsignalblog/src/assets/images/1920x668px bg - 2 2.png differ diff --git a/apps/civicsignalblog/src/assets/images/CfAlogoBW.png b/apps/civicsignalblog/src/assets/images/CfAlogoBW.png new file mode 100644 index 000000000..3ff0c7fa8 Binary files /dev/null and b/apps/civicsignalblog/src/assets/images/CfAlogoBW.png differ diff --git a/apps/civicsignalblog/src/assets/menu-icon.svg b/apps/civicsignalblog/src/assets/menu-icon.svg new file mode 100644 index 000000000..ae7b0ae3d --- /dev/null +++ b/apps/civicsignalblog/src/assets/menu-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/civicsignalblog/src/components/ArticleCard/ArticleCard.js b/apps/civicsignalblog/src/components/ArticleCard/ArticleCard.js new file mode 100644 index 000000000..0e81ba75b --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticleCard/ArticleCard.js @@ -0,0 +1,54 @@ +import { Card } from "@mui/material"; +import { styled } from "@mui/material/styles"; +import React from "react"; + +const ArticleCardRoot = styled(Card, { + overridesResolver: (props, styles) => { + const { ownerState } = props; + + return [styles.root, styles[ownerState.variant]]; + }, +})(({ theme, ownerState }) => ({ + "&:hover": { + ...(ownerState.variant === "outlined" && { + border: `1px solid ${theme.palette.highlight.main}`, + img: { + opacity: 0.8, + filter: "none", + }, + }), + }, + ...(ownerState.variant === "outlined" && { + border: "1px solid #DAD5D5", + filter: "drop-shadow(0px 4px 8px rgba(0, 0, 0, 0.1))", + }), +})); + +const ArticleCard = React.forwardRef(function ArticleCard(props, ref) { + const { + elevation = 0, + square = true, + variant = "outlined", + ...other + } = props; + + const ownerState = { + ...other, + elevation, + square, + variant, + }; + + return ( + + ); +}); + +export default ArticleCard; diff --git a/apps/civicsignalblog/src/components/ArticleCard/ArticleCard.snap.js b/apps/civicsignalblog/src/components/ArticleCard/ArticleCard.snap.js new file mode 100644 index 000000000..a929af2fb --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticleCard/ArticleCard.snap.js @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders unchanged 1`] = ` +
+
+
+`; diff --git a/apps/civicsignalblog/src/components/ArticleCard/ArticleCard.test.js b/apps/civicsignalblog/src/components/ArticleCard/ArticleCard.test.js new file mode 100644 index 000000000..fb6f471b3 --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticleCard/ArticleCard.test.js @@ -0,0 +1,16 @@ +import { createRender } from "@commons-ui/testing-library"; +import React from "react"; + +import ArticleCard from "./ArticleCard"; + +import theme from "@/civicsignalblog/theme"; + +// eslint-disable-next-line testing-library/render-result-naming-convention +const render = createRender({ theme }); + +describe("", () => { + it("renders unchanged", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/apps/civicsignalblog/src/components/ArticleCard/index.js b/apps/civicsignalblog/src/components/ArticleCard/index.js new file mode 100644 index 000000000..e93a343b5 --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticleCard/index.js @@ -0,0 +1,3 @@ +import ArticleCard from "./ArticleCard"; + +export default ArticleCard; diff --git a/apps/civicsignalblog/src/components/ArticleCardContent/ArticleCardContent.js b/apps/civicsignalblog/src/components/ArticleCardContent/ArticleCardContent.js new file mode 100644 index 000000000..be02b537e --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticleCardContent/ArticleCardContent.js @@ -0,0 +1,15 @@ +import { CardContent } from "@mui/material"; +import { styled } from "@mui/material/styles"; +import React from "react"; + +const ArticleCardContentRoot = styled(CardContent)(({ theme }) => ({ + padding: theme.typography.pxToRem(24), +})); + +const ArticleCardContent = React.forwardRef( + function ArticleCardContent(props, ref) { + return ; + }, +); + +export default ArticleCardContent; diff --git a/apps/civicsignalblog/src/components/ArticleCardContent/ArticleCardContent.snap.js b/apps/civicsignalblog/src/components/ArticleCardContent/ArticleCardContent.snap.js new file mode 100644 index 000000000..14102b70a --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticleCardContent/ArticleCardContent.snap.js @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders unchanged 1`] = ` +
+
+
+`; diff --git a/apps/civicsignalblog/src/components/ArticleCardContent/ArticleCardContent.test.js b/apps/civicsignalblog/src/components/ArticleCardContent/ArticleCardContent.test.js new file mode 100644 index 000000000..a09c7cf26 --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticleCardContent/ArticleCardContent.test.js @@ -0,0 +1,16 @@ +import { createRender } from "@commons-ui/testing-library"; +import React from "react"; + +import ArticleCardContent from "./ArticleCardContent"; + +import theme from "@/civicsignalblog/theme"; + +// eslint-disable-next-line testing-library/render-result-naming-convention +const render = createRender({ theme }); + +describe("", () => { + it("renders unchanged", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/apps/civicsignalblog/src/components/ArticleCardContent/index.js b/apps/civicsignalblog/src/components/ArticleCardContent/index.js new file mode 100644 index 000000000..8fb7e8518 --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticleCardContent/index.js @@ -0,0 +1,3 @@ +import ArticleCardContent from "./ArticleCardContent"; + +export default ArticleCardContent; diff --git a/apps/civicsignalblog/src/components/ArticleCardList/ArticleCardList.js b/apps/civicsignalblog/src/components/ArticleCardList/ArticleCardList.js new file mode 100644 index 000000000..1dae13fd5 --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticleCardList/ArticleCardList.js @@ -0,0 +1,59 @@ +import { Link } from "@commons-ui/next"; +import { CardActionArea, Grid, Typography } from "@mui/material"; +import React from "react"; + +import ArticleCard from "@/civicsignalblog/components/ArticleCard"; +import ArticleCardContent from "@/civicsignalblog/components/ArticleCardContent"; +import ArticleCardMedia from "@/civicsignalblog/components/ArticleCardMedia"; + +const ArticleCardList = React.forwardRef(function ArticleCardList(props, ref) { + const { articles, ...other } = props; + + if (!articles?.length) { + return null; + } + return ( + + {articles?.map((article) => ( + + + + + + {article.title} + + {article.publishedOn} + + + + + + ))} + + ); +}); + +export default ArticleCardList; diff --git a/apps/civicsignalblog/src/components/ArticleCardList/ArticleCardList.snap.js b/apps/civicsignalblog/src/components/ArticleCardList/ArticleCardList.snap.js new file mode 100644 index 000000000..067f7f48f --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticleCardList/ArticleCardList.snap.js @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders unchanged 1`] = `
`; diff --git a/apps/civicsignalblog/src/components/ArticleCardList/ArticleCardList.test.js b/apps/civicsignalblog/src/components/ArticleCardList/ArticleCardList.test.js new file mode 100644 index 000000000..b6409fd91 --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticleCardList/ArticleCardList.test.js @@ -0,0 +1,16 @@ +import { createRender } from "@commons-ui/testing-library"; +import React from "react"; + +import ArticleCardList from "./ArticleCardList"; + +import theme from "@/civicsignalblog/theme"; + +// eslint-disable-next-line testing-library/render-result-naming-convention +const render = createRender({ theme }); + +describe("", () => { + it("renders unchanged", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/apps/civicsignalblog/src/components/ArticleCardList/index.js b/apps/civicsignalblog/src/components/ArticleCardList/index.js new file mode 100644 index 000000000..212f15536 --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticleCardList/index.js @@ -0,0 +1,3 @@ +import ArticleCardList from "./ArticleCardList"; + +export default ArticleCardList; diff --git a/apps/civicsignalblog/src/components/ArticleCardMedia/ArticleCardMedia.js b/apps/civicsignalblog/src/components/ArticleCardMedia/ArticleCardMedia.js new file mode 100644 index 000000000..ff15b0193 --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticleCardMedia/ArticleCardMedia.js @@ -0,0 +1,13 @@ +import { CardMedia } from "@mui/material"; +import { styled } from "@mui/material/styles"; +import React from "react"; + +const ArticleCardMediaRoot = styled(CardMedia)({}); + +const ArticleCardMedia = React.forwardRef( + function ArticleCardMedia(props, ref) { + return ; + }, +); + +export default ArticleCardMedia; diff --git a/apps/civicsignalblog/src/components/ArticleCardMedia/ArticleCardMedia.snap.js b/apps/civicsignalblog/src/components/ArticleCardMedia/ArticleCardMedia.snap.js new file mode 100644 index 000000000..ef6d13f74 --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticleCardMedia/ArticleCardMedia.snap.js @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders unchanged 1`] = ` +
+ +
+`; diff --git a/apps/civicsignalblog/src/components/ArticleCardMedia/ArticleCardMedia.test.js b/apps/civicsignalblog/src/components/ArticleCardMedia/ArticleCardMedia.test.js new file mode 100644 index 000000000..ca9dc5f56 --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticleCardMedia/ArticleCardMedia.test.js @@ -0,0 +1,16 @@ +import { createRender } from "@commons-ui/testing-library"; +import React from "react"; + +import ArticleCardMedia from "./ArticleCardMedia"; + +import theme from "@/civicsignalblog/theme"; + +// eslint-disable-next-line testing-library/render-result-naming-convention +const render = createRender({ theme }); + +describe("", () => { + it("renders unchanged", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/apps/civicsignalblog/src/components/ArticleCardMedia/index.js b/apps/civicsignalblog/src/components/ArticleCardMedia/index.js new file mode 100644 index 000000000..85fa5029b --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticleCardMedia/index.js @@ -0,0 +1,3 @@ +import ArticleCardMedia from "./ArticleCardMedia"; + +export default ArticleCardMedia; diff --git a/apps/civicsignalblog/src/components/ArticleGrid/ArticleGrid.js b/apps/civicsignalblog/src/components/ArticleGrid/ArticleGrid.js new file mode 100644 index 000000000..174bb8eb2 --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticleGrid/ArticleGrid.js @@ -0,0 +1,89 @@ +import { Section } from "@commons-ui/core"; +import { RichTypography } from "@commons-ui/next"; +import { Grid } from "@mui/material"; +import React from "react"; + +import ArticleCardList from "@/civicsignalblog/components/ArticleCardList"; +import FeaturedArticle from "@/civicsignalblog/components/FeaturedArticle"; +import FilterBar from "@/civicsignalblog/components/FilterBar"; + +const ArticleGrid = React.forwardRef(function ArticleGrid(props, ref) { + const { + articles, + featuredArticle, + onChangeTag, + onChangeQ, + q, + selectedTag, + sx, + tags, + title, + searchLabel, + readMoreLabel, + ...other + } = props; + + return ( +
+ + 0 ? "flex" : "none", + }} + > + {/* title is below featuredArticle in md and above hence needs margin-top */} + + {title} + + + + {featuredArticle ? ( + + + + ) : null} + {articles?.length > 0 ? ( + + + + ) : null} + +
+ ); +}); + +export default ArticleGrid; diff --git a/apps/civicsignalblog/src/components/ArticleGrid/ArticleGrid.snap.js b/apps/civicsignalblog/src/components/ArticleGrid/ArticleGrid.snap.js new file mode 100644 index 000000000..72f8db681 --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticleGrid/ArticleGrid.snap.js @@ -0,0 +1,64 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders unchanged 1`] = ` +
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+
+`; diff --git a/apps/civicsignalblog/src/components/ArticleGrid/ArticleGrid.test.js b/apps/civicsignalblog/src/components/ArticleGrid/ArticleGrid.test.js new file mode 100644 index 000000000..86ab3fc4e --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticleGrid/ArticleGrid.test.js @@ -0,0 +1,16 @@ +import { createRender } from "@commons-ui/testing-library"; +import React from "react"; + +import ArticleGrid from "./ArticleGrid"; + +import theme from "@/civicsignalblog/theme"; + +// eslint-disable-next-line testing-library/render-result-naming-convention +const render = createRender({ theme }); + +describe("", () => { + it("renders unchanged", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/apps/civicsignalblog/src/components/ArticleGrid/index.js b/apps/civicsignalblog/src/components/ArticleGrid/index.js new file mode 100644 index 000000000..cc853204c --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticleGrid/index.js @@ -0,0 +1,3 @@ +import ArticleGrid from "./ArticleGrid"; + +export default ArticleGrid; diff --git a/apps/civicsignalblog/src/components/ArticleHeader/ArticleHeader.js b/apps/civicsignalblog/src/components/ArticleHeader/ArticleHeader.js new file mode 100644 index 000000000..c0396d629 --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticleHeader/ArticleHeader.js @@ -0,0 +1,86 @@ +import { Section } from "@commons-ui/core"; +import { Link, RichTypography } from "@commons-ui/next"; +import PropTypes from "prop-types"; +import React from "react"; + +import ChoiceChip from "@/civicsignalblog/components/ChoiceChip"; +import ChoiceChipGroup from "@/civicsignalblog/components/ChoiceChipGroup"; +import ShareThisPage from "@/civicsignalblog/components/ShareThisPage"; + +const ArticleHeader = React.forwardRef(function ArticleHeader(props, ref) { + const { date, excerpt, sx, tags, title, primaryTag } = props; + + return ( +
+ + {date} + + + {title} + + + {excerpt} + + {tags?.length > 0 ? ( + + {tags.map((tag) => ( + + ))} + + ) : null} + +
+ ); +}); + +ArticleHeader.propTypes = { + title: PropTypes.string, + date: PropTypes.string, + excerpt: PropTypes.string, + tags: PropTypes.arrayOf(PropTypes.shape({})), +}; + +ArticleHeader.defaultProps = { + title: undefined, + date: undefined, + tags: undefined, + excerpt: undefined, +}; + +export default ArticleHeader; diff --git a/apps/civicsignalblog/src/components/ArticleHeader/ArticleHeader.snap.js b/apps/civicsignalblog/src/components/ArticleHeader/ArticleHeader.snap.js new file mode 100644 index 000000000..3c11724c8 --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticleHeader/ArticleHeader.snap.js @@ -0,0 +1,62 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders unchanged 1`] = ` +
+
+
+
+ Share This Article +
+
+ + + +
+
+
+
+`; diff --git a/apps/civicsignalblog/src/components/ArticleHeader/ArticleHeader.test.js b/apps/civicsignalblog/src/components/ArticleHeader/ArticleHeader.test.js new file mode 100644 index 000000000..d1a2f5aeb --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticleHeader/ArticleHeader.test.js @@ -0,0 +1,16 @@ +import { createRender } from "@commons-ui/testing-library"; +import React from "react"; + +import ArticleHeader from "./ArticleHeader"; + +import theme from "@/civicsignalblog/theme"; + +// eslint-disable-next-line testing-library/render-result-naming-convention +const render = createRender({ theme }); + +describe("", () => { + it("renders unchanged", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/apps/civicsignalblog/src/components/ArticleHeader/index.js b/apps/civicsignalblog/src/components/ArticleHeader/index.js new file mode 100644 index 000000000..b014933ff --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticleHeader/index.js @@ -0,0 +1,3 @@ +import ArticleHeader from "./ArticleHeader"; + +export default ArticleHeader; diff --git a/apps/civicsignalblog/src/components/ArticlePage/ArticlePage.js b/apps/civicsignalblog/src/components/ArticlePage/ArticlePage.js new file mode 100644 index 000000000..532fe74e6 --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticlePage/ArticlePage.js @@ -0,0 +1,96 @@ +/* eslint-disable camelcase */ +import { Section } from "@commons-ui/core"; +import { Figure } from "@commons-ui/next"; +import { Box } from "@mui/material"; +import React from "react"; + +import ArticleHeader from "@/civicsignalblog/components/ArticleHeader"; +import Author from "@/civicsignalblog/components/Author"; +import CMSContent from "@/civicsignalblog/components/CMSContent"; +import SectionDivider from "@/civicsignalblog/components/SectionDivider"; +import equalsIgnoreCase from "@/civicsignalblog/utils/equalsIgnoreCase"; + +function ArticlePage({ + authors, + excerpt, + tags, + title, + coverImage: { src }, + content, + publishedOn, + primaryTag, +}) { + const filteredTags = tags.filter( + (tag) => !equalsIgnoreCase(tag.name, primaryTag), + ); + return ( + +
+ + + {content} + + +
+ {authors?.map((author) => ( + + ))} +
+ + ); +} + +export default ArticlePage; diff --git a/apps/civicsignalblog/src/components/ArticlePage/ArticlePage.snap.js b/apps/civicsignalblog/src/components/ArticlePage/ArticlePage.snap.js new file mode 100644 index 000000000..88b9c27cd --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticlePage/ArticlePage.snap.js @@ -0,0 +1,168 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders unchanged 1`] = ` +
+
+
+ Article +
+
+
+ Sept 1, 2021 +
+
+ Article +
+ +
+
+ Share This Article +
+
+ + + +
+
+
+
+
+
+
+
+
+
+ Article by +
+
+
+ Author 1 +
+
+ Author 1 bio +
+
+
+
+
+ Article by +
+
+
+ Author 2 +
+
+ Author 2 bio +
+
+
+
+
+
+`; diff --git a/apps/civicsignalblog/src/components/ArticlePage/ArticlePage.test.js b/apps/civicsignalblog/src/components/ArticlePage/ArticlePage.test.js new file mode 100644 index 000000000..bb4e931ab --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticlePage/ArticlePage.test.js @@ -0,0 +1,96 @@ +import { createRender } from "@commons-ui/testing-library"; +import React from "react"; + +import ArticlePage from "./ArticlePage"; + +import theme from "@/civicsignalblog/theme"; + +// eslint-disable-next-line testing-library/render-result-naming-convention +const render = createRender({ theme }); + +const defaultProps = { + authors: [ + { + name: "Author 1", + bio: "Author 1 bio", + }, + { + name: "Author 2", + bio: "Author 2 bio", + }, + ], + coverImage: { + src: "/images/stories-1.png", + alt: "Stories", + }, + title: "Article", + content: [ + { + children: [ + { + text: "Women make up only 22% of the people seen, heard or read about in the news in Africa, the results of the ", + children: null, + }, + { + type: "link", + newTab: false, + url: "https://whomakesthenews.org/gmmp-2020-final-reports/", + children: [ + { + text: "Global Media Monitoring Project", + children: null, + }, + ], + href: "https://whomakesthenews.org/gmmp-2020-final-reports/", + }, + { + text: " report launched on 14 July show. The performance of the continent’s news media has stagnated in comparison to the media in the rest of the world which has improved in the quarter century that the research has been running.", + children: null, + }, + ], + }, + { + children: [ + { + text: "“At the world level, women are only 1 in 4 of subjects and sources in the news. This proportion is marginally better in the digital news space but at the cumulative pace of change over time, it will take at least 67 more years to close the average global gender equality gap in news media content,” Sarah Macharia, Coordinator of WACC’s Global Media Monitoring Project (GMMP) said.", + children: null, + }, + ], + }, + { + children: [ + { + text: "However, in a more positive development, women in Africa tend to be featured more as subjects on science and health stories at 30%. Interestingly, this topic is also covered by more women reporters than average. The positive correlation supports the idea that having a more diverse newsroom leads to more diverse stories.", + children: null, + }, + ], + }, + { + children: [ + { + text: "Taking place every five years, the GMMP study is the largest and longest longitudinal study on gender in the world’s media. The 6th edition took place in 2020, in the midst of the pandemic and some adjustments had to be made on the schedule and data capture, including offline coding tools.", + children: null, + }, + ], + }, + ], + tags: [ + { + name: "tag1", + slug: "tag1", + }, + { + name: "tag2", + slug: "tag2", + }, + ], + primaryTag: "tag1", + publishedOn: "Sept 1, 2021", +}; + +describe("", () => { + it("renders unchanged", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/apps/civicsignalblog/src/components/ArticlePage/index.js b/apps/civicsignalblog/src/components/ArticlePage/index.js new file mode 100644 index 000000000..808a64577 --- /dev/null +++ b/apps/civicsignalblog/src/components/ArticlePage/index.js @@ -0,0 +1,3 @@ +import ArticlePage from "./ArticlePage"; + +export default ArticlePage; diff --git a/apps/civicsignalblog/src/components/Articles/Articles.js b/apps/civicsignalblog/src/components/Articles/Articles.js new file mode 100644 index 000000000..6ad6b0135 --- /dev/null +++ b/apps/civicsignalblog/src/components/Articles/Articles.js @@ -0,0 +1,114 @@ +/* eslint-env browser */ +import { useRouter } from "next/router"; +import React, { useEffect, useState } from "react"; + +import useArticles from "./useArticles"; + +import ArticleGrid from "@/civicsignalblog/components/ArticleGrid"; +import NextPreviousPagination from "@/civicsignalblog/components/NextPreviousPagination"; +import useFilterQuery, { + ALL_TAG, +} from "@/civicsignalblog/components/useFilterQuery"; +import equalsIgnoreCase from "@/civicsignalblog/utils/equalsIgnoreCase"; + +const Articles = React.forwardRef(function Articles(props, ref) { + const { + articles: articlesList, + featured: featuredArticle, + sx, + tags, + title, + labels: { search, readMore }, + pagination: { count: countProp, page: pageProp = 1 }, + primaryTag, + } = props; + const filteredTags = tags.filter( + (tag) => !equalsIgnoreCase(tag.name, primaryTag), + ); + const allTag = { + name: ALL_TAG, + slug: ALL_TAG, + }; + const [articles, setArticles] = useState(articlesList); + const [count, setCount] = useState(countProp); + const [page, setPage] = useState(pageProp); + const [q, setQ] = useState(); + const [filtering, setFiltering] = useState(false); + const [tag, setTag] = useState(allTag); + const queryParams = useFilterQuery({ page, q, tag }); + + const router = useRouter(); + + const handleChangePage = (_, value) => { + setPage(value); + }; + + const handleChangeQ = (_, value) => { + setQ(value || undefined); + }; + + const handleChangeTag = (_, value) => { + const newValue = + (value && tags.find((t) => equalsIgnoreCase(value, t.slug))) || allTag; + setTag(newValue); + setPage(1); + }; + + useEffect(() => { + const isFiltering = page !== 1 || q || !equalsIgnoreCase(tag.slug, ALL_TAG); + setFiltering(isFiltering); + }, [page, q, tag]); + + const { data } = useArticles( + { page, q, tag }, + { + primaryTag, + featured: !filtering && featuredArticle ? featuredArticle.slug : null, + }, + ); + useEffect(() => { + if (data) { + const { posts: results, pagination } = data; + setCount(pagination.count); + setArticles(results); + } + }, [data, filtering]); + + useEffect(() => { + const { pathname } = window.location; + const url = `${pathname}${queryParams}`; + router.push(url, undefined, { + scroll: true, + shallow: true, + }); + + // We don't want to listen to router changes here since we're the ones + // updating them + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [queryParams]); + + return ( +
+ + +
+ ); +}); + +export default Articles; diff --git a/apps/civicsignalblog/src/components/Articles/Articles.snap.js b/apps/civicsignalblog/src/components/Articles/Articles.snap.js new file mode 100644 index 000000000..985217f09 --- /dev/null +++ b/apps/civicsignalblog/src/components/Articles/Articles.snap.js @@ -0,0 +1,259 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders unchanged 1`] = ` + +`; diff --git a/apps/civicsignalblog/src/components/Articles/Articles.test.js b/apps/civicsignalblog/src/components/Articles/Articles.test.js new file mode 100644 index 000000000..65070a1b7 --- /dev/null +++ b/apps/civicsignalblog/src/components/Articles/Articles.test.js @@ -0,0 +1,58 @@ +import { createRender } from "@commons-ui/testing-library"; +import React from "react"; + +import Articles from "./Articles"; + +import theme from "@/civicsignalblog/theme"; + +// eslint-disable-next-line testing-library/render-result-naming-convention +const render = createRender({ theme }); + +const defaultProps = { + articles: [ + { + title: "Battle for gender equality in African media continues", + excerpt: + "Lorem ipsum dolor sit amet consectetur adipiscing elit mattis, vestibulum potenti rhoncus eget lacus fermentum taciti quam, quis curae accumsan viverra semper dapibus sed.", + publishedOn: "Jan 6, 2022", + image: { + src: "https://res.cloudinary.com/code-for-africa/image/upload/v1650885664/codeforafrica/unsplash_L6hr1BptcNc_of23p3.png", + alt: "Featured Article Image", + }, + readMoreLabel: "Read Story", + href: "/stories/article-1", + }, + ], + featured: { + title: "Battle for gender equality in African media continues", + excerpt: + "Lorem ipsum dolor sit amet consectetur adipiscing elit mattis, vestibulum potenti rhoncus eget lacus fermentum taciti quam, quis curae accumsan viverra semper dapibus sed.", + publishedOn: "Jan 6, 2022", + image: { + src: "https://res.cloudinary.com/code-for-africa/image/upload/v1650885664/codeforafrica/unsplash_L6hr1BptcNc_of23p3.png", + alt: "Featured Article Image", + }, + readMoreLabel: "Read Story", + href: "/stories/article-1", + }, + pagination: { + count: 10, + page: 1, + }, + labels: { + readMore: "Read More", + search: "Search", + }, + tags: [ + { name: "tag1", slug: "tag1" }, + { name: "tag2", slug: "tag2" }, + ], + title: "Title", +}; + +describe("", () => { + it("renders unchanged", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/apps/civicsignalblog/src/components/Articles/index.js b/apps/civicsignalblog/src/components/Articles/index.js new file mode 100644 index 000000000..2b63632e7 --- /dev/null +++ b/apps/civicsignalblog/src/components/Articles/index.js @@ -0,0 +1,3 @@ +import Articles from "./Articles"; + +export default Articles; diff --git a/apps/civicsignalblog/src/components/Articles/useArticles.js b/apps/civicsignalblog/src/components/Articles/useArticles.js new file mode 100644 index 000000000..2d40f9471 --- /dev/null +++ b/apps/civicsignalblog/src/components/Articles/useArticles.js @@ -0,0 +1,23 @@ +import useSWR from "swr"; + +import useFilterQuery from "@/civicsignalblog/components/useFilterQuery"; + +const fetcher = (url) => fetch(url).then((res) => res.json()); + +function useArticles(params, { primaryTag, featured }) { + const queryParams = useFilterQuery(params); + const separator = queryParams ? "" : "?"; + const filterQuery = queryParams ? `${queryParams}&` : ""; + const primaryTagQuery = `primaryTag=${primaryTag}`; + const featuredQuery = featured ? `&featured=${featured}` : ""; + const query = `${separator}${filterQuery}${primaryTagQuery}${featuredQuery}`; + const { data, error } = useSWR(`/api/v1/posts${query}`, fetcher); + + return { + data, + isLoading: !error && !data, + error, + }; +} + +export default useArticles; diff --git a/apps/civicsignalblog/src/components/Author/Author.js b/apps/civicsignalblog/src/components/Author/Author.js new file mode 100644 index 000000000..6592399f1 --- /dev/null +++ b/apps/civicsignalblog/src/components/Author/Author.js @@ -0,0 +1,46 @@ +import { RichTypography } from "@commons-ui/next"; +import { Stack } from "@mui/material"; +import PropTypes from "prop-types"; +import React from "react"; + +const Author = React.forwardRef(function Author(props, ref) { + const { bio, name, sx } = props; + + return ( + + Article by + + {name} + {bio} + + + ); +}); + +Author.propTypes = { + name: PropTypes.string, + bio: PropTypes.string, +}; + +Author.defaultProps = { + name: undefined, + bio: undefined, +}; + +export default Author; diff --git a/apps/civicsignalblog/src/components/Author/Author.snap.js b/apps/civicsignalblog/src/components/Author/Author.snap.js new file mode 100644 index 000000000..5dc4806e6 --- /dev/null +++ b/apps/civicsignalblog/src/components/Author/Author.snap.js @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders unchanged 1`] = ` +
+
+
+ Article by +
+
+
+
+`; diff --git a/apps/civicsignalblog/src/components/Author/Author.test.js b/apps/civicsignalblog/src/components/Author/Author.test.js new file mode 100644 index 000000000..819ecd57c --- /dev/null +++ b/apps/civicsignalblog/src/components/Author/Author.test.js @@ -0,0 +1,16 @@ +import { createRender } from "@commons-ui/testing-library"; +import React from "react"; + +import Author from "./Author"; + +import theme from "@/civicsignalblog/theme"; + +// eslint-disable-next-line testing-library/render-result-naming-convention +const render = createRender({ theme }); + +describe("", () => { + it("renders unchanged", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/apps/civicsignalblog/src/components/Author/index.js b/apps/civicsignalblog/src/components/Author/index.js new file mode 100644 index 000000000..f394d2d50 --- /dev/null +++ b/apps/civicsignalblog/src/components/Author/index.js @@ -0,0 +1,3 @@ +import Author from "./Author"; + +export default Author; diff --git a/apps/civicsignalblog/src/components/Breadcrumbs/Breadcrumbs.js b/apps/civicsignalblog/src/components/Breadcrumbs/Breadcrumbs.js new file mode 100644 index 000000000..d5f9f1ac3 --- /dev/null +++ b/apps/civicsignalblog/src/components/Breadcrumbs/Breadcrumbs.js @@ -0,0 +1,44 @@ +import { Link, RichTypography } from "@commons-ui/next"; +import { Breadcrumbs as MuiBreadcrumbs } from "@mui/material"; +import { styled } from "@mui/material/styles"; +import React from "react"; + +const BreadcrumbsRoot = styled(MuiBreadcrumbs, { + slot: "Root", +})(({ theme }) => ({ + color: theme.palette.text.primary, +})); + +const Breadcrumbs = React.forwardRef(function Breadcrumbs(props, ref) { + const { crumbs, ...other } = props; + + if (!crumbs?.length) { + return null; + } + return ( + + {crumbs.map(({ href, label }) => { + if (href) { + return ( + + {label} + + ); + } + return ( + + {label} + + ); + })} + + ); +}); + +export default Breadcrumbs; diff --git a/apps/civicsignalblog/src/components/Breadcrumbs/Breadcrumbs.snap.js b/apps/civicsignalblog/src/components/Breadcrumbs/Breadcrumbs.snap.js new file mode 100644 index 000000000..02dc684b0 --- /dev/null +++ b/apps/civicsignalblog/src/components/Breadcrumbs/Breadcrumbs.snap.js @@ -0,0 +1,39 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders unchanged 1`] = ` +
+ +
+`; diff --git a/apps/civicsignalblog/src/components/Breadcrumbs/Breadcrumbs.test.js b/apps/civicsignalblog/src/components/Breadcrumbs/Breadcrumbs.test.js new file mode 100644 index 000000000..fcc0330f8 --- /dev/null +++ b/apps/civicsignalblog/src/components/Breadcrumbs/Breadcrumbs.test.js @@ -0,0 +1,20 @@ +import { createRender } from "@commons-ui/testing-library"; +import React from "react"; + +import Breadcrumbs from "./Breadcrumbs"; + +import theme from "@/civicsignalblog/theme"; + +// eslint-disable-next-line testing-library/render-result-naming-convention +const render = createRender({ theme }); + +const defaultProps = { + crumbs: [{ label: "Our Work", href: "/projects" }, { label: "Initiatives" }], +}; + +describe("", () => { + it("renders unchanged", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/apps/civicsignalblog/src/components/Breadcrumbs/index.js b/apps/civicsignalblog/src/components/Breadcrumbs/index.js new file mode 100644 index 000000000..a8f3eef13 --- /dev/null +++ b/apps/civicsignalblog/src/components/Breadcrumbs/index.js @@ -0,0 +1,3 @@ +import Breadcrumbs from "./Breadcrumbs"; + +export default Breadcrumbs; diff --git a/apps/civicsignalblog/src/components/CMSContent/CMSContent.js b/apps/civicsignalblog/src/components/CMSContent/CMSContent.js new file mode 100644 index 000000000..ed4778eaf --- /dev/null +++ b/apps/civicsignalblog/src/components/CMSContent/CMSContent.js @@ -0,0 +1,41 @@ +/* eslint-env browser */ +import { Section } from "@commons-ui/core"; +import PropTypes from "prop-types"; +import React from "react"; + +import LongFormExternalEmbed from "@/civicsignalblog/components/LongFormExternalEmbed"; +import LongFormMedia from "@/civicsignalblog/components/LongFormMedia"; +import LongFormRichText from "@/civicsignalblog/components/LongFormRichText"; + +const CMSContent = React.forwardRef(function CMSContent({ children, sx }, ref) { + const COMPONENT_BY_CONTENT_TYPE = { + richText: LongFormRichText, + mediaBlock: LongFormMedia, + "external-embed": LongFormExternalEmbed, + }; + return ( +
+ {children.map((c) => { + const Component = COMPONENT_BY_CONTENT_TYPE[c.blockType]; + if (Component) { + return ; + } + return null; + })} +
+ ); +}); + +CMSContent.propTypes = { + sx: PropTypes.shape({}), +}; + +CMSContent.defaultProps = { + sx: undefined, +}; + +export default CMSContent; diff --git a/apps/civicsignalblog/src/components/CMSContent/CMSContent.snap.js b/apps/civicsignalblog/src/components/CMSContent/CMSContent.snap.js new file mode 100644 index 000000000..7c7132a05 --- /dev/null +++ b/apps/civicsignalblog/src/components/CMSContent/CMSContent.snap.js @@ -0,0 +1,84 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders unchanged 1`] = ` +
+
+
+

+ Women make up only 22% of the people seen, heard or read about in the news in Africa, the results of the  + + Global Media Monitoring Project + +  report launched on 14 July show. The performance of the continent’s news media has stagnated in comparison to the media in the rest of the world which has improved in the quarter century that the research has been running. +

+

+ “At the world level, women are only 1 in 4 of subjects and sources in the news. This proportion is marginally better in the digital news space but at the cumulative pace of change over time, it will take at least 67 more years to close the average global gender equality gap in news media content,” Sarah Macharia, Coordinator of WACC’s Global Media Monitoring Project (GMMP) said. +

+

+ However, in a more positive development, women in Africa tend to be featured more as subjects on science and health stories at 30%. Interestingly, this topic is also covered by more women reporters than average. The positive correlation supports the idea that having a more diverse newsroom leads to more diverse stories. +

+

+ Taking place every five years, the GMMP study is the largest and longest longitudinal study on gender in the world’s media. The 6th edition took place in 2020, in the midst of the pandemic and some adjustments had to be made on the schedule and data capture, including offline coding tools. +

+
+
+
+ alt +
+ alt +
+
+
+
+
+ ', + }, + caption: "caption", + url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ", + }, + ], +}; + +describe("", () => { + it("renders unchanged", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/apps/civicsignalblog/src/components/CMSContent/index.js b/apps/civicsignalblog/src/components/CMSContent/index.js new file mode 100644 index 000000000..a844b893c --- /dev/null +++ b/apps/civicsignalblog/src/components/CMSContent/index.js @@ -0,0 +1,3 @@ +import CMSContent from "./CMSContent"; + +export default CMSContent; diff --git a/apps/civicsignalblog/src/components/ChoiceChip/ChoiceChip.js b/apps/civicsignalblog/src/components/ChoiceChip/ChoiceChip.js new file mode 100644 index 000000000..09a16d08a --- /dev/null +++ b/apps/civicsignalblog/src/components/ChoiceChip/ChoiceChip.js @@ -0,0 +1,50 @@ +import { Chip } from "@mui/material"; +import { styled } from "@mui/material/styles"; +import React from "react"; + +const ChoiceChipRoot = styled(Chip, { + slot: "Root", + // fullWidth comes when ChoiceChip is used inside ToggleButtonGroup + shouldForwardProp: (prop) => prop !== "fullWidth", +})(({ theme }) => ({ + ...theme.typography.body1SemiBold, +})); + +const ChoiceChip = React.forwardRef(function ChoiceChip(props, ref) { + const { + color: colorProp = "default", + onChange, + onClick, + selected = false, + value, + variant: variantProp = "filled", + ...other + } = props; + const color = selected ? "primary" : colorProp; + const variant = selected ? "filled" : variantProp; + const handleChange = (event) => { + if (onClick) { + onClick(event, value); + if (event.defaultPrevented) { + return; + } + } + + if (onChange) { + onChange(event, value); + } + }; + + return ( + + ); +}); + +export default ChoiceChip; diff --git a/apps/civicsignalblog/src/components/ChoiceChip/ChoiceChip.snap.js b/apps/civicsignalblog/src/components/ChoiceChip/ChoiceChip.snap.js new file mode 100644 index 000000000..7437d3b86 --- /dev/null +++ b/apps/civicsignalblog/src/components/ChoiceChip/ChoiceChip.snap.js @@ -0,0 +1,15 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders unchanged 1`] = ` +
+
+ +
+
+`; diff --git a/apps/civicsignalblog/src/components/ChoiceChip/ChoiceChip.test.js b/apps/civicsignalblog/src/components/ChoiceChip/ChoiceChip.test.js new file mode 100644 index 000000000..f70d82a19 --- /dev/null +++ b/apps/civicsignalblog/src/components/ChoiceChip/ChoiceChip.test.js @@ -0,0 +1,16 @@ +import { createRender } from "@commons-ui/testing-library"; +import React from "react"; + +import ChoiceChip from "./ChoiceChip"; + +import theme from "@/civicsignalblog/theme"; + +// eslint-disable-next-line testing-library/render-result-naming-convention +const render = createRender({ theme }); + +describe("", () => { + it("renders unchanged", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/apps/civicsignalblog/src/components/ChoiceChip/index.js b/apps/civicsignalblog/src/components/ChoiceChip/index.js new file mode 100644 index 000000000..a29aea241 --- /dev/null +++ b/apps/civicsignalblog/src/components/ChoiceChip/index.js @@ -0,0 +1,3 @@ +import ChoiceChip from "./ChoiceChip"; + +export default ChoiceChip; diff --git a/apps/civicsignalblog/src/components/ChoiceChipGroup/ChoiceChipGroup.js b/apps/civicsignalblog/src/components/ChoiceChipGroup/ChoiceChipGroup.js new file mode 100644 index 000000000..8492a8980 --- /dev/null +++ b/apps/civicsignalblog/src/components/ChoiceChipGroup/ChoiceChipGroup.js @@ -0,0 +1,40 @@ +import { ToggleButtonGroup } from "@mui/material"; +import { styled } from "@mui/material/styles"; +import React from "react"; + +const ChoiceChipGroupRoot = styled(ToggleButtonGroup, { + slot: "Root", +})(({ theme }) => ({ + // We're styling ToggleButtonGroup here instead of in the theme because + // this isn't the primary use of ToggleButtonGroup and hence we don't want + // to globally change its styling. + borderRadius: 3, + display: "flex", + flexWrap: "wrap", + gap: 10, + "& .MuiToggleButtonGroup-grouped:not(:first-of-type)": { + borderBottomLeftRadius: 3, + borderTopLeftRadius: 3, + }, + "& .MuiToggleButtonGroup-grouped:not(:last-of-type)": { + borderBottomRightRadius: 3, + borderTopRightRadius: 3, + }, + "& .MuiChip-filled.MuiToggleButtonGroup-grouped": { + "&:not(:first-of-type)": { + margin: 0, + border: `1px solid ${theme.palette.background.main}`, + }, + }, + "& .MuiChip-filledPrimary.MuiToggleButtonGroup-grouped": { + "&:not(:first-of-type)": { + border: `1px solid ${theme.palette.primary.main}`, + }, + }, +})); + +const ChoiceChipGroup = React.forwardRef(function ChoiceChipGroup(props, ref) { + return ; +}); + +export default ChoiceChipGroup; diff --git a/apps/civicsignalblog/src/components/ChoiceChipGroup/ChoiceChipGroup.snap.js b/apps/civicsignalblog/src/components/ChoiceChipGroup/ChoiceChipGroup.snap.js new file mode 100644 index 000000000..ce7f4f7df --- /dev/null +++ b/apps/civicsignalblog/src/components/ChoiceChipGroup/ChoiceChipGroup.snap.js @@ -0,0 +1,10 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders unchanged 1`] = ` +
+
+
+`; diff --git a/apps/civicsignalblog/src/components/ChoiceChipGroup/ChoiceChipGroup.test.js b/apps/civicsignalblog/src/components/ChoiceChipGroup/ChoiceChipGroup.test.js new file mode 100644 index 000000000..d61adb39e --- /dev/null +++ b/apps/civicsignalblog/src/components/ChoiceChipGroup/ChoiceChipGroup.test.js @@ -0,0 +1,16 @@ +import { createRender } from "@commons-ui/testing-library"; +import React from "react"; + +import ChoiceChipGroup from "./ChoiceChipGroup"; + +import theme from "@/civicsignalblog/theme"; + +// eslint-disable-next-line testing-library/render-result-naming-convention +const render = createRender({ theme }); + +describe("", () => { + it("renders unchanged", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/apps/civicsignalblog/src/components/ChoiceChipGroup/index.js b/apps/civicsignalblog/src/components/ChoiceChipGroup/index.js new file mode 100644 index 000000000..50c25a939 --- /dev/null +++ b/apps/civicsignalblog/src/components/ChoiceChipGroup/index.js @@ -0,0 +1,3 @@ +import ChoiceChipGroup from "./ChoiceChipGroup"; + +export default ChoiceChipGroup; diff --git a/apps/civicsignalblog/src/components/ConnectBar/ConnectBar.js b/apps/civicsignalblog/src/components/ConnectBar/ConnectBar.js new file mode 100644 index 000000000..d1aad092d --- /dev/null +++ b/apps/civicsignalblog/src/components/ConnectBar/ConnectBar.js @@ -0,0 +1,34 @@ +import React from "react"; + +import SocialMediaBar from "@/civicsignalblog/components/SocialMediaBar"; +import SocialMediaButton from "@/civicsignalblog/components/SocialMediaButton"; + +const ConnectBar = React.forwardRef(function ConnectBar(props, ref) { + const { sx, title, links } = props; + + if (!links?.length) { + return null; + } + + const socialConnections = links.map(({ platform, url }) => ({ + name: platform?.toLowerCase(), + url, + })); + + return ( + + {socialConnections.map((connection) => ( + + ))} + + ); +}); + +export default ConnectBar; diff --git a/apps/civicsignalblog/src/components/ConnectBar/ConnectBar.snap.js b/apps/civicsignalblog/src/components/ConnectBar/ConnectBar.snap.js new file mode 100644 index 000000000..46da7d44d --- /dev/null +++ b/apps/civicsignalblog/src/components/ConnectBar/ConnectBar.snap.js @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders unchanged 1`] = `
`; diff --git a/apps/civicsignalblog/src/components/ConnectBar/ConnectBar.test.js b/apps/civicsignalblog/src/components/ConnectBar/ConnectBar.test.js new file mode 100644 index 000000000..44b63bba1 --- /dev/null +++ b/apps/civicsignalblog/src/components/ConnectBar/ConnectBar.test.js @@ -0,0 +1,22 @@ +import { createRender } from "@commons-ui/testing-library"; +import React from "react"; + +import ConnectBar from "./ConnectBar"; + +import theme from "@/civicsignalblog/theme"; + +// eslint-disable-next-line testing-library/render-result-naming-convention +const render = createRender({ theme }); + +const defaultProps = { + links: { + facebook: "https://www.facebook.com/", + }, +}; + +describe("", () => { + it("renders unchanged", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/apps/civicsignalblog/src/components/ConnectBar/index.js b/apps/civicsignalblog/src/components/ConnectBar/index.js new file mode 100644 index 000000000..2f05ed810 --- /dev/null +++ b/apps/civicsignalblog/src/components/ConnectBar/index.js @@ -0,0 +1,3 @@ +import ConnectBar from "./ConnectBar"; + +export default ConnectBar; diff --git a/apps/civicsignalblog/src/components/CustomPageHeader/CustomPageHeader.js b/apps/civicsignalblog/src/components/CustomPageHeader/CustomPageHeader.js new file mode 100644 index 000000000..17728dbe7 --- /dev/null +++ b/apps/civicsignalblog/src/components/CustomPageHeader/CustomPageHeader.js @@ -0,0 +1,88 @@ +import { Section } from "@commons-ui/core"; +import { RichTypography } from "@commons-ui/next"; +import { Box } from "@mui/material"; +import React from "react"; + +import Breadcrumbs from "@/civicsignalblog/components/Breadcrumbs"; + +const CustomPageHeader = React.forwardRef( + function CustomPageHeader(props, ref) { + const { crumbs, image: imageProp, title, subtitle } = props; + + if (!(title || subtitle)) { + return null; + } + const crumbsLineHeight = crumbs?.length ? 23 : 0; + const image = imageProp?.url || imageProp; + return ( + +
+ + + {title} + + + {subtitle} + +
+
+ ); + }, +); + +export default CustomPageHeader; diff --git a/apps/civicsignalblog/src/components/CustomPageHeader/CustomPageHeader.snap.js b/apps/civicsignalblog/src/components/CustomPageHeader/CustomPageHeader.snap.js new file mode 100644 index 000000000..ce3ed3bb0 --- /dev/null +++ b/apps/civicsignalblog/src/components/CustomPageHeader/CustomPageHeader.snap.js @@ -0,0 +1,24 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders unchanged 1`] = ` +
+
+
+

+ About Us +

+

+ We are Africa’s largest network of civic technology and data journalism labs +

+
+
+
+`; diff --git a/apps/civicsignalblog/src/components/CustomPageHeader/CustomPageHeader.test.js b/apps/civicsignalblog/src/components/CustomPageHeader/CustomPageHeader.test.js new file mode 100644 index 000000000..7564e35d9 --- /dev/null +++ b/apps/civicsignalblog/src/components/CustomPageHeader/CustomPageHeader.test.js @@ -0,0 +1,25 @@ +import { createRender } from "@commons-ui/testing-library"; +import React from "react"; + +import CustomPageHeader from "./CustomPageHeader"; + +import theme from "@/civicsignalblog/theme"; + +// eslint-disable-next-line testing-library/render-result-naming-convention +const render = createRender({ theme }); + +const defaultProps = { + title: "About Us", + subtitle: + "We are Africa’s largest network of civic technology and data journalism labs", + image: { + src: "https://res.cloudinary.com/code-for-africa/image/upload/v1656064173/codeforafrica/images/1_IgrT4_1tGZh1WnpYzvZN1A_1_twneqf.jpg", + }, +}; + +describe("", () => { + it("renders unchanged", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/apps/civicsignalblog/src/components/CustomPageHeader/index.js b/apps/civicsignalblog/src/components/CustomPageHeader/index.js new file mode 100644 index 000000000..f78cc1fd9 --- /dev/null +++ b/apps/civicsignalblog/src/components/CustomPageHeader/index.js @@ -0,0 +1,3 @@ +import CustomPageHeader from "./CustomPageHeader"; + +export default CustomPageHeader; diff --git a/apps/civicsignalblog/src/components/DesktopNavBar/DesktopNavBar.js b/apps/civicsignalblog/src/components/DesktopNavBar/DesktopNavBar.js new file mode 100644 index 000000000..edc4a37dd --- /dev/null +++ b/apps/civicsignalblog/src/components/DesktopNavBar/DesktopNavBar.js @@ -0,0 +1,57 @@ +import { Grid, Box } from "@mui/material"; +import PropTypes from "prop-types"; +import React from "react"; + +import NavBarNavList from "@/civicsignalblog/components/NavBarNavList"; +import NextImageButton from "@/civicsignalblog/components/NextImageButton"; + +const DesktopNavBar = React.forwardRef(function DesktopNavBar(props, ref) { + const { logo, menus, socialLinks, sx } = props; + + return ( + + + + + + + + + + + ); +}); + +DesktopNavBar.propTypes = { + direction: PropTypes.string, + menu: PropTypes.arrayOf( + PropTypes.shape({ + label: PropTypes.string, + href: PropTypes.string, + }), + ), +}; + +DesktopNavBar.defaultProps = { + direction: undefined, + menu: undefined, +}; + +export default DesktopNavBar; diff --git a/apps/civicsignalblog/src/components/DesktopNavBar/DesktopNavBar.snap.js b/apps/civicsignalblog/src/components/DesktopNavBar/DesktopNavBar.snap.js new file mode 100644 index 000000000..3f3646c7a --- /dev/null +++ b/apps/civicsignalblog/src/components/DesktopNavBar/DesktopNavBar.snap.js @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders unchanged 1`] = ` +
+
+
+
+
+
+
+`; diff --git a/apps/civicsignalblog/src/components/DesktopNavBar/DesktopNavBar.test.js b/apps/civicsignalblog/src/components/DesktopNavBar/DesktopNavBar.test.js new file mode 100644 index 000000000..43ceb7768 --- /dev/null +++ b/apps/civicsignalblog/src/components/DesktopNavBar/DesktopNavBar.test.js @@ -0,0 +1,16 @@ +import { createRender } from "@commons-ui/testing-library"; +import React from "react"; + +import DesktopNavigation from "."; + +import theme from "@/civicsignalblog/theme"; + +// eslint-disable-next-line testing-library/render-result-naming-convention +const render = createRender({ theme }); + +describe("", () => { + it("renders unchanged", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/apps/civicsignalblog/src/components/DesktopNavBar/index.js b/apps/civicsignalblog/src/components/DesktopNavBar/index.js new file mode 100644 index 000000000..3919164ee --- /dev/null +++ b/apps/civicsignalblog/src/components/DesktopNavBar/index.js @@ -0,0 +1,3 @@ +import DesktopNavBar from "./DesktopNavBar"; + +export default DesktopNavBar; diff --git a/apps/civicsignalblog/src/components/FeaturedArticle/FeaturedArticle.js b/apps/civicsignalblog/src/components/FeaturedArticle/FeaturedArticle.js new file mode 100644 index 000000000..73129b6cc --- /dev/null +++ b/apps/civicsignalblog/src/components/FeaturedArticle/FeaturedArticle.js @@ -0,0 +1,168 @@ +/* eslint-disable camelcase */ +import { Link } from "@commons-ui/next"; +import { Button, CardActionArea, Grid, Typography } from "@mui/material"; +import React from "react"; + +import ArticleCard from "@/civicsignalblog/components/ArticleCard"; +import ArticleCardContent from "@/civicsignalblog/components/ArticleCardContent"; +import ArticleCardMedia from "@/civicsignalblog/components/ArticleCardMedia"; + +const FeaturedArticle = React.forwardRef(function FeaturedArticle(props, ref) { + const { + excerpt, + image, + href, + publishedOn, + sx, + title, + variant = "standard", + readMoreLabel = "Read Story", + } = props; + + return ( + + + + + + + + + + {title} + + + {excerpt} + + + + {publishedOn} + + + + + + + + {title} + + + + + + + + + ); +}); + +export default FeaturedArticle; diff --git a/apps/civicsignalblog/src/components/FeaturedArticle/FeaturedArticle.snap.js b/apps/civicsignalblog/src/components/FeaturedArticle/FeaturedArticle.snap.js new file mode 100644 index 000000000..0e811eb98 --- /dev/null +++ b/apps/civicsignalblog/src/components/FeaturedArticle/FeaturedArticle.snap.js @@ -0,0 +1,86 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders unchanged 1`] = ` + +`; diff --git a/apps/civicsignalblog/src/components/FeaturedArticle/FeaturedArticle.test.js b/apps/civicsignalblog/src/components/FeaturedArticle/FeaturedArticle.test.js new file mode 100644 index 000000000..d92e16498 --- /dev/null +++ b/apps/civicsignalblog/src/components/FeaturedArticle/FeaturedArticle.test.js @@ -0,0 +1,29 @@ +import { createRender } from "@commons-ui/testing-library"; +import React from "react"; + +import FeaturedArticle from "./FeaturedArticle"; + +import theme from "@/civicsignalblog/theme"; + +// eslint-disable-next-line testing-library/render-result-naming-convention +const render = createRender({ theme }); + +const defaultProps = { + title: "Battle for gender equality in African media continues", + excerpt: + "Lorem ipsum dolor sit amet consectetur adipiscing elit mattis, vestibulum potenti rhoncus eget lacus fermentum taciti quam, quis curae accumsan viverra semper dapibus sed.", + publishedOn: "Jan 6, 2022", + image: { + src: "https://res.cloudinary.com/code-for-africa/image/upload/v1650885664/codeforafrica/unsplash_L6hr1BptcNc_of23p3.png", + alt: "Featured Article Image", + }, + readMoreLabel: "Read Story", + href: "/stories/article-1", +}; + +describe("", () => { + it("renders unchanged", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/apps/civicsignalblog/src/components/FeaturedArticle/index.js b/apps/civicsignalblog/src/components/FeaturedArticle/index.js new file mode 100644 index 000000000..16261624b --- /dev/null +++ b/apps/civicsignalblog/src/components/FeaturedArticle/index.js @@ -0,0 +1,3 @@ +import FeaturedArticle from "./FeaturedArticle"; + +export default FeaturedArticle; diff --git a/apps/civicsignalblog/src/components/FilterBar/FilterBar.js b/apps/civicsignalblog/src/components/FilterBar/FilterBar.js new file mode 100644 index 000000000..2dfc60f26 --- /dev/null +++ b/apps/civicsignalblog/src/components/FilterBar/FilterBar.js @@ -0,0 +1,114 @@ +import { Stack } from "@mui/material"; +import { useRouter } from "next/router"; +import React, { useEffect, useState } from "react"; + +import ChoiceChip from "@/civicsignalblog/components/ChoiceChip"; +import ChoiceChipGroup from "@/civicsignalblog/components/ChoiceChipGroup"; +import SearchInput from "@/civicsignalblog/components/SearchInput"; + +const FilterBar = React.forwardRef(function FilterBar(props, ref) { + const { + ChoiceChipGroupProps, + ChoiceChipProps, + SearchInputProps, + onChangeQ, + onChangeTag, + q, + tag, + tags, + ...other + } = props; + const [search, setSearch] = useState(); + const router = useRouter(); + + useEffect(() => { + if (router.isReady) { + const { tag: initialTag, q: initialQ } = router.query; + if (initialTag && onChangeTag) { + onChangeTag(undefined, initialTag); + } + if (initialQ && onChangeQ) { + onChangeQ(undefined, initialQ); + } + } + // We're only interested in initial isReady and not any subsequent + // router.query changes e.g. due to search + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [router.isReady]); + + const handleChangeChoice = (e, value) => { + if (onChangeTag) { + onChangeTag(e, value); + } + }; + + const handleChangeQ = (e, value) => { + if (onChangeQ) { + onChangeQ(e, value); + } + }; + + const handleChangeSearch = (e) => { + setSearch(e.target.value); + }; + + const handleClickSearch = (e) => { + handleChangeQ(e, search); + }; + + const handleKeyPressSearch = (e) => { + if (e.key === "Enter") { + handleChangeQ(e, search); + } + }; + + return ( + + + {tags?.length > 0 ? ( + + {tags.map((t) => ( + + ))} + + ) : null} + + ); +}); + +export default FilterBar; diff --git a/apps/civicsignalblog/src/components/FilterBar/FilterBar.snap.js b/apps/civicsignalblog/src/components/FilterBar/FilterBar.snap.js new file mode 100644 index 000000000..4154bd4f9 --- /dev/null +++ b/apps/civicsignalblog/src/components/FilterBar/FilterBar.snap.js @@ -0,0 +1,69 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders unchanged 1`] = ` +
+
+
+
+ + + +
+
+
+
+ + tag + +
+
+
+
+`; diff --git a/apps/civicsignalblog/src/components/FilterBar/FilterBar.test.js b/apps/civicsignalblog/src/components/FilterBar/FilterBar.test.js new file mode 100644 index 000000000..4c76ef1ed --- /dev/null +++ b/apps/civicsignalblog/src/components/FilterBar/FilterBar.test.js @@ -0,0 +1,29 @@ +import { createRender } from "@commons-ui/testing-library"; +import React from "react"; + +import FilterBar from "./FilterBar"; + +import theme from "@/civicsignalblog/theme"; + +// eslint-disable-next-line testing-library/render-result-naming-convention +const render = createRender({ theme }); + +const defaultProps = { + tags: [ + { + name: "tag", + slug: "tag", + }, + ], + tag: { + name: "tag", + slug: "tag", + }, +}; + +describe("", () => { + it("renders unchanged", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/apps/civicsignalblog/src/components/FilterBar/index.js b/apps/civicsignalblog/src/components/FilterBar/index.js new file mode 100644 index 000000000..7779fcb2b --- /dev/null +++ b/apps/civicsignalblog/src/components/FilterBar/index.js @@ -0,0 +1,3 @@ +import FilterBar from "./FilterBar"; + +export default FilterBar; diff --git a/apps/civicsignalblog/src/components/Footer/Footer.js b/apps/civicsignalblog/src/components/Footer/Footer.js new file mode 100644 index 000000000..7dc766fc8 --- /dev/null +++ b/apps/civicsignalblog/src/components/Footer/Footer.js @@ -0,0 +1,106 @@ +import { Section } from "@commons-ui/core"; +import { Box, Grid } from "@mui/material"; +import { styled } from "@mui/material/styles"; +import PropTypes from "prop-types"; +import React from "react"; + +import FooterDescription from "./FooterDescription"; + +import FooterLinks from "@/civicsignalblog/components/FooterLinks"; +import NewsletterSubscription from "@/civicsignalblog/components/NewsletterSubscription"; +import StayInTouch from "@/civicsignalblog/components/StayInTouch"; + +const FooterRoot = styled(Box)( + ({ theme: { breakpoints, palette, typography } }) => ({ + backgroundColor: palette.primary.dark, + color: palette.text.secondary, + padding: `${typography.pxToRem(80)} 0`, + [breakpoints.up("md")]: { + padding: `${typography.pxToRem(110)} 0`, + }, + [breakpoints.up("lg")]: { + padding: `${typography.pxToRem(100)} 0`, + }, + }), +); + +const Footer = React.forwardRef(function Footer(props, ref) { + const { + connect, + description, + logo, + newsletter, + primaryMenus, + secondaryMenus, + } = props; + + return ( + +
+ {/* Increase number of columns to getter columns size */} + + + + + + + + + + + + + + + + + + + + +
+
+ ); +}); + +Footer.propTypes = { + newsletter: PropTypes.shape({}), + menus: PropTypes.arrayOf( + PropTypes.shape({ + label: PropTypes.string, + href: PropTypes.string, + }), + ), +}; + +Footer.defaultProps = { + newsletter: undefined, + menus: undefined, +}; + +export default Footer; diff --git a/apps/civicsignalblog/src/components/Footer/Footer.snap.js b/apps/civicsignalblog/src/components/Footer/Footer.snap.js new file mode 100644 index 000000000..d2e2211b9 --- /dev/null +++ b/apps/civicsignalblog/src/components/Footer/Footer.snap.js @@ -0,0 +1,42 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`