diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..646404463 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,278 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +`@devtron-labs/devtron-fe-common-lib` is a React TypeScript component library for the Devtron platform, built with Vite. It provides shared UI components, utilities, and page-level modules consumed by Devtron applications. + +## Commands + +### Development +```bash +npm run dev # Start development server with Vite +npm run build # Build library with sourcemaps +npm run build-watch # Build in watch mode for development +``` + +### Linting & Type Checking +```bash +npm run lint # Run TypeScript type check and ESLint +npm run lint-fix # Auto-fix ESLint issues +npx tsc --noEmit # Run TypeScript type check only +``` + +### Asset Generation +```bash +npm run generate-icon # Generate Icon.tsx from SVG files in IconV2/ +npm run generate-illustration # Generate Illustration.tsx from SVG files in Illustration/ +``` + +Note: These scripts run automatically in pre-commit hooks when icon/illustration files are modified. + +## Architecture + +### Module Organization + +The codebase is divided into four main directories under `src/`: + +1. **`Common/`** - Core UI components and utilities + - Reusable components: Checkbox, EmptyState, Progressing, SearchBar, Tooltip, etc. + - Form components: RadioGroup, DebouncedSearch, CustomTagSelector + - Layout components: BreadCrumb, Drawer, Modals + - RJSF (React JSON Schema Form) customizations + - Shared services, hooks, types, and constants + +2. **`Shared/`** - Shared infrastructure + - **Components/**: 100+ specialized components (CodeEditor, MaterialHistory, FileUpload, etc.) + - **Providers/**: Context providers (ThemeProvider, UserEmailProvider, MainContextProvider, ImageSelectionUtility) + - **Services/**: API services, ToastManager, and utilities + - **Hooks/**: Custom React hooks + - **Analytics/**: Analytics integration + - Shared helpers, validations, constants, and types + +3. **`Pages/`** - Legacy page-level components + - Applications (CI/CD pipeline types and components) + - BulkEdit + - GlobalConfigurations + - ResourceBrowser + +4. **`Pages-Devtron-2.0/`** - Redesigned pages organized by business domain + - ApplicationManagement + - Automation&Enablement + - CostVisibility + - DataProtectionManagement + - GlobalConfiguration + - GlobalOverview + - InfrastructureManagement + - Navigation + - SecurityCenter + - Shared + - SoftwareReleaseManagement + +5. **`Assets/`** - Static assets + - `Icon/`: SVG icons (legacy) + - `IconV2/`: Modern SVG icons (auto-generated into Icon component) + - `Illustration/`: SVG illustrations (auto-generated into Illustration component) + - `Img/`: PNG/JPG images + - `Sounds/`: Audio files + +### Path Aliases + +TypeScript path aliases are configured in [tsconfig.json](tsconfig.json): + +```typescript +@Icons/* → src/Assets/Icon/* +@IconsV2/* → src/Assets/IconV2/* +@Illustrations/* → src/Assets/Illustration/* +@Sounds/* → src/Assets/Sounds/* +@Images/* → src/Assets/Img/* +@Common/* → src/Common/* +@Shared/* → src/Shared/* +@Pages/* → src/Pages/* +@PagesDevtron2.0/* → src/Pages-Devtron-2.0/* +``` + +Always use these aliases instead of relative paths when importing across directories. + +### Build Configuration + +The library uses Vite with manual chunk splitting for optimal bundle size ([vite.config.ts](vite.config.ts)): + +- `@react-dates` - Date picker components +- `@framer-motion` - Animation library +- `@moment` - Date manipulation +- `@react-select` - Select components +- `@react-virtualized-sticky-tree` - Tree component +- `@code-editor` - CodeMirror and related code editor components +- `@common-rjsf` - React JSON Schema Form components +- `@vendor` - All other node_modules +- `@src-assets-icons` - Icon assets +- `@src-assets-images` - Image assets + +When adding new large dependencies, consider adding them to the manual chunks configuration to optimize bundle loading. + +## Code Standards + +### Import Restrictions (Enforced by ESLint) + +The following imports are **prohibited**: + +1. **Toast notifications**: Never import from `react-toastify` directly + ```typescript + // ❌ DON'T + import { toast } from 'react-toastify' + + // ✅ DO + import { ToastManager } from '@Shared/Services' + ToastManager.showToast(...) + ``` + +2. **Icons**: Never use `IconBase` directly + ```typescript + // ❌ DON'T + import { IconBase } from '...' + + // ✅ DO + import { Icon } from '@Shared/Components' + + ``` + +3. **Illustrations**: Never use `IllustrationBase` directly + ```typescript + // ❌ DON'T + import { IllustrationBase } from '...' + + // ✅ DO + import { Illustration } from '@Shared/Components' + + ``` + +### Import Order + +ESLint enforces a specific import order (configured in [.eslintrc.cjs](.eslintrc.cjs:116-134)): + +1. React and external packages (`react`, `@?\\w`) +2. Devtron packages (`@devtron-labs`) +3. Internal path aliases (`@Common/*`, `@Shared/*`, etc.) +4. Side effect imports +5. Relative imports (`../`, `./`) +6. Style imports (`.css`, `.scss`) + +The `simple-import-sort` plugin will auto-fix import order on save. + +### Component Patterns + +- **Function components**: Use arrow functions for all components (enforced by ESLint) + ```typescript + // ✅ DO + export const MyComponent = () => { ... } + + // ❌ DON'T + export function MyComponent() { ... } + ``` + +- **TypeScript**: Strict mode is disabled, but type safety is encouraged +- **React Query**: Use `@tanstack/react-query` (<5) for data fetching + - Supports custom meta fields: `showToastError?: boolean` (defaults to `true`) + +### Pre-commit Hooks + +The pre-commit hook ([.husky/pre-commit](.husky/pre-commit)) enforces: + +1. **Icon generation**: If IconV2/ files change, auto-generates `src/Shared/Components/Icon/Icon.tsx` +2. **Illustration generation**: If Illustration/ files change, auto-generates `src/Shared/Components/Illustration/Illustration.tsx` +3. **TypeScript check**: `tsc --noEmit` must pass +4. **Lint-staged**: ESLint check on staged files + +When adding/modifying SVG assets in IconV2/ or Illustration/, the pre-commit hook will automatically regenerate the component files and add them to the commit. + +## Common Tasks + +### Adding a New Icon + +1. Add SVG file to `src/Assets/IconV2/` +2. Stage changes: `git add src/Assets/IconV2/ic-your-icon.svg` +3. The pre-commit hook will auto-generate `Icon.tsx` +4. Use in code: + ```typescript + import { Icon } from '@Shared/Components' + + ``` + +### Adding a New Illustration + +1. Add SVG file to `src/Assets/Illustration/` +2. Stage changes: `git add src/Assets/Illustration/your-illustration.svg` +3. The pre-commit hook will auto-generate `Illustration.tsx` +4. Use in code: + ```typescript + import { Illustration } from '@Shared/Components' + + ``` + +### Working with React Query + +Custom meta fields are available for queries and mutations: + +```typescript +import { useQuery, useMutation } from '@tanstack/react-query' + +// Suppress error toasts for a specific query +useQuery({ + queryKey: ['data'], + queryFn: fetchData, + meta: { showToastError: false } // Won't show toast on error +}) + +// Error toasts are shown by default +useMutation({ + mutationFn: updateData, + // meta: { showToastError: true } is default +}) +``` + +### Extending Environment Configuration + +Environment configuration types are defined in [src/index.ts](src/index.ts:21-193) as `customEnv` interface. When adding new environment variables: + +1. Add type to `customEnv` interface +2. Document with JSDoc comments (especially for feature flags) +3. Include `@default` value if applicable +4. Access via `window._env_` in components + +## Testing & CI + +The repository has GitHub Actions workflows: + +- `.github/workflows/ci.yml` - CI checks +- `.github/workflows/release-package.yml` - Package publishing + +Currently, tests are stubbed (`npm test` exits 0), but the infrastructure supports `@testing-library/react` and `@testing-library/jest-dom`. + +## Key Dependencies + +**UI & Components:** +- React 17.0.2 +- @xyflow/react (flow diagrams) +- chart.js (charts) +- react-dates (date pickers) +- react-select 5.8.0 +- framer-motion (animations) + +**Code Editing:** +- @uiw/react-codemirror + CodeMirror 6 extensions +- codemirror-json-schema (JSON/YAML schema validation) + +**Forms:** +- @rjsf/core 5.13.3 (React JSON Schema Forms) + +**Data Management:** +- @tanstack/react-query (<5) +- rxjs 7.8.1 + +**Utilities:** +- dayjs (date formatting) +- marked (Markdown rendering) +- dompurify (HTML sanitization) +- yaml 2.4.1 diff --git a/package-lock.json b/package-lock.json index 1006f6532..c359fe5b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.23.1", + "version": "1.23.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.23.1", + "version": "1.23.2", "hasInstallScript": true, "license": "ISC", "dependencies": { diff --git a/package.json b/package.json index 11e1b18c4..4bdf371f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.23.1", + "version": "1.23.2", "description": "Supporting common component library", "type": "module", "main": "dist/index.js", diff --git a/src/Assets/IconV2/ic-arrow-up-circle-with-dot.svg b/src/Assets/IconV2/ic-arrow-up-circle-with-dot.svg new file mode 100644 index 000000000..78a366131 --- /dev/null +++ b/src/Assets/IconV2/ic-arrow-up-circle-with-dot.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/Assets/IconV2/ic-arrow-up-circle.svg b/src/Assets/IconV2/ic-arrow-up-circle.svg new file mode 100644 index 000000000..2fc3fed17 --- /dev/null +++ b/src/Assets/IconV2/ic-arrow-up-circle.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/Assets/IconV2/ic-medium-upgrade.svg b/src/Assets/IconV2/ic-medium-upgrade.svg new file mode 100644 index 000000000..23d11107b --- /dev/null +++ b/src/Assets/IconV2/ic-medium-upgrade.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/Assets/Illustration/empty-state-key.svg b/src/Assets/Illustration/empty-state-key.svg new file mode 100644 index 000000000..870c511ad --- /dev/null +++ b/src/Assets/Illustration/empty-state-key.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/src/Shared/Components/ActionMenu/ActionMenu.component.tsx b/src/Shared/Components/ActionMenu/ActionMenu.component.tsx index eadb01844..daca6c2b5 100644 --- a/src/Shared/Components/ActionMenu/ActionMenu.component.tsx +++ b/src/Shared/Components/ActionMenu/ActionMenu.component.tsx @@ -81,7 +81,7 @@ export const ActionMenu = ({ buttonProps={buttonProps} triggerElement={children} > -
+
{isSearchable && (
secondary: 'border__secondary', none: '', 'secondary-translucent': 'border__secondary-translucent', + primary: 'border__primary', } diff --git a/src/Shared/Components/GenericModal/types.ts b/src/Shared/Components/GenericModal/types.ts index a71884e9e..a54747d93 100644 --- a/src/Shared/Components/GenericModal/types.ts +++ b/src/Shared/Components/GenericModal/types.ts @@ -17,7 +17,7 @@ import { BackdropProps } from '../Backdrop' import { ButtonProps } from '../Button' -export type BorderVariantType = 'secondary' | 'none' | 'secondary-translucent' +export type BorderVariantType = 'secondary' | 'none' | 'secondary-translucent' | 'primary' export interface GenericModalProps extends Partial> { /** Unique identifier for the modal */ diff --git a/src/Shared/Components/Header/HelpButton.tsx b/src/Shared/Components/Header/HelpButton.tsx index 7e745eb14..6287c1792 100644 --- a/src/Shared/Components/Header/HelpButton.tsx +++ b/src/Shared/Components/Header/HelpButton.tsx @@ -75,6 +75,7 @@ export const HelpButton = ({ setSidePanelConfig, loginCount, showGettingStartedCard, + setShowUpgradeToOSSPlusDialog, } = useMainContext() const { appTheme } = useTheme() const isSecureConnection = useIsSecureConnection() @@ -126,6 +127,9 @@ export const HelpButton = ({ case HelpMenuItems.VIEW_DOCUMENTATION: handleViewDocumentationClick(item, e) break + case HelpMenuItems.UPGRADE_TO_OSS_PLUS: + setShowUpgradeToOSSPlusDialog?.(true) + break default: } } diff --git a/src/Shared/Components/Header/constants.ts b/src/Shared/Components/Header/constants.ts index bd3d3e2ae..7847ef083 100644 --- a/src/Shared/Components/Header/constants.ts +++ b/src/Shared/Components/Header/constants.ts @@ -77,6 +77,13 @@ export const OSS_HELP_ACTION_MENU_ITEMS: HelpButtonActionMenuProps['options'][nu componentType: 'anchor', href: RAISE_ISSUE, }, + { + id: HelpMenuItems.UPGRADE_TO_OSS_PLUS, + label: 'Upgrade to OSS Plus', + startIcon: { name: 'ic-arrow-up-circle' }, + componentType: 'button', + description: 'Run Devtron OSS with Expert Support', + }, ] export const ENTERPRISE_TRIAL_HELP_ACTION_MENU_ITEMS: HelpButtonActionMenuProps['options'][number]['items'] = [ diff --git a/src/Shared/Components/Header/types.ts b/src/Shared/Components/Header/types.ts index 4065c8a4e..6eab1c890 100644 --- a/src/Shared/Components/Header/types.ts +++ b/src/Shared/Components/Header/types.ts @@ -71,6 +71,7 @@ export enum HelpMenuItems { GIVE_FEEDBACK = 'give-feedback', CHAT_WITH_SUPPORT = 'chat-with-support', RAISE_ISSUE_REQUEST = 'raise-issue-request', + UPGRADE_TO_OSS_PLUS = 'upgrade-to-oss-plus', DEVTRON_GPT = 'devtron-gpt', } diff --git a/src/Shared/Components/Icon/Icon.tsx b/src/Shared/Components/Icon/Icon.tsx index 67d39ff4f..23e0cbab1 100644 --- a/src/Shared/Components/Icon/Icon.tsx +++ b/src/Shared/Components/Icon/Icon.tsx @@ -18,6 +18,8 @@ import { ReactComponent as ICArrowClockwise } from '@IconsV2/ic-arrow-clockwise. import { ReactComponent as ICArrowLineDown } from '@IconsV2/ic-arrow-line-down.svg' import { ReactComponent as ICArrowRight } from '@IconsV2/ic-arrow-right.svg' import { ReactComponent as ICArrowSquareOut } from '@IconsV2/ic-arrow-square-out.svg' +import { ReactComponent as ICArrowUpCircle } from '@IconsV2/ic-arrow-up-circle.svg' +import { ReactComponent as ICArrowUpCircleWithDot } from '@IconsV2/ic-arrow-up-circle-with-dot.svg' import { ReactComponent as ICArrowsClockwise } from '@IconsV2/ic-arrows-clockwise.svg' import { ReactComponent as ICArrowsLeftRight } from '@IconsV2/ic-arrows-left-right.svg' import { ReactComponent as ICAsterisk } from '@IconsV2/ic-asterisk.svg' @@ -215,6 +217,7 @@ import { ReactComponent as ICMagnifyingGlass } from '@IconsV2/ic-magnifying-glas import { ReactComponent as ICMediumDelete } from '@IconsV2/ic-medium-delete.svg' import { ReactComponent as ICMediumMegaphone } from '@IconsV2/ic-medium-megaphone.svg' import { ReactComponent as ICMediumPaintbucket } from '@IconsV2/ic-medium-paintbucket.svg' +import { ReactComponent as ICMediumUpgrade } from '@IconsV2/ic-medium-upgrade.svg' import { ReactComponent as ICMegaphoneLeft } from '@IconsV2/ic-megaphone-left.svg' import { ReactComponent as ICMegaphoneRight } from '@IconsV2/ic-megaphone-right.svg' import { ReactComponent as ICMemory } from '@IconsV2/ic-memory.svg' @@ -356,6 +359,8 @@ export const iconMap = { 'ic-arrow-line-down': ICArrowLineDown, 'ic-arrow-right': ICArrowRight, 'ic-arrow-square-out': ICArrowSquareOut, + 'ic-arrow-up-circle-with-dot': ICArrowUpCircleWithDot, + 'ic-arrow-up-circle': ICArrowUpCircle, 'ic-arrows-clockwise': ICArrowsClockwise, 'ic-arrows-left-right': ICArrowsLeftRight, 'ic-asterisk': ICAsterisk, @@ -553,6 +558,7 @@ export const iconMap = { 'ic-medium-delete': ICMediumDelete, 'ic-medium-megaphone': ICMediumMegaphone, 'ic-medium-paintbucket': ICMediumPaintbucket, + 'ic-medium-upgrade': ICMediumUpgrade, 'ic-megaphone-left': ICMegaphoneLeft, 'ic-megaphone-right': ICMegaphoneRight, 'ic-memory': ICMemory, diff --git a/src/Shared/Components/Illustration/Illustration.tsx b/src/Shared/Components/Illustration/Illustration.tsx index 409da2299..a477b8d24 100644 --- a/src/Shared/Components/Illustration/Illustration.tsx +++ b/src/Shared/Components/Illustration/Illustration.tsx @@ -3,6 +3,7 @@ import CmdBarVisual from '@Illustrations/cmd-bar-visual.webp' import CreateBackupSchedule from '@Illustrations/create-backup-schedule.webp' import CreateBackupSnapshot from '@Illustrations/create-backup-snapshot.webp' +import { ReactComponent as EmptyStateKey } from '@Illustrations/empty-state-key.svg' import { ReactComponent as ImgCelebration } from '@Illustrations/img-celebration.svg' import ImgCode from '@Illustrations/img-code.webp' import ImgDevtronFreemium from '@Illustrations/img-devtron-freemium.webp' @@ -23,6 +24,7 @@ import { IllustrationBase } from './IllustrationBase' import { IllustrationBaseProps } from './types' export const illustrationMap = { + 'empty-state-key': EmptyStateKey, 'img-celebration': ImgCelebration, 'img-folder-empty': ImgFolderEmpty, 'img-install-freemium-saas': ImgInstallFreemiumSaas, diff --git a/src/Shared/Providers/MainContextProvider/types.ts b/src/Shared/Providers/MainContextProvider/types.ts index cff7ce452..6f09df3c2 100644 --- a/src/Shared/Providers/MainContextProvider/types.ts +++ b/src/Shared/Providers/MainContextProvider/types.ts @@ -217,6 +217,8 @@ export type MainContext = CommonMainContextProps & AIRecommendations?: FunctionComponent featureAskDevtronExpert: EnvironmentDataValuesDTO['featureAskDevtronExpert'] AskDevtronButton?: FunctionComponent + showUpgradeToOSSPlusDialog: boolean + setShowUpgradeToOSSPlusDialog: (show: boolean) => void } | { isLicenseDashboard: true @@ -238,6 +240,8 @@ export type MainContext = CommonMainContextProps & AIRecommendations?: null featureAskDevtronExpert?: null AskDevtronButton?: null + showUpgradeToOSSPlusDialog?: null + setShowUpgradeToOSSPlusDialog?: null } ) diff --git a/src/Shared/types.ts b/src/Shared/types.ts index 1179c6df8..e96b02019 100644 --- a/src/Shared/types.ts +++ b/src/Shared/types.ts @@ -1215,6 +1215,7 @@ export type IconBaseSizeType = | 42 | 44 | 48 + | 56 | 64 | 72 | 80