feat: Add AI subtask reproduction script, refactor Admin MCP settings UI, update Vite config, and add i18n gamification rules.
continuous-integration/drone/push Build is passing
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -1 +1,7 @@
|
||||
DATABASE_URL=postgresql://taskflow:taskflow_password@localhost:5432/taskflow
|
||||
|
||||
POSTGRES_USER=taskflow
|
||||
POSTGRES_PASSWORD=taskflow_password
|
||||
POSTGRES_DB=taskflow
|
||||
SESSION_SECRET=local_development_secret
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
64: } catch (e) {
|
||||
95: } catch (e) {
|
||||
127: } catch (e) {
|
||||
138: } catch (e) {
|
||||
164: } catch (e) {
|
||||
190: } catch (e) {
|
||||
216: } catch (e) {
|
||||
238: } catch (e) {
|
||||
298: } catch (e: any) {
|
||||
326: } catch (e: any) {
|
||||
340: } catch (e) {
|
||||
363: } catch (e) {
|
||||
392: } catch (e) {
|
||||
419: } catch (e) {
|
||||
432: } catch (e) {
|
||||
457: } catch (e) {
|
||||
472: } catch (e) {
|
||||
489: } catch (e: any) {
|
||||
556: } catch (e: any) {
|
||||
633: } catch (e: any) {
|
||||
665: } catch (error) {
|
||||
677: } catch (error) {
|
||||
704: } catch (error) {
|
||||
732: } catch (error) {
|
||||
755: } catch (error) {
|
||||
789: } catch (e) {
|
||||
813: } catch (e) {
|
||||
833: } catch (e) {
|
||||
844: } catch (error) {
|
||||
858: } catch (error) {
|
||||
892: } catch (e: any) {
|
||||
926: } catch (error) {
|
||||
968: } catch (error: any) {
|
||||
991: } catch (error) {
|
||||
1002: } catch (error) {
|
||||
1015: } catch (error) {
|
||||
1025: } catch (error) {
|
||||
1052: } catch (error) {
|
||||
1137: } catch (error) {
|
||||
1175: } catch (e) {
|
||||
1195: } catch (e) {
|
||||
1214: } catch (e) {
|
||||
1232: } catch (e) {
|
||||
1248: } catch (e) {
|
||||
1266: } catch (e) {
|
||||
1287: } catch (e) {
|
||||
1300: } catch (e) {
|
||||
1325: } catch (e) {
|
||||
1345: } catch (e) {
|
||||
1370: } catch (e) {
|
||||
1384: } catch (e) {
|
||||
1400: } catch (e) {
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"gamification": {
|
||||
"rules": {
|
||||
"title": "Gamification Rules",
|
||||
"xpSystem": "XP System",
|
||||
"levelRequirements": "Level Requirements",
|
||||
"actions": "Actions & Rewards",
|
||||
"level": "Level {{level}}",
|
||||
"xp": "{{xp}} XP",
|
||||
"action": "Action",
|
||||
"points": "Points",
|
||||
"createTask": "Create Task",
|
||||
"createSubtask": "Create Subtask",
|
||||
"updateTask": "Update Task",
|
||||
"completeTask": "Complete Task (On Time)",
|
||||
"completeTaskLate": "Complete Task (Late)",
|
||||
"aiAction": "Use AI Feature",
|
||||
"dailyStreak": "Daily Streak Bonus"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,10 @@ export default function AdminSettings() {
|
||||
<Settings className="h-4 w-4" />
|
||||
General & AI
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="mcp" className="flex items-center gap-2">
|
||||
<Bot className="h-4 w-4" />
|
||||
MCP Integration
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="audit" className="flex items-center gap-2">
|
||||
<FileText className="h-4 w-4" />
|
||||
Audit Logs
|
||||
@@ -45,10 +49,13 @@ export default function AdminSettings() {
|
||||
|
||||
<TabsContent value="settings" className="space-y-6">
|
||||
<AiSettingsCard />
|
||||
<McpSettingsCard />
|
||||
<SMTPSettingsCard />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="mcp">
|
||||
<McpSettingsCard />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="audit">
|
||||
<AuditLogsTable />
|
||||
</TabsContent>
|
||||
|
||||
@@ -330,64 +330,7 @@ export default function Settings({ onNavigateToTemplates }: SettingsProps) {
|
||||
|
||||
<ShareAccessModal open={isShareAccessOpen} onOpenChange={setIsShareAccessOpen} />
|
||||
|
||||
{/* MCP Integration */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Server className="w-5 h-5" />
|
||||
{t('settings.mcp.title')}
|
||||
</CardTitle>
|
||||
<CardDescription>{t('settings.mcp.description')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-0.5">
|
||||
<p className="font-medium">{t('settings.mcp.status')}</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-green-500">
|
||||
<div className="w-2 h-2 rounded-full bg-green-500 animate-pulse" />
|
||||
<span className="font-medium">{t('settings.mcp.running')}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm font-medium">{t('settings.mcp.url')}</p>
|
||||
<div className="flex gap-2">
|
||||
<Input readOnly value={`${window.location.protocol}//${window.location.host}/api/mcp/sse`} />
|
||||
<Button variant="outline" size="icon" onClick={() => copyToClipboard(`${window.location.protocol}//${window.location.host}/api/mcp/sse`)}>
|
||||
<Copy className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm font-medium">{t('settings.mcp.apiKey')}</p>
|
||||
{user?.apiKey ? (
|
||||
<div className="flex gap-2">
|
||||
<Input type="password" readOnly value={user.apiKey} />
|
||||
{/* Show full key on click/copy only, usually concealed */}
|
||||
<Button variant="outline" size="icon" onClick={() => copyToClipboard(user.apiKey!)}>
|
||||
<Copy className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button variant="destructive" onClick={() => revokeApiKeyMutation.mutate()} disabled={revokeApiKeyMutation.isPending}>
|
||||
{t('settings.mcp.revoke')}
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm text-muted-foreground">{t('settings.mcp.noKey')}</p>
|
||||
<Button onClick={() => generateApiKeyMutation.mutate()} disabled={generateApiKeyMutation.isPending}>
|
||||
{t('settings.mcp.generate')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="bg-muted/50 p-3 rounded-lg text-sm text-muted-foreground">
|
||||
{t('settings.mcp.instructions')}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -30,8 +30,7 @@ services:
|
||||
PORT: 5000
|
||||
SESSION_SECRET: ${SESSION_SECRET}
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
- postgres
|
||||
healthcheck:
|
||||
test: [ "CMD", "node", "-e", "require('http').get('http://localhost:5000/api/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})" ]
|
||||
interval: 30s
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "rest-express",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.4",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "rest-express",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.4",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "rest-express",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.4",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
|
||||
import { AiService } from "./ai";
|
||||
import { MemStorage } from "./storage";
|
||||
import { User } from "../shared/schema";
|
||||
|
||||
async function run() {
|
||||
console.log("Starting Subtask Repro...");
|
||||
const storage = new MemStorage();
|
||||
|
||||
// Create dummy user
|
||||
const user: User = await storage.createUser({
|
||||
username: "testuser",
|
||||
password: "hashedpassword",
|
||||
role: "user",
|
||||
aiEnabled: true,
|
||||
xp: 0,
|
||||
level: 1
|
||||
});
|
||||
|
||||
// Mock settings - Use env variables if available for real testing, or mock if just testing logic flow (but we need real AI for logic flow testing)
|
||||
if (process.env.OPENAI_API_KEY) {
|
||||
await storage.updateSystemSetting("ai_provider", "openai");
|
||||
await storage.updateSystemSetting("ai_api_key", process.env.OPENAI_API_KEY);
|
||||
await storage.updateSystemSetting("ai_model", "gpt-4o");
|
||||
} else {
|
||||
console.warn("No OPENAI_API_KEY provided. This test requires a valid key to actually hit the AI.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const aiService = new AiService(storage);
|
||||
|
||||
// Scenario 1: Create new parent and subtask in one go
|
||||
console.log("\n--- Scenario 1: Create New Parent + Subtask ---");
|
||||
const prompt1 = "Create a task 'Main Project A'. Then create a subtask 'Subtask A1' for it. Also edit 'Subtask A1' to be 'In Progress'.";
|
||||
try {
|
||||
const response1 = await aiService.chat(
|
||||
[{ role: "user", content: prompt1 }],
|
||||
user,
|
||||
"Current Date: 2025-12-12. No existing tasks."
|
||||
);
|
||||
console.log("Response 1:", response1);
|
||||
} catch (e) {
|
||||
console.error("Error in Scenario 1:", e);
|
||||
}
|
||||
|
||||
const tasks1 = await storage.getTasks(user.id);
|
||||
console.log("Tasks after Scenario 1:", tasks1);
|
||||
|
||||
// Scenario 2: Add subtask to existing task
|
||||
console.log("\n--- Scenario 2: Add Subtask to Existing ---");
|
||||
// Create an existing task manually first
|
||||
const existingTask = await storage.createTask({
|
||||
title: "Existing Project B",
|
||||
priority: "high",
|
||||
status: "todo",
|
||||
userId: user.id
|
||||
});
|
||||
console.log("Created existing task:", existingTask.id, existingTask.title);
|
||||
|
||||
const prompt2 = "Add a subtask 'Subtask B1' to 'Existing Project B'.";
|
||||
try {
|
||||
const response2 = await aiService.chat(
|
||||
[{ role: "user", content: prompt2 }],
|
||||
user,
|
||||
`Current Date: 2025-12-12.`
|
||||
);
|
||||
console.log("Response 2:", response2);
|
||||
} catch (e) {
|
||||
console.error("Error in Scenario 2:", e);
|
||||
}
|
||||
|
||||
const tasks2 = await storage.getTasks(user.id);
|
||||
console.log("Final Task List:", tasks2.map(t => ({ id: t.id, title: t.title, parent: t.parentTaskId, status: t.status })));
|
||||
}
|
||||
|
||||
run();
|
||||
@@ -218,6 +218,8 @@
|
||||
- [x] Create `pre-commit` hook for patch increments
|
||||
- [x] Update `vite.config.ts` to expose version
|
||||
- [x] Display version in Admin Settings UI
|
||||
- [x] Refactor MCP Settings UI
|
||||
- [x] Move MCP settings to dedicated tab in Admin Settings
|
||||
- [x] Implement Shared Labels
|
||||
- [x] Schema: `sharedLabels` table
|
||||
- [x] Storage: Logic for sharing and retrieving labels (MemStorage verified)
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
47: try {
|
||||
71: try {
|
||||
102: try {
|
||||
135: try {
|
||||
144: try {
|
||||
170: try {
|
||||
196: try {
|
||||
235: try {
|
||||
293: try {
|
||||
311: try {
|
||||
337: try {
|
||||
349: try {
|
||||
372: try {
|
||||
403: try {
|
||||
427: try {
|
||||
440: try {
|
||||
465: try {
|
||||
483: try {
|
||||
501: try {
|
||||
568: try {
|
||||
646: try {
|
||||
671: try {
|
||||
683: try {
|
||||
711: try {
|
||||
738: try {
|
||||
763: try {
|
||||
796: try {
|
||||
820: try {
|
||||
841: try {
|
||||
852: try {
|
||||
865: try {
|
||||
900: try {
|
||||
933: try {
|
||||
975: try {
|
||||
998: try {
|
||||
1008: try {
|
||||
1022: try {
|
||||
1031: try {
|
||||
1123: try {
|
||||
1144: try {
|
||||
1182: try {
|
||||
1192: try {
|
||||
1202: try {
|
||||
1222: try {
|
||||
1239: try {
|
||||
1255: try {
|
||||
1273: try {
|
||||
1295: try {
|
||||
1307: try {
|
||||
1332: try {
|
||||
1352: try {
|
||||
1377: try {
|
||||
1397: try {
|
||||
+11
-7
@@ -1,13 +1,17 @@
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import path, { dirname } from "path";
|
||||
import path from "path";
|
||||
import { readFileSync } from "fs";
|
||||
import runtimeErrorOverlay from "@replit/vite-plugin-runtime-error-modal";
|
||||
import { fileURLToPath } from "url";
|
||||
import { VitePWA } from 'vite-plugin-pwa';
|
||||
|
||||
// Read package.json to get the version
|
||||
const packageJson = JSON.parse(readFileSync(path.resolve(process.cwd(), "package.json"), "utf-8"));
|
||||
|
||||
export default defineConfig({
|
||||
define: {
|
||||
__APP_VERSION__: JSON.stringify(process.env.npm_package_version),
|
||||
__APP_VERSION__: JSON.stringify(packageJson.version),
|
||||
},
|
||||
plugins: [
|
||||
react(),
|
||||
@@ -24,15 +28,15 @@ export default defineConfig({
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(import.meta.dirname, "client", "src"),
|
||||
"@shared": path.resolve(import.meta.dirname, "shared"),
|
||||
"@assets": path.resolve(import.meta.dirname, "attached_assets"),
|
||||
"@": path.resolve(process.cwd(), "client", "src"),
|
||||
"@shared": path.resolve(process.cwd(), "shared"),
|
||||
"@assets": path.resolve(process.cwd(), "attached_assets"),
|
||||
},
|
||||
},
|
||||
root: path.resolve(import.meta.dirname, "client"),
|
||||
root: path.resolve(process.cwd(), "client"),
|
||||
base: process.env.VITE_BASE_PATH || "/",
|
||||
build: {
|
||||
outDir: path.resolve(import.meta.dirname, "dist/public"),
|
||||
outDir: path.resolve(process.cwd(), "dist/public"),
|
||||
emptyOutDir: true,
|
||||
},
|
||||
server: {
|
||||
|
||||
Reference in New Issue
Block a user