diff --git a/apps/api/src/app/bridge/usecases/sync/sync.usecase.ts b/apps/api/src/app/bridge/usecases/sync/sync.usecase.ts index c827a6a4f77..40ed76f02b5 100644 --- a/apps/api/src/app/bridge/usecases/sync/sync.usecase.ts +++ b/apps/api/src/app/bridge/usecases/sync/sync.usecase.ts @@ -11,8 +11,6 @@ import { CreateWorkflow, CreateWorkflowCommand, ExecuteBridgeRequest, - GetFeatureFlag, - GetFeatureFlagCommand, NotificationStep, UpdateWorkflow, UpdateWorkflowCommand, @@ -20,7 +18,6 @@ import { UpsertWorkflowPreferencesCommand, } from '@novu/application-generic'; import { - FeatureFlagsKeysEnum, WorkflowCreationSourceEnum, WorkflowOriginEnum, WorkflowTypeEnum, @@ -43,8 +40,7 @@ export class Sync { private environmentRepository: EnvironmentRepository, private executeBridgeRequest: ExecuteBridgeRequest, private analyticsService: AnalyticsService, - private upsertPreferences: UpsertPreferences, - private getFeatureFlag: GetFeatureFlag + private upsertPreferences: UpsertPreferences ) {} async execute(command: SyncCommand): Promise { const environment = await this.environmentRepository.findOne({ _id: command.environmentId }); @@ -177,26 +173,15 @@ export class Sync { savedWorkflow = await this.createWorkflow(notificationGroupId, isWorkflowActive, command, workflow); } - const isWorkflowPreferencesEnabled = await this.getFeatureFlag.execute( - GetFeatureFlagCommand.create({ - key: FeatureFlagsKeysEnum.IS_WORKFLOW_PREFERENCES_ENABLED, - environmentId: command.environmentId, - organizationId: command.organizationId, - userId: command.userId, + await this.upsertPreferences.upsertWorkflowPreferences( + UpsertWorkflowPreferencesCommand.create({ + environmentId: savedWorkflow._environmentId, + organizationId: savedWorkflow._organizationId, + templateId: savedWorkflow._id, + preferences: this.getWorkflowPreferences(workflow), }) ); - if (isWorkflowPreferencesEnabled) { - await this.upsertPreferences.upsertWorkflowPreferences( - UpsertWorkflowPreferencesCommand.create({ - environmentId: savedWorkflow._environmentId, - organizationId: savedWorkflow._organizationId, - templateId: savedWorkflow._id, - preferences: this.getWorkflowPreferences(workflow), - }) - ); - } - return savedWorkflow; }) ); diff --git a/apps/api/src/app/events/e2e/bridge-sync.e2e.ts b/apps/api/src/app/events/e2e/bridge-sync.e2e.ts index 8a6df9d0692..545d42e7f00 100644 --- a/apps/api/src/app/events/e2e/bridge-sync.e2e.ts +++ b/apps/api/src/app/events/e2e/bridge-sync.e2e.ts @@ -22,8 +22,6 @@ describe('Bridge Sync - /bridge/sync (POST)', async () => { let bridgeServer: BridgeServer; beforeEach(async () => { - // @ts-ignore - process.env[FeatureFlagsKeysEnum.IS_WORKFLOW_PREFERENCES_ENABLED] = 'true'; session = new UserSession(); await session.initialize(); bridgeServer = new BridgeServer(); @@ -31,8 +29,6 @@ describe('Bridge Sync - /bridge/sync (POST)', async () => { afterEach(async () => { await bridgeServer.stop(); - // @ts-ignore - process.env[FeatureFlagsKeysEnum.IS_WORKFLOW_PREFERENCES_ENABLED] = 'false'; }); it('should update bridge url', async () => { diff --git a/apps/api/src/app/events/e2e/bridge-trigger.e2e.ts b/apps/api/src/app/events/e2e/bridge-trigger.e2e.ts index 9f112e2cbb2..ed9ef43261e 100644 --- a/apps/api/src/app/events/e2e/bridge-trigger.e2e.ts +++ b/apps/api/src/app/events/e2e/bridge-trigger.e2e.ts @@ -200,7 +200,7 @@ contexts.forEach((context: Context) => { await syncWorkflow(session, workflowsRepository, workflowIdSkipByStatic, bridgeServer); } - await triggerEvent(session, workflowIdSkipByStatic, subscriber.subscriberId, null, bridge); + await triggerEvent(session, workflowIdSkipByStatic, subscriber.subscriberId, {}, bridge); await session.awaitRunningJobs(); const executedMessageByStatic = await messageRepository.find({ @@ -264,7 +264,7 @@ contexts.forEach((context: Context) => { await syncWorkflow(session, workflowsRepository, workflowIdSkipByVariable, bridgeServer); } - await triggerEvent(session, workflowIdSkipByVariable, subscriber.subscriberId, null, bridge); + await triggerEvent(session, workflowIdSkipByVariable, subscriber.subscriberId, {}, bridge); await session.awaitRunningJobs(); const executedMessage = await messageRepository.find({ @@ -556,7 +556,7 @@ contexts.forEach((context: Context) => { await discoverAndSyncBridge(session, workflowsRepository, workflowId, bridgeServer); } - await triggerEvent(session, workflowId, subscriber.subscriberId, null, bridge); + await triggerEvent(session, workflowId, subscriber.subscriberId, {}, bridge); await session.awaitRunningJobs(); @@ -672,7 +672,7 @@ contexts.forEach((context: Context) => { } const controls = { steps: { [stepId]: { name: 'stored_control_name' } } }; - await triggerEvent(session, workflowId, subscriber.subscriberId, undefined, bridge, controls); + await triggerEvent(session, workflowId, subscriber.subscriberId, {}, bridge, controls); await session.awaitRunningJobs(); const sentMessage = await messageRepository.find({ @@ -715,7 +715,6 @@ contexts.forEach((context: Context) => { }); it(`should deliver message if the Workflow Definition doesn't contain preferences [${context.name}]`, async () => { - process.env.IS_WORKFLOW_PREFERENCES_ENABLED = 'true'; const workflowId = `without-preferences-workflow-${`${context.name}-${uuidv4()}`}`; const newWorkflow = workflow(workflowId, async ({ step }) => { await step.inApp('send-in-app', () => ({ body: 'Hello there 1' })); @@ -725,6 +724,7 @@ contexts.forEach((context: Context) => { * Delete `preferences` from the Workflow Definition to simulate an old * Workflow Definition (i.e. from old Framework version) that doesn't have the `preferences` property. */ + // @ts-ignore - The operand of a 'delete' operator must be optional. delete newWorkflow.definition.preferences; await bridgeServer.start({ workflows: [newWorkflow] }); @@ -747,7 +747,6 @@ contexts.forEach((context: Context) => { }); it(`should deliver message if inApp is enabled via workflow preferences [${context.name}]`, async () => { - process.env.IS_WORKFLOW_PREFERENCES_ENABLED = 'true'; const workflowId = `enabled-inapp-workflow-${`${context.name}-${uuidv4()}`}`; const newWorkflow = workflow( workflowId, @@ -785,7 +784,6 @@ contexts.forEach((context: Context) => { }); it(`should NOT deliver message if inApp is disabled via workflow preferences [${context.name}]`, async () => { - process.env.IS_WORKFLOW_PREFERENCES_ENABLED = 'true'; const workflowId = `disabled-inapp-workflow-${`${context.name}-${uuidv4()}`}`; const newWorkflow = workflow( workflowId, @@ -834,7 +832,6 @@ contexts.forEach((context: Context) => { }); it(`should deliver inApp message if workflow is disabled via workflow preferences and inApp is enabled [${context.name}]`, async () => { - process.env.IS_WORKFLOW_PREFERENCES_ENABLED = 'true'; const workflowId = `disabled-workflow-${`${context.name}-${uuidv4()}`}`; const newWorkflow = workflow( workflowId, @@ -875,7 +872,6 @@ contexts.forEach((context: Context) => { }); it(`should NOT deliver inApp message if workflow is disabled via workflow preferences [${context.name}]`, async () => { - process.env.IS_WORKFLOW_PREFERENCES_ENABLED = 'true'; const workflowId = `disabled-workflow-${`${context.name}-${uuidv4()}`}`; const newWorkflow = workflow( workflowId, @@ -921,8 +917,8 @@ contexts.forEach((context: Context) => { expect(executionDetailsWorkflowFiltered.length).to.be.eq(1); }); + // eslint-disable-next-line max-len it(`should deliver inApp message if subscriber disabled inApp channel for readOnly workflow with inApp enabled [${context.name}]`, async () => { - process.env.IS_WORKFLOW_PREFERENCES_ENABLED = 'true'; const workflowId = `enabled-readonly-workflow-level-${`${context.name}-${uuidv4()}`}`; const newWorkflow = workflow( workflowId, @@ -974,8 +970,8 @@ contexts.forEach((context: Context) => { expect(sentMessages.length).to.be.eq(1); }); + // eslint-disable-next-line max-len it(`should NOT deliver inApp message if subscriber enables inApp channel for readOnly workflow with inApp disabled [${context.name}]`, async () => { - process.env.IS_WORKFLOW_PREFERENCES_ENABLED = 'true'; const workflowId = `disabled-readonly-workflow-level-${`${context.name}-${uuidv4()}`}`; const newWorkflow = workflow( workflowId, @@ -1038,8 +1034,8 @@ contexts.forEach((context: Context) => { expect(executionDetailsWorkflowFiltered.length).to.be.eq(1); }); + // eslint-disable-next-line max-len it(`should deliver inApp message if subscriber disabled inApp channel globally for readOnly workflow with inApp enabled [${context.name}]`, async () => { - process.env.IS_WORKFLOW_PREFERENCES_ENABLED = 'true'; const workflowId = `enabled-readonly-global-level-${`${context.name}-${uuidv4()}`}`; const newWorkflow = workflow( workflowId, @@ -1089,8 +1085,8 @@ contexts.forEach((context: Context) => { expect(sentMessages.length).to.be.eq(1); }); + // eslint-disable-next-line max-len it(`should NOT deliver inApp message if subscriber enabled inApp channel globally for readOnly workflow with inApp disabled [${context.name}]`, async () => { - process.env.IS_WORKFLOW_PREFERENCES_ENABLED = 'true'; const workflowId = `disabled-readonly-global-level-${`${context.name}-${uuidv4()}`}`; const newWorkflow = workflow( workflowId, @@ -1151,6 +1147,7 @@ contexts.forEach((context: Context) => { expect(executionDetailsWorkflowFiltered.length).to.be.eq(1); }); + // eslint-disable-next-line max-len it(`should deliver inApp message if subscriber enabled inApp channel globally for workflow with inApp disabled [${context.name}]`, async () => { if (!context.isStateful) { /* @@ -1159,7 +1156,6 @@ contexts.forEach((context: Context) => { */ expect(true).to.equal(true); } else { - process.env.IS_WORKFLOW_PREFERENCES_ENABLED = 'true'; const workflowId = `disabled-editable-global-level-${`${context.name}-${uuidv4()}`}`; const newWorkflow = workflow( workflowId, @@ -1206,6 +1202,7 @@ contexts.forEach((context: Context) => { } }); + // eslint-disable-next-line max-len it(`should NOT deliver inApp message if subscriber disabled inApp channel globally for workflow with inApp enabled [${context.name}]`, async () => { if (!context.isStateful) { /* @@ -1214,7 +1211,6 @@ contexts.forEach((context: Context) => { */ expect(true).to.equal(true); } else { - process.env.IS_WORKFLOW_PREFERENCES_ENABLED = 'true'; const workflowId = `enabled-editable-global-level-${`${context.name}-${uuidv4()}`}`; const newWorkflow = workflow( workflowId, @@ -1272,6 +1268,7 @@ contexts.forEach((context: Context) => { } }); + // eslint-disable-next-line max-len it(`should deliver inApp message if subscriber disabled inApp channel globally but enabled inApp for workflow with inApp disabled [${context.name}]`, async () => { if (!context.isStateful) { /* @@ -1280,7 +1277,6 @@ contexts.forEach((context: Context) => { */ expect(true).to.equal(true); } else { - process.env.IS_WORKFLOW_PREFERENCES_ENABLED = 'true'; const workflowId = `disabled-editable-global-workflow-level-${`${context.name}-${uuidv4()}`}`; const newWorkflow = workflow( workflowId, @@ -1336,6 +1332,7 @@ contexts.forEach((context: Context) => { } }); + // eslint-disable-next-line max-len it(`should NOT deliver inApp message if subscriber enabled inApp channel globally but disabled inApp for workflow with inApp enabled [${context.name}]`, async () => { if (!context.isStateful) { /* @@ -1344,7 +1341,6 @@ contexts.forEach((context: Context) => { */ expect(true).to.equal(true); } else { - process.env.IS_WORKFLOW_PREFERENCES_ENABLED = 'true'; const workflowId = `enabled-editable-global-workflow-level-${`${context.name}-${uuidv4()}`}`; const newWorkflow = workflow( workflowId, @@ -1546,7 +1542,7 @@ async function saveControlValues( session: UserSession, workflowIdentifier?: string, stepIdentifier?: string, - payloadBody?: any + payloadBody?: Record ) { return await session.testAgent.put(`/v1/bridge/controls/${workflowIdentifier}/${stepIdentifier}`).send(payloadBody); } diff --git a/apps/api/src/app/events/e2e/trigger-event.e2e.ts b/apps/api/src/app/events/e2e/trigger-event.e2e.ts index e51c829411f..5e01422cc65 100644 --- a/apps/api/src/app/events/e2e/trigger-event.e2e.ts +++ b/apps/api/src/app/events/e2e/trigger-event.e2e.ts @@ -3406,7 +3406,11 @@ describe(`Trigger event - ${eventTriggerPath} (POST)`, function () { expect(messages2.length).to.equal(0); }); - it('should override - active true', async function () { + /* + * TODO: we need to add support for Tenants in V2 Preferences + * This test is skipped for now as the tenant-level active flag is not taken into account for V2 Preferences + */ + it.skip('should override - active true', async function () { const subscriberOverride = SubscriberRepository.createObjectId(); // Create active workflow diff --git a/apps/api/src/app/preferences/preferences.controller.ts b/apps/api/src/app/preferences/preferences.controller.ts index 24f6a54a3f8..6046d10d8b7 100644 --- a/apps/api/src/app/preferences/preferences.controller.ts +++ b/apps/api/src/app/preferences/preferences.controller.ts @@ -4,15 +4,12 @@ import { Controller, Delete, Get, - NotFoundException, Post, Query, UseGuards, UseInterceptors, } from '@nestjs/common'; import { - GetFeatureFlag, - GetFeatureFlagCommand, GetPreferences, GetPreferencesCommand, UpsertPreferences, @@ -20,7 +17,7 @@ import { UserAuthGuard, UserSession, } from '@novu/application-generic'; -import { FeatureFlagsKeysEnum, UserSessionData } from '@novu/shared'; +import { UserSessionData } from '@novu/shared'; import { ApiExcludeController } from '@nestjs/swagger'; import { UpsertPreferencesDto } from './dtos/upsert-preferences.dto'; @@ -30,15 +27,12 @@ import { UpsertPreferencesDto } from './dtos/upsert-preferences.dto'; export class PreferencesController { constructor( private upsertPreferences: UpsertPreferences, - private getPreferences: GetPreferences, - private getFeatureFlag: GetFeatureFlag + private getPreferences: GetPreferences ) {} @Get('/') @UseGuards(UserAuthGuard) async get(@UserSession() user: UserSessionData, @Query('workflowId') workflowId: string) { - await this.verifyPreferencesApiAvailability(user); - return this.getPreferences.execute( GetPreferencesCommand.create({ templateId: workflowId, @@ -51,8 +45,6 @@ export class PreferencesController { @Post('/') @UseGuards(UserAuthGuard) async upsert(@Body() data: UpsertPreferencesDto, @UserSession() user: UserSessionData) { - await this.verifyPreferencesApiAvailability(user); - return this.upsertPreferences.upsertUserWorkflowPreferences( UpsertUserWorkflowPreferencesCommand.create({ environmentId: user.environmentId, @@ -67,8 +59,6 @@ export class PreferencesController { @Delete('/') @UseGuards(UserAuthGuard) async delete(@UserSession() user: UserSessionData, @Query('workflowId') workflowId: string) { - await this.verifyPreferencesApiAvailability(user); - return this.upsertPreferences.upsertUserWorkflowPreferences( UpsertUserWorkflowPreferencesCommand.create({ environmentId: user.environmentId, @@ -79,19 +69,4 @@ export class PreferencesController { }) ); } - - private async verifyPreferencesApiAvailability(user: UserSessionData) { - const isEnabled = await this.getFeatureFlag.execute( - GetFeatureFlagCommand.create({ - userId: user._id, - environmentId: user.environmentId, - organizationId: user.organizationId, - key: FeatureFlagsKeysEnum.IS_WORKFLOW_PREFERENCES_ENABLED, - }) - ); - - if (!isEnabled) { - throw new NotFoundException(); - } - } } diff --git a/apps/api/src/app/preferences/preferences.spec.ts b/apps/api/src/app/preferences/preferences.spec.ts index 816f9b09e95..358b211d309 100644 --- a/apps/api/src/app/preferences/preferences.spec.ts +++ b/apps/api/src/app/preferences/preferences.spec.ts @@ -23,8 +23,6 @@ describe('Preferences', function () { let session: UserSession; beforeEach(async () => { - // @ts-ignore - process.env[FeatureFlagsKeysEnum.IS_WORKFLOW_PREFERENCES_ENABLED] = 'true'; const moduleRef = await Test.createTestingModule({ imports: [PreferencesModule, AuthModule], providers: [], diff --git a/apps/api/src/app/workflows-v2/generate-preview.e2e.ts b/apps/api/src/app/workflows-v2/generate-preview.e2e.ts index 0a11d5c2175..b2b4416fdf7 100644 --- a/apps/api/src/app/workflows-v2/generate-preview.e2e.ts +++ b/apps/api/src/app/workflows-v2/generate-preview.e2e.ts @@ -25,8 +25,6 @@ describe('Generate Preview', () => { session = new UserSession(); await session.initialize(); workflowsClient = createWorkflowClient(session.serverUrl, getHeaders()); - // @ts-ignore - process.env[FeatureFlagsKeysEnum.IS_WORKFLOW_PREFERENCES_ENABLED] = 'true'; }); after(async () => { await sleep(1000); diff --git a/apps/api/src/app/workflows-v2/workflow.controller.e2e.ts b/apps/api/src/app/workflows-v2/workflow.controller.e2e.ts index 0e93970c61c..481757d0215 100644 --- a/apps/api/src/app/workflows-v2/workflow.controller.e2e.ts +++ b/apps/api/src/app/workflows-v2/workflow.controller.e2e.ts @@ -38,8 +38,6 @@ describe('Workflow Controller E2E API Testing', () => { let workflowsClient: ReturnType; beforeEach(async () => { - // @ts-ignore - process.env.IS_WORKFLOW_PREFERENCES_ENABLED = 'true'; session = new UserSession(); await session.initialize(); workflowsClient = createWorkflowClient(session.serverUrl, getHeaders()); @@ -52,8 +50,6 @@ describe('Workflow Controller E2E API Testing', () => { } it('Smoke Testing', async () => { - // @ts-ignore - process.env.IS_WORKFLOW_PREFERENCES_ENABLED = 'true'; const workflowCreated = await createWorkflowAndValidate(); await getWorkflowAndValidate(workflowCreated); const updateRequest = buildUpdateRequest(workflowCreated); diff --git a/apps/dashboard/src/api/workflows.ts b/apps/dashboard/src/api/workflows.ts index 104af4d656c..70e978f091c 100644 --- a/apps/dashboard/src/api/workflows.ts +++ b/apps/dashboard/src/api/workflows.ts @@ -6,18 +6,18 @@ import type { } from '@novu/shared'; import { getV2, post, postV2, putV2 } from './api.client'; -export const fetchWorkflow = async ({ workflowId }: { workflowId?: string }): Promise => { - const { data } = await getV2<{ data: WorkflowResponseDto }>(`/workflows/${workflowId}`); +export const fetchWorkflow = async ({ workflowSlug }: { workflowSlug?: string }): Promise => { + const { data } = await getV2<{ data: WorkflowResponseDto }>(`/workflows/${workflowSlug}`); return data; }; export const fetchWorkflowTestData = async ({ - workflowId, + workflowSlug, }: { - workflowId?: string; + workflowSlug?: string; }): Promise => { - const { data } = await getV2<{ data: WorkflowTestDataResponseDto }>(`/workflows/${workflowId}/test-data`); + const { data } = await getV2<{ data: WorkflowTestDataResponseDto }>(`/workflows/${workflowSlug}/test-data`); return data; }; diff --git a/apps/dashboard/src/components/primitives/input.tsx b/apps/dashboard/src/components/primitives/input.tsx index ec02bddc581..598ec92d302 100644 --- a/apps/dashboard/src/components/primitives/input.tsx +++ b/apps/dashboard/src/components/primitives/input.tsx @@ -5,11 +5,12 @@ import { cva, VariantProps } from 'class-variance-authority'; import { inputVariants } from '@/components/primitives/variants'; const inputFieldVariants = cva( - 'text-foreground-950 flex w-full flex-nowrap items-center gap-1.5 rounded-md border bg-transparent shadow-sm transition-colors focus-within:outline-none focus-visible:outline-none hover:bg-neutral-50 has-[input:disabled]:cursor-not-allowed has-[input:disabled]:opacity-50 has-[input[value=""]]:text-foreground-400 has-[input:disabled]:bg-neutral-alpha-100 has-[input:disabled]:text-foreground-300', + 'text-foreground-950 flex w-full flex-nowrap items-center gap-1.5 rounded-md border bg-transparent transition-colors focus-within:outline-none focus-visible:outline-none hover:bg-neutral-50 has-[input:disabled]:cursor-not-allowed has-[input:disabled]:opacity-50 has-[input[value=""]]:text-foreground-400 has-[input:disabled]:bg-neutral-alpha-100 has-[input:disabled]:text-foreground-300', { variants: { size: { default: 'h-8 px-2 [&>input]:py-1.5', + md: 'h-10 px-3 [&>input]:py-2.5', }, state: { default: diff --git a/apps/dashboard/src/components/primitives/tabs.tsx b/apps/dashboard/src/components/primitives/tabs.tsx index f194a5d7a61..78570186c44 100644 --- a/apps/dashboard/src/components/primitives/tabs.tsx +++ b/apps/dashboard/src/components/primitives/tabs.tsx @@ -2,43 +2,76 @@ import * as React from 'react'; import * as TabsPrimitive from '@radix-ui/react-tabs'; import { cn } from '@/utils/ui'; +import { cva, VariantProps } from 'class-variance-authority'; -const TabsList = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); +const tabsListVariants = cva('inline-flex items-center', { + variants: { + variant: { + default: 'h-9 justify-center rounded-[10px] bg-neutral-alpha-100 p-1 text-muted-foreground', + regular: 'border-neutral-alpha-200 w-full justify-start gap-6 border-b border-t px-3.5', + }, + }, + defaultVariants: { + variant: 'default', + }, +}); + +type TabsListProps = React.ComponentPropsWithoutRef & VariantProps; + +const TabsList = React.forwardRef, TabsListProps>( + ({ className, variant, ...props }, ref) => ( + + ) +); TabsList.displayName = TabsPrimitive.List.displayName; -const TabsTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); +const tabsTriggerVariants = cva( + 'inline-flex items-center justify-center whitespace-nowrap rounded-md font-medium ring-offset-background transition-all text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none data-[state=active]:text-foreground disabled:opacity-50', + { + variants: { + variant: { + default: + 'px-3 py-1 data-[state=active]:bg-background data-[state=active]:shadow data-[state=inactive]:text-foreground-400', + regular: + "text-foreground-600 data-[state=active]:after:border-primary data-[state=active]:text-foreground-950 relative py-3.5 duration-300 ease-out after:absolute after:bottom-0 after:left-0 after:h-[2px] after:w-full after:border-b-2 after:border-b-transparent after:content-['']", + }, + }, + defaultVariants: { + variant: 'default', + }, + } +); + +type TabsTriggerProps = React.ComponentPropsWithoutRef & + VariantProps; + +const TabsTrigger = React.forwardRef, TabsTriggerProps>( + ({ className, variant, ...props }, ref) => ( + + ) +); TabsTrigger.displayName = TabsPrimitive.Trigger.displayName; -const TabsContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); +const tabsContentVariants = cva('focus-visible:outline-none', { + variants: { + variant: { + default: 'ring-offset-background focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2', + regular: 'mt-2', + }, + }, + defaultVariants: { + variant: 'default', + }, +}); + +type TabsContentProps = React.ComponentPropsWithoutRef & + VariantProps; + +const TabsContent = React.forwardRef, TabsContentProps>( + ({ className, variant, ...props }, ref) => ( + + ) +); TabsContent.displayName = TabsPrimitive.Content.displayName; const Tabs = React.forwardRef< diff --git a/apps/dashboard/src/components/primitives/textarea.tsx b/apps/dashboard/src/components/primitives/textarea.tsx index 072953042d4..c871447948b 100644 --- a/apps/dashboard/src/components/primitives/textarea.tsx +++ b/apps/dashboard/src/components/primitives/textarea.tsx @@ -9,6 +9,7 @@ const textareaVariants = cva( variants: { size: { default: 'h-8 px-2 py-1.5', + md: 'h-10 px-3 py-2.5', }, state: { default: diff --git a/apps/dashboard/src/components/primitives/url-input.tsx b/apps/dashboard/src/components/primitives/url-input.tsx index 18c646f812d..ec5976936d3 100644 --- a/apps/dashboard/src/components/primitives/url-input.tsx +++ b/apps/dashboard/src/components/primitives/url-input.tsx @@ -1,6 +1,6 @@ 'use client'; -import { Input, InputField, InputProps } from '@/components/primitives/input'; +import { Input, InputField, InputFieldProps, InputProps } from '@/components/primitives/input'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/primitives/select'; import { RedirectTargetEnum } from '@novu/shared'; import { forwardRef } from 'react'; @@ -10,19 +10,19 @@ type URLValue = { url: string; }; -type URLInputProps = Omit & { +type URLInputProps = Omit & { options: string[]; value: URLValue; onChange: (value: URLValue) => void; -}; +} & Pick; export const URLInput = forwardRef((props, ref) => { - const { options, value, onChange, ...rest } = props; + const { options, value, onChange, size, ...rest } = props; return (
- + ((props, ref) {...rest} /> + +
+