Skip to content

Commit c6bb7c0

Browse files
committed
[add] Activity model & card component
[migrate] replace SCSS with LESS for CSS [remove] Undici proxy agent
1 parent 74321b3 commit c6bb7c0

File tree

20 files changed

+312
-152
lines changed

20 files changed

+312
-152
lines changed

.env

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,7 @@ NEXT_PUBLIC_LARK_API_HOST = https://open.feishu.cn/open-apis/
66
NEXT_PUBLIC_LARK_APP_ID = cli_a8094a652022900d
77
NEXT_PUBLIC_LARK_WIKI_URL = https://open-source-bazaar.feishu.cn/wiki/space/7052192153363054596
88

9+
NEXT_PUBLIC_LARK_BITABLE_ID = PNOGbGqhPacsHOsvJqHctS77nje
10+
NEXT_PUBLIC_ACTIVITY_TABLE_ID = tblREEMxDOECZZrK
11+
912
NEXT_PUBLIC_STRAPI_API_HOST = https://china-ngo-db.onrender.com/api/

components/Activity/Card.tsx

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { TimeDistance } from 'idea-react';
2+
import { TableCellLocation } from 'mobx-lark';
3+
import type { FC } from 'react';
4+
import { Card, Col, Row } from 'react-bootstrap';
5+
6+
import { type Activity, ActivityModel } from '../../models/Activity';
7+
import { LarkImage } from '../LarkImage';
8+
import { TimeOption } from '../data';
9+
import { BadgeBar } from 'mobx-restful-table';
10+
11+
export interface ActivityCardProps extends Activity {
12+
className?: string;
13+
}
14+
15+
export const ActivityCard: FC<ActivityCardProps> = ({
16+
className = '',
17+
id,
18+
host,
19+
name,
20+
startTime,
21+
city,
22+
location,
23+
image,
24+
...activity
25+
}) => (
26+
<Card
27+
className={`shadow-sm ${className}`}
28+
style={{ contentVisibility: 'auto', containIntrinsicHeight: '23rem' }}
29+
>
30+
<div className="position-relative w-100" style={{ paddingBottom: '56%' }}>
31+
<div className="position-absolute top-0 left-0 w-100 h-100">
32+
<LarkImage
33+
className="card-img-top h-100 object-fit-cover"
34+
style={{ objectPosition: 'top left' }}
35+
src={image}
36+
/>
37+
</div>
38+
</div>
39+
<Card.Body className="d-flex flex-column">
40+
<Card.Title as="h3" className="h5 flex-fill">
41+
<a
42+
className="text-decoration-none text-secondary text-truncate-lines"
43+
href={ActivityModel.getLink({ id, ...activity })}
44+
>
45+
{name as string}
46+
</a>
47+
</Card.Title>
48+
49+
<Row className="mt-2 flex-fill">
50+
<Col className="text-start">
51+
<Card.Text
52+
className="mt-1 text-truncate"
53+
title={(location as TableCellLocation)?.full_address}
54+
>
55+
<span className="me-1">{city as string}</span>
56+
57+
{(location as TableCellLocation)?.full_address}
58+
</Card.Text>
59+
</Col>
60+
</Row>
61+
<Row as="footer" className="flex-fill small mt-1">
62+
<Col xs={8}>
63+
<BadgeBar
64+
list={(host as string[]).map(text => ({
65+
text,
66+
link: `/search/activity?keywords=${text}`,
67+
}))}
68+
/>
69+
</Col>
70+
<Col className="text-end" xs={4}>
71+
<TimeDistance {...TimeOption} date={startTime as number} />
72+
</Col>
73+
</Row>
74+
</Card.Body>
75+
</Card>
76+
);

components/Navigator/MainNavigator.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Container, Image, Nav, Navbar, NavDropdown } from 'react-bootstrap';
66

77
import { DefaultImage } from '../../models/configuration';
88
import { i18n, I18nContext } from '../../models/Translation';
9+
import { SearchBar } from './SearchBar';
910

1011
const LanguageMenu = dynamic(() => import('./LanguageMenu'), { ssr: false });
1112

@@ -107,7 +108,10 @@ export const MainNavigator: FC<MainNavigatorProps> = observer(({ menu }) => {
107108
)}
108109
</Nav>
109110

110-
<LanguageMenu />
111+
<div className="d-flex justify-content-around gap-3">
112+
<SearchBar />
113+
<LanguageMenu />
114+
</div>
111115
</Navbar.Collapse>
112116
</Container>
113117
</Navbar>

components/Navigator/SearchBar.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,16 @@ import { I18nContext } from '../../models/Translation';
1313
import styles from './SearchBar.module.less';
1414

1515
export interface SearchBarProps
16-
extends Omit<FormProps, 'onChange'>,
16+
extends
17+
Omit<FormProps, 'onChange'>,
1718
Pick<InputGroupProps, 'size'>,
18-
Pick<
19-
FormControlProps,
20-
'name' | 'placeholder' | 'defaultValue' | 'value' | 'onChange'
21-
> {
19+
Pick<FormControlProps, 'name' | 'placeholder' | 'defaultValue' | 'value' | 'onChange'> {
2220
expanded?: boolean;
2321
}
2422

2523
export const SearchBar: FC<SearchBarProps> = observer(
2624
({
27-
action = '/search',
25+
action = '/search/activity',
2826
size,
2927
name = 'keywords',
3028
placeholder,
File renamed without changes.

components/PageContent/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import { MDXProvider } from '@mdx-js/react';
22
import type { FC, PropsWithChildren } from 'react';
33
import { Card, Container } from 'react-bootstrap';
44

5-
import styles from '../../styles/Home.module.scss';
6-
import pageContentStyles from './PageContent.module.scss';
5+
import styles from '../../styles/Home.module.less';
6+
import pageContentStyles from './PageContent.module.less';
77

88
export type PageContentProps = PropsWithChildren<{}>;
99

components/data.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { TimeDistanceProps } from 'idea-react';
2+
3+
export const TimeOption: Pick<TimeDistanceProps, 'unitWords' | 'beforeWord' | 'afterWord'> = {
4+
unitWords: {
5+
ms: '毫秒',
6+
s: '秒',
7+
m: '分',
8+
H: '时',
9+
D: '日',
10+
W: '周',
11+
M: '月',
12+
Y: '年',
13+
},
14+
beforeWord: '前',
15+
afterWord: '后',
16+
};

models/Activity.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import {
2+
BiDataQueryOptions,
3+
BiDataTable,
4+
BiSearch,
5+
normalizeText,
6+
TableCellLink,
7+
TableCellRelation,
8+
TableCellValue,
9+
TableRecord,
10+
} from 'mobx-lark';
11+
12+
import { LarkBase, larkClient } from './Base';
13+
import { ActivityTableId, LarkBitableId } from './configuration';
14+
15+
export type Activity = LarkBase &
16+
Record<
17+
| 'name'
18+
| 'alias'
19+
| 'type'
20+
| 'tags'
21+
| 'summary'
22+
| 'image'
23+
| 'cardImage'
24+
| `${'start' | 'end'}Time`
25+
| 'city'
26+
| 'location'
27+
| 'host'
28+
| 'link'
29+
| 'liveLink'
30+
| `database${'' | 'Schema'}`,
31+
TableCellValue
32+
>;
33+
34+
export class ActivityModel extends BiDataTable<Activity>() {
35+
client = larkClient;
36+
37+
queryOptions: BiDataQueryOptions = { text_field_as_array: false };
38+
39+
constructor(appId = LarkBitableId, tableId = ActivityTableId) {
40+
super(appId, tableId);
41+
}
42+
43+
static getLink = ({
44+
id,
45+
alias,
46+
link,
47+
database,
48+
}: Pick<Activity, 'id' | 'alias' | 'link' | 'database'>) =>
49+
database ? `/activity/${alias || id}` : link + '';
50+
51+
extractFields({ id, fields: { host, city, link, database, ...fields } }: TableRecord<Activity>) {
52+
return {
53+
...fields,
54+
id: id!,
55+
host: (host as TableCellRelation[])?.map(normalizeText),
56+
city: (city as TableCellRelation[])?.map(normalizeText),
57+
link: (link as TableCellLink)?.link,
58+
database: (database as TableCellLink)?.link,
59+
};
60+
}
61+
}
62+
63+
export class SearchActivityModel extends BiSearch<Activity>(ActivityModel) {
64+
searchKeys = ['name', 'alias', 'type', 'tags', 'summary', 'city', 'location', 'host'];
65+
}

models/Base.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ export const makeGithubSearchCondition = (queryMap: DataObject) =>
5151
.map(([key, value]) => `${key}:${value}`)
5252
.join(' ');
5353

54+
export type LarkBase = Record<'id' | 'createdAt' | 'updatedAt', TableCellValue>;
55+
5456
export const larkClient = new HTTPClient({
5557
baseURI: LARK_API_HOST,
5658
responseType: 'json',

models/System.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import { observable } from 'mobx';
22
import { BiSearchModelClass } from 'mobx-lark';
3-
import { BaseModel, ListModel, toggle } from 'mobx-restful';
4-
import { Base, SearchableFilter } from 'mobx-strapi';
3+
import { BaseModel, DataObject, Filter, ListModel, toggle } from 'mobx-restful';
54
import { Constructor } from 'web-utility';
65

6+
import { SearchActivityModel } from './Activity';
77
import { ownClient } from './Base';
88
import { OrganizationModel } from './Organization';
99

10-
export type SearchModel<T extends Base = any> = ListModel<T, SearchableFilter<T>>;
10+
export type SearchableFilter<D extends DataObject> = Filter<D> & {
11+
keywords?: string;
12+
};
13+
export type SearchModel<T extends DataObject = any> = ListModel<T, SearchableFilter<T>>;
1114

1215
export type SearchPageMeta = Pick<
1316
InstanceType<BiSearchModelClass>,
@@ -18,8 +21,9 @@ export type CityCoordinateMap = Record<string, [number, number]>;
1821

1922
export class SystemModel extends BaseModel {
2023
searchMap = {
24+
activity: SearchActivityModel,
2125
NGO: OrganizationModel,
22-
} as Record<string, Constructor<SearchModel<Base>>>;
26+
} as Record<string, Constructor<SearchModel<DataObject>>>;
2327

2428
@observable
2529
accessor screenNarrow = false;

0 commit comments

Comments
 (0)