Skip to content

Conversation

@JoshuaRileyDev
Copy link
Contributor

@JoshuaRileyDev JoshuaRileyDev commented Dec 26, 2025

Summary

This PR adds comprehensive Dokploy deployment enhancements including support for linking to existing applications, granular deployment control, and improved service management UI.

Key Features

🔗 Application Linking to All Resource Types

  • Support for all Dokploy resources: applications, compose, mariadb, mongo, mysql, postgres, redis
  • Nested API parsing: Properly extracts all application types from all environments in project.all endpoint
  • Enhanced UI: Shows app type badges and environment names in selection dropdown
  • Metadata tracking: Stores application type in deployment info for future reference
  • Flexible architecture: Each service can link to applications from different projects

⚙️ Granular Deployment Control

  • Deploy Now Toggle: New toggle to control immediate deployment vs. setup-only (defaults to ON)
  • Smart visibility: Only shown for "Create New" mode (hidden for "Link Existing")
  • API integration: Automatically triggers deployment via `application.deploy` endpoint when enabled
  • Dynamic messaging: Different success messages based on deployment vs. setup-only mode
  • Optimal placement: Toggle positioned at bottom of service card for better UX flow

📊 Improved Service Management

  • Bulk operations: "Select All / Deselect All" button for quick toggling
  • Progress tracking: Real-time display of services left to configure
  • Smart button states: Button text adapts based on configuration state:
    • All linking → "Link Applications (count)"
    • All creating → "Configure Apps"
    • Mixed mode → "Link/Configure (count)"
  • Visual feedback: Enabled services have highlighted borders and backgrounds

🎨 UI/UX Enhancements

  • Clean, intuitive service card layout
  • Clear deployment action descriptions and helper text
  • Responsive button states based on service configuration
  • Non-blocking error handling for deployment trigger failures
  • Real-time configuration status updates

Technical Implementation

Type System

```typescript
// New application type enum
export type DokployApplicationType =
| 'application'
| 'compose'
| 'mariadb'
| 'mongo'
| 'mysql'
| 'postgres'
| 'redis';

// Enhanced deployment info
export interface DokployServiceDeployment {
// ... existing fields
applicationType?: DokployApplicationType; // NEW: track app type
}
```

API Integration

  • Refactored `fetchDokployProjects` to parse nested `project.all` structure
  • Iterates through all environments and resource type arrays
  • Flattens into unified application list with type metadata
  • Calls `application.deploy` endpoint when `deployNow` is enabled

State Management

New computed values for real-time service tracking:

  • `configuredServices`: Services fully set up (ready to deploy/link)
  • `servicesLeftToDeploy`: Enabled but unconfigured services
  • `isAllLinked`: All services in link mode with apps selected
  • `isAllCreateNew`: All services in create new mode

Error Handling

  • Deployment trigger failures are logged but don't block the process
  • Applications remain configured even if deployment trigger fails
  • Users can manually deploy from Dokploy dashboard as fallback

Files Changed

New Files

  • `apps/frontend/src/renderer/components/DokployDeployModal.tsx` - Main deployment modal
  • `apps/frontend/src/renderer/components/Deploy.tsx` - Deploy entry component
  • `apps/frontend/src/renderer/components/settings/DeploymentSettings.tsx` - Settings UI
  • `apps/frontend/src/main/ipc-handlers/dokploy-handlers.ts` - IPC handlers
  • `apps/frontend/src/preload/api/modules/dokploy-api.ts` - API module
  • `apps/frontend/src/shared/types/dokploy.ts` - Type definitions
  • `apps/frontend/src/shared/i18n/locales/en/deploy.json` - English translations
  • `apps/frontend/src/shared/i18n/locales/fr/deploy.json` - French translations

Modified Files

  • Various integration points for navigation, settings, and IPC registration

Testing Recommendations

  • Test linking to different application types (regular apps, compose, databases)
  • Test "Deploy Now" toggle ON: verify deployment is triggered
  • Test "Deploy Now" toggle OFF: verify only configuration happens
  • Test mixed mode: some services create new, some link existing
  • Test "Select All / Deselect All" button
  • Test service configuration state tracking
  • Test with projects that have multiple environments
  • Verify error handling when deployment trigger fails

Screenshots

Service card with deployment control:
```
☑ Frontend • application • React
├─ Mode: [Create New] | Link Existing
├─ App Name: frontend
├─ Branch: main
├─ Domain: app.example.com (optional)
├─ Environment Variables (3/5)
├─ Build path: /apps/frontend
└─ Deploy immediately after setup [ON/OFF]
```

Application selection with type badges:
```
Select an application:

  • api [application] • production
  • database [compose] • production
  • cache [redis] • staging
    ```

Breaking Changes

None. All changes are additive and backward compatible.


🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added Deploy view with a full Dokploy deployment workflow including per-service configuration, resync and multi-step deployment UI.
    • Added Deployment settings UI to manage Dokploy accounts and credentials.
    • Enabled reading/aggregating .env files and editing/syncing environment variables per service.
    • Persisted deployment metadata and integrated save/get/delete plus deployment actions (create, link, deploy).
    • Added translations and navigation entries for deployment.

✏️ Tip: You can customize this high-level summary in your review settings.

…ol options

Add support for linking existing Dokploy applications and granular deployment control:

## New Features

### Application Linking
- Support linking to ALL Dokploy resource types (applications, compose, mariadb, mongo, mysql, postgres, redis)
- Parse nested project.all API structure to extract all application types from all environments
- Display app type badges and environment names in selection dropdown
- Store application type metadata in deployment info for future reference
- Allow per-service project selection (each service can link to different projects)

### Deployment Control
- Add "Deploy immediately after setup" toggle (defaults to ON)
- Toggle only visible for "Create New" mode (not applicable for linking)
- Automatically trigger deployment via application.deploy API when enabled
- Show different success messages based on deployment vs. setup-only mode
- Position toggle at bottom of service card for better UX

### Service Management
- Add "Select All / Deselect All" button for bulk service toggling
- Calculate and display services left to configure
- Smart button text based on configuration state:
  - All linking: "Link Applications (count)"
  - All creating: "Configure Apps"
  - Mixed mode: "Link/Configure (count)"
- Track configured vs. unconfigured services in real-time

### UI/UX Improvements
- Visual service status indicators (enabled services highlighted)
- Clear deployment action descriptions
- Responsive button states based on service configuration
- Error handling for deployment trigger failures (non-blocking)

## Technical Changes

### Type Definitions
- Add DokployApplicationType enum for all resource types
- Update DokployServiceDeployment to include applicationType field
- Enhance dokployProjects state type with app type and environment metadata

### API Integration
- Refactor fetchDokployProjects to parse nested project.all structure
- Add deployment trigger logic via application.deploy endpoint
- Graceful error handling for deployment triggers

### Computed State
- Add configuredServices tracker (services ready to deploy/link)
- Add servicesLeftToDeploy calculator
- Add isAllLinked and isAllCreateNew mode detectors
- Optimize service state calculations with useMemo

## Files Modified
- apps/frontend/src/renderer/components/DokployDeployModal.tsx (main implementation)
- apps/frontend/src/shared/types/dokploy.ts (type definitions)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 26, 2025

📝 Walkthrough

Walkthrough

Adds Dokploy deployment support to the frontend: new IPC handlers and channels, preload API, types, renderer UI (Deploy view, DokployDeployModal, settings), i18n for English/French, browser mocks, and local persistence of deployment metadata (.dokploy.json).

Changes

Cohort / File(s) Summary
IPC Handlers
apps/frontend/src/main/ipc-handlers/dokploy-handlers.ts, apps/frontend/src/main/ipc-handlers/index.ts
New Dokploy IPC handlers (API proxy, env-file aggregation, save/get/delete .dokploy.json) and registration wired into setupIpcHandlers.
Preload / Electron API
apps/frontend/src/preload/api/modules/dokploy-api.ts, apps/frontend/src/preload/api/index.ts
New DokployAPI façade exposing five methods via ipcRenderer.invoke and integrated into ElectronAPI.
Types
apps/frontend/src/shared/types/dokploy.ts, apps/frontend/src/shared/types/ipc.ts, apps/frontend/src/shared/types/index.ts, apps/frontend/src/shared/types/settings.ts
New Dokploy domain types (servers, repos, projects, apps, branches, deployments), Dokploy API request/response shapes, added exports, and ElectronAPI/type surface updates including AppSettings extension for deploymentProviders.
Renderer — Deploy UI
apps/frontend/src/renderer/components/Deploy.tsx, apps/frontend/src/renderer/components/DokployDeployModal.tsx, apps/frontend/src/renderer/App.tsx, apps/frontend/src/renderer/components/Sidebar.tsx
New Deploy view, DokployDeployModal (multi-step deploy/resync workflow), App and Sidebar wired to surface Deploy when a project is selected.
Renderer — Settings UI
apps/frontend/src/renderer/components/settings/DeploymentSettings.tsx, apps/frontend/src/renderer/components/settings/AppSettings.tsx, apps/frontend/src/renderer/components/settings/index.ts
New DeploymentSettings UI to list/add/edit Dokploy accounts; integrated into AppSettings navigation and settings persistence.
IPC Channels & i18n
apps/frontend/src/shared/constants/ipc.ts, apps/frontend/src/shared/i18n/index.ts, apps/frontend/src/shared/i18n/locales/en/*, apps/frontend/src/shared/i18n/locales/fr/*, apps/frontend/src/shared/i18n/locales/en/navigation.json, apps/frontend/src/shared/i18n/locales/en/settings.json, apps/frontend/src/shared/i18n/locales/fr/navigation.json, apps/frontend/src/shared/i18n/locales/fr/settings.json
Added dokploy:* IPC channel constants; added deploy i18n namespace and English/French locale files for deploy UI and settings.
Browser Mock & UI tweak
apps/frontend/src/renderer/lib/browser-mock.ts, apps/frontend/src/renderer/components/ui/dialog.tsx
Added Dokploy mocks to browserMockAPI and adjusted Dialog close button styling.

Sequence Diagram

sequenceDiagram
    autonumber
    actor User
    participant Sidebar
    participant DeployView as Deploy
    participant Modal as DokployDeployModal
    participant Preload as Preload API
    participant Main as Main (IPC Handlers)
    participant Dokploy as Dokploy API
    Note over Preload,Main: New IPC channels: dokploy:api, dokploy:read-env, dokploy:save-deployment,\n dokploy:get-deployment, dokploy:delete-deployment

    User->>Sidebar: Click "Deploy"
    Sidebar->>DeployView: Render(projectId)
    DeployView->>Preload: request deploymentProviders
    alt no providers
        DeployView->>User: Prompt open settings
    else providers exist
        User->>DeployView: Open modal
        DeployView->>Modal: open(projectId)
        Modal->>Preload: dokployReadEnv(servicePath)
        Preload->>Main: IPC invoke (dokploy:read-env)
        Main->>Modal: return env vars (via Preload)
        Modal->>Preload: dokployApi(GET servers, GET github providers, etc.)
        Preload->>Main: IPC invoke (dokploy:api)
        Main->>Dokploy: HTTP GET/POST requests
        Dokploy-->>Main: responses
        Main-->>Preload: IPC responses
        Preload-->>Modal: data
        User->>Modal: Configure & Click Deploy
        Modal->>Preload: dokployApi (create project/apps, configure GH, deploy)
        Preload->>Main: IPC invoke (dokploy:api POSTs)
        Main->>Dokploy: HTTP POSTs
        Dokploy-->>Main: results
        Main-->>Preload: responses
        Preload-->>Modal: deployment results
        Modal->>Preload: dokploySaveDeployment (.dokploy.json)
        Preload->>Main: IPC invoke (dokploy:save-deployment)
        Main-->>Preload: write result
        Preload-->>Modal: success / error
        Modal->>User: Show success / error
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

area/frontend, size/M

Suggested reviewers

  • AlexMadera

Poem

🐰 A tiny rabbit hops with glee,

New channels hum and UIs see,
Env files found and projects spin,
Deploys saved neat in .dokploy.json,
Hooray — the build begins! 🚀

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and accurately summarizes the main feature: adding Dokploy deployment with linking and control options. It is clear, specific, and reflects the primary change.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ae91ac4 and 3cd2b02.

📒 Files selected for processing (4)
  • apps/frontend/src/renderer/App.tsx
  • apps/frontend/src/renderer/lib/browser-mock.ts
  • apps/frontend/src/shared/constants/ipc.ts
  • apps/frontend/src/shared/types/ipc.ts
🧰 Additional context used
📓 Path-based instructions (2)
apps/frontend/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/frontend/src/**/*.{ts,tsx}: Always use translation keys with useTranslation() for all user-facing text in React/TypeScript frontend components - use format namespace:section.key (e.g., navigation:items.githubPRs)
Never use hardcoded strings in JSX/TSX files for user-facing text - always reference translation keys from apps/frontend/src/shared/i18n/locales/

Files:

  • apps/frontend/src/renderer/App.tsx
  • apps/frontend/src/shared/types/ipc.ts
  • apps/frontend/src/renderer/lib/browser-mock.ts
  • apps/frontend/src/shared/constants/ipc.ts
apps/frontend/**/*.{ts,tsx}

⚙️ CodeRabbit configuration file

apps/frontend/**/*.{ts,tsx}: Review React patterns and TypeScript type safety.
Check for proper state management and component composition.

Files:

  • apps/frontend/src/renderer/App.tsx
  • apps/frontend/src/shared/types/ipc.ts
  • apps/frontend/src/renderer/lib/browser-mock.ts
  • apps/frontend/src/shared/constants/ipc.ts
🧬 Code graph analysis (2)
apps/frontend/src/renderer/App.tsx (1)
apps/frontend/src/renderer/components/Deploy.tsx (1)
  • Deploy (14-84)
apps/frontend/src/shared/types/ipc.ts (1)
apps/frontend/src/shared/types/dokploy.ts (3)
  • DokployApiRequest (87-93)
  • DokployApiResponse (95-99)
  • DokployProjectDeployment (115-130)
🔇 Additional comments (6)
apps/frontend/src/shared/constants/ipc.ts (1)

364-371: LGTM! Clean IPC channel additions.

The new Dokploy deployment IPC channels follow the established naming conventions and patterns. The trailing comma on line 364 is a good practice for cleaner future diffs.

apps/frontend/src/renderer/App.tsx (2)

47-47: LGTM! Proper component import.

The Deploy component import follows the existing pattern for other view components.


691-699: LGTM! Deploy view integration follows established patterns.

The Deploy component integration is consistent with other view implementations in this file (GitHub Issues, GitHub PRs, Changelog, Worktrees). The conditional rendering and props are correctly configured, and the onOpenSettings callback properly navigates to the 'deployment' section.

apps/frontend/src/renderer/lib/browser-mock.ts (1)

113-118: LGTM! Browser mocks properly implemented.

The Dokploy mock implementations follow the established patterns in this file. The dokployApi correctly returns an error indicating unavailability in the browser environment, while the other methods return appropriate success responses for UI development/testing purposes.

apps/frontend/src/shared/types/ipc.ts (2)

59-59: LGTM! Dokploy type imports added.

The import statement correctly references the Dokploy types that are used in the ElectronAPI interface extension below.


599-604: LGTM! ElectronAPI properly extended with Dokploy operations.

The five new Dokploy method signatures are well-designed:

  • dokployApi uses a generic type parameter for flexible response typing
  • All methods return properly wrapped DokployApiResponse types
  • Method signatures are consistent with existing IPC patterns in the interface
  • Type safety is maintained throughout

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cb6b216 and 49d1657.

📒 Files selected for processing (25)
  • apps/frontend/src/main/ipc-handlers/dokploy-handlers.ts
  • apps/frontend/src/main/ipc-handlers/index.ts
  • apps/frontend/src/preload/api/index.ts
  • apps/frontend/src/preload/api/modules/dokploy-api.ts
  • apps/frontend/src/renderer/App.tsx
  • apps/frontend/src/renderer/components/Deploy.tsx
  • apps/frontend/src/renderer/components/DokployDeployModal.tsx
  • apps/frontend/src/renderer/components/Sidebar.tsx
  • apps/frontend/src/renderer/components/settings/AppSettings.tsx
  • apps/frontend/src/renderer/components/settings/DeploymentSettings.tsx
  • apps/frontend/src/renderer/components/settings/index.ts
  • apps/frontend/src/renderer/components/ui/dialog.tsx
  • apps/frontend/src/renderer/lib/browser-mock.ts
  • apps/frontend/src/shared/constants/ipc.ts
  • apps/frontend/src/shared/i18n/index.ts
  • apps/frontend/src/shared/i18n/locales/en/deploy.json
  • apps/frontend/src/shared/i18n/locales/en/navigation.json
  • apps/frontend/src/shared/i18n/locales/en/settings.json
  • apps/frontend/src/shared/i18n/locales/fr/deploy.json
  • apps/frontend/src/shared/i18n/locales/fr/navigation.json
  • apps/frontend/src/shared/i18n/locales/fr/settings.json
  • apps/frontend/src/shared/types/dokploy.ts
  • apps/frontend/src/shared/types/index.ts
  • apps/frontend/src/shared/types/ipc.ts
  • apps/frontend/src/shared/types/settings.ts
🧰 Additional context used
📓 Path-based instructions (3)
apps/frontend/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/frontend/src/**/*.{ts,tsx}: Always use translation keys with useTranslation() for all user-facing text in React/TypeScript frontend components - use format namespace:section.key (e.g., navigation:items.githubPRs)
Never use hardcoded strings in JSX/TSX files for user-facing text - always reference translation keys from apps/frontend/src/shared/i18n/locales/

Files:

  • apps/frontend/src/renderer/components/settings/index.ts
  • apps/frontend/src/renderer/components/settings/AppSettings.tsx
  • apps/frontend/src/renderer/components/ui/dialog.tsx
  • apps/frontend/src/renderer/components/Deploy.tsx
  • apps/frontend/src/renderer/lib/browser-mock.ts
  • apps/frontend/src/renderer/components/settings/DeploymentSettings.tsx
  • apps/frontend/src/shared/types/settings.ts
  • apps/frontend/src/shared/types/ipc.ts
  • apps/frontend/src/renderer/App.tsx
  • apps/frontend/src/main/ipc-handlers/dokploy-handlers.ts
  • apps/frontend/src/shared/constants/ipc.ts
  • apps/frontend/src/shared/types/index.ts
  • apps/frontend/src/renderer/components/Sidebar.tsx
  • apps/frontend/src/renderer/components/DokployDeployModal.tsx
  • apps/frontend/src/preload/api/index.ts
  • apps/frontend/src/shared/i18n/index.ts
  • apps/frontend/src/shared/types/dokploy.ts
  • apps/frontend/src/preload/api/modules/dokploy-api.ts
  • apps/frontend/src/main/ipc-handlers/index.ts
apps/frontend/**/*.{ts,tsx}

⚙️ CodeRabbit configuration file

apps/frontend/**/*.{ts,tsx}: Review React patterns and TypeScript type safety.
Check for proper state management and component composition.

Files:

  • apps/frontend/src/renderer/components/settings/index.ts
  • apps/frontend/src/renderer/components/settings/AppSettings.tsx
  • apps/frontend/src/renderer/components/ui/dialog.tsx
  • apps/frontend/src/renderer/components/Deploy.tsx
  • apps/frontend/src/renderer/lib/browser-mock.ts
  • apps/frontend/src/renderer/components/settings/DeploymentSettings.tsx
  • apps/frontend/src/shared/types/settings.ts
  • apps/frontend/src/shared/types/ipc.ts
  • apps/frontend/src/renderer/App.tsx
  • apps/frontend/src/main/ipc-handlers/dokploy-handlers.ts
  • apps/frontend/src/shared/constants/ipc.ts
  • apps/frontend/src/shared/types/index.ts
  • apps/frontend/src/renderer/components/Sidebar.tsx
  • apps/frontend/src/renderer/components/DokployDeployModal.tsx
  • apps/frontend/src/preload/api/index.ts
  • apps/frontend/src/shared/i18n/index.ts
  • apps/frontend/src/shared/types/dokploy.ts
  • apps/frontend/src/preload/api/modules/dokploy-api.ts
  • apps/frontend/src/main/ipc-handlers/index.ts
apps/frontend/src/shared/i18n/locales/**/*.json

📄 CodeRabbit inference engine (CLAUDE.md)

Add new translation keys to ALL language files (minimum: en/*.json and fr/*.json) in apps/frontend/src/shared/i18n/locales/

Files:

  • apps/frontend/src/shared/i18n/locales/fr/deploy.json
  • apps/frontend/src/shared/i18n/locales/fr/navigation.json
  • apps/frontend/src/shared/i18n/locales/en/navigation.json
  • apps/frontend/src/shared/i18n/locales/en/deploy.json
  • apps/frontend/src/shared/i18n/locales/en/settings.json
  • apps/frontend/src/shared/i18n/locales/fr/settings.json
🧠 Learnings (4)
📓 Common learnings
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-25T18:29:32.954Z
Learning: Applies to apps/frontend/src/shared/i18n/locales/**/*.json : Add new translation keys to ALL language files (minimum: `en/*.json` and `fr/*.json`) in `apps/frontend/src/shared/i18n/locales/`
📚 Learning: 2025-12-25T18:29:32.954Z
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-25T18:29:32.954Z
Learning: Applies to apps/frontend/src/**/*.{ts,tsx} : Always use translation keys with `useTranslation()` for all user-facing text in React/TypeScript frontend components - use format `namespace:section.key` (e.g., `navigation:items.githubPRs`)

Applied to files:

  • apps/frontend/src/renderer/components/settings/AppSettings.tsx
  • apps/frontend/src/shared/i18n/index.ts
📚 Learning: 2025-12-25T18:29:32.954Z
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-25T18:29:32.954Z
Learning: Applies to apps/frontend/src/shared/i18n/locales/**/*.json : Add new translation keys to ALL language files (minimum: `en/*.json` and `fr/*.json`) in `apps/frontend/src/shared/i18n/locales/`

Applied to files:

  • apps/frontend/src/shared/i18n/locales/fr/deploy.json
  • apps/frontend/src/shared/i18n/locales/fr/navigation.json
  • apps/frontend/src/shared/i18n/locales/en/navigation.json
  • apps/frontend/src/shared/i18n/locales/en/deploy.json
  • apps/frontend/src/shared/i18n/locales/en/settings.json
  • apps/frontend/src/shared/i18n/locales/fr/settings.json
  • apps/frontend/src/shared/i18n/index.ts
📚 Learning: 2025-12-25T18:29:32.954Z
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-25T18:29:32.954Z
Learning: Applies to apps/frontend/src/**/*.{ts,tsx} : Never use hardcoded strings in JSX/TSX files for user-facing text - always reference translation keys from `apps/frontend/src/shared/i18n/locales/`

Applied to files:

  • apps/frontend/src/shared/i18n/index.ts
🪛 Biome (2.1.2)
apps/frontend/src/renderer/components/Deploy.tsx

[error] 57-60: Provide an explicit type prop for the button element.

The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset

(lint/a11y/useButtonType)

🔇 Additional comments (27)
apps/frontend/src/renderer/components/ui/dialog.tsx (1)

62-70: LGTM! Close button styling improvements enhance UX.

The updated close button styling provides better visual feedback and interaction:

  • Larger clickable area with clear visual states
  • Background and border make the button more discoverable
  • pointer-events-none on the icon ensures click events properly register on the button element
  • Smooth color transitions improve polish
apps/frontend/src/shared/i18n/locales/fr/navigation.json (1)

17-18: LGTM!

The French translation for the new "deploy" navigation item is correctly added alongside "worktrees", matching the structure in the English locale file.

apps/frontend/src/shared/i18n/index.ts (1)

12-12: LGTM!

The deploy namespace is correctly integrated: imports from both EN and FR locales, registered in the resources object for both languages, and added to the namespace array. This follows the established i18n patterns.

Also applies to: 22-22, 34-35, 44-45, 56-56

apps/frontend/src/shared/i18n/locales/fr/deploy.json (1)

1-65: LGTM!

The French deploy translation file is comprehensive and mirrors the English structure. All deployment UI strings including error messages, loading states, and form labels are properly translated.

apps/frontend/src/renderer/components/DokployDeployModal.tsx (2)

863-1083: Deployment flow is well-structured.

The handleDeploy function properly sequences project creation, per-service app creation, GitHub configuration, domain setup, and deployment triggering. Non-blocking error handling for domain, env vars, and deployment trigger failures is intentional and appropriate for UX resilience.


1789-1808: Footer button logic is adaptive and correct.

The primary action button correctly adapts based on deployment mode (all linked, all create new, or mixed), with appropriate disabled states and dynamic labels.

apps/frontend/src/shared/types/settings.ts (1)

104-105: LGTM!

The deploymentProviders optional field is correctly integrated into AppSettings, allowing Dokploy configuration to be stored alongside other app settings.

apps/frontend/src/renderer/components/settings/index.ts (1)

11-11: LGTM!

The DeploymentSettings component is correctly exported from the settings barrel, following the established pattern for other settings components.

apps/frontend/src/shared/types/index.ts (1)

19-19: LGTM!

The Dokploy types are correctly re-exported from the central types barrel, following the established pattern and placement order.

apps/frontend/src/shared/i18n/locales/en/settings.json (1)

39-43: Verify French translation keys are added for deployment section.

Per coding guidelines, new translation keys must be added to ALL language files. Confirm that apps/frontend/src/shared/i18n/locales/fr/settings.json contains matching deployment keys corresponding to the additions in the English file (sections.deployment and top-level deployment object with Dokploy configuration).

apps/frontend/src/renderer/components/settings/AppSettings.tsx (1)

19-21: LGTM! Clean integration of deployment settings.

The deployment settings are properly integrated following the existing patterns. Translation keys are correctly used, type safety is maintained, and the component follows the established navigation structure.

Also applies to: 40-40, 56-56, 70-70, 179-180

apps/frontend/src/main/ipc-handlers/index.ts (1)

31-31: LGTM! Standard IPC handler integration.

The Dokploy IPC handlers are registered following the established pattern used by other handlers. The placement and export structure are consistent with the existing codebase.

Also applies to: 102-103, 126-127

apps/frontend/src/renderer/App.tsx (1)

47-47: LGTM! Deploy component properly integrated.

The Deploy component is integrated following the same pattern as other view components (GitHubPRs, Changelog, Worktrees). The conditional rendering logic and callback wiring are correct.

Also applies to: 696-704

apps/frontend/src/renderer/components/Sidebar.tsx (1)

20-21: LGTM! Sidebar integration follows established patterns.

The deploy view is properly integrated into the sidebar:

  • SidebarView type extended correctly
  • Navigation item follows the existing structure
  • Keyboard shortcut 'Y' is unique and appropriate
  • Translation key follows the established pattern

Also applies to: 54-54, 77-78

apps/frontend/src/preload/api/index.ts (1)

11-11: LGTM! DokployAPI properly integrated into preload API surface.

The Dokploy API is correctly integrated following the established pattern used by other APIs:

  • Proper imports and type definitions
  • ElectronAPI interface extended correctly
  • Factory function integrated with spread operator
  • Consistent export structure for types and creators

Also applies to: 22-23, 37-37, 52-53, 66-67

apps/frontend/src/shared/i18n/locales/en/navigation.json (1)

17-18: Ensure the French translation file is updated with the "deploy" key.

The new "deploy" translation key added to apps/frontend/src/shared/i18n/locales/en/navigation.json must also be added to apps/frontend/src/shared/i18n/locales/fr/navigation.json per the translation guidelines requiring all keys across all language files.

apps/frontend/src/shared/i18n/locales/en/deploy.json (1)

1-65: Ensure French translation file exists and is complete.

The apps/frontend/src/shared/i18n/locales/fr/deploy.json file must exist and contain all keys from the English version. Per the translation guidelines, every new translation key must be added to ALL language files (minimum: en/*.json and fr/*.json). Verify the French file has matching keys for all entries including nested objects under noProviders, providers, modal, and modal.error.

apps/frontend/src/renderer/components/Deploy.tsx (1)

1-84: Verify French translation file exists for deploy namespace.

Ensure that apps/frontend/src/shared/i18n/locales/fr/deploy.json exists and contains all keys used in this component:

  • noProviders.title, noProviders.description, noProviders.openSettings
  • title, description
  • providers.dokploy.title, providers.dokploy.description

Per project requirements, all translation keys must be added to all language files (minimum: en/deploy.json and fr/deploy.json).

apps/frontend/src/renderer/lib/browser-mock.ts (1)

113-119: LGTM!

The Dokploy browser mocks are correctly implemented with appropriate return types matching the DokployApiResponse interface. The dokployApi correctly returns an error state for browser context while other operations return success with sensible defaults.

apps/frontend/src/shared/constants/ipc.ts (1)

363-368: LGTM!

The new Dokploy IPC channel constants follow the established naming convention (dokploy:*) and are properly defined within the as const assertion for type safety.

apps/frontend/src/renderer/components/settings/DeploymentSettings.tsx (2)

31-62: LGTM!

The component properly uses useTranslation hooks for both settings and common namespaces. State management is well-organized with clear separation between add/edit modes. The updateDokployAccounts helper correctly propagates changes through the parent settings callback.


133-136: LGTM!

The maskApiKey function provides appropriate masking for API keys, showing only the first and last 4 characters for longer keys while completely masking short keys. This is a good security practice for displaying sensitive credentials.

apps/frontend/src/shared/i18n/locales/fr/settings.json (1)

244-261: LGTM!

The French translations for the Dokploy deployment settings are complete and follow the established translation structure. All keys match the expected structure used in DeploymentSettings.tsx. Based on learnings, ensure the corresponding English translations in en/settings.json contain identical keys.

apps/frontend/src/shared/types/ipc.ts (1)

594-599: LGTM!

The Dokploy operations are correctly added to the ElectronAPI interface with proper typing. The method signatures align with the DokployAPI interface in the preload module and the IPC handlers in the main process.

apps/frontend/src/preload/api/modules/dokploy-api.ts (1)

1-24: LGTM!

The Dokploy preload API module is well-structured with a clean separation between the interface definition and factory function. The IPC channel mappings are correct, and type safety is properly maintained through the generic type parameter on dokployApi.

apps/frontend/src/shared/types/dokploy.ts (2)

14-29: LGTM!

The DokployGitHubProvider interface is well-designed with support for both the nested github object structure and legacy top-level fields. The inline comments clearly document the purpose of each field variant.


101-130: LGTM!

The DokployServiceDeployment and DokployProjectDeployment interfaces are well-structured for persisting deployment state. Using ISO date strings for deployedAt, createdAt, and updatedAt ensures consistent serialization. The applicationType field properly references the union type for type safety.

Comment on lines +45 to +53
const response = await fetch(url, options);

if (!response.ok) {
const errorText = await response.text();
return {
success: false,
error: `API error: ${response.status} - ${errorText}`
};
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add a timeout to the fetch request to prevent indefinite hangs.

The fetch call has no timeout configured. If the Dokploy server is unresponsive, the request will hang indefinitely, potentially blocking the UI or causing resource exhaustion.

🔎 Proposed fix using AbortController
+const DOKPLOY_REQUEST_TIMEOUT_MS = 30000; // 30 seconds

 async function makeDokployRequest<T>(
   baseUrl: string,
   apiKey: string,
   endpoint: string,
   method: 'GET' | 'POST',
   body?: Record<string, unknown>,
   query?: Record<string, string>
 ): Promise<DokployApiResponse<T>> {
   try {
     // Build URL - Dokploy uses TRPC format
     // baseUrl should be like https://dokploy.example.com/api
     let url = `${baseUrl}/${endpoint}`;

     // For GET requests with TRPC, we need to pass input as JSON in query string
     if (method === 'GET') {
       const input = query && Object.keys(query).length > 0 ? query : {};
       url += `?input=${encodeURIComponent(JSON.stringify(input))}`;
     }

+    const controller = new AbortController();
+    const timeoutId = setTimeout(() => controller.abort(), DOKPLOY_REQUEST_TIMEOUT_MS);

     const options: RequestInit = {
       method,
       headers: {
         'Content-Type': 'application/json',
         'x-api-key': apiKey
-      }
+      },
+      signal: controller.signal
     };

     if (method === 'POST' && body) {
       options.body = JSON.stringify(body);
     }

     const response = await fetch(url, options);
+    clearTimeout(timeoutId);
🤖 Prompt for AI Agents
In apps/frontend/src/main/ipc-handlers/dokploy-handlers.ts around lines 45-53,
the fetch call lacks a timeout and can hang indefinitely; wrap the request with
an AbortController: create a controller, pass controller.signal to fetch, start
a setTimeout that calls controller.abort() after a chosen timeout (e.g., 10s),
and clear the timeout when fetch completes. Catch the abort error and return a
structured { success: false, error: 'Request timed out' } (or include the
abort/error message) while preserving existing API error handling for non-ok
responses. Ensure no memory leak by clearing the timer in all code paths.

Comment on lines +76 to +101
console.log('[Dokploy Backend] readEnvFile called with:', servicePath);

// Common env file names to check (in priority order)
const envFileNames = ['.env', '.env.local', '.env.development', '.env.production'];
let allEnvVars: Record<string, string> = {};

for (const fileName of envFileNames) {
const envPath = path.join(servicePath, fileName);
console.log('[Dokploy Backend] Checking:', envPath, 'exists:', existsSync(envPath));

if (existsSync(envPath)) {
try {
const content = readFileSync(envPath, 'utf-8');
console.log(`[Dokploy Backend] Found ${fileName}, length:`, content.length);
const parsed = parseEnvFile(content);
console.log(`[Dokploy Backend] Parsed from ${fileName}:`, Object.keys(parsed));
// Merge (earlier files take precedence)
allEnvVars = { ...parsed, ...allEnvVars };
} catch (err) {
console.error(`[Dokploy Backend] Error reading ${fileName}:`, err);
}
}
}

console.log('[Dokploy Backend] Total env vars found:', Object.keys(allEnvVars));
return allEnvVars;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Remove or conditionally enable verbose logging in production.

The console.log statements throughout readEnvFile are useful for debugging but will clutter production logs. Consider using a debug flag or removing them.

🔎 Suggested approach
+const DEBUG_DOKPLOY = process.env.NODE_ENV === 'development';

 function readEnvFile(servicePath: string): Record<string, string> {
-  console.log('[Dokploy Backend] readEnvFile called with:', servicePath);
+  if (DEBUG_DOKPLOY) console.log('[Dokploy Backend] readEnvFile called with:', servicePath);

   // Common env file names to check (in priority order)
   const envFileNames = ['.env', '.env.local', '.env.development', '.env.production'];
   let allEnvVars: Record<string, string> = {};

   for (const fileName of envFileNames) {
     const envPath = path.join(servicePath, fileName);
-    console.log('[Dokploy Backend] Checking:', envPath, 'exists:', existsSync(envPath));
+    if (DEBUG_DOKPLOY) console.log('[Dokploy Backend] Checking:', envPath, 'exists:', existsSync(envPath));
     // ... apply similar changes to other log statements
   }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/frontend/src/main/ipc-handlers/dokploy-handlers.ts around lines 76 to
101, the function readEnvFile contains multiple console.log/console.error calls
that should not run unconditionally in production; remove the noisy console
statements or wrap them behind a configurable debug flag or environment check
(e.g., process.env.DEBUG or an app logger.isDebugEnabled()) and replace direct
console usage with the app's logger where available so that logging is only
emitted when debugging is enabled and errors still get logged appropriately in
production.

Comment on lines +177 to +196
// Get deployment info from project directory
ipcMain.handle(
IPC_CHANNELS.DOKPLOY_GET_DEPLOYMENT,
async (_, projectPath: string): Promise<DokployApiResponse<DokployProjectDeployment | null>> => {
try {
const filePath = path.join(projectPath, DEPLOYMENT_FILE);
if (!existsSync(filePath)) {
return { success: true, data: null };
}
const content = readFileSync(filePath, 'utf-8');
const deployment = JSON.parse(content) as DokployProjectDeployment;
return { success: true, data: deployment };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to read deployment info'
};
}
}
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Handle JSON parse errors gracefully for corrupted deployment files.

If .dokploy.json contains malformed JSON, JSON.parse will throw and the error handler will return a generic message. Consider providing a more specific error message for parse failures.

🔎 Proposed improvement
   ipcMain.handle(
     IPC_CHANNELS.DOKPLOY_GET_DEPLOYMENT,
     async (_, projectPath: string): Promise<DokployApiResponse<DokployProjectDeployment | null>> => {
       try {
         const filePath = path.join(projectPath, DEPLOYMENT_FILE);
         if (!existsSync(filePath)) {
           return { success: true, data: null };
         }
         const content = readFileSync(filePath, 'utf-8');
-        const deployment = JSON.parse(content) as DokployProjectDeployment;
-        return { success: true, data: deployment };
+        try {
+          const deployment = JSON.parse(content) as DokployProjectDeployment;
+          return { success: true, data: deployment };
+        } catch (parseError) {
+          return {
+            success: false,
+            error: `Invalid deployment file format: ${parseError instanceof Error ? parseError.message : 'parse error'}`
+          };
+        }
       } catch (error) {
         return {
           success: false,
           error: error instanceof Error ? error.message : 'Failed to read deployment info'
         };
       }
     }
   );
🤖 Prompt for AI Agents
In apps/frontend/src/main/ipc-handlers/dokploy-handlers.ts around lines 177 to
196, the code treats any exception from reading/parsing .dokploy.json the same
and returns a generic error message; detect JSON parse failures specifically and
return a clearer message. Change the try/catch so that after reading the file
you catch JSON.parse errors (e.g. detect instanceof SyntaxError or inspect the
error message) and return success: false with a descriptive message like
"Malformed .dokploy.json: failed to parse JSON" (including filePath for
context), while preserving the existing generic error branch for other I/O
errors.

Comment on lines +57 to +60
<button
onClick={() => setShowDokployModal(true)}
className="w-full flex items-center justify-between p-4 rounded-lg border border-border bg-card hover:bg-accent/50 transition-colors text-left"
>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add explicit type="button" to the button element.

The button element should have an explicit type="button" attribute to prevent potential form submission issues if this component is ever used within a form context.

🔎 Proposed fix
           <button
+            type="button"
             onClick={() => setShowDokployModal(true)}
             className="w-full flex items-center justify-between p-4 rounded-lg border border-border bg-card hover:bg-accent/50 transition-colors text-left"
           >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<button
onClick={() => setShowDokployModal(true)}
className="w-full flex items-center justify-between p-4 rounded-lg border border-border bg-card hover:bg-accent/50 transition-colors text-left"
>
<button
type="button"
onClick={() => setShowDokployModal(true)}
className="w-full flex items-center justify-between p-4 rounded-lg border border-border bg-card hover:bg-accent/50 transition-colors text-left"
>
🧰 Tools
🪛 Biome (2.1.2)

[error] 57-60: Provide an explicit type prop for the button element.

The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset

(lint/a11y/useButtonType)

🤖 Prompt for AI Agents
In apps/frontend/src/renderer/components/Deploy.tsx around lines 57 to 60, the
button element lacks an explicit type attribute which can cause it to default to
"submit" inside forms; update the JSX to add type="button" on the button element
to ensure it does not trigger form submission when clicked.

Comment on lines +205 to +228
useEffect(() => {
const loadExistingDeployment = async () => {
if (open && project?.path) {
setIsLoadingDeployment(true);
try {
const result = await window.electronAPI.dokployGetDeployment(project.path);
if (result.success && result.data) {
setExistingDeployment(result.data);
// If deployment exists, auto-select that account and switch to resync mode
setSelectedAccountId(result.data.accountId);
setStep('resync');
} else {
setExistingDeployment(null);
}
} catch (err) {
console.error('Failed to load deployment info:', err);
setExistingDeployment(null);
} finally {
setIsLoadingDeployment(false);
}
}
};
loadExistingDeployment();
}, [open, project?.path]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Async effect lacks cleanup/cancellation.

This effect performs an async operation but doesn't handle component unmount. If the modal closes while loadExistingDeployment is in-flight, the state setters (setExistingDeployment, setSelectedAccountId, etc.) will be called on an unmounted component.

Consider using an abort flag or AbortController:

🔎 Suggested pattern
 useEffect(() => {
+  let cancelled = false;
   const loadExistingDeployment = async () => {
     if (open && project?.path) {
       setIsLoadingDeployment(true);
       try {
         const result = await window.electronAPI.dokployGetDeployment(project.path);
+        if (cancelled) return;
         if (result.success && result.data) {
           setExistingDeployment(result.data);
           setSelectedAccountId(result.data.accountId);
           setStep('resync');
         } else {
           setExistingDeployment(null);
         }
       } catch (err) {
+        if (cancelled) return;
         console.error('Failed to load deployment info:', err);
         setExistingDeployment(null);
       } finally {
+        if (!cancelled) setIsLoadingDeployment(false);
-        setIsLoadingDeployment(false);
       }
     }
   };
   loadExistingDeployment();
+  return () => { cancelled = true; };
 }, [open, project?.path]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
const loadExistingDeployment = async () => {
if (open && project?.path) {
setIsLoadingDeployment(true);
try {
const result = await window.electronAPI.dokployGetDeployment(project.path);
if (result.success && result.data) {
setExistingDeployment(result.data);
// If deployment exists, auto-select that account and switch to resync mode
setSelectedAccountId(result.data.accountId);
setStep('resync');
} else {
setExistingDeployment(null);
}
} catch (err) {
console.error('Failed to load deployment info:', err);
setExistingDeployment(null);
} finally {
setIsLoadingDeployment(false);
}
}
};
loadExistingDeployment();
}, [open, project?.path]);
useEffect(() => {
let cancelled = false;
const loadExistingDeployment = async () => {
if (open && project?.path) {
setIsLoadingDeployment(true);
try {
const result = await window.electronAPI.dokployGetDeployment(project.path);
if (cancelled) return;
if (result.success && result.data) {
setExistingDeployment(result.data);
// If deployment exists, auto-select that account and switch to resync mode
setSelectedAccountId(result.data.accountId);
setStep('resync');
} else {
setExistingDeployment(null);
}
} catch (err) {
if (cancelled) return;
console.error('Failed to load deployment info:', err);
setExistingDeployment(null);
} finally {
if (!cancelled) setIsLoadingDeployment(false);
}
}
};
loadExistingDeployment();
return () => { cancelled = true; };
}, [open, project?.path]);
🤖 Prompt for AI Agents
In apps/frontend/src/renderer/components/DokployDeployModal.tsx around lines 205
to 228, the async useEffect launches loadExistingDeployment but does not cancel
or guard state updates when the component/unmount or modal closes; add a
cancellation guard (e.g., let cancelled = false or an AbortController) and in
the cleanup return set cancelled = true (or abort the controller), then before
each setState (setIsLoadingDeployment, setExistingDeployment,
setSelectedAccountId, setStep) check the guard and only update state when not
cancelled; if the dokployGetDeployment supports AbortSignal pass the signal to
it and handle abort errors appropriately to avoid setting state after unmount.

Comment on lines +1177 to +1322
if (step === 'resync' && existingDeployment) {
return (
<div className="space-y-4">
{/* Deployment Info */}
<div className="p-3 rounded-lg bg-muted/50 border border-border">
<div className="flex items-center gap-2 mb-2">
<CheckCircle2 className="h-4 w-4 text-green-500" />
<span className="font-medium text-sm">Deployed to Dokploy</span>
</div>
<div className="text-xs text-muted-foreground space-y-1">
<p>Project: {existingDeployment.projectName}</p>
<p>Account: {existingDeployment.accountName}</p>
<p>Last updated: {new Date(existingDeployment.updatedAt).toLocaleString()}</p>
</div>
</div>

{/* Services with resync option */}
<div className="space-y-3">
<Label className="flex items-center gap-2">
<Package className="h-4 w-4" />
Deployed Services
</Label>
<ScrollArea className="h-[350px] rounded-md border p-2">
<div className="space-y-3">
{existingDeployment.services.map((deployedService) => {
const service = services.find(s => s.key === deployedService.serviceKey);
const config = serviceConfigs[deployedService.serviceKey];
if (!config) return null;

const enabledCount = Object.values(config.envVarsEnabled).filter(Boolean).length;
const totalCount = Object.keys(config.envVars).length;

return (
<div key={deployedService.serviceKey} className="p-3 rounded-lg border bg-muted/30">
<div className="flex items-center justify-between mb-2">
<div>
<span className="font-medium text-sm">{deployedService.serviceName}</span>
<span className="text-xs text-muted-foreground ml-2">
({deployedService.appName})
</span>
</div>
<Button
size="sm"
variant="outline"
onClick={() => handleResyncEnvVars(deployedService.serviceKey)}
disabled={resyncService !== null}
>
{resyncService === deployedService.serviceKey ? (
<>
<Loader2 className="h-3 w-3 animate-spin mr-1" />
Syncing...
</>
) : (
<>
<RefreshCw className="h-3 w-3 mr-1" />
Sync Env Vars
</>
)}
</Button>
</div>

{/* Environment Variables */}
<div className="mt-2">
<button
type="button"
onClick={() => toggleEnvVars(deployedService.serviceKey)}
className="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors w-full"
>
{expandedEnvVars[deployedService.serviceKey] ? (
<ChevronDown className="h-3 w-3" />
) : (
<ChevronRight className="h-3 w-3" />
)}
Environment Variables ({enabledCount}/{totalCount})
</button>
{expandedEnvVars[deployedService.serviceKey] && (
<div className="bg-muted/30 rounded p-2 space-y-2 max-h-48 overflow-y-auto mt-1">
{Object.entries(config.envVars).map(([key, value]) => {
const isEnabled = config.envVarsEnabled[key] !== false;
return (
<div key={key} className={`flex items-center gap-2 ${!isEnabled ? 'opacity-50' : ''}`}>
<Checkbox
checked={isEnabled}
onCheckedChange={(checked) => {
const newEnvVarsEnabled = { ...config.envVarsEnabled, [key]: !!checked };
updateServiceConfig(deployedService.serviceKey, { envVarsEnabled: newEnvVarsEnabled });
}}
className="h-4 w-4"
/>
<span className="text-xs font-mono text-foreground min-w-[120px] truncate" title={key}>
{key}
</span>
<Input
value={value}
onChange={(e) => {
const newEnvVars = { ...config.envVars, [key]: e.target.value };
updateServiceConfig(deployedService.serviceKey, { envVars: newEnvVars });
}}
className="h-7 text-xs font-mono flex-1"
placeholder="Value"
disabled={!isEnabled}
/>
</div>
);
})}

{/* Add Custom Variable */}
<div className="flex items-center gap-2 pt-2 border-t border-border/50">
<Input
value={customEnvKey}
onChange={(e) => setCustomEnvKey(e.target.value)}
placeholder="KEY"
className="h-7 text-xs font-mono w-[120px]"
/>
<Input
value={customEnvValue}
onChange={(e) => setCustomEnvValue(e.target.value)}
placeholder="value"
className="h-7 text-xs font-mono flex-1"
/>
<Button
size="sm"
variant="outline"
className="h-7 px-2"
onClick={() => addCustomEnvVar(deployedService.serviceKey)}
disabled={!customEnvKey.trim()}
>
Add
</Button>
</div>
</div>
)}
</div>
</div>
);
})}
</div>
</ScrollArea>
</div>

{error && (
<p className="text-sm text-destructive">{error}</p>
)}
</div>
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Resync UI contains hardcoded strings.

The resync step (lines 1177-1322) has multiple hardcoded strings that should use translation keys:

  • Line 1184: "Deployed to Dokploy"
  • Line 1187-1189: "Project:", "Account:", "Last updated:"
  • Line 1197: "Deployed Services"
  • Line 1227/1232: "Syncing...", "Sync Env Vars"
  • Line 1250: "Environment Variables"
  • Line 1288/1295: "KEY", "value" placeholders
  • Line 1304: "Add"

As per coding guidelines, add these to deploy.json locale files.

Comment on lines +1493 to +1515
{config.enabled && (
<>
{/* Mode Toggle */}
<div className="flex items-center gap-2 mt-2 p-1 rounded-md bg-muted/30 border border-border/50">
<button
type="button"
onClick={() => updateServiceConfig(service.key, { linkMode: false })}
className={`flex-1 py-1 px-2 rounded text-xs font-medium transition-colors ${
!config.linkMode ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:text-foreground'
}`}
>
Create New
</button>
<button
type="button"
onClick={() => updateServiceConfig(service.key, { linkMode: true })}
className={`flex-1 py-1 px-2 rounded text-xs font-medium transition-colors ${
config.linkMode ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:text-foreground'
}`}
>
Link Existing
</button>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Mode toggle buttons use hardcoded text.

The "Create New" / "Link Existing" toggle buttons (lines 1504, 1513) use hardcoded strings. These should be translation keys like t('modal.mode.createNew') and t('modal.mode.linkExisting').

🤖 Prompt for AI Agents
In apps/frontend/src/renderer/components/DokployDeployModal.tsx around lines
1493 to 1515, the two mode toggle buttons render hardcoded labels "Create New"
and "Link Existing"; replace those literals with translation calls (e.g.
t('modal.mode.createNew') and t('modal.mode.linkExisting')), ensuring the
component imports/has access to the t function from your i18n hook/context and
that corresponding translation keys are added to the locale files; keep the
surrounding JSX and className logic unchanged.

Comment on lines +83 to +85
const handleDeleteAccount = (accountId: string) => {
updateDokployAccounts(dokployAccounts.filter(a => a.id !== accountId));
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider adding a confirmation dialog before deleting accounts.

The delete action immediately removes the account without user confirmation. For destructive operations involving credentials, a confirmation step helps prevent accidental deletions.

🔎 Suggested approach
+ const [accountToDelete, setAccountToDelete] = useState<string | null>(null);

  const handleDeleteAccount = (accountId: string) => {
+   // Show confirmation before deleting
+   if (!window.confirm(t('deployment.dokploy.confirmDelete'))) return;
    updateDokployAccounts(dokployAccounts.filter(a => a.id !== accountId));
  };

Alternatively, implement a proper modal confirmation dialog for a more polished UX.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleDeleteAccount = (accountId: string) => {
updateDokployAccounts(dokployAccounts.filter(a => a.id !== accountId));
};
const handleDeleteAccount = (accountId: string) => {
// Show confirmation before deleting
if (!window.confirm(t('deployment.dokploy.confirmDelete'))) return;
updateDokployAccounts(dokployAccounts.filter(a => a.id !== accountId));
};
🤖 Prompt for AI Agents
In apps/frontend/src/renderer/components/settings/DeploymentSettings.tsx around
lines 83 to 85, the handleDeleteAccount function immediately deletes an account
without confirmation; add a user confirmation step before calling
updateDokployAccounts. Modify the handler to prompt the user (e.g.,
window.confirm or a modal) with a clear warning including the account
identifier, and only call updateDokployAccounts(dokployAccounts.filter(...))
when the user explicitly confirms; if using a modal, manage modal open/close
state and proceed with deletion on confirm, canceling on dismissal.

<X className="h-4 w-4" />
<span className="sr-only">Close</span>
<X className="h-4 w-4 pointer-events-none" />
<span className="sr-only pointer-events-none">Close</span>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Replace hardcoded "Close" text with translation key.

The hardcoded "Close" text violates the coding guidelines. Screen reader labels are user-facing content and must be internationalized.

As per coding guidelines: "Always use translation keys with useTranslation() for all user-facing text in React/TypeScript frontend components" and "Never use hardcoded strings in JSX/TSX files for user-facing text."

🔎 Proposed fix using translation key

First, add the translation key to all language files (minimum en/*.json and fr/*.json) in apps/frontend/src/shared/i18n/locales/. For example, in the appropriate namespace file (e.g., common.json):

{
  "dialog": {
    "close": "Close"
  }
}

Then update the component to use the translation:

+import { useTranslation } from 'react-i18next';
+
 const DialogContent = React.forwardRef<
   React.ElementRef<typeof DialogPrimitive.Content>,
   DialogContentProps
 >(({ className, children, hideCloseButton, ...props }, ref) => (
+  const { t } = useTranslation('common');
+  return (
   <DialogPortal>
     <DialogOverlay />
     <DialogPrimitive.Content
       ...
     >
       {children}
       {!hideCloseButton && (
         <DialogPrimitive.Close
           ...
         >
           <X className="h-4 w-4 pointer-events-none" />
-          <span className="sr-only pointer-events-none">Close</span>
+          <span className="sr-only pointer-events-none">{t('dialog.close')}</span>
         </DialogPrimitive.Close>
       )}
     </DialogPrimitive.Content>
   </DialogPortal>
+  );
 ));

Based on learnings, ensure the translation key is added to ALL language files, minimum en/*.json and fr/*.json.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/frontend/src/renderer/components/ui/dialog.tsx around line 71, the
screen-reader label uses a hardcoded "Close" string which must be
internationalized; import and use useTranslation() from react-i18next (or
existing i18n hook) and replace the literal with a translation lookup like
t('dialog.close') for the sr-only span, and ensure the key "dialog.close" is
added to all locale files (at minimum
apps/frontend/src/shared/i18n/locales/en/... and fr/...) under the appropriate
namespace (e.g., common.json) with the translated values.

Comment on lines +81 to +92
// Deployment provider account types
export interface DokployAccount {
id: string;
name: string;
apiKey: string;
baseUrl: string; // Default: https://app.dokploy.com/api
createdAt: Date;
}

export interface DeploymentProviders {
dokploy: DokployAccount[];
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider string type for createdAt to match JSON serialization.

DokployAccount.createdAt is typed as Date, but when settings are persisted to JSON and reloaded, it will become a string. This could cause type mismatches at runtime.

Consider using string (ISO format) or adding serialization/deserialization logic:

 export interface DokployAccount {
   id: string;
   name: string;
   apiKey: string;
   baseUrl: string;
-  createdAt: Date;
+  createdAt: string; // ISO date string
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Deployment provider account types
export interface DokployAccount {
id: string;
name: string;
apiKey: string;
baseUrl: string; // Default: https://app.dokploy.com/api
createdAt: Date;
}
export interface DeploymentProviders {
dokploy: DokployAccount[];
}
// Deployment provider account types
export interface DokployAccount {
id: string;
name: string;
apiKey: string;
baseUrl: string; // Default: https://app.dokploy.com/api
createdAt: string; // ISO date string
}
export interface DeploymentProviders {
dokploy: DokployAccount[];
}
🤖 Prompt for AI Agents
In apps/frontend/src/shared/types/settings.ts around lines 81 to 92,
DokployAccount.createdAt is declared as Date but JSON persistence will serialize
it to a string; change the type to string (ISO timestamp) or implement explicit
serialization/deserialization so the runtime type matches the declared type —
e.g., update the interface to use createdAt: string and ensure code that
consumes it parses new Date(createdAt) where a Date object is required, or keep
createdAt: Date and add toJSON/fromJSON conversion when saving/loading settings.

@JoshuaRileyDev JoshuaRileyDev changed the title feat: Enhance Dokploy deployment with comprehensive linking and control options feat: Add Dokploy deployment with comprehensive linking and control options Dec 26, 2025
JoshuaRileyDev and others added 2 commits December 26, 2025 20:54
Replace hardcoded "Close" button text with i18n translation keys to comply with coding guidelines requiring all user-facing text to be internationalized.

Changes:
- Add "close" key to en/deploy.json ("Close")
- Add "close" key to fr/deploy.json ("Fermer")
- Replace hardcoded "Close" text with {t('modal.close')} in DokployDeployModal (2 instances)

Fixes compliance with i18n guidelines: "Always use translation keys with useTranslation() for all user-facing text in React/TypeScript frontend components"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

♻️ Duplicate comments (8)
apps/frontend/src/renderer/components/DokployDeployModal.tsx (8)

205-228: Async effect lacks cleanup/cancellation.

This effect performs an async operation but doesn't handle component unmount. If the modal closes while loadExistingDeployment is in-flight, the state setters will be called on an unmounted component, potentially causing memory leaks and console warnings.

🔎 Suggested pattern
 useEffect(() => {
+  let cancelled = false;
   const loadExistingDeployment = async () => {
     if (open && project?.path) {
       setIsLoadingDeployment(true);
       try {
         const result = await window.electronAPI.dokployGetDeployment(project.path);
+        if (cancelled) return;
         if (result.success && result.data) {
           setExistingDeployment(result.data);
           setSelectedAccountId(result.data.accountId);
           setStep('resync');
         } else {
           setExistingDeployment(null);
         }
       } catch (err) {
+        if (cancelled) return;
         console.error('Failed to load deployment info:', err);
         setExistingDeployment(null);
       } finally {
+        if (!cancelled) setIsLoadingDeployment(false);
-        setIsLoadingDeployment(false);
       }
     }
   };
   loadExistingDeployment();
+  return () => { cancelled = true; };
 }, [open, project?.path]);

264-329: Async service config initialization should guard against unmount.

The initializeConfigs async function reads env vars for each service but lacks cancellation handling. If the modal closes mid-initialization, state updates will target an unmounted component.

Apply the same cancellation pattern as suggested for the deployment loading effect: add a cancelled flag and return a cleanup function that sets it to true, then check the flag before calling setServiceConfigs.


274-298: Remove debug console.log statements before merging.

Lines 275-298 contain extensive debug logging (console.log('[Dokploy]...')). These should be removed or replaced with a proper logging utility that can be disabled in production.

Similar debug statements appear throughout the file (lines 632, 637, 643, 689, 796, 881, 982, 989, 1002, 1009, 1014, 1029, 1073).

Consider either:

  • Removing all debug logs before production
  • Wrapping them in a development-only guard (if (import.meta.env.DEV) or similar)
  • Replacing with a project logging utility that respects environment/verbosity settings

1104-1175: Hardcoded user-facing strings violate i18n guidelines.

Multiple UI strings in the deploying, success, and error step renders are hardcoded instead of using translation keys. Per coding guidelines, all user-facing text must use useTranslation().

Examples in this section:

  • Line 1110: "Configuring ${deployingService}..." (use interpolation with t())
  • Line 1114: "Configured:"
  • Line 1128: "Deployment Started" / "Applications Configured"
  • Lines 1133-1141: Success description text
  • Line 1147: "Configured services:"
  • Line 1171: "Try Again"

Consider adding these keys to deploy.json:

  • modal.deploying.configuring (with {{service}} interpolation)
  • modal.deploying.settingUp
  • modal.deploying.configuredLabel
  • modal.success.titleDeployed
  • modal.success.titleConfigured
  • modal.success.descriptionDeployed
  • modal.success.descriptionConfigured
  • modal.success.configuredServicesLabel
  • modal.error.tryAgain

Based on learnings, all user-facing text should use translation keys.


1177-1322: Resync UI contains hardcoded strings.

The resync step has multiple hardcoded strings that should use translation keys:

  • Line 1184: "Deployed to Dokploy"
  • Lines 1187-1189: "Project:", "Account:", "Last updated:"
  • Line 1197: "Deployed Services"
  • Lines 1227/1232: "Syncing...", "Sync Env Vars"
  • Line 1250: "Environment Variables"
  • Lines 1288/1295: "KEY", "value" placeholders
  • Line 1304: "Add"

As per coding guidelines, add these to deploy.json locale files and use t() function calls throughout.

Based on learnings, all user-facing text should use translation keys.


1493-1515: Mode toggle buttons use hardcoded text.

The "Create New" / "Link Existing" toggle buttons (lines 1504, 1513) use hardcoded strings. These should be translation keys like t('modal.mode.createNew') and t('modal.mode.linkExisting').

Add to translation files:

"modal": {
  "mode": {
    "createNew": "Create New",
    "linkExisting": "Link Existing"
  }
}

Based on learnings, all user-facing text should use translation keys.


1605-1730: Create new mode UI contains hardcoded strings.

The create new mode section contains multiple hardcoded strings:

  • Line 1606: "App Name"
  • Line 1612: "app-name" (placeholder)
  • Line 1621: "Branch"
  • Line 1630: "Select branch" (placeholder)
  • Line 1646: "Domain (optional)"
  • Line 1653: "app.example.com" (placeholder)
  • Line 1671: Environment variables label with count
  • Line 1710: "Build path:" label
  • Line 1717: "Deploy immediately after setup"
  • Line 1720: Toggle description text

All user-facing labels, placeholders, and descriptions should use translation keys from the deploy.json files.

Based on learnings, all user-facing text should use translation keys.


1520-1595: Link mode UI contains hardcoded strings.

The link existing mode section contains multiple hardcoded strings:

  • Line 1523: "Loading projects..."
  • Line 1529: "Dokploy Project"
  • Line 1541: "Select a project"
  • Line 1544: "None (skip this service)"
  • Line 1557: "Application"
  • Line 1562: "No applications in this project"
  • Line 1575: "Select an application"

These should all be moved to the translation files and accessed via t().

Based on learnings, all user-facing text should use translation keys.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 49d1657 and ae91ac4.

📒 Files selected for processing (3)
  • apps/frontend/src/renderer/components/DokployDeployModal.tsx
  • apps/frontend/src/shared/i18n/locales/en/deploy.json
  • apps/frontend/src/shared/i18n/locales/fr/deploy.json
🧰 Additional context used
📓 Path-based instructions (3)
apps/frontend/src/shared/i18n/locales/**/*.json

📄 CodeRabbit inference engine (CLAUDE.md)

Add new translation keys to ALL language files (minimum: en/*.json and fr/*.json) in apps/frontend/src/shared/i18n/locales/

Files:

  • apps/frontend/src/shared/i18n/locales/en/deploy.json
  • apps/frontend/src/shared/i18n/locales/fr/deploy.json
apps/frontend/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/frontend/src/**/*.{ts,tsx}: Always use translation keys with useTranslation() for all user-facing text in React/TypeScript frontend components - use format namespace:section.key (e.g., navigation:items.githubPRs)
Never use hardcoded strings in JSX/TSX files for user-facing text - always reference translation keys from apps/frontend/src/shared/i18n/locales/

Files:

  • apps/frontend/src/renderer/components/DokployDeployModal.tsx
apps/frontend/**/*.{ts,tsx}

⚙️ CodeRabbit configuration file

apps/frontend/**/*.{ts,tsx}: Review React patterns and TypeScript type safety.
Check for proper state management and component composition.

Files:

  • apps/frontend/src/renderer/components/DokployDeployModal.tsx
🧠 Learnings (3)
📚 Learning: 2025-12-25T18:29:32.954Z
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-25T18:29:32.954Z
Learning: Applies to apps/frontend/src/shared/i18n/locales/**/*.json : Add new translation keys to ALL language files (minimum: `en/*.json` and `fr/*.json`) in `apps/frontend/src/shared/i18n/locales/`

Applied to files:

  • apps/frontend/src/shared/i18n/locales/en/deploy.json
  • apps/frontend/src/shared/i18n/locales/fr/deploy.json
  • apps/frontend/src/renderer/components/DokployDeployModal.tsx
📚 Learning: 2025-12-25T18:29:32.954Z
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-25T18:29:32.954Z
Learning: Applies to apps/frontend/src/**/*.{ts,tsx} : Never use hardcoded strings in JSX/TSX files for user-facing text - always reference translation keys from `apps/frontend/src/shared/i18n/locales/`

Applied to files:

  • apps/frontend/src/renderer/components/DokployDeployModal.tsx
📚 Learning: 2025-12-25T18:29:32.954Z
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-25T18:29:32.954Z
Learning: Applies to apps/frontend/src/**/*.{ts,tsx} : Always use translation keys with `useTranslation()` for all user-facing text in React/TypeScript frontend components - use format `namespace:section.key` (e.g., `navigation:items.githubPRs`)

Applied to files:

  • apps/frontend/src/renderer/components/DokployDeployModal.tsx
🔇 Additional comments (10)
apps/frontend/src/shared/i18n/locales/fr/deploy.json (1)

1-66: LGTM! French translations are complete and properly structured.

The French translation file correctly mirrors the English version's structure, with all keys present including the newly added "close" key at line 49. The translations are properly formatted and comprehensive, covering all deployment UI strings for the Dokploy integration.

Based on learnings, this follows the guideline to add new translation keys to all language files (minimum: en and fr).

apps/frontend/src/shared/i18n/locales/en/deploy.json (1)

1-66: LGTM! English translations are complete and well-structured.

The English translation file provides comprehensive coverage of the Dokploy deployment UI, with clear and professional text for all user-facing strings. The structure correctly matches the French translation file, ensuring consistency across locales.

Based on learnings, this properly implements the guideline to maintain translation keys in all language files.

apps/frontend/src/renderer/components/DokployDeployModal.tsx (8)

1-68: LGTM! Imports and type definitions are well-structured.

The imports are properly organized, including the necessary i18n hook (useTranslation), UI components, and type definitions. The interface definitions (DokployDeployModalProps, DeployStep, ServiceDeployConfig) are clear and appropriately typed for the component's functionality.


69-127: LGTM! Component initialization and state management are properly implemented.

The component correctly initializes the translation hook with the 'deploy' namespace and declares comprehensive state management for the complex deployment workflow. State organization is logical and covers all necessary aspects of the multi-step deployment process.


372-475: LGTM! Helper functions and handlers are well-implemented.

The helper functions for managing service configurations and the async handlers for resyncing and resetting deployments are properly structured with appropriate error handling.


543-595: LGTM! Computed values are efficiently implemented.

The useMemo hooks for enabledServices, configuredServices, servicesLeftToDeploy, isAllLinked, and isAllCreateNew provide efficient derived state with proper dependencies, improving rendering performance.


597-861: LGTM! API fetch functions are properly structured.

The callback functions for fetching servers, GitHub providers, and Dokploy projects are well-implemented with appropriate error handling and loading states. The nested parsing logic for extracting applications from all environments and resource types (lines 692-794) is thorough and handles all Dokploy application types correctly.

Note: Debug console.log statements in this section were already flagged in the earlier review comment.


863-1083: LGTM! Main deployment handler implements comprehensive workflow.

The handleDeploy function correctly orchestrates the multi-step deployment process: creating projects, configuring applications per service, setting up GitHub providers with service-specific paths, configuring domains, uploading environment variables, and optionally triggering deployment. Error handling is robust, with warnings logged for non-critical failures (domains, env vars) to prevent full deployment collapse.

Note: Debug console.log and hardcoded string issues in this section are flagged in separate comments.


1749-1758: LGTM! Dialog structure is properly implemented.

The main Dialog component is correctly structured with proper header, content rendering, and conditional footer based on the current step. The use of the translation hook for the title and description demonstrates correct i18n usage.


896-896: Hardcoded error string should use translation key.

Line 896 contains a hardcoded error message that should be moved to the translation files.

Add to translation files:

"modal": {
  "error": {
    ...
    "noEnvironment": "No environment found in project"
  }
}

Then update:

-      throw new Error('No environment found in project');
+      throw new Error(t('modal.error.noEnvironment'));

Based on learnings, all user-facing text should use translation keys.

⛔ Skipped due to learnings
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-25T18:29:32.954Z
Learning: Applies to apps/frontend/src/**/*.{ts,tsx} : Never use hardcoded strings in JSX/TSX files for user-facing text - always reference translation keys from `apps/frontend/src/shared/i18n/locales/`
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-25T18:29:32.954Z
Learning: Applies to apps/frontend/src/**/*.{ts,tsx} : Always use translation keys with `useTranslation()` for all user-facing text in React/TypeScript frontend components - use format `namespace:section.key` (e.g., `navigation:items.githubPRs`)

Comment on lines +185 to +188
setRepoDetectionError('Could not parse GitHub repository URL');
}
} else {
setRepoDetectionError('No GitHub remote found in project');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hardcoded error strings should use translation keys.

Lines 185 and 188 contain hardcoded error messages that should be moved to the translation files for consistency with the i18n guidelines.

🔎 Suggested fix

Add these keys to en/deploy.json and fr/deploy.json:

"modal": {
  ...
  "error": {
    ...
    "parseRepo": "Could not parse GitHub repository URL",
    "noGitRemote": "No GitHub remote found in project"
  }
}

Then update the code:

        if (parsed) {
          setDetectedRepo({
            owner: parsed.owner,
            name: parsed.name,
            fullName: `${parsed.owner}/${parsed.name}`
          });
        } else {
-         setRepoDetectionError('Could not parse GitHub repository URL');
+         setRepoDetectionError(t('modal.error.parseRepo'));
        }
      } else {
-       setRepoDetectionError('No GitHub remote found in project');
+       setRepoDetectionError(t('modal.error.noGitRemote'));
      }

Based on learnings, all user-facing text should use translation keys from the locale files.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/frontend/src/renderer/components/DokployDeployModal.tsx around lines 185
to 188, replace the two hardcoded error strings with translation keys and add
those keys to the locale files; create entries "modal.error.parseRepo" = "Could
not parse GitHub repository URL" and "modal.error.noGitRemote" = "No GitHub
remote found in project" in en/deploy.json (and corresponding fr/deploy.json),
then update the component to call the i18n translation function (e.g.
t('modal.error.parseRepo') and t('modal.error.noGitRemote')) when setting repo
detection errors.

}

if (deployedServices.length === 0) {
setError('Please link at least one service to an application');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hardcoded error string should use translation key.

Line 509 contains a hardcoded error message that should be moved to the translation files.

Add to translation files:

"modal": {
  "error": {
    ...
    "noServicesLinked": "Please link at least one service to an application"
  }
}

Then update:

-      setError('Please link at least one service to an application');
+      setError(t('modal.error.noServicesLinked'));

Based on learnings, all user-facing text should use translation keys.

setDeployedApps(deployedServices.map(s => s.serviceName));
} catch (err) {
console.error('Failed to save linked deployment:', err);
setError('Failed to save deployment configuration');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hardcoded error string should use translation key.

Line 538 contains a hardcoded error message that should be moved to the translation files.

Add to translation files:

"modal": {
  "error": {
    ...
    "saveDeployment": "Failed to save deployment configuration"
  }
}

Then update:

-      setError('Failed to save deployment configuration');
+      setError(t('modal.error.saveDeployment'));

Based on learnings, all user-facing text should use translation keys.

🤖 Prompt for AI Agents
In apps/frontend/src/renderer/components/DokployDeployModal.tsx around line 538
the error message is hardcoded; add the key "modal.error.saveDeployment":
"Failed to save deployment configuration" to the translation JSON files, then
replace the literal string with the translation lookup (e.g. use the existing
i18n/t hook or import/useTranslation and call t('modal.error.saveDeployment'))
and ensure the translation hook is available in this component so the translated
string is used instead of the hardcoded text.

Comment on lines +1093 to +1102
const getDeployBlocker = (): string | null => {
if (!selectedAccountId) return 'Select a Dokploy account';
if (!projectName.trim()) return 'Enter a project name';
if (!selectedGitHubProviderId) return 'No GitHub provider configured in Dokploy';
if (!detectedRepo) return 'Could not detect repository';
if (enabledServices.length === 0) return 'Enable at least one service';
const missingConfig = enabledServices.find(s => !serviceConfigs[s.key]?.appName || !serviceConfigs[s.key]?.branch);
if (missingConfig) return `Configure app name and branch for ${missingConfig.name}`;
return null;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hardcoded strings in helper function should use translation keys.

The getDeployBlocker function contains multiple hardcoded error/instruction messages that should be moved to translation files for consistency with i18n guidelines.

🔎 Suggested fix

Add these keys to translation files:

"modal": {
  "blockers": {
    "selectAccount": "Select a Dokploy account",
    "enterProjectName": "Enter a project name",
    "noGitHubProvider": "No GitHub provider configured in Dokploy",
    "noRepo": "Could not detect repository",
    "enableService": "Enable at least one service",
    "configureService": "Configure app name and branch for {{serviceName}}"
  }
}

Then refactor the function to use translations with interpolation.

Based on learnings, all user-facing text should use translation keys.

🤖 Prompt for AI Agents
In apps/frontend/src/renderer/components/DokployDeployModal.tsx around lines
1093 to 1102, move all hardcoded user-facing strings in getDeployBlocker into
translation keys and refactor to use the i18n translator: add the suggested keys
(modal.blockers.selectAccount, enterProjectName, noGitHubProvider, noRepo,
enableService, configureService) to the locale files, replace each literal
return with t('modal.blockers.xxx') and for the configureService case use
interpolation t('modal.blockers.configureService', { serviceName:
missingConfig.name }), keeping the existing logic (trim checks, enabledServices
lookup) intact.

Comment on lines +1454 to +1459
Loading project services...
</div>
) : services.length === 0 ? (
<div className="text-sm text-muted-foreground py-2">
No services detected in this project.
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hardcoded loading and empty state messages should use translation keys.

Lines 1454 and 1458 contain hardcoded messages that should be moved to translation files.

Add to translation files:

"modal": {
  "loadingServices": "Loading project services...",
  "noServices": "No services detected in this project."
}

Then update:

-                    Loading project services...
+                    {t('modal.loadingServices')}
...
-                    No services detected in this project.
+                    {t('modal.noServices')}

Based on learnings, all user-facing text should use translation keys.

🤖 Prompt for AI Agents
In apps/frontend/src/renderer/components/DokployDeployModal.tsx around lines
1454 to 1459, replace the two hardcoded UI strings ("Loading project
services..." and "No services detected in this project.") with translation keys
(e.g. t('modal.loadingServices') and t('modal.noServices')); also add the
corresponding keys to the project's translation files (e.g. locales/en.json and
other locale files) with "modal": { "loadingServices": "Loading project
services...", "noServices": "No services detected in this project." } so all
user-facing text uses the i18n system.

Comment on lines +1762 to +1810
{step === 'configure' && (
<DialogFooter className="flex-col sm:flex-row gap-2">
{existingDeployment && (
<Button variant="ghost" size="sm" className="mr-auto" onClick={() => setStep('resync')}>
<RefreshCw className="h-3 w-3 mr-1" />
Manage Existing Deployment
</Button>
)}

{/* Show services left to deploy */}
{servicesLeftToDeploy.length > 0 && !existingDeployment && (
<p className="text-xs text-muted-foreground mr-auto">
{servicesLeftToDeploy.length} service{servicesLeftToDeploy.length !== 1 ? 's' : ''} left to configure
</p>
)}

{!canDeploy && getDeployBlocker() && !existingDeployment && servicesLeftToDeploy.length === 0 && (
<p className="text-xs text-destructive mr-auto">
{getDeployBlocker()}
</p>
)}

<div className="flex gap-2">
<Button variant="outline" onClick={() => onOpenChange(false)}>
{t('modal.cancel')}
</Button>

{isAllLinked ? (
<Button
onClick={handleLinkExisting}
disabled={configuredServices.length === 0}
>
Link Applications ({configuredServices.length})
</Button>
) : isAllCreateNew ? (
<Button onClick={handleDeploy} disabled={!canDeploy}>
<Rocket className="mr-2 h-4 w-4" />
{existingDeployment ? 'Create New Deployment' : 'Configure Apps'}
</Button>
) : (
<Button
onClick={configuredServices.some(s => serviceConfigs[s.key]?.linkMode) ? handleLinkExisting : handleDeploy}
disabled={configuredServices.length === 0}
>
{configuredServices.some(s => serviceConfigs[s.key]?.linkMode) ? 'Link' : 'Configure'} ({configuredServices.length})
</Button>
)}
</div>
</DialogFooter>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Dialog footer contains hardcoded strings.

The configure step footer contains multiple hardcoded strings:

  • Line 1767: "Manage Existing Deployment"
  • Line 1774: Service count message with pluralization
  • Lines 1780: Deploy blocker message
  • Lines 1794, 1799, 1806: Button labels and counts

These should all be moved to translation files with proper support for pluralization where needed (e.g., using i18next's plural forms for "service" vs "services").

Based on learnings, all user-facing text should use translation keys.

Comment on lines +1821 to +1834
{step === 'resync' && (
<DialogFooter className="flex-col sm:flex-row gap-2">
<div className="flex gap-2 mr-auto">
<Button variant="ghost" size="sm" onClick={() => setStep('configure')}>
New Deployment
</Button>
<Button variant="ghost" size="sm" className="text-destructive hover:text-destructive" onClick={handleResetDeployment}>
Reset Config
</Button>
</div>
<Button variant="outline" onClick={() => onOpenChange(false)}>
{t('modal.close')}
</Button>
</DialogFooter>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Resync footer contains hardcoded strings.

The resync step footer contains hardcoded button labels:

  • Line 1825: "New Deployment"
  • Line 1828: "Reset Config"

Add to translation files:

"modal": {
  "resync": {
    "newDeployment": "New Deployment",
    "resetConfig": "Reset Config"
  }
}

Then update the JSX to use t('modal.resync.newDeployment') and t('modal.resync.resetConfig').

Based on learnings, all user-facing text should use translation keys.

🤖 Prompt for AI Agents
In apps/frontend/src/renderer/components/DokployDeployModal.tsx around lines
1821 to 1834 the resync footer uses hardcoded button labels ("New Deployment"
and "Reset Config"); update the JSX to use translation keys
t('modal.resync.newDeployment') and t('modal.resync.resetConfig') instead of the
literal strings, and add the corresponding entries to your translation JSON
files under "modal.resync": { "newDeployment": "New Deployment", "resetConfig":
"Reset Config" } so all user-facing text is localized.

@MikeeBuilds MikeeBuilds added feature New feature or request area/fullstack This is Frontend + Backend labels Dec 26, 2025
@AndyMik90 AndyMik90 self-assigned this Dec 28, 2025
@MikeeBuilds MikeeBuilds added the size/XL Extra large (1000+ lines) label Dec 28, 2025
@AlexMadera AlexMadera self-assigned this Dec 29, 2025
@AlexMadera AlexMadera added this to the Release 2.8 milestone Dec 30, 2025
@CLAassistant
Copy link

CLAassistant commented Dec 30, 2025

CLA assistant check
All committers have signed the CLA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/fullstack This is Frontend + Backend feature New feature or request ❌ MERGE CONFLICTS size/XL Extra large (1000+ lines)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants