Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
d6aed50
Initial plan
Copilot Sep 6, 2025
495d672
Fix build issues and prepare for project evaluation page implementation
Copilot Sep 6, 2025
e78d115
Complete implementation of project evaluation page with chat interface
Copilot Sep 6, 2025
75e3479
Implement all user feedback - JSX rendering, class component, ScrollL…
Copilot Sep 7, 2025
44cb859
Implement user feedback - remove compatibility layer, move ProjectCar…
Copilot Sep 8, 2025
9ed840a
Address user feedback: Fix translations, EvaluationDisplay component,…
Copilot Sep 8, 2025
a16c2e8
Address code review feedback: Update EvaluationDisplay to use semanti…
Copilot Sep 8, 2025
a632ece
Request clarification on RequirementEvaluation type structure
Copilot Sep 8, 2025
f200531
[fix] some detail bugs of Koa router & Back-end API
TechQuery Sep 9, 2025
dd9a343
Fix @idea2app/data-server package integration - update EvaluationDisp…
Copilot Sep 9, 2025
19b1a0b
[fix] several GitHub copilot bugs
TechQuery Sep 9, 2025
20fa8db
Addressing PR comments
Copilot Sep 9, 2025
7a9a6a6
Add new project creation to dashboard and commercial version link to …
Copilot Sep 9, 2025
a7971f8
Address PR comments: Remove test file, unified requirement form with …
Copilot Sep 9, 2025
ba01d8a
Address PR feedback: Extract VersionComparison component, use proper …
Copilot Sep 9, 2025
9b542f9
[fix] ESLint sorting of TypeScript importing
TechQuery Sep 9, 2025
0c10252
Add chat input interface to project evaluation page
Copilot Sep 9, 2025
c7e69d1
Implement advanced message handling with local caching, auto-scroll, …
Copilot Sep 10, 2025
59c3d32
Address PR feedback: Fix models with lodash.debounce, proper scroll t…
Copilot Sep 10, 2025
2ae6715
Fix TypeScript lint issues: sort imports and replace any type with pr…
Copilot Sep 10, 2025
1f63b76
[fix] many GitHub copilot bugs
TechQuery Sep 10, 2025
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
112 changes: 112 additions & 0 deletions components/Project/EvaluationDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { RequirementEvaluation, UserRole } from '@idea2app/data-server';
import { Box, Typography } from '@mui/material';
import { observer } from 'mobx-react';
import { FC, useContext } from 'react';

import { I18nContext } from '../../models/Translation';
import userStore from '../../models/User';

export const EvaluationDisplay: FC<RequirementEvaluation> = observer(
({
title,
scopes = [],
developerCount,
designerCount,
workload,
monthPeriod,
budget,
factor,
}) => {
const { t } = useContext(I18nContext),
{ roles } = userStore.session || {};

return (
<Box
sx={{
'& .evaluation-item': {
marginBottom: 1,
fontSize: '0.875rem',
padding: '4px 0',
borderLeft: '3px solid',
borderColor: 'primary.light',
paddingLeft: 1,
},
'& ul': {
margin: '8px 0',
paddingLeft: '20px',
},
'& li': {
marginBottom: '4px',
},
}}
>
{title && (
<Box className="evaluation-item">
<Typography component="h3" sx={{ fontWeight: 600, mb: 1 }}>
{title}
</Typography>
</Box>
)}
{scopes[0] && (
<Box className="evaluation-item">
<Typography component="h4" sx={{ fontWeight: 600 }}>
{t('development_scopes')}
</Typography>
<Box component="ul" sx={{ mt: 0.5 }}>
{scopes.map(scope => (
<Box key={scope} component="li" sx={{ ml: 1 }}>
{scope}
</Box>
))}
</Box>
</Box>
)}
{workload && (
<Box className="evaluation-item">
<Typography component="h4" sx={{ fontWeight: 600 }}>
{t('workload')}
</Typography>{' '}
{workload} {t('hours')}
</Box>
)}
{monthPeriod && (
<Box className="evaluation-item">
<Typography component="h4" sx={{ fontWeight: 600 }}>
{t('timeline')}
</Typography>{' '}
{monthPeriod} {t('months')}
</Box>
)}
{budget && (
<Box className="evaluation-item">
<Typography component="h4" sx={{ fontWeight: 600 }}>
{t('budget')}
</Typography>{' '}
RMB¥{budget.toLocaleString()}
</Box>
)}
{(developerCount || designerCount) && (
<Box className="evaluation-item">
<Typography component="h4" sx={{ fontWeight: 600 }}>
{t('team_size')}
</Typography>{' '}
{[
developerCount && `${developerCount} ${t('developers')}`,
designerCount && `${designerCount} ${t('designers')}`,
]
.filter(Boolean)
.join(', ')}
</Box>
)}
{roles && roles.includes(2 as UserRole.Client) && (
<Box className="evaluation-item">
<Typography component="h4" sx={{ fontWeight: 600 }}>
{t('complexity_factor')}
</Typography>{' '}
{factor}
</Box>
)}
</Box>
);
},
);
52 changes: 52 additions & 0 deletions components/Project/NewCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Project } from '@idea2app/data-server';
import { Button, Card, CardActions, CardContent, Typography } from '@mui/material';
import { observer } from 'mobx-react';
import Link from 'next/link';
import { FC, useContext } from 'react';

import { I18nContext } from '../../models/Translation';

export const ProjectCard: FC<Project> = observer(({ id, name, projectStatus }) => {
const { t } = useContext(I18nContext);

return (
<Card>
<CardContent>
<Typography variant="h6" component="h3" gutterBottom>
{name}
</Typography>
<Typography
variant="caption"
sx={{
px: 1,
py: 0.5,
borderRadius: 1,
bgcolor:
projectStatus === '1'
? 'success.light'
: projectStatus === '0'
? 'grey.200'
: 'warning.light',
color:
projectStatus === '1'
? 'success.contrastText'
: projectStatus === '0'
? 'text.primary'
: 'warning.contrastText',
}}
>
{projectStatus === '1'
? t('project_open')
: projectStatus === '0'
? t('project_closed')
: t('project_pending')}
</Typography>
</CardContent>
<CardActions>
<Button component={Link} href={`/dashboard/project/${id}`} size="small" variant="contained">
{t('view_evaluation')}
</Button>
</CardActions>
</Card>
);
});
2 changes: 1 addition & 1 deletion components/ScrollList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { debounce } from 'lodash';
import debounce from 'lodash.debounce';
import { when } from 'mobx';
import { TranslationModel } from 'mobx-i18n';
import { observer } from 'mobx-react';
Expand Down
47 changes: 47 additions & 0 deletions components/VersionComparison.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Box, Card, CardContent, Grid, Typography } from '@mui/material';
import { observer } from 'mobx-react';
import { FC, useContext } from 'react';

import { I18nContext } from '../models/Translation';

export const VersionComparison: FC = observer(() => {
const { t } = useContext(I18nContext);

return (
<Grid container spacing={4} sx={{ mt: 4 }}>
<Grid size={{ xs: 12, md: 6 }}>
<Card sx={{ height: '100%' }}>
<CardContent>
<Typography variant="h5" component="h2" gutterBottom>
{t('public_version')}
</Typography>
<Box component="ol" sx={{ pl: 2 }}>
<Typography component="li">{t('github_one_click_login')}</Typography>
<Typography component="li">{t('free_evaluation_daily_limit')}</Typography>
<Typography component="li">{t('submitted_data_public')}</Typography>
<Typography component="li">{t('volunteer_community_support')}</Typography>
<Typography component="li">{t('open_source_bounty_development')}</Typography>
</Box>
</CardContent>
</Card>
</Grid>

<Grid size={{ xs: 12, md: 6 }}>
<Card sx={{ height: '100%' }}>
<CardContent>
<Typography variant="h5" component="h2" gutterBottom>
{t('commercial_version')}
</Typography>
<Box component="ol" sx={{ pl: 2 }}>
<Typography component="li">{t('phone_one_click_register')}</Typography>
<Typography component="li">{t('unlimited_evaluation_24_7')}</Typography>
<Typography component="li">{t('project_data_confidential')}</Typography>
<Typography component="li">{t('daily_engineer_review')}</Typography>
<Typography component="li">{t('professional_development_team')}</Typography>
</Box>
</CardContent>
</Card>
</Grid>
</Grid>
);
});
3 changes: 1 addition & 2 deletions models/Issue.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { isEmpty } from 'lodash';
import { Issue } from 'mobx-github';
import { Filter, ListModel } from 'mobx-restful';
import { buildURLData } from 'web-utility';
import { buildURLData, isEmpty } from 'web-utility';

import { githubClient } from './Base';

Expand Down
53 changes: 53 additions & 0 deletions models/ProjectEvaluation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { ConsultMessage, Project, ProjectFilter, UserBaseFilter } from '@idea2app/data-server';
import debounce from 'lodash.debounce';
import { IDType, NewData, toggle } from 'mobx-restful';
import { Second } from 'web-utility';

import { TableModel } from './Base';
import userStore from './User';

export type ExtendedProjectFilter = ProjectFilter & UserBaseFilter;

export class ProjectModel extends TableModel<Project, ExtendedProjectFilter> {
baseURI = 'project';
client = userStore.client;
}

export class ConsultMessageModel extends TableModel<ConsultMessage> {
baseURI = '';
client = userStore.client;

constructor(public projectId: number) {
super();
this.baseURI = `project/${projectId}/consult-message`;
}

@toggle('uploading')
async updateOne({ content }: Partial<NewData<ConsultMessage>>, id?: IDType) {
const { allItems } = this;

const newMessage = {
id: Date.now(),
content,
createdAt: new Date().toJSON(),
createdBy: userStore.session!,
project: { id: this.projectId },
} as ConsultMessage;

this.restoreList({ allItems: [...allItems, newMessage] });

const message = await super.updateOne({ content });

this.restoreList({ allItems: [...allItems, message] });

this.triggerEvaluation();

return message;
}

private triggerEvaluation = debounce(async () => {
const { body } = await this.client.post<ConsultMessage>(`${this.baseURI}/evaluation`);

this.restoreList({ allItems: [...this.allItems, body!] });
}, Second);
}
44 changes: 21 additions & 23 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,30 @@
"@emotion/styled": "^11.14.1",
"@giscus/react": "^3.1.0",
"@koa/bodyparser": "^6.0.0",
"@koa/router": "^14.0.0",
"@mui/lab": "^7.0.0-beta.16",
"@mui/material": "^7.3.1",
"@mui/material-nextjs": "^7.3.0",
"@mui/lab": "^7.0.0-beta.17",
"@mui/material": "^7.3.2",
"@mui/material-nextjs": "^7.3.2",
"@passwordless-id/webauthn": "^2.3.1",
"@sentry/nextjs": "^10.8.0",
"@sentry/nextjs": "^10.11.0",
"file-type": "^21.0.0",
"idb-keyval": "^6.2.2",
"jsonwebtoken": "^9.0.2",
"koa": "^3.0.1",
"koa-jwt": "^4.0.4",
"koajax": "^3.1.2",
"lodash": "^4.17.21",
"lodash.debounce": "^4.0.8",
"marked": "^16.2.1",
"mime": "^4.0.7",
"mobx": "^6.13.7",
"mobx-github": "^0.4.0",
"mobx-github": "^0.5.1",
"mobx-i18n": "^0.7.1",
"mobx-lark": "^2.4.0",
"mobx-lark": "^2.4.1",
"mobx-react": "^9.2.0",
"mobx-react-helper": "^0.5.1",
"mobx-restful": "^2.1.0",
"next": "^15.5.2",
"next-pwa": "~5.6.0",
"next-ssr-middleware": "^1.0.2",
"next-ssr-middleware": "^1.0.3",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"web-utility": "^4.5.3",
Expand All @@ -47,39 +46,38 @@
"@babel/plugin-proposal-decorators": "^7.28.0",
"@babel/plugin-transform-typescript": "^7.28.0",
"@babel/preset-react": "^7.27.1",
"@cspell/eslint-plugin": "^9.2.0",
"@cspell/eslint-plugin": "^9.2.1",
"@eslint/compat": "^1.3.2",
"@eslint/js": "^9.34.0",
"@idea2app/data-server": "^1.0.0-rc.0",
"@eslint/js": "^9.35.0",
"@idea2app/data-server": "1.0.0-rc.1",
"@next/eslint-plugin-next": "^15.5.2",
"@stylistic/eslint-plugin": "^5.2.3",
"@tailwindcss/postcss": "^4.1.12",
"@stylistic/eslint-plugin": "^5.3.1",
"@tailwindcss/postcss": "^4.1.13",
"@tailwindcss/typography": "^0.5.16",
"@types/eslint-config-prettier": "^6.11.3",
"@types/jsonwebtoken": "^9.0.10",
"@types/koa": "^3.0.0",
"@types/koa__router": "^12.0.4",
"@types/lodash": "^4.17.20",
"@types/lodash.debounce": "^4.0.9",
"@types/next-pwa": "^5.6.9",
"@types/node": "^22.18.0",
"@types/node": "^22.18.1",
"@types/react": "^19.1.12",
"eslint": "^9.34.0",
"eslint": "^9.35.0",
"eslint-config-next": "^15.5.2",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-simple-import-sort": "^12.1.1",
"git-utility": "^0.2.0",
"globals": "^16.3.0",
"globals": "^16.4.0",
"husky": "^9.1.7",
"jiti": "^2.5.1",
"lint-staged": "^16.1.5",
"lint-staged": "^16.1.6",
"postcss": "^8.5.6",
"prettier": "^3.6.2",
"prettier-plugin-css-order": "^2.1.2",
"prettier-plugin-tailwindcss": "^0.6.14",
"tailwindcss": "^4.1.12",
"tailwindcss": "^4.1.13",
"typescript": "~5.9.2",
"typescript-eslint": "^8.41.0"
"typescript-eslint": "^8.43.0"
},
"resolutions": {
"mobx-github": "$mobx-github",
Expand Down Expand Up @@ -108,7 +106,7 @@
"*.{html,md,less,json,yml,js,mjs,ts,tsx}": "prettier --write"
},
"scripts": {
"e": "pnpx dotenv-cli -e .env.personal.local -- pnpm",
"e": "pnpx @dotenvx/dotenvx run -f .env.personal.local -- pnpm",
"prepare": "husky || true",
"install": "xgit download https://github.com/idea2app/key-vault main idea2app.github.io || true",
"dev": "next dev",
Expand Down
Loading