diff --git a/src/App.tsx b/src/App.tsx index 9a7e303..91d0f0e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -63,6 +63,7 @@ const ResetPasswordConfirmation = lazy(() => import('./pages/auth/ResetPasswordC const EmailVerification = lazy(() => import('./pages/auth/EmailVerification')); const OauthCallback = lazy(() => import('./pages/auth/OauthCallback')); const About = lazy(() => import('./pages/About')); +const User = lazy(() => import('./pages/User')); const { Content } = Layout; @@ -202,6 +203,7 @@ const App = ({ ui, tuneData }: { ui: UIState, tuneData: TuneDataState }) => { } />} /> } />} /> } />} /> + } />} /> } />} /> } />} /> diff --git a/src/css/App.less b/src/css/App.less index bcd132b..c2a86a7 100644 --- a/src/css/App.less +++ b/src/css/App.less @@ -196,7 +196,8 @@ select:-webkit-autofill:focus { animation: wiggle 2s linear 1; } -.ant-table-row { +.ant-table-row, +.ant-list-item { &.unlisted { opacity: 0.5; } diff --git a/src/hooks/useDb.ts b/src/hooks/useDb.ts index 813b10d..a33d384 100644 --- a/src/hooks/useDb.ts +++ b/src/hooks/useDb.ts @@ -126,6 +126,7 @@ const useDb = () => { const list = await tunesCollection.getList(page, perPage, { sort: '-updated', filter: `author = "${userId}"`, + expand: 'author', }); return Promise.resolve({ diff --git a/src/pages/Hub.tsx b/src/pages/Hub.tsx index 93de021..83098f3 100644 --- a/src/pages/Hub.tsx +++ b/src/pages/Hub.tsx @@ -1,3 +1,4 @@ +import { Link } from 'react-router-dom'; import { Button, Grid, @@ -166,6 +167,11 @@ const Hub = () => { dataIndex: 'authorUsername', key: 'authorUsername', responsive: ['sm'], + render: (userName: string, record: TunesRecordFull) => ( + + {userName} + + ), }, { title: 'Signature', @@ -188,7 +194,7 @@ const Hub = () => { { dataIndex: 'tuneId', fixed: 'right', - render: (tuneId: string, record) => { + render: (tuneId: string, record: TunesRecordFull) => { const isOwner = currentUser?.id === record.author; const size = isOwner ? 'small' : 'middle'; diff --git a/src/pages/User.tsx b/src/pages/User.tsx new file mode 100644 index 0000000..ed1b675 --- /dev/null +++ b/src/pages/User.tsx @@ -0,0 +1,113 @@ +import { + useEffect, + useState, +} from 'react'; +import { + generatePath, + useMatch, + useNavigate, +} from 'react-router-dom'; +import { + Button, + Divider, + List, + Pagination, + Typography, +} from 'antd'; +import { ArrowRightOutlined } from '@ant-design/icons'; +import { Routes } from '../routes'; +import { formatTime } from '../utils/time'; +import useDb from '../hooks/useDb'; +import { aspirationMapper } from '../utils/tune/mappers'; +import { + TunesRecordFull, + UsersRecordFull, +} from '../types/dbData'; + +const tunePath = (tuneId: string) => generatePath(Routes.TUNE_TUNE, { tuneId }); + +const Profile = () => { + const navigate = useNavigate(); + const route = useMatch(Routes.USER_ROOT); + const { getUserTunes } = useDb(); + const [page, setPage] = useState(1); + const [pageSize, setPageSize] = useState(10); + const [total, setTotal] = useState(0); + const [isTunesLoading, setIsTunesLoading] = useState(false); + const [tunesDataSource, setTunesDataSource] = useState([]); + const [username, setUsername] = useState(); + + const loadData = async () => { + setIsTunesLoading(true); + try { + const { items, totalItems } = await getUserTunes(route?.params.userId!, page, pageSize); + setTotal(totalItems); + setUsername((items[0].expand.author as UsersRecordFull).username); + const mapped = items.map((tune) => ({ + ...tune, + key: tune.tuneId, + year: tune.year, + displacement: `${tune.displacement}l`, + aspiration: aspirationMapper[tune.aspiration], + published: formatTime(tune.updated), + })); + setTunesDataSource(mapped as any); + } catch (error) { + // request cancelled + } finally { + setIsTunesLoading(false); + } + }; + + useEffect(() => { + loadData(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [page]); + + return ( +
+ {username ? `${username}'s tunes` : 'No tunes yet'} + ( + } onClick={() => navigate(tunePath(tune.tuneId))} />, + ]} + className={tune.visibility} + > + + {tune.vehicleName} {tune.signature} + } + description={<> + {tune.engineMake}, {tune.engineCode}, {tune.displacement}, {tune.aspiration} + } + /> +
+ {tune.published} +
+
+ )} + footer={ +
+ { + setIsTunesLoading(true); + setPage(newPage); + setPageSize(newPageSize); + }} + /> +
+ } + /> +
+ ); +}; + +export default Profile; diff --git a/src/pages/auth/Profile.tsx b/src/pages/auth/Profile.tsx index c37a04d..ac5f8e6 100644 --- a/src/pages/auth/Profile.tsx +++ b/src/pages/auth/Profile.tsx @@ -217,10 +217,7 @@ const Profile = () => { {tune.vehicleName} {tune.signature} } description={<> - {tune.engineMake}, - {tune.engineCode}, - {tune.displacement}, - {tune.aspiration} + {tune.engineMake}, {tune.engineCode}, {tune.displacement}, {tune.aspiration} } />
diff --git a/src/routes.ts b/src/routes.ts index 08b5022..5705c85 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -26,6 +26,7 @@ export enum Routes { OAUTH_CALLBACK = '/auth/oauth-callback/:provider', ABOUT = '/about', + USER_ROOT = '/user/:userId', REDIRECT_PAGE_OAUTH_CALLBACK = 'oauth', }