Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions components/Navigator/MainNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const topNavBarMenu = ({ t }: typeof i18n): MenuItem[] => [
title: t('open_source_projects'),
subs: [
{ href: '/project', title: t('self_developed_projects') },
{ href: '/library', title: t('open_library') },
{ href: '/search/project', title: t('bazaar_projects') },
{ href: '/issue', title: 'GitHub issues' },
{ href: '/license-filter', title: t('license_filter') },
Expand Down
249 changes: 249 additions & 0 deletions pages/library.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
import { observer } from 'mobx-react';
import Link from 'next/link';
import { FC, useContext } from 'react';
import { Badge, Button, Card, Col, Container, ListGroup, Row, Stack } from 'react-bootstrap';

import { PageHead } from '../components/Layout/PageHead';
import { I18nContext, I18nKey } from '../models/Translation';

type LibraryStatus = 'available' | 'borrowed';

interface LibraryBook {
id: string;
titleKey: I18nKey;
authorKey: I18nKey;
descriptionKey: I18nKey;
categoryKey: I18nKey;
status: LibraryStatus;
shelfCode: string;
borrowInfoKey: I18nKey;
}

const libraryBooks: LibraryBook[] = [
{
id: 'cathedral-bazaar',
titleKey: 'library_book_cathedral_title',
authorKey: 'library_book_cathedral_author',
descriptionKey: 'library_book_cathedral_description',
categoryKey: 'library_category_open_source',
status: 'available',
shelfCode: 'OS-001',
borrowInfoKey: 'library_book_available_note',
},
{
id: 'producing-oss',
titleKey: 'library_book_producing_oss_title',
authorKey: 'library_book_producing_oss_author',
descriptionKey: 'library_book_producing_oss_description',
categoryKey: 'library_category_community',
status: 'borrowed',
shelfCode: 'OS-002',
borrowInfoKey: 'library_book_borrowed_note',
},
{
id: 'pro-git',
titleKey: 'library_book_pro_git_title',
authorKey: 'library_book_pro_git_author',
descriptionKey: 'library_book_pro_git_description',
categoryKey: 'library_category_engineering',
status: 'available',
shelfCode: 'ENG-003',
borrowInfoKey: 'library_book_available_note',
},
{
id: 'community-playbook',
titleKey: 'library_book_community_playbook_title',
authorKey: 'library_book_community_playbook_author',
descriptionKey: 'library_book_community_playbook_description',
categoryKey: 'library_category_community',
status: 'available',
shelfCode: 'COM-004',
borrowInfoKey: 'library_book_available_note',
},
{
id: 'governance-guide',
titleKey: 'library_book_governance_title',
authorKey: 'library_book_governance_author',
descriptionKey: 'library_book_governance_description',
categoryKey: 'library_category_governance',
status: 'borrowed',
shelfCode: 'GOV-005',
borrowInfoKey: 'library_book_borrowed_note',
},
{
id: 'license-handbook',
titleKey: 'library_book_license_title',
authorKey: 'library_book_license_author',
descriptionKey: 'library_book_license_description',
categoryKey: 'library_category_license',
status: 'available',
shelfCode: 'LIC-006',
borrowInfoKey: 'library_book_available_note',
},
];

const guideStepKeys: I18nKey[] = [
'library_guide_step_1',
'library_guide_step_2',
'library_guide_step_3',
];

const statusVariant: Record<LibraryStatus, string> = {
available: 'success',
borrowed: 'secondary',
};

const statusLabelKey: Record<LibraryStatus, I18nKey> = {
available: 'library_status_available',
borrowed: 'library_status_borrowed',
};

const LibraryPage: FC = observer(() => {
const { t } = useContext(I18nContext);
const availableCount = libraryBooks.filter(({ status }) => status === 'available').length;
const borrowedCount = libraryBooks.length - availableCount;

return (
<Container className="py-5">
<PageHead title={t('open_library')} description={t('library_page_description')} />

<section className="py-5 text-center text-md-start">
<Badge bg="primary" pill className="mb-3">
{t('library_badge')}
</Badge>
<h1 className="display-5 fw-bold mb-3">{t('library_page_title')}</h1>
<p className="lead text-muted mb-4">{t('library_page_description')}</p>
<Stack
direction="horizontal"
gap={3}
className="justify-content-center justify-content-md-start"
>
<Button href="#library-catalog" variant="primary">
{t('library_browse_catalog')}
</Button>
<Button href="#borrow-guide" variant="outline-primary">
{t('library_borrow_guide')}
</Button>
</Stack>
</section>

<Row className="g-4 align-items-stretch mb-5">
<Col md={4}>
<Card body className="h-100 border-0 shadow-sm">
<Card.Subtitle className="text-muted mb-2">
{t('library_stat_total_label')}
</Card.Subtitle>
<Card.Title as="p" className="display-6 fw-bold mb-0">
{libraryBooks.length}
</Card.Title>
<Card.Text className="text-muted mt-2">{t('library_stat_total_desc')}</Card.Text>
</Card>
</Col>
<Col md={4}>
<Card body className="h-100 border-0 shadow-sm">
<Card.Subtitle className="text-muted mb-2">
{t('library_stat_available_label')}
</Card.Subtitle>
<Card.Title as="p" className="display-6 fw-bold mb-0 text-success">
{availableCount}
</Card.Title>
<Card.Text className="text-muted mt-2">{t('library_stat_available_desc')}</Card.Text>
</Card>
</Col>
<Col md={4}>
<Card body className="h-100 border-0 shadow-sm">
<Card.Subtitle className="text-muted mb-2">
{t('library_stat_borrowed_label')}
</Card.Subtitle>
<Card.Title as="p" className="display-6 fw-bold mb-0 text-secondary">
{borrowedCount}
</Card.Title>
<Card.Text className="text-muted mt-2">{t('library_stat_borrowed_desc')}</Card.Text>
</Card>
</Col>
</Row>

<Row className="g-4">
<Col lg={8}>
<section id="library-catalog">
<div className="d-flex justify-content-between align-items-end gap-3 mb-3">
<div>
<h2 className="h3 mb-1">{t('library_catalog_title')}</h2>
<p className="text-muted mb-0">{t('library_catalog_description')}</p>
</div>
<Badge bg="light" text="dark" className="border">
{t('library_status_legend')}
</Badge>
</div>

<Row as="ul" className="list-unstyled g-4" xs={1} md={2}>
{libraryBooks.map(book => (
<Col key={book.id} as="li">
<Card className="h-100 shadow-sm">
<Card.Body>
<Stack direction="horizontal" gap={2} className="mb-3">
<Badge bg={statusVariant[book.status]}>
{t(statusLabelKey[book.status])}
</Badge>
<Badge bg="light" text="dark" className="border">
{t(book.categoryKey)}
</Badge>
</Stack>

<Card.Title as="h3" className="h5">
{t(book.titleKey)}
</Card.Title>
<Card.Subtitle className="text-muted mb-3">{t(book.authorKey)}</Card.Subtitle>
<Card.Text>{t(book.descriptionKey)}</Card.Text>
</Card.Body>
<ListGroup variant="flush">
<ListGroup.Item className="d-flex justify-content-between gap-3">
<span className="text-muted">{t('library_shelf_code')}</span>
<span className="fw-semibold">{book.shelfCode}</span>
</ListGroup.Item>
<ListGroup.Item className="d-flex justify-content-between gap-3">
<span className="text-muted">{t('library_borrow_status')}</span>
<span>{t(book.borrowInfoKey)}</span>
</ListGroup.Item>
</ListGroup>
</Card>
</Col>
))}
</Row>
</section>
</Col>

<Col lg={4}>
<section id="borrow-guide" className="position-sticky" style={{ top: '5rem' }}>
<Card className="shadow-sm">
<Card.Body>
<Card.Title as="h2" className="h4">
{t('library_borrow_guide')}
</Card.Title>
<Card.Text className="text-muted">
{t('library_borrow_guide_description')}
</Card.Text>
<ListGroup as="ol" numbered variant="flush">
{guideStepKeys.map(key => (
<ListGroup.Item key={key} as="li" className="px-0">
{t(key)}
</ListGroup.Item>
))}
</ListGroup>
</Card.Body>
<Card.Footer className="bg-white">
<Link href="/bounty" passHref legacyBehavior>
<Button className="w-100" variant="outline-primary">
{t('library_submit_books')}
</Button>
</Link>
</Card.Footer>
</Card>
</section>
</Col>
</Row>
</Container>
);
});

export default LibraryPage;
65 changes: 65 additions & 0 deletions translation/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,71 @@ export default {
'Find corresponding ETFs or index funds, paying attention to fees and fund size.',
finance_edu_next_3: 'Set up a recurring investment plan and track valuation and drawdown.',

// Open Library
open_library: 'Open Library',
library_page_title: 'Open Library',
library_page_description:
'Browse books about open collaboration, open-source governance, and engineering practice with clear borrowing status and instructions.',
library_badge: 'Open Library',
library_browse_catalog: 'Browse catalog',
library_borrow_guide: 'Borrowing guide',
library_stat_total_label: 'Books in catalog',
library_stat_total_desc:
'Covering open-source culture, community operations, engineering collaboration, and licensing practice.',
library_stat_available_label: 'Available now',
library_stat_available_desc: 'Contact an administrator to register a borrowing request.',
library_stat_borrowed_label: 'Borrowed',
library_stat_borrowed_desc: 'Check borrowing details and reserve the next slot after return.',
library_catalog_title: 'Book catalog',
library_catalog_description:
'Review topics, shelf codes, availability, and borrowing notes for each book.',
library_status_legend: 'Status: available / borrowed',
library_status_available: 'Available',
library_status_borrowed: 'Borrowed',
library_category_open_source: 'Open-source culture',
library_category_community: 'Community operations',
library_category_engineering: 'Engineering collaboration',
library_category_governance: 'Governance practice',
library_category_license: 'Licensing',
library_book_cathedral_title: 'The Cathedral and the Bazaar',
library_book_cathedral_author: 'Eric S. Raymond',
library_book_cathedral_description:
'A classic open-source culture essay about open collaboration, fast iteration, and community-driven software development.',
library_book_producing_oss_title: 'Producing Open Source Software',
library_book_producing_oss_author: 'Karl Fogel',
library_book_producing_oss_description:
'A practical guide to launching open-source projects, communication, contribution workflows, and maintainer responsibilities.',
library_book_pro_git_title: 'Pro Git',
library_book_pro_git_author: 'Scott Chacon and Ben Straub',
library_book_pro_git_description:
'A systematic guide to daily Git collaboration, branching models, and distributed version control workflows.',
library_book_community_playbook_title: 'Open Source Community Playbook',
library_book_community_playbook_author: 'Open Source Bazaar',
library_book_community_playbook_description:
'Templates and practices for community organizers covering events, volunteer collaboration, contributor growth, and retrospectives.',
library_book_governance_title: 'Open Source Governance Guide',
library_book_governance_author: 'Open Source Initiative',
library_book_governance_description:
'Guidance on governance models, decision-making, codes of conduct, and long-term project sustainability.',
library_book_license_title: 'Open Source License Handbook',
library_book_license_author: 'Open Source Bazaar',
library_book_license_description:
'Helps contributors understand common licenses, reuse boundaries, and project compliance checklists.',
library_book_available_note:
'Available to borrow after registration and administrator confirmation.',
library_book_borrowed_note:
'Currently borrowed. Contact an administrator to reserve the next slot after return.',
library_shelf_code: 'Shelf code',
library_borrow_status: 'Borrowing info',
library_borrow_guide_description:
'Register in three steps; after confirmation, pick up the book offline or agree on a handoff method.',
library_guide_step_1: 'Find the book in the catalog and record its title and shelf code.',
library_guide_step_2:
'Contact an administrator through the bounty or activity page with your name, contact information, and expected return date.',
library_guide_step_3:
'After confirmation, complete the registration and receive return reminders or reservation queue updates.',
library_submit_books: 'Submit a book or request borrowing',

// Hackathon
hackathon_detail: 'Hackathon Details',
hackathon_highlights: 'Tracks',
Expand Down
55 changes: 55 additions & 0 deletions translation/zh-CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,61 @@ export default {
finance_edu_next_2: '了解对应的 ETF 或联接基金,关注费率与规模。',
finance_edu_next_3: '设定定投计划并持续跟踪估值、回撤。',

// Open Library
open_library: '开源图书馆',
library_page_title: '开源图书馆',
library_page_description:
'浏览开放协作、开源治理与工程实践相关书籍,并快速了解可借状态与借阅方式。',
library_badge: 'Open Library',
library_browse_catalog: '浏览图书目录',
library_borrow_guide: '借阅指南',
library_stat_total_label: '馆藏图书',
library_stat_total_desc: '覆盖开源文化、社区运营、工程协作与许可证实践。',
library_stat_available_label: '当前可借',
library_stat_available_desc: '可直接联系管理员登记借阅。',
library_stat_borrowed_label: '已借出',
library_stat_borrowed_desc: '可查看归还信息并预约下一次借阅。',
library_catalog_title: '图书目录',
library_catalog_description: '按主题查看馆藏、书架编号、可借状态和借阅说明。',
library_status_legend: '状态:可借 / 已借出',
library_status_available: '可借',
library_status_borrowed: '已借出',
library_category_open_source: '开源文化',
library_category_community: '社区运营',
library_category_engineering: '工程协作',
library_category_governance: '治理实践',
library_category_license: '许可证',
library_book_cathedral_title: '《大教堂与集市》',
library_book_cathedral_author: 'Eric S. Raymond',
library_book_cathedral_description:
'经典开源文化读本,介绍开放协作、快速迭代与社区共创的软件开发模式。',
library_book_producing_oss_title: '《Producing Open Source Software》',
library_book_producing_oss_author: 'Karl Fogel',
library_book_producing_oss_description:
'围绕开源项目启动、沟通、贡献流程与维护者职责的实用指南。',
library_book_pro_git_title: '《Pro Git》',
library_book_pro_git_author: 'Scott Chacon 与 Ben Straub',
library_book_pro_git_description: '系统讲解 Git 的日常协作、分支模型和分布式版本控制工作流。',
library_book_community_playbook_title: '开源社区行动手册',
library_book_community_playbook_author: 'Open Source Bazaar',
library_book_community_playbook_description:
'面向社区组织者的活动策划、志愿者协作、贡献者成长与复盘模板。',
library_book_governance_title: '开源治理指南',
library_book_governance_author: 'Open Source Initiative',
library_book_governance_description: '梳理治理结构、决策机制、行为规范和项目可持续性建设要点。',
library_book_license_title: '开源许可证手册',
library_book_license_author: 'Open Source Bazaar',
library_book_license_description: '帮助贡献者理解常见许可证、复用边界和项目合规检查清单。',
library_book_available_note: '可借阅,提交登记后等待管理员确认。',
library_book_borrowed_note: '已借出,可联系管理员预约归还后的下一位。',
library_shelf_code: '书架编号',
library_borrow_status: '借阅信息',
library_borrow_guide_description: '三步完成借阅登记,管理员确认后即可线下领取或约定传递方式。',
library_guide_step_1: '在目录中找到想借阅的书籍,记录书名与书架编号。',
library_guide_step_2: '通过开源市集悬赏或活动页面联系管理员,提交姓名、联系方式和预计归还时间。',
library_guide_step_3: '管理员确认可借状态后完成登记,并同步归还提醒或预约排队信息。',
library_submit_books: '提交新书或借阅请求',

// Hackathon
hackathon_detail: '黑客松详情',
hackathon_highlights: '赛道方向',
Expand Down
Loading