renders unchanged 1`] = `
John Doe
+ Developer
+
+
+ Charter Africa
+
+
San Francisco, CA
Full-stack web developer with over 5 years of experience.
renders unchanged 1`] = `
/>
+
+
+
+ Organisations
+
+
+
+
+
diff --git a/apps/charterafrica/src/components/Entity/Entity.test.js b/apps/charterafrica/src/components/Entity/Entity.test.js
index 54590acc6..be8c92c5b 100644
--- a/apps/charterafrica/src/components/Entity/Entity.test.js
+++ b/apps/charterafrica/src/components/Entity/Entity.test.js
@@ -12,8 +12,18 @@ const defaultProps = {
name: "John Doe",
location: "San Francisco, CA",
description: "Full-stack web developer with over 5 years of experience.",
- twitter: "https://twitter.com/johndoe",
- github: "https://github.com/johndoe",
+ socialMedia: [
+ {
+ name: "twitter",
+ link: "https://twitter.com/johndoe",
+ id: 1,
+ },
+ {
+ name: "github",
+ link: "https://github.com/johndoe",
+ id: 2,
+ },
+ ],
email: "johndoe@example.com",
image: "/static/images/avatar/1.jpg",
tools: [
@@ -36,6 +46,28 @@ const defaultProps = {
},
],
toolsTitle: "Favorite Tools",
+ role: "Developer",
+ currentOrganisation: "Charter Africa",
+ repositories: Array.from({ length: 3 }, (_, i) => ({
+ id: 1,
+ name: `Repository ${i}`,
+ stargazers: 100,
+ visibility: "PUBLIC",
+ description: "Charter Africa website",
+ url: "https://charter.africa",
+ updatedAt: "2021-10-01T00:00:00Z",
+ techSkills: "React, Next.js, TypeScript",
+ })),
+ repositoriesTitle: "Repositories",
+ organisations: Array.from({ length: 3 }, (_, i) => ({
+ id: 1,
+ name: `Organisation ${i}`,
+ avatarUrl: "/static/images/charterafrica.png",
+ link: {
+ href: "https://charter.africa",
+ },
+ })),
+ organisationsTitle: "Organisations",
};
describe("
", () => {
diff --git a/apps/charterafrica/src/components/OrganisationCard/OrganisationImageLink.js b/apps/charterafrica/src/components/OrganisationCard/OrganisationImageLink.js
new file mode 100644
index 000000000..13b8e0451
--- /dev/null
+++ b/apps/charterafrica/src/components/OrganisationCard/OrganisationImageLink.js
@@ -0,0 +1,58 @@
+import { RichTypography } from "@commons-ui/core";
+import { Link, Figure } from "@commons-ui/next";
+import { Button, Stack } from "@mui/material";
+import PropTypes from "prop-types";
+import React from "react";
+
+const OrganisationImageLink = React.forwardRef(
+ function OrganisationImageLink(props, ref) {
+ const { avatarUrl: image, link, name } = props;
+
+ return (
+
+ );
+ },
+);
+
+OrganisationImageLink.propTypes = {
+ name: PropTypes.string,
+ avatarUrl: PropTypes.string,
+};
+
+OrganisationImageLink.defaultProps = {
+ name: undefined,
+ avatarUrl: undefined,
+};
+
+export default OrganisationImageLink;
diff --git a/apps/charterafrica/src/components/OrganisationCard/OrganisationImageLink.snap.js b/apps/charterafrica/src/components/OrganisationCard/OrganisationImageLink.snap.js
new file mode 100644
index 000000000..9faad6639
--- /dev/null
+++ b/apps/charterafrica/src/components/OrganisationCard/OrganisationImageLink.snap.js
@@ -0,0 +1,38 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`
renders unchanged 1`] = `
+
+`;
diff --git a/apps/charterafrica/src/components/OrganisationCard/OrganisationImageLink.test.js b/apps/charterafrica/src/components/OrganisationCard/OrganisationImageLink.test.js
new file mode 100644
index 000000000..a1f31844a
--- /dev/null
+++ b/apps/charterafrica/src/components/OrganisationCard/OrganisationImageLink.test.js
@@ -0,0 +1,24 @@
+import { createRender } from "@commons-ui/testing-library";
+import React from "react";
+
+import OrganisationImageLink from "./OrganisationImageLink";
+
+import theme from "@/charterafrica/theme";
+
+// eslint-disable-next-line testing-library/render-result-naming-convention
+const render = createRender({ theme });
+
+const defaultProps = {
+ name: "Organisation Name",
+ avatarUrl: "/static/images/avatar/1.jpg",
+ link: {
+ href: "https://charter.africa",
+ },
+};
+
+describe("
", () => {
+ it("renders unchanged", () => {
+ const { container } = render(
);
+ expect(container).toMatchSnapshot();
+ });
+});
diff --git a/apps/charterafrica/src/components/OrganisationCard/index.js b/apps/charterafrica/src/components/OrganisationCard/index.js
index 8b775635d..ed50bf468 100644
--- a/apps/charterafrica/src/components/OrganisationCard/index.js
+++ b/apps/charterafrica/src/components/OrganisationCard/index.js
@@ -1,3 +1,5 @@
import OrganisationCard from "./OrganisationCard";
+import OrganisationImageLink from "./OrganisationImageLink";
export default OrganisationCard;
+export { OrganisationImageLink };
diff --git a/apps/charterafrica/src/components/Repository/Repository.js b/apps/charterafrica/src/components/Repository/Repository.js
new file mode 100644
index 000000000..9606b774e
--- /dev/null
+++ b/apps/charterafrica/src/components/Repository/Repository.js
@@ -0,0 +1,72 @@
+import { RichTypography } from "@commons-ui/core";
+import { Link } from "@commons-ui/next";
+import { Grid, Chip } from "@mui/material";
+import React from "react";
+
+import StarIcon from "@/charterafrica/assets/icons/Type=Star, Size=24, Color=CurrentColor.svg";
+import { neutral } from "@/charterafrica/colors";
+import formatDateTime from "@/charterafrica/utils/formatDate";
+
+const Repository = React.forwardRef(function Tools(props, ref) {
+ const {
+ name,
+ stargazers,
+ visibility,
+ description,
+ url,
+ updatedAt,
+ techSkills,
+ sx,
+ } = props;
+ const updatedDate = formatDateTime(updatedAt, { includeTime: false });
+
+ return (
+
+
+
+
+ {name}
+
+
+ {description}
+
+
+ {techSkills}
+
+
+ {updatedDate}
+
+
+
+
+
+ {stargazers}
+
+
+
+
+
+ );
+});
+
+export default Repository;
diff --git a/apps/charterafrica/src/components/Repository/Repository.snap.js b/apps/charterafrica/src/components/Repository/Repository.snap.js
new file mode 100644
index 000000000..1aadefc2d
--- /dev/null
+++ b/apps/charterafrica/src/components/Repository/Repository.snap.js
@@ -0,0 +1,66 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`
renders unchanged 1`] = `
+
+`;
diff --git a/apps/charterafrica/src/components/Repository/Repository.test.js b/apps/charterafrica/src/components/Repository/Repository.test.js
new file mode 100644
index 000000000..38010a89b
--- /dev/null
+++ b/apps/charterafrica/src/components/Repository/Repository.test.js
@@ -0,0 +1,27 @@
+import { createRender } from "@commons-ui/testing-library";
+import React from "react";
+
+import Repository from "./Repository";
+
+import theme from "@/charterafrica/theme";
+
+// eslint-disable-next-line testing-library/render-result-naming-convention
+const render = createRender({ theme });
+
+const defaultProps = {
+ id: 1,
+ name: "Repository 1",
+ stargazers: 100,
+ visibility: "PUBLIC",
+ description: "Charter Africa website",
+ url: "https://charter.africa",
+ updatedAt: "2021-10-01T00:00:00Z",
+ techSkills: "React, Next.js, TypeScript",
+};
+
+describe("
", () => {
+ it("renders unchanged", () => {
+ const { container } = render(
);
+ expect(container).toMatchSnapshot();
+ });
+});
diff --git a/apps/charterafrica/src/components/Repository/index.js b/apps/charterafrica/src/components/Repository/index.js
new file mode 100644
index 000000000..ca287922a
--- /dev/null
+++ b/apps/charterafrica/src/components/Repository/index.js
@@ -0,0 +1,3 @@
+import Repository from "./Repository";
+
+export default Repository;
diff --git a/apps/charterafrica/src/lib/data/common/processPageContributors.js b/apps/charterafrica/src/lib/data/common/processPageContributors.js
index 7fafbd62c..415f2fca5 100644
--- a/apps/charterafrica/src/lib/data/common/processPageContributors.js
+++ b/apps/charterafrica/src/lib/data/common/processPageContributors.js
@@ -69,9 +69,7 @@ async function processPagePerson(page, api, context) {
return null;
}
- const block = blocks.findIndex(
- ({ slug: bSlug }) => bSlug === "our-contributors",
- );
+ const block = blocks.find(({ slug: bSlug }) => bSlug === "our-contributors");
const contributor = docs[0] || {};
const { docs: toolDocs } = await api.getCollection(TOOL_COLLECTION, {
locale,
@@ -91,6 +89,22 @@ async function processPagePerson(page, api, context) {
};
});
+ const { socialMedia = [] } = contributor;
+ if (contributor.source === "github") {
+ const github = {
+ link: `https://github.com/${contributor?.externalId || ""}`,
+ name: "gitHub",
+ };
+ socialMedia.push(github);
+ }
+ if (contributor.email) {
+ const email = {
+ link: `mailto:${contributor.email}`,
+ name: "email",
+ };
+ socialMedia.push(email);
+ }
+
return {
...page,
blocks: [
@@ -99,18 +113,20 @@ async function processPagePerson(page, api, context) {
id: block.id ?? null,
image: contributor.avatarUrl ?? null,
name: contributor?.fullName ?? contributor?.externalId ?? null,
+ role: contributor.role ?? null,
+ currentOrganisation: contributor.currentOrganisation ?? null,
location: contributor.location ?? null,
description: contributor.description ?? null,
- email: contributor.email ?? null,
toolsTitle: block?.toolsTitle ?? null,
lastActive: contributor.lastActive
? formatDateTime(contributor.lastActive, {})
: null,
- github:
- contributor.source === "github"
- ? `https://github.com/${contributor?.externalId || ""}`
- : "",
tools,
+ socialMedia,
+ repositories: contributor.repositories ?? [],
+ repositoriesTitle: block?.repositoriesTitle ?? null,
+ organisations: contributor.organisations ?? [],
+ organisationsTitle: block?.organisationsTitle ?? null,
},
],
};
diff --git a/apps/charterafrica/src/lib/ecosystem/airtable/processData.js b/apps/charterafrica/src/lib/ecosystem/airtable/processData.js
index 140e19fa6..7880e3f32 100644
--- a/apps/charterafrica/src/lib/ecosystem/airtable/processData.js
+++ b/apps/charterafrica/src/lib/ecosystem/airtable/processData.js
@@ -153,6 +153,7 @@ export function processContributor(item, config) {
contributorTableColumns.socialMediaColumns,
data,
);
+ const organisations = getValue(data, contributorTableColumns.organisations);
const foundDescription = locales.reduce((acc, curr) => {
const val = getValue(data, contributorTableColumns.description[curr]);
if (val) {
@@ -170,10 +171,16 @@ export function processContributor(item, config) {
return {
airtableId: data.id,
classification: getValue(data, contributorTableColumns.classification),
+ role: getValue(data, contributorTableColumns.role),
+ currentOrganisation: getValue(
+ data,
+ contributorTableColumns.currentOrganisation,
+ ),
externalId,
repoLink,
socialMedia,
description,
+ organisations,
};
}
diff --git a/apps/charterafrica/src/lib/ecosystem/ecosystem/processData.js b/apps/charterafrica/src/lib/ecosystem/ecosystem/processData.js
index 2b4854a0a..39d1d9473 100644
--- a/apps/charterafrica/src/lib/ecosystem/ecosystem/processData.js
+++ b/apps/charterafrica/src/lib/ecosystem/ecosystem/processData.js
@@ -15,7 +15,16 @@ export async function prepareContributors(airtableData, config) {
const { contributors } = airtableData;
await bulkMarkDeleted(CONTRIBUTORS_COLLECTION, contributors);
const toProcess = airtableData?.contributors?.map(async (item) => {
- return createCollection(CONTRIBUTORS_COLLECTION, item, config);
+ const rawOrganisations = item?.organisations || [];
+ const organisations = await getCollectionIdsPerAirtableId(
+ ORGANIZATION_COLLECTION,
+ rawOrganisations,
+ );
+ const toCreate = {
+ ...item,
+ organisations,
+ };
+ return createCollection(CONTRIBUTORS_COLLECTION, toCreate, config);
});
return Promise.allSettled(toProcess);
}
@@ -56,18 +65,24 @@ export async function prepareTools(airtableData, config) {
return Promise.allSettled(toProcess);
}
-export async function updateContributor(forceUpdate) {
- const { docs } = await api.getCollection(CONTRIBUTORS_COLLECTION);
+export async function updateContributor() {
+ const { docs } = await api.getCollection(CONTRIBUTORS_COLLECTION, {
+ pagination: false,
+ });
+ const githubContributors = await github.bulkFetchContributors(
+ docs.map(({ externalId }) => externalId),
+ );
const updatePromises = docs.map(async (item) => {
- const itemToFetch = forceUpdate ? { ...item, eTag: null } : item;
- const updated = await github.fetchContributor(itemToFetch);
+ const updated = githubContributors[item.externalId];
return api.updateCollection(CONTRIBUTORS_COLLECTION, item.id, updated);
});
return Promise.allSettled(updatePromises);
}
export async function updateOrganisation(forceUpdate) {
- const { docs } = await api.getCollection(ORGANIZATION_COLLECTION);
+ const { docs } = await api.getCollection(ORGANIZATION_COLLECTION, {
+ pagination: false,
+ });
const updatePromises = docs.map(async (item) => {
const itemToFetch = forceUpdate ? { ...item, eTag: null } : item;
const updated = await github.fetchOrganisation(itemToFetch);
@@ -77,7 +92,9 @@ export async function updateOrganisation(forceUpdate) {
}
export async function updateTool(forceUpdate) {
- const { docs } = await api.getCollection(TOOL_COLLECTION);
+ const { docs } = await api.getCollection(TOOL_COLLECTION, {
+ pagination: false,
+ });
const updatePromises = docs.map(async (item) => {
const itemToFetch = forceUpdate ? { ...item, eTag: null } : item;
const updated = await github.fetchTool(itemToFetch);
diff --git a/apps/charterafrica/src/lib/ecosystem/github/github.js b/apps/charterafrica/src/lib/ecosystem/github/github.js
new file mode 100644
index 000000000..b20de6018
--- /dev/null
+++ b/apps/charterafrica/src/lib/ecosystem/github/github.js
@@ -0,0 +1,58 @@
+import * as Sentry from "@sentry/nextjs";
+
+import fetchJson, { FetchError } from "@/charterafrica/utils/fetchJson";
+
+const BASE_URL = "https://api.github.com";
+
+async function graphQuery(query, variables) {
+ const url = `${BASE_URL}/graphql`;
+ const headers = {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
+ };
+ const data = {
+ variables,
+ query,
+ };
+ const res = await fetchJson.post(url, { data, headers });
+ if (res?.data) {
+ return res.data;
+ }
+ const message = `Unable to fetch from github errors ${JSON.stringify(
+ res.errors,
+ )}`;
+ Sentry.captureException(message);
+ throw new FetchError(message, res.errors, 500);
+}
+
+async function restQuery(path, tag) {
+ const url = `${BASE_URL}/${path}`;
+ const headers = {
+ "If-None-Match": tag,
+ Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
+ };
+ try {
+ const res = await fetch(url, { headers });
+ if (res.ok) {
+ const response = await res.json();
+ const eTag = res.headers.get("ETag");
+ return { ...response, eTag };
+ }
+ if (res.status !== 304) {
+ const response = await res.json();
+ const message = `Error fetching "${url}" from github errors ${JSON.stringify(
+ response,
+ )}`;
+ throw new FetchError(message, res, 500);
+ }
+ return null;
+ } catch (e) {
+ Sentry.captureException(e);
+ return null;
+ }
+}
+
+export default {
+ graphQuery,
+ restQuery,
+};
diff --git a/apps/charterafrica/src/lib/ecosystem/github/index.js b/apps/charterafrica/src/lib/ecosystem/github/index.js
index a07565e17..db76597f0 100644
--- a/apps/charterafrica/src/lib/ecosystem/github/index.js
+++ b/apps/charterafrica/src/lib/ecosystem/github/index.js
@@ -1,7 +1,11 @@
-import { fetchTool, fetchOrganisation, fetchContributor } from "./processData";
+import {
+ bulkFetchContributors,
+ fetchTool,
+ fetchOrganisation,
+} from "./processData";
export default {
fetchTool,
fetchOrganisation,
- fetchContributor,
+ bulkFetchContributors,
};
diff --git a/apps/charterafrica/src/lib/ecosystem/github/processData.js b/apps/charterafrica/src/lib/ecosystem/github/processData.js
index e113a7725..000299153 100644
--- a/apps/charterafrica/src/lib/ecosystem/github/processData.js
+++ b/apps/charterafrica/src/lib/ecosystem/github/processData.js
@@ -1,8 +1,7 @@
import * as Sentry from "@sentry/nextjs";
-import fetchJson, { FetchError } from "@/charterafrica/utils/fetchJson";
+import github from "./github";
-const BASE_URL = "https://api.github.com";
const GET_REPOSITORY = `query($repositoryOwner: String!, $repositoryName: String!) {
repository(owner: $repositoryOwner, name: $repositoryName) {
name
@@ -47,52 +46,37 @@ const GET_REPOSITORY = `query($repositoryOwner: String!, $repositoryName: String
}
}`;
-async function fetchRepository(variables) {
- const url = `${BASE_URL}/graphql`;
- const headers = {
- "Content-Type": "application/json",
- Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
- };
- const data = {
- variables,
- query: GET_REPOSITORY,
- };
- const res = await fetchJson.post(url, { data, headers });
- if (res?.data?.repository) {
- return res.data.repository;
- }
- const message = `Unable to fetch ${variables.repositoryOwner}/${
- variables.repositoryName
- } from github errors ${JSON.stringify(res.errors)}`;
- Sentry.captureException(message);
- throw new FetchError(message, res.errors, 500);
-}
-
-async function fetchGithubApi(path, tag) {
- const url = `${BASE_URL}/${path}`;
- const headers = {
- "If-None-Match": tag,
- Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
- };
- try {
- const res = await fetch(url, { headers });
- if (res.ok) {
- const response = await res.json();
- const eTag = res.headers.get("ETag");
- return { ...response, eTag };
- }
- if (res.status !== 304) {
- const response = await res.json();
- const message = `Error fetching "${url}" from github errors ${JSON.stringify(
- response,
- )}`;
- throw new FetchError(message, res, 500);
+function fetchUserQuery(username) {
+ return `user(login: $${username}) {
+ name
+ avatarUrl
+ url
+ email
+ location
+ login
+ websiteUrl
+ repositories(first: 3, orderBy: {field: STARGAZERS, direction: DESC}) {
+ edges {
+ node {
+ name
+ description
+ stargazers {
+ totalCount
+ }
+ visibility
+ url
+ updatedAt
+ languages(first:5) {
+ edges {
+ node {
+ name
+ }
+ }
+ }
+ }
}
- return null;
- } catch (e) {
- Sentry.captureException(e);
- return null;
}
+}`;
}
export async function fetchTool({ externalId }) {
@@ -108,34 +92,36 @@ export async function fetchTool({ externalId }) {
return null;
}
- const data = await fetchRepository({
+ const data = await github.graphQuery(GET_REPOSITORY, {
repositoryOwner,
repositoryName,
});
- const techSkills = data.languages?.nodes?.map((language) => ({
+ const { repository } = data;
+
+ const techSkills = repository.languages?.nodes?.map((language) => ({
language: language?.name,
}));
- const commit = data?.defaultBranchRef?.target.history.edges?.[0]?.node;
+ const commit = repository?.defaultBranchRef?.target.history.edges?.[0]?.node;
return {
externalId,
- repoLink: data.url,
- link: data.homepageUrl,
+ repoLink: repository.url,
+ link: repository.homepageUrl,
techSkills,
lastCommit: {
author: commit?.author?.name,
committedDate: commit?.committedDate,
message: commit?.message,
},
- stars: data?.stargazers?.totalCount,
- views: data?.watchers?.totalCount,
- forks: data?.forks?.totalCount,
+ stars: repository?.stargazers?.totalCount,
+ views: repository?.watchers?.totalCount,
+ forks: repository?.forks?.totalCount,
source: "github",
- sourceUpdatedAt: new Date(data.updatedAt),
+ sourceUpdatedAt: new Date(repository.updatedAt),
};
}
export async function fetchOrganisation({ externalId, eTag }) {
- const data = await fetchGithubApi(`orgs/${externalId}`, eTag);
+ const data = await github.restQuery(`orgs/${externalId}`, eTag);
if (!data) {
return null;
}
@@ -150,19 +136,97 @@ export async function fetchOrganisation({ externalId, eTag }) {
};
}
-export async function fetchContributor({ externalId, eTag }) {
- const data = await fetchGithubApi(`users/${externalId}`, eTag);
+function processGithubContributor(data) {
if (!data) {
return null;
}
+ const { name, avatarUrl, url, email, location, websiteUrl, repositories } =
+ data;
+
+ const repos = repositories?.edges?.map((edge) => {
+ const {
+ name: repoName,
+ description,
+ stargazers,
+ visibility,
+ url: repoURL,
+ updatedAt,
+ languages,
+ } = edge?.node ?? {};
+
+ const techSkills = languages?.edges
+ ?.map((language) => language?.node?.name)
+ .join(", ");
+
+ return {
+ name: repoName,
+ description,
+ stargazers: stargazers?.totalCount,
+ visibility,
+ url: repoURL,
+ updatedAt,
+ techSkills,
+ };
+ });
return {
- fullName: data.name,
- avatarUrl: data.avatar_url,
- repoLink: data.html_url,
- location: data.location,
- website: data.blog,
- email: data.email,
- eTag: data.eTag,
+ fullName: name,
+ avatarUrl,
+ repoLink: url,
+ location,
+ website: websiteUrl,
+ email,
+ repositories: repos,
};
}
+
+function chunkArray(array, chunkSize) {
+ return Array.from(
+ { length: Math.ceil(array.length / chunkSize) },
+ (_, index) => array.slice(index * chunkSize, (index + 1) * chunkSize),
+ );
+}
+
+function constructGraphQLQuery(ids) {
+ const queryParts = ids.map((id) => {
+ const queryPart = fetchUserQuery(id);
+ return `${id}: ${queryPart}`;
+ });
+ return `query(${ids
+ .map((id) => `$${id}: String!`)
+ .join(", ")}){\n${queryParts.join("\n")}\n}`;
+}
+
+async function fetchContributorsData(users) {
+ const variables = users.reduce((acc, id) => {
+ const sanitizedId = id.replaceAll("-", "_");
+ acc[sanitizedId] = id;
+ return acc;
+ }, {});
+
+ const query = constructGraphQLQuery(Object.keys(variables));
+ const data = await github.graphQuery(query, variables);
+
+ return Object.keys(data || {}).reduce((acc, key) => {
+ acc[variables[key]] = processGithubContributor(data[key]);
+ return acc;
+ }, {});
+}
+
+const USER_QUERY_LIMIT = 80;
+
+export async function bulkFetchContributors(externalIds) {
+ const contributors = {};
+ const chunkedArrays = chunkArray(externalIds, USER_QUERY_LIMIT);
+
+ const promises = chunkedArrays.map(async (arr) => {
+ try {
+ const data = await fetchContributorsData(arr);
+ Object.assign(contributors, data);
+ } catch (error) {
+ Sentry.captureMessage(error.message);
+ }
+ });
+ await Promise.allSettled(promises);
+ return contributors;
+}
diff --git a/apps/charterafrica/src/payload/blocks/Contributors.js b/apps/charterafrica/src/payload/blocks/Contributors.js
index 23047d40b..5d6e1defb 100644
--- a/apps/charterafrica/src/payload/blocks/Contributors.js
+++ b/apps/charterafrica/src/payload/blocks/Contributors.js
@@ -56,6 +56,28 @@ const Contributors = {
required: true,
localized: true,
},
+ {
+ type: "text",
+ label: {
+ en: "Repositories Title",
+ fr: "Titre des dépôts",
+ pt: "Título dos repositórios",
+ },
+ name: "repositoriesTitle",
+ required: true,
+ localized: true,
+ },
+ {
+ type: "text",
+ label: {
+ en: "Organisations Title",
+ fr: "Titre des organisations",
+ pt: "Título das organizações",
+ },
+ name: "organisationsTitle",
+ required: true,
+ localized: true,
+ },
],
};
export default Contributors;
diff --git a/apps/charterafrica/src/payload/collections/Contributors.js b/apps/charterafrica/src/payload/collections/Contributors.js
index 62faec402..f339b617b 100644
--- a/apps/charterafrica/src/payload/collections/Contributors.js
+++ b/apps/charterafrica/src/payload/collections/Contributors.js
@@ -2,7 +2,10 @@ import avatarUrl from "../fields/avatarUrl";
import dateField from "../fields/dateField";
import slug from "../fields/slug";
import source from "../fields/source";
-import { CONTRIBUTORS_COLLECTION } from "../utils/collections";
+import {
+ CONTRIBUTORS_COLLECTION,
+ ORGANIZATION_COLLECTION,
+} from "../utils/collections";
import nestCollectionUnderPage from "../utils/nestCollectionUnderPage";
function useFullNameOrExternalId({ doc }) {
@@ -48,6 +51,26 @@ const Contributors = {
readOnly: true,
},
},
+ {
+ name: "role",
+ type: "text",
+ label: { en: "Role", fr: "Rôle", pt: "Função" },
+ admin: {
+ readOnly: true,
+ },
+ },
+ {
+ name: "currentOrganisation",
+ type: "text",
+ label: {
+ en: "Current Organization",
+ fr: "Organisation actuelle",
+ pt: "Organização atual",
+ },
+ admin: {
+ readOnly: true,
+ },
+ },
{
name: "classification",
type: "text",
@@ -83,18 +106,6 @@ const Contributors = {
readOnly: true,
},
},
- {
- name: "twitter",
- type: "text",
- label: {
- en: "Twitter handle",
- fr: "Twitter de la personne",
- pt: "Twitter da Pessoa",
- },
- admin: {
- readOnly: true,
- },
- },
{
name: "email",
type: "email",
@@ -173,6 +184,78 @@ const Contributors = {
position: "sidebar",
},
},
+ {
+ name: "organisations",
+ type: "relationship",
+ hasMany: true,
+ admin: {
+ readOnly: true,
+ },
+ relationTo: ORGANIZATION_COLLECTION,
+ label: {
+ en: "Organizations",
+ fr: "Organisations",
+ pt: "Organizações",
+ },
+ },
+ {
+ name: "repositories",
+ type: "array",
+ admin: {
+ readOnly: true,
+ initCollapsed: true,
+ },
+ label: {
+ en: "Repositories",
+ fr: "Dépôts",
+ pt: "Repositórios",
+ },
+ fields: [
+ {
+ name: "name",
+ type: "text",
+ label: { en: "Name", fr: "Nom", pt: "Nome" },
+ },
+ {
+ name: "description",
+ type: "textarea",
+ label: { en: "Description", fr: "Description", pt: "Descrição" },
+ },
+ {
+ name: "stargazers",
+ type: "number",
+ label: { en: "Stargazers", fr: "Stargazers", pt: "Stargazers" },
+ },
+ {
+ name: "visibility",
+ type: "text",
+ label: { en: "Visibility", fr: "Visibilité", pt: "Visibilidade" },
+ },
+ {
+ name: "url",
+ type: "text",
+ label: { en: "URL", fr: "URL", pt: "URL" },
+ },
+ {
+ name: "techSkills",
+ type: "text",
+ label: {
+ en: "Tech Skills",
+ fr: "Compétences techniques",
+ pt: "Habilidades técnicas",
+ },
+ },
+ {
+ name: "updatedAt",
+ type: "date",
+ label: {
+ en: "Updated At",
+ fr: "Mis à jour",
+ pt: "Atualizado em",
+ },
+ },
+ ],
+ },
],
hooks: {
afterRead: [
diff --git a/apps/charterafrica/src/payload/globals/Ecosystem.js b/apps/charterafrica/src/payload/globals/Ecosystem.js
index 191bb54dc..7811d37f8 100644
--- a/apps/charterafrica/src/payload/globals/Ecosystem.js
+++ b/apps/charterafrica/src/payload/globals/Ecosystem.js
@@ -369,6 +369,38 @@ const Ecosystem = {
},
},
}),
+ airtableColumnSelect({
+ schema,
+ tableField: "contributorTableId",
+ overrides: {
+ name: "role",
+ label: { en: "Role", fr: "Rôle", pt: "Função" },
+ },
+ }),
+ airtableColumnSelect({
+ schema,
+ tableField: "contributorTableId",
+ overrides: {
+ name: "currentOrganisation",
+ label: {
+ en: "Current Organisation",
+ fr: "Organisation actuelle",
+ pt: "Organização atual",
+ },
+ },
+ }),
+ airtableColumnSelect({
+ schema,
+ tableField: "contributorTableId",
+ overrides: {
+ name: "organisations",
+ label: {
+ en: "Organisations",
+ fr: "Organisations",
+ pt: "Organizações",
+ },
+ },
+ }),
airtableColumnSelect({
schema,
tableField: "contributorTableId",
diff --git a/apps/charterafrica/src/theme/index.js b/apps/charterafrica/src/theme/index.js
index 7e5ff961d..893441e04 100644
--- a/apps/charterafrica/src/theme/index.js
+++ b/apps/charterafrica/src/theme/index.js
@@ -110,6 +110,7 @@ const theme = createTheme({
p2SemiBold: initializeTypographyVariant(16, 19, 600),
p3: initializeTypographyVariant(18, 21.6),
p3SemiBold: initializeTypographyVariant(18, 21.6, 600),
+ p4: initializeTypographyVariant(23, 28),
body1: undefined,
body2: undefined,
caption: initializeTypographyVariant(12, 14),