Skip to content

Commit 99320ce

Browse files
committed
feat: Complete MVP layout improvements
1 parent 23870d8 commit 99320ce

16 files changed

Lines changed: 757 additions & 1418 deletions

File tree

components/Layout/CardPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Col, Pagination, Row } from 'react-bootstrap';
44
import { SearchPageMeta } from '../../models/System';
55

66
export interface CardPageProps extends SearchPageMeta {
7-
Card: ComponentClass<any> | FC<any>;
7+
Card: ComponentClass<Record<string, unknown>> | FC<Record<string, unknown>>;
88
cardLinkOf?: (id: string) => string;
99
pageLinkOf: (page: number) => string;
1010
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import Link from 'next/link';
2+
import React from 'react';
3+
import { Button, Card, Image } from 'react-bootstrap';
4+
5+
import { t } from '../../models/Translation';
6+
7+
export interface Book {
8+
id: number;
9+
title: string;
10+
author: string;
11+
cover?: string;
12+
status?: 'available' | 'borrowed';
13+
category?: string;
14+
description?: string;
15+
}
16+
17+
interface BookCardProps {
18+
book: Book;
19+
showStatus?: boolean;
20+
variant?: 'featured' | 'catalog';
21+
}
22+
23+
const BookCard: React.FC<BookCardProps> = ({
24+
book,
25+
showStatus = false,
26+
variant = 'featured',
27+
}) => {
28+
const cardClass =
29+
variant === 'featured'
30+
? 'h-100 shadow-sm border-0'
31+
: 'h-100 shadow border-0';
32+
33+
return (
34+
<Card className={cardClass}>
35+
<div className="text-center p-3 position-relative">
36+
{showStatus && (
37+
<div className="position-absolute top-0 end-0 m-2">
38+
<span
39+
className={`badge ${
40+
book.status === 'available'
41+
? 'bg-success'
42+
: 'bg-warning text-dark'
43+
}`}
44+
>
45+
{book.status === 'available' ? '可借阅' : '已借出'}
46+
</span>
47+
</div>
48+
)}
49+
<div
50+
className="d-flex align-items-center justify-content-center overflow-hidden rounded bg-light"
51+
style={{ height: '180px' }}
52+
>
53+
<Image
54+
src={book.cover || '/images/placeholder-book.svg'}
55+
alt={`${book.title} 封面`}
56+
className="img-fluid"
57+
style={{
58+
maxHeight: '160px',
59+
maxWidth: '120px',
60+
objectFit: 'contain',
61+
}}
62+
/>
63+
</div>
64+
</div>
65+
<Card.Body className="text-center d-flex flex-column p-3">
66+
<Card.Title
67+
className="fw-bold h6 mb-2 text-truncate"
68+
title={book.title}
69+
>
70+
{book.title}
71+
</Card.Title>
72+
<Card.Text
73+
className="text-muted small mb-2 text-truncate"
74+
title={book.author}
75+
>
76+
{book.author}
77+
</Card.Text>
78+
{book.category && (
79+
<Card.Text className="text-muted small mb-3">
80+
<i className="bi bi-tag me-1"></i>
81+
{book.category}
82+
</Card.Text>
83+
)}
84+
<div className="mt-auto">
85+
<Link href={`/open-library/book/${book.id}`} passHref legacyBehavior>
86+
<Button
87+
variant="outline-success"
88+
size="sm"
89+
as="a"
90+
className="rounded-pill px-3 fw-medium"
91+
>
92+
{t('view_details')}
93+
</Button>
94+
</Link>
95+
</div>
96+
</Card.Body>
97+
</Card>
98+
);
99+
};
100+
101+
export default BookCard;
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import Link from 'next/link';
2+
import React from 'react';
3+
import { Button, Col, Row } from 'react-bootstrap';
4+
5+
import BookCard, { Book } from './BookCard';
6+
import { ContentContainer } from './Layout';
7+
8+
interface FeaturedBooksProps {
9+
books: Book[];
10+
title?: string;
11+
subtitle?: string;
12+
showViewAll?: boolean;
13+
viewAllLink?: string;
14+
viewAllText?: string;
15+
}
16+
17+
const FeaturedBooks: React.FC<FeaturedBooksProps> = ({
18+
books,
19+
title = '精选图书',
20+
subtitle = '社区成员推荐的优质读物,涵盖技术、设计、创业等多个领域',
21+
showViewAll = true,
22+
viewAllLink = '/open-library/books',
23+
viewAllText = '查看全部图书',
24+
}) => {
25+
return (
26+
<section className="py-5 bg-light">
27+
<ContentContainer>
28+
<div className="text-center mb-5">
29+
<h2 className="display-5 fw-bold text-dark mb-3 position-relative">
30+
{title}
31+
<div className="position-absolute start-50 translate-middle-x mt-2">
32+
<div
33+
className="bg-success rounded-pill"
34+
style={{ width: '60px', height: '3px' }}
35+
></div>
36+
</div>
37+
</h2>
38+
<p className="lead text-muted mx-auto" style={{ maxWidth: '600px' }}>
39+
{subtitle}
40+
</p>
41+
</div>
42+
43+
<Row xs={1} sm={2} lg={3} xl={4} className="g-4 justify-content-center">
44+
{books.slice(0, 8).map(book => (
45+
<Col key={book.id}>
46+
<BookCard book={book} variant="featured" />
47+
</Col>
48+
))}
49+
</Row>
50+
51+
{showViewAll && (
52+
<div className="text-center mt-5">
53+
<Link href={viewAllLink} passHref legacyBehavior>
54+
<Button
55+
variant="outline-success"
56+
size="lg"
57+
as="a"
58+
className="rounded-pill px-4 fw-semibold"
59+
>
60+
<i className="bi bi-collection me-2"></i>
61+
{viewAllText}
62+
<i className="bi bi-arrow-right ms-2"></i>
63+
</Button>
64+
</Link>
65+
</div>
66+
)}
67+
</ContentContainer>
68+
</section>
69+
);
70+
};
71+
72+
export default FeaturedBooks;

components/open-library/Footer.tsx

Lines changed: 52 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,11 @@ import { Col, Nav, Row } from 'react-bootstrap';
33

44
import { t } from '../../models/Translation';
55

6-
//
6+
// 使用 Bootstrap 工具类替换内联样式的 ContentContainer
77
const ContentContainer: React.FC<{ children: React.ReactNode }> = ({
88
children,
99
}) => {
10-
return (
11-
<div
12-
style={{
13-
maxWidth: '1200px',
14-
margin: '0 auto',
15-
padding: '0 20px',
16-
width: '100%',
17-
boxSizing: 'border-box',
18-
}}
19-
>
20-
{children}
21-
</div>
22-
);
10+
return <div className="container-xl px-3">{children}</div>;
2311
};
2412

2513
const FooterComponent = () => {
@@ -31,75 +19,83 @@ const FooterComponent = () => {
3119
}, []);
3220

3321
return (
34-
<footer className="bg-dark text-light py-4 mt-5">
22+
<footer className="bg-dark text-light py-4">
3523
<ContentContainer>
3624
<Row>
3725
<Col md={4} className="mb-3 mb-md-0">
38-
<h5>{t('open_library')}</h5>
39-
<p>{t('footer_description')}</p>
40-
<div className="mt-2">
41-
<a href="#github" className="text-light me-3">
26+
<h5 className="fw-bold mb-3">{t('open_library')}</h5>
27+
<p className="text-light opacity-75 lh-base">
28+
{t('footer_description')}
29+
</p>
30+
<div className="mt-3">
31+
<a
32+
href="#github"
33+
className="text-light text-decoration-none me-3 hover-opacity"
34+
>
35+
<i className="bi bi-github me-1"></i>
4236
GitHub
4337
</a>
44-
<a href="#twitter" className="text-light me-3">
38+
<a
39+
href="#twitter"
40+
className="text-light text-decoration-none me-3 hover-opacity"
41+
>
42+
<i className="bi bi-twitter me-1"></i>
4543
Twitter
4644
</a>
47-
<a href="#feishu" className="text-light me-3">
45+
<a
46+
href="#feishu"
47+
className="text-light text-decoration-none me-3 hover-opacity"
48+
>
49+
<i className="bi bi-chat-dots me-1"></i>
4850
Feishu
4951
</a>
50-
<a href="#instagram" className="text-light">
51-
Instagram
52-
</a>
5352
</div>
5453
</Col>
5554
<Col md={3} className="mb-3 mb-md-0">
56-
<h5>{t('quick_links_footer')}</h5>
55+
<h5 className="fw-bold mb-3">{t('quick_links_footer')}</h5>
5756
<Nav className="flex-column">
58-
<Nav.Link href="/open-library/books" className="text-light px-0">
57+
<Nav.Link
58+
href="/open-library/books"
59+
className="text-light px-0 py-1 text-decoration-none"
60+
>
61+
<i className="bi bi-book me-2"></i>
5962
{t('catalog_footer')}
6063
</Nav.Link>
61-
<Nav.Link href="/open-library/donate" className="text-light px-0">
62-
{t('donate_footer')}
63-
</Nav.Link>
6464
<Nav.Link
6565
href="/open-library/how-to-borrow"
66-
className="text-light px-0"
66+
className="text-light px-0 py-1 text-decoration-none"
6767
>
68+
<i className="bi bi-info-circle me-2"></i>
6869
{t('how_to_borrow')}
6970
</Nav.Link>
70-
<Nav.Link href="/open-library/about" className="text-light px-0">
71-
{t('about_us_footer')}
72-
</Nav.Link>
73-
<Nav.Link href="/open-library/faq" className="text-light px-0">
74-
{t('faq')}
75-
</Nav.Link>
7671
</Nav>
7772
</Col>
7873
<Col md={5}>
79-
<h5>{t('contact')}</h5>
80-
<p className="mb-1">freeCodeCamp Chengdu Community</p>
81-
<p className="mb-1">Chengdu, Sichuan, China</p>
82-
<p className="mb-1">Email: contact@openlibrary.org</p>
83-
<p>WeChat: FCCChengdu</p>
74+
<h5 className="fw-bold mb-3">{t('contact')}</h5>
75+
<div className="text-light opacity-75">
76+
<p className="mb-2">
77+
<i className="bi bi-geo-alt me-2"></i>
78+
freeCodeCamp Chengdu Community
79+
</p>
80+
<p className="mb-2">
81+
<i className="bi bi-pin-map me-2"></i>
82+
Chengdu, Sichuan, China
83+
</p>
84+
<p className="mb-2">
85+
<i className="bi bi-envelope me-2"></i>
86+
Email: contact@openlibrary.org
87+
</p>
88+
<p className="mb-0">
89+
<i className="bi bi-wechat me-2"></i>
90+
WeChat: FCCChengdu
91+
</p>
92+
</div>
8493
</Col>
8594
</Row>
8695

87-
<hr
88-
className="mt-4 mb-3"
89-
style={{ borderColor: 'rgba(255,255,255,0.2)' }}
90-
/>
96+
<hr className="mt-4 mb-3 border-secondary opacity-25" />
9197

92-
{/* Use a more direct approach with inline styles to ensure visibility */}
93-
<div
94-
className="py-2"
95-
style={{
96-
textAlign: 'center',
97-
color: '#6c757d',
98-
fontSize: '0.875rem',
99-
width: '100%',
100-
display: 'block',
101-
}}
102-
>
98+
<div className="text-center text-light opacity-75 py-2">
10399
{isMounted ? (
104100
<>
105101
&copy; {new Date().getFullYear()} {t('open_library')}.{' '}

0 commit comments

Comments
 (0)