Skip to content

Commit 1074236

Browse files
authored
Merge pull request #1918 from line/dev
release: 8.2532.96
2 parents f8d05f8 + b90eda5 commit 1074236

302 files changed

Lines changed: 18216 additions & 6576 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.nvmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
22.16.0
1+
22.18.0

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,17 @@ For more detailed setup instructions, refer to the [Getting Started](#getting-st
4848
| ![Image 1](./assets/01-feedback-tag.png) | ![Image 2](./assets/02-Issue-Kanban.png) |
4949
| ------------------------------------------- | ----------------------------------------- |
5050
| ![Image 3](./assets/03-issue-tracker.png) | ![Image 4](./assets/04-single-signon.png) |
51-
| ![Image 3](./assets/05-role-management.png) | ![Image 4](./assets/06-dashboard.png) |
51+
| ![Image 5](./assets/05-role-management.png) | ![Image 6](./assets/06-dashboard.png) |
52+
| ![Image 7](./assets/07-ai-field.png) | ![Image 8](./assets/08-ai-issue.png) |
5253

5354
- **Feedback Tag**: You can assign tags to each feedback to categorize them by topic.
5455
- **Kanban Mode**: Experience the advantage of organizing and visualizing issue groups efficiently with Kanban mode.
5556
- **Issue Tracker**: The Issue feature has a status indicator that lets you use it as a simple issue tracker. You can also link each issue to a ticket in your own issue tracker system.
5657
- **Single Sign-on**: Authentication offers OAuth to accommodate enterprise-level single sign-on (SSO) requirements.
5758
- **Role Management**: Role Based Access Control (RBAC)
5859
- **Dashboard**: Dashboard help you visualize statistical data about your feedback and issues so you can learn about them at a glance.
60+
- **🤖 AI Field**: Write custom prompts to get AI-powered analysis of your feedback data. Examples include summarization, translation, keyword extraction, and sentiment analysis.
61+
- **🤖 AI Issue Recommandation**: Get intelligent issue recommendations based on feedback analysis using AI-powered insights.
5962

6063
## Getting Started
6164

apps/api/package.json

Lines changed: 54 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -26,89 +26,89 @@
2626
},
2727
"prettier": "@ufb/prettier-config",
2828
"dependencies": {
29-
"@aws-sdk/client-s3": "^3.750.0",
30-
"@aws-sdk/s3-request-presigner": "^3.750.0",
31-
"@fastify/multipart": "^9.0.0",
32-
"@fastify/static": "^8.0.0",
33-
"@nestjs-modules/mailer": "^2.0.0",
34-
"@nestjs/axios": "^4.0.0",
35-
"@nestjs/common": "^11.0.0",
36-
"@nestjs/config": "^4.0.0",
37-
"@nestjs/core": "^11.0.0",
38-
"@nestjs/event-emitter": "^3.0.0",
29+
"@aws-sdk/client-s3": "^3.859.0",
30+
"@aws-sdk/s3-request-presigner": "^3.859.0",
31+
"@fastify/multipart": "^9.0.3",
32+
"@fastify/static": "^8.2.0",
33+
"@nestjs-modules/mailer": "^2.0.2",
34+
"@nestjs/axios": "^4.0.1",
35+
"@nestjs/common": "^11.1.5",
36+
"@nestjs/config": "^4.0.2",
37+
"@nestjs/core": "^11.1.5",
38+
"@nestjs/event-emitter": "^3.0.1",
3939
"@nestjs/jwt": "^11.0.0",
40-
"@nestjs/passport": "^11.0.0",
41-
"@nestjs/platform-express": "^11.0.0",
42-
"@nestjs/platform-fastify": "^11.0.0",
40+
"@nestjs/passport": "^11.0.5",
41+
"@nestjs/platform-express": "^11.1.5",
42+
"@nestjs/platform-fastify": "^11.1.5",
4343
"@nestjs/schedule": "^6.0.0",
44-
"@nestjs/swagger": "^11.0.0",
44+
"@nestjs/swagger": "^11.2.0",
4545
"@nestjs/terminus": "^11.0.0",
4646
"@nestjs/typeorm": "^11.0.0",
47-
"@opensearch-project/opensearch": "^3.0.0",
47+
"@opensearch-project/opensearch": "^3.5.1",
4848
"@types/passport-jwt": "^4.0.1",
4949
"@types/passport-local": "^1.0.38",
5050
"@ufb/shared": "workspace:*",
51-
"@willsoto/nestjs-prometheus": "^6.0.0",
52-
"axios": "^1.7.2",
51+
"@willsoto/nestjs-prometheus": "^6.0.2",
52+
"axios": "^1.11.0",
5353
"bcrypt": "^6.0.0",
5454
"class-transformer": "^0.5.1",
55-
"class-validator": "^0.14.1",
55+
"class-validator": "^0.14.2",
5656
"cron": "^4.3.0",
57-
"dotenv": "^16.4.5",
57+
"dotenv": "^17.2.1",
5858
"exceljs": "^4.4.0",
59-
"fast-csv": "^5.0.1",
60-
"fastify": "^5.0.0",
61-
"joi": "^17.12.3",
62-
"luxon": "^3.4.4",
63-
"magic-bytes.js": "^1.10.0",
64-
"mysql2": "^3.9.7",
65-
"nestjs-cls": "^5.0.0",
66-
"nestjs-pino": "^4.0.0",
67-
"nestjs-typeorm-paginate": "^4.0.4",
68-
"nodemailer": "^7.0.0",
59+
"fast-csv": "^5.0.5",
60+
"fastify": "^5.4.0",
61+
"joi": "^18.0.0",
62+
"luxon": "^3.7.1",
63+
"magic-bytes.js": "^1.12.1",
64+
"mysql2": "^3.14.3",
65+
"nestjs-cls": "^6.0.1",
66+
"nestjs-pino": "^4.4.0",
67+
"nestjs-typeorm-paginate": "^4.1.0",
68+
"nodemailer": "^7.0.5",
6969
"passport": "^0.7.0",
7070
"passport-custom": "^1.1.1",
7171
"passport-jwt": "^4.0.1",
7272
"passport-local": "^1.0.0",
73-
"pino-http": "^10.0.0",
74-
"pino-pretty": "^13.0.0",
75-
"prom-client": "^15.1.2",
73+
"pino-http": "^10.5.0",
74+
"pino-pretty": "^13.1.1",
75+
"prom-client": "^15.1.3",
7676
"reflect-metadata": "^0.2.2",
77-
"rxjs": "^7.8.1",
77+
"rxjs": "^7.8.2",
7878
"source-map-support": "^0.5.21",
79-
"typeorm": "^0.3.20",
79+
"typeorm": "^0.3.25",
8080
"typeorm-naming-strategies": "^4.1.0",
8181
"typeorm-transactional": "^0.5.0",
82-
"uuid": "^11.0.0"
82+
"uuid": "^11.1.0"
8383
},
8484
"devDependencies": {
85-
"@faker-js/faker": "^9.0.0",
86-
"@nestjs/cli": "^11.0.0",
87-
"@nestjs/schematics": "^11.0.0",
88-
"@nestjs/testing": "^11.0.0",
89-
"@swc-node/jest": "^1.8.0",
90-
"@swc/cli": "0.7.7",
85+
"@faker-js/faker": "^9.9.0",
86+
"@nestjs/cli": "^11.0.10",
87+
"@nestjs/schematics": "^11.0.7",
88+
"@nestjs/testing": "^11.1.5",
89+
"@swc-node/jest": "^1.8.13",
90+
"@swc/cli": "0.7.8",
9191
"@swc/core": "^1.4.16",
92-
"@swc/helpers": "^0.5.10",
93-
"@types/bcrypt": "^5.0.2",
94-
"@types/express": "^5.0.2",
95-
"@types/jest": "^29.5.12",
96-
"@types/luxon": "^3.4.2",
97-
"@types/node": "22.15.23",
98-
"@types/nodemailer": "^6.4.15",
92+
"@swc/helpers": "^0.5.17",
93+
"@types/bcrypt": "^6.0.0",
94+
"@types/express": "^5.0.3",
95+
"@types/jest": "^30.0.0",
96+
"@types/luxon": "^3.7.1",
97+
"@types/node": "22.17.0",
98+
"@types/nodemailer": "^6.4.17",
9999
"@types/passport-jwt": "*",
100-
"@types/supertest": "^6.0.2",
101-
"@typescript-eslint/parser": "^8.0.0",
100+
"@types/supertest": "^6.0.3",
101+
"@typescript-eslint/parser": "^8.39.0",
102102
"@ufb/eslint-config": "workspace:*",
103103
"@ufb/prettier-config": "workspace:*",
104104
"@ufb/tsconfig": "workspace:*",
105105
"eslint": "catalog:",
106-
"jest": "^29.7.0",
106+
"jest": "^30.0.5",
107107
"mockdate": "^3.0.5",
108108
"prettier": "catalog:",
109-
"supertest": "^7.0.0",
110-
"ts-jest": "^29.1.2",
111-
"ts-loader": "^9.5.1",
109+
"supertest": "^7.1.4",
110+
"ts-jest": "^29.4.1",
111+
"ts-loader": "^9.5.2",
112112
"ts-node": "^10.9.2",
113113
"tsconfig-paths": "^4.2.0",
114114
"typescript": "catalog:"

apps/api/src/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import { FieldModule } from './domains/admin/channel/field/field.module';
4141
import { OptionModule } from './domains/admin/channel/option/option.module';
4242
import { FeedbackModule } from './domains/admin/feedback/feedback.module';
4343
import { HistoryModule } from './domains/admin/history/history.module';
44+
import { AIModule } from './domains/admin/project/ai/ai.module';
4445
import { ApiKeyModule } from './domains/admin/project/api-key/api-key.module';
4546
import { CategoryModule } from './domains/admin/project/category/category.module';
4647
import { IssueTrackerModule } from './domains/admin/project/issue-tracker/issue-tracker.module';
@@ -83,6 +84,7 @@ export const domainModules = [
8384
FeedbackIssueStatisticsModule,
8485
APIModule,
8586
SchedulerLockModule,
87+
AIModule,
8688
] as (typeof AuthModule)[];
8789

8890
@Module({

apps/web/src/shared/types/svg.d.ts renamed to apps/api/src/common/enums/ai-prompt-status.enum.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,9 @@
1313
* License for the specific language governing permissions and limitations
1414
* under the License.
1515
*/
16-
declare module '*.svg' {
17-
import type * as React from 'react';
1816

19-
export const ReactComponent: React.FunctionComponent<
20-
React.ComponentProps<'svg'> & { title?: string }
21-
>;
17+
export enum AIPromptStatusEnum {
18+
success = 'success',
19+
error = 'error',
20+
loading = 'loading',
2221
}

packages/ufb-tailwindcss/src/components/toaster.css renamed to apps/api/src/common/enums/ai-providers.enum.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,8 @@
1313
* License for the specific language governing permissions and limitations
1414
* under the License.
1515
*/
16-
.toaster {
17-
}
18-
19-
.toast {
20-
@apply !absolute !left-0;
21-
}
2216

23-
.toast-close {
24-
@apply order-1;
17+
export enum AIProvidersEnum {
18+
OPEN_AI = 'OPEN_AI',
19+
GEMINI = 'GEMINI',
2520
}

apps/api/src/common/enums/field-format.enum.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export enum FieldFormatEnum {
2121
multiSelect = 'multiSelect',
2222
date = 'date',
2323
images = 'images',
24+
aiField = 'aiField',
2425
}
2526

2627
export function isSelectFieldFormat(type: FieldFormatEnum) {

apps/api/src/common/filters/http-exception.filter.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export class HttpExceptionFilter implements ExceptionFilter {
2929
const statusCode = exception.getStatus();
3030
const exceptionResponse = exception.getResponse();
3131

32+
this.logger.error({ statusCode, exceptionResponse });
3233
if (typeof exceptionResponse === 'string') {
3334
void response.status(statusCode).send({
3435
response: exceptionResponse,

apps/api/src/common/repositories/opensearch.repository.spec.ts

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ describe('Opensearch Repository Test suite', () => {
7979

8080
await osRepo.createIndex({ index });
8181

82-
expect(osClient.indices.create).toBeCalledTimes(1);
83-
expect(osClient.indices.create).toBeCalledWith({
82+
expect(osClient.indices.create).toHaveBeenCalledTimes(1);
83+
expect(osClient.indices.create).toHaveBeenCalledWith({
8484
index: indexName,
8585
body: {
8686
settings: {
@@ -105,8 +105,8 @@ describe('Opensearch Repository Test suite', () => {
105105
},
106106
},
107107
});
108-
expect(osClient.indices.putAlias).toBeCalledTimes(1);
109-
expect(osClient.indices.putAlias).toBeCalledWith({
108+
expect(osClient.indices.putAlias).toHaveBeenCalledTimes(1);
109+
expect(osClient.indices.putAlias).toHaveBeenCalledWith({
110110
index: indexName,
111111
name: index,
112112
});
@@ -125,9 +125,9 @@ describe('Opensearch Repository Test suite', () => {
125125

126126
await osRepo.putMappings(dto);
127127

128-
expect(osClient.indices.exists).toBeCalledTimes(1);
129-
expect(osClient.indices.putMapping).toBeCalledTimes(1);
130-
expect(osClient.indices.putMapping).toBeCalledWith({
128+
expect(osClient.indices.exists).toHaveBeenCalledTimes(1);
129+
expect(osClient.indices.putMapping).toHaveBeenCalledTimes(1);
130+
expect(osClient.indices.putMapping).toHaveBeenCalledWith({
131131
index: dto.index,
132132
body: { properties: dto.mappings },
133133
});
@@ -141,12 +141,12 @@ describe('Opensearch Repository Test suite', () => {
141141
.mockResolvedValue({ statusCode: 404 } as never);
142142
jest.spyOn(osClient.indices, 'putMapping');
143143

144-
await expect(osRepo.putMappings(dto)).rejects.toThrowError(
144+
await expect(osRepo.putMappings(dto)).rejects.toThrow(
145145
new NotFoundException('index is not found'),
146146
);
147147

148-
expect(osClient.indices.exists).toBeCalledTimes(1);
149-
expect(osClient.indices.putMapping).not.toBeCalled();
148+
expect(osClient.indices.exists).toHaveBeenCalledTimes(1);
149+
expect(osClient.indices.putMapping).not.toHaveBeenCalled();
150150
});
151151
});
152152

@@ -179,9 +179,9 @@ describe('Opensearch Repository Test suite', () => {
179179
const response = await osRepo.createData(dto);
180180

181181
expect(response.id).toEqual(dto.id);
182-
expect(osClient.indices.getMapping).toBeCalledTimes(1);
183-
expect(osClient.index).toBeCalledTimes(1);
184-
expect(osClient.index).toBeCalledWith({
182+
expect(osClient.indices.getMapping).toHaveBeenCalledTimes(1);
183+
expect(osClient.index).toHaveBeenCalledTimes(1);
184+
expect(osClient.index).toHaveBeenCalledWith({
185185
id: dto.id,
186186
index: 'channel_' + index,
187187
body: dto.data,
@@ -213,13 +213,13 @@ describe('Opensearch Repository Test suite', () => {
213213
},
214214
} as never);
215215

216-
await expect(osRepo.createData(dto)).rejects.toThrowError(
216+
await expect(osRepo.createData(dto)).rejects.toThrow(
217217
new NotFoundException('index is not found'),
218218
);
219219

220-
expect(osClient.indices.exists).toBeCalledTimes(1);
221-
expect(osClient.indices.getMapping).not.toBeCalled();
222-
expect(osClient.index).not.toBeCalled();
220+
expect(osClient.indices.exists).toHaveBeenCalledTimes(1);
221+
expect(osClient.indices.getMapping).not.toHaveBeenCalled();
222+
expect(osClient.index).not.toHaveBeenCalled();
223223
});
224224
it('creating data fails with invalid data', async () => {
225225
const index = faker.number.int().toString();
@@ -250,13 +250,13 @@ describe('Opensearch Repository Test suite', () => {
250250
},
251251
} as never);
252252

253-
await expect(osRepo.createData(dto)).rejects.toThrowError(
253+
await expect(osRepo.createData(dto)).rejects.toThrow(
254254
new InternalServerErrorException('error!!!'),
255255
);
256256

257-
expect(osClient.indices.exists).toBeCalledTimes(1);
258-
expect(osClient.indices.getMapping).toBeCalledTimes(1);
259-
expect(osClient.index).not.toBeCalled();
257+
expect(osClient.indices.exists).toHaveBeenCalledTimes(1);
258+
expect(osClient.indices.getMapping).toHaveBeenCalledTimes(1);
259+
expect(osClient.index).not.toHaveBeenCalled();
260260
});
261261
});
262262

0 commit comments

Comments
 (0)