diff --git a/Modules/AI/controller.js b/Modules/AI/controller.js index 99d3bf75..d787e966 100644 --- a/Modules/AI/controller.js +++ b/Modules/AI/controller.js @@ -38,15 +38,31 @@ exports.generatePrompt = (req,res) => { if (promptsText.toLowerCase().includes(prompt.key.toLowerCase())) { var parts = promptsText.split(prompt.key); promptsText = parts.join(prompt.value); - promptsText = promptsText + promptRes.outputFormat } }) + // Append the schema reminder once after all substitutions. + // Previously this lived INSIDE the forEach above, so a + // prompt with N substitution keys ended up with N copies + // of `outputFormat` glued to the end — wasted tokens and + // potentially confusing for the model. + promptsText = promptsText + promptRes.outputFormat; addChat(req.body.uniqueUserId,{role: "user",content: promptsText }); let curlUrl = "https://api.openai.com/v1/chat/completions"; let axiosData = { "model": config.AI_MODEL, "messages": [{"role": "user", "content": `${JSON.stringify(promptsText)}`}] } + // Optional per-prompt OpenAI parameters declared on the + // prompt entry in utils/aiPrompts.json. Backwards- + // compatible: prompts that don't declare these keep the + // OpenAI defaults (and the streaming branch keeps its + // own hardcoded response_format below). + if (promptRes.responseFormat) { + axiosData.response_format = promptRes.responseFormat; + } + if (typeof promptRes.temperature === 'number') { + axiosData.temperature = promptRes.temperature; + } const header = { headers: { Authorization: `Bearer ${config.AI_API_KEY}`, diff --git a/frontend/src/locales/en.js b/frontend/src/locales/en.js index 635d6beb..8ae455ce 100644 --- a/frontend/src/locales/en.js +++ b/frontend/src/locales/en.js @@ -1652,6 +1652,41 @@ export default { ai_not_integrated: "AI is not integrated in your system", limit_reached: "You have reached your limit", }, + AICard: { + button_label: "AI Create Card", + title: "AI Create Card", + prompt_label: "Describe the card you want", + prompt_subtitle: "Tell us in plain English what you want to see and AI will assemble a card for you.", + prompt_placeholder: "e.g. Show a pie chart of urgent open tasks for the AlianHub project", + helper_idle: "Minimum {n} characters", + helper_more: "{remaining} more to go", + examples_heading: "Try one of these", + chip_workload: "Workload by status", + chip_workload_prompt: "Pie chart of workload by status", + chip_assignee: "Tasks by assignee", + chip_assignee_prompt: "Bar chart of tasks grouped by assignee", + chip_urgent: "Urgent tasks", + chip_urgent_prompt: "List of my urgent open tasks", + chip_calendar: "Due-date calendar", + chip_calendar_prompt: "Calendar view of upcoming due dates", + chip_time: "Time tracking", + chip_time_prompt: "Time tracking summary for this week", + generate: "Generate", + generating: "Generating your card…", + preview_heading: "Preview", + card_type: "Card type", + card_name: "Card name", + projects: "Projects", + filters: "Filters", + add_to_dashboard: "Add to Dashboard", + regenerate: "Regenerate", + try_again: "Try Again", + error_heading: "Couldn't generate a card", + error_invalid_json: "The AI response wasn't valid card JSON. Try rephrasing your request.", + error_unknown_card: "The AI picked a card type that doesn't exist. Try rephrasing your request.", + error_engine: "The AI engine returned an error. Please try again.", + fallback_card_name: "AI generated card", + }, Toast: { Your_card_is_expired: "Your card is expired.", Password_set_new_has_been_successfully: diff --git a/frontend/src/plugins/dashboard/components/AICardSidebar.vue b/frontend/src/plugins/dashboard/components/AICardSidebar.vue new file mode 100644 index 00000000..10030f53 --- /dev/null +++ b/frontend/src/plugins/dashboard/components/AICardSidebar.vue @@ -0,0 +1,962 @@ + + + + + diff --git a/frontend/src/plugins/dashboard/views/Home.vue b/frontend/src/plugins/dashboard/views/Home.vue index 19fda744..e9555456 100644 --- a/frontend/src/plugins/dashboard/views/Home.vue +++ b/frontend/src/plugins/dashboard/views/Home.vue @@ -10,6 +10,16 @@ lock{{ companyUser.dashboardLocked ? $t('Home.unlock') : $t('Home.lock') }} + +
@@ -297,6 +307,16 @@ const addItem = () => { myRefsss.value.handleToggle(); }; +// Gate the AI button on the same plan flag the rest of the AI features use. +// Falsy → button stays hidden, plan-upgrade prompt is handled by useAiApiFunction +// downstream so users on free plans never see this entry point. +const selectedCompanyData = computed(() => getters['settings/selectedCompany']); +const aiEnabled = computed(() => !!selectedCompanyData.value?.planFeature?.aiPermission); + +const openAiCardSidebar = () => { + myRefsss.value && myRefsss.value.openAiSidebar(); +}; + const dragFunction = (value) => { isDrag.value = value; } diff --git a/frontend/src/plugins/dashboard/views/HomePage.vue b/frontend/src/plugins/dashboard/views/HomePage.vue index 5b029c9a..161ce73f 100644 --- a/frontend/src/plugins/dashboard/views/HomePage.vue +++ b/frontend/src/plugins/dashboard/views/HomePage.vue @@ -128,6 +128,13 @@
+ +