Skip to content

refactor(home): redesign hackathon top bar with aurora gradient style#72

Open
dethan3 wants to merge 3 commits intomainfrom
labor-hackathon-2026
Open

refactor(home): redesign hackathon top bar with aurora gradient style#72
dethan3 wants to merge 3 commits intomainfrom
labor-hackathon-2026

Conversation

@dethan3
Copy link
Copy Markdown
Member

@dethan3 dethan3 commented Apr 30, 2026

PR-72 PR-72 PR-72 Powered by Pull Request Badge

Checklist(清单):

  • Labels
  • Assignees
  • Reviewers
  1. 增加首页活动横幅
  2. pnpm-lock.yaml 有变动导致 eslint.config.ts:29@next/next 插件类型不匹配

Summary by CodeRabbit

发布说明

  • 新功能

    • 首页顶部新增活动轮播展示区域,自动加载显示最新的活动信息,用户可通过轮播切换查看不同活动并跳转至详情页面
  • 改进

    • 优化导航栏样式,防止品牌名称和导航链接文本不必要的换行

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 30, 2026

📝 Walkthrough

Walkthrough

主页新增英雄轮播组件,展示五条排序后的活动数据;导航栏文本添加防换行处理;引入配套样式模块定义轮播UI布局与响应式行为。

Changes

组件 / 文件 变更摘要
英雄轮播功能
pages/index.tsx, components/Activity/HeroCarousel.tsx, components/Activity/HeroCarousel.module.less
新增 HeroCarousel 组件,在挂载时获取活动数据、按 startTime 排序并显示最多五条轮播幻灯片;包含徽章、格式化日期、位置和行为按钮;提供本地化后备幻灯片;通过 ResizeObserver 追踪导航栏高度以设置 CSS 偏移量;新增 Less 样式模块定义深色背景、动态最小高度、指示器定位/尺寸、渐变叠加层、按钮悬停过渡及移动端响应式调整。
导航栏优化
components/Navigator/MainNavigator.tsx
为品牌名称和顶级非下拉菜单链接添加 text-nowrap 类,防止文本换行。

Sequence Diagram(s)

sequenceDiagram
    participant Page as 首页
    participant HeroCarousel as HeroCarousel组件
    participant API as Activity API
    participant LarkImage as LarkImage
    participant DOM as 浏览器

    Page->>HeroCarousel: 挂载组件
    activate HeroCarousel
    HeroCarousel->>API: 获取活动数据
    activate API
    API-->>HeroCarousel: 返回活动列表
    deactivate API
    
    HeroCarousel->>HeroCarousel: 按 startTime 排序<br/>取前5条
    HeroCarousel->>DOM: 初始化 ResizeObserver<br/>测量导航栏高度
    activate DOM
    DOM-->>HeroCarousel: 返回高度值
    deactivate DOM
    
    HeroCarousel->>HeroCarousel: 设置 CSS 变量<br/>--nav-height
    
    loop 每条活动
        HeroCarousel->>LarkImage: 加载活动图片
        activate LarkImage
        LarkImage-->>HeroCarousel: 图片加载完成
        deactivate LarkImage
    end
    
    HeroCarousel->>DOM: 渲染轮播卡片<br/>含徽章、日期、位置、按钮
    HeroCarousel->>DOM: 监听窗口resize事件
    
    deactivate HeroCarousel
    
    DOM-->>Page: 显示英雄轮播
Loading

Estimated code review effort

🎯 3 (中等) | ⏱️ ~20 分钟

审查重点

  1. Pages Router 数据获取 — 确认组件内 fetch on mount 是否应迁移至 getServerSidePropsgetStaticProps,避免客户端水合不匹配
  2. UI 文本国际化 — 验证所有用户可见文本(包括后备幻灯片、标签等)是否统一使用 t() 国际化函数
  3. TypeScript 强类型 — 检查 Activity 类型定义完整性、FC 泛型参数、各数据流类型一致性
  4. 性能优化 — 评估 ResizeObserver 使用是否恰当、LarkImage 的懒加载策略、是否需要 React.memo 防止无谓重渲染
  5. React Bootstrap 一致性 — 若使用了 Bootstrap 组件应统一调用方式

Suggested labels

enhancement

Suggested reviewers

  • TechQuery

Poem

🎠 轮播转转展英姿,
活动闪耀主页驻,
响应灵动如流水,
导航整洁显品味,
一抹渐变添诗意。 ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive 标题描述的是「黑客马拉松顶部栏重新设计」,但实际变更是添加首页事件横幅(HeroCarousel 组件);标题中的「aurora gradient style」与代码变更内容(添加轮播、导航栏防换行、样式模块)关联不够直接。 建议调整标题以更准确反映主要变更,例如「feat(home): 添加事件横幅轮播组件」或「refactor(home): 用 HeroCarousel 替换首页顶部横幅」。
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch labor-hackathon-2026
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch labor-hackathon-2026

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

@dethan3 dethan3 requested a review from TechQuery April 30, 2026 09:26
@dethan3 dethan3 added the feature New feature or request label Apr 30, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pages/index.tsx`:
- Line 69: Replace the hardcoded event name inside the span (className
styles.hackathonTopBarEventName) with a call to the i18n t() function and add a
corresponding translation key (e.g. "hackathon.eventName") to your locale files;
update pages/index.tsx to use t('hackathon.eventName') (or the chosen key) so
the visible text is localized and ensure the new key exists in all supported
locales.
- Around line 57-84: The top-banner uses raw HTML elements and a hard-coded
event name; replace the <aside>/<div>/<a>/<button>/<span> usage with React
Bootstrap components (e.g., Alert, Card, Button/CloseButton) while preserving
behavior tied to isHackathonTopBarVisible, hackathonTopBarStyle,
HackathonTopBarLink and the closeHackathonTopBar handler, and move the literal
"Labor AI Hackathon 2026" into i18n as home_hackathon_top_bar_event_name,
rendering it via t('home_hackathon_top_bar_event_name') alongside the existing
t('home_hackathon_top_bar_title'), t('home_hackathon_top_bar_description') and
t('home_hackathon_top_bar_action'); ensure aria-label/title still use
t('home_hackathon_top_bar_aria_label') and t('home_hackathon_top_bar_close').
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: afcd89f5-d1e7-40d2-bfc2-16789a915968

📥 Commits

Reviewing files that changed from the base of the PR and between b160b03 and 3b66c69.

⛔ Files ignored due to path filters (6)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml, !pnpm-lock.yaml and included by none
  • styles/Home.module.less is excluded by none and included by none
  • styles/globals.css is excluded by none and included by none
  • translation/en-US.ts is excluded by none and included by none
  • translation/zh-CN.ts is excluded by none and included by none
  • translation/zh-TW.ts is excluded by none and included by none
📒 Files selected for processing (1)
  • pages/index.tsx

Comment thread pages/index.tsx Outdated
Comment thread pages/index.tsx Outdated
Comment thread styles/globals.css Outdated
Comment thread translation/zh-CN.ts
Comment on lines +34 to +37
home_hackathon_top_bar_aria_label: 'Labor AI Hackathon 2026 活动宣传',
home_hackathon_top_bar_badge: '活动招募中',
home_hackathon_top_bar_title: '打工人の终极反叛',
home_hackathon_top_bar_description: '48 小时,来这里创造未来',
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

不要硬编码任何来自数据库的数据。

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

是指从多维表格中取得数据吗?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

是指从多维表格中取得数据吗?

是,直接照抄我以前写的代码:
https://github.com/kaiyuanshe/kaiyuanshe.github.io/blob/a9afddbee79d1e3fe501d491d2aa7f0699ac56c9/pages/index.tsx#L99-L124

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

我尝试了代码复用,但是没有拿到 name 的数据,麻烦 @TechQuery 再帮我看一下代码

Comment thread pages/index.tsx
- Replace raw HTML with Alert, Alert.Link, CloseButton

- Move complex styles from styled-jsx to PromoBar.module.less

- Add Bootstrap utility classes for layout

- Fetch activity name/link from Lark Bitable via ActivityModel

- Keep localStorage persistence commented out
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/Activity/PromoBar.tsx`:
- Around line 41-50: 在 useEffect 中对异步加载添加卸载保护:在 useEffect 里创建一个标志(例如 let mounted
= true 或使用 AbortController 如果 ActivityModel.getOne 支持取消),在 getOne
调用前保留该标志/控制器,并在 Promise resolve 前检查 mounted,只有 mounted 为 true 时才调用 setActivity;在
useEffect 返回的 cleanup 函数将 mounted 设为 false(或调用
controller.abort())以阻止组件卸载后继续更新或消耗请求资源。确保引用的符号为 useEffect、ActivityModel.getOne 和
setActivity。
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4a3b4906-bb3f-4728-9d11-7adac39ad05a

📥 Commits

Reviewing files that changed from the base of the PR and between 3b66c69 and ed2cf15.

📒 Files selected for processing (4)
  • components/Activity/PromoBar.module.less
  • components/Activity/PromoBar.tsx
  • components/Navigator/MainNavigator.tsx
  • pages/index.tsx
✅ Files skipped from review due to trivial changes (3)
  • pages/index.tsx
  • components/Activity/PromoBar.module.less
  • components/Navigator/MainNavigator.tsx

Comment thread components/Activity/PromoBar.tsx Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/Activity/HeroCarousel.module.less`:
- Around line 14-47: The stylelint error is caused by CSS Modules :global()
being treated as an unknown pseudo-class; update the linter config to allow it
by adding an exception for "global" to the selector-pseudo-class-no-unknown rule
(e.g., set "selector-pseudo-class-no-unknown": ["error", {
"ignorePseudoClasses": ["global"] }]) in .stylelintrc.json, or alternatively
enable Less parsing by adding customSyntax: "postcss-less" (and installing
postcss-less) so Less files and CSS Modules :global() are recognized; adjust the
config rather than changing components/Activity/HeroCarousel.module.less.

In `@components/Activity/HeroCarousel.tsx`:
- Around line 73-90: The useEffect in HeroCarousel (the async IIFE that
constructs ActivityModel, calls model.getAll(), filters/sorts/slices to
MAX_ITEMS and then calls setActivities) can update state after the component
unmounts; add an unmount guard: declare a local mounted/isActive flag before the
async work, check it before calling setActivities, and set the flag to false in
the effect cleanup returned function so the state update is skipped when the
component has been unmounted.
- Around line 103-107: Replace the native <section> root in HeroCarousel.tsx
with React Bootstrap's Container using as="section" to preserve semantics;
update the JSX root where the element with className={`${styles.heroCarousel}
position-relative`} aria-label={t('home_hackathon_top_bar_aria_label')}
style={heroStyle} is defined to use <Container as="section" ...> and the
matching closing tag, and add the Container import from 'react-bootstrap' if
missing so the component follows the project's React Bootstrap convention.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d51b74c7-7d3b-4b10-b791-0270372de013

📥 Commits

Reviewing files that changed from the base of the PR and between ed2cf15 and 51d09ee.

⛔ Files ignored due to path filters (1)
  • tsconfig.json is excluded by none and included by none
📒 Files selected for processing (3)
  • components/Activity/HeroCarousel.module.less
  • components/Activity/HeroCarousel.tsx
  • pages/index.tsx
✅ Files skipped from review due to trivial changes (1)
  • pages/index.tsx

Comment on lines +14 to +47
:global(.carousel-inner) {
min-height: inherit;
}

:global(.carousel-item) {
min-height: inherit;
}

:global(.carousel-indicators) {
right: auto;
bottom: 2rem;
left: 1rem;
justify-content: flex-start;
margin: 0;
}

:global(.carousel-indicators button) {
opacity: 0.9;
margin: 0 0.45rem 0 0;
border: 0;
border-radius: 999px;
background-color: rgb(255 255 255 / 65%);
width: 2.25rem;
height: 0.32rem;
}

:global(.carousel-control-prev),
:global(.carousel-control-next) {
opacity: 0.95;
width: 6rem;
}

:global(.carousel-control-prev-icon),
:global(.carousel-control-next-icon) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== 查找 stylelint 配置文件 =="
fd -HI -i 'stylelint' .
fd -HI -i '.stylelintrc' .

echo
echo "== 检查是否已允许 :global 或配置了 less/css-modules 语法 =="
rg -n --iglob '*stylelint*' 'selector-pseudo-class-no-unknown|ignorePseudoClasses|global|customSyntax|postcss-less|css-modules'

Repository: Open-Source-Bazaar/Open-Source-Bazaar.github.io

Length of output: 267


🏁 Script executed:

cat .stylelintrc.json

Repository: Open-Source-Bazaar/Open-Source-Bazaar.github.io

Length of output: 1451


🏁 Script executed:

# Check if postcss-less is in package.json
rg -A 2 -B 2 'postcss-less' package.json

# Check for .stylelintignore
cat .stylelintignore 2>/dev/null || echo ".stylelintignore not found"

# Check package.json for stylelint and postcss dependencies
rg 'stylelint|postcss' package.json | head -20

Repository: Open-Source-Bazaar/Open-Source-Bazaar.github.io

Length of output: 118


🏁 Script executed:

# Check if HeroCarousel.module.less exists
ls -la components/Activity/HeroCarousel.module.less 2>/dev/null && echo "File exists" || echo "File not found"

# Check package.json for stylelint scripts and dependencies
head -200 package.json | grep -A 5 -B 5 'stylelint\|scripts'

# Search for any mentions of less or module in package.json
rg 'less|module' package.json | head -20

Repository: Open-Source-Bazaar/Open-Source-Bazaar.github.io

Length of output: 806


🏁 Script executed:

head -50 components/Activity/HeroCarousel.module.less

Repository: Open-Source-Bazaar/Open-Source-Bazaar.github.io

Length of output: 1032


stylelint 配置缺失 CSS Modules :global() 支持,需调整规则

components/Activity/HeroCarousel.module.less 的第 14-47 行和 123-137 行使用了 CSS Modules 的 :global() 选择器来样式化 Bootstrap carousel 组件。当前 .stylelintrc.json 扩展的是 stylelint-config-standard-scss,未配置 Less 语法支持或为 selector-pseudo-class-no-unknown 规则添加 ignorePseudoClasses: ['global'] 设置。这会导致 stylelint 将 :global() 视为未知伪类,触发 lint 错误并阻塞 CI。

建议在 .stylelintrc.json 中修改:

"rules": {
  "selector-pseudo-class-no-unknown": ["error", { "ignorePseudoClasses": ["global"] }]
}

或配置 Less 语法支持(安装 postcss-less 并在配置中添加 customSyntax)。

🧰 Tools
🪛 Stylelint (17.9.0)

[error] 14-14: Unknown pseudo-class selector ":global" (selector-pseudo-class-no-unknown)

(selector-pseudo-class-no-unknown)


[error] 18-18: Unknown pseudo-class selector ":global" (selector-pseudo-class-no-unknown)

(selector-pseudo-class-no-unknown)


[error] 22-22: Unknown pseudo-class selector ":global" (selector-pseudo-class-no-unknown)

(selector-pseudo-class-no-unknown)


[error] 30-30: Unknown pseudo-class selector ":global" (selector-pseudo-class-no-unknown)

(selector-pseudo-class-no-unknown)


[error] 40-40: Unknown pseudo-class selector ":global" (selector-pseudo-class-no-unknown)

(selector-pseudo-class-no-unknown)


[error] 41-41: Unknown pseudo-class selector ":global" (selector-pseudo-class-no-unknown)

(selector-pseudo-class-no-unknown)


[error] 46-46: Unknown pseudo-class selector ":global" (selector-pseudo-class-no-unknown)

(selector-pseudo-class-no-unknown)


[error] 47-47: Unknown pseudo-class selector ":global" (selector-pseudo-class-no-unknown)

(selector-pseudo-class-no-unknown)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/Activity/HeroCarousel.module.less` around lines 14 - 47, The
stylelint error is caused by CSS Modules :global() being treated as an unknown
pseudo-class; update the linter config to allow it by adding an exception for
"global" to the selector-pseudo-class-no-unknown rule (e.g., set
"selector-pseudo-class-no-unknown": ["error", { "ignorePseudoClasses":
["global"] }]) in .stylelintrc.json, or alternatively enable Less parsing by
adding customSyntax: "postcss-less" (and installing postcss-less) so Less files
and CSS Modules :global() are recognized; adjust the config rather than changing
components/Activity/HeroCarousel.module.less.

Comment on lines +73 to +90
useEffect(() => {
(async () => {
try {
const model = new ActivityModel();
const data = await model.getAll();
const latestActivities = data
.filter(({ name }) => Boolean(name))
.sort(
({ startTime: left }, { startTime: right }) => timestampOf(right) - timestampOf(left),
)
.slice(0, MAX_ITEMS);

setActivities(latestActivities);
} catch (err) {
console.error('Failed to load activities:', err);
}
})();
}, []);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

为异步加载 effect 增加卸载保护,避免卸载后更新状态

Line 73-90 的异步请求完成后直接 setActivities,组件已卸载时仍可能触发状态更新。建议加取消标记。

建议修改
   useEffect(() => {
+    let cancelled = false;
     (async () => {
       try {
         const model = new ActivityModel();
         const data = await model.getAll();
         const latestActivities = data
           .filter(({ name }) => Boolean(name))
           .sort(
             ({ startTime: left }, { startTime: right }) => timestampOf(right) - timestampOf(left),
           )
           .slice(0, MAX_ITEMS);

-        setActivities(latestActivities);
+        if (!cancelled) setActivities(latestActivities);
       } catch (err) {
         console.error('Failed to load activities:', err);
       }
     })();
+
+    return () => {
+      cancelled = true;
+    };
   }, []);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/Activity/HeroCarousel.tsx` around lines 73 - 90, The useEffect in
HeroCarousel (the async IIFE that constructs ActivityModel, calls
model.getAll(), filters/sorts/slices to MAX_ITEMS and then calls setActivities)
can update state after the component unmounts; add an unmount guard: declare a
local mounted/isActive flag before the async work, check it before calling
setActivities, and set the flag to false in the effect cleanup returned function
so the state update is skipped when the component has been unmounted.

Comment on lines +103 to +107
<section
className={`${styles.heroCarousel} position-relative`}
aria-label={t('home_hackathon_top_bar_aria_label')}
style={heroStyle}
>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

根节点建议改为 React Bootstrap 组件承载语义 section

Line 103 当前使用原生 <section>,与组件层统一使用 React Bootstrap 的规范不一致。可用 Container 并通过 as="section" 保留语义。

建议修改
-    <section
-      className={`${styles.heroCarousel} position-relative`}
-      aria-label={t('home_hackathon_top_bar_aria_label')}
-      style={heroStyle}
-    >
+    <Container
+      as="section"
+      fluid
+      className={`${styles.heroCarousel} position-relative`}
+      aria-label={t('home_hackathon_top_bar_aria_label')}
+      style={heroStyle}
+    >
...
-    </section>
+    </Container>

As per coding guidelines, “{pages,components}/**/*.tsx: ALWAYS use React Bootstrap components instead of custom HTML elements in UI code”.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<section
className={`${styles.heroCarousel} position-relative`}
aria-label={t('home_hackathon_top_bar_aria_label')}
style={heroStyle}
>
<Container
as="section"
fluid
className={`${styles.heroCarousel} position-relative`}
aria-label={t('home_hackathon_top_bar_aria_label')}
style={heroStyle}
>
{/* content */}
</Container>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/Activity/HeroCarousel.tsx` around lines 103 - 107, Replace the
native <section> root in HeroCarousel.tsx with React Bootstrap's Container using
as="section" to preserve semantics; update the JSX root where the element with
className={`${styles.heroCarousel} position-relative`}
aria-label={t('home_hackathon_top_bar_aria_label')} style={heroStyle} is defined
to use <Container as="section" ...> and the matching closing tag, and add the
Container import from 'react-bootstrap' if missing so the component follows the
project's React Bootstrap convention.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants