Skip to content

Global Author list#2432

Open
jakebayliss wants to merge 10 commits intomainfrom
global-people-v3
Open

Global Author list#2432
jakebayliss wants to merge 10 commits intomainfrom
global-people-v3

Conversation

@jakebayliss
Copy link
Member

Description

Adding global author list using SSW.People.Profiles.
This PR generates a new people index json file which we use as the source of truth for the author drop down list

Screenshot (optional)

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request implements a global author management system by integrating with the SSW.People.Profiles repository. The changes replace the previous author storage format (storing full name and URL) with a slug-based system that references a centrally maintained people index.

Changes:

  • Introduces a new Author type that stores only author slugs, replacing the previous object structure with title and url fields
  • Adds a people index generation script that fetches profiles from SSW.People.Profiles and creates a local JSON index
  • Implements custom TinaCMS field component (AuthorSelector) with searchable dropdown UI for selecting authors from the global people list

Reviewed changes

Copilot reviewed 16 out of 18 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
types/author.ts New type definition for author references containing just the slug
types/rule.ts Updated to use the new Author type
models/Rule.ts Updated to use the new Author type for type consistency
scripts/generate-people-index.js New script to fetch and generate people index from GitHub
tina/fields/AuthorSelector.tsx New custom field component for selecting authors with search functionality
tina/collection/rule.tsx Updated rule schema to use new author field structure
tina/queries/queries.gql Updated GraphQL queries to reference author slugs instead of title/url
tina/tina-lock.json Schema lock file update reflecting the structural changes
lib/people/index.ts Server-side utilities for resolving author slugs to person data
lib/people/usePeople.ts Client-side hooks for author resolution
components/AuthorsCard.tsx Refactored to resolve author slugs to full person data
app/user/client-page.tsx Updated to use author slugs instead of names
app/api/people/route.ts New API endpoint to serve the people index
app/api/tina/rules-by-author/route.ts Updated to query by authorSlug instead of authorTitle
app/api/rules/by-guid/route.ts Updated author data mapping
package.json Added new npm script for generating people index
.gitignore Added people-index.json and people folder to ignore list
.github/workflows/build-artifacts.yml Added step to generate people index during build

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1 to +2
export interface Author {
author?: string | null;
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The field name author within the Author interface is confusing and redundant. Consider renaming the field to slug or authorSlug for better clarity, since it's storing the author's slug, not the full author object. This would make the code more self-documenting.

Suggested change
export interface Author {
author?: string | null;
export interface Author {
/**
* @deprecated Use `authorSlug` instead.
*/
author?: string | null;
authorSlug?: string | null;

Copilot uses AI. Check for mistakes.
Comment on lines +82 to +87
function nameToSlug(name) {
return name
.toLowerCase()
.replace(/['']/g, "")
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "");
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nameToSlug function may produce duplicate slugs for names that differ only by special characters or apostrophes. For example, "O'Brien" and "OBrien" would both become "obrien". Consider maintaining a set of generated slugs and appending a numeric suffix when duplicates are detected to ensure uniqueness, or logging warnings when duplicate slugs are generated so they can be manually reviewed.

Suggested change
function nameToSlug(name) {
return name
.toLowerCase()
.replace(/['']/g, "")
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "");
const generatedSlugs = new Set();
function nameToSlug(name) {
const baseSlug = name
.toLowerCase()
.replace(/['']/g, "")
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "");
let slug = baseSlug;
let counter = 2;
if (generatedSlugs.has(slug)) {
// Append a numeric suffix to ensure uniqueness
while (generatedSlugs.has(slug)) {
slug = `${baseSlug}-${counter}`;
counter += 1;
}
console.warn(
`Duplicate slug detected for name "${name}". Using unique slug "${slug}".`
);
}
generatedSlugs.add(slug);
return slug;

Copilot uses AI. Check for mistakes.
Comment on lines +154 to +168
<div className="relative z-1000">
<input type="hidden" id={input.name} {...input} />
<Popover>
{({ open }) => (
<>
<PopoverButton className="text-sm h-11 px-4 justify-between w-full bg-white border border-gray-200 rounded-full hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-colors flex items-center">
<span className="flex items-center gap-2 truncate">
<BiUser className="w-4 h-4 text-gray-400 shrink-0" />
<span className="truncate" title={selectedAuthorLabel || undefined}>
{selectedAuthorLabel || "Select an author"}
</span>
</span>
<BiChevronDown className={`w-4 h-4 transition-transform ${open ? "rotate-180" : ""}`} />
</PopoverButton>
<div className="absolute inset-x-0 -bottom-2 translate-y-full z-1000">
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The z-1000 class is used twice (lines 154 and 168) which may cause z-index issues. The z-1000 is not a standard Tailwind class (Tailwind's z-index scale typically goes up to z-50). Consider using a proper z-index value that's defined in your Tailwind configuration or use standard values like z-50. Also, applying z-1000 to both the parent container and the dropdown may be redundant.

Copilot uses AI. Check for mistakes.
Comment on lines +50 to +53
return {
slug: profile.slug || profile.fullName?.toLowerCase().replace(/\s+/g, "-") || "",
fullName: profile.fullName || "",
};
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The resolveAuthor function has a fallback slug generation that may not match the slug format used in the people index. The fallback profile.fullName?.toLowerCase().replace(/\s+/g, "-") is a simple conversion, but the actual slug generation in scripts/generate-people-index.js uses the nameToSlug function which also removes apostrophes and handles special characters differently. This inconsistency could lead to slug mismatches. Consider using a shared utility function for slug generation or ensuring the API response includes the correct slug.

Copilot uses AI. Check for mistakes.
import { type NextRequest, NextResponse } from "next/server";
import path from "path";

export const dynamic = "force-static";
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dynamic = "force-static" export combined with revalidate = 3600 may not work as expected. When you use force-static, Next.js will try to statically generate this route at build time, but the route depends on people-index.json which is generated during the build process. If the file doesn't exist at build time, the route will return an empty object. Consider using dynamic = "force-dynamic" or removing the dynamic export to allow Next.js to handle caching appropriately with ISR (Incremental Static Regeneration) via the revalidate setting.

Suggested change
export const dynamic = "force-static";

Copilot uses AI. Check for mistakes.
Comment on lines 218 to 221
- name: Generate people index
run: |
npm ci
npm run generate:people
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running npm ci in the workflow installs all dependencies, which can be time-consuming and unnecessary if only the people index generation script needs to run. Consider either:

  1. Adding a caching step for node_modules to speed up the workflow
  2. Installing only the required dependencies (gray-matter) if the script doesn't need the full dependency tree

Example caching approach:

- name: Cache node_modules
  uses: actions/cache@v3
  with:
    path: node_modules
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}

Copilot uses AI. Check for mistakes.
Comment on lines 39 to +43
useEffect(() => {
if (resolvedAuthors.length > 0) {
setImgSrcList(resolvedAuthors.map(getImgSource));
}
}, [resolvedAuthors, getImgSource]);

useEffect(() => {
if (resolvedAuthors.length > 0) {
resolvedAuthors.forEach((author) => {
if (!author.title) {
console.warn(`Profile title is missing for author with URL: ${author.url}`);
}
});
if (displayAuthors.length > 0) {
setImgSrcList(displayAuthors.map((a) => a.imageUrl || placeholderImg));
}
}, [resolvedAuthors]);
}, [displayAuthors, placeholderImg]);
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The imageUrl is already set to placeholderImg when building displayAuthors (line 33), so checking for it again in the effect (line 41) and when rendering (lines 83, 96) is redundant. You can simplify the logic since displayAuthors[i].imageUrl will never be falsy. This current implementation could cause confusion and unnecessary defensive checks.

Copilot uses AI. Check for mistakes.
isArchived?: boolean;
archivedreason?: string;
authors?: ({ title?: string | null; url?: string | null } | null)[] | null;
authors?: (Author | null)[] | null;
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Author type is referenced but not imported. You need to add an import statement at the top of this file:

import { Author } from "@/types/author";

Without this import, TypeScript will fail to compile as it cannot resolve the Author type.

Copilot uses AI. Check for mistakes.
function nameToSlug(name) {
return name
.toLowerCase()
.replace(/['']/g, "")
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Character ''' is repeated in the same character class.

Suggested change
.replace(/['']/g, "")
.replace(/'/g, "")

Copilot uses AI. Check for mistakes.
jakebayliss and others added 4 commits February 6, 2026 10:51
Bump tina packages to latest
#2429)

* Update github action to use the current branch name as the tina branch

* Pass in tina branch from pr-open
* Initial plan

* Change SEO Description field to textarea with 3 rows

Co-authored-by: Aibono1225 <127192800+Aibono1225@users.noreply.github.com>

* Complete SEO Description textarea implementation

Co-authored-by: Aibono1225 <127192800+Aibono1225@users.noreply.github.com>

* Remove test rule file

* Revert changes to extensions.json

* tina-lock

* update tina lock

* update lock

* bypass Tina UIField typing for textarea props

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Aibono1225 <127192800+Aibono1225@users.noreply.github.com>
Co-authored-by: Chloe Lin <chloelin@ssw.com.au>
Co-authored-by: Jake Bayliss <bayliss.jw@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants