From 74174bda99f1784030bfaa18f460e4efe255f1f6 Mon Sep 17 00:00:00 2001 From: ethan 3 Date: Mon, 24 Feb 2025 07:49:28 +0000 Subject: [PATCH 1/8] library flow --- pages/_app.tsx | 3 +++ pages/library.mdx | 35 +++++++++++++++++++++++++++++++++++ translation/en-US.ts | 3 +++ translation/zh-CN.ts | 3 +++ translation/zh-TW.ts | 3 +++ 5 files changed, 47 insertions(+) create mode 100644 pages/library.mdx diff --git a/pages/_app.tsx b/pages/_app.tsx index b2e8102..b894528 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -45,6 +45,9 @@ const App: FC = observer(({ Component, pageProps }) => { href: 'https://github.com/Open-Source-Bazaar/Git-Hackathon-scaffold', name: t('hackathon'), }, + { + href: '/library', name: t('open_library'), + }, ]; return ( <> diff --git a/pages/library.mdx b/pages/library.mdx new file mode 100644 index 0000000..57c90da --- /dev/null +++ b/pages/library.mdx @@ -0,0 +1,35 @@ +# 社区图书馆 + +## 简介 + +freeCodeCamp 成都社区「Open Library」是一个开放、共享的社区图书馆,旨在促进知识交流,推动社区成员之间的学习和成长,增强社区成员之间的互动与信任。我们深知知识的流动能够点燃创新的火花,因此,我们采用了一种独特的“无储存”借阅模式,让书籍在会员之间自由流转,而非集中存放。 + +## 申请会员 + +如果你热爱阅读,愿意分享知识,欢迎填写 fCC 成都社区图书馆-会员申请,加入 freeCodeCamp 成都社区「Open Library」,成为知识流动的一部分! + +## 书籍募捐 + +我们鼓励社区成员捐赠书籍,共同丰富书目资源。捐赠流程如下: + +1. **填写捐赠申请**:填写 fCC 成都社区图书馆-书籍捐赠。 + +2. **书籍登记**:如所捐赠书籍在「 fCC 成都社区图书馆」还未登记,请先填写 fCC 成都社区图书馆-书籍登记,然后回到第 1 步继续捐赠。建议在移动端设备上登记,方便扫描书籍 ISBN 码。 + +3. **书籍进入图书馆**:完成以上流程后,书籍正式进入 Open Library 的传递流程,开始流转于社区成员之间。 + +## 借阅与传递 + +在 Open Library,所有书籍均来自社区成员的捐赠,并由借阅者直接**传递**给下一位借书人。 + +借阅流程如下: + +1. **查阅书籍**:社区成员可以在 fCC 成都社区图书馆中查找自己感兴趣的书籍。 +2. **申请借阅**:填写 fCC 成都社区图书馆-书籍借入申请,与当前持书者取得联系。 +3. **线下传递**:双方约定时间和传递方式,通常可用快递传递书籍。请传递者填写 fCC 成都社区图书馆-书籍传递,再将书籍传递出去。 +4. **阅读与分享**:借阅者在阅读完毕后,可以分享自己的阅读感悟,并在社区推荐给下一位感兴趣的成员。 +5. **继续传递**:当有新的借阅者申请时,当前持书人将书籍传递给下一位读者,确保知识的持续流动。 + +## 未来 + +期待未来可以与更多开放空间合作,定期举办主题阅读会。 \ No newline at end of file diff --git a/translation/en-US.ts b/translation/en-US.ts index abbb596..7e0ac36 100644 --- a/translation/en-US.ts +++ b/translation/en-US.ts @@ -11,4 +11,7 @@ export default { // Search keywords: 'Keywords', search_results: 'Search Results', + + // Open Library + open_library: "Open Library", }; diff --git a/translation/zh-CN.ts b/translation/zh-CN.ts index 7baf329..44edfe7 100644 --- a/translation/zh-CN.ts +++ b/translation/zh-CN.ts @@ -11,4 +11,7 @@ export default { // Search keywords: '关键词', search_results: '搜索结果', + + // Open Library + open_library: "开源图书馆", }; diff --git a/translation/zh-TW.ts b/translation/zh-TW.ts index 024e7fb..be691cc 100644 --- a/translation/zh-TW.ts +++ b/translation/zh-TW.ts @@ -11,4 +11,7 @@ export default { // Search keywords: '關鍵詞', search_results: '搜尋結果', + + // Open Library + open_library: "開源圖書館", }; From 07745ad86bc52e9c5ce68a5b83fd84eeacf70fb1 Mon Sep 17 00:00:00 2001 From: dethan3 Date: Fri, 18 Apr 2025 16:16:34 +0800 Subject: [PATCH 2/8] [feat] Initial open-library homepage, component & hook fix --- .husky/pre-commit | 2 +- components/open-library/Footer.tsx | 69 ++++ components/open-library/Navbar.tsx | 46 +++ package.json | 3 +- pages/_app.tsx | 3 +- pages/open-library/index.tsx | 345 ++++++++++++++++++ pages/{library.mdx => open-library/readme.md} | 2 +- 7 files changed, 466 insertions(+), 4 deletions(-) create mode 100644 components/open-library/Footer.tsx create mode 100644 components/open-library/Navbar.tsx create mode 100644 pages/open-library/index.tsx rename pages/{library.mdx => open-library/readme.md} (99%) diff --git a/.husky/pre-commit b/.husky/pre-commit index 255814b..98475b5 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1 +1 @@ -npm test +pnpm test diff --git a/components/open-library/Footer.tsx b/components/open-library/Footer.tsx new file mode 100644 index 0000000..124e088 --- /dev/null +++ b/components/open-library/Footer.tsx @@ -0,0 +1,69 @@ +import React from 'react'; +import { Container, Row, Col, Nav } from 'react-bootstrap'; + +const FooterComponent = () => { + return ( +
+ + + +
Open Library
+

+ A community-driven library for sharing knowledge and stories. + Built with open source. Powered by generosity. +

+ + +
Quick Links
+ + + +
Contact
+

Email: info@openlibrary.community

+

Address: 123 Library Lane, Knowledge City, World

+ + +
Follow Us
+ + +
+ + + + © {new Date().getFullYear()} Open Library Community. All + Rights Reserved. + + + +
+
+ ); +}; + +export default FooterComponent; diff --git a/components/open-library/Navbar.tsx b/components/open-library/Navbar.tsx new file mode 100644 index 0000000..28767c5 --- /dev/null +++ b/components/open-library/Navbar.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { + Navbar, + Nav, + Container, + Button, + Form, + FormControl, +} from 'react-bootstrap'; + +const NavbarComponent = () => { + return ( + + + + Open Library + + + + +
+ + + + +
+
+
+ ); +}; + +export default NavbarComponent; diff --git a/package.json b/package.json index 2e2cd5e..d3c513d 100644 --- a/package.json +++ b/package.json @@ -81,5 +81,6 @@ }, "lint-staged": { "*.{html,md,scss,json,yml,js,mjs,ts,tsx}": "prettier --write" - } + }, + "packageManager": "pnpm@10.8.1+sha512.c50088ba998c67b8ca8c99df8a5e02fd2ae2e2b29aaf238feaa9e124248d3f48f9fb6db2424949ff901cffbb5e0f0cc1ad6aedb602cd29450751d11c35023677" } diff --git a/pages/_app.tsx b/pages/_app.tsx index b894528..6818a20 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -46,7 +46,8 @@ const App: FC = observer(({ Component, pageProps }) => { name: t('hackathon'), }, { - href: '/library', name: t('open_library'), + href: '/open-library', + name: t('open_library'), }, ]; return ( diff --git a/pages/open-library/index.tsx b/pages/open-library/index.tsx new file mode 100644 index 0000000..ef893f5 --- /dev/null +++ b/pages/open-library/index.tsx @@ -0,0 +1,345 @@ +import React from 'react'; +import Link from 'next/link'; +import { + Container, + Row, + Col, + Button, + Card, + Form, + Image, + Stack, +} from 'react-bootstrap'; +import NavbarComponent from '../../components/open-library/Navbar'; +import FooterComponent from '../../components/open-library/Footer'; + +// TODO: Define a type for Book and Testimonial +type Book = { + id: number; + title: string; + author: string; + cover?: string; // Optional cover image URL +}; + +type Testimonial = { + id: number; + name: string; + quote: string; +}; + +export default function OpenLibraryHomepage() { + // Sample featured books data + const featuredBooks: Book[] = [ + { + id: 1, + title: 'Clean Code', + author: 'Robert C. Martin', + cover: '/images/placeholder-book.svg', + }, // TODO: Replace placeholder path + { + id: 2, + title: 'Eloquent JavaScript', + author: 'Marijn Haverbeke', + cover: '/images/placeholder-book.svg', + }, + { + id: 3, + title: 'Design Patterns', + author: 'Erich Gamma et al.', + cover: '/images/placeholder-book.svg', + }, + { + id: 4, + title: "You Don't Know JS", + author: 'Kyle Simpson', + cover: '/images/placeholder-book.svg', + }, + ]; + + // Sample testimonials data + const testimonials: Testimonial[] = [ + { + id: 1, + name: 'Zhang Wei', + quote: + "Open Library helped me discover amazing tech books I couldn't find elsewhere.", + }, + { + id: 2, + name: 'Li Mei', + quote: + "The community is so supportive. I've both borrowed and donated books here.", + }, + { + id: 3, + name: 'Wang Chen', + quote: + 'As a student, this resource has been invaluable for my studies in computer science.', + }, + ]; + + return ( + <> + {/* --- Navbar --- */} + + +
+ {/* --- Hero Section --- */} +
+ + + +

+ Free knowledge flows here +

+

+ Share and borrow books in our open-source community. Join + freeCodeCamp Chengdu's initiative to make learning accessible + to everyone. +

+ {/* TODO: Link this button to the actual Feishu form or member sign-up page */} + + + + {/* TODO: Replace placeholder */} + People sharing books + +
+
+
+ + {/* --- Featured Books Section --- */} +
+ +

Featured Books

+

+ Discover what our community is reading right now +

+ + {featuredBooks.map(book => ( + // TODO: Potentially move Card rendering to components/open-library/BookCard.tsx + + +
+ {`${book.title} +
+ + + {book.title} + + + {book.author} + +
+ + + +
+
+
+ + ))} +
+
+ + + +
+
+
+ + {/* --- Donation Callout Section --- */} +
+ + + +

Share Your Knowledge

+

+ Have books collecting dust on your shelf? Donate them to our + community and help others learn and grow. Your contribution + makes a difference! +

+ + + + + + {/* TODO: Replace placeholder */} + Book donation + +
+
+
+ + {/* --- How It Works Section --- */} +
+ +

How It Works

+

+ Three simple steps to borrow books from our community +

+ + +
+ +
+

1. Find a Book

+

+ Browse our collection and find the book that interests you. + Filter by category, author, or popularity. +

+ + +
+ +
+

2. Apply to Borrow

+

+ Submit a simple request form. We'll connect you with the book + owner and arrange the handover. +

+ + +
+ +
+

3. Receive and Pass It On

+

+ Enjoy your book and return it when you're done. Consider + donating your own books to keep knowledge flowing. +

+ +
+
+ + + +
+
+
+ + {/* --- Testimonials Section --- */} +
+ +

Community Voices

+

+ What our members say about Open Library +

+ + {testimonials.map(testimonial => ( + + + +
+

"{testimonial.quote}"

+
+ {testimonial.name} +
+
+
+
+ + ))} +
+
+ + + +
+
+
+ + {/* --- Newsletter Section --- */} +
+ + + + + +

Stay Updated

+

+ Subscribe to our newsletter for new book arrivals and + community events. +

+ {/* TODO: Implement actual newsletter subscription logic */} +
e.preventDefault()}> + {' '} + {/* Prevent default form submission */} + + + + +
+
+
+ +
+
+
+
+ + {/* --- Footer --- */} + + + ); +} diff --git a/pages/library.mdx b/pages/open-library/readme.md similarity index 99% rename from pages/library.mdx rename to pages/open-library/readme.md index 57c90da..b9d7c47 100644 --- a/pages/library.mdx +++ b/pages/open-library/readme.md @@ -32,4 +32,4 @@ freeCodeCamp 成都社区「Open Library」是一个开放、共享的社区图 ## 未来 -期待未来可以与更多开放空间合作,定期举办主题阅读会。 \ No newline at end of file +期待未来可以与更多开放空间合作,定期举办主题阅读会。 From e10b0b1f5259722d1416132eefa73b4d6b683c6e Mon Sep 17 00:00:00 2001 From: dethan3 Date: Thu, 24 Apr 2025 12:57:24 +0800 Subject: [PATCH 3/8] feat: customize Open Library layout and styling --- components/open-library/Footer.tsx | 25 +++- components/open-library/Layout.tsx | 60 +++++++++ components/open-library/Navbar.tsx | 36 +++-- pages/_app.tsx | 126 +++++++++-------- pages/open-library/index.tsx | 208 ++++++++++++++++++++++++----- 5 files changed, 352 insertions(+), 103 deletions(-) create mode 100644 components/open-library/Layout.tsx diff --git a/components/open-library/Footer.tsx b/components/open-library/Footer.tsx index 124e088..cde0a5f 100644 --- a/components/open-library/Footer.tsx +++ b/components/open-library/Footer.tsx @@ -1,10 +1,29 @@ import React from 'react'; -import { Container, Row, Col, Nav } from 'react-bootstrap'; +import { Row, Col, Nav } from 'react-bootstrap'; + +// +const ContentContainer: React.FC<{ children: React.ReactNode }> = ({ + children, +}) => { + return ( +
+ {children} +
+ ); +}; const FooterComponent = () => { return (
- +
Open Library
@@ -61,7 +80,7 @@ const FooterComponent = () => {
-
+
); }; diff --git a/components/open-library/Layout.tsx b/components/open-library/Layout.tsx new file mode 100644 index 0000000..bace01f --- /dev/null +++ b/components/open-library/Layout.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import Head from 'next/head'; +import NavbarComponent from './Navbar'; +import FooterComponent from './Footer'; + +// 内容容器组件,使内容居中但不添加边框 +const ContentContainer: React.FC<{ children: React.ReactNode }> = ({ + children, +}) => { + return ( +
+ {children} +
+ ); +}; + +interface LayoutProps { + children: React.ReactNode; + title?: string; +} + +/** + * Open Library 的共享布局组件 + * 包含导航栏、页脚和内容容器 + * 所有 Open Library 页面都应使用此布局 + */ +const Layout: React.FC = ({ + children, + title = 'Open Library - Open Source Bazaar', +}) => { + return ( + <> + + {title} + + + + {/* 导航栏 */} + + + {/* 主要内容 */} +
{children}
+ + {/* 页脚 */} + + + ); +}; + +// 导出布局组件和内容容器组件,以便在页面中使用 +export { Layout, ContentContainer }; +export default Layout; diff --git a/components/open-library/Navbar.tsx b/components/open-library/Navbar.tsx index 28767c5..8e40419 100644 --- a/components/open-library/Navbar.tsx +++ b/components/open-library/Navbar.tsx @@ -11,19 +11,37 @@ import { const NavbarComponent = () => { return ( - - + {/* 使用自定义的居中容器替代 Container fluid,与页面内容保持一致的宽度和居中效果 */} +
+ Open Library
{ Login/Register - +
); }; diff --git a/pages/_app.tsx b/pages/_app.tsx index 6818a20..99c7009 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -35,6 +35,9 @@ const App: FC = observer(({ Component, pageProps }) => { const { pathname } = useRouter(); const thisFullYear = new Date().getFullYear(); + // 检查是否是 Open Library 路径 + const isOpenLibraryPath = pathname.startsWith('/open-library'); + const topNavBarMenu = [ { href: '/about', name: t('about') }, { href: '/history', name: t('history') }, @@ -59,62 +62,75 @@ const App: FC = observer(({ Component, pageProps }) => { - - - - {t('open_source_bazaar')} - - - - - - - - - - -
- - - -
- - + )} ); }); diff --git a/pages/open-library/index.tsx b/pages/open-library/index.tsx index ef893f5..91d6bfa 100644 --- a/pages/open-library/index.tsx +++ b/pages/open-library/index.tsx @@ -1,17 +1,7 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import Link from 'next/link'; -import { - Container, - Row, - Col, - Button, - Card, - Form, - Image, - Stack, -} from 'react-bootstrap'; -import NavbarComponent from '../../components/open-library/Navbar'; -import FooterComponent from '../../components/open-library/Footer'; +import { Row, Col, Button, Card, Form, Image, Stack } from 'react-bootstrap'; +import { Layout, ContentContainer } from '../../components/open-library/Layout'; // TODO: Define a type for Book and Testimonial type Book = { @@ -78,11 +68,160 @@ export default function OpenLibraryHomepage() { }, ]; - return ( - <> - {/* --- Navbar --- */} - + // Use client-side code to hide the main site header and adjust layout + useEffect(() => { + // Function to apply style to an element + const applyStyle = ( + element: HTMLElement | null, + styles: Partial, + ) => { + if (!element) return; + + Object.entries(styles).forEach(([property, value]) => { + if (value) { + // @ts-ignore: dynamic property assignment + element.style[property] = value; + } + }); + }; + + // Hide the main site header + const mainHeader = document.querySelector( + 'nav.navbar.bg-dark.navbar-dark.fixed-top', + ); + applyStyle(mainHeader as HTMLElement, { display: 'none' }); + + // Remove top margin that accommodates the main header + const mainContent = document.querySelector('div.mt-5.pt-2'); + applyStyle(mainContent as HTMLElement, { + marginTop: '0', + paddingTop: '0', + maxWidth: '100%', + width: '100%', + }); + + // Remove background and padding from main content + const mainWrapper = document.querySelector( + 'main.flex-fill.d-flex.flex-column.justify-content-start.align-items-center', + ); + applyStyle(mainWrapper as HTMLElement, { + background: 'none', + padding: '0', + margin: '0', + maxWidth: '100%', + width: '100%', + }); + + // Remove container constraints + const containers = document.querySelectorAll('.container'); + containers.forEach(container => { + applyStyle(container as HTMLElement, { + maxWidth: '100%', + padding: '0', + margin: '0', + }); + }); + + // Remove card styling + const cards = document.querySelectorAll('.card'); + cards.forEach(card => { + if ( + card.closest( + 'main.flex-fill.d-flex.flex-column.justify-content-start.align-items-center', + ) + ) { + applyStyle(card as HTMLElement, { + background: 'transparent', + border: 'none', + boxShadow: 'none', + padding: '0', + margin: '0', + }); + } + }); + + // Remove card body padding + const cardBodies = document.querySelectorAll('.card-body'); + cardBodies.forEach(cardBody => { + if ( + cardBody.closest( + 'main.flex-fill.d-flex.flex-column.justify-content-start.align-items-center', + ) + ) { + applyStyle(cardBody as HTMLElement, { + padding: '0', + }); + } + }); + // Hide the main site footer + const mainFooter = document.querySelector( + 'footer.mw-100.bg-dark.text-white', + ); + applyStyle(mainFooter as HTMLElement, { display: 'none' }); + + // Remove all padding and margin from body and html + applyStyle(document.body, { + margin: '0', + padding: '0', + overflow: 'auto', + }); + + applyStyle(document.documentElement, { + margin: '0', + padding: '0', + overflow: 'auto', + }); + + // Target the MDXProvider wrapper (main white background) + const mdxProvider = document.querySelector(`.MDXProvider`); + applyStyle(mdxProvider as HTMLElement, { + padding: '0 !important', + margin: '0 !important', + background: 'transparent !important', + border: 'none !important', + boxShadow: 'none !important', + }); + + // Add custom styles to ensure full width + const style = document.createElement('style'); + style.textContent = ` + body, html { + margin: 0 !important; + padding: 0 !important; + overflow-x: hidden !important; + } + .container, .container-fluid { + max-width: 100% !important; + padding-left: 0 !important; + padding-right: 0 !important; + margin-left: 0 !important; + margin-right: 0 !important; + } + main.flex-fill.d-flex.flex-column.justify-content-start.align-items-center { + padding: 0 !important; + margin: 0 !important; + max-width: 100% !important; + width: 100% !important; + } + main.flex-fill.d-flex.flex-column.justify-content-start.align-items-center > .container { + max-width: 100% !important; + padding: 0 !important; + margin: 0 !important; + } + .MDXProvider { + padding: 0 !important; + margin: 0 !important; + background: transparent !important; + border: none !important; + box-shadow: none !important; + } + `; + document.head.appendChild(style); + }, []); + + return ( +
{/* --- Hero Section --- */}
- +

@@ -119,12 +258,12 @@ export default function OpenLibraryHomepage() { /> - +

{/* --- Featured Books Section --- */}
- +

Featured Books

Discover what our community is reading right now @@ -174,16 +313,16 @@ export default function OpenLibraryHomepage() { variant="link" className="text-success text-decoration-none" > - Browse All Books → + Browse All Books - +

{/* --- Donation Callout Section --- */}
- +

Share Your Knowledge

@@ -207,12 +346,12 @@ export default function OpenLibraryHomepage() { />
-
+
{/* --- How It Works Section --- */}
- +

How It Works

Three simple steps to borrow books from our community @@ -255,16 +394,16 @@ export default function OpenLibraryHomepage() { variant="link" className="text-success text-decoration-none" > - Learn More About Borrowing → + Learn More About Borrowing - +

{/* --- Testimonials Section --- */}
- +

Community Voices

What our members say about Open Library @@ -291,16 +430,16 @@ export default function OpenLibraryHomepage() { variant="link" className="text-success text-decoration-none" > - Read More Reviews → + Read More Reviews - +

{/* --- Newsletter Section --- */}
- + @@ -334,12 +473,9 @@ export default function OpenLibraryHomepage() { - +
- - {/* --- Footer --- */} - - +
); } From 69dbf8b67af1c783a8ff0cc1bdb724648dca1d3e Mon Sep 17 00:00:00 2001 From: dethan3 Date: Thu, 24 Apr 2025 13:38:45 +0800 Subject: [PATCH 4/8] fix: resolve import sorting and TS annotation issues --- components/open-library/Footer.tsx | 2 +- components/open-library/Layout.tsx | 7 ++++--- components/open-library/Navbar.tsx | 9 +-------- pages/open-library/index.tsx | 9 +++++---- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/components/open-library/Footer.tsx b/components/open-library/Footer.tsx index cde0a5f..d9acd09 100644 --- a/components/open-library/Footer.tsx +++ b/components/open-library/Footer.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Row, Col, Nav } from 'react-bootstrap'; +import { Col, Nav, Row } from 'react-bootstrap'; // const ContentContainer: React.FC<{ children: React.ReactNode }> = ({ diff --git a/components/open-library/Layout.tsx b/components/open-library/Layout.tsx index bace01f..2f4def9 100644 --- a/components/open-library/Layout.tsx +++ b/components/open-library/Layout.tsx @@ -1,7 +1,8 @@ -import React from 'react'; import Head from 'next/head'; -import NavbarComponent from './Navbar'; +import React from 'react'; + import FooterComponent from './Footer'; +import NavbarComponent from './Navbar'; // 内容容器组件,使内容居中但不添加边框 const ContentContainer: React.FC<{ children: React.ReactNode }> = ({ @@ -56,5 +57,5 @@ const Layout: React.FC = ({ }; // 导出布局组件和内容容器组件,以便在页面中使用 -export { Layout, ContentContainer }; export default Layout; +export { ContentContainer, Layout }; diff --git a/components/open-library/Navbar.tsx b/components/open-library/Navbar.tsx index 8e40419..a71ac23 100644 --- a/components/open-library/Navbar.tsx +++ b/components/open-library/Navbar.tsx @@ -1,12 +1,5 @@ import React from 'react'; -import { - Navbar, - Nav, - Container, - Button, - Form, - FormControl, -} from 'react-bootstrap'; +import { Button, Form, FormControl, Nav, Navbar } from 'react-bootstrap'; const NavbarComponent = () => { return ( diff --git a/pages/open-library/index.tsx b/pages/open-library/index.tsx index 91d6bfa..074b5e7 100644 --- a/pages/open-library/index.tsx +++ b/pages/open-library/index.tsx @@ -1,7 +1,8 @@ -import React, { useEffect } from 'react'; import Link from 'next/link'; -import { Row, Col, Button, Card, Form, Image, Stack } from 'react-bootstrap'; -import { Layout, ContentContainer } from '../../components/open-library/Layout'; +import React, { useEffect } from 'react'; +import { Button, Card, Col, Form, Image, Row, Stack } from 'react-bootstrap'; + +import { ContentContainer, Layout } from '../../components/open-library/Layout'; // TODO: Define a type for Book and Testimonial type Book = { @@ -79,7 +80,7 @@ export default function OpenLibraryHomepage() { Object.entries(styles).forEach(([property, value]) => { if (value) { - // @ts-ignore: dynamic property assignment + // @ts-expect-error: dynamic property assignment element.style[property] = value; } }); From a72c2eee8578d4200caaae0bc51e79d08120dba8 Mon Sep 17 00:00:00 2001 From: dethan3 Date: Fri, 25 Apr 2025 14:18:41 +0800 Subject: [PATCH 5/8] feat: implement Open Library basic framework with multilingual support --- .husky/pre-push | 2 +- components/open-library/Footer.tsx | 120 +++-- components/open-library/Layout.tsx | 13 +- components/open-library/Navbar.tsx | 48 +- .../open-library/useOpenLibraryLayout.ts | 20 + pages/_app.tsx | 1 + pages/api/hello.ts | 10 +- pages/open-library/about.tsx | 201 ++++++++ pages/open-library/book/[id].tsx | 453 ++++++++++++++++++ pages/open-library/books/index.tsx | 318 ++++++++++++ pages/open-library/donate.tsx | 254 ++++++++++ pages/open-library/how-to-borrow.tsx | 294 ++++++++++++ pages/open-library/index.tsx | 241 ++-------- pages/open-library/review.tsx | 372 ++++++++++++++ styles/open-library.css | 65 +++ translation/en-US.ts | 149 +++++- translation/zh-CN.ts | 146 +++++- translation/zh-TW.ts | 145 +++++- 18 files changed, 2570 insertions(+), 282 deletions(-) create mode 100644 components/open-library/useOpenLibraryLayout.ts create mode 100644 pages/open-library/about.tsx create mode 100644 pages/open-library/book/[id].tsx create mode 100644 pages/open-library/books/index.tsx create mode 100644 pages/open-library/donate.tsx create mode 100644 pages/open-library/how-to-borrow.tsx create mode 100644 pages/open-library/review.tsx create mode 100644 styles/open-library.css diff --git a/.husky/pre-push b/.husky/pre-push index da7be55..0c958d2 100644 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1 +1 @@ -npm run build +pnpm run build diff --git a/components/open-library/Footer.tsx b/components/open-library/Footer.tsx index d9acd09..c17b219 100644 --- a/components/open-library/Footer.tsx +++ b/components/open-library/Footer.tsx @@ -1,6 +1,8 @@ import React from 'react'; import { Col, Nav, Row } from 'react-bootstrap'; +import { t } from '../../models/Translation'; + // const ContentContainer: React.FC<{ children: React.ReactNode }> = ({ children, @@ -21,65 +23,95 @@ const ContentContainer: React.FC<{ children: React.ReactNode }> = ({ }; const FooterComponent = () => { + // Use client-side rendering for the copyright text to avoid hydration issues + const [isMounted, setIsMounted] = React.useState(false); + + React.useEffect(() => { + setIsMounted(true); + }, []); + return (
-
Open Library
-

- A community-driven library for sharing knowledge and stories. - Built with open source. Powered by generosity. -

- - -
Quick Links
- - - -
Contact
-

Email: info@openlibrary.community

-

Address: 123 Library Lane, Knowledge City, World

- - -
Follow Us
-
- - Facebook +
{t('open_library')}
+

{t('footer_description')}

+
- - - - - © {new Date().getFullYear()} Open Library Community. All - Rights Reserved. - + +
{t('quick_links_footer')}
+ + + +
{t('contact')}
+

freeCodeCamp Chengdu Community

+

Chengdu, Sichuan, China

+

Email: contact@openlibrary.org

+

WeChat: FCCChengdu

+ +
+ + {/* Use a more direct approach with inline styles to ensure visibility */} +
+ {isMounted ? ( + <> + © {new Date().getFullYear()} {t('open_library')}.{' '} + {t('all_rights_reserved')} + + ) : ( + <> + © {new Date().getFullYear()} Open Library. All rights + reserved. + + )} +
); diff --git a/components/open-library/Layout.tsx b/components/open-library/Layout.tsx index 2f4def9..92e09d5 100644 --- a/components/open-library/Layout.tsx +++ b/components/open-library/Layout.tsx @@ -2,9 +2,9 @@ import Head from 'next/head'; import React from 'react'; import FooterComponent from './Footer'; -import NavbarComponent from './Navbar'; +import LibraryNavbar from './Navbar'; +import { useOpenLibraryLayout } from './useOpenLibraryLayout'; -// 内容容器组件,使内容居中但不添加边框 const ContentContainer: React.FC<{ children: React.ReactNode }> = ({ children, }) => { @@ -37,6 +37,9 @@ const Layout: React.FC = ({ children, title = 'Open Library - Open Source Bazaar', }) => { + // Apply Open Library layout styles + useOpenLibraryLayout(); + return ( <> @@ -44,18 +47,14 @@ const Layout: React.FC = ({ - {/* 导航栏 */} - + - {/* 主要内容 */}
{children}
- {/* 页脚 */} ); }; -// 导出布局组件和内容容器组件,以便在页面中使用 export default Layout; export { ContentContainer, Layout }; diff --git a/components/open-library/Navbar.tsx b/components/open-library/Navbar.tsx index a71ac23..1b2fd35 100644 --- a/components/open-library/Navbar.tsx +++ b/components/open-library/Navbar.tsx @@ -1,7 +1,22 @@ +import dynamic from 'next/dynamic'; import React from 'react'; import { Button, Form, FormControl, Nav, Navbar } from 'react-bootstrap'; -const NavbarComponent = () => { +import { t } from '../../models/Translation'; + +// 动态导入 LanguageMenu 组件,禁用 SSR +const LanguageMenu = dynamic(() => import('../Navigator/LanguageMenu'), { + ssr: false, +}); + +const LibraryNavbar = () => { + // Use a client-side only rendering approach for the brand text to avoid hydration mismatch + const [isMounted, setIsMounted] = React.useState(false); + + React.useEffect(() => { + setIsMounted(true); + }, []); + return ( {/* 使用自定义的居中容器替代 Container fluid,与页面内容保持一致的宽度和居中效果 */} @@ -22,36 +37,27 @@ const NavbarComponent = () => { href="/open-library" className="d-flex align-items-center" > - Open Library + {/* Use a static placeholder during SSR and replace it with translated content after hydration */} + {isMounted ? t('open_library') : 'Open Library'} - - - - - + {/* 添加语言切换菜单 */} + ); }; -export default NavbarComponent; +export default LibraryNavbar; diff --git a/components/open-library/useOpenLibraryLayout.ts b/components/open-library/useOpenLibraryLayout.ts new file mode 100644 index 0000000..cd15c3d --- /dev/null +++ b/components/open-library/useOpenLibraryLayout.ts @@ -0,0 +1,20 @@ +import { useEffect } from 'react'; + +/** + * Hook to apply Open Library layout styles + * This adds the open-library class to the body and html elements + * which triggers the CSS rules in styles/open-library.css + */ +export function useOpenLibraryLayout() { + useEffect(() => { + // Add open-library class to body and html + document.body.classList.add('open-library'); + document.documentElement.classList.add('open-library'); + + // Clean up function to remove classes when component unmounts + return () => { + document.body.classList.remove('open-library'); + document.documentElement.classList.remove('open-library'); + }; + }, []); +} diff --git a/pages/_app.tsx b/pages/_app.tsx index 99c7009..12b87f9 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,4 +1,5 @@ import '../styles/globals.css'; +import '../styles/open-library.css'; import { HTTPError } from 'koajax'; import { configure } from 'mobx'; diff --git a/pages/api/hello.ts b/pages/api/hello.ts index f8bcc7e..46714b3 100644 --- a/pages/api/hello.ts +++ b/pages/api/hello.ts @@ -1,13 +1,13 @@ // Next.js API route support: https://nextjs.org/docs/api-routes/introduction -import type { NextApiRequest, NextApiResponse } from 'next' +import type { NextApiRequest, NextApiResponse } from 'next'; type Data = { - name: string -} + name: string; +}; export default function handler( req: NextApiRequest, - res: NextApiResponse + res: NextApiResponse, ) { - res.status(200).json({ name: 'John Doe' }) + res.status(200).json({ name: 'John Doe' }); } diff --git a/pages/open-library/about.tsx b/pages/open-library/about.tsx new file mode 100644 index 0000000..c8a8bf1 --- /dev/null +++ b/pages/open-library/about.tsx @@ -0,0 +1,201 @@ +import React from 'react'; +import { Card, Col, Container, Image, Row } from 'react-bootstrap'; + +import { ContentContainer, Layout } from '../../components/open-library/Layout'; + +export default function AboutPage() { + return ( + + +
+

关于社区图书馆

+ + + +

我们的使命

+

+ freeCodeCamp 成都社区「Open + Library」是一个开放、共享的社区图书馆,旨在促进知识交流,推动社区成员之间的学习和成长,增强社区成员之间的互动与信任。 +

+

+ 我们深知知识的流动能够点燃创新的火花,因此,我们采用了一种独特的"无储存"借阅模式,让书籍在会员之间自由流转,而非集中存放。这种模式不仅节省了物理空间,更重要的是促进了社区成员之间的直接交流和互动。 +

+
+
+ + + + + +

我们的价值观

+
    +
  • +
    +
    + +
    +
    +
    开放共享
    +

    + 我们相信知识应该是开放和共享的,每个人都有权利获取和贡献知识。 +

    +
    +
    +
  • +
  • +
    +
    + +
    +
    +
    社区驱动
    +

    + 我们的图书馆完全由社区成员驱动,每个人都可以贡献和受益。 +

    +
    +
    +
  • +
  • +
    +
    + +
    +
    +
    信任与责任
    +

    + 我们基于互相信任建立借阅系统,同时鼓励每个人对所借书籍负责。 +

    +
    +
    +
  • +
+
+
+ + + + +

我们的特色

+
    +
  • +
    +
    + +
    +
    +
    "无储存"借阅模式
    +

    + 书籍在会员之间直接流转,无需集中存放,促进社区成员之间的直接交流。 +

    +
    +
    +
  • +
  • +
    +
    + +
    +
    +
    社区捐赠
    +

    + 所有书籍均来自社区成员的捐赠,体现了共享和互助的精神。 +

    +
    +
    +
  • +
  • +
    +
    + +
    +
    +
    线上管理
    +

    + 通过飞书多维表格进行书籍管理和借阅记录,方便透明。 +

    +
    +
    +
  • +
+
+
+ +
+ + + +

我们的团队

+

+ Open Library 由 freeCodeCamp + 成都社区的志愿者团队运营。我们的团队成员来自各行各业,但都有一个共同的目标:促进知识共享和社区建设。 +

+ + + + +
张伟
+

图书馆创始人

+ + + +
李梅
+

社区协调员

+ + + +
王晨
+

技术支持

+ +
+
+
+ + + +

加入我们

+

+ 如果你热爱阅读,愿意分享知识,欢迎加入 freeCodeCamp + 成都社区「Open Library」,成为知识流动的一部分! +

+

成为会员后,你可以:

+
    +
  • 借阅其他会员捐赠的书籍
  • +
  • 捐赠自己的书籍给社区
  • +
  • 参与社区组织的读书会和讨论
  • +
  • 结识志同道合的朋友
  • +
+ +
+
+
+
+
+ ); +} diff --git a/pages/open-library/book/[id].tsx b/pages/open-library/book/[id].tsx new file mode 100644 index 0000000..bf330a8 --- /dev/null +++ b/pages/open-library/book/[id].tsx @@ -0,0 +1,453 @@ +import { useRouter } from 'next/router'; +import React, { useEffect, useState } from 'react'; +import { Badge, Button, Card, Col, Row, Tab, Tabs } from 'react-bootstrap'; + +import { + ContentContainer, + Layout, +} from '../../../components/open-library/Layout'; + +// Book type definition +type Book = { + id: number; + title: string; + author: string; + cover?: string; + category: string; + language: string; + status: 'available' | 'borrowed'; + currentHolder?: string; + description?: string; + isbn?: string; + publisher?: string; + publishYear?: number; + pageCount?: number; + borrowHistory?: { + borrower: string; + borrowDate: string; + returnDate?: string; + }[]; + reviews?: { + reviewer: string; + rating: number; + comment: string; + date: string; + }[]; +}; + +// Mock database of books +const booksDatabase: Book[] = [ + { + id: 1, + title: 'Clean Code', + author: 'Robert C. Martin', + cover: '/images/placeholder-book.svg', + category: 'Programming', + language: 'English', + status: 'available', + description: + "A handbook of agile software craftsmanship. Even bad code can function. But if code isn't clean, it can bring a development organization to its knees. Every year, countless hours and significant resources are lost because of poorly written code. But it doesn't have to be that way.", + isbn: '9780132350884', + publisher: 'Prentice Hall', + publishYear: 2008, + pageCount: 464, + borrowHistory: [ + { + borrower: 'Wang Chen', + borrowDate: '2023-10-15', + returnDate: '2023-11-20', + }, + { + borrower: 'Li Mei', + borrowDate: '2023-08-01', + returnDate: '2023-09-05', + }, + ], + reviews: [ + { + reviewer: 'Wang Chen', + rating: 5, + comment: + 'This book completely changed how I approach writing code. Highly recommended for all developers!', + date: '2023-11-25', + }, + { + reviewer: 'Li Mei', + rating: 4, + comment: + 'Great principles that have stood the test of time. Some examples are a bit dated but the concepts are solid.', + date: '2023-09-10', + }, + ], + }, + { + id: 2, + title: 'Eloquent JavaScript', + author: 'Marijn Haverbeke', + cover: '/images/placeholder-book.svg', + category: 'Programming', + language: 'English', + status: 'borrowed', + currentHolder: 'Zhang Wei', + description: + 'A modern introduction to programming. JavaScript lies at the heart of almost every modern web application, from social apps like Twitter to browser-based game frameworks like Phaser and Babylon. Though simple for beginners to pick up and play with, JavaScript is a flexible, complex language that you can use to build full-scale applications.', + isbn: '9781593279509', + publisher: 'No Starch Press', + publishYear: 2018, + pageCount: 472, + borrowHistory: [ + { + borrower: 'Zhang Wei', + borrowDate: '2024-03-10', + }, + ], + reviews: [ + { + reviewer: 'Liu Jie', + rating: 5, + comment: + 'Perfect for beginners and intermediate JavaScript developers. The exercises are particularly helpful.', + date: '2023-12-05', + }, + ], + }, + { + id: 3, + title: 'Design Patterns', + author: 'Erich Gamma et al.', + cover: '/images/placeholder-book.svg', + category: 'Programming', + language: 'English', + status: 'available', + description: + 'Elements of Reusable Object-Oriented Software. Capturing a wealth of experience about the design of object-oriented software, four top-notch designers present a catalog of simple and succinct solutions to commonly occurring design problems.', + isbn: '9780201633610', + publisher: 'Addison-Wesley Professional', + publishYear: 1994, + pageCount: 416, + borrowHistory: [ + { + borrower: 'Chen Ming', + borrowDate: '2023-06-15', + returnDate: '2023-07-20', + }, + ], + reviews: [ + { + reviewer: 'Chen Ming', + rating: 4, + comment: + 'A classic that has stood the test of time. The examples are in C++ and Smalltalk, but the concepts apply to any OO language.', + date: '2023-07-25', + }, + ], + }, + { + id: 4, + title: "You Don't Know JS", + author: 'Kyle Simpson', + cover: '/images/placeholder-book.svg', + category: 'Programming', + language: 'English', + status: 'available', + description: + 'A book series on JavaScript. This is a series of books diving deep into the core mechanisms of the JavaScript language.', + isbn: '9781491924464', + publisher: "O'Reilly Media", + publishYear: 2015, + pageCount: 278, + borrowHistory: [], + reviews: [], + }, + { + id: 5, + title: '深入理解计算机系统', + author: 'Randal E. Bryant', + cover: '/images/placeholder-book.svg', + category: 'Computer Science', + language: 'Chinese', + status: 'borrowed', + currentHolder: 'Li Mei', + description: + "Computer Systems: A Programmer's Perspective (Chinese Edition). This book introduces the important and enduring concepts that underlie computer systems.", + isbn: '9787111544937', + publisher: '机械工业出版社', + publishYear: 2016, + pageCount: 731, + borrowHistory: [ + { + borrower: 'Li Mei', + borrowDate: '2024-02-01', + }, + ], + reviews: [ + { + reviewer: 'Zhang Wei', + rating: 5, + comment: + '这是一本非常全面的计算机系统书籍,对理解计算机底层工作原理非常有帮助。', + date: '2023-10-15', + }, + ], + }, +]; + +export default function BookDetail() { + const router = useRouter(); + const { id } = router.query; + const [book, setBook] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + if (id) { + // In a real app, this would be an API call + const bookId = Array.isArray(id) + ? parseInt(id[0]) + : parseInt(id as string); + const foundBook = booksDatabase.find(b => b.id === bookId); + + setBook(foundBook || null); + setLoading(false); + } + }, [id]); + + if (loading) { + return ( + + +
+
+ Loading... +
+
+
+
+ ); + } + + if (!book) { + return ( + + +
+

Book Not Found

+

Sorry, we couldn't find the book you're looking for.

+ +
+
+
+ ); + } + + return ( + + +
+ + + + + + + {book.title} +
+ + {book.status === 'available' + ? 'Available' + : 'Currently Borrowed'} + +
+ {book.status === 'borrowed' && book.currentHolder && ( +
+ Current holder: {book.currentHolder} +
+ )} + + +

{book.title}

+
by {book.author}
+ +
+ + {book.category} + + + {book.language} + +
+ +

{book.description}

+ + + +
ISBN
+
{book.isbn || 'N/A'}
+ + +
Publisher
+
{book.publisher || 'N/A'}
+ + +
Published
+
{book.publishYear || 'N/A'}
+ + +
Pages
+
{book.pageCount || 'N/A'}
+ +
+ +
+ {book.status === 'available' ? ( + + ) : ( + + )} +
+ +
+
+
+ +
+ + + {book.reviews && book.reviews.length > 0 ? ( +
+ {book.reviews.map((review, index) => ( + + +
+
{review.reviewer}
+
+ {[...Array(5)].map((_, i) => ( + + {i < review.rating ? '\u2605' : '\u2606'} + + ))} +
+
+ {review.comment} + + + {new Date(review.date).toLocaleDateString()} + + +
+
+ ))} +
+ +
+
+ ) : ( +
+

+ No reviews yet. Be the first to review this book! +

+ +
+ )} +
+ + {book.borrowHistory && book.borrowHistory.length > 0 ? ( +
+ + + + + + + + + + + {book.borrowHistory.map((history, index) => ( + + + + + + + ))} + +
BorrowerBorrow DateReturn DateStatus
{history.borrower} + {new Date( + history.borrowDate, + ).toLocaleDateString()} + + {history.returnDate + ? new Date( + history.returnDate, + ).toLocaleDateString() + : '-'} + + {history.returnDate ? ( + Returned + ) : ( + + Active + + )} +
+
+ ) : ( +
+

This book has not been borrowed yet.

+
+ )} +
+
+
+
+
+
+ ); +} diff --git a/pages/open-library/books/index.tsx b/pages/open-library/books/index.tsx new file mode 100644 index 0000000..c18b6a0 --- /dev/null +++ b/pages/open-library/books/index.tsx @@ -0,0 +1,318 @@ +import React, { useEffect, useState } from 'react'; +import { + Button, + Card, + Col, + Form, + FormControl, + InputGroup, + Row, +} from 'react-bootstrap'; + +import { + ContentContainer, + Layout, +} from '../../../components/open-library/Layout'; + +// Book type definition +type Book = { + id: number; + title: string; + author: string; + cover?: string; + category: string; + language: string; + status: 'available' | 'borrowed'; + currentHolder?: string; + description?: string; +}; + +export default function BookCatalog() { + // Sample books data - in a real app, this would come from an API + const [books, setBooks] = useState([ + { + id: 1, + title: 'Clean Code', + author: 'Robert C. Martin', + cover: '/images/placeholder-book.svg', + category: 'Programming', + language: 'English', + status: 'available', + description: 'A handbook of agile software craftsmanship', + }, + { + id: 2, + title: 'Eloquent JavaScript', + author: 'Marijn Haverbeke', + cover: '/images/placeholder-book.svg', + category: 'Programming', + language: 'English', + status: 'borrowed', + currentHolder: 'Zhang Wei', + description: 'A modern introduction to programming', + }, + { + id: 3, + title: 'Design Patterns', + author: 'Erich Gamma et al.', + cover: '/images/placeholder-book.svg', + category: 'Programming', + language: 'English', + status: 'available', + description: 'Elements of Reusable Object-Oriented Software', + }, + { + id: 4, + title: "You Don't Know JS", + author: 'Kyle Simpson', + cover: '/images/placeholder-book.svg', + category: 'Programming', + language: 'English', + status: 'available', + description: 'A book series on JavaScript', + }, + { + id: 5, + title: '深入理解计算机系统', + author: 'Randal E. Bryant', + cover: '/images/placeholder-book.svg', + category: 'Computer Science', + language: 'Chinese', + status: 'borrowed', + currentHolder: 'Li Mei', + description: + "Computer Systems: A Programmer's Perspective (Chinese Edition)", + }, + { + id: 6, + title: '算法导论', + author: 'Thomas H. Cormen et al.', + cover: '/images/placeholder-book.svg', + category: 'Computer Science', + language: 'Chinese', + status: 'available', + description: 'Introduction to Algorithms (Chinese Edition)', + }, + { + id: 7, + title: 'JavaScript高级程序设计', + author: 'Nicholas C. Zakas', + cover: '/images/placeholder-book.svg', + category: 'Programming', + language: 'Chinese', + status: 'available', + description: + 'Professional JavaScript for Web Developers (Chinese Edition)', + }, + { + id: 8, + title: 'CSS揭秘', + author: 'Lea Verou', + cover: '/images/placeholder-book.svg', + category: 'Web Development', + language: 'Chinese', + status: 'borrowed', + currentHolder: 'Wang Chen', + description: 'CSS Secrets (Chinese Edition)', + }, + ]); + + const [filteredBooks, setFilteredBooks] = useState(books); + const [searchTerm, setSearchTerm] = useState(''); + const [categoryFilter, setCategoryFilter] = useState(''); + const [languageFilter, setLanguageFilter] = useState(''); + const [statusFilter, setStatusFilter] = useState(''); + + // Extract unique categories and languages for filter dropdowns + const categories = [...new Set(books.map(book => book.category))]; + const languages = [...new Set(books.map(book => book.language))]; + + // Apply filters when any filter changes + useEffect(() => { + let results = books; + + // Apply search term filter + if (searchTerm) { + const term = searchTerm.toLowerCase(); + results = results.filter( + book => + book.title.toLowerCase().includes(term) || + book.author.toLowerCase().includes(term) || + (book.description && book.description.toLowerCase().includes(term)), + ); + } + + // Apply category filter + if (categoryFilter) { + results = results.filter(book => book.category === categoryFilter); + } + + // Apply language filter + if (languageFilter) { + results = results.filter(book => book.language === languageFilter); + } + + // Apply status filter + if (statusFilter) { + results = results.filter(book => book.status === statusFilter); + } + + setFilteredBooks(results); + }, [searchTerm, categoryFilter, languageFilter, statusFilter, books]); + + // Reset all filters + const resetFilters = () => { + setSearchTerm(''); + setCategoryFilter(''); + setLanguageFilter(''); + setStatusFilter(''); + }; + + return ( + + +

Book Catalog

+ + {/* Search and Filters */} + + + + + + setSearchTerm(e.target.value)} + /> + + + + + + + setCategoryFilter(e.target.value)} + > + + {categories.map(category => ( + + ))} + + + + setLanguageFilter(e.target.value)} + > + + {languages.map(language => ( + + ))} + + + + setStatusFilter(e.target.value)} + > + + + + + + + + +
+
+ + Showing {filteredBooks.length} of {books.length} books + +
+ +
+
+
+ + {/* Book Grid */} + + {filteredBooks.map(book => ( + + +
+ +
+ + {book.title} + + {book.author} + + + {book.description} + +
+
+ + {book.category} + + + {book.language} + +
+
+ + {book.status === 'available' ? 'Available' : 'Borrowed'} + + +
+ {book.status === 'borrowed' && ( +
+ Currently with: {book.currentHolder} +
+ )} +
+
+
+ + ))} +
+ + {/* No Results */} + {filteredBooks.length === 0 && ( +
+

No books found

+

Try adjusting your search or filters

+ +
+ )} +
+
+ ); +} diff --git a/pages/open-library/donate.tsx b/pages/open-library/donate.tsx new file mode 100644 index 0000000..7e54e0d --- /dev/null +++ b/pages/open-library/donate.tsx @@ -0,0 +1,254 @@ +import React from 'react'; +import { Button, Card, Col, Row } from 'react-bootstrap'; + +import { ContentContainer, Layout } from '../../components/open-library/Layout'; + +export default function DonatePage() { + return ( + + +
+

书籍募捐

+ + + +

为什么捐赠书籍?

+

+ 您的一本书,可能会改变他人的一生。通过捐赠书籍,您不仅能够让闲置的知识重新流动,还能够帮助更多的人获取学习资源。 +

+

+ 在 Open + Library,我们相信知识应该是流动的。您捐赠的每一本书都将在社区成员之间传递,让更多人受益。当您捐赠一本书,您不仅是在分享一个物品,更是在分享知识、经验和思想。 +

+
+
+ + + + + +

捐赠流程

+
    +
  1. +
    +
    填写捐赠申请
    +

    + 填写我们的在线表单,提供您想要捐赠的书籍信息。 +

    +
    +
  2. +
  3. +
    +
    书籍登记
    +

    + 如所捐赠书籍在「fCC + 成都社区图书馆」还未登记,请先填写书籍登记表,然后回到第 + 1 步继续捐赠。建议在移动端设备上登记,方便扫描书籍 + ISBN 码。 +

    +
    +
  4. +
  5. +
    +
    书籍进入图书馆
    +

    + 完成以上流程后,书籍正式进入 Open Library + 的传递流程,开始流转于社区成员之间。 +

    +
    +
  6. +
+ +
+
+ + + + +

书籍登记

+

+ 如果您要捐赠的书籍在我们的图书馆中还未登记,请先完成书籍登记流程。这有助于我们维护一个完整的书籍数据库,方便其他会员查找和借阅。 +

+

登记时,您需要提供以下信息:

+
    +
  • 书籍标题
  • +
  • 作者
  • +
  • ISBN(可通过扫描获取)
  • +
  • 出版社
  • +
  • 出版年份
  • +
  • 书籍类别
  • +
  • 书籍语言
  • +
  • 书籍简介
  • +
  • 书籍封面照片(可选)
  • +
+ +
+
+ +
+ + + +

我们接受什么样的书籍?

+ + +
✓ 我们欢迎
+
    +
  • 技术类书籍(编程、设计、数据科学等)
  • +
  • 科学类书籍
  • +
  • 商业和管理类书籍
  • +
  • 自我提升类书籍
  • +
  • 文学和小说
  • +
  • 状态良好的二手书
  • +
+ + +
✗ 不适合捐赠
+
    +
  • 严重破损或缺页的书籍
  • +
  • 有大量笔记或标记的书籍
  • +
  • 过时的技术书籍(5年以上的技术书可能已过时)
  • +
  • 教科书和习题集
  • +
  • 杂志和期刊
  • +
+ +
+
+
+ + + + +
+ + + +

常见问题

+
+
+

+ +

+
+
+ 您捐赠的书籍将进入我们的借阅系统,供社区成员借阅。当有人申请借阅时,书籍将从当前持有者(可能是您)直接传递给下一位借阅者。 +
+
+
+
+

+ +

+
+
+ 一旦书籍捐赠给图书馆,我们视为您已将书籍所有权转让给社区。如果您希望再次阅读这本书,可以通过正常的借阅流程申请借阅。 +
+
+
+
+

+ +

+
+
+ 您可以在我们的飞书多维表格中查看每本书的借阅记录,包括当前持有者和历史借阅情况。 +
+
+
+
+

+ +

+
+
+ 是的,我们要求捐赠者成为 Open Library + 的会员。这有助于我们建立一个基于信任的社区,并确保书籍的流转质量。 +
+
+
+
+
+ +
+
+ +
+

准备好捐赠书籍了吗?

+ +
+
+
+
+ ); +} diff --git a/pages/open-library/how-to-borrow.tsx b/pages/open-library/how-to-borrow.tsx new file mode 100644 index 0000000..451dfea --- /dev/null +++ b/pages/open-library/how-to-borrow.tsx @@ -0,0 +1,294 @@ +import React from 'react'; +import { Button, Card, Col, Row } from 'react-bootstrap'; + +import { ContentContainer, Layout } from '../../components/open-library/Layout'; + +export default function HowToBorrowPage() { + return ( + + +
+

如何借阅

+ + + +

借阅与传递模式

+

+ 在 Open Library,所有书籍均来自社区成员的捐赠,并由借阅者直接 + 传递给下一位借书人。 +

+

+ 我们采用的是一种独特的"无储存"借阅模式,让书籍在会员之间自由流转,而非集中存放。这种模式不仅节省了物理空间,更重要的是促进了社区成员之间的直接交流和互动。 +

+
+
+ + + + + +

借阅流程

+
+
+ +
+
+

1

+
+
+
查阅书籍
+

+ 社区成员可以在{' '} + + fCC 成都社区图书馆 + {' '} + 中查找自己感兴趣的书籍,或者在我们的{' '} + 书籍目录 中浏览。 +

+
+
+ +
+
+

2

+
+
+
申请借阅
+

+ 找到心仪的书籍后,填写{' '} + + fCC 成都社区图书馆-书籍借入 + {' '} + 申请,与当前持书者取得联系。 +

+
+
+ +
+
+

3

+
+
+
线下传递
+

+ 双方约定时间和传递方式,通常可用快递传递书籍。请传递者填写{' '} + + fCC 成都社区图书馆-书籍传递 + + ,再将书籍传递出去。 +

+
+
+ +
+
+

4

+
+
+
阅读与分享
+

+ 借阅者在阅读完毕后,可以分享自己的阅读感悟,并在社区推荐给下一位感兴趣的成员。我们鼓励借阅者在归还前写下简短的书评。 +

+
+
+ +
+
+

5

+
+
+
继续传递
+

+ 当有新的借阅者申请时,当前持书人将书籍传递给下一位读者,确保知识的持续流动。 +

+
+
+
+
+
+ + + + +

借阅规则

+
    +
  • + 借阅期限: 每本书的标准借阅期为 30 + 天,如需延长可与图书馆管理员联系。 +
  • +
  • + 借阅数量: 每位会员同时最多可借阅 3 + 本书。 +
  • +
  • + 书籍状态:{' '} + 借阅者有责任保持书籍的良好状态,避免损坏、标记或丢失。 +
  • +
  • + 传递责任:{' '} + 当前持书人负责将书籍安全传递给下一位借阅者,并承担相关的传递费用。 +
  • +
  • + 丢失或损坏:{' '} + 如果书籍在您借阅期间丢失或严重损坏,请联系图书馆管理员并考虑捐赠一本相同或类似的书籍作为替代。 +
  • +
+
+
+ + + +

快速链接

+ +
+
+ +
+ + + +

常见问题解答

+ + +
如何知道一本书是否可借?
+

+ 您可以在飞书多维表格或我们的网站书籍目录中查看书籍的当前状态。如果标记为"可借阅",则表示该书可以申请借阅。 +

+ + +
我需要支付借阅费用吗?
+

+ Open Library + 不收取借阅费用,但借阅者需要承担书籍传递的相关费用(如快递费)。 +

+ + +
如果当前没有人申请借我手中的书,我需要归还吗?
+

+ 标准借阅期为 30 + 天。如果期满后没有新的借阅申请,您可以继续保留该书,但请随时准备传递给下一位申请者。 +

+ + +
如何联系当前持书人?
+

+ 当您提交借阅申请后,我们会为您提供当前持书人的联系方式,以便您们协商传递事宜。 +

+ + +
如果我想长期保留一本书怎么办?
+

+ Open Library + 的宗旨是促进知识流动,我们鼓励书籍在会员之间传递。如果您特别喜欢某本书,建议购买一本自己的副本,或者考虑捐赠一本相同的书籍给图书馆。 +

+ + +
我可以借阅电子书吗?
+

+ 目前 Open Library + 主要提供实体书的借阅服务。我们正在考虑未来增加电子书资源,敬请期待。 +

+ +
+
+
+ +
+

准备好借阅了吗?

+ +
+
+
+
+ ); +} diff --git a/pages/open-library/index.tsx b/pages/open-library/index.tsx index 074b5e7..5853be1 100644 --- a/pages/open-library/index.tsx +++ b/pages/open-library/index.tsx @@ -1,8 +1,9 @@ import Link from 'next/link'; -import React, { useEffect } from 'react'; +import React from 'react'; import { Button, Card, Col, Form, Image, Row, Stack } from 'react-bootstrap'; import { ContentContainer, Layout } from '../../components/open-library/Layout'; +import { t } from '../../models/Translation'; // TODO: Define a type for Book and Testimonial type Book = { @@ -69,160 +70,8 @@ export default function OpenLibraryHomepage() { }, ]; - // Use client-side code to hide the main site header and adjust layout - useEffect(() => { - // Function to apply style to an element - const applyStyle = ( - element: HTMLElement | null, - styles: Partial, - ) => { - if (!element) return; - - Object.entries(styles).forEach(([property, value]) => { - if (value) { - // @ts-expect-error: dynamic property assignment - element.style[property] = value; - } - }); - }; - - // Hide the main site header - const mainHeader = document.querySelector( - 'nav.navbar.bg-dark.navbar-dark.fixed-top', - ); - applyStyle(mainHeader as HTMLElement, { display: 'none' }); - - // Remove top margin that accommodates the main header - const mainContent = document.querySelector('div.mt-5.pt-2'); - applyStyle(mainContent as HTMLElement, { - marginTop: '0', - paddingTop: '0', - maxWidth: '100%', - width: '100%', - }); - - // Remove background and padding from main content - const mainWrapper = document.querySelector( - 'main.flex-fill.d-flex.flex-column.justify-content-start.align-items-center', - ); - applyStyle(mainWrapper as HTMLElement, { - background: 'none', - padding: '0', - margin: '0', - maxWidth: '100%', - width: '100%', - }); - - // Remove container constraints - const containers = document.querySelectorAll('.container'); - containers.forEach(container => { - applyStyle(container as HTMLElement, { - maxWidth: '100%', - padding: '0', - margin: '0', - }); - }); - - // Remove card styling - const cards = document.querySelectorAll('.card'); - cards.forEach(card => { - if ( - card.closest( - 'main.flex-fill.d-flex.flex-column.justify-content-start.align-items-center', - ) - ) { - applyStyle(card as HTMLElement, { - background: 'transparent', - border: 'none', - boxShadow: 'none', - padding: '0', - margin: '0', - }); - } - }); - - // Remove card body padding - const cardBodies = document.querySelectorAll('.card-body'); - cardBodies.forEach(cardBody => { - if ( - cardBody.closest( - 'main.flex-fill.d-flex.flex-column.justify-content-start.align-items-center', - ) - ) { - applyStyle(cardBody as HTMLElement, { - padding: '0', - }); - } - }); - - // Hide the main site footer - const mainFooter = document.querySelector( - 'footer.mw-100.bg-dark.text-white', - ); - applyStyle(mainFooter as HTMLElement, { display: 'none' }); - - // Remove all padding and margin from body and html - applyStyle(document.body, { - margin: '0', - padding: '0', - overflow: 'auto', - }); - - applyStyle(document.documentElement, { - margin: '0', - padding: '0', - overflow: 'auto', - }); - - // Target the MDXProvider wrapper (main white background) - const mdxProvider = document.querySelector(`.MDXProvider`); - applyStyle(mdxProvider as HTMLElement, { - padding: '0 !important', - margin: '0 !important', - background: 'transparent !important', - border: 'none !important', - boxShadow: 'none !important', - }); - - // Add custom styles to ensure full width - const style = document.createElement('style'); - style.textContent = ` - body, html { - margin: 0 !important; - padding: 0 !important; - overflow-x: hidden !important; - } - .container, .container-fluid { - max-width: 100% !important; - padding-left: 0 !important; - padding-right: 0 !important; - margin-left: 0 !important; - margin-right: 0 !important; - } - main.flex-fill.d-flex.flex-column.justify-content-start.align-items-center { - padding: 0 !important; - margin: 0 !important; - max-width: 100% !important; - width: 100% !important; - } - main.flex-fill.d-flex.flex-column.justify-content-start.align-items-center > .container { - max-width: 100% !important; - padding: 0 !important; - margin: 0 !important; - } - .MDXProvider { - padding: 0 !important; - margin: 0 !important; - background: transparent !important; - border: none !important; - box-shadow: none !important; - } - `; - document.head.appendChild(style); - }, []); - return ( - +
{/* --- Hero Section --- */}
-

- Free knowledge flows here -

-

- Share and borrow books in our open-source community. Join - freeCodeCamp Chengdu's initiative to make learning accessible - to everyone. -

+

{t('hero_title')}

+

{t('hero_subtitle')}

{/* TODO: Link this button to the actual Feishu form or member sign-up page */} - @@ -265,9 +114,11 @@ export default function OpenLibraryHomepage() { {/* --- Featured Books Section --- */}
-

Featured Books

+

+ {t('featured_books')} +

- Discover what our community is reading right now + {t('featured_books_subtitle')}

{featuredBooks.map(book => ( @@ -299,7 +150,7 @@ export default function OpenLibraryHomepage() { legacyBehavior > @@ -314,7 +165,7 @@ export default function OpenLibraryHomepage() { variant="link" className="text-success text-decoration-none" > - Browse All Books + {t('view_all_books')} → @@ -326,14 +177,10 @@ export default function OpenLibraryHomepage() { -

Share Your Knowledge

-

- Have books collecting dust on your shelf? Donate them to our - community and help others learn and grow. Your contribution - makes a difference! -

+

{t('share_your_knowledge')}

+

{t('donation_callout_subtitle')}

- + @@ -353,40 +200,31 @@ export default function OpenLibraryHomepage() { {/* --- How It Works Section --- */}
-

How It Works

+

{t('how_it_works')}

- Three simple steps to borrow books from our community + {t('how_it_works_description')}

-

1. Find a Book

-

- Browse our collection and find the book that interests you. - Filter by category, author, or popularity. -

+

{t('step_1_find_book')}

+

{t('step_1_description')}

-

2. Apply to Borrow

-

- Submit a simple request form. We'll connect you with the book - owner and arrange the handover. -

+

{t('step_2_apply')}

+

{t('step_2_description')}

-

3. Receive and Pass It On

-

- Enjoy your book and return it when you're done. Consider - donating your own books to keep knowledge flowing. -

+

{t('step_3_receive')}

+

{t('step_3_description')}

@@ -395,7 +233,7 @@ export default function OpenLibraryHomepage() { variant="link" className="text-success text-decoration-none" > - Learn More About Borrowing + {t('learn_more_about_borrowing')}
@@ -405,9 +243,11 @@ export default function OpenLibraryHomepage() { {/* --- Testimonials Section --- */}
-

Community Voices

+

+ {t('community_voices')} +

- What our members say about Open Library + {t('community_voices_description')}

{testimonials.map(testimonial => ( @@ -431,7 +271,7 @@ export default function OpenLibraryHomepage() { variant="link" className="text-success text-decoration-none" > - Read More Reviews + {t('read_more_reviews')} @@ -445,10 +285,9 @@ export default function OpenLibraryHomepage() { -

Stay Updated

+

{t('stay_updated')}

- Subscribe to our newsletter for new book arrivals and - community events. + {t('newsletter_description')}

{/* TODO: Implement actual newsletter subscription logic */}
e.preventDefault()}> @@ -462,11 +301,11 @@ export default function OpenLibraryHomepage() { > diff --git a/pages/open-library/review.tsx b/pages/open-library/review.tsx new file mode 100644 index 0000000..28222cf --- /dev/null +++ b/pages/open-library/review.tsx @@ -0,0 +1,372 @@ +import React, { useState } from 'react'; +import { Button, Card, Col, Form, Row, Tab, Tabs } from 'react-bootstrap'; + +import { ContentContainer, Layout } from '../../components/open-library/Layout'; + +// Review type definition +type Review = { + id: number; + bookTitle: string; + bookAuthor: string; + bookCover?: string; + reviewer: string; + rating: number; + comment: string; + date: string; +}; + +export default function ReviewPage() { + // Sample reviews data - in a real app, this would come from an API + const [reviews, setReviews] = useState([ + { + id: 1, + bookTitle: 'Clean Code', + bookAuthor: 'Robert C. Martin', + bookCover: '/images/placeholder-book.svg', + reviewer: 'Wang Chen', + rating: 5, + comment: + 'This book completely changed how I approach writing code. Highly recommended for all developers!', + date: '2023-11-25', + }, + { + id: 2, + bookTitle: 'Clean Code', + bookAuthor: 'Robert C. Martin', + bookCover: '/images/placeholder-book.svg', + reviewer: 'Li Mei', + rating: 4, + comment: + 'Great principles that have stood the test of time. Some examples are a bit dated but the concepts are solid.', + date: '2023-09-10', + }, + { + id: 3, + bookTitle: 'Eloquent JavaScript', + bookAuthor: 'Marijn Haverbeke', + bookCover: '/images/placeholder-book.svg', + reviewer: 'Liu Jie', + rating: 5, + comment: + 'Perfect for beginners and intermediate JavaScript developers. The exercises are particularly helpful.', + date: '2023-12-05', + }, + { + id: 4, + bookTitle: 'Design Patterns', + bookAuthor: 'Erich Gamma et al.', + bookCover: '/images/placeholder-book.svg', + reviewer: 'Chen Ming', + rating: 4, + comment: + 'A classic that has stood the test of time. The examples are in C++ and Smalltalk, but the concepts apply to any OO language.', + date: '2023-07-25', + }, + { + id: 5, + bookTitle: '深入理解计算机系统', + bookAuthor: 'Randal E. Bryant', + bookCover: '/images/placeholder-book.svg', + reviewer: 'Zhang Wei', + rating: 5, + comment: + '这是一本非常全面的计算机系统书籍,对理解计算机底层工作原理非常有帮助。', + date: '2023-10-15', + }, + ]); + + // Function to render star rating + const renderStars = (rating: number) => { + return ( +
+ {[...Array(5)].map((_, i) => ( + + {i < rating ? '\u2605' : '\u2606'} + + ))} +
+ ); + }; + + return ( + + +
+

书籍评价

+ + + +

社区书评

+

+ 阅读是一种体验,分享让这种体验更加丰富。浏览社区成员的书评,或者分享您自己的阅读感受。 +

+
+ +
+
+
+ + + +
+ {reviews.map(review => ( + + + + +
+ {review.bookTitle} +
{review.bookTitle}
+

+ {review.bookAuthor} +

+
+ + +
+
{review.reviewer}
+ {renderStars(review.rating)} +
+

{review.comment}

+
+ + {new Date(review.date).toLocaleDateString( + 'en-US', + { + year: 'numeric', + month: '2-digit', + day: '2-digit', + }, + )} + + +
+ +
+
+
+ ))} +
+
+ +
+ {reviews + .sort( + (a, b) => + new Date(b.date).getTime() - new Date(a.date).getTime(), + ) + .slice(0, 3) + .map(review => ( + + + + +
+ {review.bookTitle} +
+ {review.bookTitle} +
+

+ {review.bookAuthor} +

+
+ + +
+
{review.reviewer}
+ {renderStars(review.rating)} +
+

{review.comment}

+
+ + {new Date(review.date).toLocaleDateString( + 'en-US', + { + year: 'numeric', + month: '2-digit', + day: '2-digit', + }, + )} + + +
+ +
+
+
+ ))} +
+
+ +
+ {reviews + .filter(review => review.rating >= 5) + .map(review => ( + + + + +
+ {review.bookTitle} +
+ {review.bookTitle} +
+

+ {review.bookAuthor} +

+
+ + +
+
{review.reviewer}
+ {renderStars(review.rating)} +
+

{review.comment}

+
+ + {new Date(review.date).toLocaleDateString( + 'en-US', + { + year: 'numeric', + month: '2-digit', + day: '2-digit', + }, + )} + + +
+ +
+
+
+ ))} +
+
+
+ + + +

写下您的书评

+

+ 阅读完一本书后,请花几分钟时间分享您的阅读体验。您的评价将帮助其他社区成员找到适合他们的书籍。 +

+
+ +
+
+
+ + + +

书评指南

+ + +
如何写一篇有帮助的书评
+
    +
  • 简要概述书籍内容,但不要透露关键情节
  • +
  • 分享您从书中获得的主要收获
  • +
  • 提及书籍的优点和可能的缺点
  • +
  • 说明适合阅读这本书的人群
  • +
  • 分享书中让您印象深刻的观点或引用
  • +
+ + +
评分参考
+
    +
  • +
    +
    ★★★★★
    +
    杰出,强烈推荐,改变思维的书籍
    +
    +
  • +
  • +
    +
    ★★★★☆
    +
    非常好,值得推荐,有一些小缺点
    +
    +
  • +
  • +
    +
    ★★★☆☆
    +
    好,有价值,但不突出
    +
    +
  • +
  • +
    +
    ★★☆☆☆
    +
    一般,有一些有用信息,但整体平淡
    +
    +
  • +
  • +
    +
    ★☆☆☆☆
    +
    不推荐,内容质量较低或不准确
    +
    +
  • +
+ +
+
+
+
+
+
+ ); +} diff --git a/styles/open-library.css b/styles/open-library.css new file mode 100644 index 0000000..540c4bd --- /dev/null +++ b/styles/open-library.css @@ -0,0 +1,65 @@ +/* Open Library Global Styles */ + +/* Reset styles for Open Library section */ +body.open-library, +html.open-library { + margin: 0 !important; + padding: 0 !important; + overflow-x: hidden !important; +} + +/* Hide main site header and footer */ +body.open-library nav.navbar.bg-dark.navbar-dark.fixed-top, +body.open-library footer.mw-100.bg-dark.text-white { + display: none !important; +} + +/* Remove top margin that accommodates the main header */ +body.open-library div.mt-5.pt-2 { + margin-top: 0 !important; + padding-top: 0 !important; + max-width: 100% !important; + width: 100% !important; +} + +/* Adjust main wrapper */ +body.open-library main.flex-fill.d-flex.flex-column.justify-content-start.align-items-center { + background: none !important; + padding: 0 !important; + margin: 0 !important; + max-width: 100% !important; + width: 100% !important; +} + +/* Adjust container styles */ +body.open-library .container, +body.open-library .container-fluid { + max-width: 100% !important; + padding-left: 0 !important; + padding-right: 0 !important; + margin-left: 0 !important; + margin-right: 0 !important; +} + +/* Remove card styling from main content */ +body.open-library main.flex-fill.d-flex.flex-column.justify-content-start.align-items-center .card { + background: transparent !important; + border: none !important; + box-shadow: none !important; + padding: 0 !important; + margin: 0 !important; +} + +/* Remove card body padding */ +body.open-library main.flex-fill.d-flex.flex-column.justify-content-start.align-items-center .card-body { + padding: 0 !important; +} + +/* Target the MDXProvider wrapper (main white background) */ +body.open-library .MDXProvider { + padding: 0 !important; + margin: 0 !important; + background: transparent !important; + border: none !important; + box-shadow: none !important; +} diff --git a/translation/en-US.ts b/translation/en-US.ts index 7e0ac36..fa87442 100644 --- a/translation/en-US.ts +++ b/translation/en-US.ts @@ -13,5 +13,152 @@ export default { search_results: 'Search Results', // Open Library - open_library: "Open Library", + open_library: 'Open Library', + + // Open Library Navigation + home: 'Home', + catalog: 'Catalog', + donate: 'Donate', + how_to_borrow: 'How to Borrow', + review: 'Review', + + // Open Library Home Page + featured_books: 'Featured Books', + view_all_books: 'Browse All Books', + browse_catalog: 'Browse Catalog', + testimonials: 'Testimonials', + join_community: 'Join Our Community', + become_member: 'Become a Member', + donate_books: 'Donate Books', + how_it_works: 'How It Works', + how_it_works_description: + 'Three simple steps to borrow books from our community', + step_1_find_book: '1. Find a Book', + step_1_description: + 'Browse our collection and find the book that interests you. Filter by category, author, or popularity.', + step_2_apply: '2. Apply to Borrow', + step_2_description: + "Submit a simple request form. We'll connect you with the book owner and arrange the handover.", + step_3_receive: '3. Receive and Pass It On', + step_3_description: + "Enjoy your book and return it when you're done. Consider donating your own books to keep knowledge flowing.", + learn_more_about_borrowing: 'Learn More About Borrowing', + community_voices: 'Community Voices', + community_voices_description: 'What our members say about Open Library', + read_more_reviews: 'Read More Reviews', + share_your_knowledge: 'Share Your Knowledge', + donate_a_book: 'Donate a Book', + stay_updated: 'Stay Updated', + newsletter_description: + 'Subscribe to our newsletter for new book arrivals and community events.', + email_placeholder: 'Your email address', + subscribe: 'Subscribe', + + // Open Library Books Page + book_catalog: 'Book Catalog', + search_books: 'Search Books', + all_categories: 'All Categories', + all_languages: 'All Languages', + all_status: 'All Status', + available: 'Available', + borrowed: 'Borrowed', + reset_filters: 'Reset Filters', + showing_books: 'Showing {0} of {1} books', + no_books_found: 'No books found', + try_adjusting_filters: 'Try adjusting your search or filters', + reset_all_filters: 'Reset All Filters', + view_details: 'View Details', + currently_with: 'Currently with: {0}', + + // Open Library Book Detail Page + loading: 'Loading...', + book_not_found: 'Book Not Found', + return_to_catalog: 'Return to Catalog', + back: 'Back', + by_author: 'by {0}', + currently_borrowed: 'Currently Borrowed', + currently_unavailable: 'Currently Unavailable', + request_to_borrow: 'Request to Borrow', + reviews: 'Reviews', + borrow_history: 'Borrow History', + no_reviews_yet: 'No reviews yet', + be_first_to_review: 'Be the first to review this book!', + write_review: 'Write a Review', + add_your_review: 'Add Your Review', + borrower: 'Borrower', + borrow_date: 'Borrow Date', + return_date: 'Return Date', + status: 'Status', + returned: 'Returned', + active: 'Active', + not_borrowed_yet: 'This book has not been borrowed yet', + + // Open Library About Page + about_open_library: 'About Open Library', + our_mission: 'Our Mission', + our_values: 'Our Values', + our_features: 'Our Features', + our_team: 'Our Team', + join_us_open_library: 'Join Us', + + // Open Library Donate Page + book_donation: 'Book Donation', + why_donate: 'Why Donate Books?', + donation_process: 'Donation Process', + book_registration: 'Book Registration', + what_books_we_accept: 'What Books Do We Accept?', + we_welcome: 'We Welcome', + not_suitable: 'Not Suitable for Donation', + faq: 'Frequently Asked Questions', + ready_to_donate: 'Ready to Donate Books?', + fill_donation_form: 'Fill Donation Form', + apply_for_membership: 'Apply for Membership', + + // Open Library How to Borrow Page + how_to_borrow_page: 'How to Borrow', + borrowing_and_passing: 'Borrowing and Passing Model', + borrowing_process: 'Borrowing Process', + borrowing_rules: 'Borrowing Rules', + quick_links: 'Quick Links', + view_full_catalog: 'View Full Catalog', + fill_borrow_request: 'Fill Borrow Request', + fill_book_passing_form: 'Fill Book Passing Form', + browse_book_catalog: 'Browse Book Catalog', + + // Open Library Review Page + book_reviews: 'Book Reviews', + community_reviews: 'Community Reviews', + all_reviews: 'All Reviews', + recent_reviews: 'Recent Reviews', + top_reviews: 'Top Reviews', + write_your_review: 'Write Your Review', + review_guidelines: 'Review Guidelines', + how_to_write_helpful_review: 'How to Write a Helpful Review', + rating_reference: 'Rating Reference', + + // Homepage Hero Section + hero_title: 'Free knowledge flows here', + hero_subtitle: + "Share and borrow books in our open-source community. Join freeCodeCamp Chengdu's initiative to make learning accessible to everyone.", + + // Featured Books Section + featured_books_subtitle: 'Discover what our community is reading right now', + + // Donation Callout Section + donation_callout_subtitle: + 'Have books collecting dust on your shelf? Donate them to our community and help others learn and grow. Your contribution makes a difference!', + + // Footer + footer_description: + 'A community-driven library for sharing knowledge and stories. Built with open source. Powered by generosity.', + quick_links_footer: 'Quick Links', + home_footer: 'Home', + catalog_footer: 'Catalog', + about_us_footer: 'About Us', + donate_footer: 'Donate', + contact: 'Contact', + contact_email: 'Email: info@openlibrary.community', + contact_address: 'Address: 123 Library Lane, Knowledge City, World', + follow_us: 'Follow Us', + all_rights_reserved: 'All Rights Reserved', }; diff --git a/translation/zh-CN.ts b/translation/zh-CN.ts index 44edfe7..35e7ebc 100644 --- a/translation/zh-CN.ts +++ b/translation/zh-CN.ts @@ -13,5 +13,149 @@ export default { search_results: '搜索结果', // Open Library - open_library: "开源图书馆", + open_library: '开源图书馆', + + // Open Library Navigation + home: '首页', + catalog: '书籍目录', + donate: '书籍募捐', + how_to_borrow: '如何借阅', + review: '书籍评价', + + // Open Library Home Page + featured_books: '精选书籍', + view_all_books: '浏览全部书籍', + browse_catalog: '浏览书目', + testimonials: '读者反馈', + join_community: '加入社区', + become_member: '成为会员', + donate_books: '捐赠书籍', + how_it_works: '运作方式', + how_it_works_description: '三个简单步骤借阅社区书籍', + step_1_find_book: '1. 寻找书籍', + step_1_description: + '浏览我们的藏书并找到您感兴趣的书籍。可按类别、作者或热度筛选。', + step_2_apply: '2. 申请借阅', + step_2_description: '提交简单的申请表。我们将为您联系书籍所有者并安排交接。', + step_3_receive: '3. 接收并传递', + step_3_description: + '阅读完毕后归还书籍。考虑捐赠您自己的书籍,让知识持续流动。', + learn_more_about_borrowing: '了解更多借阅信息', + community_voices: '社区声音', + community_voices_description: '会员对开源图书馆的评价', + read_more_reviews: '阅读更多评价', + share_your_knowledge: '分享您的知识', + donate_a_book: '捐赠一本书', + stay_updated: '保持更新', + newsletter_description: '订阅我们的通讯,获取新书到达和社区活动信息。', + email_placeholder: '您的电子邮箱', + subscribe: '订阅', + + // Open Library Books Page + book_catalog: '书籍目录', + search_books: '搜索书籍', + all_categories: '所有分类', + all_languages: '所有语言', + all_status: '所有状态', + available: '可借阅', + borrowed: '已借出', + reset_filters: '重置筛选', + showing_books: '显示 {0} 本书,共 {1} 本', + no_books_found: '未找到书籍', + try_adjusting_filters: '尝试调整筛选条件', + reset_all_filters: '重置所有筛选', + view_details: '查看详情', + currently_with: '当前持有者: {0}', + + // Open Library Book Detail Page + loading: '加载中...', + book_not_found: '未找到书籍', + return_to_catalog: '返回目录', + back: '返回', + by_author: '作者: {0}', + currently_borrowed: '当前已借出', + currently_unavailable: '当前不可用', + request_to_borrow: '申请借阅', + reviews: '评价', + borrow_history: '借阅历史', + no_reviews_yet: '暂无评价', + be_first_to_review: '成为第一个评价的人!', + write_review: '写评价', + add_your_review: '添加您的评价', + borrower: '借阅者', + borrow_date: '借阅日期', + return_date: '归还日期', + status: '状态', + returned: '已归还', + active: '借阅中', + not_borrowed_yet: '此书尚未被借阅过', + + // Open Library About Page + about_open_library: '关于社区图书馆', + our_mission: '我们的使命', + our_values: '我们的价值观', + our_features: '我们的特色', + our_team: '我们的团队', + join_us_open_library: '加入我们', + + // Open Library Donate Page + book_donation: '书籍募捐', + why_donate: '为什么捐赠书籍?', + donation_process: '捐赠流程', + book_registration: '书籍登记', + what_books_we_accept: '我们接受什么样的书籍?', + we_welcome: '我们欢迎', + not_suitable: '不适合捐赠', + faq: '常见问题', + ready_to_donate: '准备好捐赠书籍了吗?', + fill_donation_form: '填写捐赠申请', + apply_for_membership: '申请成为会员', + + // Open Library How to Borrow Page + how_to_borrow_page: '如何借阅', + borrowing_and_passing: '借阅与传递模式', + borrowing_process: '借阅流程', + borrowing_rules: '借阅规则', + quick_links: '快速链接', + view_full_catalog: '查看完整书籍目录', + fill_borrow_request: '填写借阅申请', + fill_book_passing_form: '填写书籍传递表', + browse_book_catalog: '浏览书籍目录', + + // Open Library Review Page + book_reviews: '书籍评价', + community_reviews: '社区书评', + all_reviews: '所有评价', + recent_reviews: '最新评价', + top_reviews: '高分评价', + write_your_review: '写下您的书评', + review_guidelines: '书评指南', + how_to_write_helpful_review: '如何写一篇有帮助的书评', + rating_reference: '评分参考', + + // Homepage Hero Section + hero_title: '知识在这里自由流动', + hero_subtitle: + '在我们的开源社区中分享和借阅书籍。加入 freeCodeCamp 成都社区的倡议,让学习对每个人都触手可及。', + + // Featured Books Section + featured_books_subtitle: '发现我们社区现在正在阅读的书籍', + + // Donation Callout Section + donation_callout_subtitle: + '您的书架上有积灰的书吗?将它们捐赠给我们的社区,帮助他人学习和成长。您的贡献会产生影响!', + + // Footer + footer_description: + '一个由社区驱动的图书馆,用于分享知识和故事。开源构建。由慷慨驱动。', + quick_links_footer: '快速链接', + home_footer: '首页', + catalog_footer: '目录', + about_us_footer: '关于我们', + donate_footer: '捐赠', + contact: '联系我们', + contact_email: '邮箱:info@openlibrary.community', + contact_address: '地址:123 图书馆路,知识城,世界', + follow_us: '关注我们', + all_rights_reserved: '版权所有', }; diff --git a/translation/zh-TW.ts b/translation/zh-TW.ts index be691cc..ba0dc09 100644 --- a/translation/zh-TW.ts +++ b/translation/zh-TW.ts @@ -13,5 +13,148 @@ export default { search_results: '搜尋結果', // Open Library - open_library: "開源圖書館", + open_library: '開源圖書館', + + // Open Library Navigation + home: '首頁', + catalog: '目錄', + donate: '捐贈', + how_to_borrow: '如何借書', + review: '書評', + + // Open Library Home Page + featured_books: '特色書籍', + view_all_books: '查看所有書籍', + browse_catalog: '瀏覽目錄', + testimonials: '證言', + join_community: '加入社群', + become_member: '成為會員', + donate_books: '捐贈書籍', + how_it_works: '如何運作', + how_it_works_description: '九步驟加入社群圖書館', + step_1_find_book: '1. 找書', + step_1_description: + '瀏覽我們的書籍目錄,找到您想要的書籍。您可以按類別、作者或標題搜索。', + step_2_apply: '2. 申請借書', + step_2_description: '填寫借書申請表格,我們會與您聯絡安排借書事宜。', + step_3_receive: '3. 收書', + step_3_description: '書籍將會寄送到您指定的地址,請注意查收。', + learn_more_about_borrowing: '了解更多借書資訊', + community_voices: '社群聲音', + community_voices_description: '聽聽我們的社群成員怎麼說', + read_more_reviews: '閱讀更多書評', + share_your_knowledge: '分享您的知識', + donate_a_book: '捐贈一本書', + stay_updated: '保持更新', + newsletter_description: '訂閱我們的電子報,保持與社群圖書館的聯絡。', + email_placeholder: '您的電子郵件地址', + subscribe: '訂閱', + + // Open Library Books Page + book_catalog: '書籍目錄', + search_books: '搜尋書籍', + all_categories: '所有類別', + all_languages: '所有語言', + all_status: '所有狀態', + available: '可借', + borrowed: '已借出', + reset_filters: '重設篩選', + showing_books: '顯示 {0} 本書,共 {1} 本', + no_books_found: '沒有找到書籍', + try_adjusting_filters: '試著調整篩選條件', + reset_all_filters: '重設所有篩選', + view_details: '查看詳情', + currently_with: '目前由 {0} 借出', + + // Open Library Book Detail Page + loading: '載入中...', + book_not_found: '沒有找到書籍', + return_to_catalog: '返回目錄', + back: '返回', + by_author: '作者:{0}', + currently_borrowed: '目前已借出', + currently_unavailable: '目前不可借', + request_to_borrow: '申請借書', + reviews: '書評', + borrow_history: '借書歷史', + no_reviews_yet: '尚無書評', + be_first_to_review: '成為第一個書評的人!', + write_review: '寫書評', + add_your_review: '新增您的書評', + borrower: '借書人', + borrow_date: '借書日期', + return_date: '歸還日期', + status: '狀態', + returned: '已歸還', + active: '借書中', + not_borrowed_yet: '尚未借出', + + // Open Library About Page + about_open_library: '關於開源圖書館', + our_mission: '我們的使命', + our_values: '我們的價值', + our_features: '我們的特色', + our_team: '我們的團隊', + join_us_open_library: '加入我們', + + // Open Library Donate Page + book_donation: '書籍捐贈', + why_donate: '為什麼捐贈?', + donation_process: '捐贈流程', + book_registration: '書籍登錄', + what_books_we_accept: '我們接受什麼書籍?', + we_welcome: '我們歡迎', + not_suitable: '不適合', + faq: '常見問題', + ready_to_donate: '準備捐贈書籍?', + fill_donation_form: '填寫捐贈表單', + apply_for_membership: '申請會員', + + // Open Library How to Borrow Page + how_to_borrow_page: '如何借書', + borrowing_and_passing: '借閱與傳遞模式', + borrowing_process: '借書流程', + borrowing_rules: '借書規則', + quick_links: '快速連結', + view_full_catalog: '查看完整目錄', + fill_borrow_request: '填寫借書申請', + fill_book_passing_form: '填寫書籍傳遞表單', + browse_book_catalog: '瀏覽書籍目錄', + + // Open Library Review Page + book_reviews: '書籍書評', + community_reviews: '社群書評', + all_reviews: '所有書評', + recent_reviews: '最新書評', + top_reviews: '最佳書評', + write_your_review: '寫您的書評', + review_guidelines: '書評指南', + how_to_write_helpful_review: '如何寫有用的書評', + rating_reference: '評分參考', + + // Homepage Hero Section + hero_title: '在我們的開源圖書館中探索', + hero_subtitle: + '在我們的社群圖書館中探索免費的書籍和資源。加入 freeCodeCamp 成為社群圖書館的一部分,學習和分享知識。', + + // Featured Books Section + featured_books_subtitle: '我們社群圖書館中最受歡迎的書籍', + + // Donation Callout Section + donation_callout_subtitle: + '您的捐贈將有助於我們的社群圖書館的發展。您的支持將有助於我們提供更多免費的書籍和資源給社群成員。', + + // Footer + footer_description: + '一個開源的社群圖書館,提供免費的書籍和資源給社群成員。開源。免費。無限可能。', + quick_links_footer: '快速連結', + home_footer: '首頁', + catalog_footer: '目錄', + about_us_footer: '關於我們', + donate_footer: '捐贈', + contact: '聯絡我們', + contact_email: '聯絡郵件:info@openlibrary.community', + contact_address: '地址:123 圖書館路,社群圖書館,香港', + follow_us: '關注我們', + all_rights_reserved: '版權所有', }; From 23870d8e610d79e33acf509fb18e85e2e4c6dba3 Mon Sep 17 00:00:00 2001 From: dethan3 Date: Fri, 25 Apr 2025 14:22:20 +0800 Subject: [PATCH 6/8] fix: replace tags with Next.js components and fix TypeScript errors --- components/open-library/Navbar.tsx | 2 +- pages/open-library/books/index.tsx | 4 ++-- pages/open-library/how-to-borrow.tsx | 20 ++++++++++++-------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/components/open-library/Navbar.tsx b/components/open-library/Navbar.tsx index 1b2fd35..c870c9a 100644 --- a/components/open-library/Navbar.tsx +++ b/components/open-library/Navbar.tsx @@ -1,6 +1,6 @@ import dynamic from 'next/dynamic'; import React from 'react'; -import { Button, Form, FormControl, Nav, Navbar } from 'react-bootstrap'; +import { Nav, Navbar } from 'react-bootstrap'; import { t } from '../../models/Translation'; diff --git a/pages/open-library/books/index.tsx b/pages/open-library/books/index.tsx index c18b6a0..f46f708 100644 --- a/pages/open-library/books/index.tsx +++ b/pages/open-library/books/index.tsx @@ -124,8 +124,8 @@ export default function BookCatalog() { const [statusFilter, setStatusFilter] = useState(''); // Extract unique categories and languages for filter dropdowns - const categories = [...new Set(books.map(book => book.category))]; - const languages = [...new Set(books.map(book => book.language))]; + const categories = Array.from(new Set(books.map(book => book.category))); + const languages = Array.from(new Set(books.map(book => book.language))); // Apply filters when any filter changes useEffect(() => { diff --git a/pages/open-library/how-to-borrow.tsx b/pages/open-library/how-to-borrow.tsx index 451dfea..3304bdd 100644 --- a/pages/open-library/how-to-borrow.tsx +++ b/pages/open-library/how-to-borrow.tsx @@ -1,3 +1,4 @@ +import Link from 'next/link'; import React from 'react'; import { Button, Card, Col, Row } from 'react-bootstrap'; @@ -61,7 +62,10 @@ export default function HowToBorrowPage() { fCC 成都社区图书馆 {' '} 中查找自己感兴趣的书籍,或者在我们的{' '} - 书籍目录 中浏览。 + + 书籍目录 + {' '} + 中浏览。

@@ -214,9 +218,9 @@ export default function HowToBorrowPage() { > 填写书籍传递表 - - 浏览书籍目录 - + + 浏览书籍目录 +
@@ -274,14 +278,14 @@ export default function HowToBorrowPage() {

准备好借阅了吗?

- - 浏览书籍目录 - + + 浏览书籍目录 + 申请成为会员 From 99320ce1c351c3a0d180c03d122e1d268626f663 Mon Sep 17 00:00:00 2001 From: dethan3 Date: Thu, 19 Jun 2025 09:23:18 +0800 Subject: [PATCH 7/8] feat: Complete MVP layout improvements --- components/Layout/CardPage.tsx | 2 +- components/open-library/BookCard.tsx | 101 +++++ components/open-library/FeaturedBooks.tsx | 72 ++++ components/open-library/Footer.tsx | 108 +++-- components/open-library/HeroSection.tsx | 81 ++++ components/open-library/HowItWorks.tsx | 163 +++++++ components/open-library/Layout.tsx | 18 +- components/open-library/Navbar.tsx | 24 +- pages/api/core.ts | 2 +- pages/open-library/about.tsx | 201 --------- pages/open-library/book/[id].tsx | 5 +- pages/open-library/books/index.tsx | 274 +++--------- pages/open-library/donate.tsx | 254 ----------- pages/open-library/how-to-borrow.tsx | 2 +- pages/open-library/index.tsx | 496 +++++++++------------- pages/open-library/review.tsx | 372 ---------------- 16 files changed, 757 insertions(+), 1418 deletions(-) create mode 100644 components/open-library/BookCard.tsx create mode 100644 components/open-library/FeaturedBooks.tsx create mode 100644 components/open-library/HeroSection.tsx create mode 100644 components/open-library/HowItWorks.tsx delete mode 100644 pages/open-library/about.tsx delete mode 100644 pages/open-library/donate.tsx delete mode 100644 pages/open-library/review.tsx diff --git a/components/Layout/CardPage.tsx b/components/Layout/CardPage.tsx index 66ca2c6..f6da494 100644 --- a/components/Layout/CardPage.tsx +++ b/components/Layout/CardPage.tsx @@ -4,7 +4,7 @@ import { Col, Pagination, Row } from 'react-bootstrap'; import { SearchPageMeta } from '../../models/System'; export interface CardPageProps extends SearchPageMeta { - Card: ComponentClass | FC; + Card: ComponentClass> | FC>; cardLinkOf?: (id: string) => string; pageLinkOf: (page: number) => string; } diff --git a/components/open-library/BookCard.tsx b/components/open-library/BookCard.tsx new file mode 100644 index 0000000..9b607d6 --- /dev/null +++ b/components/open-library/BookCard.tsx @@ -0,0 +1,101 @@ +import Link from 'next/link'; +import React from 'react'; +import { Button, Card, Image } from 'react-bootstrap'; + +import { t } from '../../models/Translation'; + +export interface Book { + id: number; + title: string; + author: string; + cover?: string; + status?: 'available' | 'borrowed'; + category?: string; + description?: string; +} + +interface BookCardProps { + book: Book; + showStatus?: boolean; + variant?: 'featured' | 'catalog'; +} + +const BookCard: React.FC = ({ + book, + showStatus = false, + variant = 'featured', +}) => { + const cardClass = + variant === 'featured' + ? 'h-100 shadow-sm border-0' + : 'h-100 shadow border-0'; + + return ( + +
+ {showStatus && ( +
+ + {book.status === 'available' ? '可借阅' : '已借出'} + +
+ )} +
+ {`${book.title} +
+
+ + + {book.title} + + + {book.author} + + {book.category && ( + + + {book.category} + + )} +
+ + + +
+
+
+ ); +}; + +export default BookCard; diff --git a/components/open-library/FeaturedBooks.tsx b/components/open-library/FeaturedBooks.tsx new file mode 100644 index 0000000..b65c970 --- /dev/null +++ b/components/open-library/FeaturedBooks.tsx @@ -0,0 +1,72 @@ +import Link from 'next/link'; +import React from 'react'; +import { Button, Col, Row } from 'react-bootstrap'; + +import BookCard, { Book } from './BookCard'; +import { ContentContainer } from './Layout'; + +interface FeaturedBooksProps { + books: Book[]; + title?: string; + subtitle?: string; + showViewAll?: boolean; + viewAllLink?: string; + viewAllText?: string; +} + +const FeaturedBooks: React.FC = ({ + books, + title = '精选图书', + subtitle = '社区成员推荐的优质读物,涵盖技术、设计、创业等多个领域', + showViewAll = true, + viewAllLink = '/open-library/books', + viewAllText = '查看全部图书', +}) => { + return ( +
+ +
+

+ {title} +
+
+
+

+

+ {subtitle} +

+
+ + + {books.slice(0, 8).map(book => ( + + + + ))} + + + {showViewAll && ( +
+ + + +
+ )} +
+
+ ); +}; + +export default FeaturedBooks; diff --git a/components/open-library/Footer.tsx b/components/open-library/Footer.tsx index c17b219..7e3a39e 100644 --- a/components/open-library/Footer.tsx +++ b/components/open-library/Footer.tsx @@ -3,23 +3,11 @@ import { Col, Nav, Row } from 'react-bootstrap'; import { t } from '../../models/Translation'; -// +// 使用 Bootstrap 工具类替换内联样式的 ContentContainer const ContentContainer: React.FC<{ children: React.ReactNode }> = ({ children, }) => { - return ( -
- {children} -
- ); + return
{children}
; }; const FooterComponent = () => { @@ -31,75 +19,83 @@ const FooterComponent = () => { }, []); return ( -
+
-
{t('open_library')}
-

{t('footer_description')}

-
- +
{t('open_library')}
+

+ {t('footer_description')} +

+
-
{t('quick_links_footer')}
+
{t('quick_links_footer')}
-
{t('contact')}
-

freeCodeCamp Chengdu Community

-

Chengdu, Sichuan, China

-

Email: contact@openlibrary.org

-

WeChat: FCCChengdu

+
{t('contact')}
+
+

+ + freeCodeCamp Chengdu Community +

+

+ + Chengdu, Sichuan, China +

+

+ + Email: contact@openlibrary.org +

+

+ + WeChat: FCCChengdu +

+
-
+
- {/* Use a more direct approach with inline styles to ensure visibility */} -
+
{isMounted ? ( <> © {new Date().getFullYear()} {t('open_library')}.{' '} diff --git a/components/open-library/HeroSection.tsx b/components/open-library/HeroSection.tsx new file mode 100644 index 0000000..3ecc17c --- /dev/null +++ b/components/open-library/HeroSection.tsx @@ -0,0 +1,81 @@ +import React from 'react'; +import { Button, Col, Image, Row } from 'react-bootstrap'; + +import { ContentContainer } from './Layout'; + +interface HeroSectionProps { + title?: string; + subtitle?: string; + ctaText?: string; + ctaLink?: string; + heroImage?: string; + heroImageAlt?: string; +} + +const HeroSection: React.FC = ({ + title = 'freeCodeCamp 成都社区 Open Library', + subtitle = '让知识在社区中自由流动,连接每一个热爱学习的心', + ctaText = '加入我们', + ctaLink = 'https://open-source-bazaar.feishu.cn/share/base/form/shrcngQgMrhjTh6ycO1zcaEWZld', + heroImage = '/images/open-library-hero.svg', + heroImageAlt = '社区成员分享书籍', +}) => { + return ( +
+ + + +
+

{title}

+

{subtitle}

+
+ + +
+
+ + +
+ {heroImageAlt} +
+ +
+
+
+ ); +}; + +export default HeroSection; diff --git a/components/open-library/HowItWorks.tsx b/components/open-library/HowItWorks.tsx new file mode 100644 index 0000000..ccc6b4c --- /dev/null +++ b/components/open-library/HowItWorks.tsx @@ -0,0 +1,163 @@ +import Link from 'next/link'; +import React from 'react'; +import { Button, Col, Row } from 'react-bootstrap'; + +import { ContentContainer } from './Layout'; + +interface Step { + id: number; + title: string; + description: string; + icon: string; + color: string; +} + +interface HowItWorksProps { + title?: string; + subtitle?: string; + steps?: Step[]; + showLearnMore?: boolean; + learnMoreLink?: string; +} + +const defaultSteps: Step[] = [ + { + id: 1, + title: '浏览图书', + description: '在图书目录中找到你感兴趣的书籍,查看详细信息和当前状态', + icon: 'bi-search', + color: '#6f42c1', + }, + { + id: 2, + title: '申请借阅', + description: '填写借阅申请表单,系统会自动联系当前持书人', + icon: 'bi-file-earmark-text', + color: '#0d6efd', + }, + { + id: 3, + title: '线下传递', + description: '与持书人约定传递方式,通常通过快递或面对面交接', + icon: 'bi-arrow-left-right', + color: '#fd7e14', + }, + { + id: 4, + title: '享受阅读', + description: '阅读完成后,可以写下书评并准备传递给下一位读者', + icon: 'bi-book-half', + color: '#198754', + }, +]; + +const HowItWorks: React.FC = ({ + title = '如何使用 Open Library', + subtitle = '简单四步,让知识在社区中自由流动', + steps = defaultSteps, + showLearnMore = true, + learnMoreLink = '/open-library/how-to-borrow', +}) => { + return ( +
+ +
+

+ {title} +
+
+
+

+

+ {subtitle} +

+
+ + + {steps.map((step, index) => ( + +
+
+
+ +
+ + {step.id} + + {/* 连接线仅在大屏幕显示且不是最后一个 */} + {index < steps.length - 1 && ( +
+
+
+ )} +
+

{step.title}

+

{step.description}

+
+ + ))} +
+ + {showLearnMore && ( +
+ + + +
+ )} +
+
+ ); +}; + +export default HowItWorks; diff --git a/components/open-library/Layout.tsx b/components/open-library/Layout.tsx index 92e09d5..c126f93 100644 --- a/components/open-library/Layout.tsx +++ b/components/open-library/Layout.tsx @@ -8,19 +8,7 @@ import { useOpenLibraryLayout } from './useOpenLibraryLayout'; const ContentContainer: React.FC<{ children: React.ReactNode }> = ({ children, }) => { - return ( -
- {children} -
- ); + return
{children}
; }; interface LayoutProps { @@ -49,7 +37,9 @@ const Layout: React.FC = ({ -
{children}
+
+ {children} +
diff --git a/components/open-library/Navbar.tsx b/components/open-library/Navbar.tsx index c870c9a..966c73a 100644 --- a/components/open-library/Navbar.tsx +++ b/components/open-library/Navbar.tsx @@ -18,21 +18,8 @@ const LibraryNavbar = () => { }, []); return ( - - {/* 使用自定义的居中容器替代 Container fluid,与页面内容保持一致的宽度和居中效果 */} -
+ +
{ - {/* 添加语言切换菜单 */} +
diff --git a/pages/api/core.ts b/pages/api/core.ts index 222dfb0..39db567 100644 --- a/pages/api/core.ts +++ b/pages/api/core.ts @@ -9,7 +9,7 @@ if (HTTP_PROXY) setGlobalDispatcher(new ProxyAgent(HTTP_PROXY)); export type NextAPI = ( req: NextApiRequest, res: NextApiResponse, -) => Promise; +) => Promise; export function safeAPI(handler: NextAPI): NextAPI { return async (req, res) => { diff --git a/pages/open-library/about.tsx b/pages/open-library/about.tsx deleted file mode 100644 index c8a8bf1..0000000 --- a/pages/open-library/about.tsx +++ /dev/null @@ -1,201 +0,0 @@ -import React from 'react'; -import { Card, Col, Container, Image, Row } from 'react-bootstrap'; - -import { ContentContainer, Layout } from '../../components/open-library/Layout'; - -export default function AboutPage() { - return ( - - -
-

关于社区图书馆

- - - -

我们的使命

-

- freeCodeCamp 成都社区「Open - Library」是一个开放、共享的社区图书馆,旨在促进知识交流,推动社区成员之间的学习和成长,增强社区成员之间的互动与信任。 -

-

- 我们深知知识的流动能够点燃创新的火花,因此,我们采用了一种独特的"无储存"借阅模式,让书籍在会员之间自由流转,而非集中存放。这种模式不仅节省了物理空间,更重要的是促进了社区成员之间的直接交流和互动。 -

-
-
- - - - - -

我们的价值观

-
    -
  • -
    -
    - -
    -
    -
    开放共享
    -

    - 我们相信知识应该是开放和共享的,每个人都有权利获取和贡献知识。 -

    -
    -
    -
  • -
  • -
    -
    - -
    -
    -
    社区驱动
    -

    - 我们的图书馆完全由社区成员驱动,每个人都可以贡献和受益。 -

    -
    -
    -
  • -
  • -
    -
    - -
    -
    -
    信任与责任
    -

    - 我们基于互相信任建立借阅系统,同时鼓励每个人对所借书籍负责。 -

    -
    -
    -
  • -
-
-
- - - - -

我们的特色

-
    -
  • -
    -
    - -
    -
    -
    "无储存"借阅模式
    -

    - 书籍在会员之间直接流转,无需集中存放,促进社区成员之间的直接交流。 -

    -
    -
    -
  • -
  • -
    -
    - -
    -
    -
    社区捐赠
    -

    - 所有书籍均来自社区成员的捐赠,体现了共享和互助的精神。 -

    -
    -
    -
  • -
  • -
    -
    - -
    -
    -
    线上管理
    -

    - 通过飞书多维表格进行书籍管理和借阅记录,方便透明。 -

    -
    -
    -
  • -
-
-
- -
- - - -

我们的团队

-

- Open Library 由 freeCodeCamp - 成都社区的志愿者团队运营。我们的团队成员来自各行各业,但都有一个共同的目标:促进知识共享和社区建设。 -

- - - - -
张伟
-

图书馆创始人

- - - -
李梅
-

社区协调员

- - - -
王晨
-

技术支持

- -
-
-
- - - -

加入我们

-

- 如果你热爱阅读,愿意分享知识,欢迎加入 freeCodeCamp - 成都社区「Open Library」,成为知识流动的一部分! -

-

成为会员后,你可以:

-
    -
  • 借阅其他会员捐赠的书籍
  • -
  • 捐赠自己的书籍给社区
  • -
  • 参与社区组织的读书会和讨论
  • -
  • 结识志同道合的朋友
  • -
- -
-
-
-
-
- ); -} diff --git a/pages/open-library/book/[id].tsx b/pages/open-library/book/[id].tsx index bf330a8..2dd8c62 100644 --- a/pages/open-library/book/[id].tsx +++ b/pages/open-library/book/[id].tsx @@ -1,3 +1,4 @@ +import Image from 'next/image'; import { useRouter } from 'next/router'; import React, { useEffect, useState } from 'react'; import { Badge, Button, Card, Col, Row, Tab, Tabs } from 'react-bootstrap'; @@ -263,11 +264,13 @@ export default function BookDetail() { - {book.title}
([ + const books: Book[] = [ { id: 1, title: 'Clean Code', @@ -48,18 +40,18 @@ export default function BookCatalog() { category: 'Programming', language: 'English', status: 'borrowed', - currentHolder: 'Zhang Wei', + currentHolder: 'John Doe', description: 'A modern introduction to programming', }, { id: 3, - title: 'Design Patterns', - author: 'Erich Gamma et al.', + title: 'The Pragmatic Programmer', + author: 'Andrew Hunt & David Thomas', cover: '/images/placeholder-book.svg', category: 'Programming', language: 'English', status: 'available', - description: 'Elements of Reusable Object-Oriented Software', + description: 'Your journey to mastery', }, { id: 4, @@ -68,13 +60,14 @@ export default function BookCatalog() { cover: '/images/placeholder-book.svg', category: 'Programming', language: 'English', - status: 'available', + status: 'borrowed', + currentHolder: 'Jane Smith', description: 'A book series on JavaScript', }, { id: 5, title: '深入理解计算机系统', - author: 'Randal E. Bryant', + author: "Randal E. Bryant & David R. O'Hallaron", cover: '/images/placeholder-book.svg', category: 'Computer Science', language: 'Chinese', @@ -115,203 +108,76 @@ export default function BookCatalog() { currentHolder: 'Wang Chen', description: 'CSS Secrets (Chinese Edition)', }, - ]); - - const [filteredBooks, setFilteredBooks] = useState(books); - const [searchTerm, setSearchTerm] = useState(''); - const [categoryFilter, setCategoryFilter] = useState(''); - const [languageFilter, setLanguageFilter] = useState(''); - const [statusFilter, setStatusFilter] = useState(''); - - // Extract unique categories and languages for filter dropdowns - const categories = Array.from(new Set(books.map(book => book.category))); - const languages = Array.from(new Set(books.map(book => book.language))); - - // Apply filters when any filter changes - useEffect(() => { - let results = books; - - // Apply search term filter - if (searchTerm) { - const term = searchTerm.toLowerCase(); - results = results.filter( - book => - book.title.toLowerCase().includes(term) || - book.author.toLowerCase().includes(term) || - (book.description && book.description.toLowerCase().includes(term)), - ); - } - - // Apply category filter - if (categoryFilter) { - results = results.filter(book => book.category === categoryFilter); - } - - // Apply language filter - if (languageFilter) { - results = results.filter(book => book.language === languageFilter); - } - - // Apply status filter - if (statusFilter) { - results = results.filter(book => book.status === statusFilter); - } - - setFilteredBooks(results); - }, [searchTerm, categoryFilter, languageFilter, statusFilter, books]); - - // Reset all filters - const resetFilters = () => { - setSearchTerm(''); - setCategoryFilter(''); - setLanguageFilter(''); - setStatusFilter(''); - }; + ]; return ( - + -

Book Catalog

- - {/* Search and Filters */} - - - - - - setSearchTerm(e.target.value)} - /> - - - - - - - setCategoryFilter(e.target.value)} - > - - {categories.map(category => ( - - ))} - - - - setLanguageFilter(e.target.value)} - > - - {languages.map(language => ( - - ))} - - - - setStatusFilter(e.target.value)} - > - - - - - - - - -
-
- - Showing {filteredBooks.length} of {books.length} books - -
- -
-
-
+
+
+

图书目录

+

+ 浏览我们的社区共享图书馆,发现有趣的书籍 +

+
- {/* Book Grid */} - - {filteredBooks.map(book => ( - - -
- -
- - {book.title} - - {book.author} - - - {book.description} - -
-
- - {book.category} - - - {book.language} - -
-
+ + {books.map(book => ( + + +
+ +
- {book.status === 'available' ? 'Available' : 'Borrowed'} + {book.status === 'available' ? '可借阅' : '已借出'} -
+ + + {book.title} + + + {book.author} + + + {book.category} • {book.language} + + {book.description && ( + + {book.description} + + )} + {book.status === 'borrowed' && book.currentHolder && ( + + 当前持书人: {book.currentHolder} + + )} +
+ - View Details - + 查看详情 +
- {book.status === 'borrowed' && ( -
- Currently with: {book.currentHolder} -
- )} -
- -
- - ))} -
- - {/* No Results */} - {filteredBooks.length === 0 && ( -
-

No books found

-

Try adjusting your search or filters

- -
- )} + + + + ))} + +
); diff --git a/pages/open-library/donate.tsx b/pages/open-library/donate.tsx deleted file mode 100644 index 7e54e0d..0000000 --- a/pages/open-library/donate.tsx +++ /dev/null @@ -1,254 +0,0 @@ -import React from 'react'; -import { Button, Card, Col, Row } from 'react-bootstrap'; - -import { ContentContainer, Layout } from '../../components/open-library/Layout'; - -export default function DonatePage() { - return ( - - -
-

书籍募捐

- - - -

为什么捐赠书籍?

-

- 您的一本书,可能会改变他人的一生。通过捐赠书籍,您不仅能够让闲置的知识重新流动,还能够帮助更多的人获取学习资源。 -

-

- 在 Open - Library,我们相信知识应该是流动的。您捐赠的每一本书都将在社区成员之间传递,让更多人受益。当您捐赠一本书,您不仅是在分享一个物品,更是在分享知识、经验和思想。 -

-
-
- - - - - -

捐赠流程

-
    -
  1. -
    -
    填写捐赠申请
    -

    - 填写我们的在线表单,提供您想要捐赠的书籍信息。 -

    -
    -
  2. -
  3. -
    -
    书籍登记
    -

    - 如所捐赠书籍在「fCC - 成都社区图书馆」还未登记,请先填写书籍登记表,然后回到第 - 1 步继续捐赠。建议在移动端设备上登记,方便扫描书籍 - ISBN 码。 -

    -
    -
  4. -
  5. -
    -
    书籍进入图书馆
    -

    - 完成以上流程后,书籍正式进入 Open Library - 的传递流程,开始流转于社区成员之间。 -

    -
    -
  6. -
- -
-
- - - - -

书籍登记

-

- 如果您要捐赠的书籍在我们的图书馆中还未登记,请先完成书籍登记流程。这有助于我们维护一个完整的书籍数据库,方便其他会员查找和借阅。 -

-

登记时,您需要提供以下信息:

-
    -
  • 书籍标题
  • -
  • 作者
  • -
  • ISBN(可通过扫描获取)
  • -
  • 出版社
  • -
  • 出版年份
  • -
  • 书籍类别
  • -
  • 书籍语言
  • -
  • 书籍简介
  • -
  • 书籍封面照片(可选)
  • -
- -
-
- -
- - - -

我们接受什么样的书籍?

- - -
✓ 我们欢迎
-
    -
  • 技术类书籍(编程、设计、数据科学等)
  • -
  • 科学类书籍
  • -
  • 商业和管理类书籍
  • -
  • 自我提升类书籍
  • -
  • 文学和小说
  • -
  • 状态良好的二手书
  • -
- - -
✗ 不适合捐赠
-
    -
  • 严重破损或缺页的书籍
  • -
  • 有大量笔记或标记的书籍
  • -
  • 过时的技术书籍(5年以上的技术书可能已过时)
  • -
  • 教科书和习题集
  • -
  • 杂志和期刊
  • -
- -
-
-
- - - - -
- - - -

常见问题

-
-
-

- -

-
-
- 您捐赠的书籍将进入我们的借阅系统,供社区成员借阅。当有人申请借阅时,书籍将从当前持有者(可能是您)直接传递给下一位借阅者。 -
-
-
-
-

- -

-
-
- 一旦书籍捐赠给图书馆,我们视为您已将书籍所有权转让给社区。如果您希望再次阅读这本书,可以通过正常的借阅流程申请借阅。 -
-
-
-
-

- -

-
-
- 您可以在我们的飞书多维表格中查看每本书的借阅记录,包括当前持有者和历史借阅情况。 -
-
-
-
-

- -

-
-
- 是的,我们要求捐赠者成为 Open Library - 的会员。这有助于我们建立一个基于信任的社区,并确保书籍的流转质量。 -
-
-
-
-
- -
-
- -
-

准备好捐赠书籍了吗?

- -
-
-
-
- ); -} diff --git a/pages/open-library/how-to-borrow.tsx b/pages/open-library/how-to-borrow.tsx index 3304bdd..1eb823a 100644 --- a/pages/open-library/how-to-borrow.tsx +++ b/pages/open-library/how-to-borrow.tsx @@ -1,6 +1,6 @@ import Link from 'next/link'; import React from 'react'; -import { Button, Card, Col, Row } from 'react-bootstrap'; +import { Card, Col, Row } from 'react-bootstrap'; import { ContentContainer, Layout } from '../../components/open-library/Layout'; diff --git a/pages/open-library/index.tsx b/pages/open-library/index.tsx index 5853be1..b25fd4a 100644 --- a/pages/open-library/index.tsx +++ b/pages/open-library/index.tsx @@ -1,321 +1,231 @@ +import Head from 'next/head'; import Link from 'next/link'; import React from 'react'; -import { Button, Card, Col, Form, Image, Row, Stack } from 'react-bootstrap'; +import { Card, Col, Row } from 'react-bootstrap'; -import { ContentContainer, Layout } from '../../components/open-library/Layout'; +import FeaturedBooks from '../../components/open-library/FeaturedBooks'; +import FooterComponent from '../../components/open-library/Footer'; +import HeroSection from '../../components/open-library/HeroSection'; +import HowItWorks from '../../components/open-library/HowItWorks'; +import { ContentContainer } from '../../components/open-library/Layout'; +import LibraryNavbar from '../../components/open-library/Navbar'; +import { useOpenLibraryLayout } from '../../components/open-library/useOpenLibraryLayout'; import { t } from '../../models/Translation'; -// TODO: Define a type for Book and Testimonial -type Book = { - id: number; - title: string; - author: string; - cover?: string; // Optional cover image URL -}; +// Sample data - these could be moved to a separate data file in the future +const featuredBooks = [ + { + id: 1, + title: 'Clean Code', + author: 'Robert C. Martin', + cover: '/images/placeholder-book.svg', + description: '编写优雅代码的艺术', + status: 'available' as const, + rating: 4.8, + tags: ['编程', '软件工程'], + }, + { + id: 2, + title: 'Eloquent JavaScript', + author: 'Marijn Haverbeke', + cover: '/images/placeholder-book.svg', + description: 'JavaScript 程序设计精粹', + status: 'borrowed' as const, + rating: 4.6, + tags: ['JavaScript', '前端'], + }, + { + id: 3, + title: 'Design Patterns', + author: 'Erich Gamma et al.', + cover: '/images/placeholder-book.svg', + description: '可复用面向对象软件的基础', + status: 'available' as const, + rating: 4.7, + tags: ['设计模式', '软件架构'], + }, + { + id: 4, + title: "You Don't Know JS", + author: 'Kyle Simpson', + cover: '/images/placeholder-book.svg', + description: 'JavaScript 深度解析系列', + status: 'available' as const, + rating: 4.5, + tags: ['JavaScript', '进阶'], + }, +]; -type Testimonial = { - id: number; - name: string; - quote: string; -}; +const workflowSteps = [ + { + id: 1, + icon: 'bi-search', + title: '浏览图书', + description: '在图书目录中寻找你感兴趣的书籍', + color: '#6f42c1', + }, + { + id: 2, + icon: 'bi-file-earmark-text', + title: '申请借阅', + description: '填写借阅申请表单,说明你的借阅意向', + color: '#0d6efd', + }, + { + id: 3, + icon: 'bi-people', + title: '联系持书人', + description: '我们会协助你联系当前的持书人安排交接', + color: '#fd7e14', + }, + { + id: 4, + icon: 'bi-book-half', + title: '传递分享', + description: '阅读完成后,将书籍传递给下一位读者', + color: '#198754', + }, +]; export default function OpenLibraryHomepage() { - // Sample featured books data - const featuredBooks: Book[] = [ - { - id: 1, - title: 'Clean Code', - author: 'Robert C. Martin', - cover: '/images/placeholder-book.svg', - }, // TODO: Replace placeholder path - { - id: 2, - title: 'Eloquent JavaScript', - author: 'Marijn Haverbeke', - cover: '/images/placeholder-book.svg', - }, - { - id: 3, - title: 'Design Patterns', - author: 'Erich Gamma et al.', - cover: '/images/placeholder-book.svg', - }, - { - id: 4, - title: "You Don't Know JS", - author: 'Kyle Simpson', - cover: '/images/placeholder-book.svg', - }, - ]; - - // Sample testimonials data - const testimonials: Testimonial[] = [ - { - id: 1, - name: 'Zhang Wei', - quote: - "Open Library helped me discover amazing tech books I couldn't find elsewhere.", - }, - { - id: 2, - name: 'Li Mei', - quote: - "The community is so supportive. I've both borrowed and donated books here.", - }, - { - id: 3, - name: 'Wang Chen', - quote: - 'As a student, this resource has been invaluable for my studies in computer science.', - }, - ]; + // Apply Open Library layout styles + useOpenLibraryLayout(); return ( - -
- {/* --- Hero Section --- */} -
- - - -

{t('hero_title')}

-

{t('hero_subtitle')}

- {/* TODO: Link this button to the actual Feishu form or member sign-up page */} - - - - {/* TODO: Replace placeholder */} - People sharing books - -
-
-
+ <> + + {`${t('open_library')} - freeCodeCamp 成都社区`} + + - {/* --- Featured Books Section --- */} -
- -

- {t('featured_books')} -

-

- {t('featured_books_subtitle')} -

- - {featuredBooks.map(book => ( - // TODO: Potentially move Card rendering to components/open-library/BookCard.tsx - - -
- {`${book.title} -
- - - {book.title} - - - {book.author} - -
- - - -
-
-
- - ))} -
-
- - - + + + + +
+ {/* About Section */} + +
+
+

+ 📚 关于我们 +

+

+ freeCodeCamp 成都社区「Open Library」开放共享图书馆 +

- -
- {/* --- Donation Callout Section --- */} -
- - - -

{t('share_your_knowledge')}

-

{t('donation_callout_subtitle')}

- - - + + + + +

🌟 我们的使命

+

+ 促进知识交流,推动社区成员之间的学习和成长,增强社区成员之间的互动与信任。 + 我们采用独特的"无储存"借阅模式,让书籍在会员之间自由流转。 +

+
+
- - {/* TODO: Replace placeholder */} - Book donation + + + +

💡 核心理念

+
    +
  • + ✦ 知识流动 - 书籍自由流转,促进知识传播 +
  • +
  • + ✦ 社区驱动 - 所有书籍来自成员捐赠 +
  • +
  • + ✦ 开放共享 - 促进交流和互动 +
  • +
+
+
-
-
+
+ - {/* --- How It Works Section --- */} -
+ {/* Featured Books Section */} +
-

{t('how_it_works')}

-

- {t('how_it_works_description')} -

- - -
- -
-

{t('step_1_find_book')}

-

{t('step_1_description')}

- - -
- -
-

{t('step_2_apply')}

-

{t('step_2_description')}

- - -
- -
-

{t('step_3_receive')}

-

{t('step_3_description')}

- -
-
- - - +
+

+ 📖 精选图书 +

+

发现社区成员推荐的优质图书

+ -
+
+ + + + - {/* --- Testimonials Section --- */} -
+ {/* Simple Donation Section */} +
-

- {t('community_voices')} -

-

- {t('community_voices_description')} -

- - {testimonials.map(testimonial => ( - - - -
-

"{testimonial.quote}"

-
- {testimonial.name} -
-
-
-
- - ))} -
-
- - - + + GitHub Sponsors + + + + + 分享你的图书 + + +
-
- - {/* --- Newsletter Section --- */} -
- - - - - -

{t('stay_updated')}

-

- {t('newsletter_description')} -

- {/* TODO: Implement actual newsletter subscription logic */} -
e.preventDefault()}> - {' '} - {/* Prevent default form submission */} - - - - -
-
-
- -
-
-
+
-
+ + + ); } diff --git a/pages/open-library/review.tsx b/pages/open-library/review.tsx deleted file mode 100644 index 28222cf..0000000 --- a/pages/open-library/review.tsx +++ /dev/null @@ -1,372 +0,0 @@ -import React, { useState } from 'react'; -import { Button, Card, Col, Form, Row, Tab, Tabs } from 'react-bootstrap'; - -import { ContentContainer, Layout } from '../../components/open-library/Layout'; - -// Review type definition -type Review = { - id: number; - bookTitle: string; - bookAuthor: string; - bookCover?: string; - reviewer: string; - rating: number; - comment: string; - date: string; -}; - -export default function ReviewPage() { - // Sample reviews data - in a real app, this would come from an API - const [reviews, setReviews] = useState([ - { - id: 1, - bookTitle: 'Clean Code', - bookAuthor: 'Robert C. Martin', - bookCover: '/images/placeholder-book.svg', - reviewer: 'Wang Chen', - rating: 5, - comment: - 'This book completely changed how I approach writing code. Highly recommended for all developers!', - date: '2023-11-25', - }, - { - id: 2, - bookTitle: 'Clean Code', - bookAuthor: 'Robert C. Martin', - bookCover: '/images/placeholder-book.svg', - reviewer: 'Li Mei', - rating: 4, - comment: - 'Great principles that have stood the test of time. Some examples are a bit dated but the concepts are solid.', - date: '2023-09-10', - }, - { - id: 3, - bookTitle: 'Eloquent JavaScript', - bookAuthor: 'Marijn Haverbeke', - bookCover: '/images/placeholder-book.svg', - reviewer: 'Liu Jie', - rating: 5, - comment: - 'Perfect for beginners and intermediate JavaScript developers. The exercises are particularly helpful.', - date: '2023-12-05', - }, - { - id: 4, - bookTitle: 'Design Patterns', - bookAuthor: 'Erich Gamma et al.', - bookCover: '/images/placeholder-book.svg', - reviewer: 'Chen Ming', - rating: 4, - comment: - 'A classic that has stood the test of time. The examples are in C++ and Smalltalk, but the concepts apply to any OO language.', - date: '2023-07-25', - }, - { - id: 5, - bookTitle: '深入理解计算机系统', - bookAuthor: 'Randal E. Bryant', - bookCover: '/images/placeholder-book.svg', - reviewer: 'Zhang Wei', - rating: 5, - comment: - '这是一本非常全面的计算机系统书籍,对理解计算机底层工作原理非常有帮助。', - date: '2023-10-15', - }, - ]); - - // Function to render star rating - const renderStars = (rating: number) => { - return ( -
- {[...Array(5)].map((_, i) => ( - - {i < rating ? '\u2605' : '\u2606'} - - ))} -
- ); - }; - - return ( - - -
-

书籍评价

- - - -

社区书评

-

- 阅读是一种体验,分享让这种体验更加丰富。浏览社区成员的书评,或者分享您自己的阅读感受。 -

-
- -
-
-
- - - -
- {reviews.map(review => ( - - - - -
- {review.bookTitle} -
{review.bookTitle}
-

- {review.bookAuthor} -

-
- - -
-
{review.reviewer}
- {renderStars(review.rating)} -
-

{review.comment}

-
- - {new Date(review.date).toLocaleDateString( - 'en-US', - { - year: 'numeric', - month: '2-digit', - day: '2-digit', - }, - )} - - -
- -
-
-
- ))} -
-
- -
- {reviews - .sort( - (a, b) => - new Date(b.date).getTime() - new Date(a.date).getTime(), - ) - .slice(0, 3) - .map(review => ( - - - - -
- {review.bookTitle} -
- {review.bookTitle} -
-

- {review.bookAuthor} -

-
- - -
-
{review.reviewer}
- {renderStars(review.rating)} -
-

{review.comment}

-
- - {new Date(review.date).toLocaleDateString( - 'en-US', - { - year: 'numeric', - month: '2-digit', - day: '2-digit', - }, - )} - - -
- -
-
-
- ))} -
-
- -
- {reviews - .filter(review => review.rating >= 5) - .map(review => ( - - - - -
- {review.bookTitle} -
- {review.bookTitle} -
-

- {review.bookAuthor} -

-
- - -
-
{review.reviewer}
- {renderStars(review.rating)} -
-

{review.comment}

-
- - {new Date(review.date).toLocaleDateString( - 'en-US', - { - year: 'numeric', - month: '2-digit', - day: '2-digit', - }, - )} - - -
- -
-
-
- ))} -
-
-
- - - -

写下您的书评

-

- 阅读完一本书后,请花几分钟时间分享您的阅读体验。您的评价将帮助其他社区成员找到适合他们的书籍。 -

-
- -
-
-
- - - -

书评指南

- - -
如何写一篇有帮助的书评
-
    -
  • 简要概述书籍内容,但不要透露关键情节
  • -
  • 分享您从书中获得的主要收获
  • -
  • 提及书籍的优点和可能的缺点
  • -
  • 说明适合阅读这本书的人群
  • -
  • 分享书中让您印象深刻的观点或引用
  • -
- - -
评分参考
-
    -
  • -
    -
    ★★★★★
    -
    杰出,强烈推荐,改变思维的书籍
    -
    -
  • -
  • -
    -
    ★★★★☆
    -
    非常好,值得推荐,有一些小缺点
    -
    -
  • -
  • -
    -
    ★★★☆☆
    -
    好,有价值,但不突出
    -
    -
  • -
  • -
    -
    ★★☆☆☆
    -
    一般,有一些有用信息,但整体平淡
    -
    -
  • -
  • -
    -
    ★☆☆☆☆
    -
    不推荐,内容质量较低或不准确
    -
    -
  • -
- -
-
-
-
-
-
- ); -} From 73f74abc04a296ec7da87d3c22eb956ae685447d Mon Sep 17 00:00:00 2001 From: dethan3 Date: Sat, 20 Jun 2026 16:08:09 +0800 Subject: [PATCH 8/8] =?UTF-8?q?refactor(open-library):=20address=20PR=20#2?= =?UTF-8?q?0=20review=20=E2=80=94=20i18n,=20types,=20mixin=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 with ComponentType to fix pre-existing TS2322 errors where FC was not assignable to FC> 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() --- .husky/pre-commit | 2 +- .husky/pre-push | 2 +- components/Activity/Hackathon/theme.less | 14 +- components/Layout/CardPage.tsx | 5 +- components/Navigator/MainNavigator.tsx | 10 +- components/open-library/BookCard.tsx | 64 +- components/open-library/FeaturedBooks.tsx | 102 +- components/open-library/Footer.tsx | 111 +- components/open-library/HeroSection.tsx | 48 +- .../open-library/HowItWorks.module.less | 43 + components/open-library/HowItWorks.tsx | 117 +- components/open-library/Layout.tsx | 47 +- components/open-library/Navbar.tsx | 34 +- .../open-library/useOpenLibraryLayout.ts | 20 - models/Book.ts | 32 + package.json | 3 +- pages/_app.tsx | 112 +- pages/api/open-library/books.ts | 219 ++++ pages/open-library/book/[id].tsx | 413 ++----- pages/open-library/books/index.tsx | 194 +--- pages/open-library/how-to-borrow.tsx | 461 ++++---- pages/open-library/index.tsx | 263 ++--- pnpm-lock.yaml | 1001 +++++++++++++++-- pnpm-workspace.yaml | 5 + public/images/placeholder-book.svg | 12 + public/images/placeholder-hero.svg | 29 + styles/open-library.css | 65 -- styles/open-library.module.scss | 11 + translation/en-US.ts | 48 +- translation/zh-CN.ts | 48 +- translation/zh-TW.ts | 48 +- 31 files changed, 2050 insertions(+), 1533 deletions(-) create mode 100644 components/open-library/HowItWorks.module.less delete mode 100644 components/open-library/useOpenLibraryLayout.ts create mode 100644 models/Book.ts create mode 100644 pages/api/open-library/books.ts create mode 100644 pnpm-workspace.yaml create mode 100644 public/images/placeholder-book.svg create mode 100644 public/images/placeholder-hero.svg delete mode 100644 styles/open-library.css create mode 100644 styles/open-library.module.scss diff --git a/.husky/pre-commit b/.husky/pre-commit index 98475b5..72c4429 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1 +1 @@ -pnpm test +npm test diff --git a/.husky/pre-push b/.husky/pre-push index 0c958d2..d6cb288 100644 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1 +1 @@ -pnpm run build +npm run build diff --git a/components/Activity/Hackathon/theme.less b/components/Activity/Hackathon/theme.less index ebfeffa..7472806 100644 --- a/components/Activity/Hackathon/theme.less +++ b/components/Activity/Hackathon/theme.less @@ -30,23 +30,23 @@ .section-frame() { .section { - ; + .section-shell(); } .sectionHeader { - ; + .section-header(); } .sectionTitle { - ; + .section-title(); } .sectionSubtitle { - ; + .section-subtitle(); } .accentLine { - ; + .accent-line(); } } @@ -116,7 +116,7 @@ } .button-primary() { - ; + .button-base(); box-shadow: 0 0 24px rgba(44, 232, 255, 0.18); border-color: rgba(44, 232, 255, 0.3); background: rgba(44, 232, 255, 0.12); @@ -130,7 +130,7 @@ } .button-ghost() { - ; + .button-base(); border-color: rgba(255, 255, 255, 0.14); background: rgba(255, 255, 255, 0.04); color: @copy; diff --git a/components/Layout/CardPage.tsx b/components/Layout/CardPage.tsx index f6da494..fd97894 100644 --- a/components/Layout/CardPage.tsx +++ b/components/Layout/CardPage.tsx @@ -1,10 +1,11 @@ -import { ComponentClass, FC } from 'react'; +import { ComponentType, FC } from 'react'; import { Col, Pagination, Row } from 'react-bootstrap'; import { SearchPageMeta } from '../../models/System'; export interface CardPageProps extends SearchPageMeta { - Card: ComponentClass> | FC>; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + Card: ComponentType; cardLinkOf?: (id: string) => string; pageLinkOf: (page: number) => string; } diff --git a/components/Navigator/MainNavigator.tsx b/components/Navigator/MainNavigator.tsx index fd1f512..25350fa 100644 --- a/components/Navigator/MainNavigator.tsx +++ b/components/Navigator/MainNavigator.tsx @@ -83,7 +83,7 @@ const topNavBarMenu = ({ t }: typeof i18n): MenuItem[] => [ }, { href: '/open-library', - name: t('open_library'), + title: t('open_library'), }, ]; @@ -98,14 +98,6 @@ export const MainNavigator: FC = observer(({ menu }) => { menu ||= topNavBarMenu(i18n); - // 检查是否是 Open Library 路径 - const isOpenLibraryPath = pathname.startsWith('/open-library'); - - // 如果是 Open Library 路径,不渲染主站导航栏 - if (isOpenLibraryPath) { - return null; - } - return ( diff --git a/components/open-library/BookCard.tsx b/components/open-library/BookCard.tsx index 54d568c..32cb844 100644 --- a/components/open-library/BookCard.tsx +++ b/components/open-library/BookCard.tsx @@ -1,65 +1,52 @@ import Link from 'next/link'; -import React, { useContext } from 'react'; -import { Button, Card, Image } from 'react-bootstrap'; +import { observer } from 'mobx-react'; +import { FC, useContext } from 'react'; +import { Badge, Button, Card, CardProps } from 'react-bootstrap'; +export type { Book } from '../../models/Book'; +import type { Book } from '../../models/Book'; import { I18nContext } from '../../models/Translation'; -export interface Book { - id: number; - title: string; - author: string; - cover?: string; - status?: 'available' | 'borrowed'; - category?: string; - description?: string; -} - -interface BookCardProps { +export interface BookCardProps extends CardProps { book: Book; showStatus?: boolean; variant?: 'featured' | 'catalog'; } -const BookCard: React.FC = ({ +export const BookCard: FC = observer(({ + className = '', book, showStatus = false, variant = 'featured', + ...cardProps }) => { - const cardClass = - variant === 'featured' - ? 'h-100 shadow-sm border-0' - : 'h-100 shadow border-0'; + const isFeatured = variant === 'featured'; const { t } = useContext(I18nContext); return ( - +
{showStatus && (
- - {book.status === 'available' ? '可借阅' : '已借出'} - + {book.status === 'available' ? t('available') : t('borrowed')} +
)} -
- +
@@ -79,8 +66,7 @@ const BookCard: React.FC = ({ {book.category && ( - - {book.category} + 🏷️ {book.category} )}
@@ -98,6 +84,6 @@ const BookCard: React.FC = ({ ); -}; +}); export default BookCard; diff --git a/components/open-library/FeaturedBooks.tsx b/components/open-library/FeaturedBooks.tsx index 7d7e586..25d3065 100644 --- a/components/open-library/FeaturedBooks.tsx +++ b/components/open-library/FeaturedBooks.tsx @@ -1,7 +1,9 @@ import Link from 'next/link'; -import React from 'react'; +import { observer } from 'mobx-react'; +import { FC, useContext } from 'react'; import { Button, Col, Row } from 'react-bootstrap'; +import { I18nContext } from '../../models/Translation'; import BookCard, { Book } from './BookCard'; import { ContentContainer } from './Layout'; @@ -14,57 +16,57 @@ interface FeaturedBooksProps { viewAllText?: string; } -const FeaturedBooks: React.FC = ({ - books, - title = '精选图书', - subtitle = '社区成员推荐的优质读物,涵盖技术、设计、创业等多个领域', - showViewAll = true, - viewAllLink = '/open-library/books', - viewAllText = '查看全部图书', -}) => ( -
- -
-

- {title} -
-
-
-

-

- {subtitle} -

-
+const FeaturedBooks: FC = observer( + ({ books, title, subtitle, showViewAll = true, viewAllLink = '/open-library/books', viewAllText }) => { + const { t } = useContext(I18nContext); + const resolvedTitle = title ?? t('featured_books'); + const resolvedSubtitle = subtitle ?? t('featured_books_subtitle'); + const resolvedViewAllText = viewAllText ?? t('view_all_books'); - - {books.slice(0, 8).map(book => ( - - - - ))} - + return ( +
+ +
+

+ {resolvedTitle} +
+
+
+

+

+ {resolvedSubtitle} +

+
- {showViewAll && ( -
- - - -
- )} -
-
+ + {books.slice(0, 8).map(book => ( + + + + ))} + + + {showViewAll && ( +
+ + + +
+ )} +
+
+ ); + }, ); export default FeaturedBooks; diff --git a/components/open-library/Footer.tsx b/components/open-library/Footer.tsx index 53a09b4..b863bee 100644 --- a/components/open-library/Footer.tsx +++ b/components/open-library/Footer.tsx @@ -1,53 +1,35 @@ -import React, { PropsWithChildren, useContext } from 'react'; -import { Col, Nav, Row } from 'react-bootstrap'; +import { observer } from 'mobx-react'; +import { useContext } from 'react'; +import { Col, Container, Nav, Row } from 'react-bootstrap'; import { I18nContext } from '../../models/Translation'; -// 使用 Bootstrap 工具类替换内联样式的 ContentContainer -const ContentContainer: React.FC = ({ children }) => ( -
{children}
-); - -const FooterComponent = () => { - // Use client-side rendering for the copyright text to avoid hydration issues - const [isMounted, setIsMounted] = React.useState(false); - +const FooterComponent = observer(() => { const { t } = useContext(I18nContext); - React.useEffect(() => { - setIsMounted(true); - }, []); - return (
- +
{t('open_library')}
-

- {t('footer_description')} -

+

{t('footer_description')}

@@ -58,59 +40,54 @@ const FooterComponent = () => { href="/open-library/books" className="text-light px-0 py-1 text-decoration-none" > - - {t('catalog_footer')} + 📖 {t('catalog_footer')} - - {t('how_to_borrow')} + ℹ️ {t('how_to_borrow')}
{t('contact')}
-
-

- - freeCodeCamp Chengdu Community -

-

- - Chengdu, Sichuan, China -

-

- - Email: contact@openlibrary.org -

-

- - WeChat: FCCChengdu -

-
+

- {isMounted ? ( - <> - © {new Date().getFullYear()} {t('open_library')}.{' '} - {t('all_rights_reserved')} - - ) : ( - <> - © {new Date().getFullYear()} Open Library. All rights - reserved. - - )} + © {new Date().getFullYear()} {t('open_library')}. {t('all_rights_reserved')}
-
+
); -}; +}); export default FooterComponent; diff --git a/components/open-library/HeroSection.tsx b/components/open-library/HeroSection.tsx index 805bbf2..982745a 100644 --- a/components/open-library/HeroSection.tsx +++ b/components/open-library/HeroSection.tsx @@ -4,35 +4,38 @@ import { Button, Col, Image, Row } from 'react-bootstrap'; import { ContentContainer } from './Layout'; interface HeroSectionProps { - title?: string; - subtitle?: string; - ctaText?: string; - ctaLink?: string; - heroImage?: string; - heroImageAlt?: string; + title: string; + subtitle: string; + ctaText: string; + ctaLink: string; + browseText: string; + browseLink?: string; + heroImage: string; + heroImageAlt: string; } const HeroSection: React.FC = ({ - title = 'freeCodeCamp 成都社区 Open Library', - subtitle = '让知识在社区中自由流动,连接每一个热爱学习的心', - ctaText = '加入我们', - ctaLink = 'https://open-source-bazaar.feishu.cn/share/base/form/shrcngQgMrhjTh6ycO1zcaEWZld', - heroImage = '/images/open-library-hero.svg', - heroImageAlt = '社区成员分享书籍', + title, + subtitle, + ctaText, + ctaLink, + browseText, + browseLink = '/open-library/books', + heroImage, + heroImageAlt, }) => (
- +

{title}

@@ -46,29 +49,22 @@ const HeroSection: React.FC = ({ rel="noopener noreferrer" className="me-3 mb-2 rounded-pill px-4 fw-bold text-dark" > - - {ctaText} + 👥 {ctaText}
- {heroImageAlt} + {heroImageAlt}
diff --git a/components/open-library/HowItWorks.module.less b/components/open-library/HowItWorks.module.less new file mode 100644 index 0000000..ca6c35d --- /dev/null +++ b/components/open-library/HowItWorks.module.less @@ -0,0 +1,43 @@ +.sectionTitleMarker { + width: 60px; + height: 3px; +} + +.stepIconWrap { + height: 80px; +} + +.stepIcon { + background-color: var(--step-color); + width: 60px; + height: 60px; +} + +.stepNumber { + top: -10px; + right: calc(50% - 42px); + width: 24px; + height: 24px; + font-size: 0.75rem; +} + +.connector { + top: 30px; + left: calc(100% - 30px); + width: 60px; + height: 2px; +} + +.connectorArrow { + top: -4px; + right: -6px; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-left: 6px solid #6c757d; + width: 0; + height: 0; +} + +.subtitle { + max-width: 600px; +} diff --git a/components/open-library/HowItWorks.tsx b/components/open-library/HowItWorks.tsx index 33e289c..61daaf9 100644 --- a/components/open-library/HowItWorks.tsx +++ b/components/open-library/HowItWorks.tsx @@ -1,8 +1,11 @@ import Link from 'next/link'; -import React from 'react'; +import { observer } from 'mobx-react'; +import { CSSProperties, FC, useContext } from 'react'; import { Button, Col, Row } from 'react-bootstrap'; +import { I18nContext } from '../../models/Translation'; import { ContentContainer } from './Layout'; +import styles from './HowItWorks.module.less'; interface Step { id: number; @@ -20,114 +23,53 @@ interface HowItWorksProps { learnMoreLink?: string; } -const defaultSteps: Step[] = [ - { - id: 1, - title: '浏览图书', - description: '在图书目录中找到你感兴趣的书籍,查看详细信息和当前状态', - icon: 'bi-search', - color: '#6f42c1', - }, - { - id: 2, - title: '申请借阅', - description: '填写借阅申请表单,系统会自动联系当前持书人', - icon: 'bi-file-earmark-text', - color: '#0d6efd', - }, - { - id: 3, - title: '线下传递', - description: '与持书人约定传递方式,通常通过快递或面对面交接', - icon: 'bi-arrow-left-right', - color: '#fd7e14', - }, - { - id: 4, - title: '享受阅读', - description: '阅读完成后,可以写下书评并准备传递给下一位读者', - icon: 'bi-book-half', - color: '#198754', - }, -]; +const HowItWorks: FC = observer(({ title, subtitle, steps, showLearnMore = true, learnMoreLink = '/open-library/how-to-borrow' }) => { + const { t } = useContext(I18nContext); + const resolvedTitle = title ?? t('how_it_works'); + const resolvedSubtitle = subtitle ?? t('how_it_works_description'); + const resolvedSteps: Step[] = steps ?? [ + { id: 1, title: t('step_1_find_book'), description: t('step_1_description'), icon: '�', color: '#6f42c1' }, + { id: 2, title: t('step_2_apply'), description: t('step_2_description'), icon: '📄', color: '#0d6efd' }, + { id: 3, title: t('step_3_receive'), description: t('step_3_description'), icon: '↔️', color: '#fd7e14' }, + ]; -const HowItWorks: React.FC = ({ - title = '如何使用 Open Library', - subtitle = '简单四步,让知识在社区中自由流动', - steps = defaultSteps, - showLearnMore = true, - learnMoreLink = '/open-library/how-to-borrow', -}) => ( + return (

- {title} + {resolvedTitle}
-
+

-

- {subtitle} -

+

{resolvedSubtitle}

- {steps.map((step, index) => ( + {resolvedSteps.map((step, index) => (
- + {step.icon}
{step.id} {/* 连接线仅在大屏幕显示且不是最后一个 */} - {index < steps.length - 1 && ( + {index < resolvedSteps.length - 1 && (
-
+
)}
@@ -147,15 +89,14 @@ const HowItWorks: React.FC = ({ as="a" className="rounded-pill px-4 fw-semibold" > - - 了解详细流程 - + ℹ️ {t('learn_more_details')} →
)}
-); + ); +}); export default HowItWorks; diff --git a/components/open-library/Layout.tsx b/components/open-library/Layout.tsx index 5476eef..b11081f 100644 --- a/components/open-library/Layout.tsx +++ b/components/open-library/Layout.tsx @@ -1,47 +1,8 @@ -import Head from 'next/head'; -import React, { PropsWithChildren } from 'react'; +import { FC, PropsWithChildren } from 'react'; -import FooterComponent from './Footer'; -import LibraryNavbar from './Navbar'; -import { useOpenLibraryLayout } from './useOpenLibraryLayout'; - -const ContentContainer: React.FC = ({ children }) => ( +const ContentContainer: FC = ({ children }) => (
{children}
); -interface LayoutProps { - title?: string; -} - -/** - * Open Library 的共享布局组件 - * 包含导航栏、页脚和内容容器 - * 所有 Open Library 页面都应使用此布局 - */ -const Layout: React.FC> = ({ - children, - title = 'Open Library - Open Source Bazaar', -}) => { - // Apply Open Library layout styles - useOpenLibraryLayout(); - - return ( - <> - - {title} - - - - - -
- {children} -
- - - - ); -}; - -export default Layout; -export { ContentContainer, Layout }; +export default ContentContainer; +export { ContentContainer }; diff --git a/components/open-library/Navbar.tsx b/components/open-library/Navbar.tsx index 7b08fe9..88b4587 100644 --- a/components/open-library/Navbar.tsx +++ b/components/open-library/Navbar.tsx @@ -1,43 +1,25 @@ -import dynamic from 'next/dynamic'; -import React, { useContext } from 'react'; +import { observer } from 'mobx-react'; +import { useContext } from 'react'; import { Nav, Navbar } from 'react-bootstrap'; import { I18nContext } from '../../models/Translation'; +import LanguageMenu from '../Navigator/LanguageMenu'; -// 动态导入 LanguageMenu 组件,禁用 SSR -const LanguageMenu = dynamic(() => import('../Navigator/LanguageMenu'), { - ssr: false, -}); - -const LibraryNavbar = () => { - // Use the new i18n context +export const LibraryNavbar = observer(() => { const { t } = useContext(I18nContext); - // Use a client-side only rendering approach for the brand text to avoid hydration mismatch - const [isMounted, setIsMounted] = React.useState(false); - - React.useEffect(() => { - setIsMounted(true); - }, []); - return (
- - {/* Use a static placeholder during SSR and replace it with translated content after hydration */} - {isMounted ? t('open_library') : 'Open Library'} + + {t('open_library')} @@ -45,6 +27,6 @@ const LibraryNavbar = () => {
); -}; +}); export default LibraryNavbar; diff --git a/components/open-library/useOpenLibraryLayout.ts b/components/open-library/useOpenLibraryLayout.ts deleted file mode 100644 index cd15c3d..0000000 --- a/components/open-library/useOpenLibraryLayout.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { useEffect } from 'react'; - -/** - * Hook to apply Open Library layout styles - * This adds the open-library class to the body and html elements - * which triggers the CSS rules in styles/open-library.css - */ -export function useOpenLibraryLayout() { - useEffect(() => { - // Add open-library class to body and html - document.body.classList.add('open-library'); - document.documentElement.classList.add('open-library'); - - // Clean up function to remove classes when component unmounts - return () => { - document.body.classList.remove('open-library'); - document.documentElement.classList.remove('open-library'); - }; - }, []); -} diff --git a/models/Book.ts b/models/Book.ts new file mode 100644 index 0000000..04425fa --- /dev/null +++ b/models/Book.ts @@ -0,0 +1,32 @@ +export interface BorrowHistory { + borrower: string; + borrowDate: string; + returnDate?: string; +} + +export interface BookReview { + reviewer: string; + rating: number; + comment: string; + date: string; +} + +export interface Book { + id: number; + title: string; + author: string; + cover?: string; + status: 'available' | 'borrowed'; + category: string; + language: string; + currentHolder?: string; + description?: string; + isbn?: string; + publisher?: string; + publishYear?: number; + pageCount?: number; + borrowHistory?: BorrowHistory[]; + reviews?: BookReview[]; + rating?: number; + tags?: string[]; +} diff --git a/package.json b/package.json index 6dfda4a..e412ea8 100644 --- a/package.json +++ b/package.json @@ -112,6 +112,5 @@ }, "lint-staged": { "*.{html,md,less,scss,json,yml,js,mjs,ts,tsx}": "prettier --write" - }, - "packageManager": "pnpm@10.12.4+sha512.5ea8b0deed94ed68691c9bad4c955492705c5eeb8a87ef86bc62c74a26b037b08ff9570f108b2e4dbd1dd1a9186fea925e527f141c648e85af45631074680184" + } } diff --git a/pages/_app.tsx b/pages/_app.tsx index f864fef..b73b61a 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,5 +1,4 @@ import '../styles/globals.css'; -import '../styles/open-library.css'; import { HTTPError } from 'koajax'; import { configure } from 'mobx'; @@ -10,6 +9,8 @@ import { Image } from 'react-bootstrap'; import { MainNavigator } from '../components/Navigator/MainNavigator'; import { PageContent } from '../components/PageContent'; +import FooterComponent from '../components/open-library/Footer'; +import { LibraryNavbar } from '../components/open-library/Navbar'; import { isServer } from '../models/configuration'; import { createI18nStore, I18nContext, I18nProps, loadSSRLanguage } from '../models/Translation'; @@ -39,6 +40,68 @@ export default class CustomApp extends App { }); } + renderFooter(thisFullYear: number, t: (key: 'open_source_bazaar') => string) { + return ( +
+

+ + © 2021{thisFullYear === 2021 ? '' : `-${thisFullYear}`}{' '} + {t('open_source_bazaar')} + + + Powered by + + Vercel Logo + + +

+
+ ); + } + + renderPageContent(Component: typeof this.props.Component, pageProps: typeof this.props.pageProps) { + return ; + } + + renderOpenLibraryFrame( + Component: typeof this.props.Component, + pageProps: typeof this.props.pageProps, + ) { + return ( + <> + +
{this.renderPageContent(Component, pageProps)}
+ + + ); + } + + renderSiteFrame( + Component: typeof this.props.Component, + pageProps: typeof this.props.pageProps, + isArticlePage: boolean, + ) { + const content = this.renderPageContent(Component, pageProps); + + return ( + <> + + +
{isArticlePage ? {content} : content}
+ + ); + } + render() { const { Component, pageProps, router } = this.props, { t } = this.i18nStore; @@ -48,7 +111,7 @@ export default class CustomApp extends App { isActivityPage = asPath.startsWith('/hackathon'); // 检查是否是 Open Library 路径 - const isOpenLibraryPath = router.route.startsWith('/open-library'); + const isOpenLibraryPath = router.asPath.startsWith('/open-library'); return ( @@ -58,44 +121,13 @@ export default class CustomApp extends App { {t('open_source_bazaar')} - {isActivityPage || isOpenLibraryPath ? ( - - ) : ( - <> - - -
- {isArticlePage ? ( - - - - ) : ( - - )} -
- - )} - - {!isActivityPage && !isOpenLibraryPath && ( -
-

- - © 2021{thisFullYear === 2021 ? '' : `-${thisFullYear}`} {t('open_source_bazaar')} - - - Powered by - - Vercel Logo - - -

-
- )} + {isActivityPage + ? this.renderPageContent(Component, pageProps) + : isOpenLibraryPath + ? this.renderOpenLibraryFrame(Component, pageProps) + : this.renderSiteFrame(Component, pageProps, isArticlePage)} + + {!isActivityPage && !isOpenLibraryPath && this.renderFooter(thisFullYear, t)}
); } diff --git a/pages/api/open-library/books.ts b/pages/api/open-library/books.ts new file mode 100644 index 0000000..844febe --- /dev/null +++ b/pages/api/open-library/books.ts @@ -0,0 +1,219 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; + +import type { Book } from '../../../models/Book'; + +export const openLibraryBooks: Book[] = [ + { + id: 1, + title: 'Clean Code', + author: 'Robert C. Martin', + cover: '/images/placeholder-book.svg', + category: 'Programming', + language: 'English', + status: 'available', + description: 'A handbook of agile software craftsmanship', + isbn: '9780132350884', + publisher: 'Prentice Hall', + publishYear: 2008, + pageCount: 464, + borrowHistory: [ + { + borrower: 'Wang Chen', + borrowDate: '2023-10-15', + returnDate: '2023-11-20', + }, + { + borrower: 'Li Mei', + borrowDate: '2023-08-01', + returnDate: '2023-09-05', + }, + ], + reviews: [ + { + reviewer: 'Wang Chen', + rating: 5, + comment: + 'This book completely changed how I approach writing code. Highly recommended for all developers!', + date: '2023-11-25', + }, + { + reviewer: 'Li Mei', + rating: 4, + comment: + 'Great principles that have stood the test of time. Some examples are a bit dated but the concepts are solid.', + date: '2023-09-10', + }, + ], + }, + { + id: 2, + title: 'Eloquent JavaScript', + author: 'Marijn Haverbeke', + cover: '/images/placeholder-book.svg', + category: 'Programming', + language: 'English', + status: 'borrowed', + currentHolder: 'Zhang Wei', + description: 'A modern introduction to programming', + isbn: '9781593279509', + publisher: 'No Starch Press', + publishYear: 2018, + pageCount: 472, + borrowHistory: [ + { + borrower: 'Zhang Wei', + borrowDate: '2024-03-10', + }, + ], + reviews: [ + { + reviewer: 'Liu Jie', + rating: 5, + comment: + 'Perfect for beginners and intermediate JavaScript developers. The exercises are particularly helpful.', + date: '2023-12-05', + }, + ], + }, + { + id: 3, + title: 'The Pragmatic Programmer', + author: 'Andrew Hunt & David Thomas', + cover: '/images/placeholder-book.svg', + category: 'Programming', + language: 'English', + status: 'available', + description: 'Your journey to mastery', + isbn: '9780201616224', + publisher: 'Addison-Wesley Professional', + publishYear: 1999, + pageCount: 352, + borrowHistory: [ + { + borrower: 'Chen Ming', + borrowDate: '2023-06-15', + returnDate: '2023-07-20', + }, + ], + reviews: [ + { + reviewer: 'Chen Ming', + rating: 4, + comment: + 'A classic that has stood the test of time. The advice is still practical for teams of all sizes.', + date: '2023-07-25', + }, + ], + }, + { + id: 4, + title: "You Don't Know JS", + author: 'Kyle Simpson', + cover: '/images/placeholder-book.svg', + category: 'Programming', + language: 'English', + status: 'borrowed', + currentHolder: 'Jane Smith', + description: 'A book series on JavaScript', + isbn: '9781491924464', + publisher: "O'Reilly Media", + publishYear: 2015, + pageCount: 278, + borrowHistory: [], + reviews: [], + }, + { + id: 5, + title: '深入理解计算机系统', + author: "Randal E. Bryant & David R. O'Hallaron", + cover: '/images/placeholder-book.svg', + category: 'Computer Science', + language: 'Chinese', + status: 'borrowed', + currentHolder: 'Li Mei', + description: "Computer Systems: A Programmer's Perspective (Chinese Edition)", + isbn: '9787111544937', + publisher: '机械工业出版社', + publishYear: 2016, + pageCount: 731, + borrowHistory: [ + { + borrower: 'Li Mei', + borrowDate: '2024-02-01', + }, + ], + reviews: [ + { + reviewer: 'Zhang Wei', + rating: 5, + comment: '这是一本非常全面的计算机系统书籍,对理解计算机底层工作原理非常有帮助。', + date: '2023-10-15', + }, + ], + }, + { + id: 6, + title: '算法导论', + author: 'Thomas H. Cormen et al.', + cover: '/images/placeholder-book.svg', + category: 'Computer Science', + language: 'Chinese', + status: 'available', + description: 'Introduction to Algorithms (Chinese Edition)', + isbn: '9787111407010', + publisher: '机械工业出版社', + publishYear: 2013, + pageCount: 780, + borrowHistory: [], + reviews: [], + }, + { + id: 7, + title: 'JavaScript高级程序设计', + author: 'Nicholas C. Zakas', + cover: '/images/placeholder-book.svg', + category: 'Programming', + language: 'Chinese', + status: 'available', + description: 'Professional JavaScript for Web Developers (Chinese Edition)', + isbn: '9787115545381', + publisher: '人民邮电出版社', + publishYear: 2020, + pageCount: 984, + borrowHistory: [], + reviews: [], + }, + { + id: 8, + title: 'CSS揭秘', + author: 'Lea Verou', + cover: '/images/placeholder-book.svg', + category: 'Web Development', + language: 'Chinese', + status: 'borrowed', + currentHolder: 'Wang Chen', + description: 'CSS Secrets (Chinese Edition)', + isbn: '9787115416940', + publisher: '人民邮电出版社', + publishYear: 2016, + pageCount: 288, + borrowHistory: [ + { + borrower: 'Wang Chen', + borrowDate: '2024-01-08', + }, + ], + reviews: [ + { + reviewer: 'Li Mei', + rating: 4, + comment: '案例很实用,适合前端开发者反复翻阅。', + date: '2023-12-18', + }, + ], + }, +]; + +export default function handler(_: NextApiRequest, response: NextApiResponse) { + response.status(200).json(openLibraryBooks); +} diff --git a/pages/open-library/book/[id].tsx b/pages/open-library/book/[id].tsx index 901fd47..e697a87 100644 --- a/pages/open-library/book/[id].tsx +++ b/pages/open-library/book/[id].tsx @@ -1,263 +1,42 @@ import Image from 'next/image'; +import { GetServerSideProps } from 'next'; +import { observer } from 'mobx-react'; +import { useContext } from 'react'; import { useRouter } from 'next/router'; -import React, { useEffect, useState } from 'react'; -import { Badge, Button, Card, Col, Row, Tab, Tabs } from 'react-bootstrap'; +import { Badge, Button, Card, Col, Row, Tab, Table, Tabs } from 'react-bootstrap'; +import { formatDate } from 'web-utility'; -import { - ContentContainer, - Layout, -} from '../../../components/open-library/Layout'; +import { PageHead } from '../../../components/Layout/PageHead'; +import { ContentContainer } from '../../../components/open-library/Layout'; +import type { Book } from '../../../models/Book'; +import { I18nContext } from '../../../models/Translation'; +import { openLibraryBooks } from '../../api/open-library/books'; -// Book type definition -interface Book { - id: number; - title: string; - author: string; - cover?: string; - category: string; - language: string; - status: 'available' | 'borrowed'; - currentHolder?: string; - description?: string; - isbn?: string; - publisher?: string; - publishYear?: number; - pageCount?: number; - borrowHistory?: { - borrower: string; - borrowDate: string; - returnDate?: string; - }[]; - reviews?: { - reviewer: string; - rating: number; - comment: string; - date: string; - }[]; +interface BookDetailProps { + book: Book; } -// Mock database of books -const booksDatabase: Book[] = [ - { - id: 1, - title: 'Clean Code', - author: 'Robert C. Martin', - cover: '/images/placeholder-book.svg', - category: 'Programming', - language: 'English', - status: 'available', - description: - "A handbook of agile software craftsmanship. Even bad code can function. But if code isn't clean, it can bring a development organization to its knees. Every year, countless hours and significant resources are lost because of poorly written code. But it doesn't have to be that way.", - isbn: '9780132350884', - publisher: 'Prentice Hall', - publishYear: 2008, - pageCount: 464, - borrowHistory: [ - { - borrower: 'Wang Chen', - borrowDate: '2023-10-15', - returnDate: '2023-11-20', - }, - { - borrower: 'Li Mei', - borrowDate: '2023-08-01', - returnDate: '2023-09-05', - }, - ], - reviews: [ - { - reviewer: 'Wang Chen', - rating: 5, - comment: - 'This book completely changed how I approach writing code. Highly recommended for all developers!', - date: '2023-11-25', - }, - { - reviewer: 'Li Mei', - rating: 4, - comment: - 'Great principles that have stood the test of time. Some examples are a bit dated but the concepts are solid.', - date: '2023-09-10', - }, - ], - }, - { - id: 2, - title: 'Eloquent JavaScript', - author: 'Marijn Haverbeke', - cover: '/images/placeholder-book.svg', - category: 'Programming', - language: 'English', - status: 'borrowed', - currentHolder: 'Zhang Wei', - description: - 'A modern introduction to programming. JavaScript lies at the heart of almost every modern web application, from social apps like Twitter to browser-based game frameworks like Phaser and Babylon. Though simple for beginners to pick up and play with, JavaScript is a flexible, complex language that you can use to build full-scale applications.', - isbn: '9781593279509', - publisher: 'No Starch Press', - publishYear: 2018, - pageCount: 472, - borrowHistory: [ - { - borrower: 'Zhang Wei', - borrowDate: '2024-03-10', - }, - ], - reviews: [ - { - reviewer: 'Liu Jie', - rating: 5, - comment: - 'Perfect for beginners and intermediate JavaScript developers. The exercises are particularly helpful.', - date: '2023-12-05', - }, - ], - }, - { - id: 3, - title: 'Design Patterns', - author: 'Erich Gamma et al.', - cover: '/images/placeholder-book.svg', - category: 'Programming', - language: 'English', - status: 'available', - description: - 'Elements of Reusable Object-Oriented Software. Capturing a wealth of experience about the design of object-oriented software, four top-notch designers present a catalog of simple and succinct solutions to commonly occurring design problems.', - isbn: '9780201633610', - publisher: 'Addison-Wesley Professional', - publishYear: 1994, - pageCount: 416, - borrowHistory: [ - { - borrower: 'Chen Ming', - borrowDate: '2023-06-15', - returnDate: '2023-07-20', - }, - ], - reviews: [ - { - reviewer: 'Chen Ming', - rating: 4, - comment: - 'A classic that has stood the test of time. The examples are in C++ and Smalltalk, but the concepts apply to any OO language.', - date: '2023-07-25', - }, - ], - }, - { - id: 4, - title: "You Don't Know JS", - author: 'Kyle Simpson', - cover: '/images/placeholder-book.svg', - category: 'Programming', - language: 'English', - status: 'available', - description: - 'A book series on JavaScript. This is a series of books diving deep into the core mechanisms of the JavaScript language.', - isbn: '9781491924464', - publisher: "O'Reilly Media", - publishYear: 2015, - pageCount: 278, - borrowHistory: [], - reviews: [], - }, - { - id: 5, - title: '深入理解计算机系统', - author: 'Randal E. Bryant', - cover: '/images/placeholder-book.svg', - category: 'Computer Science', - language: 'Chinese', - status: 'borrowed', - currentHolder: 'Li Mei', - description: - "Computer Systems: A Programmer's Perspective (Chinese Edition). This book introduces the important and enduring concepts that underlie computer systems.", - isbn: '9787111544937', - publisher: '机械工业出版社', - publishYear: 2016, - pageCount: 731, - borrowHistory: [ - { - borrower: 'Li Mei', - borrowDate: '2024-02-01', - }, - ], - reviews: [ - { - reviewer: 'Zhang Wei', - rating: 5, - comment: - '这是一本非常全面的计算机系统书籍,对理解计算机底层工作原理非常有帮助。', - date: '2023-10-15', - }, - ], - }, -]; +export const getServerSideProps: GetServerSideProps = async ({ params }) => { + const id = Array.isArray(params?.id) ? params?.id[0] : params?.id, + bookId = Number(id); + const book = openLibraryBooks.find(({ id }) => id === bookId); -export default function BookDetail() { - const router = useRouter(); - const { id } = router.query; - const [book, setBook] = useState(null); - const [loading, setLoading] = useState(true); - - useEffect(() => { - if (id) { - // In a real app, this would be an API call - const bookId = Array.isArray(id) - ? parseInt(id[0]) - : parseInt(id as string); - const foundBook = booksDatabase.find(b => b.id === bookId); - - setBook(foundBook || null); - setLoading(false); - } - }, [id]); + if (!book) return { notFound: true }; - if (loading) { - return ( - - -
-
- Loading... -
-
-
-
- ); - } + return { props: { book } }; +}; - if (!book) { - return ( - - -
-

Book Not Found

-

Sorry, we couldn't find the book you're looking for.

- -
-
-
- ); - } +const BookDetail = observer(({ book }: BookDetailProps) => { + const router = useRouter(); + const { t } = useContext(I18nContext); return ( - - + + +
- @@ -278,20 +57,21 @@ export default function BookDetail() { text={book.status === 'available' ? 'white' : 'dark'} className="px-3 py-2" > - {book.status === 'available' - ? 'Available' - : 'Currently Borrowed'} + {book.status === 'available' ? t('available') : t('currently_borrowed')}
{book.status === 'borrowed' && book.currentHolder && (
- Current holder: {book.currentHolder} + {t('currently_with').replace('{0}', book.currentHolder)}
)}

{book.title}

-
by {book.author}
+

+ {t('by_author').replace('{0}', '')} + {book.author} +

@@ -304,22 +84,22 @@ export default function BookDetail() {

{book.description}

- - + +
ISBN
-
{book.isbn || 'N/A'}
+
{book.isbn || t('not_available')}
- -
Publisher
-
{book.publisher || 'N/A'}
+ +
{t('book_publisher')}
+
{book.publisher || t('not_available')}
- -
Published
-
{book.publishYear || 'N/A'}
+ +
{t('book_published_year')}
+
{book.publishYear || t('not_available')}
- -
Pages
-
{book.pageCount || 'N/A'}
+ +
{t('book_page_count')}
+
{book.pageCount || t('not_available')}
@@ -332,11 +112,11 @@ export default function BookDetail() { target="_blank" rel="noopener noreferrer" > - Request to Borrow + {t('request_to_borrow')} ) : ( )}
@@ -347,9 +127,9 @@ export default function BookDetail() {
- - {book.reviews && book.reviews.length > 0 ? ( -
+ + {book.reviews?.[0] ? ( + <> {book.reviews.map((review, index) => ( @@ -366,7 +146,7 @@ export default function BookDetail() { {review.comment} - {new Date(review.date).toLocaleDateString()} + {formatDate(review.date, 'YYYY-MM-DD')} @@ -379,78 +159,67 @@ export default function BookDetail() { target="_blank" rel="noopener noreferrer" > - Add Your Review + {t('add_your_review')}
-
+ ) : (
-

- No reviews yet. Be the first to review this book! -

+

{t('be_first_to_review')}

)} - - {book.borrowHistory && book.borrowHistory.length > 0 ? ( -
- - - - - - - + + {book.borrowHistory?.[0] ? ( +
BorrowerBorrow DateReturn DateStatus
+ + + + + + + + + + {book.borrowHistory.map((history, index) => ( + + + + + - - - {book.borrowHistory.map((history, index) => ( - - - - - - - ))} - -
{t('borrower')}{t('borrow_date')}{t('return_date')}{t('status')}
{history.borrower}{formatDate(history.borrowDate, 'YYYY-MM-DD')} + {history.returnDate ? formatDate(history.returnDate, 'YYYY-MM-DD') : '-'} + + {history.returnDate ? ( + {t('returned')} + ) : ( + + {t('active')} + + )} +
{history.borrower} - {new Date( - history.borrowDate, - ).toLocaleDateString()} - - {history.returnDate - ? new Date( - history.returnDate, - ).toLocaleDateString() - : '-'} - - {history.returnDate ? ( - Returned - ) : ( - - Active - - )} -
-
+ ))} + + ) : (
-

This book has not been borrowed yet.

+

{t('not_borrowed_yet')}

)}
- -
+ ); -} +}); + +export default BookDetail; diff --git a/pages/open-library/books/index.tsx b/pages/open-library/books/index.tsx index 7e38a5c..1969374 100644 --- a/pages/open-library/books/index.tsx +++ b/pages/open-library/books/index.tsx @@ -1,184 +1,36 @@ -import React from 'react'; -import { Card, Col, Row } from 'react-bootstrap'; +import { observer } from 'mobx-react'; +import { useContext } from 'react'; +import { Col, Row } from 'react-bootstrap'; -import { - ContentContainer, - Layout, -} from '../../../components/open-library/Layout'; +import { PageHead } from '../../../components/Layout/PageHead'; +import BookCard from '../../../components/open-library/BookCard'; +import { ContentContainer } from '../../../components/open-library/Layout'; +import { I18nContext } from '../../../models/Translation'; +import { openLibraryBooks } from '../../api/open-library/books'; -// Book type definition -interface Book { - id: number; - title: string; - author: string; - cover?: string; - category: string; - language: string; - status: 'available' | 'borrowed'; - currentHolder?: string; - description?: string; -} - -export default function BookCatalog() { - // Sample books data - in a real app, this would come from an API - const books: Book[] = [ - { - id: 1, - title: 'Clean Code', - author: 'Robert C. Martin', - cover: '/images/placeholder-book.svg', - category: 'Programming', - language: 'English', - status: 'available', - description: 'A handbook of agile software craftsmanship', - }, - { - id: 2, - title: 'Eloquent JavaScript', - author: 'Marijn Haverbeke', - cover: '/images/placeholder-book.svg', - category: 'Programming', - language: 'English', - status: 'borrowed', - currentHolder: 'John Doe', - description: 'A modern introduction to programming', - }, - { - id: 3, - title: 'The Pragmatic Programmer', - author: 'Andrew Hunt & David Thomas', - cover: '/images/placeholder-book.svg', - category: 'Programming', - language: 'English', - status: 'available', - description: 'Your journey to mastery', - }, - { - id: 4, - title: "You Don't Know JS", - author: 'Kyle Simpson', - cover: '/images/placeholder-book.svg', - category: 'Programming', - language: 'English', - status: 'borrowed', - currentHolder: 'Jane Smith', - description: 'A book series on JavaScript', - }, - { - id: 5, - title: '深入理解计算机系统', - author: "Randal E. Bryant & David R. O'Hallaron", - cover: '/images/placeholder-book.svg', - category: 'Computer Science', - language: 'Chinese', - status: 'borrowed', - currentHolder: 'Li Mei', - description: - "Computer Systems: A Programmer's Perspective (Chinese Edition)", - }, - { - id: 6, - title: '算法导论', - author: 'Thomas H. Cormen et al.', - cover: '/images/placeholder-book.svg', - category: 'Computer Science', - language: 'Chinese', - status: 'available', - description: 'Introduction to Algorithms (Chinese Edition)', - }, - { - id: 7, - title: 'JavaScript高级程序设计', - author: 'Nicholas C. Zakas', - cover: '/images/placeholder-book.svg', - category: 'Programming', - language: 'Chinese', - status: 'available', - description: - 'Professional JavaScript for Web Developers (Chinese Edition)', - }, - { - id: 8, - title: 'CSS揭秘', - author: 'Lea Verou', - cover: '/images/placeholder-book.svg', - category: 'Web Development', - language: 'Chinese', - status: 'borrowed', - currentHolder: 'Wang Chen', - description: 'CSS Secrets (Chinese Edition)', - }, - ]; +const BookCatalog = observer(() => { + const { t } = useContext(I18nContext); return ( - - + + +
-

图书目录

-

- 浏览我们的社区共享图书馆,发现有趣的书籍 -

+

{t('book_catalog')}

+

{t('book_catalog_description')}

- - {books.map(book => ( - - -
- -
- - {book.status === 'available' ? '可借阅' : '已借出'} - -
-
- - - {book.title} - - - {book.author} - - - {book.category} • {book.language} - - {book.description && ( - - {book.description} - - )} - {book.status === 'borrowed' && book.currentHolder && ( - - 当前持书人: {book.currentHolder} - - )} -
- - 查看详情 - -
-
-
+ + {openLibraryBooks.map(book => ( + + ))}
-
-
+ ); -} +}); + +export default BookCatalog; diff --git a/pages/open-library/how-to-borrow.tsx b/pages/open-library/how-to-borrow.tsx index 6116a36..f73822f 100644 --- a/pages/open-library/how-to-borrow.tsx +++ b/pages/open-library/how-to-borrow.tsx @@ -1,298 +1,235 @@ import Link from 'next/link'; -import React from 'react'; -import { Card, Col, Row } from 'react-bootstrap'; +import { observer } from 'mobx-react'; +import { useContext } from 'react'; +import { Button, Card, Col, ListGroup, Row } from 'react-bootstrap'; -import { ContentContainer, Layout } from '../../components/open-library/Layout'; +import { PageHead } from '../../components/Layout/PageHead'; +import { ContentContainer } from '../../components/open-library/Layout'; +import { I18nContext } from '../../models/Translation'; + +const catalogURL = + 'https://open-source-bazaar.feishu.cn/share/base/view/shrcnvT0Lyk8LKS8KtPbO9HPPHb'; +const borrowFormURL = + 'https://open-source-bazaar.feishu.cn/share/base/form/shrcnNiKwb9ApzFFGI3YkCXDdwe?prefill_%E6%93%8D%E4%BD%9C=%E5%80%9F%E5%85%A5'; +const handoffFormURL = + 'https://open-source-bazaar.feishu.cn/share/base/form/shrcnuDb3oOuhMjSaXNIHEPA4Ef?prefill_%E6%93%8D%E4%BD%9C=%E5%80%9F%E5%87%BA'; +const membershipFormURL = + 'https://open-source-bazaar.feishu.cn/share/base/form/shrcngQgMrhjTh6ycO1zcaEWZld'; + +const borrowSteps = [ + { + title: '查阅书籍', + description: ( + <> + 社区成员可以在{' '} + + fCC 成都社区图书馆 + {' '} + 中查找自己感兴趣的书籍,或者在我们的 书籍目录{' '} + 中浏览。 + + ), + }, + { + title: '申请借阅', + description: ( + <> + 找到心仪的书籍后,填写{' '} + + fCC 成都社区图书馆-书籍借入 + {' '} + 申请,与当前持书者取得联系。 + + ), + }, + { + title: '线下传递', + description: ( + <> + 双方约定时间和传递方式,通常可用快递传递书籍。请传递者填写{' '} + + fCC 成都社区图书馆-书籍传递 + + ,再将书籍传递出去。 + + ), + }, + { + title: '阅读与分享', + description: + '借阅者在阅读完毕后,可以分享自己的阅读感悟,并在社区推荐给下一位感兴趣的成员。我们鼓励借阅者在归还前写下简短的书评。', + }, + { + title: '继续传递', + description: '当有新的借阅者申请时,当前持书人将书籍传递给下一位读者,确保知识的持续流动。', + }, +]; + +const borrowRules = [ + ['借阅期限', '每本书的标准借阅期为 30 天,如需延长可与图书馆管理员联系。'], + ['借阅数量', '每位会员同时最多可借阅 3 本书。'], + ['书籍状态', '借阅者有责任保持书籍的良好状态,避免损坏、标记或丢失。'], + ['传递责任', '当前持书人负责将书籍安全传递给下一位借阅者,并承担相关的传递费用。'], + [ + '丢失或损坏', + '如果书籍在您借阅期间丢失或严重损坏,请联系图书馆管理员并考虑捐赠一本相同或类似的书籍作为替代。', + ], +]; + +const questions = [ + [ + '如何知道一本书是否可借?', + '您可以在飞书多维表格或我们的网站书籍目录中查看书籍的当前状态。如果标记为“可借阅”,则表示该书可以申请借阅。', + ], + [ + '我需要支付借阅费用吗?', + 'Open Library 不收取借阅费用,但借阅者需要承担书籍传递的相关费用(如快递费)。', + ], + [ + '如果当前没有人申请借我手中的书,我需要归还吗?', + '标准借阅期为 30 天。如果期满后没有新的借阅申请,您可以继续保留该书,但请随时准备传递给下一位申请者。', + ], + [ + '如何联系当前持书人?', + '当您提交借阅申请后,我们会为您提供当前持书人的联系方式,以便您们协商传递事宜。', + ], + [ + '如果我想长期保留一本书怎么办?', + 'Open Library 的宗旨是促进知识流动,我们鼓励书籍在会员之间传递。如果您特别喜欢某本书,建议购买一本自己的副本,或者考虑捐赠一本相同的书籍给图书馆。', + ], + [ + '我可以借阅电子书吗?', + '目前 Open Library 主要提供实体书的借阅服务。我们正在考虑未来增加电子书资源,敬请期待。', + ], +]; + +const HowToBorrowPage = observer(() => { + const { t } = useContext(I18nContext); -export default function HowToBorrowPage() { return ( - - + + +
-

如何借阅

+

{t('how_to_borrow_page')}

- - -

借阅与传递模式

-

- 在 Open Library,所有书籍均来自社区成员的捐赠,并由借阅者直接 - 传递给下一位借书人。 -

-

- 我们采用的是一种独特的"无储存"借阅模式,让书籍在会员之间自由流转,而非集中存放。这种模式不仅节省了物理空间,更重要的是促进了社区成员之间的直接交流和互动。 -

-
+ +

{t('borrowing_and_passing')}

+

+ 在 Open Library,所有书籍均来自社区成员的捐赠,并由借阅者直接 + 传递给下一位借书人。 +

+

+ 我们采用的是一种独特的“无储存”借阅模式,让书籍在会员之间自由流转,而非集中存放。这种模式不仅节省了物理空间,更重要的是促进了社区成员之间的直接交流和互动。 +

- - -

借阅流程

-
-
- -
-
-

1

-
-
-
查阅书籍
-

- 社区成员可以在{' '} - - fCC 成都社区图书馆 - {' '} - 中查找自己感兴趣的书籍,或者在我们的{' '} - - 书籍目录 - {' '} - 中浏览。 -

-
-
- -
-
-

2

-
-
-
申请借阅
-

- 找到心仪的书籍后,填写{' '} - - fCC 成都社区图书馆-书籍借入 - {' '} - 申请,与当前持书者取得联系。 -

-
-
- -
-
-

3

-
-
-
线下传递
-

- 双方约定时间和传递方式,通常可用快递传递书籍。请传递者填写{' '} - - fCC 成都社区图书馆-书籍传递 - - ,再将书籍传递出去。 -

-
-
- -
-
-

4

-
-
-
阅读与分享
-

- 借阅者在阅读完毕后,可以分享自己的阅读感悟,并在社区推荐给下一位感兴趣的成员。我们鼓励借阅者在归还前写下简短的书评。 -

-
-
+ +

{t('borrowing_process')}

-
-
+ {borrowSteps.map(({ title, description }, index) => ( +
  • + -

    5

    -
  • + {index + 1} +
    -
    继续传递
    -

    - 当有新的借阅者申请时,当前持书人将书籍传递给下一位读者,确保知识的持续流动。 -

    +

    {title}

    +

    {description}

    -
    -
    - + + ))} + + - - -

    借阅规则

    -
      -
    • - 借阅期限: 每本书的标准借阅期为 30 - 天,如需延长可与图书馆管理员联系。 -
    • -
    • - 借阅数量: 每位会员同时最多可借阅 3 - 本书。 -
    • -
    • - 书籍状态:{' '} - 借阅者有责任保持书籍的良好状态,避免损坏、标记或丢失。 -
    • -
    • - 传递责任:{' '} - 当前持书人负责将书籍安全传递给下一位借阅者,并承担相关的传递费用。 -
    • -
    • - 丢失或损坏:{' '} - 如果书籍在您借阅期间丢失或严重损坏,请联系图书馆管理员并考虑捐赠一本相同或类似的书籍作为替代。 -
    • -
    -
    + +

    {t('borrowing_rules')}

    + + {borrowRules.map(([term, detail]) => ( + + {term}: + {detail} + + ))} +
    - - -

    快速链接

    - -
    + +

    {t('quick_links')}

    +
    - - -

    常见问题解答

    - - -
    如何知道一本书是否可借?
    -

    - 您可以在飞书多维表格或我们的网站书籍目录中查看书籍的当前状态。如果标记为"可借阅",则表示该书可以申请借阅。 -

    - - -
    我需要支付借阅费用吗?
    -

    - Open Library - 不收取借阅费用,但借阅者需要承担书籍传递的相关费用(如快递费)。 -

    + +

    {t('faq')}

    + + {questions.map(([question, answer]) => ( + +
    {question}
    +
    {answer}
    - -
    如果当前没有人申请借我手中的书,我需要归还吗?
    -

    - 标准借阅期为 30 - 天。如果期满后没有新的借阅申请,您可以继续保留该书,但请随时准备传递给下一位申请者。 -

    - - -
    如何联系当前持书人?
    -

    - 当您提交借阅申请后,我们会为您提供当前持书人的联系方式,以便您们协商传递事宜。 -

    - - -
    如果我想长期保留一本书怎么办?
    -

    - Open Library - 的宗旨是促进知识流动,我们鼓励书籍在会员之间传递。如果您特别喜欢某本书,建议购买一本自己的副本,或者考虑捐赠一本相同的书籍给图书馆。 -

    - - -
    我可以借阅电子书吗?
    -

    - 目前 Open Library - 主要提供实体书的借阅服务。我们正在考虑未来增加电子书资源,敬请期待。 -

    - -
    -
    + ))} +
    -

    准备好借阅了吗?

    +

    {t('ready_to_borrow')}

    - - 浏览书籍目录 + + - - 申请成为会员 - + {t('apply_for_membership')} +
    - - + ); -} +}); + +export default HowToBorrowPage; diff --git a/pages/open-library/index.tsx b/pages/open-library/index.tsx index 7e19a39..81f63dd 100644 --- a/pages/open-library/index.tsx +++ b/pages/open-library/index.tsx @@ -1,158 +1,94 @@ -import Head from 'next/head'; import Link from 'next/link'; -import React, { useContext } from 'react'; -import { Card, Col, Row } from 'react-bootstrap'; +import { observer } from 'mobx-react'; +import { useContext } from 'react'; +import { Button, Card, Col, Row } from 'react-bootstrap'; +import { PageHead } from '../../components/Layout/PageHead'; import FeaturedBooks from '../../components/open-library/FeaturedBooks'; -import FooterComponent from '../../components/open-library/Footer'; import HeroSection from '../../components/open-library/HeroSection'; import HowItWorks from '../../components/open-library/HowItWorks'; import { ContentContainer } from '../../components/open-library/Layout'; -import LibraryNavbar from '../../components/open-library/Navbar'; -import { useOpenLibraryLayout } from '../../components/open-library/useOpenLibraryLayout'; import { I18nContext } from '../../models/Translation'; +import { openLibraryBooks } from '../api/open-library/books'; +import styles from '../../styles/open-library.module.scss'; -// Sample data - these could be moved to a separate data file in the future -const featuredBooks = [ - { - id: 1, - title: 'Clean Code', - author: 'Robert C. Martin', - cover: '/images/placeholder-book.svg', - description: '编写优雅代码的艺术', - status: 'available' as const, - rating: 4.8, - tags: ['编程', '软件工程'], - }, - { - id: 2, - title: 'Eloquent JavaScript', - author: 'Marijn Haverbeke', - cover: '/images/placeholder-book.svg', - description: 'JavaScript 程序设计精粹', - status: 'borrowed' as const, - rating: 4.6, - tags: ['JavaScript', '前端'], - }, - { - id: 3, - title: 'Design Patterns', - author: 'Erich Gamma et al.', - cover: '/images/placeholder-book.svg', - description: '可复用面向对象软件的基础', - status: 'available' as const, - rating: 4.7, - tags: ['设计模式', '软件架构'], - }, - { - id: 4, - title: "You Don't Know JS", - author: 'Kyle Simpson', - cover: '/images/placeholder-book.svg', - description: 'JavaScript 深度解析系列', - status: 'available' as const, - rating: 4.5, - tags: ['JavaScript', '进阶'], - }, -]; - -const workflowSteps = [ - { - id: 1, - icon: 'bi-search', - title: '浏览图书', - description: '在图书目录中寻找你感兴趣的书籍', - color: '#6f42c1', - }, - { - id: 2, - icon: 'bi-file-earmark-text', - title: '申请借阅', - description: '填写借阅申请表单,说明你的借阅意向', - color: '#0d6efd', - }, - { - id: 3, - icon: 'bi-people', - title: '联系持书人', - description: '我们会协助你联系当前的持书人安排交接', - color: '#fd7e14', - }, - { - id: 4, - icon: 'bi-book-half', - title: '传递分享', - description: '阅读完成后,将书籍传递给下一位读者', - color: '#198754', - }, -]; - -export default function OpenLibraryHomepage() { - // Apply Open Library layout styles - useOpenLibraryLayout(); - - // Use the new i18n context +const OpenLibraryHomepage = observer(() => { const { t } = useContext(I18nContext); + const featuredBooks = openLibraryBooks.slice(0, 4); + const workflowSteps = [ + { + id: 1, + icon: '🔍', + title: t('step_1_find_book'), + description: t('step_1_description'), + color: '#6f42c1', + }, + { + id: 2, + icon: '📄', + title: t('step_2_apply'), + description: t('step_2_description'), + color: '#0d6efd', + }, + { + id: 3, + icon: '↔️', + title: t('step_3_receive'), + description: t('step_3_description'), + color: '#fd7e14', + }, + { + id: 4, + icon: '📚', + title: t('share_your_knowledge'), + description: t('learn_more_about_borrowing'), + color: '#198754', + }, + ]; return ( - <> - - {`${t('open_library')} - freeCodeCamp 成都社区`} - - +
    + - + - - -
    - {/* About Section */}
    -

    - 📚 关于我们 -

    -

    - freeCodeCamp 成都社区「Open Library」开放共享图书馆 -

    +

    📚 {t('about_us_footer')}

    +

    {t('open_library_community_intro')}

    -

    🌟 我们的使命

    -

    - 促进知识交流,推动社区成员之间的学习和成长,增强社区成员之间的互动与信任。 - 我们采用独特的"无储存"借阅模式,让书籍在会员之间自由流转。 -

    +

    🌟 {t('our_mission')}

    +

    {t('our_mission_text')}

    -

    💡 核心理念

    -
      +

      💡 {t('core_concepts')}

      +
      • - ✦ 知识流动 - 书籍自由流转,促进知识传播 + {t('knowledge_flow')} - {t('knowledge_flow_desc')}
      • - ✦ 社区驱动 - 所有书籍来自成员捐赠 + {t('community_driven')} - {t('community_driven_desc')}
      • - ✦ 开放共享 - 促进交流和互动 + {t('open_sharing')} - {t('open_sharing_desc')}
      @@ -162,73 +98,50 @@ export default function OpenLibraryHomepage() {
    - {/* Featured Books Section */} -
    - -
    -

    - 📖 精选图书 -

    -

    发现社区成员推荐的优质图书

    -
    - -
    -
    + - - - + - {/* Simple Donation Section */}
    -

    💖 支持我们

    -

    - Open Library - 完全由社区成员的热情和贡献支撑。如果这个项目对你有帮助, -
    - 欢迎通过以下方式支持我们的发展。 -

    +

    💖 {t('support_us')}

    +

    {t('support_us_description')}

    - - - GitHub Sponsors - - - - - 分享你的图书 - + ❤️ GitHub Sponsors + + +
    -
    - - +
    ); -} +}); + +export default OpenLibraryHomepage; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1c56da0..a770de0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,13 +1,9 @@ lockfileVersion: '9.0' settings: - autoInstallPeers: false + autoInstallPeers: true excludeLinksFromLockfile: false -overrides: - mobx-react-helper: ^0.5.1 - next: ^16.2.4 - importers: .: @@ -20,25 +16,25 @@ importers: version: 15.4.0(koa@3.2.0) '@mdx-js/loader': specifier: ^3.1.1 - version: 3.1.1 + version: 3.1.1(webpack@5.106.2) '@mdx-js/react': specifier: ^3.1.1 version: 3.1.1(@types/react@19.2.14)(react@19.2.5) '@next/mdx': specifier: ^16.2.4 - version: 16.2.4(@mdx-js/loader@3.1.1)(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.5)) + version: 16.2.4(@mdx-js/loader@3.1.1(webpack@5.106.2))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.5)) core-js: specifier: ^3.49.0 version: 3.49.0 echarts-jsx: specifier: ^0.6.0 - version: 0.6.0(react@19.2.5)(typescript@5.9.3) + version: 0.6.0(element-internals-polyfill@3.0.2)(react@19.2.5)(typescript@5.9.3) file-type: specifier: ^22.0.1 version: 22.0.1 idea-react: specifier: ^2.2.2 - version: 2.2.2(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react-is@16.13.1)(react@19.2.5)(typescript@5.9.3) + version: 2.2.2(@types/react@19.2.14)(element-internals-polyfill@3.0.2)(react-dom@19.2.5(react@19.2.5))(react-is@16.13.1)(react@19.2.5)(typescript@5.9.3) jsonwebtoken: specifier: ^9.0.3 version: 9.0.3 @@ -50,7 +46,7 @@ importers: version: 4.0.4 koajax: specifier: ^3.3.0 - version: 3.3.0(core-js@3.49.0)(typescript@5.9.3) + version: 3.3.0(core-js@3.49.0)(element-internals-polyfill@3.0.2)(jsdom@29.1.1)(typescript@5.9.3) license-filter: specifier: ^0.2.5 version: 0.2.5 @@ -65,43 +61,43 @@ importers: version: 6.15.0 mobx-github: specifier: ^0.6.2 - version: 0.6.2(core-js@3.49.0)(typescript@5.9.3) + version: 0.6.2(core-js@3.49.0)(element-internals-polyfill@3.0.2)(jsdom@29.1.1)(typescript@5.9.3) mobx-i18n: specifier: ^0.7.2 - version: 0.7.2(mobx@6.15.0)(typescript@5.9.3) + version: 0.7.2(element-internals-polyfill@3.0.2)(mobx@6.15.0)(typescript@5.9.3) mobx-lark: specifier: ^2.8.1 - version: 2.8.1(core-js@3.49.0)(react@19.2.5)(typescript@5.9.3) + version: 2.8.1(core-js@3.49.0)(element-internals-polyfill@3.0.2)(jsdom@29.1.1)(react@19.2.5)(typescript@5.9.3) mobx-react: specifier: ^9.2.1 version: 9.2.1(mobx@6.15.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) mobx-react-helper: specifier: ^0.5.1 - version: 0.5.1(mobx@6.15.0)(react@19.2.5)(typescript@5.9.3) + version: 0.5.1(element-internals-polyfill@3.0.2)(mobx@6.15.0)(react@19.2.5)(typescript@5.9.3) mobx-restful: specifier: ^2.1.4 - version: 2.1.4(core-js@3.49.0)(mobx@6.15.0)(typescript@5.9.3) + version: 2.1.4(core-js@3.49.0)(element-internals-polyfill@3.0.2)(jsdom@29.1.1)(mobx@6.15.0)(typescript@5.9.3) mobx-restful-table: specifier: ^2.6.3 - version: 2.6.3(@types/react@19.2.14)(core-js@3.49.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3) + version: 2.6.3(@types/react@19.2.14)(core-js@3.49.0)(element-internals-polyfill@3.0.2)(jsdom@29.1.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3) mobx-strapi: specifier: ^0.8.1 - version: 0.8.1(core-js@3.49.0)(typescript@5.9.3) + version: 0.8.1(core-js@3.49.0)(element-internals-polyfill@3.0.2)(jsdom@29.1.1)(typescript@5.9.3) next: specifier: ^16.2.4 version: 16.2.4(@babel/core@7.29.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0) next-pwa: specifier: ^5.6.0 - version: 5.6.0(@babel/core@7.29.0)(next@16.2.4(@babel/core@7.29.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0)) + version: 5.6.0(@babel/core@7.29.0)(next@16.2.4(@babel/core@7.29.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0))(webpack@5.106.2) next-ssr-middleware: specifier: ^1.1.0 - version: 1.1.0(next@16.2.4(@babel/core@7.29.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0))(react@19.2.5)(typescript@5.9.3) + version: 1.1.0(element-internals-polyfill@3.0.2)(next@16.2.4(@babel/core@7.29.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0))(react@19.2.5)(typescript@5.9.3) nodemailer: specifier: ^8.0.7 version: 8.0.7 open-react-map: specifier: ^0.9.1 - version: 0.9.1(core-js@3.49.0)(mobx-react@9.2.1(mobx@6.15.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(mobx@6.15.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3) + version: 0.9.1(core-js@3.49.0)(element-internals-polyfill@3.0.2)(jsdom@29.1.1)(mobx-react@9.2.1(mobx@6.15.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(mobx@6.15.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3) react: specifier: ^19.2.5 version: 19.2.5 @@ -122,7 +118,7 @@ importers: version: 5.2.0 web-utility: specifier: ^4.6.6 - version: 4.6.6(typescript@5.9.3) + version: 4.6.6(element-internals-polyfill@3.0.2)(typescript@5.9.3) yaml: specifier: ^2.8.4 version: 2.8.4 @@ -180,7 +176,7 @@ importers: version: 10.3.0(jiti@2.6.1) eslint-config-next: specifier: ^16.2.4 - version: 16.2.4(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3) + version: 16.2.4(@typescript-eslint/parser@8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3))(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3) eslint-config-prettier: specifier: ^10.1.8 version: 10.1.8(eslint@10.3.0(jiti@2.6.1)) @@ -204,13 +200,13 @@ importers: version: 4.6.4 less-loader: specifier: ^12.3.2 - version: 12.3.2(less@4.6.4) + version: 12.3.2(less@4.6.4)(webpack@5.106.2) lint-staged: specifier: ^16.4.0 version: 16.4.0 next-with-less: specifier: ^3.0.1 - version: 3.0.1(less-loader@12.3.2(less@4.6.4))(less@4.6.4)(next@16.2.4(@babel/core@7.29.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0)) + version: 3.0.1(less-loader@12.3.2(less@4.6.4)(webpack@5.106.2))(less@4.6.4)(next@16.2.4(@babel/core@7.29.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0)) prettier: specifier: ^3.8.3 version: 3.8.3 @@ -235,6 +231,21 @@ packages: peerDependencies: ajv: '>=8' + '@asamuzakjp/css-color@5.1.11': + resolution: {integrity: sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + '@asamuzakjp/dom-selector@7.1.1': + resolution: {integrity: sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + '@asamuzakjp/generational-cache@1.0.1': + resolution: {integrity: sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + '@asamuzakjp/nwsapi@2.3.9': + resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + '@babel/code-frame@7.29.0': resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} @@ -802,6 +813,10 @@ packages: '@borewit/text-codec@0.2.2': resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==} + '@bramus/specificity@2.4.2': + resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} + hasBin: true + '@codexteam/icons@0.0.4': resolution: {integrity: sha512-V8N/TY2TGyas4wLrPIFq7bcow68b3gu8DfDt1+rrHPtXxcexadKauRJL6eQgfG7Z0LCrN4boLRawR4S9gjIh/Q==} @@ -1037,6 +1052,42 @@ packages: resolution: {integrity: sha512-q+0pHQ8DbqjemyaOn/mTtBRbCuKDqhnsVbZ6J9zkTsxPgMpccjy0s5oLXwomfrrxMRBH+UcbERwtUmE+SbnoIQ==} engines: {node: '>=22.18.0'} + '@csstools/color-helpers@6.0.2': + resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==} + engines: {node: '>=20.19.0'} + + '@csstools/css-calc@3.2.1': + resolution: {integrity: sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-color-parser@4.1.1': + resolution: {integrity: sha512-eZ5XOtyhK+mggRafYUWzA0tvaYOFgdY8AkgQiCJF9qNAePnUo/zmsqqYubBBb3sQ8uNUaSKTY9s9klfRaAXL0g==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-parser-algorithms@4.0.0': + resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-syntax-patches-for-csstree@1.1.4': + resolution: {integrity: sha512-wgsqt92b7C7tQhIdPNxj0n9zuUbQlvAuI1exyzeNrOKOi62SD7ren8zqszmpVREjAOqg8cD2FqYhQfAuKjk4sw==} + peerDependencies: + css-tree: ^3.2.1 + peerDependenciesMeta: + css-tree: + optional: true + + '@csstools/css-tokenizer@4.0.0': + resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} + engines: {node: '>=20.19.0'} + '@editorjs/caret@1.1.0': resolution: {integrity: sha512-dzUjrPV7mtqM0HR/7IwT1/1PfyMxUfe5MRXo9joFmBRDIcrQ5ikbqVxSL9+Hy5tQ+BUWBwqLUgX1UKTJylWW9Q==} @@ -1100,6 +1151,15 @@ packages: resolution: {integrity: sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} + '@exodus/bytes@1.15.0': + resolution: {integrity: sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + '@noble/hashes': ^1.8.0 || ^2.0.0 + peerDependenciesMeta: + '@noble/hashes': + optional: true + '@giscus/react@3.1.0': resolution: {integrity: sha512-0TCO2TvL43+oOdyVVGHDItwxD1UMKP2ZYpT6gXmhFOqfAJtZxTzJ9hkn34iAF/b6YzyJ4Um89QIt9z/ajmAEeg==} peerDependencies: @@ -1351,6 +1411,9 @@ packages: '@napi-rs/wasm-runtime@0.2.12': resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + '@next/env@13.5.11': + resolution: {integrity: sha512-fbb2C7HChgM7CemdCY+y3N1n8pcTKdqtQLbC7/EQtPdLvlMUT9JX/dBYl8MMZAtYG4uVMyPFHXckb68q/NRwqg==} + '@next/env@16.2.4': resolution: {integrity: sha512-dKkkOzOSwFYe5RX6y26fZgkSpVAlIOJKQHIiydQcrWH6y/97+RceSOAdjZ14Qa3zLduVUy0TXcn+EiM6t4rPgw==} @@ -1368,18 +1431,37 @@ packages: '@mdx-js/react': optional: true + '@next/swc-darwin-arm64@13.5.9': + resolution: {integrity: sha512-pVyd8/1y1l5atQRvOaLOvfbmRwefxLhqQOzYo/M7FQ5eaRwA1+wuCn7t39VwEgDd7Aw1+AIWwd+MURXUeXhwDw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + '@next/swc-darwin-arm64@16.2.4': resolution: {integrity: sha512-OXTFFox5EKN1Ym08vfrz+OXxmCcEjT4SFMbNRsWZE99dMqt2Kcusl5MqPXcW232RYkMLQTy0hqgAMEsfEd/l2A==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] + '@next/swc-darwin-x64@13.5.9': + resolution: {integrity: sha512-DwdeJqP7v8wmoyTWPbPVodTwCybBZa02xjSJ6YQFIFZFZ7dFgrieKW4Eo0GoIcOJq5+JxkQyejmI+8zwDp3pwA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + '@next/swc-darwin-x64@16.2.4': resolution: {integrity: sha512-XhpVnUfmYWvD3YrXu55XdcAkQtOnvaI6wtQa8fuF5fGoKoxIUZ0kWPtcOfqJEWngFF/lOS9l3+O9CcownhiQxQ==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] + '@next/swc-linux-arm64-gnu@13.5.9': + resolution: {integrity: sha512-wdQsKsIsGSNdFojvjW3Ozrh8Q00+GqL3wTaMjDkQxVtRbAqfFBtrLPO0IuWChVUP2UeuQcHpVeUvu0YgOP00+g==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@next/swc-linux-arm64-gnu@16.2.4': resolution: {integrity: sha512-Mx/tjlNA3G8kg14QvuGAJ4xBwPk1tUHq56JxZ8CXnZwz1Etz714soCEzGQQzVMz4bEnGPowzkV6Xrp6wAkEWOQ==} engines: {node: '>= 10'} @@ -1387,6 +1469,13 @@ packages: os: [linux] libc: [glibc] + '@next/swc-linux-arm64-musl@13.5.9': + resolution: {integrity: sha512-6VpS+bodQqzOeCwGxoimlRoosiWlSc0C224I7SQWJZoyJuT1ChNCo+45QQH+/GtbR/s7nhaUqmiHdzZC9TXnXA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + '@next/swc-linux-arm64-musl@16.2.4': resolution: {integrity: sha512-iVMMp14514u7Nup2umQS03nT/bN9HurK8ufylC3FZNykrwjtx7V1A7+4kvhbDSCeonTVqV3Txnv0Lu+m2oDXNg==} engines: {node: '>= 10'} @@ -1394,6 +1483,13 @@ packages: os: [linux] libc: [musl] + '@next/swc-linux-x64-gnu@13.5.9': + resolution: {integrity: sha512-XxG3yj61WDd28NA8gFASIR+2viQaYZEFQagEodhI/R49gXWnYhiflTeeEmCn7Vgnxa/OfK81h1gvhUZ66lozpw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + '@next/swc-linux-x64-gnu@16.2.4': resolution: {integrity: sha512-EZOvm1aQWgnI/N/xcWOlnS3RQBk0VtVav5Zo7n4p0A7UKyTDx047k8opDbXgBpHl4CulRqRfbw3QrX2w5UOXMQ==} engines: {node: '>= 10'} @@ -1401,6 +1497,13 @@ packages: os: [linux] libc: [glibc] + '@next/swc-linux-x64-musl@13.5.9': + resolution: {integrity: sha512-/dnscWqfO3+U8asd+Fc6dwL2l9AZDl7eKtPNKW8mKLh4Y4wOpjJiamhe8Dx+D+Oq0GYVjuW0WwjIxYWVozt2bA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + '@next/swc-linux-x64-musl@16.2.4': resolution: {integrity: sha512-h9FxsngCm9cTBf71AR4fGznDEDx1hS7+kSEiIRjq5kO1oXWm07DxVGZjCvk0SGx7TSjlUqhI8oOyz7NfwAdPoA==} engines: {node: '>= 10'} @@ -1408,12 +1511,30 @@ packages: os: [linux] libc: [musl] + '@next/swc-win32-arm64-msvc@13.5.9': + resolution: {integrity: sha512-T/iPnyurOK5a4HRUcxAlss8uzoEf5h9tkd+W2dSWAfzxv8WLKlUgbfk+DH43JY3Gc2xK5URLuXrxDZ2mGfk/jw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + '@next/swc-win32-arm64-msvc@16.2.4': resolution: {integrity: sha512-3NdJV5OXMSOeJYijX+bjaLge3mJBlh4ybydbT4GFoB/2hAojWHtMhl3CYlYoMrjPuodp0nzFVi4Tj2+WaMg+Ow==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] + '@next/swc-win32-ia32-msvc@13.5.9': + resolution: {integrity: sha512-BLiPKJomaPrTAb7ykjA0LPcuuNMLDVK177Z1xe0nAem33+9FIayU4k/OWrtSn9SAJW/U60+1hoey5z+KCHdRLQ==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@next/swc-win32-x64-msvc@13.5.9': + resolution: {integrity: sha512-/72/dZfjXXNY/u+n8gqZDjI6rxKMpYsgBBYNZKWOQw0BpBF7WCnPflRy3ZtvQ2+IYI3ZH2bPyj7K+6a6wNk90Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + '@next/swc-win32-x64-msvc@16.2.4': resolution: {integrity: sha512-kMVGgsqhO5YTYODD9IPGGhA6iprWidQckK3LmPeW08PIFENRmgfb4MjXHO+p//d+ts2rpjvK5gXWzXSMrPl9cw==} engines: {node: '>= 10'} @@ -1635,6 +1756,9 @@ packages: '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + '@swc/helpers@0.5.2': + resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} + '@swc/helpers@0.5.21': resolution: {integrity: sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg==} @@ -1672,6 +1796,12 @@ packages: '@types/eslint-config-prettier@6.11.3': resolution: {integrity: sha512-3wXCiM8croUnhg9LdtZUJQwNcQYGWxxdOWDjPe1ykCqJFPVpzAKfs/2dgSoCtAvdPeaponcWPI7mPcGGp9dkKQ==} + '@types/eslint-scope@3.7.7': + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + + '@types/eslint@9.6.1': + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + '@types/esrecurse@4.3.1': resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} @@ -1864,6 +1994,7 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + deprecated: Potential CWE-502 - Update to 1.3.1 or higher '@unrs/resolver-binding-android-arm-eabi@1.11.1': resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} @@ -1968,10 +2099,67 @@ packages: cpu: [x64] os: [win32] + '@webassemblyjs/ast@1.14.1': + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} + + '@webassemblyjs/floating-point-hex-parser@1.13.2': + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} + + '@webassemblyjs/helper-api-error@1.13.2': + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} + + '@webassemblyjs/helper-buffer@1.14.1': + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} + + '@webassemblyjs/helper-numbers@1.13.2': + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} + + '@webassemblyjs/helper-wasm-section@1.14.1': + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} + + '@webassemblyjs/ieee754@1.13.2': + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} + + '@webassemblyjs/leb128@1.13.2': + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} + + '@webassemblyjs/utf8@1.13.2': + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} + + '@webassemblyjs/wasm-edit@1.14.1': + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} + + '@webassemblyjs/wasm-gen@1.14.1': + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} + + '@webassemblyjs/wasm-opt@1.14.1': + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} + + '@webassemblyjs/wasm-parser@1.14.1': + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} + + '@webassemblyjs/wast-printer@1.14.1': + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + + '@xtuc/ieee754@1.2.0': + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + + '@xtuc/long@4.2.2': + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} + acorn-import-phases@1.0.4: + resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} + engines: {node: '>=10.13.0'} + peerDependencies: + acorn: ^8.14.0 + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1988,6 +2176,11 @@ packages: ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true ajv-keywords@3.5.2: resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} @@ -2146,6 +2339,9 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + bidi-js@1.0.3: + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + big.js@5.2.2: resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} @@ -2181,6 +2377,10 @@ packages: resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} engines: {node: '>=6'} + busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} @@ -2219,6 +2419,10 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} + chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} + engines: {node: '>=6.0'} + classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} @@ -2368,12 +2572,20 @@ packages: css-line-break@2.1.0: resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==} + css-tree@3.2.1: + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + data-urls@7.0.0: + resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} @@ -2403,6 +2615,9 @@ packages: supports-color: optional: true + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + decode-named-character-reference@1.3.0: resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} @@ -2497,6 +2712,9 @@ packages: electron-to-chromium@1.5.349: resolution: {integrity: sha512-QsWVGyRuY07Aqb234QytTfwd5d9AJlfNIQ5wIOl1L+PZDzI9d9+Fn0FRale/QYlFxt/bUnB0/nLd1jFPGxGK1A==} + element-internals-polyfill@3.0.2: + resolution: {integrity: sha512-uB0/Qube3lkwh8SmkTnGIyUgJ9YdqVSzIoHMRCEQjAbD4Y5UzsVbch1tIxjTgUe5k3gy1U0ZMKMJ90A81lqwig==} + emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -2511,6 +2729,14 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} + enhanced-resolve@5.21.3: + resolution: {integrity: sha512-QyL119InA+XXEkNLNTPCXPugSvOfhwv0JOlGNzvxs0hZaiHLNvXSpudUWsOlsXGWJh8G6ckCScEkVHfX3kw/2Q==} + engines: {node: '>=10.13.0'} + + entities@8.0.0: + resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} + engines: {node: '>=20.19.0'} + env-paths@4.0.0: resolution: {integrity: sha512-pxP8eL2SwwaTRi/KHYwLYXinDs7gL3jxFcBYmEdYfZmZXbaVDvdppd0XBU8qVz03rDfKZMXg1omHCbsJjZrMsw==} engines: {node: '>=20'} @@ -2539,6 +2765,9 @@ packages: resolution: {integrity: sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw==} engines: {node: '>= 0.4'} + es-module-lexer@2.1.0: + resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -2661,6 +2890,10 @@ packages: peerDependencies: eslint: '>=5.0.0' + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + eslint-scope@9.1.2: resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} @@ -2708,6 +2941,10 @@ packages: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + estraverse@5.3.0: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} @@ -2746,6 +2983,10 @@ packages: eventemitter3@5.0.4: resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -2901,6 +3142,9 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me @@ -2978,6 +3222,10 @@ packages: hermes-parser@0.25.1: resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + html-encoding-sniffer@6.0.0: + resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + html2canvas@1.4.1: resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==} engines: {node: '>=8.0.0'} @@ -3192,6 +3440,9 @@ packages: resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} engines: {node: '>=0.10.0'} + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -3281,6 +3532,15 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + jsdom@29.1.1: + resolution: {integrity: sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -3420,6 +3680,10 @@ packages: lit@3.3.2: resolution: {integrity: sha512-NF9zbsP79l4ao2SNrH3NkfmFgN/hBYSQo90saIVI1o5GpjAdCPVstVzO1MrLOakHoEhYkrtRjPK6Ob521aoYWQ==} + loader-runner@4.3.2: + resolution: {integrity: sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==} + engines: {node: '>=6.11.5'} + loader-utils@2.0.4: resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==} engines: {node: '>=8.9.0'} @@ -3482,6 +3746,10 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + lru-cache@11.3.6: + resolution: {integrity: sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -3544,6 +3812,9 @@ packages: mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + mdn-data@2.27.1: + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} @@ -3707,6 +3978,12 @@ packages: peerDependencies: react: '>=16' + mobx-react-helper@0.4.1: + resolution: {integrity: sha512-+chcWzOznL5/c6n33iswIGKvFJI/afmWRMFZ5NjjJyD3DJuoGuaiayEEhL3FITVKpwOkPKF2K5Werz8vhk6xEA==} + peerDependencies: + mobx: '>=6.11' + react: '>=16' + mobx-react-helper@0.5.1: resolution: {integrity: sha512-8jwR6LbPmC5s0tcmPz6CjXs1uarAcKjeTD+Oqbd7Vk4Ce49yDxeUOxG07VAcWZVnjnJXE0n79oG3z9c2XEEWTw==} peerDependencies: @@ -3783,15 +4060,18 @@ packages: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + next-pwa@5.6.0: resolution: {integrity: sha512-XV8g8C6B7UmViXU8askMEYhWwQ4qc/XqJGnexbLV68hzKaGHZDMtHsm2TNxFcbR7+ypVuth/wwpiIlMwpRJJ5A==} peerDependencies: - next: ^16.2.4 + next: '>=9.0.0' next-ssr-middleware@1.1.0: resolution: {integrity: sha512-eYKTZExd+4yq4Cs2lrQ+XJlgegKAgmCvigy9Ro3ScaHjUNevyXovHO/bbdTYIvr4DtYDbZPJkw4VbYAaVZ5x7w==} peerDependencies: - next: ^16.2.4 + next: '>=9.3' react: '>=18' next-with-less@3.0.1: @@ -3799,7 +4079,22 @@ packages: peerDependencies: less: '*' less-loader: '>= 7.0.0' - next: ^16.2.4 + next: '>= 11.0.1' + + next@13.5.11: + resolution: {integrity: sha512-WUPJ6WbAX9tdC86kGTu92qkrRdgRqVrY++nwM+shmWQwmyxt4zhZfR59moXSI4N8GDYCBY3lIAqhzjDd4rTC8Q==} + engines: {node: '>=16.14.0'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + react: ^18.2.0 + react-dom: ^18.2.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + sass: + optional: true next@16.2.4: resolution: {integrity: sha512-kPvz56wF5frc+FxlHI5qnklCzbq53HTwORaWBGdT0vNoKh1Aya9XC8aPauH4NJxqtzbWsS5mAbctm4cr+EkQ2Q==} @@ -3938,6 +4233,9 @@ packages: resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} engines: {node: '>= 0.10'} + parse5@8.0.1: + resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==} + parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} @@ -4303,6 +4601,10 @@ packages: resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} engines: {node: '>=11.0.0'} + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} @@ -4442,6 +4744,10 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} + streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + string-argv@0.3.2: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} engines: {node: '>=0.6.19'} @@ -4506,6 +4812,19 @@ packages: style-to-object@1.0.14: resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + styled-jsx@5.1.1: + resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + styled-jsx@5.1.6: resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} engines: {node: '>= 12.0.0'} @@ -4536,10 +4855,17 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + synckit@0.11.12: resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==} engines: {node: ^14.18.0 || >=16.0.0} + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + temp-dir@2.0.0: resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} engines: {node: '>=8'} @@ -4587,6 +4913,13 @@ packages: resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} engines: {node: '>=12.0.0'} + tldts-core@7.0.30: + resolution: {integrity: sha512-uiHN8PIB1VmWyS98eZYja4xzlYqeFZVjb4OuYlJQnZAuJhMw4PbKQOKgHKhBdJR3FE/t5mUQ1Kd80++B+qhD1Q==} + + tldts@7.0.30: + resolution: {integrity: sha512-ELrFxuqsDdHUwoh0XxDbxuLD3Wnz49Z57IFvTtvWy1hJdcMZjXLIuonjilCiWHlT2GbE4Wlv1wKVTzDFnXH1aw==} + hasBin: true + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -4602,9 +4935,17 @@ packages: toml@3.0.0: resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} + tough-cookie@6.0.1: + resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==} + engines: {node: '>=16'} + tr46@1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + tr46@6.0.0: + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} + engines: {node: '>=20'} + trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -4715,6 +5056,10 @@ packages: undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + undici@7.25.0: + resolution: {integrity: sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==} + engines: {node: '>=20.18.1'} + unicode-canonical-property-names-ecmascript@2.0.1: resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} engines: {node: '>=4'} @@ -4812,9 +5157,21 @@ packages: vscode-uri@3.1.0: resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + warning@4.0.3: resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} + watchpack@2.4.0: + resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} + engines: {node: '>=10.13.0'} + + watchpack@2.5.1: + resolution: {integrity: sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==} + engines: {node: '>=10.13.0'} + web-streams-polyfill@4.2.0: resolution: {integrity: sha512-0rYDzGOh9EZpig92umN5g5D/9A1Kff7k0/mzPSSCY8jEQeYkgRMoY7LhbXtUCWzLCMX0TUE9aoHkjFNB7D9pfA==} engines: {node: '>= 8'} @@ -4828,9 +5185,35 @@ packages: webidl-conversions@4.0.2: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + webidl-conversions@8.0.1: + resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} + engines: {node: '>=20'} + webpack-sources@1.4.3: resolution: {integrity: sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==} + webpack-sources@3.4.1: + resolution: {integrity: sha512-eACpxRN02yaawnt+uUNIF7Qje6A9zArxBbcAJjK1PK3S9Ycg5jIuJ8pW4q8EMnwNZCEGltcjkRx1QzOxOkKD8A==} + engines: {node: '>=10.13.0'} + + webpack@5.106.2: + resolution: {integrity: sha512-wGN3qcrBQIFmQ/c0AiOAQBvrZ5lmY8vbbMv4Mxfgzqd/B6+9pXtLo73WuS1dSGXM5QYY3hZnIbvx+K1xxe6FyA==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + + whatwg-mimetype@5.0.0: + resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} + engines: {node: '>=20'} + + whatwg-url@16.0.1: + resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + whatwg-url@7.1.0: resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} @@ -4927,6 +5310,13 @@ packages: resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} engines: {node: '>=12'} + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -4962,6 +5352,26 @@ snapshots: jsonpointer: 5.0.1 leven: 3.1.0 + '@asamuzakjp/css-color@5.1.11': + dependencies: + '@asamuzakjp/generational-cache': 1.0.1 + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@asamuzakjp/dom-selector@7.1.1': + dependencies: + '@asamuzakjp/generational-cache': 1.0.1 + '@asamuzakjp/nwsapi': 2.3.9 + bidi-js: 1.0.3 + css-tree: 3.2.1 + is-potential-custom-element-name: 1.0.1 + + '@asamuzakjp/generational-cache@1.0.1': {} + + '@asamuzakjp/nwsapi@2.3.9': {} + '@babel/code-frame@7.29.0': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -5705,6 +6115,10 @@ snapshots: '@borewit/text-codec@0.2.2': {} + '@bramus/specificity@2.4.2': + dependencies: + css-tree: 3.2.1 + '@codexteam/icons@0.0.4': {} '@cspell/cspell-bundled-dicts@10.0.0': @@ -5929,6 +6343,30 @@ snapshots: '@cspell/url@10.0.0': {} + '@csstools/color-helpers@6.0.2': {} + + '@csstools/css-calc@3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-color-parser@4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/color-helpers': 6.0.2 + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-syntax-patches-for-csstree@1.1.4(css-tree@3.2.1)': + optionalDependencies: + css-tree: 3.2.1 + + '@csstools/css-tokenizer@4.0.0': {} + '@editorjs/caret@1.1.0': dependencies: '@editorjs/dom': 1.1.0 @@ -6001,6 +6439,8 @@ snapshots: '@eslint/core': 1.2.1 levn: 0.4.1 + '@exodus/bytes@1.15.0': {} + '@giscus/react@3.1.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: giscus: 1.6.0 @@ -6182,10 +6622,12 @@ snapshots: dependencies: '@lit-labs/ssr-dom-shim': 1.5.1 - '@mdx-js/loader@3.1.1': + '@mdx-js/loader@3.1.1(webpack@5.106.2)': dependencies: '@mdx-js/mdx': 3.1.1 source-map: 0.7.6 + optionalDependencies: + webpack: 5.106.2 transitivePeerDependencies: - supports-color @@ -6234,40 +6676,69 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true + '@next/env@13.5.11': {} + '@next/env@16.2.4': {} '@next/eslint-plugin-next@16.2.4': dependencies: fast-glob: 3.3.1 - '@next/mdx@16.2.4(@mdx-js/loader@3.1.1)(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.5))': + '@next/mdx@16.2.4(@mdx-js/loader@3.1.1(webpack@5.106.2))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.5))': dependencies: source-map: 0.7.6 optionalDependencies: - '@mdx-js/loader': 3.1.1 + '@mdx-js/loader': 3.1.1(webpack@5.106.2) '@mdx-js/react': 3.1.1(@types/react@19.2.14)(react@19.2.5) + '@next/swc-darwin-arm64@13.5.9': + optional: true + '@next/swc-darwin-arm64@16.2.4': optional: true + '@next/swc-darwin-x64@13.5.9': + optional: true + '@next/swc-darwin-x64@16.2.4': optional: true + '@next/swc-linux-arm64-gnu@13.5.9': + optional: true + '@next/swc-linux-arm64-gnu@16.2.4': optional: true + '@next/swc-linux-arm64-musl@13.5.9': + optional: true + '@next/swc-linux-arm64-musl@16.2.4': optional: true + '@next/swc-linux-x64-gnu@13.5.9': + optional: true + '@next/swc-linux-x64-gnu@16.2.4': optional: true + '@next/swc-linux-x64-musl@13.5.9': + optional: true + '@next/swc-linux-x64-musl@16.2.4': optional: true + '@next/swc-win32-arm64-msvc@13.5.9': + optional: true + '@next/swc-win32-arm64-msvc@16.2.4': optional: true + '@next/swc-win32-ia32-msvc@13.5.9': + optional: true + + '@next/swc-win32-x64-msvc@13.5.9': + optional: true + '@next/swc-win32-x64-msvc@16.2.4': optional: true @@ -6469,6 +6940,10 @@ snapshots: dependencies: tslib: 2.8.1 + '@swc/helpers@0.5.2': + dependencies: + tslib: 2.8.1 + '@swc/helpers@0.5.21': dependencies: tslib: 2.8.1 @@ -6520,6 +6995,16 @@ snapshots: '@types/eslint-config-prettier@6.11.3': {} + '@types/eslint-scope@3.7.7': + dependencies: + '@types/eslint': 9.6.1 + '@types/estree': 1.0.8 + + '@types/eslint@9.6.1': + dependencies: + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@types/esrecurse@4.3.1': {} '@types/estree-jsx@1.0.5': @@ -6607,15 +7092,13 @@ snapshots: '@types/node': 24.12.2 '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - next: 16.2.4(@babel/core@7.29.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0) + next: 13.5.11(@babel/core@7.29.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0) workbox-build: 6.6.0 transitivePeerDependencies: - '@babel/core' - '@opentelemetry/api' - - '@playwright/test' - '@types/babel__core' - babel-plugin-macros - - babel-plugin-react-compiler - react - react-dom - sass @@ -6828,11 +7311,95 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true + '@webassemblyjs/ast@1.14.1': + dependencies: + '@webassemblyjs/helper-numbers': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + + '@webassemblyjs/floating-point-hex-parser@1.13.2': {} + + '@webassemblyjs/helper-api-error@1.13.2': {} + + '@webassemblyjs/helper-buffer@1.14.1': {} + + '@webassemblyjs/helper-numbers@1.13.2': + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.13.2 + '@webassemblyjs/helper-api-error': 1.13.2 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} + + '@webassemblyjs/helper-wasm-section@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/wasm-gen': 1.14.1 + + '@webassemblyjs/ieee754@1.13.2': + dependencies: + '@xtuc/ieee754': 1.2.0 + + '@webassemblyjs/leb128@1.13.2': + dependencies: + '@xtuc/long': 4.2.2 + + '@webassemblyjs/utf8@1.13.2': {} + + '@webassemblyjs/wasm-edit@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/helper-wasm-section': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-opt': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + '@webassemblyjs/wast-printer': 1.14.1 + + '@webassemblyjs/wasm-gen@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wasm-opt@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + + '@webassemblyjs/wasm-parser@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-api-error': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wast-printer@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@xtuc/long': 4.2.2 + + '@xtuc/ieee754@1.2.0': {} + + '@xtuc/long@4.2.2': {} + accepts@1.3.8: dependencies: mime-types: 2.1.35 negotiator: 0.6.3 + acorn-import-phases@1.0.4(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + acorn-jsx@5.3.2(acorn@8.16.0): dependencies: acorn: 8.16.0 @@ -6844,8 +7411,8 @@ snapshots: clean-stack: 2.2.0 indent-string: 4.0.0 - ajv-formats@2.1.1: - dependencies: + ajv-formats@2.1.1(ajv@8.20.0): + optionalDependencies: ajv: 8.20.0 ajv-keywords@3.5.2(ajv@6.15.0): @@ -6982,13 +7549,14 @@ snapshots: axobject-query@4.1.0: {} - babel-loader@8.4.1(@babel/core@7.29.0): + babel-loader@8.4.1(@babel/core@7.29.0)(webpack@5.106.2): dependencies: '@babel/core': 7.29.0 find-cache-dir: 3.3.2 loader-utils: 2.0.4 make-dir: 3.1.0 schema-utils: 2.7.1 + webpack: 5.106.2 babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.0): dependencies: @@ -7024,6 +7592,10 @@ snapshots: baseline-browser-mapping@2.10.25: {} + bidi-js@1.0.3: + dependencies: + require-from-string: 2.0.2 + big.js@5.2.2: {} brace-expansion@1.1.14: @@ -7059,6 +7631,10 @@ snapshots: builtin-modules@3.3.0: {} + busboy@1.6.0: + dependencies: + streamsearch: 1.1.0 + bytes@3.1.2: {} call-bind-apply-helpers@1.0.2: @@ -7094,13 +7670,16 @@ snapshots: dependencies: readdirp: 4.1.2 + chrome-trace-event@1.0.4: {} + classnames@2.5.1: {} clean-stack@2.2.0: {} - clean-webpack-plugin@4.0.0: + clean-webpack-plugin@4.0.0(webpack@5.106.2): dependencies: del: 4.1.1 + webpack: 5.106.2 cli-cursor@5.0.0: dependencies: @@ -7253,10 +7832,22 @@ snapshots: dependencies: utrie: 1.0.2 + css-tree@3.2.1: + dependencies: + mdn-data: 2.27.1 + source-map-js: 1.2.1 + csstype@3.2.3: {} damerau-levenshtein@1.0.8: {} + data-urls@7.0.0: + dependencies: + whatwg-mimetype: 5.0.0 + whatwg-url: 16.0.1 + transitivePeerDependencies: + - '@noble/hashes' + data-view-buffer@1.0.2: dependencies: call-bound: 1.0.4 @@ -7283,6 +7874,8 @@ snapshots: dependencies: ms: 2.1.3 + decimal.js@10.6.0: {} + decode-named-character-reference@1.3.0: dependencies: character-entities: 2.0.2 @@ -7355,12 +7948,12 @@ snapshots: dependencies: safe-buffer: 5.2.1 - echarts-jsx@0.6.0(react@19.2.5)(typescript@5.9.3): + echarts-jsx@0.6.0(element-internals-polyfill@3.0.2)(react@19.2.5)(typescript@5.9.3): dependencies: echarts: 6.0.0 lodash: 4.18.1 react: 19.2.5 - web-utility: 4.6.6(typescript@5.9.3) + web-utility: 4.6.6(element-internals-polyfill@3.0.2)(typescript@5.9.3) transitivePeerDependencies: - element-internals-polyfill - typescript @@ -7372,7 +7965,7 @@ snapshots: editorjs-html@4.0.5: {} - edkit@1.2.7(typescript@5.9.3): + edkit@1.2.7(element-internals-polyfill@3.0.2)(typescript@5.9.3): dependencies: '@swc/helpers': 0.5.21 '@types/turndown': 5.0.6 @@ -7381,7 +7974,7 @@ snapshots: regenerator-runtime: 0.14.1 turndown: 7.2.4 turndown-plugin-gfm: 1.0.2 - web-utility: 4.6.6(typescript@5.9.3) + web-utility: 4.6.6(element-internals-polyfill@3.0.2)(typescript@5.9.3) transitivePeerDependencies: - element-internals-polyfill - typescript @@ -7394,6 +7987,8 @@ snapshots: electron-to-chromium@1.5.349: {} + element-internals-polyfill@3.0.2: {} + emoji-regex@10.6.0: {} emoji-regex@9.2.2: {} @@ -7402,6 +7997,13 @@ snapshots: encodeurl@2.0.0: {} + enhanced-resolve@5.21.3: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + entities@8.0.0: {} + env-paths@4.0.0: dependencies: is-safe-filename: 0.1.1 @@ -7493,6 +8095,8 @@ snapshots: iterator.prototype: 1.1.5 math-intrinsics: 1.1.0 + es-module-lexer@2.1.0: {} + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -7536,13 +8140,13 @@ snapshots: escape-string-regexp@5.0.0: {} - eslint-config-next@16.2.4(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3): + eslint-config-next@16.2.4(@typescript-eslint/parser@8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3))(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3): dependencies: '@next/eslint-plugin-next': 16.2.4 eslint: 10.3.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.10 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@10.3.0(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@10.3.0(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@10.3.0(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@10.3.0(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@10.3.0(jiti@2.6.1)) eslint-plugin-react-hooks: 7.1.1(eslint@10.3.0(jiti@2.6.1)) @@ -7579,21 +8183,22 @@ snapshots: tinyglobby: 0.2.16 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@10.3.0(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@10.3.0(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@10.3.0(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@10.3.0(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: + '@typescript-eslint/parser': 8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3) eslint: 10.3.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.10 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@10.3.0(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@10.3.0(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@10.3.0(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -7604,7 +8209,7 @@ snapshots: doctrine: 2.1.0 eslint: 10.3.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.10 - eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@10.3.0(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@10.3.0(jiti@2.6.1)) hasown: 2.0.3 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -7615,6 +8220,8 @@ snapshots: semver: 6.3.1 string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -7676,6 +8283,11 @@ snapshots: dependencies: eslint: 10.3.0(jiti@2.6.1) + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + eslint-scope@9.1.2: dependencies: '@types/esrecurse': 4.3.1 @@ -7748,6 +8360,8 @@ snapshots: dependencies: estraverse: 5.3.0 + estraverse@4.3.0: {} + estraverse@5.3.0: {} estree-util-attach-comments@3.0.0: @@ -7793,6 +8407,8 @@ snapshots: eventemitter3@5.0.4: {} + events@3.3.0: {} + extend@3.0.2: {} fast-deep-equal@3.1.3: {} @@ -7960,6 +8576,8 @@ snapshots: dependencies: is-glob: 4.0.3 + glob-to-regexp@0.4.1: {} + glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -8076,6 +8694,12 @@ snapshots: dependencies: hermes-estree: 0.25.1 + html-encoding-sniffer@6.0.0: + dependencies: + '@exodus/bytes': 1.15.0 + transitivePeerDependencies: + - '@noble/hashes' + html2canvas@1.4.1: dependencies: css-line-break: 2.1.0 @@ -8117,7 +8741,7 @@ snapshots: idb@7.1.1: {} - idea-react@2.2.2(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react-is@16.13.1)(react@19.2.5)(typescript@5.9.3): + idea-react@2.2.2(@types/react@19.2.14)(element-internals-polyfill@3.0.2)(react-dom@19.2.5(react@19.2.5))(react-is@16.13.1)(react@19.2.5)(typescript@5.9.3): dependencies: '@editorjs/editorjs': 2.31.6 '@editorjs/paragraph': 2.11.7 @@ -8130,7 +8754,7 @@ snapshots: lodash: 4.18.1 mobx: 6.15.0 mobx-react: 9.2.1(mobx@6.15.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - mobx-react-helper: 0.5.1(mobx@6.15.0)(react@19.2.5)(typescript@5.9.3) + mobx-react-helper: 0.5.1(element-internals-polyfill@3.0.2)(mobx@6.15.0)(react@19.2.5)(typescript@5.9.3) prismjs: 1.30.0 react: 19.2.5 react-bootstrap: 2.10.10(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) @@ -8138,7 +8762,7 @@ snapshots: react-editor-js: 2.1.0(@editorjs/editorjs@2.31.6)(@editorjs/paragraph@2.11.7)(react@19.2.5) react-element-to-jsx-string: 17.0.1(react-dom@19.2.5(react@19.2.5))(react-is@16.13.1)(react@19.2.5) react-live: 4.1.8(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - web-utility: 4.6.6(typescript@5.9.3) + web-utility: 4.6.6(element-internals-polyfill@3.0.2)(typescript@5.9.3) transitivePeerDependencies: - '@types/react' - element-internals-polyfill @@ -8298,6 +8922,8 @@ snapshots: is-plain-object@5.0.0: {} + is-potential-custom-element-name@1.0.1: {} + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -8386,6 +9012,32 @@ snapshots: js-tokens@4.0.0: {} + jsdom@29.1.1: + dependencies: + '@asamuzakjp/css-color': 5.1.11 + '@asamuzakjp/dom-selector': 7.1.1 + '@bramus/specificity': 2.4.2 + '@csstools/css-syntax-patches-for-csstree': 1.1.4(css-tree@3.2.1) + '@exodus/bytes': 1.15.0 + css-tree: 3.2.1 + data-urls: 7.0.0 + decimal.js: 10.6.0 + html-encoding-sniffer: 6.0.0 + is-potential-custom-element-name: 1.0.1 + lru-cache: 11.3.6 + parse5: 8.0.1 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 6.0.1 + undici: 7.25.0 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 8.0.1 + whatwg-mimetype: 5.0.0 + whatwg-url: 16.0.1 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - '@noble/hashes' + jsesc@3.1.0: {} json-buffer@3.0.1: {} @@ -8482,13 +9134,14 @@ snapshots: type-is: 2.0.1 vary: 1.1.2 - koajax@3.3.0(core-js@3.49.0)(typescript@5.9.3): + koajax@3.3.0(core-js@3.49.0)(element-internals-polyfill@3.0.2)(jsdom@29.1.1)(typescript@5.9.3): dependencies: '@swc/helpers': 0.5.21 core-js: 3.49.0 + jsdom: 29.1.1 regenerator-runtime: 0.14.1 web-streams-polyfill: 4.2.0 - web-utility: 4.6.6(typescript@5.9.3) + web-utility: 4.6.6(element-internals-polyfill@3.0.2)(typescript@5.9.3) transitivePeerDependencies: - element-internals-polyfill - typescript @@ -8501,9 +9154,11 @@ snapshots: leaflet@1.9.4: {} - less-loader@12.3.2(less@4.6.4): + less-loader@12.3.2(less@4.6.4)(webpack@5.106.2): dependencies: less: 4.6.4 + optionalDependencies: + webpack: 5.106.2 less@4.6.4: dependencies: @@ -8565,6 +9220,8 @@ snapshots: lit-element: 4.2.2 lit-html: 3.3.2 + loader-runner@4.3.2: {} + loader-utils@2.0.4: dependencies: big.js: 5.2.2 @@ -8619,6 +9276,8 @@ snapshots: dependencies: js-tokens: 4.0.0 + lru-cache@11.3.6: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -8755,6 +9414,8 @@ snapshots: dependencies: '@types/mdast': 4.0.4 + mdn-data@2.27.1: {} + media-typer@0.3.0: {} media-typer@1.1.0: {} @@ -9014,57 +9675,68 @@ snapshots: minimist@1.2.8: {} - mobx-github@0.6.2(core-js@3.49.0)(typescript@5.9.3): + mobx-github@0.6.2(core-js@3.49.0)(element-internals-polyfill@3.0.2)(jsdom@29.1.1)(typescript@5.9.3): dependencies: '@octokit/openapi-types': 26.0.0 '@swc/helpers': 0.5.21 '@types/lodash': 4.17.24 - koajax: 3.3.0(core-js@3.49.0)(typescript@5.9.3) + koajax: 3.3.0(core-js@3.49.0)(element-internals-polyfill@3.0.2)(jsdom@29.1.1)(typescript@5.9.3) lodash: 4.18.1 mobx: 6.15.0 - mobx-restful: 2.1.4(core-js@3.49.0)(mobx@6.15.0)(typescript@5.9.3) - web-utility: 4.6.6(typescript@5.9.3) + mobx-restful: 2.1.4(core-js@3.49.0)(element-internals-polyfill@3.0.2)(jsdom@29.1.1)(mobx@6.15.0)(typescript@5.9.3) + web-utility: 4.6.6(element-internals-polyfill@3.0.2)(typescript@5.9.3) transitivePeerDependencies: - core-js - element-internals-polyfill - jsdom - typescript - mobx-i18n@0.7.2(mobx@6.15.0)(typescript@5.9.3): + mobx-i18n@0.7.2(element-internals-polyfill@3.0.2)(mobx@6.15.0)(typescript@5.9.3): dependencies: '@swc/helpers': 0.5.21 '@types/node': 22.19.17 mobx: 6.15.0 regenerator-runtime: 0.14.1 - web-utility: 4.6.6(typescript@5.9.3) + web-utility: 4.6.6(element-internals-polyfill@3.0.2)(typescript@5.9.3) transitivePeerDependencies: - element-internals-polyfill - typescript - mobx-lark@2.8.1(core-js@3.49.0)(react@19.2.5)(typescript@5.9.3): + mobx-lark@2.8.1(core-js@3.49.0)(element-internals-polyfill@3.0.2)(jsdom@29.1.1)(react@19.2.5)(typescript@5.9.3): dependencies: '@swc/helpers': 0.5.21 '@types/react': 19.2.14 - koajax: 3.3.0(core-js@3.49.0)(typescript@5.9.3) + koajax: 3.3.0(core-js@3.49.0)(element-internals-polyfill@3.0.2)(jsdom@29.1.1)(typescript@5.9.3) lodash.memoize: 4.1.2 mobx: 6.15.0 - mobx-restful: 2.1.4(core-js@3.49.0)(mobx@6.15.0)(typescript@5.9.3) + mobx-restful: 2.1.4(core-js@3.49.0)(element-internals-polyfill@3.0.2)(jsdom@29.1.1)(mobx@6.15.0)(typescript@5.9.3) react: 19.2.5 regenerator-runtime: 0.14.1 - web-utility: 4.6.6(typescript@5.9.3) + web-utility: 4.6.6(element-internals-polyfill@3.0.2)(typescript@5.9.3) transitivePeerDependencies: - core-js - element-internals-polyfill - jsdom - typescript - mobx-react-helper@0.5.1(mobx@6.15.0)(react@19.2.5)(typescript@5.9.3): + mobx-react-helper@0.4.1(element-internals-polyfill@3.0.2)(mobx@6.15.0)(react@19.2.5)(typescript@5.9.3): dependencies: '@swc/helpers': 0.5.21 lodash.isequalwith: 4.4.0 mobx: 6.15.0 react: 19.2.5 - web-utility: 4.6.6(typescript@5.9.3) + web-utility: 4.6.6(element-internals-polyfill@3.0.2)(typescript@5.9.3) + transitivePeerDependencies: + - element-internals-polyfill + - typescript + + mobx-react-helper@0.5.1(element-internals-polyfill@3.0.2)(mobx@6.15.0)(react@19.2.5)(typescript@5.9.3): + dependencies: + '@swc/helpers': 0.5.21 + lodash.isequalwith: 4.4.0 + mobx: 6.15.0 + react: 19.2.5 + web-utility: 4.6.6(element-internals-polyfill@3.0.2)(typescript@5.9.3) transitivePeerDependencies: - element-internals-polyfill - typescript @@ -9085,21 +9757,21 @@ snapshots: optionalDependencies: react-dom: 19.2.5(react@19.2.5) - mobx-restful-table@2.6.3(@types/react@19.2.14)(core-js@3.49.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3): + mobx-restful-table@2.6.3(@types/react@19.2.14)(core-js@3.49.0)(element-internals-polyfill@3.0.2)(jsdom@29.1.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3): dependencies: '@swc/helpers': 0.5.21 classnames: 2.5.1 lodash: 4.18.1 mobx: 6.15.0 - mobx-i18n: 0.7.2(mobx@6.15.0)(typescript@5.9.3) + mobx-i18n: 0.7.2(element-internals-polyfill@3.0.2)(mobx@6.15.0)(typescript@5.9.3) mobx-react: 9.2.1(mobx@6.15.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - mobx-react-helper: 0.5.1(mobx@6.15.0)(react@19.2.5)(typescript@5.9.3) - mobx-restful: 2.1.4(core-js@3.49.0)(mobx@6.15.0)(typescript@5.9.3) + mobx-react-helper: 0.5.1(element-internals-polyfill@3.0.2)(mobx@6.15.0)(react@19.2.5)(typescript@5.9.3) + mobx-restful: 2.1.4(core-js@3.49.0)(element-internals-polyfill@3.0.2)(jsdom@29.1.1)(mobx@6.15.0)(typescript@5.9.3) react: 19.2.5 react-bootstrap: 2.10.10(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - react-bootstrap-editor: 2.1.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3) + react-bootstrap-editor: 2.1.1(element-internals-polyfill@3.0.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3) regenerator-runtime: 0.14.1 - web-utility: 4.6.6(typescript@5.9.3) + web-utility: 4.6.6(element-internals-polyfill@3.0.2)(typescript@5.9.3) transitivePeerDependencies: - '@types/react' - core-js @@ -9109,30 +9781,30 @@ snapshots: - react-native - typescript - mobx-restful@2.1.4(core-js@3.49.0)(mobx@6.15.0)(typescript@5.9.3): + mobx-restful@2.1.4(core-js@3.49.0)(element-internals-polyfill@3.0.2)(jsdom@29.1.1)(mobx@6.15.0)(typescript@5.9.3): dependencies: '@swc/helpers': 0.5.21 idb-keyval: 6.2.2 - koajax: 3.3.0(core-js@3.49.0)(typescript@5.9.3) + koajax: 3.3.0(core-js@3.49.0)(element-internals-polyfill@3.0.2)(jsdom@29.1.1)(typescript@5.9.3) mobx: 6.15.0 regenerator-runtime: 0.14.1 - web-utility: 4.6.6(typescript@5.9.3) + web-utility: 4.6.6(element-internals-polyfill@3.0.2)(typescript@5.9.3) transitivePeerDependencies: - core-js - element-internals-polyfill - jsdom - typescript - mobx-strapi@0.8.1(core-js@3.49.0)(typescript@5.9.3): + mobx-strapi@0.8.1(core-js@3.49.0)(element-internals-polyfill@3.0.2)(jsdom@29.1.1)(typescript@5.9.3): dependencies: '@swc/helpers': 0.5.21 idb-keyval: 6.2.2 - koajax: 3.3.0(core-js@3.49.0)(typescript@5.9.3) + koajax: 3.3.0(core-js@3.49.0)(element-internals-polyfill@3.0.2)(jsdom@29.1.1)(typescript@5.9.3) mobx: 6.15.0 - mobx-restful: 2.1.4(core-js@3.49.0)(mobx@6.15.0)(typescript@5.9.3) + mobx-restful: 2.1.4(core-js@3.49.0)(element-internals-polyfill@3.0.2)(jsdom@29.1.1)(mobx@6.15.0)(typescript@5.9.3) qs: 6.15.1 regenerator-runtime: 0.14.1 - web-utility: 4.6.6(typescript@5.9.3) + web-utility: 4.6.6(element-internals-polyfill@3.0.2)(typescript@5.9.3) transitivePeerDependencies: - core-js - element-internals-polyfill @@ -9163,14 +9835,16 @@ snapshots: negotiator@0.6.3: {} - next-pwa@5.6.0(@babel/core@7.29.0)(next@16.2.4(@babel/core@7.29.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0)): + neo-async@2.6.2: {} + + next-pwa@5.6.0(@babel/core@7.29.0)(next@16.2.4(@babel/core@7.29.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0))(webpack@5.106.2): dependencies: - babel-loader: 8.4.1(@babel/core@7.29.0) - clean-webpack-plugin: 4.0.0 + babel-loader: 8.4.1(@babel/core@7.29.0)(webpack@5.106.2) + clean-webpack-plugin: 4.0.0(webpack@5.106.2) globby: 11.1.0 next: 16.2.4(@babel/core@7.29.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0) - terser-webpack-plugin: 5.5.0 - workbox-webpack-plugin: 6.6.0 + terser-webpack-plugin: 5.5.0(webpack@5.106.2) + workbox-webpack-plugin: 6.6.0(webpack@5.106.2) workbox-window: 6.6.0 transitivePeerDependencies: - '@babel/core' @@ -9181,7 +9855,7 @@ snapshots: - uglify-js - webpack - next-ssr-middleware@1.1.0(next@16.2.4(@babel/core@7.29.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0))(react@19.2.5)(typescript@5.9.3): + next-ssr-middleware@1.1.0(element-internals-polyfill@3.0.2)(next@16.2.4(@babel/core@7.29.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0))(react@19.2.5)(typescript@5.9.3): dependencies: '@koa/bodyparser': 6.1.0(koa@3.2.0) '@koa/router': 15.4.0(koa@3.2.0) @@ -9193,19 +9867,45 @@ snapshots: next: 16.2.4(@babel/core@7.29.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0) react: 19.2.5 tslib: 2.8.1 - web-utility: 4.6.6(typescript@5.9.3) + web-utility: 4.6.6(element-internals-polyfill@3.0.2)(typescript@5.9.3) transitivePeerDependencies: - element-internals-polyfill - supports-color - typescript - next-with-less@3.0.1(less-loader@12.3.2(less@4.6.4))(less@4.6.4)(next@16.2.4(@babel/core@7.29.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0)): + next-with-less@3.0.1(less-loader@12.3.2(less@4.6.4)(webpack@5.106.2))(less@4.6.4)(next@16.2.4(@babel/core@7.29.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0)): dependencies: clone-deep: 4.0.1 less: 4.6.4 - less-loader: 12.3.2(less@4.6.4) + less-loader: 12.3.2(less@4.6.4)(webpack@5.106.2) next: 16.2.4(@babel/core@7.29.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0) + next@13.5.11(@babel/core@7.29.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0): + dependencies: + '@next/env': 13.5.11 + '@swc/helpers': 0.5.2 + busboy: 1.6.0 + caniuse-lite: 1.0.30001791 + postcss: 8.4.31 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + styled-jsx: 5.1.1(@babel/core@7.29.0)(react@19.2.5) + watchpack: 2.4.0 + optionalDependencies: + '@next/swc-darwin-arm64': 13.5.9 + '@next/swc-darwin-x64': 13.5.9 + '@next/swc-linux-arm64-gnu': 13.5.9 + '@next/swc-linux-arm64-musl': 13.5.9 + '@next/swc-linux-x64-gnu': 13.5.9 + '@next/swc-linux-x64-musl': 13.5.9 + '@next/swc-win32-arm64-msvc': 13.5.9 + '@next/swc-win32-ia32-msvc': 13.5.9 + '@next/swc-win32-x64-msvc': 13.5.9 + sass: 1.99.0 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + next@16.2.4(@babel/core@7.29.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0): dependencies: '@next/env': 16.2.4 @@ -9299,19 +9999,19 @@ snapshots: dependencies: mimic-function: 5.0.1 - open-react-map@0.9.1(core-js@3.49.0)(mobx-react@9.2.1(mobx@6.15.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(mobx@6.15.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3): + open-react-map@0.9.1(core-js@3.49.0)(element-internals-polyfill@3.0.2)(jsdom@29.1.1)(mobx-react@9.2.1(mobx@6.15.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(mobx@6.15.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3): dependencies: '@swc/helpers': 0.5.21 '@types/leaflet': 1.9.21 - koajax: 3.3.0(core-js@3.49.0)(typescript@5.9.3) + koajax: 3.3.0(core-js@3.49.0)(element-internals-polyfill@3.0.2)(jsdom@29.1.1)(typescript@5.9.3) leaflet: 1.9.4 mobx: 6.15.0 mobx-react: 9.2.1(mobx@6.15.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - mobx-react-helper: 0.5.1(mobx@6.15.0)(react@19.2.5)(typescript@5.9.3) + mobx-react-helper: 0.5.1(element-internals-polyfill@3.0.2)(mobx@6.15.0)(react@19.2.5)(typescript@5.9.3) react: 19.2.5 react-dom: 19.2.5(react@19.2.5) react-leaflet: 5.0.0(leaflet@1.9.4)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - web-utility: 4.6.6(typescript@5.9.3) + web-utility: 4.6.6(element-internals-polyfill@3.0.2)(typescript@5.9.3) transitivePeerDependencies: - core-js - element-internals-polyfill @@ -9378,6 +10078,10 @@ snapshots: parse-node-version@1.0.1: {} + parse5@8.0.1: + dependencies: + entities: 8.0.0 + parseurl@1.3.3: {} path-exists@4.0.0: {} @@ -9505,16 +10209,16 @@ snapshots: react-stately: 3.46.0(react@19.2.5) use-sync-external-store: 1.6.0(react@19.2.5) - react-bootstrap-editor@2.1.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3): + react-bootstrap-editor@2.1.1(element-internals-polyfill@3.0.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3): dependencies: '@swc/helpers': 0.5.21 - edkit: 1.2.7(typescript@5.9.3) + edkit: 1.2.7(element-internals-polyfill@3.0.2)(typescript@5.9.3) mobx: 6.15.0 mobx-react: 9.2.1(mobx@6.15.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - mobx-react-helper: 0.5.1(mobx@6.15.0)(react@19.2.5)(typescript@5.9.3) + mobx-react-helper: 0.4.1(element-internals-polyfill@3.0.2)(mobx@6.15.0)(react@19.2.5)(typescript@5.9.3) react: 19.2.5 react-dom: 19.2.5(react@19.2.5) - web-utility: 4.6.6(typescript@5.9.3) + web-utility: 4.6.6(element-internals-polyfill@3.0.2)(typescript@5.9.3) transitivePeerDependencies: - element-internals-polyfill - react-native @@ -9819,6 +10523,10 @@ snapshots: sax@1.6.0: optional: true + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + scheduler@0.27.0: {} schema-utils@2.7.1: @@ -9831,7 +10539,7 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 ajv: 8.20.0 - ajv-formats: 2.1.1 + ajv-formats: 2.1.1(ajv@8.20.0) ajv-keywords: 5.1.0(ajv@8.20.0) semver@5.7.2: @@ -9987,6 +10695,8 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 + streamsearch@1.1.0: {} + string-argv@0.3.2: {} string-width@7.2.0: @@ -10081,6 +10791,13 @@ snapshots: dependencies: inline-style-parser: 0.2.7 + styled-jsx@5.1.1(@babel/core@7.29.0)(react@19.2.5): + dependencies: + client-only: 0.0.1 + react: 19.2.5 + optionalDependencies: + '@babel/core': 7.29.0 + styled-jsx@5.1.6(@babel/core@7.29.0)(react@19.2.5): dependencies: client-only: 0.0.1 @@ -10108,10 +10825,14 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + symbol-tree@3.2.4: {} + synckit@0.11.12: dependencies: '@pkgr/core': 0.2.9 + tapable@2.3.3: {} + temp-dir@2.0.0: {} tempy@0.6.0: @@ -10121,12 +10842,13 @@ snapshots: type-fest: 0.16.0 unique-string: 2.0.0 - terser-webpack-plugin@5.5.0: + terser-webpack-plugin@5.5.0(webpack@5.106.2): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 terser: 5.46.2 + webpack: 5.106.2 terser@5.46.2: dependencies: @@ -10154,6 +10876,12 @@ snapshots: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 + tldts-core@7.0.30: {} + + tldts@7.0.30: + dependencies: + tldts-core: 7.0.30 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -10168,10 +10896,18 @@ snapshots: toml@3.0.0: {} + tough-cookie@6.0.1: + dependencies: + tldts: 7.0.30 + tr46@1.0.1: dependencies: punycode: 2.3.1 + tr46@6.0.0: + dependencies: + punycode: 2.3.1 + trim-lines@3.0.1: {} trough@2.2.0: {} @@ -10293,6 +11029,8 @@ snapshots: undici-types@7.16.0: {} + undici@7.25.0: {} + unicode-canonical-property-names-ecmascript@2.0.1: {} unicode-match-property-ecmascript@2.0.0: @@ -10423,25 +11161,85 @@ snapshots: vscode-uri@3.1.0: {} + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + warning@4.0.3: dependencies: loose-envify: 1.4.0 + watchpack@2.4.0: + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + + watchpack@2.5.1: + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + web-streams-polyfill@4.2.0: {} - web-utility@4.6.6(typescript@5.9.3): + web-utility@4.6.6(element-internals-polyfill@3.0.2)(typescript@5.9.3): dependencies: '@swc/helpers': 0.5.21 + element-internals-polyfill: 3.0.2 regenerator-runtime: 0.14.1 typescript: 5.9.3 webidl-conversions@4.0.2: {} + webidl-conversions@8.0.1: {} + webpack-sources@1.4.3: dependencies: source-list-map: 2.0.1 source-map: 0.6.1 + webpack-sources@3.4.1: {} + + webpack@5.106.2: + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.16.0 + acorn-import-phases: 1.0.4(acorn@8.16.0) + browserslist: 4.28.2 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.21.3 + es-module-lexer: 2.1.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + loader-runner: 4.3.2 + mime-db: 1.54.0 + neo-async: 2.6.2 + schema-utils: 4.3.3 + tapable: 2.3.3 + terser-webpack-plugin: 5.5.0(webpack@5.106.2) + watchpack: 2.5.1 + webpack-sources: 3.4.1 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + + whatwg-mimetype@5.0.0: {} + + whatwg-url@16.0.1: + dependencies: + '@exodus/bytes': 1.15.0 + tr46: 6.0.0 + webidl-conversions: 8.0.1 + transitivePeerDependencies: + - '@noble/hashes' + whatwg-url@7.1.0: dependencies: lodash.sortby: 4.7.0 @@ -10603,11 +11401,12 @@ snapshots: workbox-sw@6.6.0: {} - workbox-webpack-plugin@6.6.0: + workbox-webpack-plugin@6.6.0(webpack@5.106.2): dependencies: fast-json-stable-stringify: 2.1.0 pretty-bytes: 5.6.0 upath: 1.2.0 + webpack: 5.106.2 webpack-sources: 1.4.3 workbox-build: 6.6.0 transitivePeerDependencies: @@ -10629,6 +11428,10 @@ snapshots: xdg-basedir@5.1.0: {} + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + yallist@3.1.1: {} yaml@2.8.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..1778080 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,5 @@ +allowBuilds: + '@parcel/watcher': set this to true or false + core-js: set this to true or false + sharp: set this to true or false + unrs-resolver: set this to true or false diff --git a/public/images/placeholder-book.svg b/public/images/placeholder-book.svg new file mode 100644 index 0000000..9437627 --- /dev/null +++ b/public/images/placeholder-book.svg @@ -0,0 +1,12 @@ + + Book Placeholder + A simple placeholder cover for Open Library books. + + + + + + + + + diff --git a/public/images/placeholder-hero.svg b/public/images/placeholder-hero.svg new file mode 100644 index 0000000..7fabd8a --- /dev/null +++ b/public/images/placeholder-hero.svg @@ -0,0 +1,29 @@ + + Open Library Hero Placeholder + An abstract illustration of people sharing books. + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/styles/open-library.css b/styles/open-library.css deleted file mode 100644 index 540c4bd..0000000 --- a/styles/open-library.css +++ /dev/null @@ -1,65 +0,0 @@ -/* Open Library Global Styles */ - -/* Reset styles for Open Library section */ -body.open-library, -html.open-library { - margin: 0 !important; - padding: 0 !important; - overflow-x: hidden !important; -} - -/* Hide main site header and footer */ -body.open-library nav.navbar.bg-dark.navbar-dark.fixed-top, -body.open-library footer.mw-100.bg-dark.text-white { - display: none !important; -} - -/* Remove top margin that accommodates the main header */ -body.open-library div.mt-5.pt-2 { - margin-top: 0 !important; - padding-top: 0 !important; - max-width: 100% !important; - width: 100% !important; -} - -/* Adjust main wrapper */ -body.open-library main.flex-fill.d-flex.flex-column.justify-content-start.align-items-center { - background: none !important; - padding: 0 !important; - margin: 0 !important; - max-width: 100% !important; - width: 100% !important; -} - -/* Adjust container styles */ -body.open-library .container, -body.open-library .container-fluid { - max-width: 100% !important; - padding-left: 0 !important; - padding-right: 0 !important; - margin-left: 0 !important; - margin-right: 0 !important; -} - -/* Remove card styling from main content */ -body.open-library main.flex-fill.d-flex.flex-column.justify-content-start.align-items-center .card { - background: transparent !important; - border: none !important; - box-shadow: none !important; - padding: 0 !important; - margin: 0 !important; -} - -/* Remove card body padding */ -body.open-library main.flex-fill.d-flex.flex-column.justify-content-start.align-items-center .card-body { - padding: 0 !important; -} - -/* Target the MDXProvider wrapper (main white background) */ -body.open-library .MDXProvider { - padding: 0 !important; - margin: 0 !important; - background: transparent !important; - border: none !important; - box-shadow: none !important; -} diff --git a/styles/open-library.module.scss b/styles/open-library.module.scss new file mode 100644 index 0000000..eae5d96 --- /dev/null +++ b/styles/open-library.module.scss @@ -0,0 +1,11 @@ +.openLibrary { + margin: 0; + padding: 0; + width: 100%; + max-width: 100%; + overflow-x: hidden; +} + +.featureList { + list-style-type: '✦ '; +} diff --git a/translation/en-US.ts b/translation/en-US.ts index d0534e8..4396e61 100644 --- a/translation/en-US.ts +++ b/translation/en-US.ts @@ -360,13 +360,13 @@ export default { donate_books: 'Donate Books', how_it_works: 'How It Works', how_it_works_description: 'Three simple steps to borrow books from our community', - step_1_find_book: '1. Find a Book', + step_1_find_book: 'Find a Book', step_1_description: 'Browse our collection and find the book that interests you. Filter by category, author, or popularity.', - step_2_apply: '2. Apply to Borrow', + step_2_apply: 'Apply to Borrow', step_2_description: "Submit a simple request form. We'll connect you with the book owner and arrange the handover.", - step_3_receive: '3. Receive and Pass It On', + step_3_receive: 'Receive and Pass It On', step_3_description: "Enjoy your book and return it when you're done. Consider donating your own books to keep knowledge flowing.", learn_more_about_borrowing: 'Learn More About Borrowing', @@ -389,7 +389,8 @@ export default { available: 'Available', borrowed: 'Borrowed', reset_filters: 'Reset Filters', - showing_books: 'Showing {0} of {1} books', + showing_books: ({ shownCount, totalCount }: { shownCount: number; totalCount: number }) => + `Showing ${shownCount} of ${totalCount} books`, no_books_found: 'No books found', try_adjusting_filters: 'Try adjusting your search or filters', reset_all_filters: 'Reset All Filters', @@ -450,6 +451,38 @@ export default { fill_borrow_request: 'Fill Borrow Request', fill_book_passing_form: 'Fill Book Passing Form', browse_book_catalog: 'Browse Book Catalog', + ready_to_borrow: 'Ready to Borrow?', + + // BookCard + book_cover: 'Cover', + + // HowItWorks + learn_more_details: 'Learn More', + + // Books Page + book_catalog_description: 'Browse our community shared library and discover interesting books', + + // Homepage About & Support sections + open_library_community_intro: 'freeCodeCamp Chengdu Community Open Library', + our_mission_text: + 'Promote knowledge exchange, drive learning and growth among community members, and strengthen interaction and trust. We use a unique "no-storage" lending model to let books circulate freely among members.', + core_concepts: 'Core Concepts', + knowledge_flow: 'Knowledge Flow', + knowledge_flow_desc: 'Books circulate freely to promote knowledge sharing', + community_driven: 'Community Driven', + community_driven_desc: 'All books are donated by members', + open_sharing: 'Open Sharing', + open_sharing_desc: 'Promoting exchange and interaction', + support_us: 'Support Us', + support_us_description: + 'Open Library is completely supported by the passion and contributions of community members. If this project has been helpful to you, please support our development.', + share_your_book: 'Share Your Book', + + // Book Detail Page + book_publisher: 'Publisher', + book_published_year: 'Published', + book_page_count: 'Pages', + not_available: 'N/A', // Open Library Review Page book_reviews: 'Book Reviews', @@ -483,8 +516,11 @@ export default { about_us_footer: 'About Us', donate_footer: 'Donate', contact: 'Contact', - contact_email: 'Email: info@openlibrary.community', - contact_address: 'Address: 123 Library Lane, Knowledge City, World', + community_name: 'freeCodeCamp Chengdu Community', + community_location: 'Chengdu, Sichuan, China', + contact_email: 'team@fcc-cd.dev', + contact_address: 'Chengdu, Sichuan, China', + contact_wechat: 'freeCodeCamp 成都社区', follow_us: 'Follow Us', all_rights_reserved: 'All Rights Reserved', }; diff --git a/translation/zh-CN.ts b/translation/zh-CN.ts index 5ca0bc9..a443cd1 100644 --- a/translation/zh-CN.ts +++ b/translation/zh-CN.ts @@ -336,11 +336,11 @@ export default { donate_books: '捐赠书籍', how_it_works: '运作方式', how_it_works_description: '三个简单步骤借阅社区书籍', - step_1_find_book: '1. 寻找书籍', + step_1_find_book: '寻找书籍', step_1_description: '浏览我们的藏书并找到您感兴趣的书籍。可按类别、作者或热度筛选。', - step_2_apply: '2. 申请借阅', + step_2_apply: '申请借阅', step_2_description: '提交简单的申请表。我们将为您联系书籍所有者并安排交接。', - step_3_receive: '3. 接收并传递', + step_3_receive: '接收并传递', step_3_description: '阅读完毕后归还书籍。考虑捐赠您自己的书籍,让知识持续流动。', learn_more_about_borrowing: '了解更多借阅信息', community_voices: '社区声音', @@ -362,7 +362,8 @@ export default { available: '可借阅', borrowed: '已借出', reset_filters: '重置筛选', - showing_books: '显示 {0} 本书,共 {1} 本', + showing_books: ({ shownCount, totalCount }: { shownCount: number; totalCount: number }) => + `显示 ${shownCount} 本书,共 ${totalCount} 本`, no_books_found: '未找到书籍', try_adjusting_filters: '尝试调整筛选条件', reset_all_filters: '重置所有筛选', @@ -423,6 +424,38 @@ export default { fill_borrow_request: '填写借阅申请', fill_book_passing_form: '填写书籍传递表', browse_book_catalog: '浏览书籍目录', + ready_to_borrow: '准备好借阅了吗?', + + // BookCard + book_cover: '封面', + + // HowItWorks + learn_more_details: '了解详细流程', + + // Books Page + book_catalog_description: '浏览我们的社区共享图书馆,发现有趣的书籍', + + // Homepage About & Support sections + open_library_community_intro: 'freeCodeCamp 成都社区「Open Library」开放共享图书馆', + 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: + 'Open Library 完全由社区成员的热情和贡献支撑。如果这个项目对你有帮助,欢迎通过以下方式支持我们的发展。', + share_your_book: '分享你的图书', + + // Book Detail Page + book_publisher: '出版社', + book_published_year: '出版年份', + book_page_count: '页数', + not_available: 'N/A', // Open Library Review Page book_reviews: '书籍评价', @@ -455,8 +488,11 @@ export default { about_us_footer: '关于我们', donate_footer: '捐赠', contact: '联系我们', - contact_email: '邮箱:info@openlibrary.community', - contact_address: '地址:123 图书馆路,知识城,世界', + community_name: 'freeCodeCamp 成都社区', + community_location: '中国四川成都', + contact_email: 'team@fcc-cd.dev', + contact_address: '中国四川成都', + contact_wechat: 'freeCodeCamp 成都社区', follow_us: '关注我们', all_rights_reserved: '版权所有', }; diff --git a/translation/zh-TW.ts b/translation/zh-TW.ts index 1300dce..d9cbc53 100644 --- a/translation/zh-TW.ts +++ b/translation/zh-TW.ts @@ -336,11 +336,11 @@ export default { donate_books: '捐贈書籍', how_it_works: '如何運作', how_it_works_description: '九步驟加入社群圖書館', - step_1_find_book: '1. 找書', + step_1_find_book: '找書', step_1_description: '瀏覽我們的書籍目錄,找到您想要的書籍。您可以按類別、作者或標題搜索。', - step_2_apply: '2. 申請借書', + step_2_apply: '申請借書', step_2_description: '填寫借書申請表格,我們會與您聯絡安排借書事宜。', - step_3_receive: '3. 收書', + step_3_receive: '收書', step_3_description: '書籍將會寄送到您指定的地址,請注意查收。', learn_more_about_borrowing: '了解更多借書資訊', community_voices: '社群聲音', @@ -362,7 +362,8 @@ export default { available: '可借', borrowed: '已借出', reset_filters: '重設篩選', - showing_books: '顯示 {0} 本書,共 {1} 本', + showing_books: ({ shownCount, totalCount }: { shownCount: number; totalCount: number }) => + `顯示 ${shownCount} 本書,共 ${totalCount} 本`, no_books_found: '沒有找到書籍', try_adjusting_filters: '試著調整篩選條件', reset_all_filters: '重設所有篩選', @@ -423,6 +424,38 @@ export default { fill_borrow_request: '填寫借書申請', fill_book_passing_form: '填寫書籍傳遞表單', browse_book_catalog: '瀏覽書籍目錄', + ready_to_borrow: '準備好借書嗎?', + + // BookCard + book_cover: '封面', + + // HowItWorks + learn_more_details: '了解詳細流程', + + // Books Page + book_catalog_description: '瀏覽我們的社區共享圖書館,發現有趣的書籍', + + // Homepage About & Support sections + open_library_community_intro: 'freeCodeCamp 成都社區「Open Library」開放共享圖書館', + 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: + 'Open Library 完全由社區成員的熱情和貢獻支撐。如果這個項目對你有幫助,歡迎通過以下方式支持我們的發展。', + share_your_book: '分享你的圖書', + + // Book Detail Page + book_publisher: '出版社', + book_published_year: '出版年份', + book_page_count: '頁數', + not_available: 'N/A', // Open Library Review Page book_reviews: '書籍書評', @@ -456,8 +489,11 @@ export default { about_us_footer: '關於我們', donate_footer: '捐贈', contact: '聯絡我們', - contact_email: '聯絡郵件:info@openlibrary.community', - contact_address: '地址:123 圖書館路,社群圖書館,香港', + community_name: 'freeCodeCamp 成都社群', + community_location: '中國四川成都', + contact_email: 'team@fcc-cd.dev', + contact_address: '中國四川成都', + contact_wechat: 'freeCodeCamp 成都社群', follow_us: '關注我們', all_rights_reserved: '版權所有', };