Skip to content

Commit 3937265

Browse files
committed
refactor: reorganize imports and add new modules for S3 widget and personal table settings
1 parent 18974c3 commit 3937265

4 files changed

Lines changed: 103 additions & 197 deletions

File tree

backend/src/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ import { PersonalTableSettingsModule } from './entities/table-settings/personal-
8686
TableCategoriesModule,
8787
UserSecretModule,
8888
SignInAuditModule,
89+
PersonalTableSettingsModule,
90+
S3WidgetModule,
8991
],
9092
controllers: [AppController],
9193
providers: [

backend/src/entities/ai/ai.service.ts

Lines changed: 52 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
1-
import { Injectable } from "@nestjs/common";
2-
import { QueryOrderingEnum } from "../../enums/query-ordering.enum.js";
3-
import { WidgetTypeEnum } from "../../enums/widget-type.enum.js";
4-
import { checkFieldAutoincrement } from "../../helpers/check-field-autoincrement.js";
5-
import { TableSettingsEntity } from "../table-settings/table-settings.entity.js";
6-
import { TableWidgetEntity } from "../widget/table-widget.entity.js";
7-
import { TableInformation } from "./ai-data-entities/types/ai-module-types.js";
8-
import { AmazonBedrockAiProvider } from "./amazon-bedrock/amazon-bedrock.ai.provider.js";
1+
import { Injectable } from '@nestjs/common';
2+
import { WidgetTypeEnum } from '../../enums/widget-type.enum.js';
3+
import { checkFieldAutoincrement } from '../../helpers/check-field-autoincrement.js';
4+
import { TableWidgetEntity } from '../widget/table-widget.entity.js';
5+
import { TableInformation } from './ai-data-entities/types/ai-module-types.js';
6+
import { AmazonBedrockAiProvider } from './amazon-bedrock/amazon-bedrock.ai.provider.js';
7+
import { TableSettingsEntity } from '../table-settings/common-table-settings/table-settings.entity.js';
98

109
interface AIGeneratedTableSettings {
1110
table_name: string;
1211
display_name: string;
13-
list_fields: string[];
14-
ordering_field: string | null;
15-
ordering: "ASC" | "DESC";
1612
search_fields: string[];
1713
readonly_fields: string[];
1814
columns_view: string[];
@@ -42,47 +38,39 @@ export class AiService {
4238
}
4339

4440
private buildPrompt(tablesInformation: Array<TableInformation>): string {
45-
const widgetTypes = Object.values(WidgetTypeEnum).join(", ");
41+
const widgetTypes = Object.values(WidgetTypeEnum).join(', ');
4642

4743
const tablesDescription = tablesInformation
4844
.map((table) => {
4945
const columns = table.structure
5046
.map(
5147
(col) =>
52-
` - ${col.column_name}: ${col.data_type}${col.allow_null ? " (nullable)" : ""}${checkFieldAutoincrement(col.column_default, col.extra) ? " (auto_increment)" : ""}`,
48+
` - ${col.column_name}: ${col.data_type}${col.allow_null ? ' (nullable)' : ''}${checkFieldAutoincrement(col.column_default, col.extra) ? ' (auto_increment)' : ''}`,
5349
)
54-
.join("\n");
55-
const primaryKeys = table.primaryColumns
56-
.map((pk) => pk.column_name)
57-
.join(", ");
50+
.join('\n');
51+
const primaryKeys = table.primaryColumns.map((pk) => pk.column_name).join(', ');
5852
const foreignKeys = table.foreignKeys
59-
.map(
60-
(fk) =>
61-
` - ${fk.column_name} -> ${fk.referenced_table_name}.${fk.referenced_column_name}`,
62-
)
63-
.join("\n");
53+
.map((fk) => ` - ${fk.column_name} -> ${fk.referenced_table_name}.${fk.referenced_column_name}`)
54+
.join('\n');
6455

6556
return `
6657
Table: ${table.table_name}
67-
Primary Keys: ${primaryKeys || "none"}
58+
Primary Keys: ${primaryKeys || 'none'}
6859
Columns:
6960
${columns}
7061
Foreign Keys:
71-
${foreignKeys || " none"}`;
62+
${foreignKeys || ' none'}`;
7263
})
73-
.join("\n\n");
64+
.join('\n\n');
7465

7566
return `You are a database administration assistant. Analyze the following database tables and generate optimal settings for displaying and managing them in a web admin panel.
7667
7768
For each table, provide:
7869
1. display_name: A human-readable name for the table
79-
2. list_fields: Columns to display in the table list view (most important columns first, max 5-7 columns)
80-
3. ordering_field: The best column to sort by default (usually created_at, id, or a timestamp)
81-
4. ordering: ASC or DESC
82-
5. search_fields: Columns that should be searchable
83-
6. readonly_fields: Columns that should not be editable (like auto_increment, timestamps)
84-
7. columns_view: All columns in preferred display order
85-
8. widgets: For each column, suggest the best widget type from: ${widgetTypes}
70+
2. search_fields: Columns that should be searchable (text fields like name, email, title)
71+
3. readonly_fields: Columns that should not be editable (like auto_increment, timestamps)
72+
4. columns_view: All columns in preferred display order
73+
5. widgets: For each column, suggest the best widget type from: ${widgetTypes}
8674
8775
Available widget types and when to use them:
8876
- Password: for password fields
@@ -118,9 +106,6 @@ Respond ONLY with valid JSON in this exact format (no markdown, no explanations)
118106
{
119107
"table_name": "table_name",
120108
"display_name": "Human Readable Name",
121-
"list_fields": ["col1", "col2"],
122-
"ordering_field": "created_at",
123-
"ordering": "DESC",
124109
"search_fields": ["name", "email"],
125110
"readonly_fields": ["id", "created_at"],
126111
"columns_view": ["id", "name", "email", "created_at"],
@@ -139,12 +124,12 @@ Respond ONLY with valid JSON in this exact format (no markdown, no explanations)
139124

140125
private parseAIResponse(aiResponse: string): AIResponse {
141126
let cleanedResponse = aiResponse.trim();
142-
if (cleanedResponse.startsWith("```json")) {
127+
if (cleanedResponse.startsWith('```json')) {
143128
cleanedResponse = cleanedResponse.slice(7);
144-
} else if (cleanedResponse.startsWith("```")) {
129+
} else if (cleanedResponse.startsWith('```')) {
145130
cleanedResponse = cleanedResponse.slice(3);
146131
}
147-
if (cleanedResponse.endsWith("```")) {
132+
if (cleanedResponse.endsWith('```')) {
148133
cleanedResponse = cleanedResponse.slice(0, -3);
149134
}
150135
cleanedResponse = cleanedResponse.trim();
@@ -161,36 +146,15 @@ Respond ONLY with valid JSON in this exact format (no markdown, no explanations)
161146
tablesInformation: Array<TableInformation>,
162147
): Array<TableSettingsEntity> {
163148
return aiResponse.tables.map((tableSettings) => {
164-
const tableInfo = tablesInformation.find(
165-
(t) => t.table_name === tableSettings.table_name,
166-
);
167-
const validColumnNames =
168-
tableInfo?.structure.map((col) => col.column_name) || [];
149+
const tableInfo = tablesInformation.find((t) => t.table_name === tableSettings.table_name);
150+
const validColumnNames = tableInfo?.structure.map((col) => col.column_name) || [];
169151

170152
const settings = new TableSettingsEntity();
171153
settings.table_name = tableSettings.table_name;
172154
settings.display_name = tableSettings.display_name;
173-
settings.list_fields = this.filterValidColumns(
174-
tableSettings.list_fields,
175-
validColumnNames,
176-
);
177-
settings.ordering_field = tableSettings.ordering_field;
178-
settings.ordering =
179-
tableSettings.ordering === "DESC"
180-
? QueryOrderingEnum.DESC
181-
: QueryOrderingEnum.ASC;
182-
settings.search_fields = this.filterValidColumns(
183-
tableSettings.search_fields,
184-
validColumnNames,
185-
);
186-
settings.readonly_fields = this.filterValidColumns(
187-
tableSettings.readonly_fields,
188-
validColumnNames,
189-
);
190-
settings.columns_view = this.filterValidColumns(
191-
tableSettings.columns_view,
192-
validColumnNames,
193-
);
155+
settings.search_fields = this.filterValidColumns(tableSettings.search_fields, validColumnNames);
156+
settings.readonly_fields = this.filterValidColumns(tableSettings.readonly_fields, validColumnNames);
157+
settings.columns_view = this.filterValidColumns(tableSettings.columns_view, validColumnNames);
194158
settings.table_widgets = tableSettings.widgets
195159
.filter((w) => validColumnNames.includes(w.field_name))
196160
.map((widgetData) => {
@@ -206,38 +170,35 @@ Respond ONLY with valid JSON in this exact format (no markdown, no explanations)
206170
});
207171
}
208172

209-
private filterValidColumns(
210-
columns: string[],
211-
validColumnNames: string[],
212-
): string[] {
173+
private filterValidColumns(columns: string[], validColumnNames: string[]): string[] {
213174
return columns?.filter((col) => validColumnNames.includes(col)) || [];
214175
}
215176

216177
private mapWidgetType(widgetType: string): WidgetTypeEnum | undefined {
217178
const widgetTypeMap = new Map<string, WidgetTypeEnum>([
218-
["Password", WidgetTypeEnum.Password],
219-
["Boolean", WidgetTypeEnum.Boolean],
220-
["Date", WidgetTypeEnum.Date],
221-
["Time", WidgetTypeEnum.Time],
222-
["DateTime", WidgetTypeEnum.DateTime],
223-
["JSON", WidgetTypeEnum.JSON],
224-
["Textarea", WidgetTypeEnum.Textarea],
225-
["String", WidgetTypeEnum.String],
226-
["Readonly", WidgetTypeEnum.Readonly],
227-
["Number", WidgetTypeEnum.Number],
228-
["Select", WidgetTypeEnum.Select],
229-
["UUID", WidgetTypeEnum.UUID],
230-
["Enum", WidgetTypeEnum.Enum],
231-
["Foreign_key", WidgetTypeEnum.Foreign_key],
232-
["File", WidgetTypeEnum.File],
233-
["Image", WidgetTypeEnum.Image],
234-
["URL", WidgetTypeEnum.URL],
235-
["Code", WidgetTypeEnum.Code],
236-
["Phone", WidgetTypeEnum.Phone],
237-
["Country", WidgetTypeEnum.Country],
238-
["Color", WidgetTypeEnum.Color],
239-
["Range", WidgetTypeEnum.Range],
240-
["Timezone", WidgetTypeEnum.Timezone],
179+
['Password', WidgetTypeEnum.Password],
180+
['Boolean', WidgetTypeEnum.Boolean],
181+
['Date', WidgetTypeEnum.Date],
182+
['Time', WidgetTypeEnum.Time],
183+
['DateTime', WidgetTypeEnum.DateTime],
184+
['JSON', WidgetTypeEnum.JSON],
185+
['Textarea', WidgetTypeEnum.Textarea],
186+
['String', WidgetTypeEnum.String],
187+
['Readonly', WidgetTypeEnum.Readonly],
188+
['Number', WidgetTypeEnum.Number],
189+
['Select', WidgetTypeEnum.Select],
190+
['UUID', WidgetTypeEnum.UUID],
191+
['Enum', WidgetTypeEnum.Enum],
192+
['Foreign_key', WidgetTypeEnum.Foreign_key],
193+
['File', WidgetTypeEnum.File],
194+
['Image', WidgetTypeEnum.Image],
195+
['URL', WidgetTypeEnum.URL],
196+
['Code', WidgetTypeEnum.Code],
197+
['Phone', WidgetTypeEnum.Phone],
198+
['Country', WidgetTypeEnum.Country],
199+
['Color', WidgetTypeEnum.Color],
200+
['Range', WidgetTypeEnum.Range],
201+
['Timezone', WidgetTypeEnum.Timezone],
241202
]);
242203
return widgetTypeMap.get(widgetType);
243204
}

0 commit comments

Comments
 (0)