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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ NEXT_PUBLIC_ACTIVITY_TABLE_ID = tblREEMxDOECZZrK
NEXT_PUBLIC_PROJECT_TABLE_ID = tblGnY6Hm0nTSBR9
NEXT_PUBLIC_AWARD_TABLE_ID = tblmYd5V5BMngAp2

NEXT_PUBLIC_LIBRARY_BASE_ID = RP6xbkjfnaI7cmseKGlc2k5wnwb
NEXT_PUBLIC_BOOK_TABLE_ID = tblm8grig3e36tsC

NEXT_PUBLIC_NGO_API_HOST = https://test.jidian.org.cn/api/Lark/
NEXT_PUBLIC_NGO_BASE_ID = RVBibSQU5atsAoshn5Jcwee1nob
NEXT_PUBLIC_NGO_TABLE_ID = tblIXufXtDkiosxg
Expand Down
4 changes: 2 additions & 2 deletions components/open-library/BookCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ export const BookCard: FC<BookCardProps> = observer(
<Card.Title className="fw-bold h6 mb-2 text-truncate" title={book.title}>
{book.title}
</Card.Title>
<Card.Text className="text-muted small mb-2 text-truncate" title={book.author}>
{book.author}
<Card.Text className="text-muted small mb-2 text-truncate" title={book.authors}>
{book.authors}
</Card.Text>
{book.category && (
<Card.Text className="text-muted small mb-3">🏷️ {book.category}</Card.Text>
Expand Down
61 changes: 61 additions & 0 deletions components/open-library/Borrow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { observer } from 'mobx-react';
import { FC, useContext } from 'react';
import { Badge, Table } from 'react-bootstrap';
import { formatDate } from 'web-utility';

import { type BorrowHistory } from '../../models/Book';
import { I18nContext } from '../../models/Translation';

export const BorrowHistoryRow: FC<BorrowHistory> = observer(
({ borrower, borrowDate, returnDate }) => {
const { t } = useContext(I18nContext);

return (
<tr>
<td>{borrower}</td>
<td>{formatDate(borrowDate, 'YYYY-MM-DD')}</td>
<td>{returnDate ? formatDate(returnDate, 'YYYY-MM-DD') : '-'}</td>
<td>
{returnDate ? (
<Badge bg="success">{t('returned')}</Badge>
) : (
<Badge bg="warning" text="dark">
{t('active')}
</Badge>
)}
</td>
</tr>
);
},
);

export const BorrowHistoryTabContent: FC<{ history?: BorrowHistory[] }> = observer(
({ history }) => {
const { t } = useContext(I18nContext);

return history?.[0] ? (
<Table hover responsive>
<thead>
<tr>
<th>{t('borrower')}</th>
<th>{t('borrow_date')}</th>
<th>{t('return_date')}</th>
<th>{t('status')}</th>
</tr>
</thead>
<tbody>
{history.map(({ borrower, borrowDate, returnDate }) => (
<BorrowHistoryRow
key={`${borrower}-${borrowDate}`}
{...{ borrower, borrowDate, returnDate }}
/>
))}
</tbody>
</Table>
) : (
<div className="text-center py-5">
<p>{t('not_borrowed_yet')}</p>
</div>
);
},
);
63 changes: 63 additions & 0 deletions components/open-library/Review.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { observer } from 'mobx-react';
import { FC, useContext } from 'react';
import { Button, Card } from 'react-bootstrap';
import { formatDate } from 'web-utility';

import { type BookReview } from '../../models/Book';
import { OpenLibraryReviewFormURL } from '../../models/configuration';
import { I18nContext } from '../../models/Translation';

export const RatingStars: FC<Pick<BookReview, 'rating'>> = ({ rating }) => (
<ol className="list-unstyled d-flex gap-1 mb-0">
{[...Array(5)].map((_, index) => (
<li key={index} className="text-warning">
{index < rating ? '\u2605' : '\u2606'}
</li>
))}
</ol>
);

export const ReviewCard: FC<BookReview> = ({ reviewer, rating, comment, date }) => (
<Card className="mb-3 shadow-sm" body>
<div className="d-flex justify-content-between align-items-center mb-2">
<h5 className="mb-0">{reviewer}</h5>
<RatingStars rating={rating} />
</div>
<Card.Text>{comment}</Card.Text>
<Card.Text className="small text-muted">{formatDate(date, 'YYYY-MM-DD')}</Card.Text>
</Card>
);

export const ReviewTabContent: FC<{ reviews?: BookReview[] }> = observer(({ reviews }) => {
const { t } = useContext(I18nContext);

return reviews?.[0] ? (
<>
{reviews.map(({ reviewer, rating, comment, date }) => (
<ReviewCard key={`${reviewer}-${date}`} {...{ reviewer, rating, comment, date }} />
))}
<div className="text-center mt-4">
<Button
variant="outline-primary"
href={OpenLibraryReviewFormURL}
target="_blank"
rel="noopener noreferrer"
>
{t('add_your_review')}
</Button>
</div>
</>
) : (
<div className="text-center py-5">
<p className="mb-4">{t('be_first_to_review')}</p>
<Button
variant="primary"
href={OpenLibraryReviewFormURL}
target="_blank"
rel="noopener noreferrer"
>
{t('write_review')}
</Button>
</div>
);
});
39 changes: 36 additions & 3 deletions models/Book.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import { BiDataQueryOptions, BiDataTable, BiSearch } from 'mobx-lark';

import { larkClient } from './Base';
import { BookTableId, LibraryBaseId } from './configuration';

export interface BorrowHistory
extends Record<'borrower' | 'borrowDate', string>, Partial<Record<'returnDate', string>> {}

Expand All @@ -7,9 +12,14 @@ export interface BookReview extends Record<'reviewer' | 'comment' | 'date', stri

export interface Book
extends
Record<'title' | 'author' | 'status' | 'category' | 'language', string>,
Partial<Record<'cover' | 'currentHolder' | 'description' | 'isbn' | 'publisher', string>> {
id: number;
Record<'title' | 'isbn' | 'authors' | 'summary' | 'recommendation', string>,
Partial<Record<'cover' | 'currentHolder' | 'category' | 'language' | 'publisher', string>> {
id: string;
amount?: number;
donors?: string[];
keepers?: string[];
keeperEmails?: string[];
link?: string;
status: 'available' | 'borrowed';
publishYear?: number;
pageCount?: number;
Expand All @@ -18,3 +28,26 @@ export interface Book
rating?: number;
tags?: string[];
}

export class BookModel extends BiDataTable<Book>() {
client = larkClient;

queryOptions: BiDataQueryOptions = { text_field_as_array: false };

constructor(appId = LibraryBaseId, tableId = BookTableId) {
super(appId, tableId);
}
Comment on lines +32 to +39
}

export class SearchBookModel extends BiSearch<Book>(BookModel) {
searchKeys = [
'title',
'isbn',
'authors',
'summary',
'recommendation',
'tags',
'donors',
'keepers',
];
}
Comment on lines +42 to +53
2 changes: 2 additions & 0 deletions models/System.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Constructor } from 'web-utility';
import { SearchActivityModel } from './Activity';
import { SearchAwardModel } from './Award';
import { ownClient } from './Base';
import { SearchBookModel } from './Book';
import { SearchOrganizationModel } from './Organization';
import { SearchProjectModel } from './Project';

Expand All @@ -24,6 +25,7 @@ export type CityCoordinateMap = Record<string, [number, number]>;
export class SystemModel extends BaseModel {
searchMap = {
activity: SearchActivityModel,
book: SearchBookModel,
project: SearchProjectModel,
award: SearchAwardModel,
NGO: SearchOrganizationModel,
Expand Down
2 changes: 2 additions & 0 deletions models/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ export const LarkBitableId = process.env.NEXT_PUBLIC_LARK_BITABLE_ID!,
ProjectTableId = process.env.NEXT_PUBLIC_PROJECT_TABLE_ID!,
AwardTableId = process.env.NEXT_PUBLIC_AWARD_TABLE_ID!;

export const LibraryBaseId = process.env.NEXT_PUBLIC_LIBRARY_BASE_ID!,
BookTableId = process.env.NEXT_PUBLIC_BOOK_TABLE_ID!;
export const OpenLibraryCatalogURL =
'https://open-source-bazaar.feishu.cn/share/base/view/shrcnvT0Lyk8LKS8KtPbO9HPPHb';
export const OpenLibraryBorrowFormURL =
Expand Down
24 changes: 12 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"@koa/router": "^15.7.0",
"@mdx-js/loader": "^3.1.1",
"@mdx-js/react": "^3.1.1",
"@next/mdx": "16.2.10",
"@next/mdx": "16.2.11",
"core-js": "^3.49.0",
"echarts-jsx": "^0.6.0",
"file-type": "^22.0.1",
Expand All @@ -38,14 +38,14 @@
"mobx-restful": "^2.1.4",
"mobx-restful-table": "^2.6.3",
"mobx-strapi": "^0.8.1",
"next": "16.2.10",
"next": "16.2.11",
"next-pwa": "^5.6.0",
"next-ssr-middleware": "^1.1.1",
"nodemailer": "^9.0.3",
"open-react-map": "^0.9.1",
"react": "^19.2.7",
"react": "^19.2.8",
"react-bootstrap": "^2.10.10",
"react-dom": "^19.2.7",
"react-dom": "^19.2.8",
"react-typed-component": "^1.0.6",
"remark-frontmatter": "^5.0.0",
"remark-mdx-frontmatter": "^5.2.0",
Expand All @@ -55,7 +55,7 @@
"devDependencies": {
"@cspell/eslint-plugin": "^10.0.1",
"@eslint/js": "^10.0.1",
"@next/eslint-plugin-next": "16.2.10",
"@next/eslint-plugin-next": "16.2.11",
"@softonus/prettier-plugin-duplicate-remover": "^1.1.2",
"@stylistic/eslint-plugin": "^5.10.0",
"@types/eslint-config-prettier": "^6.11.3",
Expand All @@ -68,22 +68,22 @@
"@types/react-dom": "^19.2.3",
"cross-env": "^10.1.0",
"eslint": "^10.7.0",
"eslint-config-next": "16.2.10",
"eslint-config-next": "16.2.11",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-simple-import-sort": "^13.0.0",
"eslint-plugin-simple-import-sort": "^14.0.0",
"globals": "^17.7.0",
"husky": "^9.1.7",
"jiti": "^2.7.0",
"less": "^4.6.7",
"less": "^4.8.0",
"less-loader": "^13.0.0",
"lint-staged": "^17.0.8",
"lint-staged": "^17.2.0",
"next-with-less": "^3.0.1",
"prettier": "^3.9.5",
"prettier": "^3.9.6",
Comment thread
TechQuery marked this conversation as resolved.
"prettier-plugin-css-order": "^2.2.0",
"sass": "^1.101.0",
"sass": "^1.101.7",
"typescript": "~5.9.3",
"typescript-eslint": "^8.64.0"
"typescript-eslint": "^8.65.0"
},
"resolutions": {
"next": "$next"
Expand Down
Loading
Loading