Skip to content

Conversation

@ChengShi-1
Copy link
Contributor

What this PR does / why we need it:

The homepage displays 'Harvard’ as name of the archive and has links to the Harvard support page.

Which issue(s) this PR closes:

Special notes for your reviewer:

Suggestions on how to test this:

Check if there are any "Harvard" related text on the page

Does this PR introduce a user interface change? If mockups are available, please link/include them here:

Is there a release notes or changelog update needed for this change?:

Yes

Additional documentation:

@github-actions github-actions bot added FY26 Sprint 17 FY26 Sprint 17 (2026-02-11 - 2026-02-25) Project: HDV SPA Rollout SPA labels Feb 12, 2026
@ChengShi-1 ChengShi-1 added Size: 3 A percentage of a sprint. 2.1 hours. Project: HDV SPA Rollout FY26 Sprint 17 FY26 Sprint 17 (2026-02-11 - 2026-02-25) and removed SPA Project: HDV SPA Rollout FY26 Sprint 17 FY26 Sprint 17 (2026-02-11 - 2026-02-25) labels Feb 12, 2026
@coveralls
Copy link

coveralls commented Feb 12, 2026

Coverage Status

coverage: 97.281% (-0.06%) from 97.339%
when pulling 9693495 on 791-allow-customization-of-the-homepage-remove-harvard-specific-content
into 24bdb22 on develop.

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 makes the homepage and footer customizable by introducing runtime configuration options for organization-specific branding. Previously, the application had hard-coded references to "Harvard Dataverse" and Harvard-specific URLs, making it unsuitable for deployment by other organizations. The changes introduce three new optional configuration sections (branding, homepage, and footer) that allow organizations to customize the dataverse name, support URL, copyright holder, and privacy policy URL without rebuilding the application.

Changes:

  • Added runtime configuration schema for branding, homepage, and footer customization in src/config.ts
  • Updated Footer component to use configurable copyright holder and privacy policy URL
  • Updated Usage component to use configurable dataverse name and support URL
  • Modified translation strings to use interpolation for dynamic branding values

Reviewed changes

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

Show a summary per file
File Description
src/config.ts Added zod schema definitions for optional branding, homepage, and footer configuration objects
src/sections/layout/footer/Footer.tsx Updated to use configurable copyrightHolder and privacyPolicyUrl from app config with proper fallbacks and optional chaining
src/sections/homepage/usage/Usage.tsx Modified to use configurable dataverseName and supportUrl from app config (has critical issues with null safety)
public/config.js Added default configuration values for branding, homepage, and footer sections with Harvard-specific defaults
cypress.config.ts Added test configuration values for branding, homepage, and footer sections
tests/support/bootstrapAppConfig.ts Extended test config builder to support new branding, homepage, and footer configuration
tests/component/sections/layout/footer/Footer.spec.tsx Added test coverage for privacy policy link and updated repository interface to match expanded API
public/locales/en/homepage.json Changed hard-coded "Harvard Dataverse" strings to use {{dataverseName}} interpolation
public/locales/es/homepage.json Changed hard-coded "Harvard Dataverse" strings to use {{dataverseName}} interpolation
public/locales/en/footer.json Updated copyright translation to use {{copyrightHolder}} interpolation
public/locales/es/footer.json Updated copyright translation to use {{copyrightHolder}} interpolation
CHANGELOG.md Added entry documenting the new runtime configuration options
Comments suppressed due to low confidence (1)

src/sections/homepage/usage/Usage.tsx:89

  • The Usage component now uses configurable branding values (dataverseName, supportUrl) but lacks test coverage. Other homepage components like Metrics and SearchInput have dedicated test files in tests/component/sections/homepage/. Consider adding tests to verify that: 1) the dataverseName interpolation works correctly in the translation strings, 2) the supportUrl is properly used in the link, 3) fallback behavior works when branding config is not provided, and 4) the component handles undefined branding/homepage config objects gracefully.
export const Usage = ({ collectionId }: UsageProps) => {
  const { t } = useTranslation('homepage', { keyPrefix: 'usage' })
  const appConfig = requireAppConfig() as AppConfig
  const dataverseName = appConfig.branding.dataverseName ?? 'Dataverse'
  const supportUrl = appConfig.homepage.supportUrl

  return (
    <Row>
      <Col xs={12} lg={4} className={styles.column_card}>
        <Card className={styles.card}>
          <Card.Body className={styles.card_body}>
            <h5>{t('datasets.title')}</h5>
            <p className="small text-muted">{t('datasets.content')}</p>
            <footer className={styles.footer_wrapper}>
              <Link
                to={RouteWithParams.CREATE_DATASET(collectionId)}
                className="btn btn-secondary btn-sm">
                <Stack direction="horizontal" gap={1}>
                  <span className={styles.cta_link_text}>{t('datasets.text_button')}</span>{' '}
                  <Plus size={22} />
                </Stack>
              </Link>
            </footer>
          </Card.Body>
        </Card>
      </Col>
      <Col xs={12} lg={4} className={styles.column_card}>
        <Card className={styles.card}>
          <Card.Body className={styles.card_body}>
            <h5>{t('collections.title')}</h5>
            <p className="small text-muted">{t('collections.content')}</p>
            <footer className={styles.footer_wrapper}>
              <Link
                to={RouteWithParams.CREATE_COLLECTION(collectionId)}
                className="btn btn-secondary btn-sm">
                <Stack direction="horizontal" gap={1}>
                  <span className={styles.cta_link_text}>{t('collections.text_button')}</span>{' '}
                  <Plus size={22} />
                </Stack>
              </Link>
            </footer>
          </Card.Body>
        </Card>
      </Col>

      <Col xs={12} lg={4} className={styles.column_card}>
        <Card className={styles.card}>
          <Card.Body className={styles.card_body}>
            <h5>{t('general.title', { dataverseName })}</h5>
            <p className="small text-muted">{t('general.content')}</p>
            <footer className={styles.footer_wrapper}>
              <a
                href={supportUrl}
                target="_blank"
                rel="noreferrer noopener"
                className="btn btn-secondary btn-sm">
                <Stack direction="horizontal" gap={2}>
                  <span className={styles.cta_link_text}>{t('general.text_button')}</span>
                  <BoxArrowUpRight size={14} />
                </Stack>
              </a>
            </footer>
          </Card.Body>
        </Card>
      </Col>
    </Row>
  )
}

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

Comment on lines 73 to +74
<a
href="https://support.dataverse.harvard.edu/"
href={supportUrl}
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

No fallback value is provided for 'supportUrl'. If 'homepage.supportUrl' is not configured, this will be undefined, and the anchor tag will have an undefined href attribute, which could lead to broken links. Consider providing a fallback value like 'https://dataverse.org/' or handling the case where supportUrl is undefined similar to how privacyPolicyUrl is handled in the Footer component (by conditionally rendering the link only when the URL is available).

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +35
// Optional branding values for homepage/footer text
branding: {
// Used in homepage strings such as "{{dataverseName}} is a repository..."
dataverseName: 'Harvard Dataverse'
},
homepage: {
supportUrl: 'https://support.dataverse.harvard.edu/'
},
footer: {
copyrightHolder: 'The President & Fellows of Harvard College',
privacyPolicyUrl: 'https://support.dataverse.harvard.edu/harvard-dataverse-privacy-policy'
}
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

The default configuration still contains Harvard-specific values (dataverseName: 'Harvard Dataverse', copyrightHolder: 'The President & Fellows of Harvard College', and URLs pointing to support.dataverse.harvard.edu). According to the PR description and issue #791, the goal is to remove Harvard-specific content to make the application deployable for other organizations. While making these values configurable is a good step, the default values should probably be generic (e.g., 'Dataverse' instead of 'Harvard Dataverse') to avoid organizations accidentally deploying with Harvard branding. Organizations can then explicitly configure Harvard branding if needed, rather than having to remember to change it.

Copilot uses AI. Check for mistakes.
Comment on lines +6 to +26
import { requireAppConfig } from '@/config'
import styles from './Usage.module.scss'

interface UsageProps {
collectionId: string
}

interface AppConfig {
branding: {
dataverseName: string
}
homepage: {
supportUrl: string
}
}

export const Usage = ({ collectionId }: UsageProps) => {
const { t } = useTranslation('homepage', { keyPrefix: 'usage' })
const appConfig = requireAppConfig() as AppConfig
const dataverseName = appConfig.branding.dataverseName ?? 'Dataverse'
const supportUrl = appConfig.homepage.supportUrl
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

A local AppConfig interface is defined here, but the actual AppConfig type is already imported from '@/config' and should be used instead. This creates unnecessary duplication and could lead to inconsistencies if the config schema changes. The local interface also incorrectly makes 'branding.dataverseName' and 'homepage.supportUrl' required fields when they are actually optional in the schema (defined as z.string().optional() and z.url().optional() in src/config.ts).

Suggested change
import { requireAppConfig } from '@/config'
import styles from './Usage.module.scss'
interface UsageProps {
collectionId: string
}
interface AppConfig {
branding: {
dataverseName: string
}
homepage: {
supportUrl: string
}
}
export const Usage = ({ collectionId }: UsageProps) => {
const { t } = useTranslation('homepage', { keyPrefix: 'usage' })
const appConfig = requireAppConfig() as AppConfig
const dataverseName = appConfig.branding.dataverseName ?? 'Dataverse'
const supportUrl = appConfig.homepage.supportUrl
import { requireAppConfig, type AppConfig } from '@/config'
import styles from './Usage.module.scss'
interface UsageProps {
collectionId: string
}
export const Usage = ({ collectionId }: UsageProps) => {
const { t } = useTranslation('homepage', { keyPrefix: 'usage' })
const appConfig = requireAppConfig() as AppConfig
const dataverseName = appConfig.branding.dataverseName ?? 'Dataverse'
const supportUrl = appConfig.homepage.supportUrl ?? '#'

Copilot uses AI. Check for mistakes.
Comment on lines +13 to +24
interface AppConfig {
branding: {
dataverseName: string
}
homepage: {
supportUrl: string
}
}

export const Usage = ({ collectionId }: UsageProps) => {
const { t } = useTranslation('homepage', { keyPrefix: 'usage' })
const appConfig = requireAppConfig() as AppConfig
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

The type assertion 'as AppConfig' is used here, but it's casting to the local AppConfig interface rather than the imported type. Since 'requireAppConfig()' already returns the correct AppConfig type from '@/config', this cast is unnecessary and potentially incorrect if the schema differs from the local interface definition.

Suggested change
interface AppConfig {
branding: {
dataverseName: string
}
homepage: {
supportUrl: string
}
}
export const Usage = ({ collectionId }: UsageProps) => {
const { t } = useTranslation('homepage', { keyPrefix: 'usage' })
const appConfig = requireAppConfig() as AppConfig
export const Usage = ({ collectionId }: UsageProps) => {
const { t } = useTranslation('homepage', { keyPrefix: 'usage' })
const appConfig = requireAppConfig()

Copilot uses AI. Check for mistakes.
Comment on lines +25 to +26
const dataverseName = appConfig.branding.dataverseName ?? 'Dataverse'
const supportUrl = appConfig.homepage.supportUrl
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

Missing null safety check for 'appConfig.branding' and 'appConfig.homepage'. Since these fields are optional in the schema (lines 27-36 and 32-36 in src/config.ts), they could be undefined. While 'dataverseName' has a fallback with '??', accessing 'appConfig.branding.dataverseName' and 'appConfig.homepage.supportUrl' will throw a runtime error if 'branding' or 'homepage' are not configured. Use optional chaining: 'appConfig.branding?.dataverseName' and 'appConfig.homepage?.supportUrl'.

Copilot uses AI. Check for mistakes.
@ChengShi-1 ChengShi-1 self-assigned this Feb 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

FY26 Sprint 17 FY26 Sprint 17 (2026-02-11 - 2026-02-25) Project: HDV SPA Rollout Size: 3 A percentage of a sprint. 2.1 hours. SPA.Q1.2026.4 High Priority Bug Fixes

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Allow customization of the homepage (remove Harvard-specific content)

2 participants