Add [Open Library] & Library Work Flow#20
Conversation
TechQuery
left a comment
There was a problem hiding this comment.
- 很多问题在不同文件都有,不一一列举,但都要改
- 软件开发要一个一个模块来,代码量太大对开发者、reviewer、AI 都有巨大上下文负担,很容易跑偏、卡死
- 以后要在看板上多拆几个 issue,不要闷头搞很久后发一个差不多做完整个项目的巨大 PR……
| {book.reviews && book.reviews.length > 0 ? ( | ||
| <div> |
There was a problem hiding this comment.
| {book.reviews && book.reviews.length > 0 ? ( | |
| <div> | |
| {book.reviews?.[0] ? ( | |
| <> |
| <div className="table-responsive"> | ||
| <table className="table table-hover"> |
There was a problem hiding this comment.
# Conflicts: # .husky/pre-commit # .husky/pre-push # package.json # pages/_app.tsx # translation/en-US.ts # translation/zh-CN.ts # translation/zh-TW.ts
📝 WalkthroughWalkthrough新增完整的 Open Library 微站,包含独立导航、共享布局、书卡组件、首页、书目录页、书详情页与借阅说明页,并将其集成进 ChangesOpen Library 微站
现有代码格式化与类型修复
Sequence Diagram(s)sequenceDiagram
participant Browser as 浏览器
participant _app as _app.tsx
participant Layout as Layout
participant LibraryNavbar as LibraryNavbar
participant Page as Open Library 页面
participant FooterComponent as FooterComponent
Browser->>"_app": 访问 /open-library/*
"_app"->>"_app": isOpenLibraryPath = true
"_app"->>Layout: 渲染(跳过主站 MainNavigator 与主站 Footer)
Layout->>LibraryNavbar: 渲染独立导航(isMounted 防水合)
Layout->>Page: 渲染 children(首页/书目录/书详情/借阅说明)
Layout->>FooterComponent: 渲染 Open Library 专属页脚
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 3❌ Failed checks (2 warnings, 1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 15
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
components/Activity/Hackathon/theme.less (2)
132-143:⚠️ Potential issue | 🔴 Critical | ⚡ Quick win
.button-ghost()同样缺失.button-base()调用与
.button-primary()相同的问题,Line 133 的空语句导致 ghost 按钮丢失所有基础样式(flex 布局、padding、border-radius、字体、hover 动效等)。🐛 建议恢复 mixin 调用
.button-ghost() { - ; + .button-base(); border-color: rgba(255, 255, 255, 0.14);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@components/Activity/Hackathon/theme.less` around lines 132 - 143, The `.button-ghost()` mixin in the theme.less file contains an empty statement (a standalone semicolon) on the first line inside the mixin definition. Replace this empty statement with a call to the `.button-base()` mixin to ensure ghost buttons inherit all the foundational styles including flex layout, padding, border-radius, fonts, and transition effects. This will restore the missing base styling that ghost buttons currently lack.
118-130:⚠️ Potential issue | 🔴 Critical | ⚡ Quick win
.button-primary()缺失.button-base()调用,按钮将丢失基础样式
.button-base()提供了按钮的核心样式:display: inline-flex、justify-content、align-items、gap、transition、border-radius、padding、font系列属性以及&:hover的transform效果。当前 Line 119 的
;是空语句,导致Hero.module.less和ActionHub.module.less中调用.button-primary()的按钮将缺失这些基础布局与交互样式。🐛 建议恢复 mixin 调用
.button-primary() { - ; + .button-base(); box-shadow: 0 0 24px rgba(44, 232, 255, 0.18);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@components/Activity/Hackathon/theme.less` around lines 118 - 130, The .button-primary() mixin in theme.less is missing a required call to the .button-base() mixin. Currently, there is only an empty semicolon on line 119, which means buttons using .button-primary() are losing essential base styles like display, flexbox properties, transitions, border-radius, padding, and hover transform effects. Replace the empty semicolon in the .button-primary() mixin body with a call to .button-base() to restore these foundational styles that are needed by buttons in Hero.module.less and ActionHub.module.less.
♻️ Duplicate comments (1)
pages/open-library/index.tsx (1)
210-224:⚠️ Potential issue | 🟠 Major | ⚡ Quick win“支持我们”区块请改为 React Bootstrap 按钮组件而非原生
a/span伪按钮。Line 210-224 当前实现破坏了 UI 组件一致性,也影响可维护性与可访问性语义。
As per coding guidelines, “ALWAYS use React Bootstrap components instead of custom HTML elements in UI code”.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pages/open-library/index.tsx` around lines 210 - 224, The "支持我们" (Support Us) section uses native HTML elements (the `a` tag for GitHub Sponsors and the `span` within `Link` for book sharing) styled as buttons with className attributes, which violates the React Bootstrap component guideline. Replace the `a` element containing "GitHub Sponsors" with a React Bootstrap Button component using appropriate props for href, variant, and size instead of className. Similarly, replace the `Link` wrapper and `span` element containing "分享你的图书" with a React Bootstrap Button component, ensuring the Link functionality is preserved if needed. Import Button from react-bootstrap at the top of the file and apply consistent button variants (such as light and outline-light) using the variant prop instead of className.Source: Coding guidelines
🧹 Nitpick comments (1)
pages/open-library/index.tsx (1)
104-113: ⚡ Quick win建议复用
components/open-library/Layout.tsx,避免页面级重复装配导航/页脚/样式 Hook。当前首页手动拼装
LibraryNavbar + useOpenLibraryLayout + FooterComponent,与共享Layout的职责重复,后续容易出现页间不一致。As per coding guidelines, “Use minimal exports and avoid unnecessary custom implementations”.
Also applies to: 231-231
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pages/open-library/index.tsx` around lines 104 - 113, The page is manually assembling LibraryNavbar, useOpenLibraryLayout hook, and FooterComponent instead of using the shared Layout component from components/open-library/Layout.tsx. Remove the individual LibraryNavbar, useOpenLibraryLayout hook usage, and FooterComponent composition from the page, and instead wrap the page content with the Layout component to eliminate duplication and ensure consistency across pages. Apply this same refactoring to all locations where this manual assembly occurs, including the location at line 231.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@components/Activity/Hackathon/theme.less`:
- Around line 31-51: The `.section-frame()` mixin is missing its internal mixin
calls and only contains empty statements. Replace the empty semicolons in the
nested selectors `.section`, `.sectionHeader`, `.sectionTitle`,
`.sectionSubtitle`, and `.accentLine` with their corresponding mixin calls
(e.g., `.section()` for the `.section` selector, `.sectionHeader()` for the
`.sectionHeader` selector, and so on). This will restore the style generation
for these elements that downstream files like `Overview.module.less` depend on
when calling `.section-frame()`.
In `@components/Layout/CardPage.tsx`:
- Around line 6-10: Replace the overly broad `Record<string, unknown>` type for
the Card property in CardPageProps with a precise union type that accurately
reflects the actual prop signatures of ActivityCard, ProjectCard, and
OrganizationCard. Alternatively, update each card component to properly accept
and handle the className and linkOf props that CardPage.tsx is passing on line
24, rather than ignoring them. Choose the approach that best aligns with your
component design intent: either define Card as a union of the three specific
card component types with their actual prop interfaces, or extend each card
component's prop interface to include className and linkOf parameters.
In `@components/Navigator/MainNavigator.tsx`:
- Around line 84-87: The menu item object for the open-library navigation route
uses the incorrect field name `name` when the MenuItem interface contract
requires the field to be named `title`. Change the property name from `name` to
`title` in the navigation item object that contains href set to '/open-library'.
This will align with the MenuItem type definition and ensure the navigation
title is properly rendered.
In `@components/open-library/BookCard.tsx`:
- Line 47: The hardcoded Chinese strings on line 47 (the conditional rendering
of '可借阅' and '已借出' based on book.status) and the alt text on line 57 are not
using the i18n t() function as required by the coding guidelines. Replace these
hardcoded user-facing strings by wrapping them with the t() function and
providing appropriate i18n keys for each string. This applies to the status
display conditional in the book.status ternary operator and any image alt text
attributes, ensuring all user-visible and accessibility-related text goes
through the translation function to avoid hardcoding and enable proper
localization.
In `@components/open-library/FeaturedBooks.tsx`:
- Around line 19-24: The FeaturedBooks component has hardcoded Chinese strings
as default values for title, subtitle, and viewAllText parameters, which
bypasses internationalization. Replace these hardcoded strings with i18n
translations by either accepting already-translated strings as parameters from
the calling component, or by integrating the I18nContext within the
FeaturedBooks component to call the t() function directly for these default
values. Ensure all user-facing text in the component uses the i18n t() function
to comply with the coding guidelines.
In `@components/open-library/Footer.tsx`:
- Around line 26-29: The Footer component contains multiple hardcoded
user-facing text strings that violate the i18n requirement. Replace all
hardcoded text with t() function calls using appropriate i18n keys: the social
media platform names (GitHub, Twitter, Feishu) around the mentioned lines, the
contact information section (联系信息) in the range specified, and
copyright/placeholder text. Ensure each hardcoded string is replaced with a
corresponding t() call and that i18n keys follow the existing naming convention
used in the component for consistency.
- Around line 31-51: The social media links in the Footer component use
placeholder href values (`#github`, `#twitter`, `#feishu`) that do not navigate to
actual external URLs. Replace the href attributes in the GitHub link, Twitter
link, and Feishu link with the real external URLs to their respective platforms,
or conditionally hide these link elements if the actual URLs are not yet
available in the application configuration.
In `@components/open-library/HeroSection.tsx`:
- Around line 16-22: The HeroSection component contains hardcoded user-facing
text strings that violate the i18n requirement. Replace all hardcoded Chinese
strings with i18n t() function calls: the default parameter values for title,
subtitle, ctaText, and heroImageAlt in the component function signature need to
use t() instead of literal Chinese strings, and the "浏览图书" text on line 59 also
needs to be wrapped with t(). Ensure all user-visible text strings are
internationalized consistently throughout the HeroSection component.
In `@components/open-library/HowItWorks.tsx`:
- Around line 23-57: The HowItWorks component has hardcoded Chinese text strings
that violate the i18n requirement. Replace all hardcoded strings in the
defaultSteps array (the title and description fields for each step object), the
default values for the title and subtitle parameters in the HowItWorksProps
destructuring, and the button text (mentioned as being on line 151) with
appropriate i18n keys wrapped in the t() function. Import and use the i18n
translation function to wrap these hardcoded strings with their corresponding
translation keys for proper internationalization support.
In `@components/open-library/Layout.tsx`:
- Around line 21-33: The default page title in the Layout component is hardcoded
as a string instead of using internationalization. Replace the hardcoded default
title value in the title parameter (currently set to 'Open Library - Open Source
Bazaar') with a call to the i18n t() function, using an appropriate translation
key like t('open_library_page_title'). This will ensure the page title changes
dynamically based on the user's language preference and complies with the coding
guideline that all user-facing text must use the i18n t() function.
In `@components/open-library/Navbar.tsx`:
- Line 31: The ternary expression in Navbar.tsx contains a hardcoded fallback
string 'Open Library' that violates the i18n constraint requiring all
user-facing text to use the t() function. Replace the hardcoded string 'Open
Library' in the false branch of the ternary operator with t('open_library') so
that both branches consistently use the translation function, ensuring proper
hydration-safe i18n handling throughout the component.
In `@pages/open-library/book/`[id].tsx:
- Around line 217-224: All user-facing text strings in the book details page
component must use the i18n t() function instead of being hardcoded. Replace all
hardcoded strings (including "Loading...", "Book Not Found", "Back", "Reviews",
"Borrow History", "Returned/Active" and others at the specified line ranges:
217-224, 234-244, 260, 282-283, 288, 294, 309-323, 335, 339, 350, 382, 389, 397,
402, 408-412, 432, 435, 446) with t() function calls using appropriate i18n
keys. First define all the necessary translation keys in your i18n
configuration, then systematically update each hardcoded string reference to use
t(keyName) instead.
In `@pages/open-library/books/index.tsx`:
- Around line 118-121: Replace all hardcoded Chinese text strings in the book
directory page with i18n t() function calls to enable proper language switching.
Locate the hardcoded strings in the h1 element containing "图书目录", the p element
containing "浏览我们的社区共享图书馆,发现有趣的书籍", and also check lines 143, 164, and 172 for
additional hardcoded text (titles, status messages, hints, or button labels).
For each hardcoded string, wrap it with the t() function using an appropriate
translation key that describes the content, ensuring all user-facing text
follows the i18n guidelines and the page properly reflects language changes when
users switch locales.
In `@pages/open-library/how-to-borrow.tsx`:
- Around line 9-13: The page contains hardcoded user-facing text like "如何借阅"
that should be internationalized. Replace all hardcoded strings throughout the
component (including the h1 title and all content in the mentioned line ranges)
with the t() function calls from the i18n library. Create corresponding
translation keys in the language resource files and ensure each hardcoded string
(titles, descriptions, process steps, rules, FAQs, and CTAs) is replaced with
t('keyName') calls, maintaining consistency with the project's i18n
implementation used in other pages.
In `@pages/open-library/index.tsx`:
- Around line 124-129: Replace all hardcoded Chinese text strings in the
component with the i18n `t()` function to comply with internationalization
standards. In the affected sections (lines 124-129, 135-156, 170-173, 202-208,
and 222-223), wrap every user-facing text string including titles like "📚
关于我们", descriptions like "freeCodeCamp 成都社区「Open Library」开放共享图书馆", list items,
and button labels with the `t()` function. Ensure each string has a
corresponding key defined in your i18n translation files.
---
Outside diff comments:
In `@components/Activity/Hackathon/theme.less`:
- Around line 132-143: The `.button-ghost()` mixin in the theme.less file
contains an empty statement (a standalone semicolon) on the first line inside
the mixin definition. Replace this empty statement with a call to the
`.button-base()` mixin to ensure ghost buttons inherit all the foundational
styles including flex layout, padding, border-radius, fonts, and transition
effects. This will restore the missing base styling that ghost buttons currently
lack.
- Around line 118-130: The .button-primary() mixin in theme.less is missing a
required call to the .button-base() mixin. Currently, there is only an empty
semicolon on line 119, which means buttons using .button-primary() are losing
essential base styles like display, flexbox properties, transitions,
border-radius, padding, and hover transform effects. Replace the empty semicolon
in the .button-primary() mixin body with a call to .button-base() to restore
these foundational styles that are needed by buttons in Hero.module.less and
ActionHub.module.less.
---
Duplicate comments:
In `@pages/open-library/index.tsx`:
- Around line 210-224: The "支持我们" (Support Us) section uses native HTML elements
(the `a` tag for GitHub Sponsors and the `span` within `Link` for book sharing)
styled as buttons with className attributes, which violates the React Bootstrap
component guideline. Replace the `a` element containing "GitHub Sponsors" with a
React Bootstrap Button component using appropriate props for href, variant, and
size instead of className. Similarly, replace the `Link` wrapper and `span`
element containing "分享你的图书" with a React Bootstrap Button component, ensuring
the Link functionality is preserved if needed. Import Button from
react-bootstrap at the top of the file and apply consistent button variants
(such as light and outline-light) using the variant prop instead of className.
---
Nitpick comments:
In `@pages/open-library/index.tsx`:
- Around line 104-113: The page is manually assembling LibraryNavbar,
useOpenLibraryLayout hook, and FooterComponent instead of using the shared
Layout component from components/open-library/Layout.tsx. Remove the individual
LibraryNavbar, useOpenLibraryLayout hook usage, and FooterComponent composition
from the page, and instead wrap the page content with the Layout component to
eliminate duplication and ensure consistency across pages. Apply this same
refactoring to all locations where this manual assembly occurs, including the
location at line 231.
🪄 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: 0b924692-11c8-4d01-b037-fb6acd2a8a56
⛔ Files ignored due to path filters (10)
.github/scripts/transform-message.tsis excluded by none and included by none.github/workflows/self-scan.ymlis excluded by none and included by none.husky/pre-commitis excluded by none and included by none.husky/pre-pushis excluded by none and included by nonepackage.jsonis excluded by none and included by nonepages/open-library/readme.mdis excluded by!**/*.mdand included bypages/**styles/open-library.cssis excluded by none and included by nonetranslation/en-US.tsis excluded by none and included by nonetranslation/zh-CN.tsis excluded by none and included by nonetranslation/zh-TW.tsis excluded by none and included by none
📒 Files selected for processing (23)
components/Activity/Hackathon/Hero.tsxcomponents/Activity/Hackathon/Resources.tsxcomponents/Activity/Hackathon/Schedule.tsxcomponents/Activity/Hackathon/theme.lesscomponents/Layout/CardPage.tsxcomponents/License/helper.tscomponents/Map/ChinaMap.tsxcomponents/Navigator/MainNavigator.tsxcomponents/Navigator/SearchBar.tsxcomponents/Organization/Card.tsxcomponents/open-library/BookCard.tsxcomponents/open-library/FeaturedBooks.tsxcomponents/open-library/Footer.tsxcomponents/open-library/HeroSection.tsxcomponents/open-library/HowItWorks.tsxcomponents/open-library/Layout.tsxcomponents/open-library/Navbar.tsxcomponents/open-library/useOpenLibraryLayout.tspages/_app.tsxpages/open-library/book/[id].tsxpages/open-library/books/index.tsxpages/open-library/how-to-borrow.tsxpages/open-library/index.tsx
…n, types, mixin fixes
i18n / hardcoded strings:
- BookCard: replace hardcoded status text and cover alt with t()
- FeaturedBooks: add observer+useContext, replace hardcoded Chinese defaults with t()
- HowItWorks: add observer+useContext, move defaultSteps inside component, replace all hardcoded strings with t()
- Footer: fully i18n all text, replace placeholder hrefs with real URLs
- HeroSection: make all text props required, eliminate hardcoded defaults
- Navbar: replace hardcoded fallback with t('open_library')
- books/index.tsx: replace hardcoded subtitle with t()
- how-to-borrow.tsx: add observer+useContext, replace all section headings and buttons with t()
- index.tsx: replace all hardcoded strings in About/Support sections with t()
- book/[id].tsx: replace Publisher/Published/Pages/N/A labels with t()
translation keys: add ~20 new keys across zh-CN, en-US, zh-TW
(book_cover, learn_more_details, book_catalog_description, ready_to_borrow,
open_library_community_intro, our_mission_text, core_concepts, knowledge_flow,
knowledge_flow_desc, community_driven, community_driven_desc, open_sharing,
open_sharing_desc, support_us, support_us_description, share_your_book,
book_publisher, book_published_year, book_page_count, not_available)
type / style fixes:
- CardPage.tsx: replace Record<string, unknown> with ComponentType<any> to fix
pre-existing TS2322 errors where FC<ActivityCardProps|Project|OrganizationCardProps>
was not assignable to FC<Record<string,unknown>> under strict mode
- theme.less: fill empty semicolons in .section-frame() with correct mixin
calls; add missing .button-base() to .button-primary() and .button-ghost()
| .openLibrary { | ||
| margin: 0; | ||
| padding: 0; | ||
| width: 100%; | ||
| max-width: 100%; | ||
| overflow-x: hidden; | ||
| } |
| community_name: 'freeCodeCamp 成都社群', | ||
| community_location: '中國四川成都', | ||
| contact_email: 'team@fcc-cd.dev', | ||
| contact_address: '中國四川成都', | ||
| contact_wechat: 'freeCodeCamp 成都社群', |
There was a problem hiding this comment.
- 重复的翻译文本写一次就行了
- 邮箱直接硬编码,无需写在翻译文件
There was a problem hiding this comment.
| <Button | ||
| as="a" | ||
| href="https://github.com/sponsors/your-org" | ||
| className="btn btn-light btn-lg rounded-pill px-4" | ||
| variant="light" | ||
| size="lg" | ||
| className="rounded-pill px-4" | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| > | ||
| <i className="bi bi-heart-fill me-2" /> | ||
| GitHub Sponsors | ||
| </a> | ||
| <Link href="/open-library/books"> | ||
| <span className="btn btn-outline-light btn-lg rounded-pill px-4"> | ||
| <i className="bi bi-book me-2" /> | ||
| 分享你的图书 | ||
| </span> | ||
| ❤️ GitHub Sponsors | ||
| </Button> |
There was a problem hiding this comment.
我们目前没有开通赞助渠道,这个链接也不对,放在这里是干嘛的?
| Card: ComponentClass<any> | FC<any>; | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| Card: ComponentType<any>; |
| </p> | ||
| </footer> | ||
| {isActivityPage | ||
| ? this.renderPageContent(Component, pageProps) |
There was a problem hiding this comment.
| ? this.renderPageContent(Component, pageProps) | |
| ? <Component {...pageProps} /> |
| interface BookDetailProps { | ||
| book: Book; | ||
| } | ||
|
|
||
| export const getServerSideProps: GetServerSideProps<BookDetailProps> = async ({ params }) => { | ||
| const id = Array.isArray(params?.id) ? params?.id[0] : params?.id, | ||
| bookId = Number(id); | ||
| const book = openLibraryBooks.find(({ id }) => id === bookId); | ||
|
|
||
| if (!book) return { notFound: true }; | ||
|
|
||
| return { props: { book } }; | ||
| }; | ||
|
|
||
| const BookDetail = observer(({ book }: BookDetailProps) => { |
There was a problem hiding this comment.
| interface BookDetailProps { | |
| book: Book; | |
| } | |
| export const getServerSideProps: GetServerSideProps<BookDetailProps> = async ({ params }) => { | |
| const id = Array.isArray(params?.id) ? params?.id[0] : params?.id, | |
| bookId = Number(id); | |
| const book = openLibraryBooks.find(({ id }) => id === bookId); | |
| if (!book) return { notFound: true }; | |
| return { props: { book } }; | |
| }; | |
| const BookDetail = observer(({ book }: BookDetailProps) => { | |
| export const getServerSideProps: GetServerSideProps<Book> = async ({ params }) => { | |
| const bookId = +(params!.id + ''); | |
| const book = openLibraryBooks.find(({ id }) => id === bookId); | |
| return book ? { props: book } : { notFound: true }; | |
| }; | |
| const BookDetail: FC<BookDetailProps> = observer(({ title, cover }) => { |
运行时这个页面能进入,有且只有一个 ID。
| <Card className="border-0 shadow-sm"> | ||
| <Card.Body> |
There was a problem hiding this comment.
| <Card className="border-0 shadow-sm"> | |
| <Card.Body> | |
| <Card className="border-0 shadow-sm" body> |
| <Card key={index} className="mb-3 shadow-sm"> | ||
| <Card.Body> | ||
| <div className="d-flex justify-content-between align-items-center mb-2"> | ||
| <h5 className="mb-0">{review.reviewer}</h5> | ||
| <div> | ||
| {[...Array(5)].map((_, i) => ( | ||
| <span key={i} className="text-warning"> | ||
| {i < review.rating ? '\u2605' : '\u2606'} | ||
| </span> | ||
| ))} | ||
| </div> | ||
| </div> | ||
| <Card.Text>{review.comment}</Card.Text> | ||
| <Card.Text> | ||
| <small className="text-muted"> |
There was a problem hiding this comment.
| <Card key={index} className="mb-3 shadow-sm"> | |
| <Card.Body> | |
| <div className="d-flex justify-content-between align-items-center mb-2"> | |
| <h5 className="mb-0">{review.reviewer}</h5> | |
| <div> | |
| {[...Array(5)].map((_, i) => ( | |
| <span key={i} className="text-warning"> | |
| {i < review.rating ? '\u2605' : '\u2606'} | |
| </span> | |
| ))} | |
| </div> | |
| </div> | |
| <Card.Text>{review.comment}</Card.Text> | |
| <Card.Text> | |
| <small className="text-muted"> | |
| <Card key={index} className="mb-3 shadow-sm" body> | |
| <div className="d-flex justify-content-between align-items-center mb-2"> | |
| <h5 className="mb-0">{review.reviewer}</h5> | |
| <ol className="list-unstyled"> | |
| {[...Array(5)].map((_, i) => ( | |
| <li key={i} className="text-warning"> | |
| {i < review.rating ? '\u2605' : '\u2606'} | |
| </li> | |
| ))} | |
| </ol> | |
| </div> | |
| <Card.Text>{review.comment}</Card.Text> | |
| <Card.Text className="small text-muted"> |
| description: ( | ||
| <> | ||
| 社区成员可以在{' '} | ||
| <a href={catalogURL} target="_blank" rel="noopener noreferrer"> | ||
| fCC 成都社区图书馆 | ||
| </a>{' '} | ||
| 中查找自己感兴趣的书籍,或者在我们的 <Link href="/open-library/books">书籍目录</Link>{' '} | ||
| 中浏览。 | ||
| </> | ||
| ), |
There was a problem hiding this comment.
用函数式 i18n 翻译键来拼接外部传入的链接字符串,链接、加粗等文本格式的最终渲染可用 marked() 转换。
Checklist(清单):
Closes #15
Summary by CodeRabbit
版本发布说明
新功能
样式优化
其他改进