Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ import { createFileRoute, useParams } from '@tanstack/react-router'
import { Suspense } from 'react'
import { match } from 'ts-pattern'
import { useDeployService, useService } from '@qovery/domains/services/feature'
import { VariableList, VariablesActionToolbar } from '@qovery/domains/variables/feature'
import { Heading, LoaderSpinner, Section, toast } from '@qovery/shared/ui'
import {
ImportEnvironmentVariableModalFeature,
VariableList,
VariablesActionToolbar,
} from '@qovery/domains/variables/feature'
import { Heading, LoaderSpinner, Section, toast, useModal } from '@qovery/shared/ui'
import { useDocumentTitle } from '@qovery/shared/util-hooks'

export const Route = createFileRoute(
Expand Down Expand Up @@ -35,6 +39,7 @@ function RouteComponent() {
projectId,
environmentId,
})
const { openModal, closeModal } = useModal()

const toasterCallback = () => {
if (!service?.serviceType) {
Expand Down Expand Up @@ -65,6 +70,24 @@ function RouteComponent() {
projectId={projectId}
environmentId={environmentId}
serviceId={serviceId}
importEnvFileAccess="dropdown"
onImportEnvFile={() =>
openModal({
content: (
<ImportEnvironmentVariableModalFeature
scope={scope}
projectId={projectId}
environmentId={environmentId}
serviceId={serviceId}
closeModal={closeModal}
serviceType={service?.serviceType}
/>
),
options: {
width: 750,
},
})
}
onCreateVariable={() =>
toast(
'SUCCESS',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import * as Dialog from '@radix-ui/react-dialog'
import { APIVariableScopeEnum, type ServiceTypeForVariableEnum } from 'qovery-typescript-axios'
import { useCallback, useEffect, useState } from 'react'
import { type DropzoneRootProps, useDropzone } from 'react-dropzone'
import { Controller, FormProvider, useForm, useFormContext } from 'react-hook-form'
import { type ServiceType } from '@qovery/domains/services/data-access'
import { type EnvironmentVariableSecretOrPublic } from '@qovery/shared/interfaces'
import { Button, Dropzone, Icon, InputSelectSmall, InputTextSmall, InputToggle, useModal } from '@qovery/shared/ui'
import {
Button,
Dropzone,
Heading,
Icon,
InputSelectSmall,
InputTextSmall,
InputToggle,
useModal,
} from '@qovery/shared/ui'
import { Section } from '@qovery/shared/ui'
import { computeAvailableScope, generateScopeLabel, parseEnvText } from '@qovery/shared/util-js'
import { useImportVariables } from '../hooks/use-import-variables/use-import-variables'
import { useVariables } from '../hooks/use-variables/use-variables'
Expand Down Expand Up @@ -69,11 +80,18 @@ export function ImportEnvironmentVariableModal(props: ImportEnvironmentVariableM
const pattern = /^[^\s]+$/

return (
<div className="p-6">
<h2 className="h4 mb-6 max-w-sm text-neutral">Import variables from .env file</h2>
<Section className="p-6">
<Dialog.Title asChild>
<Heading className="mb-6">Import variables from .env file</Heading>
</Dialog.Title>

{props.showDropzone ? (
<div {...props.dropzoneGetRootProps({ className: 'dropzone' })}>
<div
{...props.dropzoneGetRootProps({
className:
'rounded outline-none focus:outline-none focus-visible:outline focus-visible:outline-2 focus-visible:outline-brand-11',
})}
>
<input data-testid="drop-input" {...props.dropzoneGetInputProps()} />
<Dropzone isDragActive={props.dropzoneIsDragActive} />
</div>
Expand Down Expand Up @@ -216,7 +234,7 @@ export function ImportEnvironmentVariableModal(props: ImportEnvironmentVariableM
</div>

<div className="flex h-full w-full grow items-center">
<Button type="button" variant="plain" size="md" onClick={() => props.deleteKey(key)}>
<Button type="button" variant="outline" iconOnly size="md" onClick={() => props.deleteKey(key)}>
<Icon className="text-base" iconName="trash-can" iconStyle="regular" />
</Button>
</div>
Expand All @@ -241,7 +259,7 @@ export function ImportEnvironmentVariableModal(props: ImportEnvironmentVariableM
</form>
</>
)}
</div>
</Section>
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ exports[`VariableList should match snapshot 1`] = `
class="no-scrollbar overflow-y-hidden overflow-x-scroll rounded-md border border-neutral bg-surface-neutral"
>
<table
class="divide-y divide-neutral text-neutral w-full min-w-[800px] text-xs"
class="divide-y divide-neutral text-neutral w-full min-w-[1200px] text-xs"
>
<thead>
<tr
Expand All @@ -46,7 +46,7 @@ exports[`VariableList should match snapshot 1`] = `
class="h-11 border-neutral px-4 text-left font-code group relative flex items-center font-medium"
>
<button
class="flex items-center gap-1 cursor-pointer select-none"
class="flex items-center gap-1 cursor-pointer select-none truncate"
type="button"
>
8 variables
Expand Down Expand Up @@ -146,7 +146,7 @@ exports[`VariableList should match snapshot 1`] = `
class="h-11 border-neutral px-4 text-left font-code group relative flex items-center font-medium"
>
<button
class="flex items-center gap-1 cursor-pointer select-none"
class="flex items-center gap-1 cursor-pointer select-none truncate"
type="button"
>
Last update
Expand Down Expand Up @@ -1359,7 +1359,7 @@ exports[`VariableList should match snapshot 1`] = `
class="no-scrollbar overflow-y-hidden overflow-x-scroll rounded-md border border-neutral bg-surface-neutral"
>
<table
class="divide-y divide-neutral text-neutral w-full min-w-[800px] text-xs"
class="divide-y divide-neutral text-neutral w-full min-w-[1200px] text-xs"
>
<thead>
<tr
Expand All @@ -1369,7 +1369,7 @@ exports[`VariableList should match snapshot 1`] = `
class="h-11 border-neutral px-4 text-left font-code group relative flex items-center font-medium"
>
<button
class="flex items-center gap-1 cursor-pointer select-none"
class="flex items-center gap-1 cursor-pointer select-none truncate"
type="button"
>
1 variable
Expand Down Expand Up @@ -1447,7 +1447,7 @@ exports[`VariableList should match snapshot 1`] = `
class="h-11 border-neutral px-4 text-left font-code group relative flex items-center font-medium"
>
<button
class="flex items-center gap-1 cursor-pointer select-none"
class="flex items-center gap-1 cursor-pointer select-none truncate"
type="button"
>
Last update
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export function VariableListSkeleton() {
const columnSizes = ['40%', '20%', '15%', '10%', '12%']

return (
<div className="flex grow flex-col justify-between">
<div className="flex min-w-[1200px] grow flex-col justify-between">
<Table.Root className="w-full">
<Table.Header>
<Table.Row>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,7 @@ export function VariableList({
const hideServiceLinkColumn = isServiceScope && !isBuiltInTable
return (
<div className="flex grow flex-col justify-between">
<Table.Root className={twMerge('w-full min-w-[800px] text-xs', className)}>
<Table.Root className={twMerge('w-full min-w-[1200px] text-xs', className)}>
<Table.Header>
{tableInstance.getHeaderGroups().map((headerGroup) => (
<Table.Row key={headerGroup.id} className={twMerge('w-full items-center text-xs', rowGridClassName)}>
Expand All @@ -634,7 +634,7 @@ export function VariableList({
type="button"
className={twMerge(
'flex items-center gap-1',
header.column.getCanSort() ? 'cursor-pointer select-none' : ''
header.column.getCanSort() ? 'cursor-pointer select-none truncate' : ''
)}
onClick={header.column.getToggleSortingHandler()}
>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { renderWithProviders, screen } from '@qovery/shared/util-tests'
import { VariablesActionToolbar, type VariablesActionToolbarProps } from './variables-action-toolbar'

const props: VariablesActionToolbarProps = {
scope: 'APPLICATION',
projectId: 'project-id',
environmentId: 'environment-id',
serviceId: 'service-id',
}

describe('VariablesActionToolbar', () => {
it('should keep the import button by default when env file import is enabled', () => {
renderWithProviders(<VariablesActionToolbar {...props} onImportEnvFile={jest.fn()} />)

expect(screen.getByRole('button', { name: /import variable/i })).toBeInTheDocument()
})

it('should render the env file import before doppler in dropdown mode', async () => {
const { userEvent } = renderWithProviders(
<VariablesActionToolbar {...props} onImportEnvFile={jest.fn()} importEnvFileAccess="dropdown" />
)

await userEvent.click(screen.getAllByRole('button')[0])

const menuItems = await screen.findAllByRole('menuitem')

expect(menuItems[0]).toHaveTextContent('Import from .env file')
expect(menuItems[1]).toHaveTextContent('Import from Doppler')
})

it('should call the env file import callback when selected from the dropdown', async () => {
const onImportEnvFile = jest.fn()
const { userEvent } = renderWithProviders(
<VariablesActionToolbar {...props} onImportEnvFile={onImportEnvFile} importEnvFileAccess="dropdown" />
)

await userEvent.click(screen.getAllByRole('button')[0])
await userEvent.click(await screen.findByRole('menuitem', { name: /import from \.env file/i }))

expect(onImportEnvFile).toHaveBeenCalledTimes(1)
})
})
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { type APIVariableScopeEnum, type VariableResponse } from 'qovery-typescript-axios'
import { ActionToolbar, Button, DropdownMenu, Icon, Tooltip, useModal } from '@qovery/shared/ui'
import { Button, DropdownMenu, Icon, Tooltip, useModal } from '@qovery/shared/ui'
import { CreateUpdateVariableModal } from '../create-update-variable-modal/create-update-variable-modal'

type Scope = Exclude<keyof typeof APIVariableScopeEnum, 'BUILT_IN'>

export type VariablesActionToolbarProps = {
onCreateVariable?: (variable: VariableResponse | void) => void
onImportEnvFile?: () => void
importEnvFileAccess?: 'button' | 'dropdown'
} & (
| {
scope: Extract<Scope, 'PROJECT'>
Expand All @@ -25,8 +26,15 @@ export type VariablesActionToolbarProps = {
}
)

export function VariablesActionToolbar({ onCreateVariable, onImportEnvFile, ...props }: VariablesActionToolbarProps) {
export function VariablesActionToolbar({
onCreateVariable,
onImportEnvFile,
importEnvFileAccess = 'button',
...props
}: VariablesActionToolbarProps) {
const { openModal, closeModal } = useModal()
const hasImportEnvFile = Boolean(onImportEnvFile)
const showImportButton = hasImportEnvFile && importEnvFileAccess === 'button'

const _onCreateVariable = (isFile?: boolean) =>
openModal({
Expand All @@ -48,7 +56,7 @@ export function VariablesActionToolbar({ onCreateVariable, onImportEnvFile, ...p
return (
<div className="flex gap-3">
<DropdownMenu.Root>
{onImportEnvFile ? (
{showImportButton ? (
<DropdownMenu.Trigger asChild>
<Button color="neutral" variant="outline" size="md" className="gap-2">
<Icon iconName="arrow-up-from-line" iconStyle="regular" />
Expand All @@ -63,7 +71,13 @@ export function VariablesActionToolbar({ onCreateVariable, onImportEnvFile, ...p
</DropdownMenu.Trigger>
)}
<DropdownMenu.Content>
{onImportEnvFile && (
{showImportButton && onImportEnvFile && (
<DropdownMenu.Item onSelect={onImportEnvFile} icon={<Icon iconName="file-import" />}>
Import from .env file
</DropdownMenu.Item>
)}

{!showImportButton && onImportEnvFile && (
<DropdownMenu.Item onSelect={onImportEnvFile} icon={<Icon iconName="file-import" />}>
Import from .env file
</DropdownMenu.Item>
Expand Down
Loading
Loading