diff --git a/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/home/page.tsx b/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/home/page.tsx
index 8f2dd8b..ace6b53 100644
--- a/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/home/page.tsx
+++ b/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/home/page.tsx
@@ -63,14 +63,11 @@ export default async function Page() {
)
}
- // NOTE: We're keeping the token approach for now, but aware it's not working
- const authToken = githubAccount.accessToken;
-
// Try using GitHub token
let octokit;
try {
octokit = new Octokit({
- auth: authToken || process.env.GITHUB_TOKEN
+ auth: process.env.GITHUB_TOKEN
});
// Test with a simple request
@@ -145,7 +142,7 @@ export default async function Page() {
Failed to access GitHub API
- {authError.message}
+ {authError instanceof Error ? authError.message : 'An error occurred'}
This issue may be related to how Clerk is managing the GitHub token.
@@ -182,7 +179,7 @@ export default async function Page() {
Failed to authenticate with GitHub
- {error.message}
+ {error instanceof Error ? error.message : 'Unknown error occurred'}
diff --git a/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/(deployments)/dep/page.tsx b/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/(deployments)/dep/page.tsx
index 4aeb380..384a9cc 100644
--- a/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/(deployments)/dep/page.tsx
+++ b/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/(deployments)/dep/page.tsx
@@ -30,7 +30,7 @@ export default function DeploymentsPage() {
// State for deployment logs modal
const [isLogsOpen, setIsLogsOpen] = useState(false);
- const [selectedDeploymentId, setSelectedDeploymentId] = useState(null);
+ const [, setSelectedDeploymentId] = useState(null);
const [deploymentLogs, setDeploymentLogs] = useState('');
// Create a default deployment
@@ -48,19 +48,6 @@ export default function DeploymentsPage() {
}
};
- const secondDeployment: Deployment = {
- id: 'previous',
- branch: 'feature/new-ui',
- status: 'COMPLETED',
- isCurrent: false,
- createdAt: Date.now() - 3 * 24 * 60 * 60 * 1000, // 3 days ago
- applicationDeploymentRecordData: {
- url: repoData ? `https://dev.${repoData.name.toLowerCase()}.example.com` : 'https://dev.example.com'
- },
- createdBy: {
- name: repoData?.owner?.login || 'username'
- }
- };
// Initialize with empty data for testing the empty state
// Comment this out to see the mock deployments
@@ -79,7 +66,9 @@ export default function DeploymentsPage() {
{
id: '1',
name: repoData ? `${repoData.name.toLowerCase()}.example.com` : 'example.com',
+ branch: 'main', // Add missing branch
status: 'ACTIVE',
+ createdAt: Date.now(), // Add missing createdAt
isCustom: false
}
];
diff --git a/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/(settings)/set/(environment-variables)/env/EnvVarsPage.tsx b/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/(settings)/set/(environment-variables)/env/EnvVarsPage.tsx
index 3416302..9c09475 100644
--- a/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/(settings)/set/(environment-variables)/env/EnvVarsPage.tsx
+++ b/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/(settings)/set/(environment-variables)/env/EnvVarsPage.tsx
@@ -3,12 +3,7 @@
import { useState } from "react";
import { LoadingOverlay } from "@/components/foundation/loading/loading-overlay";
import { PlusIcon, ChevronDownIcon, ChevronUpIcon, PencilIcon, TrashIcon } from "lucide-react";
-
-interface EnvVarItem {
- key: string;
- value: string;
- isEditing?: boolean;
-}
+import type { EnvVarItem } from '@/types'
interface EnvGroupProps {
title: string;
@@ -204,7 +199,7 @@ export default function EnvVarsPage() {
/>
diff --git a/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/(settings)/set/ProjectSettingsPage.tsx b/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/(settings)/set/ProjectSettingsPage.tsx
index 8b78ec9..f2753ba 100644
--- a/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/(settings)/set/ProjectSettingsPage.tsx
+++ b/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/(settings)/set/ProjectSettingsPage.tsx
@@ -278,11 +278,9 @@ export default function ProjectSettingsPage({ project, onProjectUpdated }: Proje
Select account
setSelectedAccount(value)}
- className="w-full mt-1"
+ onChange={(option) => setSelectedAccount(option.value)}
/>
diff --git a/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/deployments/page.tsx b/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/deployments/page.tsx
deleted file mode 100644
index 1c65931..0000000
--- a/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/deployments/page.tsx
+++ /dev/null
@@ -1,159 +0,0 @@
-'use client'
-import { useEffect, useState } from 'react'
-import { PageWrapper } from '@/components/foundation'
-import { FixedProjectCard } from '@/components/projects/project/ProjectCard/FixedProjectCard'
-import { Button } from '@workspace/ui/components/button'
-import { Shapes } from 'lucide-react'
-import { useAuth } from '@clerk/nextjs'
-
-interface Deployment {
- id: string
- name: string
- repositoryId: string
- status: 'running' | 'complete' | 'failed'
- url?: string
- branch: string
- createdAt: string
- createdBy: {
- name: string
- }
-}
-
-export default function ProjectsPage() {
- const [deployments, setDeployments] = useState([])
- const [isLoading, setIsLoading] = useState(true)
- const [error, setError] = useState(null)
-
- const { isLoaded: isAuthLoaded, userId } = useAuth()
-
- useEffect(() => {
- async function fetchDeployments() {
- if (!isAuthLoaded) {
- return;
- }
-
- setIsLoading(true);
-
- try {
- if (!userId) {
- setError('Not authenticated');
- return;
- }
-
- // In a real implementation, you would query your GraphQL backend
- // For now, we'll mock some deployments
- const mockDeployments: Deployment[] = [
- {
- id: 'dep_abc123',
- name: 'My Project',
- repositoryId: '123456',
- status: 'complete',
- url: 'https://my-project.example.com',
- branch: 'main',
- createdAt: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(),
- createdBy: {
- name: 'John Doe'
- }
- },
- {
- id: 'dep_def456',
- name: 'Another Project',
- repositoryId: '789012',
- status: 'running',
- branch: 'develop',
- createdAt: new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString(),
- createdBy: {
- name: 'Jane Smith'
- }
- }
- ];
-
- setDeployments(mockDeployments);
- } catch (err) {
- console.error('Error fetching deployments:', err)
- setError('Failed to fetch deployments')
- } finally {
- setIsLoading(false)
- }
- }
-
- fetchDeployments()
- }, [isAuthLoaded, userId]);
-
- return (
-
- {isLoading ? (
-
- ) : error ? (
-
-
-
Error: {error}
-
- There was an error loading your deployments.
-
-
-
- ) : deployments.length === 0 ? (
-
-
-
Deploy your first app
-
- You haven't deployed any projects yet. Start by importing a repository from your GitHub account.
-
-
-
- ) : (
-
-
- {deployments.map((deployment) => (
-
- ))}
-
-
- )}
-
- )
-}
\ No newline at end of file
diff --git a/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/page.tsx b/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/page.tsx
index 52895dd..f49fd76 100644
--- a/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/page.tsx
+++ b/apps/deploy-fe/src/app/(web3-authenticated)/(dashboard)/projects/[provider]/ps/[id]/page.tsx
@@ -42,6 +42,7 @@ export default function ProjectOverviewPage() {
const [deployments, setDeployments] = useState([]);
const [filteredDeployments, setFilteredDeployments] = useState([]);
const [isLogsOpen, setIsLogsOpen] = useState(false);
+ const [, setSelectedDeploymentId] = useState(null);
const [deploymentLogs, setDeploymentLogs] = useState('');
// Load project data
@@ -138,18 +139,25 @@ export default function ProjectOverviewPage() {
};
// Handle deployment logs
- const handleViewLogs = () => {
- const mockLogs = `[2025-02-12 10:03:12] INFO Starting deployment process for service: ${project?.name}
-[2025-02-12 10:03:14] INFO Fetching latest commit from main branch (commit: a1b2c3d)
-[2025-02-12 10:03:15] INFO Building Docker image: registry.company.com/${project?.name}:latest
-[2025-02-12 10:03:26] INFO Running security scan on built image
-[2025-02-12 10:03:30] INFO Pushing image to container registry
-[2025-02-12 10:03:35] INFO Updating deployment configuration
-[2025-02-12 10:03:40] INFO Scaling down old pods
-[2025-02-12 10:03:42] INFO Scaling up new pods
-[2025-02-12 10:03:50] INFO Running health checks on new pods
-[2025-02-12 10:03:55] INFO Deployment completed successfully
-[2025-02-12 10:03:56] INFO Service is now live at ${currentDeployment?.applicationDeploymentRecordData?.url}`;
+ const handleViewLogs = (deploymentId: string) => {
+ setSelectedDeploymentId(deploymentId);
+
+ // Mock logs data
+ const mockLogs = `[2025-02-12 10:03:12] INFO Starting deployment process for service: api-gateway
+ [2025-02-12 10:03:14] INFO Fetching latest commit from main branch (commit: a1b2c3d)
+ [2025-02-12 10:03:15] INFO Building Docker image: registry.company.com/api-gateway:latest
+ [2025-02-12 10:03:26] INFO Running security scan on built image
+ [2025-02-12 10:03:27] WARNING Medium severity vulnerability detected in package 'openssl'
+ [2025-02-12 10:03:30] INFO Pushing image to container registry
+ [2025-02-12 10:03:35] INFO Updating Kubernetes deployment
+ [2025-02-12 10:03:40] INFO Scaling down old pods
+ [2025-02-12 10:03:42] INFO Scaling up new pods
+ [2025-02-12 10:03:50] INFO Running health checks on new pods
+ [2025-02-12 10:03:52] ERROR Pod 'api-gateway-7df9bbb500-tx2k4' failed readiness probe (502 Bad Gateway)
+ [2025-02-12 10:03:55] INFO Retrying deployment with previous stable image
+ [2025-02-12 10:04:03] INFO Rolling back to registry.company.com/api-gateway:previous
+ [2025-02-12 10:04:10] INFO Deployment rolled back successfully
+ [2025-02-12 10:04:11] ERROR Deployment failed, please review logs and fix errors`;
setDeploymentLogs(mockLogs);
setIsLogsOpen(true);
diff --git a/apps/deploy-fe/src/app/test-connection/page.tsx b/apps/deploy-fe/src/app/test-connection/page.tsx
index f4ff77d..9c500d9 100644
--- a/apps/deploy-fe/src/app/test-connection/page.tsx
+++ b/apps/deploy-fe/src/app/test-connection/page.tsx
@@ -156,7 +156,7 @@ export default function TestConnectionPage() {
// Set default org if available
if (orgsData.organizations && orgsData.organizations.length > 0) {
- setSelectedOrg(orgsData.organizations[0].slug)
+ setSelectedOrg(orgsData.organizations[0]!.slug)
}
} catch (error) {
console.error('Error fetching organizations:', error)
@@ -230,7 +230,7 @@ const fetchDeployers = async () => {
// Auto-select first deployer if available
if (deployersData.deployers && deployersData.deployers.length > 0) {
- setSelectedDeployer(deployersData.deployers[0].deployerLrn)
+ setSelectedDeployer(deployersData.deployers[0]!.deployerLrn)
}
} catch (error) {
console.error('Error fetching deployers:', error)
diff --git a/apps/deploy-fe/src/components/AuthTest.tsx b/apps/deploy-fe/src/components/AuthTest.tsx
index ddedeea..91c92c6 100644
--- a/apps/deploy-fe/src/components/AuthTest.tsx
+++ b/apps/deploy-fe/src/components/AuthTest.tsx
@@ -194,7 +194,7 @@ Issued At: ${issuedAt}`
setDebugInfo(prev => `${prev}\nValidation response: ${JSON.stringify(responseData)}`)
// If successful, we're done
- if (response.ok && responseData.success) {
+ if (response.ok && (responseData as any).success) {
console.log('Authentication successful!')
setDebugInfo(prev => `${prev}\nAuthentication successful!`)
diff --git a/apps/deploy-fe/src/components/foundation/page-header/PageHeader.tsx b/apps/deploy-fe/src/components/foundation/page-header/PageHeader.tsx
index 7f8e998..d145f81 100644
--- a/apps/deploy-fe/src/components/foundation/page-header/PageHeader.tsx
+++ b/apps/deploy-fe/src/components/foundation/page-header/PageHeader.tsx
@@ -8,9 +8,10 @@ import {
DropdownMenuTrigger
} from '@workspace/ui/components/dropdown-menu'
import { cn } from '@workspace/ui/lib/utils'
-import { MoreVertical } from 'lucide-react'
+import { MoreVertical, ExternalLink } from 'lucide-react'
import Link from 'next/link'
import type { ReactNode } from 'react'
+import type { LucideIcon } from 'lucide-react'
/**
* Configuration for header action buttons/links
@@ -19,6 +20,7 @@ import type { ReactNode } from 'react'
* - Use onClick for JS actions OR href for navigation (not both)
* - Multiple visual styles via variant prop
* - Optional primary emphasis for main call-to-action
+ * - Optional icon support
*/
export interface PageAction {
/**
@@ -27,6 +29,23 @@ export interface PageAction {
*/
label: string
+ /**
+ * Optional icon for the action
+ * @remarks
+ * - Can be string identifier or Lucide icon component
+ * - Common values: 'external-link', 'plus', 'edit', etc.
+ * - Displayed alongside the label
+ */
+ icon?: string | LucideIcon
+
+ /**
+ * Whether this is an external link
+ * @remarks
+ * - When true, opens in new tab/window
+ * - Automatically adds external link icon if no icon specified
+ */
+ external?: boolean
+
/**
* Visual style variant for the button
* @remarks
@@ -112,6 +131,14 @@ export interface PageHeaderProps {
*/
subtitle?: string | ReactNode
+ /**
+ * Additional description text
+ * @remarks
+ * - Displayed below subtitle
+ * - Useful for longer explanatory text
+ */
+ description?: string
+
/**
* Array of action buttons/links
* @remarks
@@ -119,6 +146,7 @@ export interface PageHeaderProps {
* - Mobile: Primary actions shown, secondary in dropdown
* - Actions can be buttons (onClick) or links (href)
* - Support multiple visual styles via variant prop
+ * - Support icons and external links
*
* @see {@link PageAction} for detailed action configuration
*
@@ -128,12 +156,15 @@ export interface PageHeaderProps {
* {
* label: "Create New",
* isPrimary: true,
+ * icon: "plus",
* onClick: () => setOpen(true)
* },
* {
* label: "View All",
* href: "/items",
- * variant: "outline"
+ * variant: "outline",
+ * icon: "external-link",
+ * external: true
* }
* ]}
* ```
@@ -152,102 +183,11 @@ export interface PageHeaderProps {
/**
* A responsive page header component with title, subtitle, and actions.
- *
- * @description
- * PageHeader provides a consistent header structure with:
- * - Prominent title as h1
- * - Optional subtitle or custom component
- * - Configurable action buttons/links
- * - Responsive layout with mobile optimization
- * - Customizable styling
- *
- * @keywords header, page-title, action-buttons, responsive-header, foundation-component
- * @category Layout
- * @scope Foundation
- *
- * @usage
- * Common patterns:
- *
- * Basic title only:
- * ```tsx
- *
- * ```
- *
- * With subtitle and primary action:
- * ```tsx
- *
- * ```
- *
- * With search component and multiple actions:
- * ```tsx
- * }
- * actions={[
- * { label: "Invite", isPrimary: true, onClick: handleInvite },
- * { label: "Export", variant: "outline", onClick: handleExport },
- * { label: "Settings", href: "/team/settings", variant: "ghost" }
- * ]}
- * />
- * ```
- *
- * With navigation actions:
- * ```tsx
- *
- * ```
- *
- * @example
- * ```tsx
- * console.log("clicked")
- * }
- * ]}
- * className="mb-8"
- * />
- * ```
- *
- * @related {@link PageWrapper} - Often used together for page layout
- * @related {@link Button} - Used for rendering actions
- * @composition Uses {@link DropdownMenu} for mobile action menu
- *
- * @cssUtilities
- * - flex-col/flex-row: Responsive layout
- * - gap-6/gap-2: Consistent spacing
- * - text-2xl/text-[30px]: Responsive typography
- * - text-foreground/text-muted-foreground: Text hierarchy
- *
- * @accessibility
- * - Uses semantic h1 for title
- * - Maintains text contrast ratios
- * - Dropdown menu is keyboard navigable
- * - Preserves action button/link semantics
- *
- * @performance
- * - Conditional rendering of subtitle and actions
- * - Mobile-first CSS with responsive modifiers
- * - Efficient action rendering with key prop
*/
export default function PageHeader({
title,
subtitle,
+ description,
actions = [],
className
}: PageHeaderProps) {
@@ -255,21 +195,51 @@ export default function PageHeader({
const primaryActions = actions.filter((action) => action.isPrimary)
const secondaryActions = actions.filter((action) => !action.isPrimary)
+ // Get icon component from string or return the component directly
+ const getIconComponent = (icon: string | LucideIcon | undefined) => {
+ if (!icon) return null
+
+ if (typeof icon === 'string') {
+ switch (icon) {
+ case 'external-link':
+ return ExternalLink
+ default:
+ return ExternalLink // fallback
+ }
+ }
+
+ return icon
+ }
+
// Render an action (either as button or link)
const renderAction = (action: PageAction, key: string) => {
const variant = action.variant || (action.isPrimary ? 'default' : 'outline')
+ const IconComponent = getIconComponent(action.icon || (action.external ? 'external-link' : undefined))
+
+ const content = (
+ <>
+ {action.label}
+ {IconComponent && }
+ >
+ )
if (action.href) {
return (
)
}
return (
)
}
@@ -292,6 +262,11 @@ export default function PageHeader({
)}
)}
+ {description && (
+
+ {description}
+
+ )}
{actions.length > 0 && (
@@ -320,7 +295,16 @@ export default function PageHeader({
{secondaryActions.map((action) =>
action.href ? (
- {action.label}
+
+ {action.label}
+ {getIconComponent(action.icon || (action.external ? 'external-link' : undefined)) && (
+
+ )}
+
) : (
)
-}
+}
\ No newline at end of file
diff --git a/apps/deploy-fe/src/components/foundation/wallet-session-id/WalletSessionId.tsx b/apps/deploy-fe/src/components/foundation/wallet-session-id/WalletSessionId.tsx
index f3469ec..776b8ff 100644
--- a/apps/deploy-fe/src/components/foundation/wallet-session-id/WalletSessionId.tsx
+++ b/apps/deploy-fe/src/components/foundation/wallet-session-id/WalletSessionId.tsx
@@ -30,7 +30,7 @@
'use client'
import { useState } from 'react'
-import { useWallet } from '@/context/WalletContextProvider'
+import { useWallet } from '@/context/WalletContext'
import { Button } from '@workspace/ui/components/button'
import {
DropdownMenu,
diff --git a/apps/deploy-fe/src/components/onboarding/OnboardingDialog.tsx b/apps/deploy-fe/src/components/onboarding/OnboardingDialog.tsx
index 3806f82..7c5d4e6 100644
--- a/apps/deploy-fe/src/components/onboarding/OnboardingDialog.tsx
+++ b/apps/deploy-fe/src/components/onboarding/OnboardingDialog.tsx
@@ -269,7 +269,7 @@ const OnboardingDialog: React.FC = ({
defaultOpen = false,
onClose
}) => {
- const { nextStep, setFormData, formData, currentStep } = useOnboarding()
+ const { nextStep, setFormData, formData, currentStep, previousStep } = useOnboarding()
const [selectedRepo, setSelectedRepo] = useState(formData.githubRepo || '')
const [isImportMode, setIsImportMode] = useState(true)
const [isOpen, setIsOpen] = useState(defaultOpen)
@@ -489,7 +489,7 @@ const OnboardingDialog: React.FC = ({
@@ -527,7 +527,7 @@ const OnboardingDialog: React.FC = ({
diff --git a/apps/deploy-fe/src/components/onboarding/configure-step/configure-step.tsx b/apps/deploy-fe/src/components/onboarding/configure-step/configure-step.tsx
index 73a015a..253c0b9 100644
--- a/apps/deploy-fe/src/components/onboarding/configure-step/configure-step.tsx
+++ b/apps/deploy-fe/src/components/onboarding/configure-step/configure-step.tsx
@@ -16,6 +16,7 @@ import { Alert, AlertDescription } from '@workspace/ui/components/alert'
import { Card, CardContent, CardHeader, CardTitle } from '@workspace/ui/components/card'
import { Badge } from '@workspace/ui/components/badge'
import { toast } from 'sonner'
+import { adaptDeployers } from '../../../utils/typeAdapters';
interface Deployer {
deployerLrn: string
@@ -93,11 +94,11 @@ export function ConfigureStep() {
setIsLoadingDeployers(true)
const deployersData = await gqlClient.getDeployers()
console.log('Available deployers:', deployersData)
- setDeployers(deployersData.deployers || [])
+ setDeployers(adaptDeployers(deployersData.deployers || []));
// Auto-select first deployer if available and none selected
if (deployersData.deployers && deployersData.deployers.length > 0 && !selectedLrn) {
- setSelectedLrn(deployersData.deployers[0].deployerLrn)
+ setSelectedLrn(deployersData.deployers[0]!.deployerLrn)
}
} catch (error) {
console.error('Error fetching deployers:', error)
@@ -117,7 +118,7 @@ export function ConfigureStep() {
// Auto-select first organization if available and none selected
if (orgsData.organizations && orgsData.organizations.length > 0 && !selectedOrg) {
- setSelectedOrg(orgsData.organizations[0].slug)
+ setSelectedOrg(orgsData.organizations[0]!.slug)
}
} catch (error) {
console.error('Error fetching organizations:', error)
@@ -142,27 +143,32 @@ export function ConfigureStep() {
// Update environment variable
const updateEnvVar = (index: number, field: 'key' | 'value', value: string) => {
const newEnvVars = [...envVars]
- newEnvVars[index][field] = value
+ if (newEnvVars[index]) {
+ newEnvVars[index][field] = value
+ }
setEnvVars(newEnvVars)
}
// Toggle environment for variable
const toggleEnvironment = (index: number, environment: string) => {
const newEnvVars = [...envVars]
- const currentEnvs = newEnvVars[index].environments
+ if (newEnvVars[index]?.environments) {
+ const currentEnvs = newEnvVars[index].environments
+
- if (currentEnvs.includes(environment)) {
- newEnvVars[index].environments = currentEnvs.filter(env => env !== environment)
- } else {
- newEnvVars[index].environments = [...currentEnvs, environment]
+ if (currentEnvs.includes(environment)) {
+ newEnvVars[index].environments = currentEnvs.filter(env => env !== environment)
+ } else {
+ newEnvVars[index].environments = [...currentEnvs, environment]
+ }
+
+ // Ensure at least one environment is selected
+ if (newEnvVars[index].environments.length === 0) {
+ newEnvVars[index].environments = ['Production']
+ }
+
+ setEnvVars(newEnvVars)
}
-
- // Ensure at least one environment is selected
- if (newEnvVars[index].environments.length === 0) {
- newEnvVars[index].environments = ['Production']
- }
-
- setEnvVars(newEnvVars)
}
// Toggle deployment option
diff --git a/apps/deploy-fe/src/components/onboarding/connect-step/connect-step.tsx b/apps/deploy-fe/src/components/onboarding/connect-step/connect-step.tsx
index 2d641cb..598251b 100644
--- a/apps/deploy-fe/src/components/onboarding/connect-step/connect-step.tsx
+++ b/apps/deploy-fe/src/components/onboarding/connect-step/connect-step.tsx
@@ -17,6 +17,8 @@ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@workspace/
import { toast } from 'sonner'
import { GitHubBackendAuth } from '@/components/GitHubBackendAuth'
import { AVAILABLE_TEMPLATES, type TemplateDetail } from '@/constants/templates'
+import { Template } from '@/types/onboarding'
+import { adaptOptionalTemplate } from '@/utils/typeAdapters'
interface Repository {
id: string | number
@@ -32,8 +34,8 @@ export function ConnectStep() {
// Repository vs Template selection
const [selectedRepo, setSelectedRepo] = useState(formData.githubRepo || '')
- const [selectedTemplate, setSelectedTemplate] = useState(
- formData.template || undefined
+ const [selectedTemplate, setSelectedTemplate] = useState(
+ adaptOptionalTemplate(formData.template)
)
const [projectName, setProjectName] = useState(formData.projectName || '')
const [isImportMode, setIsImportMode] = useState(true)
@@ -44,8 +46,6 @@ export function ConnectStep() {
// Auth status hook
const {
clerk,
- wallet,
- backend,
isFullyAuthenticated,
isReady,
missing,
@@ -169,7 +169,7 @@ export function ConnectStep() {
setFormData({
deploymentMode: isImportMode ? 'repository' : 'template',
githubRepo: isImportMode ? selectedRepo : '',
- template: !isImportMode ? selectedTemplate : undefined,
+ template: !isImportMode ? (selectedTemplate as Template) : undefined,
projectName: finalProjectName
})
@@ -209,7 +209,7 @@ export function ConnectStep() {
- {clerk.user?.externalAccounts?.find(acc => acc.provider === 'github')?.username || 'git-account'}
+ {clerk.user?.externalAccounts?.find((acc: any) => acc.provider === 'github')?.username || 'git-account'}
diff --git a/apps/deploy-fe/src/components/onboarding/connect-step/disabled_connect-step.tsx b/apps/deploy-fe/src/components/onboarding/connect-step/disabled_connect-step.tsx
index 8f42204..3019ed4 100644
--- a/apps/deploy-fe/src/components/onboarding/connect-step/disabled_connect-step.tsx
+++ b/apps/deploy-fe/src/components/onboarding/connect-step/disabled_connect-step.tsx
@@ -90,6 +90,7 @@ export const ConnectStep = () => {
}
/>
{/* Git account selector */}
diff --git a/apps/deploy-fe/src/components/onboarding/deploy-step/deploy-step.tsx b/apps/deploy-fe/src/components/onboarding/deploy-step/deploy-step.tsx
index 22c8a1a..1b3cf6b 100644
--- a/apps/deploy-fe/src/components/onboarding/deploy-step/deploy-step.tsx
+++ b/apps/deploy-fe/src/components/onboarding/deploy-step/deploy-step.tsx
@@ -5,7 +5,6 @@ import { useState, useEffect } from 'react'
import { useTheme } from 'next-themes'
import { Github, Loader2, AlertTriangle, CheckCircle2 } from 'lucide-react'
import { useOnboarding } from '@/components/onboarding/useOnboarding'
-import { useGQLClient } from '@/context'
import { useWallet } from '@/context/WalletContext'
import { useDeployment } from '@/hooks/useDeployment'
import { useTemplateDeployment } from '@/hooks/useTemplate'
@@ -117,16 +116,17 @@ export function DeployStep() {
}
const config = {
- template: formData.template,
+ template: {
+ ...formData.template,
+ id: formData.template?.id || '',
+ icon: formData.template?.icon || ''
+ },
projectName: formData.projectName,
organizationSlug: formData.selectedOrg,
environmentVariables: formData.environmentVariables || [],
deployerLrn: formData.selectedLrn
- }
-
- console.log('Deploying template with config:', config)
-
- const result = await deployTemplate(config)
+ };
+ const result = await deployTemplate(config);
// Save deployment results
setFormData({
diff --git a/apps/deploy-fe/src/components/onboarding/index.ts b/apps/deploy-fe/src/components/onboarding/index.ts
index bb850c4..d99b55c 100644
--- a/apps/deploy-fe/src/components/onboarding/index.ts
+++ b/apps/deploy-fe/src/components/onboarding/index.ts
@@ -12,7 +12,6 @@
export { default as Onboarding } from './Onboarding'
export {
default as OnboardingDialog,
- hasCompletedOnboarding
} from './OnboardingDialog'
// Step components
diff --git a/apps/deploy-fe/src/components/onboarding/useOnboarding.ts b/apps/deploy-fe/src/components/onboarding/useOnboarding.ts
index 93f7060..585cd1d 100644
--- a/apps/deploy-fe/src/components/onboarding/useOnboarding.ts
+++ b/apps/deploy-fe/src/components/onboarding/useOnboarding.ts
@@ -1,86 +1,120 @@
-'use client'
-
-import { create } from 'zustand'
-import { persist } from 'zustand/middleware'
+// src/components/onboarding/useOnboarding.ts
+import { useState } from 'react'
+import type {
+ OnboardingFormData} from '@/types'
+// Step type for navigation
export type Step = 'connect' | 'configure' | 'deploy' | 'success'
-export interface EnvironmentVariables {
- key: string
- value: string
+// Initial form data
+const initialFormData: OnboardingFormData = {
+ // Step 1: Connect
+ githubRepo: undefined,
+ template: undefined,
+ projectName: undefined,
+ deploymentMode: undefined,
+
+ // Step 2: Configure
+ selectedLrn: undefined,
+ selectedOrg: undefined, // Add this property
+ environmentVariables: [], // Use array type
+ deploymentType: undefined, // Add missing properties
+ deployerCount: undefined,
+ maxPrice: undefined,
+
+ // Step 3: Deploy
+ deploymentId: undefined,
+ repositoryUrl: undefined,
+ deploymentUrl: undefined, // Add missing property
+ projectId: undefined // Add missing property
}
-export interface OnboardingFormData {
- // Connect step
- githubRepo?: string
+export function useOnboarding() {
+ const [formData, setFormData] = useState(initialFormData)
+ const [currentStep, setCurrentStep] = useState('connect')
- // Configure step
- deploymentType?: 'auction' | 'lrn'
- deployerCount?: string
- maxPrice?: string
- selectedLrn?: string
- environments?: {
- production: boolean
- preview: boolean
- development: boolean
+ // Update form data (partial update)
+ const updateFormData = (updates: Partial) => {
+ setFormData(prev => ({ ...prev, ...updates }))
}
- environmentVariables?: Record
- // Deploy step
- deploymentId?: string
- deploymentUrl?: string
+ // Reset form data
+ const resetFormData = () => {
+ setFormData(initialFormData)
+ setCurrentStep('connect')
+ }
- // Success step
- projectId?: string
-}
-
-interface OnboardingState {
- currentStep: Step
- formData: OnboardingFormData
- setCurrentStep: (step: Step) => void
- setFormData: (data: Partial) => void
- nextStep: () => void
- previousStep: () => void
- resetOnboarding: () => void
-}
-
-const STEP_ORDER: Step[] = ['connect', 'configure', 'deploy', 'success']
-
-export const useOnboarding = create()(
- persist(
- (set) => ({
- currentStep: 'connect',
- formData: {},
-
- setCurrentStep: (step) => set({ currentStep: step }),
-
- setFormData: (data) =>
- set((state) => ({
- formData: { ...state.formData, ...data }
- })),
-
- nextStep: () =>
- set((state) => {
- const currentIndex = STEP_ORDER.indexOf(state.currentStep)
- const nextStep = STEP_ORDER[currentIndex + 1]
- return nextStep ? { currentStep: nextStep } : state
- }),
-
- previousStep: () =>
- set((state) => {
- const currentIndex = STEP_ORDER.indexOf(state.currentStep)
- const previousStep = STEP_ORDER[currentIndex - 1]
- return previousStep ? { currentStep: previousStep } : state
- }),
-
- resetOnboarding: () =>
- set({
- currentStep: 'connect',
- formData: {}
- })
- }),
- {
- name: 'laconic-onboarding-storage'
+ // Step navigation
+ const nextStep = () => {
+ switch (currentStep) {
+ case 'connect':
+ setCurrentStep('configure')
+ break
+ case 'configure':
+ setCurrentStep('deploy')
+ break
+ case 'deploy':
+ setCurrentStep('success')
+ break
}
- )
-)
\ No newline at end of file
+ }
+
+ const previousStep = () => {
+ switch (currentStep) {
+ case 'configure':
+ setCurrentStep('connect')
+ break
+ case 'deploy':
+ setCurrentStep('configure')
+ break
+ case 'success':
+ setCurrentStep('deploy')
+ break
+ }
+ }
+
+ // Validation helpers
+ const validateStep1 = () => {
+ if (formData.deploymentMode === 'template') {
+ return !!(formData.template && formData.projectName)
+ }
+ return !!formData.githubRepo
+ }
+
+ const validateStep2 = () => {
+ return !!(formData.selectedLrn && formData.selectedOrg)
+ }
+
+ const validateStep3 = () => {
+ return !!formData.deploymentId
+ }
+
+ return {
+ // Data
+ formData,
+ currentStep,
+
+ // Actions
+ setFormData: updateFormData,
+ setCurrentStep,
+ resetFormData,
+ resetOnboarding: resetFormData, // Alias for compatibility
+ nextStep,
+ previousStep,
+
+ // Validation
+ validateStep1,
+ validateStep2,
+ validateStep3,
+
+ // Store-like interface for compatibility
+ getState: () => ({
+ formData,
+ currentStep,
+ nextStep,
+ previousStep,
+ setFormData: updateFormData,
+ resetFormData
+ })
+ }
+}
\ No newline at end of file
diff --git a/apps/deploy-fe/src/constants/templates.tsx b/apps/deploy-fe/src/constants/templates.tsx
index f6e7b8d..db91acb 100644
--- a/apps/deploy-fe/src/constants/templates.tsx
+++ b/apps/deploy-fe/src/constants/templates.tsx
@@ -8,9 +8,9 @@
export interface TemplateDetail {
id: string
name: string
- icon: string
+ icon: string // Required string
repoFullName: string
- description: string
+ description: string // Required string
isComingSoon?: boolean
tags?: string[]
}
diff --git a/apps/deploy-fe/src/context/BackendContext.tsx b/apps/deploy-fe/src/context/BackendContext.tsx
index efdff46..3c92296 100644
--- a/apps/deploy-fe/src/context/BackendContext.tsx
+++ b/apps/deploy-fe/src/context/BackendContext.tsx
@@ -43,7 +43,7 @@ export const BackendProvider: React.FC<{ children: ReactNode }> = ({
const [isLoading, setIsLoading] = useState(true)
// Check backend connection
- const checkBackendConnection = useCallback(async () => {
+ const checkBackendConnection = useCallback(async (): Promise => {
try {
const response = await fetch('http://localhost:8000/auth/session', {
method: 'GET',
@@ -59,11 +59,11 @@ export const BackendProvider: React.FC<{ children: ReactNode }> = ({
console.log('❌ Backend not connected')
}
- return connected
+ // Don't return anything - function returns Promise
} catch (error) {
console.error('Error checking backend connection:', error)
setIsBackendConnected(false)
- return false
+ // Don't return anything - function returns Promise
}
}, [])
diff --git a/apps/deploy-fe/src/hooks/useAuthStatus.tsx b/apps/deploy-fe/src/hooks/useAuthStatus.tsx
index 5456b3b..91fae25 100644
--- a/apps/deploy-fe/src/hooks/useAuthStatus.tsx
+++ b/apps/deploy-fe/src/hooks/useAuthStatus.tsx
@@ -143,7 +143,7 @@ export function useAuthStatus(): AuthStatus & AuthActions {
// Calculate what's missing
const missing = {
clerkSignIn: !isSignedIn,
- clerkGithub: isSignedIn && !hasGithubInClerk,
+ clerkGithub: (isSignedIn ?? false) && !hasGithubInClerk,
walletConnection: !hasWalletAddress, // Just need wallet address for this step
backendConnection: hasWalletAddress && !isWalletSessionActive, // Need SIWE auth for backend
githubBackendSync: isBackendConnected && !isGithubBackendAuth
@@ -177,7 +177,7 @@ export function useAuthStatus(): AuthStatus & AuthActions {
return {
// Individual systems
clerk: {
- isSignedIn,
+ isSignedIn: isSignedIn ?? false,
isLoaded: isClerkLoaded && isUserLoaded,
hasGithubConnected: hasGithubInClerk,
user
diff --git a/apps/deploy-fe/src/hooks/useRepoData.tsx b/apps/deploy-fe/src/hooks/useRepoData.tsx
index 5775ca4..a8d952f 100644
--- a/apps/deploy-fe/src/hooks/useRepoData.tsx
+++ b/apps/deploy-fe/src/hooks/useRepoData.tsx
@@ -37,13 +37,13 @@ export function useRepoData(repoId: string): UseRepoDataReturn {
try {
// Check if user has connected GitHub account
const githubAccount = user.externalAccounts.find(
- account => account.provider === 'oauth_github'
+ account => account.provider === 'github'
);
if (githubAccount) {
// Try to get GitHub OAuth token from Clerk
try {
- token = await user.getToken({ template: 'github' });
+ // token = await user.getToken({ template: 'github' });
console.log('Using GitHub token from Clerk');
} catch (err) {
console.error('Error getting GitHub token from Clerk:', err);
diff --git a/apps/deploy-fe/src/hooks/useRepoSelection.tsx b/apps/deploy-fe/src/hooks/useRepoSelection.tsx
index 2bdf97d..1416f94 100644
--- a/apps/deploy-fe/src/hooks/useRepoSelection.tsx
+++ b/apps/deploy-fe/src/hooks/useRepoSelection.tsx
@@ -2,6 +2,7 @@
// src/hooks/useRepoSelection.ts
import { useState, useEffect } from 'react'
import { useOctokit } from '@/context/OctokitContext'
+import { adaptRepositories, type GitHubRepo } from '../utils/typeAdapters';
interface Repository {
id: string;
@@ -58,7 +59,7 @@ export function useRepoSelection(): UseRepoSelectionResult {
}
}));
- setRepositories(repoData);
+ setRepositories(adaptRepositories(repoData as GitHubRepo[]));
}
} catch (err) {
console.error('Failed to fetch repositories:', err);
@@ -89,6 +90,9 @@ export function useRepoSelection(): UseRepoSelectionResult {
try {
if (isAuth && octokit) {
const [owner, repo] = repoFullName.split('/');
+ if (!owner || !repo) {
+ throw new Error('Invalid repository format');
+ }
const response = await octokit.request('GET /repos/{owner}/{repo}/branches', {
owner,
diff --git a/apps/deploy-fe/src/types/deployment.ts b/apps/deploy-fe/src/types/deployment.ts
index 7de5f92..fdd4238 100644
--- a/apps/deploy-fe/src/types/deployment.ts
+++ b/apps/deploy-fe/src/types/deployment.ts
@@ -4,6 +4,7 @@ export interface Deployment {
status: string
isCurrent: boolean
createdAt: string | number
+ commit?: string
createdBy?: {
name: string
}
@@ -18,4 +19,17 @@ export interface Domain {
branch: string
status: string
createdAt: string | number
+ isCustom: boolean
+}
+
+// Add missing types that are referenced in components
+export interface EnvVarItem {
+ key?: string
+ value?: string
+ environments?: string[]
+ isEditing?: boolean
+}
+
+export type EditableEnvVarItem = Partial & {
+ isEditing?: boolean
}
diff --git a/apps/deploy-fe/src/types/foundation.ts b/apps/deploy-fe/src/types/foundation.ts
new file mode 100644
index 0000000..d639ec0
--- /dev/null
+++ b/apps/deploy-fe/src/types/foundation.ts
@@ -0,0 +1,54 @@
+import type { LucideIcon } from 'lucide-react'
+import type { ReactNode } from 'react'
+
+// Page action type (for header actions)
+export interface PageAction {
+ label: string
+ href: string
+ icon?: string | LucideIcon // Add missing icon property
+ external?: boolean
+ onClick?: () => void
+}
+
+// Page header configuration
+export interface PageHeaderProps {
+ title: string
+ description?: string // Add missing description property
+ actions?: PageAction[]
+ breadcrumbs?: Array<{
+ label: string
+ href?: string
+ }>
+}
+
+// Page wrapper layout types
+export type PageLayout = 'default' | 'bento' | 'centered'
+
+export interface PageWrapperProps {
+ children: ReactNode
+ header?: PageHeaderProps
+ layout?: PageLayout
+ className?: string
+}
+
+// Dropdown component types
+export interface DropdownOption {
+ label: string
+ value: string
+}
+
+export interface DropdownProps {
+ label?: string
+ options: DropdownOption[]
+ selectedValue?: string | undefined
+ onSelect: (value: string) => void
+ placeholder?: string
+ className?: string
+}
+
+// Step header types (for onboarding)
+export interface StepHeaderProps {
+ title: string
+ description: string
+ icon?: ReactNode // Required icon property
+}
\ No newline at end of file
diff --git a/apps/deploy-fe/src/types/index.ts b/apps/deploy-fe/src/types/index.ts
index 855ed8d..c974631 100644
--- a/apps/deploy-fe/src/types/index.ts
+++ b/apps/deploy-fe/src/types/index.ts
@@ -1,2 +1,6 @@
export * from './deployment'
export * from './project'
+export * from './dashboard'
+export * from './common'
+export * from './foundation'
+export * from './onboarding'
\ No newline at end of file
diff --git a/apps/deploy-fe/src/types/project.ts b/apps/deploy-fe/src/types/project.ts
index 9320e31..572f6d7 100644
--- a/apps/deploy-fe/src/types/project.ts
+++ b/apps/deploy-fe/src/types/project.ts
@@ -18,3 +18,10 @@ export interface Project {
}
}>
}
+
+export enum ProjectStatus {
+ Building = 'Building',
+ Ready = 'Ready',
+ Error = 'Error',
+ Deleting = 'Deleting'
+}
\ No newline at end of file
diff --git a/apps/deploy-fe/src/utils/typeAdapters.ts b/apps/deploy-fe/src/utils/typeAdapters.ts
new file mode 100644
index 0000000..1052675
--- /dev/null
+++ b/apps/deploy-fe/src/utils/typeAdapters.ts
@@ -0,0 +1,174 @@
+// src/utils/typeAdapters.ts
+import {
+ Deployer as GQLDeployer,
+ DeploymentStatus } from '../../../../services/gql-client/src/types';
+
+ // Define your frontend types that may be missing
+ export type ProjectStatus = 'success' | 'pending' | 'error' | 'building' | 'deleting';
+
+ export interface TemplateDetail {
+ id?: string;
+ name: string;
+ icon: string;
+ description?: string;
+ repoFullName?: string;
+ [key: string]: any; // Allow other properties
+ }
+
+ export interface Template {
+ id?: string;
+ name: string;
+ icon?: string;
+ description?: string;
+ repoFullName?: string;
+ [key: string]: any;
+ }
+
+ export interface Repository {
+ id: string;
+ name: string;
+ full_name: string;
+ default_branch: string;
+ html_url: string;
+ description?: string; // Note: undefined, not null
+ owner: {
+ login: string;
+ avatar_url: string;
+ };
+ }
+
+ export interface Deployer {
+ deployerLrn: string;
+ deployerId: string;
+ deployerApiUrl: string;
+ baseDomain: string;
+ minimumPayment?: string; // Note: undefined, not null
+ }
+
+ export interface GitHubRepo {
+ id: string;
+ name: string;
+ full_name: string;
+ default_branch: string;
+ html_url: string;
+ description: string | null;
+ owner: {
+ login: string;
+ avatar_url: string;
+ };
+ }
+
+ /**
+ * Convert GraphQL Deployer to Frontend Deployer
+ */
+ export const adaptDeployer = (gqlDeployer: GQLDeployer): Deployer => ({
+ ...gqlDeployer,
+ minimumPayment: gqlDeployer.minimumPayment ?? undefined
+ });
+
+ /**
+ * Convert array of GraphQL Deployers to Frontend Deployers
+ */
+ export const adaptDeployers = (gqlDeployers: GQLDeployer[]): Deployer[] => {
+ return gqlDeployers.map(adaptDeployer);
+ };
+
+ /**
+ * Convert Template to TemplateDetail (ensuring icon is always string)
+ */
+ export const adaptTemplate = (template: Template): TemplateDetail => ({
+ ...template,
+ icon: template.icon || '/default-template-icon.svg' // Provide a default icon path
+ });
+
+ /**
+ * Convert DeploymentStatus to ProjectStatus
+ */
+ export const adaptDeploymentStatus = (status: string | DeploymentStatus): ProjectStatus => {
+ // Convert to string safely
+ const statusStr = String(status);
+
+ switch (statusStr) {
+ case 'Building':
+ case 'building':
+ return 'building';
+ case 'Ready':
+ case 'ready':
+ case 'complete':
+ return 'success';
+ case 'Error':
+ case 'error':
+ case 'failed':
+ return 'error';
+ case 'Deleting':
+ case 'deleting':
+ return 'deleting';
+ case 'pending':
+ return 'pending';
+ default:
+ console.warn(`Unknown deployment status: ${statusStr}`);
+ return 'pending';
+ }
+ };
+
+ /**
+ * Convert GitHub API Repository to Frontend Repository
+ */
+ export const adaptRepository = (githubRepo: GitHubRepo): Repository => ({
+ ...githubRepo,
+ description: githubRepo.description ?? undefined
+ });
+
+ /**
+ * Convert array of GitHub repositories
+ */
+ export const adaptRepositories = (githubRepos: GitHubRepo[]): Repository[] => {
+ return githubRepos.map(adaptRepository);
+ };
+
+ /**
+ * Safely extract owner and repo name from selected repository
+ */
+ export const extractRepoInfo = (selectedRepo: Repository | null | undefined): { owner: string; repo: string } => {
+ if (!selectedRepo) {
+ throw new Error('No repository selected');
+ }
+
+ const owner = selectedRepo.owner?.login;
+ const repo = selectedRepo.name;
+
+ if (!owner || !repo) {
+ throw new Error('Repository owner and name are required');
+ }
+
+ return { owner, repo };
+ };
+
+ /**
+ * Template deployment configuration adapter
+ */
+ export interface TemplateDeploymentConfig {
+ template: TemplateDetail;
+ projectName: string;
+ organizationSlug: string;
+ environmentVariables: any[];
+ deployerLrn?: string;
+ }
+
+ export const adaptTemplateDeploymentConfig = (config: {
+ template: Template;
+ projectName: string;
+ organizationSlug: string;
+ environmentVariables: any[];
+ deployerLrn?: string;
+ }): TemplateDeploymentConfig => ({
+ ...config,
+ template: adaptTemplate(config.template)
+ });
+
+ /**
+ * Utility to handle optional template conversion
+ */
+ export const adaptOptionalTemplate = (template: Template | undefined): TemplateDetail | undefined => {
+ return template ? adaptTemplate(template) : undefined;
+ };
\ No newline at end of file