mirror of
https://github.com/formbricks/formbricks.git
synced 2026-03-04 10:19:31 -06:00
Compare commits
4 Commits
epic/dashb
...
feat/ai-to
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5854bd635f | ||
|
|
25ebb6411c | ||
|
|
421a732875 | ||
|
|
03ec8603bb |
@@ -27,7 +27,7 @@ export const updateOrganizationNameAction = authenticatedActionClient
|
||||
parsedInput,
|
||||
}: {
|
||||
ctx: AuthenticatedActionClientCtx;
|
||||
parsedInput: Record<string, any>;
|
||||
parsedInput: z.infer<typeof ZUpdateOrganizationNameAction>;
|
||||
}) => {
|
||||
await checkAuthorizationUpdated({
|
||||
userId: ctx.user.id,
|
||||
@@ -51,6 +51,49 @@ export const updateOrganizationNameAction = authenticatedActionClient
|
||||
)
|
||||
);
|
||||
|
||||
const ZUpdateOrganizationAISettingsAction = z.object({
|
||||
organizationId: ZId,
|
||||
data: ZOrganizationUpdateInput.pick({ isAISmartToolsEnabled: true, isAIDataAnalysisEnabled: true }),
|
||||
});
|
||||
|
||||
export const updateOrganizationAISettingsAction = authenticatedActionClient
|
||||
.schema(ZUpdateOrganizationAISettingsAction)
|
||||
.action(
|
||||
withAuditLogging(
|
||||
"updated",
|
||||
"organization",
|
||||
async ({
|
||||
ctx,
|
||||
parsedInput,
|
||||
}: {
|
||||
ctx: AuthenticatedActionClientCtx;
|
||||
parsedInput: z.infer<typeof ZUpdateOrganizationAISettingsAction>;
|
||||
}) => {
|
||||
await checkAuthorizationUpdated({
|
||||
userId: ctx.user.id,
|
||||
organizationId: parsedInput.organizationId,
|
||||
access: [
|
||||
{
|
||||
type: "organization",
|
||||
schema: ZOrganizationUpdateInput.pick({
|
||||
isAISmartToolsEnabled: true,
|
||||
isAIDataAnalysisEnabled: true,
|
||||
}),
|
||||
data: parsedInput.data,
|
||||
roles: ["owner", "manager"],
|
||||
},
|
||||
],
|
||||
});
|
||||
ctx.auditLoggingCtx.organizationId = parsedInput.organizationId;
|
||||
const oldObject = await getOrganization(parsedInput.organizationId);
|
||||
const result = await updateOrganization(parsedInput.organizationId, parsedInput.data);
|
||||
ctx.auditLoggingCtx.oldObject = oldObject;
|
||||
ctx.auditLoggingCtx.newObject = result;
|
||||
return result;
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
const ZDeleteOrganizationAction = z.object({
|
||||
organizationId: ZId,
|
||||
});
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
"use client";
|
||||
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TOrganizationRole } from "@formbricks/types/memberships";
|
||||
import { TOrganization } from "@formbricks/types/organizations";
|
||||
import { updateOrganizationAISettingsAction } from "@/app/(app)/environments/[environmentId]/settings/(organization)/general/actions";
|
||||
import { getAccessFlags } from "@/lib/membership/utils";
|
||||
import { getFormattedErrorMessage } from "@/lib/utils/helper";
|
||||
import { Alert, AlertDescription } from "@/modules/ui/components/alert";
|
||||
import { Label } from "@/modules/ui/components/label";
|
||||
import { Switch } from "@/modules/ui/components/switch";
|
||||
|
||||
interface AISettingsToggleProps {
|
||||
organization: TOrganization;
|
||||
membershipRole?: TOrganizationRole;
|
||||
}
|
||||
|
||||
export const AISettingsToggle = ({ organization, membershipRole }: Readonly<AISettingsToggleProps>) => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
|
||||
const { isOwner, isManager } = getAccessFlags(membershipRole);
|
||||
const canEdit = isOwner || isManager;
|
||||
|
||||
const handleToggle = async (
|
||||
field: "isAISmartToolsEnabled" | "isAIDataAnalysisEnabled",
|
||||
checked: boolean
|
||||
) => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const response = await updateOrganizationAISettingsAction({
|
||||
organizationId: organization.id,
|
||||
data: { [field]: checked },
|
||||
});
|
||||
|
||||
if (response?.data) {
|
||||
toast.success(t("environments.settings.general.ai_settings_updated_successfully"));
|
||||
router.refresh();
|
||||
} else {
|
||||
const errorMessage = getFormattedErrorMessage(response);
|
||||
toast.error(errorMessage);
|
||||
}
|
||||
} catch {
|
||||
toast.error(t("common.something_went_wrong"));
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-start space-x-2">
|
||||
<Switch
|
||||
id="ai-smart-tools-toggle"
|
||||
className="mt-0.5"
|
||||
checked={organization.isAISmartToolsEnabled}
|
||||
disabled={isLoading || !canEdit}
|
||||
onCheckedChange={(checked) => handleToggle("isAISmartToolsEnabled", checked)}
|
||||
/>
|
||||
<div>
|
||||
<Label htmlFor="ai-smart-tools-toggle">
|
||||
{t("environments.settings.general.ai_smart_tools_enabled")}
|
||||
</Label>
|
||||
<p className="text-xs text-slate-500">
|
||||
{t("environments.settings.general.ai_smart_tools_enabled_description")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start space-x-2">
|
||||
<Switch
|
||||
id="ai-data-analysis-toggle"
|
||||
className="mt-0.5"
|
||||
checked={organization.isAIDataAnalysisEnabled}
|
||||
disabled={isLoading || !canEdit}
|
||||
onCheckedChange={(checked) => handleToggle("isAIDataAnalysisEnabled", checked)}
|
||||
/>
|
||||
<div>
|
||||
<Label htmlFor="ai-data-analysis-toggle">
|
||||
{t("environments.settings.general.ai_data_analysis_enabled")}
|
||||
</Label>
|
||||
<p className="text-xs text-slate-500">
|
||||
{t("environments.settings.general.ai_data_analysis_enabled_description")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!canEdit && (
|
||||
<Alert variant="warning">
|
||||
<AlertDescription>
|
||||
{t("common.only_owners_managers_and_manage_access_members_can_perform_this_action")}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -11,6 +11,7 @@ import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper
|
||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||
import packageJson from "@/package.json";
|
||||
import { SettingsCard } from "../../components/SettingsCard";
|
||||
import { AISettingsToggle } from "./components/AISettingsToggle";
|
||||
import { DeleteOrganization } from "./components/DeleteOrganization";
|
||||
import { EditOrganizationNameForm } from "./components/EditOrganizationNameForm";
|
||||
import { SecurityListTip } from "./components/SecurityListTip";
|
||||
@@ -60,6 +61,11 @@ const Page = async (props: { params: Promise<{ environmentId: string }> }) => {
|
||||
membershipRole={currentUserMembership?.role}
|
||||
/>
|
||||
</SettingsCard>
|
||||
<SettingsCard
|
||||
title={t("environments.settings.general.ai_enabled")}
|
||||
description={t("environments.settings.general.ai_enabled_description")}>
|
||||
<AISettingsToggle organization={organization} membershipRole={currentUserMembership?.role} />
|
||||
</SettingsCard>
|
||||
<EmailCustomizationSettings
|
||||
organization={organization}
|
||||
hasWhiteLabelPermission={hasWhiteLabelPermission}
|
||||
|
||||
@@ -82,7 +82,8 @@ const mockOrganization: TOrganization = {
|
||||
},
|
||||
periodStart: new Date(),
|
||||
},
|
||||
isAIEnabled: false,
|
||||
isAISmartToolsEnabled: false,
|
||||
isAIDataAnalysisEnabled: false,
|
||||
};
|
||||
|
||||
const mockSurveys: TSurvey[] = [
|
||||
|
||||
@@ -43,7 +43,8 @@ const mockOrganization: TOrganization = {
|
||||
},
|
||||
periodStart: new Date(),
|
||||
},
|
||||
isAIEnabled: false,
|
||||
isAISmartToolsEnabled: false,
|
||||
isAIDataAnalysisEnabled: false,
|
||||
};
|
||||
|
||||
const mockFollowUp: TSurveyCreateInputWithEnvironmentId["followUps"][number] = {
|
||||
|
||||
@@ -1176,6 +1176,13 @@ checksums:
|
||||
environments/settings/enterprise/sso: 95e98e279bb89233d63549b202bd9112
|
||||
environments/settings/enterprise/teams: 21ab78abcba0f16c3029741563f789ea
|
||||
environments/settings/enterprise/unlock_the_full_power_of_formbricks_free_for_30_days: 104d07b63a42911c9673ceb08a4dbd43
|
||||
environments/settings/general/ai_data_analysis_enabled: 45fabb594da6851f73fef50ca40fe525
|
||||
environments/settings/general/ai_data_analysis_enabled_description: 46d4f0bdf4ebf89e78f79cc961a2de83
|
||||
environments/settings/general/ai_enabled: 3cb1fce89c525e754448d5bd143eb6b5
|
||||
environments/settings/general/ai_enabled_description: e8c3e9f362588898a6cea85e18c013a1
|
||||
environments/settings/general/ai_settings_updated_successfully: 2a6f534dc3a246ced46becd8a4a9543d
|
||||
environments/settings/general/ai_smart_tools_enabled: 1dda984f5262c5f9120ee9a409236758
|
||||
environments/settings/general/ai_smart_tools_enabled_description: 1ceca6707746d3ab4a530712a06d91da
|
||||
environments/settings/general/bulk_invite_warning_description: e8737a2fbd5ff353db5580d17b4b5a37
|
||||
environments/settings/general/cannot_delete_only_organization: 833cc6848b28f2694a4552b4de91a6ba
|
||||
environments/settings/general/cannot_leave_only_organization: dd8463262e4299fef7ad73512225c55b
|
||||
|
||||
@@ -40,7 +40,8 @@ describe("auth", () => {
|
||||
},
|
||||
periodStart: new Date(),
|
||||
},
|
||||
isAIEnabled: false,
|
||||
isAISmartToolsEnabled: false,
|
||||
isAIDataAnalysisEnabled: false,
|
||||
},
|
||||
];
|
||||
vi.mocked(getOrganizationsByUserId).mockResolvedValue(mockOrganizations);
|
||||
|
||||
@@ -55,7 +55,8 @@ describe("Organization Service", () => {
|
||||
periodStart: new Date(),
|
||||
period: "monthly" as const,
|
||||
},
|
||||
isAIEnabled: false,
|
||||
isAISmartToolsEnabled: false,
|
||||
isAIDataAnalysisEnabled: false,
|
||||
whitelabel: false,
|
||||
};
|
||||
|
||||
@@ -110,7 +111,8 @@ describe("Organization Service", () => {
|
||||
periodStart: new Date(),
|
||||
period: "monthly" as const,
|
||||
},
|
||||
isAIEnabled: false,
|
||||
isAISmartToolsEnabled: false,
|
||||
isAIDataAnalysisEnabled: false,
|
||||
whitelabel: false,
|
||||
},
|
||||
];
|
||||
@@ -163,7 +165,8 @@ describe("Organization Service", () => {
|
||||
periodStart: new Date(),
|
||||
period: "monthly" as const,
|
||||
},
|
||||
isAIEnabled: false,
|
||||
isAISmartToolsEnabled: false,
|
||||
isAIDataAnalysisEnabled: false,
|
||||
whitelabel: false,
|
||||
};
|
||||
|
||||
@@ -224,7 +227,8 @@ describe("Organization Service", () => {
|
||||
periodStart: new Date(),
|
||||
period: "monthly" as const,
|
||||
},
|
||||
isAIEnabled: false,
|
||||
isAISmartToolsEnabled: false,
|
||||
isAIDataAnalysisEnabled: false,
|
||||
whitelabel: false,
|
||||
memberships: [{ userId: "user1" }, { userId: "user2" }],
|
||||
projects: [
|
||||
@@ -256,7 +260,8 @@ describe("Organization Service", () => {
|
||||
periodStart: expect.any(Date),
|
||||
period: "monthly",
|
||||
},
|
||||
isAIEnabled: false,
|
||||
isAISmartToolsEnabled: false,
|
||||
isAIDataAnalysisEnabled: false,
|
||||
whitelabel: false,
|
||||
});
|
||||
expect(prisma.organization.update).toHaveBeenCalledWith({
|
||||
|
||||
@@ -25,7 +25,8 @@ export const select: Prisma.OrganizationSelect = {
|
||||
updatedAt: true,
|
||||
name: true,
|
||||
billing: true,
|
||||
isAIEnabled: true,
|
||||
isAISmartToolsEnabled: true,
|
||||
isAIDataAnalysisEnabled: true,
|
||||
whitelabel: true,
|
||||
};
|
||||
|
||||
|
||||
@@ -232,7 +232,8 @@ export const mockOrganizationOutput: TOrganization = {
|
||||
name: "mock Organization",
|
||||
createdAt: currentDate,
|
||||
updatedAt: currentDate,
|
||||
isAIEnabled: false,
|
||||
isAISmartToolsEnabled: false,
|
||||
isAIDataAnalysisEnabled: false,
|
||||
billing: {
|
||||
stripeCustomerId: null,
|
||||
plan: "free",
|
||||
|
||||
@@ -73,7 +73,8 @@ describe("User Service", () => {
|
||||
},
|
||||
periodStart: new Date(),
|
||||
},
|
||||
isAIEnabled: false,
|
||||
isAISmartToolsEnabled: false,
|
||||
isAIDataAnalysisEnabled: false,
|
||||
},
|
||||
{
|
||||
id: "org2",
|
||||
@@ -93,7 +94,8 @@ describe("User Service", () => {
|
||||
},
|
||||
periodStart: new Date(),
|
||||
},
|
||||
isAIEnabled: false,
|
||||
isAISmartToolsEnabled: false,
|
||||
isAIDataAnalysisEnabled: false,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -1243,6 +1243,13 @@
|
||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "Schalte die volle Power von Formbricks frei. 30 Tage kostenlos."
|
||||
},
|
||||
"general": {
|
||||
"ai_data_analysis_enabled": "Datenanreicherung & Analyse (KI)",
|
||||
"ai_data_analysis_enabled_description": "KI, um mehr aus deinen Daten herauszuholen, Dashboards, Diagramme, Berichte und mehr einzurichten. Greift auf deine Erfahrungsdaten zu.",
|
||||
"ai_enabled": "Formbricks AI",
|
||||
"ai_enabled_description": "Verwalte KI-gestützte Funktionen für diese Organisation.",
|
||||
"ai_settings_updated_successfully": "KI-Einstellungen erfolgreich aktualisiert",
|
||||
"ai_smart_tools_enabled": "Intelligente Funktionen (KI)",
|
||||
"ai_smart_tools_enabled_description": "KI, um dir zu helfen, in kürzerer Zeit mehr zu erreichen. Greift niemals auf mit Formbricks gesammelte Daten zu. Wird nur verwendet, um z. B. Umfragen in andere Sprachen zu übersetzen.",
|
||||
"bulk_invite_warning_description": "Bitte beachte, dass im Free-Plan alle Organisationsmitglieder automatisch die Rolle \"Owner\" zugewiesen bekommen, unabhängig von der im CSV-File angegebenen Rolle.",
|
||||
"cannot_delete_only_organization": "Das ist deine einzige Organisation, sie kann nicht gelöscht werden. Erstelle zuerst eine neue Organisation.",
|
||||
"cannot_leave_only_organization": "Du kannst diese Organisation nicht verlassen, da es deine einzige Organisation ist. Erstelle zuerst eine neue Organisation.",
|
||||
|
||||
@@ -1243,6 +1243,13 @@
|
||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "Unlock the full power of Formbricks. Free for 30 days."
|
||||
},
|
||||
"general": {
|
||||
"ai_data_analysis_enabled": "Data enrichment & analysis (AI)",
|
||||
"ai_data_analysis_enabled_description": "AI to get more out of your data, setup dashboards, charts, reports and more. Touches your experience data.",
|
||||
"ai_enabled": "Formbricks AI",
|
||||
"ai_enabled_description": "Manage AI-powered features for this organization.",
|
||||
"ai_settings_updated_successfully": "AI settings updated successfully",
|
||||
"ai_smart_tools_enabled": "Smart functionality (AI)",
|
||||
"ai_smart_tools_enabled_description": "AI to help you achieve more in less time. Never touches data collected with Formbricks. Only used to e.g. translate surveys to other languages.",
|
||||
"bulk_invite_warning_description": "On the free plan, all organization members are always assigned the “Owner” role.",
|
||||
"cannot_delete_only_organization": "This is your only organization, it cannot be deleted. Create a new organization first.",
|
||||
"cannot_leave_only_organization": "You cannot leave this organization as it is your only organization. Create a new organization first.",
|
||||
|
||||
@@ -1243,6 +1243,13 @@
|
||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "Desbloquea todo el potencial de Formbricks. Gratis durante 30 días."
|
||||
},
|
||||
"general": {
|
||||
"ai_data_analysis_enabled": "Enriquecimiento y análisis de datos (IA)",
|
||||
"ai_data_analysis_enabled_description": "IA para sacar más partido a tus datos, configurar paneles, gráficos, informes y más. Accede a los datos de experiencia.",
|
||||
"ai_enabled": "IA de Formbricks",
|
||||
"ai_enabled_description": "Gestiona las funciones impulsadas por IA para esta organización.",
|
||||
"ai_settings_updated_successfully": "Configuración de IA actualizada correctamente",
|
||||
"ai_smart_tools_enabled": "Funcionalidad inteligente (IA)",
|
||||
"ai_smart_tools_enabled_description": "IA para ayudarte a conseguir más en menos tiempo. Nunca accede a los datos recopilados con Formbricks. Solo se usa para, por ejemplo, traducir encuestas a otros idiomas.",
|
||||
"bulk_invite_warning_description": "En el plan gratuito, a todos los miembros de la organización se les asigna siempre el rol de \"Propietario\".",
|
||||
"cannot_delete_only_organization": "Esta es tu única organización, no se puede eliminar. Crea una nueva organización primero.",
|
||||
"cannot_leave_only_organization": "No puedes abandonar esta organización ya que es tu única organización. Crea una nueva organización primero.",
|
||||
|
||||
@@ -1243,6 +1243,13 @@
|
||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "Débloquez tout le potentiel de Formbricks. Gratuit pendant 30 jours."
|
||||
},
|
||||
"general": {
|
||||
"ai_data_analysis_enabled": "Enrichissement et analyse des données (IA)",
|
||||
"ai_data_analysis_enabled_description": "L'IA pour tirer le meilleur parti de vos données, configurer des tableaux de bord, des graphiques, des rapports et plus encore. Accède à vos données d'expérience.",
|
||||
"ai_enabled": "IA Formbricks",
|
||||
"ai_enabled_description": "Gérer les fonctionnalités alimentées par l'IA pour cette organisation.",
|
||||
"ai_settings_updated_successfully": "Paramètres IA mis à jour avec succès",
|
||||
"ai_smart_tools_enabled": "Fonctionnalités intelligentes (IA)",
|
||||
"ai_smart_tools_enabled_description": "L'IA pour vous aider à accomplir plus en moins de temps. N'accède jamais aux données collectées avec Formbricks. Utilisée uniquement pour, par exemple, traduire les sondages dans d'autres langues.",
|
||||
"bulk_invite_warning_description": "Dans le plan gratuit, tous les membres de l'organisation se voient toujours attribuer le rôle \"Owner\".",
|
||||
"cannot_delete_only_organization": "C'est votre seule organisation, elle ne peut pas être supprimée. Créez d'abord une nouvelle organisation.",
|
||||
"cannot_leave_only_organization": "Vous ne pouvez pas quitter cette organisation car c'est votre seule organisation. Créez d'abord une nouvelle organisation.",
|
||||
|
||||
@@ -1243,6 +1243,13 @@
|
||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "A Formbricks teljes erejének feloldása. 30 napig ingyen."
|
||||
},
|
||||
"general": {
|
||||
"ai_data_analysis_enabled": "Adatgazdagítás és elemzés (AI)",
|
||||
"ai_data_analysis_enabled_description": "AI segítségével többet hozhat ki az adataiból, irányítópultokat, diagramokat, jelentéseket és egyebeket állíthat be. Hozzáfér az élményekhez kapcsolódó adatokhoz.",
|
||||
"ai_enabled": "Formbricks AI",
|
||||
"ai_enabled_description": "AI-alapú funkciók kezelése ehhez a szervezethez.",
|
||||
"ai_settings_updated_successfully": "AI beállítások sikeresen frissítve",
|
||||
"ai_smart_tools_enabled": "Intelligens funkciók (AI)",
|
||||
"ai_smart_tools_enabled_description": "AI segítségével kevesebb idő alatt többet érhet el. Soha nem fér hozzá a Formbricks által gyűjtött adatokhoz. Csak például felmérések más nyelvekre történő fordításához használatos.",
|
||||
"bulk_invite_warning_description": "Az ingyenes csomagban az összes szervezeti tag mindig a „Tulajdonos” szerephez van hozzárendelve.",
|
||||
"cannot_delete_only_organization": "Ez az egyetlen szervezete, nem lehet törölni. Először hozzon létre egy új szervezetet.",
|
||||
"cannot_leave_only_organization": "Nem hagyhatja el ezt a szervezetet, mivel ez az egyetlen szervezete. Először hozzon létre egy új szervezetet.",
|
||||
|
||||
@@ -1243,6 +1243,13 @@
|
||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "Formbricksの全機能をアンロック。30日間無料。"
|
||||
},
|
||||
"general": {
|
||||
"ai_data_analysis_enabled": "データエンリッチメントと分析(AI)",
|
||||
"ai_data_analysis_enabled_description": "AIを活用してデータから最大限の価値を引き出し、ダッシュボード、チャート、レポートなどを設定できます。エクスペリエンスデータに触れます。",
|
||||
"ai_enabled": "Formbricks AI",
|
||||
"ai_enabled_description": "この組織のAI機能を管理します。",
|
||||
"ai_settings_updated_successfully": "AI設定が正常に更新されました",
|
||||
"ai_smart_tools_enabled": "スマート機能(AI)",
|
||||
"ai_smart_tools_enabled_description": "AIを活用して、より短時間でより多くのことを達成できます。Formbricksで収集されたデータには一切触れません。アンケートを他の言語に翻訳するなどの用途にのみ使用されます。",
|
||||
"bulk_invite_warning_description": "無料プランでは、すべての組織メンバーに常に「オーナー」ロールが割り当てられます。",
|
||||
"cannot_delete_only_organization": "これはあなたの唯一の組織です。削除できません。まず新しい組織を作成してください。",
|
||||
"cannot_leave_only_organization": "これはあなたの唯一の組織であるため、離れることはできません。まず新しい組織を作成してください。",
|
||||
|
||||
@@ -1243,6 +1243,13 @@
|
||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "Ontgrendel de volledige kracht van Formbricks. 30 dagen gratis."
|
||||
},
|
||||
"general": {
|
||||
"ai_data_analysis_enabled": "Dataverrijking & analyse (AI)",
|
||||
"ai_data_analysis_enabled_description": "AI om meer uit je data te halen, dashboards op te zetten, grafieken, rapporten en meer. Raakt je ervaringsdata aan.",
|
||||
"ai_enabled": "Formbricks AI",
|
||||
"ai_enabled_description": "Beheer AI-functies voor deze organisatie.",
|
||||
"ai_settings_updated_successfully": "AI-instellingen succesvol bijgewerkt",
|
||||
"ai_smart_tools_enabled": "Slimme functionaliteit (AI)",
|
||||
"ai_smart_tools_enabled_description": "AI om je te helpen meer te bereiken in minder tijd. Raakt nooit data aan die met Formbricks is verzameld. Wordt alleen gebruikt om bijvoorbeeld enquêtes naar andere talen te vertalen.",
|
||||
"bulk_invite_warning_description": "Bij het gratis abonnement krijgen alle organisatieleden altijd de rol 'Eigenaar' toegewezen.",
|
||||
"cannot_delete_only_organization": "Dit is uw enige organisatie. Deze kan niet worden verwijderd. Maak eerst een nieuwe organisatie aan.",
|
||||
"cannot_leave_only_organization": "U kunt deze organisatie niet verlaten, aangezien dit uw enige organisatie is. Maak eerst een nieuwe organisatie aan.",
|
||||
|
||||
@@ -1243,6 +1243,13 @@
|
||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "Desbloqueie todo o poder do Formbricks. Grátis por 30 dias."
|
||||
},
|
||||
"general": {
|
||||
"ai_data_analysis_enabled": "Enriquecimento e análise de dados (IA)",
|
||||
"ai_data_analysis_enabled_description": "IA para extrair mais dos seus dados, configurar dashboards, gráficos, relatórios e muito mais. Acessa os dados da sua experiência.",
|
||||
"ai_enabled": "Formbricks AI",
|
||||
"ai_enabled_description": "Gerencie recursos com IA para esta organização.",
|
||||
"ai_settings_updated_successfully": "Configurações de IA atualizadas com sucesso",
|
||||
"ai_smart_tools_enabled": "Funcionalidades inteligentes (IA)",
|
||||
"ai_smart_tools_enabled_description": "IA para ajudar você a conquistar mais em menos tempo. Nunca acessa dados coletados com o Formbricks. Usado apenas para, por exemplo, traduzir pesquisas para outros idiomas.",
|
||||
"bulk_invite_warning_description": "Por favor, note que no Plano Gratuito, todos os membros da organização são automaticamente atribuídos ao papel de 'Owner', independentemente do papel especificado no arquivo CSV.",
|
||||
"cannot_delete_only_organization": "Essa é sua única organização, não pode ser deletada. Crie uma nova organização primeiro.",
|
||||
"cannot_leave_only_organization": "Você não pode sair dessa organização porque é a sua única. Crie uma nova organização primeiro.",
|
||||
|
||||
@@ -1243,6 +1243,13 @@
|
||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "Desbloqueie todo o poder do Formbricks. Grátis por 30 dias."
|
||||
},
|
||||
"general": {
|
||||
"ai_data_analysis_enabled": "Enriquecimento e análise de dados (IA)",
|
||||
"ai_data_analysis_enabled_description": "IA para tirar mais partido dos teus dados, configurar dashboards, gráficos, relatórios e muito mais. Acede aos dados da tua experiência.",
|
||||
"ai_enabled": "IA da Formbricks",
|
||||
"ai_enabled_description": "Gerir funcionalidades com IA para esta organização.",
|
||||
"ai_settings_updated_successfully": "Definições de IA atualizadas com sucesso",
|
||||
"ai_smart_tools_enabled": "Funcionalidade inteligente (IA)",
|
||||
"ai_smart_tools_enabled_description": "IA para te ajudar a alcançar mais em menos tempo. Nunca acede aos dados recolhidos com o Formbricks. Apenas usado para, por exemplo, traduzir inquéritos para outros idiomas.",
|
||||
"bulk_invite_warning_description": "No plano gratuito, todos os membros da organização são sempre atribuídos ao papel de \"Proprietário\".",
|
||||
"cannot_delete_only_organization": "Esta é a sua única organização, não pode ser eliminada. Crie uma nova organização primeiro.",
|
||||
"cannot_leave_only_organization": "Não pode sair desta organização, pois é a sua única organização. Crie uma nova organização primeiro.",
|
||||
|
||||
@@ -1243,6 +1243,13 @@
|
||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "Deblocați puterea completă a Formbricks. Gratuit timp de 30 de zile."
|
||||
},
|
||||
"general": {
|
||||
"ai_data_analysis_enabled": "Îmbogățire și analiză de date (AI)",
|
||||
"ai_data_analysis_enabled_description": "AI pentru a obține mai mult din datele tale, configurare dashboard-uri, grafice, rapoarte și multe altele. Accesează datele tale de experiență.",
|
||||
"ai_enabled": "Formbricks AI",
|
||||
"ai_enabled_description": "Gestionează funcționalitățile bazate pe AI pentru această organizație.",
|
||||
"ai_settings_updated_successfully": "Setările AI au fost actualizate cu succes",
|
||||
"ai_smart_tools_enabled": "Funcționalitate inteligentă (AI)",
|
||||
"ai_smart_tools_enabled_description": "AI care te ajută să faci mai mult în mai puțin timp. Nu accesează niciodată datele colectate cu Formbricks. Folosit doar, de exemplu, pentru a traduce chestionare în alte limbi.",
|
||||
"bulk_invite_warning_description": "În planul gratuit, toți membrii organizației sunt întotdeauna alocați rolului „Proprietar”.",
|
||||
"cannot_delete_only_organization": "Aceasta este singura ta organizație, nu poate fi ștearsă. Creează mai întâi o nouă organizație.",
|
||||
"cannot_leave_only_organization": "Nu poți părăsi această organizație deoarece este singura ta organizație. Creează mai întâi o nouă organizație.",
|
||||
|
||||
@@ -1243,6 +1243,13 @@
|
||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "Откройте все возможности Formbricks. Бесплатно на 30 дней."
|
||||
},
|
||||
"general": {
|
||||
"ai_data_analysis_enabled": "Обогащение и анализ данных (ИИ)",
|
||||
"ai_data_analysis_enabled_description": "ИИ для получения большего от твоих данных: настройка дашбордов, графиков, отчетов и не только. Работает с твоими данными об опыте.",
|
||||
"ai_enabled": "Formbricks AI",
|
||||
"ai_enabled_description": "Управляй функциями на базе ИИ для этой организации.",
|
||||
"ai_settings_updated_successfully": "Настройки AI успешно обновлены",
|
||||
"ai_smart_tools_enabled": "Умные функции (ИИ)",
|
||||
"ai_smart_tools_enabled_description": "ИИ помогает тебе делать больше за меньшее время. Никогда не использует данные, собранные с помощью Formbricks. Применяется, например, для перевода опросов на другие языки.",
|
||||
"bulk_invite_warning_description": "В бесплатном тарифе всем участникам организации всегда назначается роль \"Владелец\".",
|
||||
"cannot_delete_only_organization": "Это ваша единственная организация, её нельзя удалить. Сначала создайте новую организацию.",
|
||||
"cannot_leave_only_organization": "Вы не можете покинуть эту организацию, так как она у вас единственная. Сначала создайте новую организацию.",
|
||||
|
||||
@@ -1243,6 +1243,13 @@
|
||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "Lås upp Formbricks fulla kraft. Gratis i 30 dagar."
|
||||
},
|
||||
"general": {
|
||||
"ai_data_analysis_enabled": "Dataförbättring & analys (AI)",
|
||||
"ai_data_analysis_enabled_description": "AI för att få ut mer av din data, skapa dashboards, diagram, rapporter och mer. Använder din upplevelsedata.",
|
||||
"ai_enabled": "Formbricks AI",
|
||||
"ai_enabled_description": "Hantera AI-drivna funktioner för den här organisationen.",
|
||||
"ai_settings_updated_successfully": "AI-inställningarna har uppdaterats",
|
||||
"ai_smart_tools_enabled": "Smarta funktioner (AI)",
|
||||
"ai_smart_tools_enabled_description": "AI som hjälper dig att göra mer på kortare tid. Rör aldrig data som samlats in med Formbricks. Används bara till t.ex. att översätta enkäter till andra språk.",
|
||||
"bulk_invite_warning_description": "På gratisplanen tilldelas alla organisationsmedlemmar alltid rollen \"Ägare\".",
|
||||
"cannot_delete_only_organization": "Detta är din enda organisation, den kan inte tas bort. Skapa en ny organisation först.",
|
||||
"cannot_leave_only_organization": "Du kan inte lämna denna organisation eftersom det är din enda organisation. Skapa en ny organisation först.",
|
||||
|
||||
@@ -1243,6 +1243,13 @@
|
||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "解锁 Formbricks 的全部功能。免费使用 30 天。"
|
||||
},
|
||||
"general": {
|
||||
"ai_data_analysis_enabled": "数据增强与分析(AI)",
|
||||
"ai_data_analysis_enabled_description": "使用 AI 深度挖掘你的数据,设置仪表盘、图表、报告等。会处理你的体验数据。",
|
||||
"ai_enabled": "Formbricks AI",
|
||||
"ai_enabled_description": "管理该组织的 AI 驱动功能。",
|
||||
"ai_settings_updated_successfully": "AI 设置已成功更新",
|
||||
"ai_smart_tools_enabled": "智能功能(AI)",
|
||||
"ai_smart_tools_enabled_description": "AI 帮你更高效地完成更多任务。绝不会接触 Formbricks 收集的数据,仅用于如问卷翻译等功能。",
|
||||
"bulk_invite_warning_description": "在免费计划中,所有组织成员都会被分配为 \"Owner \"角色。",
|
||||
"cannot_delete_only_organization": "这是 您 唯一的 组织,不可 删除。请 先 创建一个新的 组织。",
|
||||
"cannot_leave_only_organization": "您 不能 离开 此 组织,因为 这是 您 唯一的 组织。请 先 创建一个新的 组织。",
|
||||
|
||||
@@ -1243,6 +1243,13 @@
|
||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "免費解鎖 Formbricks 的全部功能,為期 30 天。"
|
||||
},
|
||||
"general": {
|
||||
"ai_data_analysis_enabled": "資料增強與分析(AI)",
|
||||
"ai_data_analysis_enabled_description": "利用 AI 深入分析你的資料,建立儀表板、圖表、報告等。會處理你的體驗資料。",
|
||||
"ai_enabled": "Formbricks AI",
|
||||
"ai_enabled_description": "管理此組織的 AI 功能。",
|
||||
"ai_settings_updated_successfully": "AI 設定已成功更新",
|
||||
"ai_smart_tools_enabled": "智慧功能(AI)",
|
||||
"ai_smart_tools_enabled_description": "AI 幫你更快完成更多事。絕不會接觸 Formbricks 收集的資料,只用於像是將問卷翻譯成其他語言等用途。",
|
||||
"bulk_invite_warning_description": "在免費方案中,所有組織成員始終會被指派「擁有者」角色。",
|
||||
"cannot_delete_only_organization": "這是您唯一的組織,無法刪除。請先建立新組織。",
|
||||
"cannot_leave_only_organization": "您無法離開此組織,因為它是您唯一的組織。請先建立新組織。",
|
||||
|
||||
@@ -40,7 +40,8 @@ describe("getFirstOrganization", () => {
|
||||
projects: 3,
|
||||
},
|
||||
},
|
||||
isAIEnabled: false,
|
||||
isAISmartToolsEnabled: false,
|
||||
isAIDataAnalysisEnabled: false,
|
||||
};
|
||||
vi.mocked(prisma.organization.findFirst).mockResolvedValue(org);
|
||||
const result = await getFirstOrganization();
|
||||
|
||||
@@ -45,7 +45,8 @@ export const mockSamlAccount: Account = {
|
||||
export const mockOrganization: Organization = {
|
||||
id: "org-123",
|
||||
name: "Test Organization",
|
||||
isAIEnabled: false,
|
||||
isAISmartToolsEnabled: false,
|
||||
isAIDataAnalysisEnabled: false,
|
||||
whitelabel: {
|
||||
enabled: false,
|
||||
},
|
||||
|
||||
@@ -268,7 +268,8 @@ describe("utils.ts", () => {
|
||||
updatedAt: new Date("2024-01-02"),
|
||||
name: "Test Organization",
|
||||
billing: { plan: "free" },
|
||||
isAIEnabled: false,
|
||||
isAISmartToolsEnabled: false,
|
||||
isAIDataAnalysisEnabled: false,
|
||||
whitelabel: false,
|
||||
memberships: [
|
||||
{
|
||||
@@ -408,7 +409,8 @@ describe("utils.ts", () => {
|
||||
updatedAt: new Date("2024-01-02"),
|
||||
name: "Test Organization",
|
||||
billing: { plan: "free", limits: {} },
|
||||
isAIEnabled: false,
|
||||
isAISmartToolsEnabled: false,
|
||||
isAIDataAnalysisEnabled: false,
|
||||
whitelabel: false,
|
||||
memberships: [
|
||||
{
|
||||
@@ -512,7 +514,8 @@ describe("utils.ts", () => {
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
billing: { plan: "free", limits: {} },
|
||||
isAIEnabled: false,
|
||||
isAISmartToolsEnabled: false,
|
||||
isAIDataAnalysisEnabled: false,
|
||||
whitelabel: false,
|
||||
memberships: [], // No membership
|
||||
},
|
||||
@@ -596,7 +599,8 @@ describe("utils.ts", () => {
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
billing: { plan: "free", limits: {} },
|
||||
isAIEnabled: false,
|
||||
isAISmartToolsEnabled: false,
|
||||
isAIDataAnalysisEnabled: false,
|
||||
whitelabel: false,
|
||||
memberships: [{ userId: "user123", organizationId: "org123", role: "owner", accepted: true }],
|
||||
},
|
||||
@@ -635,7 +639,8 @@ describe("utils.ts", () => {
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
billing: { plan: "pro", limits: {} },
|
||||
isAIEnabled: true,
|
||||
isAISmartToolsEnabled: true,
|
||||
isAIDataAnalysisEnabled: true,
|
||||
whitelabel: true,
|
||||
memberships: [{ userId: "user123", organizationId: "org456", role: "member", accepted: true }],
|
||||
},
|
||||
|
||||
@@ -173,7 +173,8 @@ export const getEnvironmentWithRelations = reactCache(async (environmentId: stri
|
||||
updatedAt: true,
|
||||
name: true,
|
||||
billing: true,
|
||||
isAIEnabled: true,
|
||||
isAISmartToolsEnabled: true,
|
||||
isAIDataAnalysisEnabled: true,
|
||||
whitelabel: true,
|
||||
// Current user's membership only (filtered at DB level)
|
||||
memberships: {
|
||||
@@ -232,7 +233,8 @@ export const getEnvironmentWithRelations = reactCache(async (environmentId: stri
|
||||
updatedAt: data.project.organization.updatedAt,
|
||||
name: data.project.organization.name,
|
||||
billing: data.project.organization.billing,
|
||||
isAIEnabled: data.project.organization.isAIEnabled,
|
||||
isAISmartToolsEnabled: data.project.organization.isAISmartToolsEnabled,
|
||||
isAIDataAnalysisEnabled: data.project.organization.isAIDataAnalysisEnabled,
|
||||
whitelabel: data.project.organization.whitelabel,
|
||||
},
|
||||
environments: data.project.environments,
|
||||
|
||||
@@ -151,7 +151,8 @@ describe("Survey Editor Library Tests", () => {
|
||||
period: "monthly" as const,
|
||||
periodStart: new Date(),
|
||||
},
|
||||
isAIEnabled: false,
|
||||
isAISmartToolsEnabled: false,
|
||||
isAIDataAnalysisEnabled: false,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -82,8 +82,12 @@ describe("getOrganizationAIKeys", () => {
|
||||
});
|
||||
|
||||
const mockOrgId = "org_test789";
|
||||
const mockOrganizationData: Pick<Organization, "isAIEnabled" | "billing"> = {
|
||||
isAIEnabled: true,
|
||||
const mockOrganizationData: Pick<
|
||||
Organization,
|
||||
"isAISmartToolsEnabled" | "isAIDataAnalysisEnabled" | "billing"
|
||||
> = {
|
||||
isAISmartToolsEnabled: true,
|
||||
isAIDataAnalysisEnabled: true,
|
||||
billing: {
|
||||
plan: "free",
|
||||
stripeCustomerId: null,
|
||||
@@ -109,7 +113,8 @@ describe("getOrganizationAIKeys", () => {
|
||||
id: mockOrgId,
|
||||
},
|
||||
select: {
|
||||
isAIEnabled: true,
|
||||
isAISmartToolsEnabled: true,
|
||||
isAIDataAnalysisEnabled: true,
|
||||
billing: true,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -29,14 +29,17 @@ export const getOrganizationIdFromEnvironmentId = reactCache(
|
||||
);
|
||||
|
||||
export const getOrganizationAIKeys = reactCache(
|
||||
async (organizationId: string): Promise<Pick<Organization, "isAIEnabled" | "billing"> | null> => {
|
||||
async (
|
||||
organizationId: string
|
||||
): Promise<Pick<Organization, "isAISmartToolsEnabled" | "isAIDataAnalysisEnabled" | "billing"> | null> => {
|
||||
try {
|
||||
const organization = await prisma.organization.findUnique({
|
||||
where: {
|
||||
id: organizationId,
|
||||
},
|
||||
select: {
|
||||
isAIEnabled: true,
|
||||
isAISmartToolsEnabled: true,
|
||||
isAIDataAnalysisEnabled: true,
|
||||
billing: true,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -454,7 +454,8 @@ describe("data", () => {
|
||||
name: "Test Organization",
|
||||
billing: mockBilling,
|
||||
whitelabel: null,
|
||||
isAIEnabled: true,
|
||||
isAISmartToolsEnabled: true,
|
||||
isAIDataAnalysisEnabled: true,
|
||||
};
|
||||
|
||||
test("should fetch organization billing successfully", async () => {
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Organization" ADD COLUMN "isAISmartToolsEnabled" BOOLEAN NOT NULL DEFAULT false;
|
||||
ALTER TABLE "Organization" ADD COLUMN "isAIDataAnalysisEnabled" BOOLEAN NOT NULL DEFAULT false;
|
||||
ALTER TABLE "Organization" DROP COLUMN "isAIEnabled";
|
||||
@@ -663,22 +663,24 @@ model Project {
|
||||
/// @property projects - Collection of projects owned by the organization
|
||||
/// @property billing - JSON field containing billing information
|
||||
/// @property whitelabel - Whitelabel configuration for the organization
|
||||
/// @property isAIEnabled - Controls access to AI-powered features
|
||||
/// @property isAISmartToolsEnabled - Controls access to AI smart tools (e.g. translations) that never touch collected data
|
||||
/// @property isAIDataAnalysisEnabled - Controls access to AI data analysis features that touch experience data
|
||||
model Organization {
|
||||
id String @id @default(cuid())
|
||||
createdAt DateTime @default(now()) @map(name: "created_at")
|
||||
updatedAt DateTime @updatedAt @map(name: "updated_at")
|
||||
name String
|
||||
memberships Membership[]
|
||||
projects Project[]
|
||||
id String @id @default(cuid())
|
||||
createdAt DateTime @default(now()) @map(name: "created_at")
|
||||
updatedAt DateTime @updatedAt @map(name: "updated_at")
|
||||
name String
|
||||
memberships Membership[]
|
||||
projects Project[]
|
||||
/// [OrganizationBilling]
|
||||
billing Json
|
||||
billing Json
|
||||
/// [OrganizationWhitelabel]
|
||||
whitelabel Json @default("{}")
|
||||
invites Invite[]
|
||||
isAIEnabled Boolean @default(false)
|
||||
teams Team[]
|
||||
apiKeys ApiKey[]
|
||||
whitelabel Json @default("{}")
|
||||
invites Invite[]
|
||||
isAISmartToolsEnabled Boolean @default(false)
|
||||
isAIDataAnalysisEnabled Boolean @default(false)
|
||||
teams Team[]
|
||||
apiKeys ApiKey[]
|
||||
}
|
||||
|
||||
enum OrganizationRole {
|
||||
|
||||
@@ -37,5 +37,6 @@ export const ZOrganization = z.object({
|
||||
name: z.string(),
|
||||
whitelabel: ZOrganizationWhiteLabel,
|
||||
billing: ZOrganizationBilling as z.ZodType<Organization["billing"]>,
|
||||
isAIEnabled: z.boolean().default(false) as z.ZodType<Organization["isAIEnabled"]>,
|
||||
isAISmartToolsEnabled: z.boolean().default(false) as z.ZodType<Organization["isAISmartToolsEnabled"]>,
|
||||
isAIDataAnalysisEnabled: z.boolean().default(false) as z.ZodType<Organization["isAIDataAnalysisEnabled"]>,
|
||||
}) satisfies z.ZodType<Organization>;
|
||||
|
||||
@@ -134,7 +134,8 @@ export const exampleData = {
|
||||
},
|
||||
},
|
||||
},
|
||||
isAIEnabled: false,
|
||||
isAISmartToolsEnabled: false,
|
||||
isAIDataAnalysisEnabled: false,
|
||||
} as unknown as TOrganization,
|
||||
},
|
||||
|
||||
|
||||
@@ -50,7 +50,8 @@ export const ZOrganization = z.object({
|
||||
}),
|
||||
whitelabel: ZOrganizationWhitelabel.optional(),
|
||||
billing: ZOrganizationBilling,
|
||||
isAIEnabled: z.boolean().default(false),
|
||||
isAISmartToolsEnabled: z.boolean().default(false),
|
||||
isAIDataAnalysisEnabled: z.boolean().default(false),
|
||||
});
|
||||
|
||||
export const ZOrganizationCreateInput = z.object({
|
||||
@@ -64,7 +65,8 @@ export const ZOrganizationUpdateInput = z.object({
|
||||
name: z.string(),
|
||||
whitelabel: ZOrganizationWhitelabel.optional(),
|
||||
billing: ZOrganizationBilling.optional(),
|
||||
isAIEnabled: z.boolean().optional(),
|
||||
isAISmartToolsEnabled: z.boolean().optional(),
|
||||
isAIDataAnalysisEnabled: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export type TOrganizationUpdateInput = z.infer<typeof ZOrganizationUpdateInput>;
|
||||
|
||||
Reference in New Issue
Block a user