From 0fe73e3e4a2d6588985fe9d1a7af6c3cb3b939c6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 3 Jan 2026 03:18:45 +0000 Subject: [PATCH 1/8] Initial plan From f7925936770e4588aa819b17c5296ea657aacdc9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 3 Jan 2026 03:33:10 +0000 Subject: [PATCH 2/8] Add team detail page with translations and routing Co-authored-by: TechQuery <19969570+TechQuery@users.noreply.github.com> --- pages/hackathon/[id]/team/[tid].tsx | 182 ++++++++++++++++++++++++++++ translation/en-US.ts | 14 +++ translation/zh-CN.ts | 14 +++ translation/zh-TW.ts | 14 +++ 4 files changed, 224 insertions(+) create mode 100644 pages/hackathon/[id]/team/[tid].tsx diff --git a/pages/hackathon/[id]/team/[tid].tsx b/pages/hackathon/[id]/team/[tid].tsx new file mode 100644 index 0000000..e02ea0b --- /dev/null +++ b/pages/hackathon/[id]/team/[tid].tsx @@ -0,0 +1,182 @@ +import { TableCellUser } from 'mobx-lark'; +import { observer } from 'mobx-react'; +import { cache, compose, errorLogger } from 'next-ssr-middleware'; +import { FC, useContext } from 'react'; +import { Badge, Card, Col, Container, Row, Tab, Tabs } from 'react-bootstrap'; + +import { LarkImage } from '../../../../components/LarkImage'; +import { PageHead } from '../../../../components/Layout/PageHead'; +import { Activity, ActivityModel } from '../../../../models/Activity'; +import { Person, PersonModel, Project, ProjectModel } from '../../../../models/Hackathon'; +import { I18nContext } from '../../../../models/Translation'; + +export const getServerSideProps = compose<{ id: string; tid: string }>( + cache(), + errorLogger, + async ({ params }) => { + const activityModel = new ActivityModel(); + const activity = await activityModel.getOne(params!.id); + + // @ts-expect-error Upstream compatibility + const { appId, tableIdMap } = activity.databaseSchema; + + const projectModel = new ProjectModel(appId, tableIdMap.Project); + const team = await projectModel.getOne(params!.tid); + + const personModel = new PersonModel(appId, tableIdMap.Person); + const allPeople = await personModel.getAll(); + + // Filter team members from all people based on the team's members field + const teamMemberNames = (team.members as string[]) || []; + const teamMembers = allPeople.filter((person) => + teamMemberNames.includes(person.name as string), + ); + + return { + props: { + activity, + team, + teamMembers, + }, + }; + }, +); + +interface TeamPageProps { + activity: Activity; + team: Project; + teamMembers: Person[]; +} + +const TeamPage: FC = observer(({ activity, team, teamMembers }) => { + const { t } = useContext(I18nContext); + + const { name: activityName } = activity; + const { name: displayName, summary: description, createdBy, score } = team; + + const currentRoute = [ + { title: activityName as string, href: `/hackathon/${activityName}` }, + { title: displayName as string }, + ]; + + return ( + + + + + + + + + +

{displayName as string}

+

{description as string}

+ {score !== undefined && ( +
+ + {t('score')}: {score as number} + +
+ )} +
+ +

+ ๐Ÿ‘ฅ {t('team_members')} +

+
    + {teamMembers.map((member) => ( +
  • +
    + +
    +
    {member.name as string}
    + {member.githubLink && ( + + {t('view_github')} + + )} +
    +
    +
  • + ))} +
+
+
+ + + + +
{t('no_news_yet')}
+
+ +
+ {team.products && (team.products as string[]).length > 0 ? ( +
    + {(team.products as string[]).map((product, index) => ( +
  • + +
    {product}
    +
    +
  • + ))} +
+ ) : ( +
+ {t('no_news_yet')} +
+ )} +
+
+
+ +
+ + {createdBy && ( + + + + +

{t('created_by')}

+
+
{(createdBy as TableCellUser).name}
+ + {(createdBy as TableCellUser).email} + +
+
+
+ +
+ )} +
+ ); +}); + +export default TeamPage; diff --git a/translation/en-US.ts b/translation/en-US.ts index 0c272f2..a7579bd 100644 --- a/translation/en-US.ts +++ b/translation/en-US.ts @@ -233,4 +233,18 @@ export default { male: 'Male', female: 'Female', other: 'Other', + + // Team detail page + team_members: 'Team Members', + team_works: 'Team Works', + latest_news: 'Latest News', + no_news_yet: 'No news yet', + join_team: 'Join Team', + leave_team: 'Leave Team', + manage_team: 'Manage Team', + cancel_application: 'Cancel Application', + please_make_sure: 'Please confirm', + success: 'Success', + team_description: 'Team Description', + team_details: 'Team Details', }; diff --git a/translation/zh-CN.ts b/translation/zh-CN.ts index 413eaf4..254c2a6 100644 --- a/translation/zh-CN.ts +++ b/translation/zh-CN.ts @@ -229,4 +229,18 @@ export default { male: '็”ท', female: 'ๅฅณ', other: 'ๅ…ถไป–', + + // Team detail page + team_members: 'ๅ›ข้˜Ÿๆˆๅ‘˜', + team_works: 'ๅ›ข้˜Ÿไฝœๅ“', + latest_news: 'ๆœ€ๆ–ฐๅŠจๆ€', + no_news_yet: 'ๆš‚ๆ— ๅŠจๆ€', + join_team: 'ๅŠ ๅ…ฅๅ›ข้˜Ÿ', + leave_team: '้€€ๅ‡บๅ›ข้˜Ÿ', + manage_team: '็ฎก็†ๅ›ข้˜Ÿ', + cancel_application: 'ๅ–ๆถˆ็”ณ่ฏท', + please_make_sure: '่ฏท็กฎ่ฎค', + success: 'ๆˆๅŠŸ', + team_description: 'ๅ›ข้˜Ÿๆ่ฟฐ', + team_details: 'ๅ›ข้˜Ÿ่ฏฆๆƒ…', }; diff --git a/translation/zh-TW.ts b/translation/zh-TW.ts index 2270b9d..0e9d372 100644 --- a/translation/zh-TW.ts +++ b/translation/zh-TW.ts @@ -229,4 +229,18 @@ export default { male: '็”ท', female: 'ๅฅณ', other: 'ๅ…ถไป–', + + // Team detail page + team_members: 'ๅœ˜้šŠๆˆๅ“ก', + team_works: 'ๅœ˜้šŠไฝœๅ“', + latest_news: 'ๆœ€ๆ–ฐๅ‹•ๆ…‹', + no_news_yet: 'ๆšซ็„กๅ‹•ๆ…‹', + join_team: 'ๅŠ ๅ…ฅๅœ˜้šŠ', + leave_team: '้€€ๅ‡บๅœ˜้šŠ', + manage_team: '็ฎก็†ๅœ˜้šŠ', + cancel_application: 'ๅ–ๆถˆ็”ณ่ซ‹', + please_make_sure: '่ซ‹็ขบ่ช', + success: 'ๆˆๅŠŸ', + team_description: 'ๅœ˜้šŠๆ่ฟฐ', + team_details: 'ๅœ˜้šŠ่ฉณๆƒ…', }; From ab6d52e9e9428f4afdaf93f2281cf8eace0dbe37 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 3 Jan 2026 05:07:48 +0000 Subject: [PATCH 3/8] Add MemberModel, ProductModel, and CommentBox component - Added Member and Product types with corresponding models in Hackathon.ts - Updated team detail page to query members and products using new models - Added CommentBox component placeholder at bottom of team page - Improved product display with links to preview and source code Co-authored-by: TechQuery <19969570+TechQuery@users.noreply.github.com> --- components/Activity/CommentBox.tsx | 13 +++++ models/Hackathon.ts | 38 +++++++++++++++ pages/hackathon/[id]/team/[tid].tsx | 75 +++++++++++++++++++++++++---- 3 files changed, 116 insertions(+), 10 deletions(-) create mode 100644 components/Activity/CommentBox.tsx diff --git a/components/Activity/CommentBox.tsx b/components/Activity/CommentBox.tsx new file mode 100644 index 0000000..cbf51c1 --- /dev/null +++ b/components/Activity/CommentBox.tsx @@ -0,0 +1,13 @@ +import { FC } from 'react'; +import { Card } from 'react-bootstrap'; + +export const CommentBox: FC = () => { + return ( + + +

Comments

+

Comment functionality coming soon...

+
+
+ ); +}; diff --git a/models/Hackathon.ts b/models/Hackathon.ts index 63c2048..bb4f07e 100644 --- a/models/Hackathon.ts +++ b/models/Hackathon.ts @@ -140,3 +140,41 @@ export class ProjectModel extends BiDataTable() { }; } } + +export type Member = LarkBase & + Record<'summary' | 'person' | 'skills' | 'githubAccount' | 'project' | 'status', TableCellValue>; + +export class MemberModel extends BiDataTable() { + client = larkClient; + + queryOptions: BiDataQueryOptions = { text_field_as_array: false }; + + extractFields({ fields: { githubAccount, ...fields }, ...meta }: TableRecord) { + return { + ...meta, + ...fields, + githubAccount: normalizeText(githubAccount as TableCellText), + }; + } +} + +export type Product = LarkBase & + Record< + 'name' | 'project' | 'template' | 'link' | 'sourceLink' | 'file' | 'summary', + TableCellValue + >; + +export class ProductModel extends BiDataTable() { + client = larkClient; + + queryOptions: BiDataQueryOptions = { text_field_as_array: false }; + + extractFields({ fields: { link, sourceLink, ...fields }, ...meta }: TableRecord) { + return { + ...meta, + ...fields, + link: normalizeText(link as TableCellText), + sourceLink: normalizeText(sourceLink as TableCellText), + }; + } +} diff --git a/pages/hackathon/[id]/team/[tid].tsx b/pages/hackathon/[id]/team/[tid].tsx index e02ea0b..335c40a 100644 --- a/pages/hackathon/[id]/team/[tid].tsx +++ b/pages/hackathon/[id]/team/[tid].tsx @@ -4,10 +4,20 @@ import { cache, compose, errorLogger } from 'next-ssr-middleware'; import { FC, useContext } from 'react'; import { Badge, Card, Col, Container, Row, Tab, Tabs } from 'react-bootstrap'; +import { CommentBox } from '../../../../components/Activity/CommentBox'; import { LarkImage } from '../../../../components/LarkImage'; import { PageHead } from '../../../../components/Layout/PageHead'; import { Activity, ActivityModel } from '../../../../models/Activity'; -import { Person, PersonModel, Project, ProjectModel } from '../../../../models/Hackathon'; +import { + Member, + MemberModel, + Person, + PersonModel, + Product, + ProductModel, + Project, + ProjectModel, +} from '../../../../models/Hackathon'; import { I18nContext } from '../../../../models/Translation'; export const getServerSideProps = compose<{ id: string; tid: string }>( @@ -23,20 +33,34 @@ export const getServerSideProps = compose<{ id: string; tid: string }>( const projectModel = new ProjectModel(appId, tableIdMap.Project); const team = await projectModel.getOne(params!.tid); + // Get approved members for this project + const members = await new MemberModel(appId, tableIdMap.Member).getAll({ + project: team.name as string, + status: 'approved', + }); + + // Get person details for members const personModel = new PersonModel(appId, tableIdMap.Person); const allPeople = await personModel.getAll(); - // Filter team members from all people based on the team's members field - const teamMemberNames = (team.members as string[]) || []; + // Filter team members from all people based on member records + const memberPersonNames = members.map((m) => m.person as string); const teamMembers = allPeople.filter((person) => - teamMemberNames.includes(person.name as string), + memberPersonNames.includes(person.name as string), ); + // Get products for this project + const products = await new ProductModel(appId, tableIdMap.Product).getAll({ + project: team.name as string, + }); + return { props: { activity, team, teamMembers, + members, + products, }, }; }, @@ -46,9 +70,11 @@ interface TeamPageProps { activity: Activity; team: Project; teamMembers: Person[]; + members: Member[]; + products: Product[]; } -const TeamPage: FC = observer(({ activity, team, teamMembers }) => { +const TeamPage: FC = observer(({ activity, team, teamMembers, products }) => { const { t } = useContext(I18nContext); const { name: activityName } = activity; @@ -134,12 +160,39 @@ const TeamPage: FC = observer(({ activity, team, teamMembers }) =
- {team.products && (team.products as string[]).length > 0 ? ( + {products && products.length > 0 ? (
    - {(team.products as string[]).map((product, index) => ( -
  • - -
    {product}
    + {products.map((product) => ( +
  • + + +
    {product.name as string}
    + {product.summary && ( +

    {product.summary as string}

    + )} +
    + {product.link && ( + + {t('preview')} + + )} + {product.sourceLink && ( + + {t('source_code')} + + )} +
    +
  • ))} @@ -175,6 +228,8 @@ const TeamPage: FC = observer(({ activity, team, teamMembers }) = )} + + ); }); From e7d9c9500b723d22e1b762d00b25852eab9f8597 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 3 Jan 2026 05:21:41 +0000 Subject: [PATCH 4/8] Refactor team page: remove manual filtering and improve code quality - Removed manual Person filtering since MemberModel query already filters by project - Updated to use Member data directly instead of fetching all People - Destructured fields in callback functions for cleaner code - Used Card body shorthand for simpler markup - Removed unused imports (Person, PersonModel, LarkImage) - Display member info from Member table (person name, summary, githubAccount) Co-authored-by: TechQuery <19969570+TechQuery@users.noreply.github.com> --- pages/hackathon/[id]/team/[tid].tsx | 110 +++++++++++----------------- 1 file changed, 42 insertions(+), 68 deletions(-) diff --git a/pages/hackathon/[id]/team/[tid].tsx b/pages/hackathon/[id]/team/[tid].tsx index 335c40a..4148156 100644 --- a/pages/hackathon/[id]/team/[tid].tsx +++ b/pages/hackathon/[id]/team/[tid].tsx @@ -5,14 +5,11 @@ import { FC, useContext } from 'react'; import { Badge, Card, Col, Container, Row, Tab, Tabs } from 'react-bootstrap'; import { CommentBox } from '../../../../components/Activity/CommentBox'; -import { LarkImage } from '../../../../components/LarkImage'; import { PageHead } from '../../../../components/Layout/PageHead'; import { Activity, ActivityModel } from '../../../../models/Activity'; import { Member, MemberModel, - Person, - PersonModel, Product, ProductModel, Project, @@ -39,16 +36,6 @@ export const getServerSideProps = compose<{ id: string; tid: string }>( status: 'approved', }); - // Get person details for members - const personModel = new PersonModel(appId, tableIdMap.Person); - const allPeople = await personModel.getAll(); - - // Filter team members from all people based on member records - const memberPersonNames = members.map((m) => m.person as string); - const teamMembers = allPeople.filter((person) => - memberPersonNames.includes(person.name as string), - ); - // Get products for this project const products = await new ProductModel(appId, tableIdMap.Product).getAll({ project: team.name as string, @@ -58,7 +45,6 @@ export const getServerSideProps = compose<{ id: string; tid: string }>( props: { activity, team, - teamMembers, members, products, }, @@ -69,12 +55,11 @@ export const getServerSideProps = compose<{ id: string; tid: string }>( interface TeamPageProps { activity: Activity; team: Project; - teamMembers: Person[]; members: Member[]; products: Product[]; } -const TeamPage: FC = observer(({ activity, team, teamMembers, products }) => { +const TeamPage: FC = observer(({ activity, team, members, products }) => { const { t } = useContext(I18nContext); const { name: activityName } = activity; @@ -124,28 +109,21 @@ const TeamPage: FC = observer(({ activity, team, teamMembers, pro ๐Ÿ‘ฅ {t('team_members')}
      - {teamMembers.map((member) => ( -
    • -
      - -
      -
      {member.name as string}
      - {member.githubLink && ( - - {t('view_github')} - - )} -
      + {members.map(({ id, person, summary, githubAccount }) => ( +
    • +
      +
      {person as string}
      + {summary &&

      {summary as string}

      } + {githubAccount && ( + + @{githubAccount as string} + + )}
    • ))} @@ -162,37 +140,33 @@ const TeamPage: FC = observer(({ activity, team, teamMembers, pro
      {products && products.length > 0 ? (
        - {products.map((product) => ( -
      • - - -
        {product.name as string}
        - {product.summary && ( -

        {product.summary as string}

        + {products.map(({ id, name, summary, link, sourceLink }) => ( +
      • + +
        {name as string}
        + {summary &&

        {summary as string}

        } +
        + {link && ( + + {t('preview')} + + )} + {sourceLink && ( + + {t('source_code')} + )} -
        - {product.link && ( - - {t('preview')} - - )} - {product.sourceLink && ( - - {t('source_code')} - - )} -
        - +
      • ))} From 7a537948f48d990dd0f6537cd205f9ffea4b4fed Mon Sep 17 00:00:00 2001 From: TechQuery Date: Sat, 3 Jan 2026 14:53:41 +0800 Subject: [PATCH 5/8] [add] Product Card & Comment Box components [add] Evaluation form modal [fix] missing Main Navigator links --- components/Activity/CommentBox.tsx | 32 ++-- components/Activity/ProductCard.tsx | 44 ++++++ components/Navigator/MainNavigator.tsx | 18 ++- models/Hackathon.ts | 11 +- package.json | 1 + pages/hackathon/[id].tsx | 12 +- pages/hackathon/[id]/team/[tid].tsx | 209 +++++++++++-------------- pnpm-lock.yaml | 59 +++++++ 8 files changed, 254 insertions(+), 132 deletions(-) create mode 100644 components/Activity/ProductCard.tsx diff --git a/components/Activity/CommentBox.tsx b/components/Activity/CommentBox.tsx index cbf51c1..29735df 100644 --- a/components/Activity/CommentBox.tsx +++ b/components/Activity/CommentBox.tsx @@ -1,13 +1,25 @@ -import { FC } from 'react'; -import { Card } from 'react-bootstrap'; +import Giscus from '@giscus/react'; +import { observer } from 'mobx-react'; +import { useContext } from 'react'; + +import { I18nContext } from '../../models/Translation'; + +export const CommentBox = observer(() => { + const { currentLanguage } = useContext(I18nContext); -export const CommentBox: FC = () => { return ( - - -

        Comments

        -

        Comment functionality coming soon...

        -
        -
        + ); -}; +}); diff --git a/components/Activity/ProductCard.tsx b/components/Activity/ProductCard.tsx new file mode 100644 index 0000000..a2307ec --- /dev/null +++ b/components/Activity/ProductCard.tsx @@ -0,0 +1,44 @@ +import { observer } from 'mobx-react'; +import { FilePreview } from 'mobx-restful-table'; +import { FC } from 'react'; +import { CardProps, Card, Button } from 'react-bootstrap'; +import { formatDate } from 'web-utility'; + +import { Product } from '../../models/Hackathon'; + +export type ProductCardProps = Product & Omit; + +export const ProductCard: FC = observer( + ({ className = '', id, createdAt, name, sourceLink, link = sourceLink, summary, ...props }) => ( + + + + {(name || link) as string} + +

        {summary as string}

        +
        + + + {sourceLink && ( + + )} +
        + +
        +
        + ), +); diff --git a/components/Navigator/MainNavigator.tsx b/components/Navigator/MainNavigator.tsx index 76b0824..4147ae7 100644 --- a/components/Navigator/MainNavigator.tsx +++ b/components/Navigator/MainNavigator.tsx @@ -1,3 +1,4 @@ +import { textJoin } from 'mobx-i18n'; import { observer } from 'mobx-react'; import dynamic from 'next/dynamic'; import { useRouter } from 'next/router'; @@ -39,11 +40,24 @@ const topNavBarMenu = ({ t }: typeof i18n): MenuItem[] => [ subs: [ { href: '/project', title: t('open_source_projects') }, { href: '/issue', title: 'GitHub issues' }, + { href: '/license-filter', title: t('license_filter') }, + ], + }, + { + title: t('hackathon'), + subs: [ { href: 'https://github.com/Open-Source-Bazaar/Git-Hackathon-scaffold', - title: t('hackathon'), + title: textJoin('GitHub', t('hackathon')), + }, + { + href: '/search/activity?keywords=Hackathon', + title: textJoin('Lark', t('hackathon')), + }, + { + href: 'https://test.hackathon.fcc-cd.dev/open-source', + title: textJoin(t('hackathon'), t('open_source_projects')), }, - { href: '/license-filter', title: t('license_filter') }, ], }, { diff --git a/models/Hackathon.ts b/models/Hackathon.ts index bb4f07e..a7afb37 100644 --- a/models/Hackathon.ts +++ b/models/Hackathon.ts @@ -6,6 +6,7 @@ import { normalizeText, TableCellRelation, TableCellText, + TableCellUser, TableCellValue, TableRecord, } from 'mobx-lark'; @@ -24,7 +25,7 @@ export class AgendaModel extends BiDataTable() { return { ...meta, ...fields, - summary: normalizeText(summary as TableCellText), + summary: (summary as TableCellText[])!.map(normalizeText), }; } } @@ -149,10 +150,16 @@ export class MemberModel extends BiDataTable() { queryOptions: BiDataQueryOptions = { text_field_as_array: false }; - extractFields({ fields: { githubAccount, ...fields }, ...meta }: TableRecord) { + extractFields({ + fields: { summary, person, skills, githubAccount, ...fields }, + ...meta + }: TableRecord) { return { ...meta, ...fields, + person: (person as TableCellUser[])?.[0], + summary: (summary as TableCellText[])!.map(normalizeText), + skills: skills?.toString().split(/\s*,\s*/) || [], githubAccount: normalizeText(githubAccount as TableCellText), }; } diff --git a/package.json b/package.json index 4679f60..c8e004d 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "test": "lint-staged && tsc --noEmit" }, "dependencies": { + "@giscus/react": "^3.1.0", "@koa/router": "^15.1.1", "@mdx-js/loader": "^3.1.1", "@mdx-js/react": "^3.1.1", diff --git a/pages/hackathon/[id].tsx b/pages/hackathon/[id].tsx index 98e6ec7..e8b87ac 100644 --- a/pages/hackathon/[id].tsx +++ b/pages/hackathon/[id].tsx @@ -1,5 +1,6 @@ import { BiTableSchema, TableCellLocation, TableCellUser } from 'mobx-lark'; import { observer } from 'mobx-react'; +import Link from 'next/link'; import { cache, compose, errorLogger } from 'next-ssr-middleware'; import { FC, useContext } from 'react'; import { Badge, Card, Col, Container, Row } from 'react-bootstrap'; @@ -199,11 +200,18 @@ const HackathonDetail: FC = observer(({ activity, hackatho

        ๐Ÿ’ก {t('projects')}

        - {projects.map(({ name, score, summary, createdBy, members }) => ( + {projects.map(({ id, name, score, summary, createdBy, members }) => (
        -
        {name as string}
        +
        + + {name as string} + +
        {score as number}

        {summary as string}

        diff --git a/pages/hackathon/[id]/team/[tid].tsx b/pages/hackathon/[id]/team/[tid].tsx index 4148156..c40b453 100644 --- a/pages/hackathon/[id]/team/[tid].tsx +++ b/pages/hackathon/[id]/team/[tid].tsx @@ -1,10 +1,23 @@ -import { TableCellUser } from 'mobx-lark'; +import { Avatar } from 'idea-react'; +import { BiTableSchema, TableCellUser } from 'mobx-lark'; import { observer } from 'mobx-react'; import { cache, compose, errorLogger } from 'next-ssr-middleware'; -import { FC, useContext } from 'react'; -import { Badge, Card, Col, Container, Row, Tab, Tabs } from 'react-bootstrap'; +import { FC, useContext, useState } from 'react'; +import { + Breadcrumb, + Button, + Card, + Col, + Container, + Modal, + Ratio, + Row, + Tab, + Tabs, +} from 'react-bootstrap'; import { CommentBox } from '../../../../components/Activity/CommentBox'; +import { ProductCard } from '../../../../components/Activity/ProductCard'; import { PageHead } from '../../../../components/Layout/PageHead'; import { Activity, ActivityModel } from '../../../../models/Activity'; import { @@ -17,56 +30,49 @@ import { } from '../../../../models/Hackathon'; import { I18nContext } from '../../../../models/Translation'; -export const getServerSideProps = compose<{ id: string; tid: string }>( +export const getServerSideProps = compose>( cache(), errorLogger, async ({ params }) => { - const activityModel = new ActivityModel(); - const activity = await activityModel.getOne(params!.id); + const activity = await new ActivityModel().getOne(params!.id); // @ts-expect-error Upstream compatibility const { appId, tableIdMap } = activity.databaseSchema; - const projectModel = new ProjectModel(appId, tableIdMap.Project); - const team = await projectModel.getOne(params!.tid); + const project = await new ProjectModel(appId, tableIdMap.Project).getOne(params!.tid); // Get approved members for this project const members = await new MemberModel(appId, tableIdMap.Member).getAll({ - project: team.name as string, + project: project.name as string, status: 'approved', }); // Get products for this project const products = await new ProductModel(appId, tableIdMap.Product).getAll({ - project: team.name as string, + project: project.name as string, }); - return { - props: { - activity, - team, - members, - products, - }, - }; + return { props: { activity, project, members, products } }; }, ); -interface TeamPageProps { +interface ProjectPageProps { activity: Activity; - team: Project; + project: Project; members: Member[]; products: Product[]; } -const TeamPage: FC = observer(({ activity, team, members, products }) => { +const ProjectPage: FC = observer(({ activity, project, members, products }) => { const { t } = useContext(I18nContext); + const [showScoreModal, setShowScoreModal] = useState(false); - const { name: activityName } = activity; - const { name: displayName, summary: description, createdBy, score } = team; + const { name: activityName, databaseSchema } = activity; + const { formLinkMap } = databaseSchema as unknown as BiTableSchema; + const { name: displayName, summary: description, createdBy, score } = project; const currentRoute = [ - { title: activityName as string, href: `/hackathon/${activityName}` }, + { title: activityName as string, href: ActivityModel.getLink(activity) }, { title: displayName as string }, ]; @@ -74,52 +80,48 @@ const TeamPage: FC = observer(({ activity, team, members, product - - - + + {currentRoute.map(({ title, href }, index, { length }) => { + const isActive = index === length - 1; + + return ( + + {title} + + ); + })} + + + - +

        {displayName as string}

        {description as string}

        - {score !== undefined && ( + {score != null && (
        - +
        )}
        -

        - ๐Ÿ‘ฅ {t('team_members')} -

        +

        ๐Ÿ‘ฅ {t('team_members')}

          - {members.map(({ id, person, summary, githubAccount }) => ( -
        • + {members.map(({ id, person, githubAccount }) => ( +
        • + {/* @ts-expect-error Upstream compatibility */} +
          -
          {person as string}
          - {summary &&

          {summary as string}

          } +

          {(person as TableCellUser).name}

          + {githubAccount && ( @{githubAccount as string} @@ -132,80 +134,55 @@ const TeamPage: FC = observer(({ activity, team, members, product - +
          {t('no_news_yet')}
          -
          - {products && products.length > 0 ? ( -
            - {products.map(({ id, name, summary, link, sourceLink }) => ( -
          • - -
            {name as string}
            - {summary &&

            {summary as string}

            } -
            - {link && ( - - {t('preview')} - - )} - {sourceLink && ( - - {t('source_code')} - - )} -
            -
            -
          • - ))} -
          - ) : ( -
          - {t('no_news_yet')} -
          - )} -
          + {products && products.length > 0 ? ( + + {products.map(product => ( + + + + ))} + + ) : ( +
          {t('no_news_yet')}
          + )}
          - {createdBy && ( - - - - -

          {t('created_by')}

          -
          -
          {(createdBy as TableCellUser).name}
          - - {(createdBy as TableCellUser).email} - -
          -
          -
          - -
          - )} + +

          {t('created_by')}

          +
          +
          {(createdBy as TableCellUser).name}
          + + {(createdBy as TableCellUser).email} + +
          +
          + + setShowScoreModal(false)}> + + {t('score')} + + + +