@shipfox/client-projects 17.0.0 → 21.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +21 -0
  3. package/dist/pages/create-project-page.stories.js +1 -1
  4. package/dist/pages/create-project-page.stories.js.map +1 -1
  5. package/dist/pages/project-settings-page.stories.js +1 -1
  6. package/dist/pages/project-settings-page.stories.js.map +1 -1
  7. package/dist/pages/projects-hub-page.js +3 -3
  8. package/dist/pages/projects-hub-page.js.map +1 -1
  9. package/dist/routes/create-project.d.ts +3 -0
  10. package/dist/routes/create-project.js +3 -0
  11. package/dist/routes/create-project.js.map +1 -1
  12. package/dist/routes/home.d.ts +3 -0
  13. package/dist/routes/home.js +3 -0
  14. package/dist/routes/home.js.map +1 -1
  15. package/dist/routes/project-index.d.ts +3 -0
  16. package/dist/routes/project-index.d.ts.map +1 -1
  17. package/dist/routes/project-index.js +3 -0
  18. package/dist/routes/project-index.js.map +1 -1
  19. package/dist/routes/project-settings-index.d.ts +3 -0
  20. package/dist/routes/project-settings-index.d.ts.map +1 -1
  21. package/dist/routes/project-settings-index.js +3 -0
  22. package/dist/routes/project-settings-index.js.map +1 -1
  23. package/dist/routes/project-settings.d.ts +3 -0
  24. package/dist/routes/project-settings.js +3 -0
  25. package/dist/routes/project-settings.js.map +1 -1
  26. package/dist/tsconfig.test.tsbuildinfo +1 -1
  27. package/package.json +7 -7
  28. package/src/pages/create-project-page.stories.tsx +1 -1
  29. package/src/pages/project-settings-page.stories.tsx +1 -1
  30. package/src/pages/projects-hub-page.tsx +5 -5
  31. package/src/routes/create-project.tsx +4 -1
  32. package/src/routes/home.tsx +1 -0
  33. package/src/routes/project-index.tsx +1 -0
  34. package/src/routes/project-settings-index.tsx +1 -0
  35. package/src/routes/project-settings.tsx +4 -1
  36. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1,2 +1,2 @@
1
1
  $ shipfox-swc
2
- Successfully compiled: 27 files with swc (631.23ms)
2
+ Successfully compiled: 27 files with swc (63.07ms)
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @shipfox/client-projects
2
2
 
3
+ ## 21.0.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [0f4abe4]
8
+ - Updated dependencies [71d0c44]
9
+ - Updated dependencies [9c21429]
10
+ - Updated dependencies [30beb8f]
11
+ - Updated dependencies [16733a7]
12
+ - Updated dependencies [163c40a]
13
+ - Updated dependencies [c4376a1]
14
+ - Updated dependencies [6703982]
15
+ - Updated dependencies [f1d127e]
16
+ - Updated dependencies [0e860d7]
17
+ - @shipfox/react-ui@2.0.0
18
+ - @shipfox/client-shell@21.0.0
19
+ - @shipfox/client-agent@21.0.0
20
+ - @shipfox/client-auth@21.0.0
21
+ - @shipfox/client-integrations@21.0.0
22
+ - @shipfox/client-ui@21.0.0
23
+
3
24
  ## 17.0.0
4
25
 
5
26
  ### Patch Changes
@@ -53,7 +53,7 @@ function CreateProjectPageStory({ scenario }) {
53
53
  store: store,
54
54
  children: [
55
55
  /*#__PURE__*/ _jsx("div", {
56
- className: "min-h-screen bg-background-neutral-background px-24 py-32",
56
+ className: "min-h-screen bg-background-subtle-base px-24 py-32",
57
57
  children: /*#__PURE__*/ _jsx("div", {
58
58
  className: "mx-auto w-full max-w-[1120px]",
59
59
  children: /*#__PURE__*/ _jsx(RouterProvider, {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/pages/create-project-page.stories.tsx"],"sourcesContent":["import type {IntegrationConnectionDto, RepositoryDto} from '@shipfox/api-integration-core-dto';\nimport {configureApiClient} from '@shipfox/client-api';\nimport {type AuthState, authStateAtom} from '@shipfox/client-auth';\nimport {Toaster} from '@shipfox/react-ui/toast';\nimport type {Meta, StoryObj} from '@storybook/react';\nimport {QueryClient, QueryClientProvider} from '@tanstack/react-query';\nimport {\n createMemoryHistory,\n createRootRoute,\n createRoute,\n createRouter,\n Outlet,\n RouterProvider,\n} from '@tanstack/react-router';\nimport {createStore, Provider as JotaiProvider} from 'jotai';\nimport {useMemo} from 'react';\nimport {CreateProjectPage} from './create-project-page.js';\n\nconst WORKSPACE_ID = '11111111-1111-4111-8111-111111111111';\nconst WORKSPACE_PATH = '/w/acme/projects/new';\nconst GITHUB_CONNECTION_ID = '33333333-3333-4333-8333-333333333333';\nconst GITEA_CONNECTION_ID = '44444444-4444-4444-8444-444444444444';\n\ntype Scenario =\n | 'playground'\n | 'long-names'\n | 'repository-loading'\n | 'empty-repositories'\n | 'connections-error';\n\ninterface CreateProjectPageStoryProps {\n scenario: Scenario;\n}\n\nconst authState: AuthState = {\n status: 'authenticated',\n token: 'token',\n user: {\n id: '22222222-2222-4222-8222-222222222222',\n email: 'platform@example.com',\n name: 'Platform Engineer',\n emailVerifiedAt: '2026-01-01T00:00:00.000Z',\n },\n workspaces: [{id: WORKSPACE_ID, name: 'Acme', slug: 'acme', membershipId: 'membership-1'}],\n};\n\nfunction CreateProjectPageStory({scenario}: CreateProjectPageStoryProps) {\n configureApiClient({\n baseUrl: 'https://api.example.test',\n fetchImpl: fetchForScenario(scenario),\n });\n\n const queryClient = useMemo(\n () => new QueryClient({defaultOptions: {queries: {retry: false}}}),\n [],\n );\n const store = useMemo(() => {\n const nextStore = createStore();\n nextStore.set(authStateAtom, authState);\n return nextStore;\n }, []);\n const router = useMemo(() => createStoryRouter(), []);\n\n return (\n <QueryClientProvider client={queryClient}>\n <JotaiProvider store={store}>\n <div className=\"min-h-screen bg-background-neutral-background px-24 py-32\">\n <div className=\"mx-auto w-full max-w-[1120px]\">\n <RouterProvider router={router} />\n </div>\n </div>\n <Toaster />\n </JotaiProvider>\n </QueryClientProvider>\n );\n}\n\nconst meta = {\n title: 'Projects/CreateProjectPage',\n component: CreateProjectPageStory,\n parameters: {layout: 'fullscreen'},\n args: {scenario: 'playground'},\n} satisfies Meta<typeof CreateProjectPageStory>;\n\nexport default meta;\ntype Story = StoryObj<typeof meta>;\n\nexport const Playground: Story = {};\n\nexport const LongNames: Story = {\n args: {scenario: 'long-names'},\n};\n\nexport const RepositoryLoading: Story = {\n args: {scenario: 'repository-loading'},\n};\n\nexport const EmptyRepositories: Story = {\n args: {scenario: 'empty-repositories'},\n};\n\nexport const ConnectionsError: Story = {\n args: {scenario: 'connections-error'},\n};\n\nfunction createStoryRouter() {\n const rootRoute = createRootRoute({component: Outlet});\n const workspaceRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/w/$workspaceSlug',\n component: Outlet,\n });\n const createProjectRoute = createRoute({\n getParentRoute: () => workspaceRoute,\n path: 'projects/new',\n component: CreateProjectPage,\n });\n const integrationsRoute = createRoute({\n getParentRoute: () => workspaceRoute,\n path: 'integrations',\n component: () => <div />,\n });\n const modelProvidersRoute = createRoute({\n getParentRoute: () => workspaceRoute,\n path: 'settings/agents',\n component: () => <div />,\n });\n const projectRoute = createRoute({\n getParentRoute: () => workspaceRoute,\n path: 'p/$projectSlug',\n component: () => <div />,\n });\n\n return createRouter({\n history: createMemoryHistory({initialEntries: [WORKSPACE_PATH]}),\n routeTree: rootRoute.addChildren([\n workspaceRoute.addChildren([\n createProjectRoute,\n integrationsRoute,\n modelProvidersRoute,\n projectRoute,\n ]),\n ]),\n });\n}\n\nfunction fetchForScenario(scenario: Scenario): typeof fetch {\n return (input, init) => {\n const url = requestUrl(input);\n const method = input instanceof Request ? input.method : (init?.method ?? 'GET');\n if (url.pathname === `/workspaces/${WORKSPACE_ID}/agent/model-providers`) {\n return Promise.resolve(\n jsonResponse({\n configs: [modelProviderConfig()],\n default_provider_id: 'openai',\n default_harness_id: null,\n }),\n );\n }\n if (url.pathname === '/integration-connections') {\n if (scenario === 'connections-error') return Promise.resolve(errorResponse());\n return Promise.resolve(jsonResponse({connections: connectionsForScenario(scenario)}));\n }\n if (\n url.pathname.startsWith('/integration-connections/') &&\n url.pathname.endsWith('/repositories')\n ) {\n if (scenario === 'repository-loading') return new Promise<Response>(() => undefined);\n return Promise.resolve(\n jsonResponse({repositories: repositoriesForScenario(scenario), next_cursor: null}),\n );\n }\n if (url.pathname === '/projects' && method === 'POST') {\n return Promise.resolve(\n jsonResponse({\n id: '99999999-9999-4999-8999-999999999999',\n workspace_id: WORKSPACE_ID,\n name: 'Platform',\n slug: 'platform',\n source: {\n connection_id: GITHUB_CONNECTION_ID,\n external_repository_id: 'platform',\n },\n created_at: '2026-01-01T00:00:00.000Z',\n updated_at: '2026-01-01T00:00:00.000Z',\n }),\n );\n }\n return Promise.resolve(jsonResponse({}, {status: 404}));\n };\n}\n\nfunction connectionsForScenario(scenario: Scenario): IntegrationConnectionDto[] {\n if (scenario === 'long-names') {\n return [\n connection({\n id: GITHUB_CONNECTION_ID,\n provider: 'github',\n display_name: 'acme-platform-engineering-production',\n external_account_id: 'acme-platform-engineering-production',\n }),\n connection({\n id: GITEA_CONNECTION_ID,\n provider: 'gitea',\n display_name: 'git.internal.acme.example/platform-tools',\n external_account_id: 'git.internal.acme.example/platform-tools',\n }),\n ];\n }\n\n return [\n connection({\n id: GITHUB_CONNECTION_ID,\n provider: 'github',\n display_name: 'acme',\n external_account_id: 'acme',\n }),\n connection({\n id: GITEA_CONNECTION_ID,\n provider: 'gitea',\n display_name: 'git.acme.internal',\n external_account_id: 'git.acme.internal',\n }),\n ];\n}\n\nfunction repositoriesForScenario(scenario: Scenario): RepositoryDto[] {\n if (scenario === 'empty-repositories') return [];\n if (scenario === 'long-names') {\n return [\n repository({\n external_repository_id: 'acme-platform-infrastructure-control-plane',\n name: 'acme-platform-infrastructure-control-plane',\n full_name: 'acme-engineering/acme-platform-infrastructure-control-plane',\n default_branch: 'production',\n }),\n repository({\n external_repository_id: 'workflow-automation-and-runner-orchestration',\n name: 'workflow-automation-and-runner-orchestration',\n full_name: 'acme-engineering/workflow-automation-and-runner-orchestration',\n default_branch: 'main',\n }),\n repository({\n external_repository_id: 'customer-deployment-pipelines',\n name: 'customer-deployment-pipelines',\n full_name: 'acme-solutions/customer-deployment-pipelines',\n default_branch: 'release',\n }),\n repository({\n external_repository_id: 'observability-and-deployment-automation',\n name: 'observability-and-deployment-automation',\n full_name: 'acme-platform/observability-and-deployment-automation',\n default_branch: 'main',\n }),\n ];\n }\n\n return [\n repository({\n external_repository_id: 'platform',\n name: 'platform',\n full_name: 'acme/platform',\n default_branch: 'main',\n }),\n repository({\n external_repository_id: 'api',\n name: 'api',\n full_name: 'acme/api',\n default_branch: 'main',\n }),\n repository({\n external_repository_id: 'client',\n name: 'client',\n full_name: 'acme/client',\n default_branch: 'main',\n }),\n repository({\n external_repository_id: 'runners',\n name: 'runners',\n full_name: 'acme/runners',\n default_branch: 'main',\n }),\n repository({\n external_repository_id: 'workflows',\n name: 'workflows',\n full_name: 'acme/workflows',\n default_branch: 'develop',\n }),\n ];\n}\n\nfunction connection(\n overrides: Partial<IntegrationConnectionDto> & Pick<IntegrationConnectionDto, 'id'>,\n): IntegrationConnectionDto {\n return {\n workspace_id: WORKSPACE_ID,\n provider: 'github',\n external_account_id: 'acme',\n slug: 'github_acme',\n display_name: 'GitHub',\n lifecycle_status: 'active',\n capabilities: ['source_control'],\n created_at: '2026-01-01T00:00:00.000Z',\n updated_at: '2026-01-01T00:00:00.000Z',\n ...overrides,\n };\n}\n\nfunction repository(overrides: Partial<RepositoryDto>): RepositoryDto {\n return {\n connection_id: GITHUB_CONNECTION_ID,\n external_repository_id: 'platform',\n owner: 'acme',\n name: 'platform',\n full_name: 'acme/platform',\n default_branch: 'main',\n visibility: 'private',\n clone_url: 'https://github.example.test/acme/platform.git',\n html_url: 'https://github.example.test/acme/platform',\n ...overrides,\n };\n}\n\nfunction modelProviderConfig() {\n return {\n kind: 'builtin',\n workspace_id: WORKSPACE_ID,\n provider_id: 'openai',\n default_model: 'gpt-5.5-pro',\n credential_fields: ['api_key'],\n lifecycle_status: 'active',\n created_at: '2026-01-01T00:00:00.000Z',\n updated_at: '2026-01-01T00:00:00.000Z',\n };\n}\n\nfunction requestUrl(input: RequestInfo | URL): URL {\n if (input instanceof Request) return new URL(input.url);\n if (input instanceof URL) return input;\n return new URL(input, 'https://api.example.test');\n}\n\nfunction jsonResponse(body: unknown, init: ResponseInit = {}) {\n return new Response(JSON.stringify(body), {\n status: 200,\n headers: {'content-type': 'application/json'},\n ...init,\n });\n}\n\nfunction errorResponse() {\n return jsonResponse({code: 'server-error'}, {status: 500});\n}\n"],"names":["configureApiClient","authStateAtom","Toaster","QueryClient","QueryClientProvider","createMemoryHistory","createRootRoute","createRoute","createRouter","Outlet","RouterProvider","createStore","Provider","JotaiProvider","useMemo","CreateProjectPage","WORKSPACE_ID","WORKSPACE_PATH","GITHUB_CONNECTION_ID","GITEA_CONNECTION_ID","authState","status","token","user","id","email","name","emailVerifiedAt","workspaces","slug","membershipId","CreateProjectPageStory","scenario","baseUrl","fetchImpl","fetchForScenario","queryClient","defaultOptions","queries","retry","store","nextStore","set","router","createStoryRouter","client","div","className","meta","title","component","parameters","layout","args","Playground","LongNames","RepositoryLoading","EmptyRepositories","ConnectionsError","rootRoute","workspaceRoute","getParentRoute","path","createProjectRoute","integrationsRoute","modelProvidersRoute","projectRoute","history","initialEntries","routeTree","addChildren","input","init","url","requestUrl","method","Request","pathname","Promise","resolve","jsonResponse","configs","modelProviderConfig","default_provider_id","default_harness_id","errorResponse","connections","connectionsForScenario","startsWith","endsWith","undefined","repositories","repositoriesForScenario","next_cursor","workspace_id","source","connection_id","external_repository_id","created_at","updated_at","connection","provider","display_name","external_account_id","repository","full_name","default_branch","overrides","lifecycle_status","capabilities","owner","visibility","clone_url","html_url","kind","provider_id","default_model","credential_fields","URL","body","Response","JSON","stringify","headers","code"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/pages/create-project-page.stories.tsx"],"sourcesContent":["import type {IntegrationConnectionDto, RepositoryDto} from '@shipfox/api-integration-core-dto';\nimport {configureApiClient} from '@shipfox/client-api';\nimport {type AuthState, authStateAtom} from '@shipfox/client-auth';\nimport {Toaster} from '@shipfox/react-ui/toast';\nimport type {Meta, StoryObj} from '@storybook/react';\nimport {QueryClient, QueryClientProvider} from '@tanstack/react-query';\nimport {\n createMemoryHistory,\n createRootRoute,\n createRoute,\n createRouter,\n Outlet,\n RouterProvider,\n} from '@tanstack/react-router';\nimport {createStore, Provider as JotaiProvider} from 'jotai';\nimport {useMemo} from 'react';\nimport {CreateProjectPage} from './create-project-page.js';\n\nconst WORKSPACE_ID = '11111111-1111-4111-8111-111111111111';\nconst WORKSPACE_PATH = '/w/acme/projects/new';\nconst GITHUB_CONNECTION_ID = '33333333-3333-4333-8333-333333333333';\nconst GITEA_CONNECTION_ID = '44444444-4444-4444-8444-444444444444';\n\ntype Scenario =\n | 'playground'\n | 'long-names'\n | 'repository-loading'\n | 'empty-repositories'\n | 'connections-error';\n\ninterface CreateProjectPageStoryProps {\n scenario: Scenario;\n}\n\nconst authState: AuthState = {\n status: 'authenticated',\n token: 'token',\n user: {\n id: '22222222-2222-4222-8222-222222222222',\n email: 'platform@example.com',\n name: 'Platform Engineer',\n emailVerifiedAt: '2026-01-01T00:00:00.000Z',\n },\n workspaces: [{id: WORKSPACE_ID, name: 'Acme', slug: 'acme', membershipId: 'membership-1'}],\n};\n\nfunction CreateProjectPageStory({scenario}: CreateProjectPageStoryProps) {\n configureApiClient({\n baseUrl: 'https://api.example.test',\n fetchImpl: fetchForScenario(scenario),\n });\n\n const queryClient = useMemo(\n () => new QueryClient({defaultOptions: {queries: {retry: false}}}),\n [],\n );\n const store = useMemo(() => {\n const nextStore = createStore();\n nextStore.set(authStateAtom, authState);\n return nextStore;\n }, []);\n const router = useMemo(() => createStoryRouter(), []);\n\n return (\n <QueryClientProvider client={queryClient}>\n <JotaiProvider store={store}>\n <div className=\"min-h-screen bg-background-subtle-base px-24 py-32\">\n <div className=\"mx-auto w-full max-w-[1120px]\">\n <RouterProvider router={router} />\n </div>\n </div>\n <Toaster />\n </JotaiProvider>\n </QueryClientProvider>\n );\n}\n\nconst meta = {\n title: 'Projects/CreateProjectPage',\n component: CreateProjectPageStory,\n parameters: {layout: 'fullscreen'},\n args: {scenario: 'playground'},\n} satisfies Meta<typeof CreateProjectPageStory>;\n\nexport default meta;\ntype Story = StoryObj<typeof meta>;\n\nexport const Playground: Story = {};\n\nexport const LongNames: Story = {\n args: {scenario: 'long-names'},\n};\n\nexport const RepositoryLoading: Story = {\n args: {scenario: 'repository-loading'},\n};\n\nexport const EmptyRepositories: Story = {\n args: {scenario: 'empty-repositories'},\n};\n\nexport const ConnectionsError: Story = {\n args: {scenario: 'connections-error'},\n};\n\nfunction createStoryRouter() {\n const rootRoute = createRootRoute({component: Outlet});\n const workspaceRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/w/$workspaceSlug',\n component: Outlet,\n });\n const createProjectRoute = createRoute({\n getParentRoute: () => workspaceRoute,\n path: 'projects/new',\n component: CreateProjectPage,\n });\n const integrationsRoute = createRoute({\n getParentRoute: () => workspaceRoute,\n path: 'integrations',\n component: () => <div />,\n });\n const modelProvidersRoute = createRoute({\n getParentRoute: () => workspaceRoute,\n path: 'settings/agents',\n component: () => <div />,\n });\n const projectRoute = createRoute({\n getParentRoute: () => workspaceRoute,\n path: 'p/$projectSlug',\n component: () => <div />,\n });\n\n return createRouter({\n history: createMemoryHistory({initialEntries: [WORKSPACE_PATH]}),\n routeTree: rootRoute.addChildren([\n workspaceRoute.addChildren([\n createProjectRoute,\n integrationsRoute,\n modelProvidersRoute,\n projectRoute,\n ]),\n ]),\n });\n}\n\nfunction fetchForScenario(scenario: Scenario): typeof fetch {\n return (input, init) => {\n const url = requestUrl(input);\n const method = input instanceof Request ? input.method : (init?.method ?? 'GET');\n if (url.pathname === `/workspaces/${WORKSPACE_ID}/agent/model-providers`) {\n return Promise.resolve(\n jsonResponse({\n configs: [modelProviderConfig()],\n default_provider_id: 'openai',\n default_harness_id: null,\n }),\n );\n }\n if (url.pathname === '/integration-connections') {\n if (scenario === 'connections-error') return Promise.resolve(errorResponse());\n return Promise.resolve(jsonResponse({connections: connectionsForScenario(scenario)}));\n }\n if (\n url.pathname.startsWith('/integration-connections/') &&\n url.pathname.endsWith('/repositories')\n ) {\n if (scenario === 'repository-loading') return new Promise<Response>(() => undefined);\n return Promise.resolve(\n jsonResponse({repositories: repositoriesForScenario(scenario), next_cursor: null}),\n );\n }\n if (url.pathname === '/projects' && method === 'POST') {\n return Promise.resolve(\n jsonResponse({\n id: '99999999-9999-4999-8999-999999999999',\n workspace_id: WORKSPACE_ID,\n name: 'Platform',\n slug: 'platform',\n source: {\n connection_id: GITHUB_CONNECTION_ID,\n external_repository_id: 'platform',\n },\n created_at: '2026-01-01T00:00:00.000Z',\n updated_at: '2026-01-01T00:00:00.000Z',\n }),\n );\n }\n return Promise.resolve(jsonResponse({}, {status: 404}));\n };\n}\n\nfunction connectionsForScenario(scenario: Scenario): IntegrationConnectionDto[] {\n if (scenario === 'long-names') {\n return [\n connection({\n id: GITHUB_CONNECTION_ID,\n provider: 'github',\n display_name: 'acme-platform-engineering-production',\n external_account_id: 'acme-platform-engineering-production',\n }),\n connection({\n id: GITEA_CONNECTION_ID,\n provider: 'gitea',\n display_name: 'git.internal.acme.example/platform-tools',\n external_account_id: 'git.internal.acme.example/platform-tools',\n }),\n ];\n }\n\n return [\n connection({\n id: GITHUB_CONNECTION_ID,\n provider: 'github',\n display_name: 'acme',\n external_account_id: 'acme',\n }),\n connection({\n id: GITEA_CONNECTION_ID,\n provider: 'gitea',\n display_name: 'git.acme.internal',\n external_account_id: 'git.acme.internal',\n }),\n ];\n}\n\nfunction repositoriesForScenario(scenario: Scenario): RepositoryDto[] {\n if (scenario === 'empty-repositories') return [];\n if (scenario === 'long-names') {\n return [\n repository({\n external_repository_id: 'acme-platform-infrastructure-control-plane',\n name: 'acme-platform-infrastructure-control-plane',\n full_name: 'acme-engineering/acme-platform-infrastructure-control-plane',\n default_branch: 'production',\n }),\n repository({\n external_repository_id: 'workflow-automation-and-runner-orchestration',\n name: 'workflow-automation-and-runner-orchestration',\n full_name: 'acme-engineering/workflow-automation-and-runner-orchestration',\n default_branch: 'main',\n }),\n repository({\n external_repository_id: 'customer-deployment-pipelines',\n name: 'customer-deployment-pipelines',\n full_name: 'acme-solutions/customer-deployment-pipelines',\n default_branch: 'release',\n }),\n repository({\n external_repository_id: 'observability-and-deployment-automation',\n name: 'observability-and-deployment-automation',\n full_name: 'acme-platform/observability-and-deployment-automation',\n default_branch: 'main',\n }),\n ];\n }\n\n return [\n repository({\n external_repository_id: 'platform',\n name: 'platform',\n full_name: 'acme/platform',\n default_branch: 'main',\n }),\n repository({\n external_repository_id: 'api',\n name: 'api',\n full_name: 'acme/api',\n default_branch: 'main',\n }),\n repository({\n external_repository_id: 'client',\n name: 'client',\n full_name: 'acme/client',\n default_branch: 'main',\n }),\n repository({\n external_repository_id: 'runners',\n name: 'runners',\n full_name: 'acme/runners',\n default_branch: 'main',\n }),\n repository({\n external_repository_id: 'workflows',\n name: 'workflows',\n full_name: 'acme/workflows',\n default_branch: 'develop',\n }),\n ];\n}\n\nfunction connection(\n overrides: Partial<IntegrationConnectionDto> & Pick<IntegrationConnectionDto, 'id'>,\n): IntegrationConnectionDto {\n return {\n workspace_id: WORKSPACE_ID,\n provider: 'github',\n external_account_id: 'acme',\n slug: 'github_acme',\n display_name: 'GitHub',\n lifecycle_status: 'active',\n capabilities: ['source_control'],\n created_at: '2026-01-01T00:00:00.000Z',\n updated_at: '2026-01-01T00:00:00.000Z',\n ...overrides,\n };\n}\n\nfunction repository(overrides: Partial<RepositoryDto>): RepositoryDto {\n return {\n connection_id: GITHUB_CONNECTION_ID,\n external_repository_id: 'platform',\n owner: 'acme',\n name: 'platform',\n full_name: 'acme/platform',\n default_branch: 'main',\n visibility: 'private',\n clone_url: 'https://github.example.test/acme/platform.git',\n html_url: 'https://github.example.test/acme/platform',\n ...overrides,\n };\n}\n\nfunction modelProviderConfig() {\n return {\n kind: 'builtin',\n workspace_id: WORKSPACE_ID,\n provider_id: 'openai',\n default_model: 'gpt-5.5-pro',\n credential_fields: ['api_key'],\n lifecycle_status: 'active',\n created_at: '2026-01-01T00:00:00.000Z',\n updated_at: '2026-01-01T00:00:00.000Z',\n };\n}\n\nfunction requestUrl(input: RequestInfo | URL): URL {\n if (input instanceof Request) return new URL(input.url);\n if (input instanceof URL) return input;\n return new URL(input, 'https://api.example.test');\n}\n\nfunction jsonResponse(body: unknown, init: ResponseInit = {}) {\n return new Response(JSON.stringify(body), {\n status: 200,\n headers: {'content-type': 'application/json'},\n ...init,\n });\n}\n\nfunction errorResponse() {\n return jsonResponse({code: 'server-error'}, {status: 500});\n}\n"],"names":["configureApiClient","authStateAtom","Toaster","QueryClient","QueryClientProvider","createMemoryHistory","createRootRoute","createRoute","createRouter","Outlet","RouterProvider","createStore","Provider","JotaiProvider","useMemo","CreateProjectPage","WORKSPACE_ID","WORKSPACE_PATH","GITHUB_CONNECTION_ID","GITEA_CONNECTION_ID","authState","status","token","user","id","email","name","emailVerifiedAt","workspaces","slug","membershipId","CreateProjectPageStory","scenario","baseUrl","fetchImpl","fetchForScenario","queryClient","defaultOptions","queries","retry","store","nextStore","set","router","createStoryRouter","client","div","className","meta","title","component","parameters","layout","args","Playground","LongNames","RepositoryLoading","EmptyRepositories","ConnectionsError","rootRoute","workspaceRoute","getParentRoute","path","createProjectRoute","integrationsRoute","modelProvidersRoute","projectRoute","history","initialEntries","routeTree","addChildren","input","init","url","requestUrl","method","Request","pathname","Promise","resolve","jsonResponse","configs","modelProviderConfig","default_provider_id","default_harness_id","errorResponse","connections","connectionsForScenario","startsWith","endsWith","undefined","repositories","repositoriesForScenario","next_cursor","workspace_id","source","connection_id","external_repository_id","created_at","updated_at","connection","provider","display_name","external_account_id","repository","full_name","default_branch","overrides","lifecycle_status","capabilities","owner","visibility","clone_url","html_url","kind","provider_id","default_model","credential_fields","URL","body","Response","JSON","stringify","headers","code"],"mappings":"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"}
@@ -55,7 +55,7 @@ function ProjectSettingsPageStory({ scenario }) {
55
55
  store: store,
56
56
  children: [
57
57
  /*#__PURE__*/ _jsx("div", {
58
- className: "min-h-screen bg-background-neutral-background px-24 py-32",
58
+ className: "min-h-screen bg-background-subtle-base px-24 py-32",
59
59
  children: /*#__PURE__*/ _jsx("div", {
60
60
  className: "mx-auto w-full max-w-[1120px]",
61
61
  children: /*#__PURE__*/ _jsx(RouterProvider, {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/pages/project-settings-page.stories.tsx"],"sourcesContent":["import {configureApiClient} from '@shipfox/client-api';\nimport {type AuthState, authStateAtom} from '@shipfox/client-auth';\nimport {Toaster} from '@shipfox/react-ui/toast';\nimport type {Meta, StoryObj} from '@storybook/react';\nimport {QueryClient, QueryClientProvider} from '@tanstack/react-query';\nimport {\n createMemoryHistory,\n createRootRoute,\n createRoute,\n createRouter,\n Outlet,\n RouterProvider,\n} from '@tanstack/react-router';\nimport {createStore, Provider as JotaiProvider} from 'jotai';\nimport {useMemo} from 'react';\nimport {expect, screen, userEvent, within} from 'storybook/test';\nimport {ProjectSettingsPage} from './project-settings-page.js';\n\nconst WORKSPACE_ID = '11111111-1111-4111-8111-111111111111';\nconst PROJECT_ID = '99999999-9999-4999-8999-999999999999';\nconst OTHER_PROJECT_ID = '88888888-8888-4888-8888-888888888888';\n\ntype Scenario = 'default' | 'long-values' | 'taken-slug';\n\ninterface ProjectStoryResponse {\n id: string;\n workspace_id: string;\n name: string;\n slug: string;\n source: {\n connection_id: string;\n external_repository_id: string;\n };\n created_at: string;\n updated_at: string;\n}\n\ninterface ProjectSettingsPageStoryProps {\n scenario: Scenario;\n}\n\nconst authState: AuthState = {\n status: 'authenticated',\n token: 'token',\n user: {\n id: '22222222-2222-4222-8222-222222222222',\n email: 'platform@example.com',\n name: 'Platform Engineer',\n emailVerifiedAt: '2026-01-01T00:00:00.000Z',\n },\n workspaces: [{id: WORKSPACE_ID, name: 'Acme', slug: 'acme', membershipId: 'membership-1'}],\n};\n\nfunction ProjectSettingsPageStory({scenario}: ProjectSettingsPageStoryProps) {\n configureApiClient({\n baseUrl: 'https://api.example.test',\n fetchImpl: fetchForScenario(scenario),\n });\n\n const queryClient = useMemo(\n () => new QueryClient({defaultOptions: {queries: {retry: false}}}),\n [],\n );\n const store = useMemo(() => {\n const nextStore = createStore();\n nextStore.set(authStateAtom, authState);\n return nextStore;\n }, []);\n const router = useMemo(() => createStoryRouter(scenario), [scenario]);\n\n return (\n <QueryClientProvider client={queryClient}>\n <JotaiProvider store={store}>\n <div className=\"min-h-screen bg-background-neutral-background px-24 py-32\">\n <div className=\"mx-auto w-full max-w-[1120px]\">\n <RouterProvider router={router} />\n </div>\n </div>\n <Toaster />\n </JotaiProvider>\n </QueryClientProvider>\n );\n}\n\nconst meta = {\n title: 'Projects/ProjectSettingsPage',\n component: ProjectSettingsPageStory,\n parameters: {layout: 'fullscreen'},\n args: {scenario: 'default'},\n} satisfies Meta<typeof ProjectSettingsPageStory>;\n\nexport default meta;\ntype Story = StoryObj<typeof meta>;\n\nexport const Playground: Story = {};\n\nexport const LongValues: Story = {\n args: {scenario: 'long-values'},\n};\n\nexport const TakenSlug: Story = {\n args: {scenario: 'taken-slug'},\n play: async ({canvasElement}) => {\n const canvas = within(canvasElement);\n const slugInput = await canvas.findByLabelText('Project slug');\n await userEvent.clear(slugInput);\n await userEvent.type(slugInput, 'taken-platform');\n\n expect(await canvas.findByText('This slug is already taken.')).toBeInTheDocument();\n },\n};\n\nexport const SlugChangeWarning: Story = {\n play: async ({canvasElement}) => {\n const canvas = within(canvasElement);\n const slugInput = await canvas.findByLabelText('Project slug');\n await userEvent.clear(slugInput);\n await userEvent.type(slugInput, 'renamed-platform');\n await canvas.findByText('Slug is available.');\n await userEvent.click(canvas.getByRole('button', {name: 'Save changes'}));\n\n const dialog = await screen.findByRole('dialog');\n expect(dialog).toHaveTextContent('Change project slug?');\n expect(dialog).toHaveTextContent('Links and bookmarks pointing at the old URL stop working.');\n expect(dialog).toHaveTextContent(\n 'Workflows that reference this project slug may stop working.',\n );\n },\n};\n\nfunction createStoryRouter(scenario: Scenario) {\n const project = projectForScenario(scenario);\n const rootRoute = createRootRoute({component: Outlet});\n const projectSettingsRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/w/$workspaceSlug/p/$projectSlug/settings/general',\n component: ProjectSettingsPage,\n });\n\n return createRouter({\n history: createMemoryHistory({\n initialEntries: [`/w/acme/p/${project.slug}/settings/general`],\n }),\n routeTree: rootRoute.addChildren([projectSettingsRoute]),\n });\n}\n\nfunction fetchForScenario(scenario: Scenario): typeof fetch {\n return async (input, init) => {\n const url = requestUrl(input);\n const method = input instanceof Request ? input.method : (init?.method ?? 'GET');\n\n if (url.pathname === '/projects' && method === 'GET') {\n const search = url.searchParams.get('search');\n if (scenario === 'taken-slug' && search === 'taken-platform') {\n return jsonResponse({\n projects: [projectForScenario(scenario, {id: OTHER_PROJECT_ID, slug: 'taken-platform'})],\n next_cursor: null,\n });\n }\n return jsonResponse({projects: [projectForScenario(scenario)], next_cursor: null});\n }\n\n if (url.pathname === `/projects/${PROJECT_ID}` && method === 'PATCH') {\n const body = (await (input as Request).clone().json()) as {\n name?: string;\n slug?: string;\n };\n const project = projectForScenario(scenario);\n return jsonResponse({\n ...project,\n name: body.name ?? project.name,\n slug: body.slug ?? project.slug,\n });\n }\n\n return jsonResponse({}, {status: 404});\n };\n}\n\nfunction projectForScenario(\n scenario: Scenario,\n overrides: Partial<ProjectStoryResponse> = {},\n): ProjectStoryResponse {\n const project = {\n id: PROJECT_ID,\n workspace_id: WORKSPACE_ID,\n name:\n scenario === 'long-values'\n ? 'Infrastructure Control Plane and Workflow Automation'\n : 'Platform',\n slug: scenario === 'long-values' ? 'infrastructure-control-plane' : 'platform',\n source: {\n connection_id: '33333333-3333-4333-8333-333333333333',\n external_repository_id: 'platform',\n },\n created_at: '2026-01-01T00:00:00.000Z',\n updated_at: '2026-01-01T00:00:00.000Z',\n ...overrides,\n };\n return project;\n}\n\nfunction requestUrl(input: RequestInfo | URL): URL {\n if (input instanceof Request) return new URL(input.url);\n if (input instanceof URL) return input;\n return new URL(input, 'https://api.example.test');\n}\n\nfunction jsonResponse(body: unknown, init: ResponseInit = {}) {\n return new Response(JSON.stringify(body), {\n status: 200,\n headers: {'content-type': 'application/json'},\n ...init,\n });\n}\n"],"names":["configureApiClient","authStateAtom","Toaster","QueryClient","QueryClientProvider","createMemoryHistory","createRootRoute","createRoute","createRouter","Outlet","RouterProvider","createStore","Provider","JotaiProvider","useMemo","expect","screen","userEvent","within","ProjectSettingsPage","WORKSPACE_ID","PROJECT_ID","OTHER_PROJECT_ID","authState","status","token","user","id","email","name","emailVerifiedAt","workspaces","slug","membershipId","ProjectSettingsPageStory","scenario","baseUrl","fetchImpl","fetchForScenario","queryClient","defaultOptions","queries","retry","store","nextStore","set","router","createStoryRouter","client","div","className","meta","title","component","parameters","layout","args","Playground","LongValues","TakenSlug","play","canvasElement","canvas","slugInput","findByLabelText","clear","type","findByText","toBeInTheDocument","SlugChangeWarning","click","getByRole","dialog","findByRole","toHaveTextContent","project","projectForScenario","rootRoute","projectSettingsRoute","getParentRoute","path","history","initialEntries","routeTree","addChildren","input","init","url","requestUrl","method","Request","pathname","search","searchParams","get","jsonResponse","projects","next_cursor","body","clone","json","overrides","workspace_id","source","connection_id","external_repository_id","created_at","updated_at","URL","Response","JSON","stringify","headers"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/pages/project-settings-page.stories.tsx"],"sourcesContent":["import {configureApiClient} from '@shipfox/client-api';\nimport {type AuthState, authStateAtom} from '@shipfox/client-auth';\nimport {Toaster} from '@shipfox/react-ui/toast';\nimport type {Meta, StoryObj} from '@storybook/react';\nimport {QueryClient, QueryClientProvider} from '@tanstack/react-query';\nimport {\n createMemoryHistory,\n createRootRoute,\n createRoute,\n createRouter,\n Outlet,\n RouterProvider,\n} from '@tanstack/react-router';\nimport {createStore, Provider as JotaiProvider} from 'jotai';\nimport {useMemo} from 'react';\nimport {expect, screen, userEvent, within} from 'storybook/test';\nimport {ProjectSettingsPage} from './project-settings-page.js';\n\nconst WORKSPACE_ID = '11111111-1111-4111-8111-111111111111';\nconst PROJECT_ID = '99999999-9999-4999-8999-999999999999';\nconst OTHER_PROJECT_ID = '88888888-8888-4888-8888-888888888888';\n\ntype Scenario = 'default' | 'long-values' | 'taken-slug';\n\ninterface ProjectStoryResponse {\n id: string;\n workspace_id: string;\n name: string;\n slug: string;\n source: {\n connection_id: string;\n external_repository_id: string;\n };\n created_at: string;\n updated_at: string;\n}\n\ninterface ProjectSettingsPageStoryProps {\n scenario: Scenario;\n}\n\nconst authState: AuthState = {\n status: 'authenticated',\n token: 'token',\n user: {\n id: '22222222-2222-4222-8222-222222222222',\n email: 'platform@example.com',\n name: 'Platform Engineer',\n emailVerifiedAt: '2026-01-01T00:00:00.000Z',\n },\n workspaces: [{id: WORKSPACE_ID, name: 'Acme', slug: 'acme', membershipId: 'membership-1'}],\n};\n\nfunction ProjectSettingsPageStory({scenario}: ProjectSettingsPageStoryProps) {\n configureApiClient({\n baseUrl: 'https://api.example.test',\n fetchImpl: fetchForScenario(scenario),\n });\n\n const queryClient = useMemo(\n () => new QueryClient({defaultOptions: {queries: {retry: false}}}),\n [],\n );\n const store = useMemo(() => {\n const nextStore = createStore();\n nextStore.set(authStateAtom, authState);\n return nextStore;\n }, []);\n const router = useMemo(() => createStoryRouter(scenario), [scenario]);\n\n return (\n <QueryClientProvider client={queryClient}>\n <JotaiProvider store={store}>\n <div className=\"min-h-screen bg-background-subtle-base px-24 py-32\">\n <div className=\"mx-auto w-full max-w-[1120px]\">\n <RouterProvider router={router} />\n </div>\n </div>\n <Toaster />\n </JotaiProvider>\n </QueryClientProvider>\n );\n}\n\nconst meta = {\n title: 'Projects/ProjectSettingsPage',\n component: ProjectSettingsPageStory,\n parameters: {layout: 'fullscreen'},\n args: {scenario: 'default'},\n} satisfies Meta<typeof ProjectSettingsPageStory>;\n\nexport default meta;\ntype Story = StoryObj<typeof meta>;\n\nexport const Playground: Story = {};\n\nexport const LongValues: Story = {\n args: {scenario: 'long-values'},\n};\n\nexport const TakenSlug: Story = {\n args: {scenario: 'taken-slug'},\n play: async ({canvasElement}) => {\n const canvas = within(canvasElement);\n const slugInput = await canvas.findByLabelText('Project slug');\n await userEvent.clear(slugInput);\n await userEvent.type(slugInput, 'taken-platform');\n\n expect(await canvas.findByText('This slug is already taken.')).toBeInTheDocument();\n },\n};\n\nexport const SlugChangeWarning: Story = {\n play: async ({canvasElement}) => {\n const canvas = within(canvasElement);\n const slugInput = await canvas.findByLabelText('Project slug');\n await userEvent.clear(slugInput);\n await userEvent.type(slugInput, 'renamed-platform');\n await canvas.findByText('Slug is available.');\n await userEvent.click(canvas.getByRole('button', {name: 'Save changes'}));\n\n const dialog = await screen.findByRole('dialog');\n expect(dialog).toHaveTextContent('Change project slug?');\n expect(dialog).toHaveTextContent('Links and bookmarks pointing at the old URL stop working.');\n expect(dialog).toHaveTextContent(\n 'Workflows that reference this project slug may stop working.',\n );\n },\n};\n\nfunction createStoryRouter(scenario: Scenario) {\n const project = projectForScenario(scenario);\n const rootRoute = createRootRoute({component: Outlet});\n const projectSettingsRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/w/$workspaceSlug/p/$projectSlug/settings/general',\n component: ProjectSettingsPage,\n });\n\n return createRouter({\n history: createMemoryHistory({\n initialEntries: [`/w/acme/p/${project.slug}/settings/general`],\n }),\n routeTree: rootRoute.addChildren([projectSettingsRoute]),\n });\n}\n\nfunction fetchForScenario(scenario: Scenario): typeof fetch {\n return async (input, init) => {\n const url = requestUrl(input);\n const method = input instanceof Request ? input.method : (init?.method ?? 'GET');\n\n if (url.pathname === '/projects' && method === 'GET') {\n const search = url.searchParams.get('search');\n if (scenario === 'taken-slug' && search === 'taken-platform') {\n return jsonResponse({\n projects: [projectForScenario(scenario, {id: OTHER_PROJECT_ID, slug: 'taken-platform'})],\n next_cursor: null,\n });\n }\n return jsonResponse({projects: [projectForScenario(scenario)], next_cursor: null});\n }\n\n if (url.pathname === `/projects/${PROJECT_ID}` && method === 'PATCH') {\n const body = (await (input as Request).clone().json()) as {\n name?: string;\n slug?: string;\n };\n const project = projectForScenario(scenario);\n return jsonResponse({\n ...project,\n name: body.name ?? project.name,\n slug: body.slug ?? project.slug,\n });\n }\n\n return jsonResponse({}, {status: 404});\n };\n}\n\nfunction projectForScenario(\n scenario: Scenario,\n overrides: Partial<ProjectStoryResponse> = {},\n): ProjectStoryResponse {\n const project = {\n id: PROJECT_ID,\n workspace_id: WORKSPACE_ID,\n name:\n scenario === 'long-values'\n ? 'Infrastructure Control Plane and Workflow Automation'\n : 'Platform',\n slug: scenario === 'long-values' ? 'infrastructure-control-plane' : 'platform',\n source: {\n connection_id: '33333333-3333-4333-8333-333333333333',\n external_repository_id: 'platform',\n },\n created_at: '2026-01-01T00:00:00.000Z',\n updated_at: '2026-01-01T00:00:00.000Z',\n ...overrides,\n };\n return project;\n}\n\nfunction requestUrl(input: RequestInfo | URL): URL {\n if (input instanceof Request) return new URL(input.url);\n if (input instanceof URL) return input;\n return new URL(input, 'https://api.example.test');\n}\n\nfunction jsonResponse(body: unknown, init: ResponseInit = {}) {\n return new Response(JSON.stringify(body), {\n status: 200,\n headers: {'content-type': 'application/json'},\n ...init,\n });\n}\n"],"names":["configureApiClient","authStateAtom","Toaster","QueryClient","QueryClientProvider","createMemoryHistory","createRootRoute","createRoute","createRouter","Outlet","RouterProvider","createStore","Provider","JotaiProvider","useMemo","expect","screen","userEvent","within","ProjectSettingsPage","WORKSPACE_ID","PROJECT_ID","OTHER_PROJECT_ID","authState","status","token","user","id","email","name","emailVerifiedAt","workspaces","slug","membershipId","ProjectSettingsPageStory","scenario","baseUrl","fetchImpl","fetchForScenario","queryClient","defaultOptions","queries","retry","store","nextStore","set","router","createStoryRouter","client","div","className","meta","title","component","parameters","layout","args","Playground","LongValues","TakenSlug","play","canvasElement","canvas","slugInput","findByLabelText","clear","type","findByText","toBeInTheDocument","SlugChangeWarning","click","getByRole","dialog","findByRole","toHaveTextContent","project","projectForScenario","rootRoute","projectSettingsRoute","getParentRoute","path","history","initialEntries","routeTree","addChildren","input","init","url","requestUrl","method","Request","pathname","search","searchParams","get","jsonResponse","projects","next_cursor","body","clone","json","overrides","workspace_id","source","connection_id","external_repository_id","created_at","updated_at","URL","Response","JSON","stringify","headers"],"mappings":";AAAA,SAAQA,kBAAkB,QAAO,sBAAsB;AACvD,SAAwBC,aAAa,QAAO,uBAAuB;AACnE,SAAQC,OAAO,QAAO,0BAA0B;AAEhD,SAAQC,WAAW,EAAEC,mBAAmB,QAAO,wBAAwB;AACvE,SACEC,mBAAmB,EACnBC,eAAe,EACfC,WAAW,EACXC,YAAY,EACZC,MAAM,EACNC,cAAc,QACT,yBAAyB;AAChC,SAAQC,WAAW,EAAEC,YAAYC,aAAa,QAAO,QAAQ;AAC7D,SAAQC,OAAO,QAAO,QAAQ;AAC9B,SAAQC,MAAM,EAAEC,MAAM,EAAEC,SAAS,EAAEC,MAAM,QAAO,iBAAiB;AACjE,SAAQC,mBAAmB,QAAO,6BAA6B;AAE/D,MAAMC,eAAe;AACrB,MAAMC,aAAa;AACnB,MAAMC,mBAAmB;AAqBzB,MAAMC,YAAuB;IAC3BC,QAAQ;IACRC,OAAO;IACPC,MAAM;QACJC,IAAI;QACJC,OAAO;QACPC,MAAM;QACNC,iBAAiB;IACnB;IACAC,YAAY;QAAC;YAACJ,IAAIP;YAAcS,MAAM;YAAQG,MAAM;YAAQC,cAAc;QAAc;KAAE;AAC5F;AAEA,SAASC,yBAAyB,EAACC,QAAQ,EAAgC;IACzEnC,mBAAmB;QACjBoC,SAAS;QACTC,WAAWC,iBAAiBH;IAC9B;IAEA,MAAMI,cAAczB,QAClB,IAAM,IAAIX,YAAY;YAACqC,gBAAgB;gBAACC,SAAS;oBAACC,OAAO;gBAAK;YAAC;QAAC,IAChE,EAAE;IAEJ,MAAMC,QAAQ7B,QAAQ;QACpB,MAAM8B,YAAYjC;QAClBiC,UAAUC,GAAG,CAAC5C,eAAesB;QAC7B,OAAOqB;IACT,GAAG,EAAE;IACL,MAAME,SAAShC,QAAQ,IAAMiC,kBAAkBZ,WAAW;QAACA;KAAS;IAEpE,qBACE,KAAC/B;QAAoB4C,QAAQT;kBAC3B,cAAA,MAAC1B;YAAc8B,OAAOA;;8BACpB,KAACM;oBAAIC,WAAU;8BACb,cAAA,KAACD;wBAAIC,WAAU;kCACb,cAAA,KAACxC;4BAAeoC,QAAQA;;;;8BAG5B,KAAC5C;;;;AAIT;AAEA,MAAMiD,OAAO;IACXC,OAAO;IACPC,WAAWnB;IACXoB,YAAY;QAACC,QAAQ;IAAY;IACjCC,MAAM;QAACrB,UAAU;IAAS;AAC5B;AAEA,eAAegB,KAAK;AAGpB,OAAO,MAAMM,aAAoB,CAAC,EAAE;AAEpC,OAAO,MAAMC,aAAoB;IAC/BF,MAAM;QAACrB,UAAU;IAAa;AAChC,EAAE;AAEF,OAAO,MAAMwB,YAAmB;IAC9BH,MAAM;QAACrB,UAAU;IAAY;IAC7ByB,MAAM,OAAO,EAACC,aAAa,EAAC;QAC1B,MAAMC,SAAS5C,OAAO2C;QACtB,MAAME,YAAY,MAAMD,OAAOE,eAAe,CAAC;QAC/C,MAAM/C,UAAUgD,KAAK,CAACF;QACtB,MAAM9C,UAAUiD,IAAI,CAACH,WAAW;QAEhChD,OAAO,MAAM+C,OAAOK,UAAU,CAAC,gCAAgCC,iBAAiB;IAClF;AACF,EAAE;AAEF,OAAO,MAAMC,oBAA2B;IACtCT,MAAM,OAAO,EAACC,aAAa,EAAC;QAC1B,MAAMC,SAAS5C,OAAO2C;QACtB,MAAME,YAAY,MAAMD,OAAOE,eAAe,CAAC;QAC/C,MAAM/C,UAAUgD,KAAK,CAACF;QACtB,MAAM9C,UAAUiD,IAAI,CAACH,WAAW;QAChC,MAAMD,OAAOK,UAAU,CAAC;QACxB,MAAMlD,UAAUqD,KAAK,CAACR,OAAOS,SAAS,CAAC,UAAU;YAAC1C,MAAM;QAAc;QAEtE,MAAM2C,SAAS,MAAMxD,OAAOyD,UAAU,CAAC;QACvC1D,OAAOyD,QAAQE,iBAAiB,CAAC;QACjC3D,OAAOyD,QAAQE,iBAAiB,CAAC;QACjC3D,OAAOyD,QAAQE,iBAAiB,CAC9B;IAEJ;AACF,EAAE;AAEF,SAAS3B,kBAAkBZ,QAAkB;IAC3C,MAAMwC,UAAUC,mBAAmBzC;IACnC,MAAM0C,YAAYvE,gBAAgB;QAAC+C,WAAW5C;IAAM;IACpD,MAAMqE,uBAAuBvE,YAAY;QACvCwE,gBAAgB,IAAMF;QACtBG,MAAM;QACN3B,WAAWlC;IACb;IAEA,OAAOX,aAAa;QAClByE,SAAS5E,oBAAoB;YAC3B6E,gBAAgB;gBAAC,CAAC,UAAU,EAAEP,QAAQ3C,IAAI,CAAC,iBAAiB,CAAC;aAAC;QAChE;QACAmD,WAAWN,UAAUO,WAAW,CAAC;YAACN;SAAqB;IACzD;AACF;AAEA,SAASxC,iBAAiBH,QAAkB;IAC1C,OAAO,OAAOkD,OAAOC;QACnB,MAAMC,MAAMC,WAAWH;QACvB,MAAMI,SAASJ,iBAAiBK,UAAUL,MAAMI,MAAM,GAAIH,MAAMG,UAAU;QAE1E,IAAIF,IAAII,QAAQ,KAAK,eAAeF,WAAW,OAAO;YACpD,MAAMG,SAASL,IAAIM,YAAY,CAACC,GAAG,CAAC;YACpC,IAAI3D,aAAa,gBAAgByD,WAAW,kBAAkB;gBAC5D,OAAOG,aAAa;oBAClBC,UAAU;wBAACpB,mBAAmBzC,UAAU;4BAACR,IAAIL;4BAAkBU,MAAM;wBAAgB;qBAAG;oBACxFiE,aAAa;gBACf;YACF;YACA,OAAOF,aAAa;gBAACC,UAAU;oBAACpB,mBAAmBzC;iBAAU;gBAAE8D,aAAa;YAAI;QAClF;QAEA,IAAIV,IAAII,QAAQ,KAAK,CAAC,UAAU,EAAEtE,YAAY,IAAIoE,WAAW,SAAS;YACpE,MAAMS,OAAQ,MAAM,AAACb,MAAkBc,KAAK,GAAGC,IAAI;YAInD,MAAMzB,UAAUC,mBAAmBzC;YACnC,OAAO4D,aAAa;gBAClB,GAAGpB,OAAO;gBACV9C,MAAMqE,KAAKrE,IAAI,IAAI8C,QAAQ9C,IAAI;gBAC/BG,MAAMkE,KAAKlE,IAAI,IAAI2C,QAAQ3C,IAAI;YACjC;QACF;QAEA,OAAO+D,aAAa,CAAC,GAAG;YAACvE,QAAQ;QAAG;IACtC;AACF;AAEA,SAASoD,mBACPzC,QAAkB,EAClBkE,YAA2C,CAAC,CAAC;IAE7C,MAAM1B,UAAU;QACdhD,IAAIN;QACJiF,cAAclF;QACdS,MACEM,aAAa,gBACT,yDACA;QACNH,MAAMG,aAAa,gBAAgB,iCAAiC;QACpEoE,QAAQ;YACNC,eAAe;YACfC,wBAAwB;QAC1B;QACAC,YAAY;QACZC,YAAY;QACZ,GAAGN,SAAS;IACd;IACA,OAAO1B;AACT;AAEA,SAASa,WAAWH,KAAwB;IAC1C,IAAIA,iBAAiBK,SAAS,OAAO,IAAIkB,IAAIvB,MAAME,GAAG;IACtD,IAAIF,iBAAiBuB,KAAK,OAAOvB;IACjC,OAAO,IAAIuB,IAAIvB,OAAO;AACxB;AAEA,SAASU,aAAaG,IAAa,EAAEZ,OAAqB,CAAC,CAAC;IAC1D,OAAO,IAAIuB,SAASC,KAAKC,SAAS,CAACb,OAAO;QACxC1E,QAAQ;QACRwF,SAAS;YAAC,gBAAgB;QAAkB;QAC5C,GAAG1B,IAAI;IACT;AACF"}
@@ -4,10 +4,10 @@ import { ConnectionStatusBadge, IntegrationIcon, useIntegrationConnectionsQuery
4
4
  import { QueryLoadError } from '@shipfox/client-ui';
5
5
  import { Button } from '@shipfox/react-ui/button';
6
6
  import { Callout } from '@shipfox/react-ui/callout';
7
- import { Card } from '@shipfox/react-ui/card';
8
7
  import { EmptyState } from '@shipfox/react-ui/empty-state';
9
8
  import { Icon } from '@shipfox/react-ui/icon';
10
9
  import { Input } from '@shipfox/react-ui/input';
10
+ import { Panel } from '@shipfox/react-ui/panel';
11
11
  import { Skeleton } from '@shipfox/react-ui/skeleton';
12
12
  import { Header, Text } from '@shipfox/react-ui/typography';
13
13
  import { Link, useNavigate } from '@tanstack/react-router';
@@ -154,7 +154,7 @@ function ProjectsSkeleton() {
154
154
  4,
155
155
  5
156
156
  ].map((row)=>/*#__PURE__*/ _jsx("li", {
157
- children: /*#__PURE__*/ _jsx(Card, {
157
+ children: /*#__PURE__*/ _jsx(Panel, {
158
158
  className: "h-full p-panel-compact",
159
159
  children: /*#__PURE__*/ _jsxs("div", {
160
160
  className: "flex items-center gap-cluster",
@@ -215,7 +215,7 @@ function ProjectCard({ project, connection, connectionsResolved, connectionsSett
215
215
  projectSlug: project.slug
216
216
  },
217
217
  className: "block h-full rounded-8 focus-visible:shadow-button-neutral-focus focus-visible:outline-none",
218
- children: /*#__PURE__*/ _jsx(Card, {
218
+ children: /*#__PURE__*/ _jsx(Panel, {
219
219
  className: "h-full p-panel-compact transition-colors hover:bg-background-components-hover",
220
220
  children: /*#__PURE__*/ _jsxs("div", {
221
221
  className: "flex min-w-0 items-center gap-cluster",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/pages/projects-hub-page.tsx"],"sourcesContent":["import {useActiveWorkspace} from '@shipfox/client-auth';\nimport {\n ConnectionStatusBadge,\n type IntegrationConnection,\n IntegrationIcon,\n useIntegrationConnectionsQuery,\n} from '@shipfox/client-integrations';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {Card} from '@shipfox/react-ui/card';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Input} from '@shipfox/react-ui/input';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Header, Text} from '@shipfox/react-ui/typography';\nimport {Link, useNavigate} from '@tanstack/react-router';\nimport {ModelProviderReminderBanner} from '#components/model-provider-reminder-banner.js';\nimport type {Project} from '#core/project.js';\nimport {useProjectsInfiniteQuery} from '#hooks/api/projects.js';\n\nexport function ProjectsHubPage({search = ''}: {search?: string}) {\n const workspace = useActiveWorkspace();\n const navigate = useNavigate();\n const query = useProjectsInfiniteQuery(workspace.id, search || undefined);\n const projects = query.data?.pages.flatMap((page) => page.projects) ?? [];\n\n // The provider logo and connection health live on the integration connection,\n // not the project, so resolve them once for the whole list and index by id.\n // Skip the fetch when there are no cards to annotate.\n const connectionsQuery = useIntegrationConnectionsQuery(\n projects.length > 0 ? workspace.id : undefined,\n );\n const connectionsById = new Map(\n (connectionsQuery.data ?? []).map((connection) => [connection.id, connection]),\n );\n\n const isInitialLoading = query.isPending;\n const isSearching = Boolean(search) && query.isFetching && !query.isFetchingNextPage;\n const hasNoData = !query.data;\n\n return (\n <div className=\"flex w-full flex-col gap-section\">\n <header className=\"flex flex-col gap-group\">\n <div className=\"flex items-start justify-between gap-section max-[640px]:flex-col\">\n <Header variant=\"h2\">Projects</Header>\n </div>\n <div className=\"flex items-center gap-cluster max-[640px]:flex-col max-[640px]:items-stretch\">\n <Input\n type=\"search\"\n value={search}\n onChange={(event) => {\n const next = event.target.value.trim();\n navigate({search: (next ? {search: next} : {}) as never, replace: true});\n }}\n placeholder=\"Search projects…\"\n aria-label=\"Search projects\"\n className=\"flex-1\"\n iconLeft={<Icon name=\"searchLine\" className=\"size-16\" />}\n iconRight={\n isSearching ? (\n <Icon\n name=\"spinner\"\n size={16}\n className=\"text-foreground-neutral-muted\"\n aria-hidden=\"true\"\n />\n ) : undefined\n }\n />\n <Button asChild iconLeft=\"addLine\" className=\"shrink-0 max-[640px]:w-full\">\n <Link to=\"/w/$workspaceSlug/projects/new\" params={{workspaceSlug: workspace.slug}}>\n New project\n </Link>\n </Button>\n </div>\n </header>\n\n <ModelProviderReminderBanner workspaceId={workspace.id} />\n\n {isInitialLoading || (search && hasNoData && query.isFetching) ? <ProjectsSkeleton /> : null}\n\n {query.isError && hasNoData ? <QueryLoadError query={query} subject=\"projects\" /> : null}\n\n {!isInitialLoading && !query.isError && projects.length === 0 && !search ? (\n <EmptyProjects workspaceSlug={workspace.slug} />\n ) : null}\n\n {!query.isFetching && !query.isError && projects.length === 0 && search ? (\n <NoSearchResults\n search={search}\n onClear={() => navigate({search: {} as never, replace: true})}\n />\n ) : null}\n\n {projects.length > 0 ? (\n <section className=\"flex flex-col gap-group\" aria-label=\"Projects list\">\n <ul className=\"grid grid-cols-2 gap-cluster max-[760px]:grid-cols-1\">\n {projects.map((project) => (\n <ProjectCard\n project={project}\n connection={connectionsById.get(project.source.connectionId)}\n connectionsResolved={connectionsQuery.isSuccess}\n connectionsSettled={connectionsQuery.isSuccess || connectionsQuery.isError}\n key={project.id}\n workspaceSlug={workspace.slug}\n />\n ))}\n </ul>\n {query.error && query.data ? (\n <Callout role=\"alert\" type=\"error\">\n <Text size=\"sm\">\n Could not load the next page. Existing projects are still shown.\n </Text>\n </Callout>\n ) : null}\n {query.hasNextPage ? (\n <div className=\"flex justify-center\">\n <Button\n variant=\"secondary\"\n isLoading={query.isFetchingNextPage}\n onClick={() => query.fetchNextPage()}\n >\n Load more\n </Button>\n </div>\n ) : null}\n </section>\n ) : null}\n </div>\n );\n}\n\nfunction ProjectsSkeleton() {\n return (\n <ul\n role=\"status\"\n aria-label=\"Loading projects\"\n className=\"grid grid-cols-2 gap-cluster max-[760px]:grid-cols-1\"\n >\n {[0, 1, 2, 3, 4, 5].map((row) => (\n <li key={row}>\n <Card className=\"h-full p-panel-compact\">\n <div className=\"flex items-center gap-cluster\">\n <Skeleton className=\"size-24 shrink-0\" />\n <Skeleton className=\"h-16 w-1/2\" />\n </div>\n </Card>\n </li>\n ))}\n </ul>\n );\n}\n\nfunction EmptyProjects({workspaceSlug}: {workspaceSlug: string}) {\n return (\n <EmptyState\n icon=\"folderLine\"\n title=\"Create your first project\"\n description=\"Connect a repository-backed project to start building workflows.\"\n action={\n <Button asChild iconRight=\"chevronRight\">\n <Link to=\"/w/$workspaceSlug/projects/new\" params={{workspaceSlug}}>\n Create project\n </Link>\n </Button>\n }\n />\n );\n}\n\nfunction NoSearchResults({search, onClear}: {search: string; onClear: () => void}) {\n return (\n <EmptyState\n icon=\"searchLine\"\n title={`No projects match “${search}”`}\n description=\"Try a different search, or clear it to see all projects.\"\n action={\n <Button size=\"sm\" variant=\"secondary\" onClick={onClear}>\n Clear search\n </Button>\n }\n />\n );\n}\n\nfunction ProjectCard({\n project,\n connection,\n connectionsResolved,\n connectionsSettled,\n workspaceSlug,\n}: {\n project: Project;\n connection: IntegrationConnection | undefined;\n connectionsResolved: boolean;\n connectionsSettled: boolean;\n workspaceSlug: string;\n}) {\n // On a resolved fetch, `active` carries no badge while a missing connection\n // reads as an error so a broken source is still flagged. An unresolved or\n // failed fetch shows nothing, so a fetch failure never flags every card.\n const status = connectionsResolved ? (connection?.lifecycleStatus ?? 'error') : undefined;\n\n return (\n <li>\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug\"\n params={{workspaceSlug, projectSlug: project.slug}}\n className=\"block h-full rounded-8 focus-visible:shadow-button-neutral-focus focus-visible:outline-none\"\n >\n <Card className=\"h-full p-panel-compact transition-colors hover:bg-background-components-hover\">\n <div className=\"flex min-w-0 items-center gap-cluster\">\n {/* Settle on success or error: a failed fetch falls back to the\n neutral provider icon rather than spinning forever. */}\n {connectionsSettled ? (\n <IntegrationIcon\n source={connection?.provider}\n aria-hidden\n className=\"size-24 shrink-0 text-foreground-neutral-base\"\n />\n ) : (\n <Skeleton className=\"size-24 shrink-0\" />\n )}\n <div className=\"flex min-w-0 flex-1 items-center gap-inline\">\n <Text size=\"md\" bold className=\"truncate\">\n {project.name}\n </Text>\n {status ? <ConnectionStatusBadge status={status} className=\"shrink-0\" /> : null}\n </div>\n </div>\n </Card>\n </Link>\n </li>\n );\n}\n"],"names":["useActiveWorkspace","ConnectionStatusBadge","IntegrationIcon","useIntegrationConnectionsQuery","QueryLoadError","Button","Callout","Card","EmptyState","Icon","Input","Skeleton","Header","Text","Link","useNavigate","ModelProviderReminderBanner","useProjectsInfiniteQuery","ProjectsHubPage","search","workspace","navigate","query","id","undefined","projects","data","pages","flatMap","page","connectionsQuery","length","connectionsById","Map","map","connection","isInitialLoading","isPending","isSearching","Boolean","isFetching","isFetchingNextPage","hasNoData","div","className","header","variant","type","value","onChange","event","next","target","trim","replace","placeholder","aria-label","iconLeft","name","iconRight","size","aria-hidden","asChild","to","params","workspaceSlug","slug","workspaceId","ProjectsSkeleton","isError","subject","EmptyProjects","NoSearchResults","onClear","section","ul","project","ProjectCard","get","source","connectionId","connectionsResolved","isSuccess","connectionsSettled","error","role","hasNextPage","isLoading","onClick","fetchNextPage","row","li","icon","title","description","action","status","lifecycleStatus","projectSlug","provider","bold"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/pages/projects-hub-page.tsx"],"sourcesContent":["import {useActiveWorkspace} from '@shipfox/client-auth';\nimport {\n ConnectionStatusBadge,\n type IntegrationConnection,\n IntegrationIcon,\n useIntegrationConnectionsQuery,\n} from '@shipfox/client-integrations';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Input} from '@shipfox/react-ui/input';\nimport {Panel} from '@shipfox/react-ui/panel';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Header, Text} from '@shipfox/react-ui/typography';\nimport {Link, useNavigate} from '@tanstack/react-router';\nimport {ModelProviderReminderBanner} from '#components/model-provider-reminder-banner.js';\nimport type {Project} from '#core/project.js';\nimport {useProjectsInfiniteQuery} from '#hooks/api/projects.js';\n\nexport function ProjectsHubPage({search = ''}: {search?: string}) {\n const workspace = useActiveWorkspace();\n const navigate = useNavigate();\n const query = useProjectsInfiniteQuery(workspace.id, search || undefined);\n const projects = query.data?.pages.flatMap((page) => page.projects) ?? [];\n\n // The provider logo and connection health live on the integration connection,\n // not the project, so resolve them once for the whole list and index by id.\n // Skip the fetch when there are no cards to annotate.\n const connectionsQuery = useIntegrationConnectionsQuery(\n projects.length > 0 ? workspace.id : undefined,\n );\n const connectionsById = new Map(\n (connectionsQuery.data ?? []).map((connection) => [connection.id, connection]),\n );\n\n const isInitialLoading = query.isPending;\n const isSearching = Boolean(search) && query.isFetching && !query.isFetchingNextPage;\n const hasNoData = !query.data;\n\n return (\n <div className=\"flex w-full flex-col gap-section\">\n <header className=\"flex flex-col gap-group\">\n <div className=\"flex items-start justify-between gap-section max-[640px]:flex-col\">\n <Header variant=\"h2\">Projects</Header>\n </div>\n <div className=\"flex items-center gap-cluster max-[640px]:flex-col max-[640px]:items-stretch\">\n <Input\n type=\"search\"\n value={search}\n onChange={(event) => {\n const next = event.target.value.trim();\n navigate({search: (next ? {search: next} : {}) as never, replace: true});\n }}\n placeholder=\"Search projects…\"\n aria-label=\"Search projects\"\n className=\"flex-1\"\n iconLeft={<Icon name=\"searchLine\" className=\"size-16\" />}\n iconRight={\n isSearching ? (\n <Icon\n name=\"spinner\"\n size={16}\n className=\"text-foreground-neutral-muted\"\n aria-hidden=\"true\"\n />\n ) : undefined\n }\n />\n <Button asChild iconLeft=\"addLine\" className=\"shrink-0 max-[640px]:w-full\">\n <Link to=\"/w/$workspaceSlug/projects/new\" params={{workspaceSlug: workspace.slug}}>\n New project\n </Link>\n </Button>\n </div>\n </header>\n\n <ModelProviderReminderBanner workspaceId={workspace.id} />\n\n {isInitialLoading || (search && hasNoData && query.isFetching) ? <ProjectsSkeleton /> : null}\n\n {query.isError && hasNoData ? <QueryLoadError query={query} subject=\"projects\" /> : null}\n\n {!isInitialLoading && !query.isError && projects.length === 0 && !search ? (\n <EmptyProjects workspaceSlug={workspace.slug} />\n ) : null}\n\n {!query.isFetching && !query.isError && projects.length === 0 && search ? (\n <NoSearchResults\n search={search}\n onClear={() => navigate({search: {} as never, replace: true})}\n />\n ) : null}\n\n {projects.length > 0 ? (\n <section className=\"flex flex-col gap-group\" aria-label=\"Projects list\">\n <ul className=\"grid grid-cols-2 gap-cluster max-[760px]:grid-cols-1\">\n {projects.map((project) => (\n <ProjectCard\n project={project}\n connection={connectionsById.get(project.source.connectionId)}\n connectionsResolved={connectionsQuery.isSuccess}\n connectionsSettled={connectionsQuery.isSuccess || connectionsQuery.isError}\n key={project.id}\n workspaceSlug={workspace.slug}\n />\n ))}\n </ul>\n {query.error && query.data ? (\n <Callout role=\"alert\" type=\"error\">\n <Text size=\"sm\">\n Could not load the next page. Existing projects are still shown.\n </Text>\n </Callout>\n ) : null}\n {query.hasNextPage ? (\n <div className=\"flex justify-center\">\n <Button\n variant=\"secondary\"\n isLoading={query.isFetchingNextPage}\n onClick={() => query.fetchNextPage()}\n >\n Load more\n </Button>\n </div>\n ) : null}\n </section>\n ) : null}\n </div>\n );\n}\n\nfunction ProjectsSkeleton() {\n return (\n <ul\n role=\"status\"\n aria-label=\"Loading projects\"\n className=\"grid grid-cols-2 gap-cluster max-[760px]:grid-cols-1\"\n >\n {[0, 1, 2, 3, 4, 5].map((row) => (\n <li key={row}>\n <Panel className=\"h-full p-panel-compact\">\n <div className=\"flex items-center gap-cluster\">\n <Skeleton className=\"size-24 shrink-0\" />\n <Skeleton className=\"h-16 w-1/2\" />\n </div>\n </Panel>\n </li>\n ))}\n </ul>\n );\n}\n\nfunction EmptyProjects({workspaceSlug}: {workspaceSlug: string}) {\n return (\n <EmptyState\n icon=\"folderLine\"\n title=\"Create your first project\"\n description=\"Connect a repository-backed project to start building workflows.\"\n action={\n <Button asChild iconRight=\"chevronRight\">\n <Link to=\"/w/$workspaceSlug/projects/new\" params={{workspaceSlug}}>\n Create project\n </Link>\n </Button>\n }\n />\n );\n}\n\nfunction NoSearchResults({search, onClear}: {search: string; onClear: () => void}) {\n return (\n <EmptyState\n icon=\"searchLine\"\n title={`No projects match “${search}”`}\n description=\"Try a different search, or clear it to see all projects.\"\n action={\n <Button size=\"sm\" variant=\"secondary\" onClick={onClear}>\n Clear search\n </Button>\n }\n />\n );\n}\n\nfunction ProjectCard({\n project,\n connection,\n connectionsResolved,\n connectionsSettled,\n workspaceSlug,\n}: {\n project: Project;\n connection: IntegrationConnection | undefined;\n connectionsResolved: boolean;\n connectionsSettled: boolean;\n workspaceSlug: string;\n}) {\n // On a resolved fetch, `active` carries no badge while a missing connection\n // reads as an error so a broken source is still flagged. An unresolved or\n // failed fetch shows nothing, so a fetch failure never flags every card.\n const status = connectionsResolved ? (connection?.lifecycleStatus ?? 'error') : undefined;\n\n return (\n <li>\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug\"\n params={{workspaceSlug, projectSlug: project.slug}}\n className=\"block h-full rounded-8 focus-visible:shadow-button-neutral-focus focus-visible:outline-none\"\n >\n <Panel className=\"h-full p-panel-compact transition-colors hover:bg-background-components-hover\">\n <div className=\"flex min-w-0 items-center gap-cluster\">\n {/* Settle on success or error: a failed fetch falls back to the\n neutral provider icon rather than spinning forever. */}\n {connectionsSettled ? (\n <IntegrationIcon\n source={connection?.provider}\n aria-hidden\n className=\"size-24 shrink-0 text-foreground-neutral-base\"\n />\n ) : (\n <Skeleton className=\"size-24 shrink-0\" />\n )}\n <div className=\"flex min-w-0 flex-1 items-center gap-inline\">\n <Text size=\"md\" bold className=\"truncate\">\n {project.name}\n </Text>\n {status ? <ConnectionStatusBadge status={status} className=\"shrink-0\" /> : null}\n </div>\n </div>\n </Panel>\n </Link>\n </li>\n );\n}\n"],"names":["useActiveWorkspace","ConnectionStatusBadge","IntegrationIcon","useIntegrationConnectionsQuery","QueryLoadError","Button","Callout","EmptyState","Icon","Input","Panel","Skeleton","Header","Text","Link","useNavigate","ModelProviderReminderBanner","useProjectsInfiniteQuery","ProjectsHubPage","search","workspace","navigate","query","id","undefined","projects","data","pages","flatMap","page","connectionsQuery","length","connectionsById","Map","map","connection","isInitialLoading","isPending","isSearching","Boolean","isFetching","isFetchingNextPage","hasNoData","div","className","header","variant","type","value","onChange","event","next","target","trim","replace","placeholder","aria-label","iconLeft","name","iconRight","size","aria-hidden","asChild","to","params","workspaceSlug","slug","workspaceId","ProjectsSkeleton","isError","subject","EmptyProjects","NoSearchResults","onClear","section","ul","project","ProjectCard","get","source","connectionId","connectionsResolved","isSuccess","connectionsSettled","error","role","hasNextPage","isLoading","onClick","fetchNextPage","row","li","icon","title","description","action","status","lifecycleStatus","projectSlug","provider","bold"],"mappings":"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"}
@@ -1,5 +1,8 @@
1
1
  import { CreateProjectPage } from '#pages/create-project-page.js';
2
2
  declare const _default: import("@shipfox/client-shell/runtime").RouteImpl<{
3
+ readonly staticData: {
4
+ readonly frame: "content";
5
+ };
3
6
  readonly component: typeof CreateProjectPage;
4
7
  }>;
5
8
  export default _default;
@@ -1,6 +1,9 @@
1
1
  import { defineRoute } from '@shipfox/client-shell/runtime';
2
2
  import { CreateProjectPage } from '#pages/create-project-page.js';
3
3
  export default defineRoute({
4
+ staticData: {
5
+ frame: 'content'
6
+ },
4
7
  component: CreateProjectPage
5
8
  });
6
9
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/routes/create-project.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {CreateProjectPage} from '#pages/create-project-page.js';\n\nexport default defineRoute({component: CreateProjectPage});\n"],"names":["defineRoute","CreateProjectPage","component"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,iBAAiB,QAAO,gCAAgC;AAEhE,eAAeD,YAAY;IAACE,WAAWD;AAAiB,GAAG"}
1
+ {"version":3,"sources":["../../src/routes/create-project.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {CreateProjectPage} from '#pages/create-project-page.js';\n\nexport default defineRoute({\n staticData: {frame: 'content'},\n component: CreateProjectPage,\n});\n"],"names":["defineRoute","CreateProjectPage","staticData","frame","component"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,iBAAiB,QAAO,gCAAgC;AAEhE,eAAeD,YAAY;IACzBE,YAAY;QAACC,OAAO;IAAS;IAC7BC,WAAWH;AACb,GAAG"}
@@ -1,5 +1,8 @@
1
1
  import { validateProjectsSearch } from './search.js';
2
2
  declare const _default: import("@shipfox/client-shell/runtime").RouteImpl<{
3
+ readonly staticData: {
4
+ readonly frame: "content";
5
+ };
3
6
  readonly validateSearch: typeof validateProjectsSearch;
4
7
  readonly component: () => import("react").JSX.Element;
5
8
  }>;
@@ -3,6 +3,9 @@ import { defineRoute, useRouteSearch } from '@shipfox/client-shell/runtime';
3
3
  import { HomeRouter } from '#pages/home-router.js';
4
4
  import { validateProjectsSearch } from './search.js';
5
5
  export default defineRoute({
6
+ staticData: {
7
+ frame: 'content'
8
+ },
6
9
  validateSearch: validateProjectsSearch,
7
10
  component: ()=>/*#__PURE__*/ _jsx(HomeRouter, {
8
11
  search: useRouteSearch(validateProjectsSearch)
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/routes/home.tsx"],"sourcesContent":["import {defineRoute, useRouteSearch} from '@shipfox/client-shell/runtime';\nimport {HomeRouter} from '#pages/home-router.js';\nimport {validateProjectsSearch} from './search.js';\n\nexport default defineRoute({\n validateSearch: validateProjectsSearch,\n component: () => <HomeRouter search={useRouteSearch(validateProjectsSearch)} />,\n});\n"],"names":["defineRoute","useRouteSearch","HomeRouter","validateProjectsSearch","validateSearch","component","search"],"mappings":";AAAA,SAAQA,WAAW,EAAEC,cAAc,QAAO,gCAAgC;AAC1E,SAAQC,UAAU,QAAO,wBAAwB;AACjD,SAAQC,sBAAsB,QAAO,cAAc;AAEnD,eAAeH,YAAY;IACzBI,gBAAgBD;IAChBE,WAAW,kBAAM,KAACH;YAAWI,QAAQL,eAAeE;;AACtD,GAAG"}
1
+ {"version":3,"sources":["../../src/routes/home.tsx"],"sourcesContent":["import {defineRoute, useRouteSearch} from '@shipfox/client-shell/runtime';\nimport {HomeRouter} from '#pages/home-router.js';\nimport {validateProjectsSearch} from './search.js';\n\nexport default defineRoute({\n staticData: {frame: 'content'},\n validateSearch: validateProjectsSearch,\n component: () => <HomeRouter search={useRouteSearch(validateProjectsSearch)} />,\n});\n"],"names":["defineRoute","useRouteSearch","HomeRouter","validateProjectsSearch","staticData","frame","validateSearch","component","search"],"mappings":";AAAA,SAAQA,WAAW,EAAEC,cAAc,QAAO,gCAAgC;AAC1E,SAAQC,UAAU,QAAO,wBAAwB;AACjD,SAAQC,sBAAsB,QAAO,cAAc;AAEnD,eAAeH,YAAY;IACzBI,YAAY;QAACC,OAAO;IAAS;IAC7BC,gBAAgBH;IAChBI,WAAW,kBAAM,KAACL;YAAWM,QAAQP,eAAeE;;AACtD,GAAG"}
@@ -1,4 +1,7 @@
1
1
  declare const _default: import("@shipfox/client-shell/runtime").RouteImpl<{
2
+ readonly staticData: {
3
+ readonly frame: "content";
4
+ };
2
5
  readonly beforeLoad: ({ params }: {
3
6
  params: {
4
7
  workspaceSlug: string;
@@ -1 +1 @@
1
- {"version":3,"file":"project-index.d.ts","sourceRoot":"","sources":["../../src/routes/project-index.tsx"],"names":[],"mappings":";sCAIyB;QAAC,MAAM,EAAE;YAAC,aAAa,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAC,CAAA;KAAC"}
1
+ {"version":3,"file":"project-index.d.ts","sourceRoot":"","sources":["../../src/routes/project-index.tsx"],"names":[],"mappings":";;;;sCAKyB;QAAC,MAAM,EAAE;YAAC,aAAa,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAC,CAAA;KAAC"}
@@ -1,6 +1,9 @@
1
1
  import { defineRoute } from '@shipfox/client-shell/runtime';
2
2
  import { redirect } from '@tanstack/react-router';
3
3
  export default defineRoute({
4
+ staticData: {
5
+ frame: 'content'
6
+ },
4
7
  beforeLoad: ({ params })=>{
5
8
  throw redirect({
6
9
  to: '/w/$workspaceSlug/p/$projectSlug/runs',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/routes/project-index.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {redirect} from '@tanstack/react-router';\n\nexport default defineRoute({\n beforeLoad: ({params}: {params: {workspaceSlug: string; projectSlug: string}}) => {\n throw redirect({\n to: '/w/$workspaceSlug/p/$projectSlug/runs',\n params: {workspaceSlug: params.workspaceSlug, projectSlug: params.projectSlug},\n });\n },\n});\n"],"names":["defineRoute","redirect","beforeLoad","params","to","workspaceSlug","projectSlug"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,QAAQ,QAAO,yBAAyB;AAEhD,eAAeD,YAAY;IACzBE,YAAY,CAAC,EAACC,MAAM,EAAyD;QAC3E,MAAMF,SAAS;YACbG,IAAI;YACJD,QAAQ;gBAACE,eAAeF,OAAOE,aAAa;gBAAEC,aAAaH,OAAOG,WAAW;YAAA;QAC/E;IACF;AACF,GAAG"}
1
+ {"version":3,"sources":["../../src/routes/project-index.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {redirect} from '@tanstack/react-router';\n\nexport default defineRoute({\n staticData: {frame: 'content'},\n beforeLoad: ({params}: {params: {workspaceSlug: string; projectSlug: string}}) => {\n throw redirect({\n to: '/w/$workspaceSlug/p/$projectSlug/runs',\n params: {workspaceSlug: params.workspaceSlug, projectSlug: params.projectSlug},\n });\n },\n});\n"],"names":["defineRoute","redirect","staticData","frame","beforeLoad","params","to","workspaceSlug","projectSlug"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,QAAQ,QAAO,yBAAyB;AAEhD,eAAeD,YAAY;IACzBE,YAAY;QAACC,OAAO;IAAS;IAC7BC,YAAY,CAAC,EAACC,MAAM,EAAyD;QAC3E,MAAMJ,SAAS;YACbK,IAAI;YACJD,QAAQ;gBAACE,eAAeF,OAAOE,aAAa;gBAAEC,aAAaH,OAAOG,WAAW;YAAA;QAC/E;IACF;AACF,GAAG"}
@@ -1,4 +1,7 @@
1
1
  declare const _default: import("@shipfox/client-shell/runtime").RouteImpl<{
2
+ readonly staticData: {
3
+ readonly frame: "content";
4
+ };
2
5
  readonly beforeLoad: ({ params }: {
3
6
  params: {
4
7
  workspaceSlug: string;
@@ -1 +1 @@
1
- {"version":3,"file":"project-settings-index.d.ts","sourceRoot":"","sources":["../../src/routes/project-settings-index.tsx"],"names":[],"mappings":";sCAIyB;QAAC,MAAM,EAAE;YAAC,aAAa,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAC,CAAA;KAAC"}
1
+ {"version":3,"file":"project-settings-index.d.ts","sourceRoot":"","sources":["../../src/routes/project-settings-index.tsx"],"names":[],"mappings":";;;;sCAKyB;QAAC,MAAM,EAAE;YAAC,aAAa,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAC,CAAA;KAAC"}
@@ -1,6 +1,9 @@
1
1
  import { defineRoute } from '@shipfox/client-shell/runtime';
2
2
  import { redirect } from '@tanstack/react-router';
3
3
  export default defineRoute({
4
+ staticData: {
5
+ frame: 'content'
6
+ },
4
7
  beforeLoad: ({ params })=>{
5
8
  throw redirect({
6
9
  to: '/w/$workspaceSlug/p/$projectSlug/settings/general',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/routes/project-settings-index.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {redirect} from '@tanstack/react-router';\n\nexport default defineRoute({\n beforeLoad: ({params}: {params: {workspaceSlug: string; projectSlug: string}}) => {\n throw redirect({\n to: '/w/$workspaceSlug/p/$projectSlug/settings/general',\n params,\n });\n },\n});\n"],"names":["defineRoute","redirect","beforeLoad","params","to"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,QAAQ,QAAO,yBAAyB;AAEhD,eAAeD,YAAY;IACzBE,YAAY,CAAC,EAACC,MAAM,EAAyD;QAC3E,MAAMF,SAAS;YACbG,IAAI;YACJD;QACF;IACF;AACF,GAAG"}
1
+ {"version":3,"sources":["../../src/routes/project-settings-index.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {redirect} from '@tanstack/react-router';\n\nexport default defineRoute({\n staticData: {frame: 'content'},\n beforeLoad: ({params}: {params: {workspaceSlug: string; projectSlug: string}}) => {\n throw redirect({\n to: '/w/$workspaceSlug/p/$projectSlug/settings/general',\n params,\n });\n },\n});\n"],"names":["defineRoute","redirect","staticData","frame","beforeLoad","params","to"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,QAAQ,QAAO,yBAAyB;AAEhD,eAAeD,YAAY;IACzBE,YAAY;QAACC,OAAO;IAAS;IAC7BC,YAAY,CAAC,EAACC,MAAM,EAAyD;QAC3E,MAAMJ,SAAS;YACbK,IAAI;YACJD;QACF;IACF;AACF,GAAG"}
@@ -1,5 +1,8 @@
1
1
  import { ProjectSettingsPage } from '#pages/project-settings-page.js';
2
2
  declare const _default: import("@shipfox/client-shell/runtime").RouteImpl<{
3
+ readonly staticData: {
4
+ readonly frame: "content";
5
+ };
3
6
  readonly component: typeof ProjectSettingsPage;
4
7
  }>;
5
8
  export default _default;
@@ -1,6 +1,9 @@
1
1
  import { defineRoute } from '@shipfox/client-shell/runtime';
2
2
  import { ProjectSettingsPage } from '#pages/project-settings-page.js';
3
3
  export default defineRoute({
4
+ staticData: {
5
+ frame: 'content'
6
+ },
4
7
  component: ProjectSettingsPage
5
8
  });
6
9
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/routes/project-settings.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {ProjectSettingsPage} from '#pages/project-settings-page.js';\n\nexport default defineRoute({component: ProjectSettingsPage});\n"],"names":["defineRoute","ProjectSettingsPage","component"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,mBAAmB,QAAO,kCAAkC;AAEpE,eAAeD,YAAY;IAACE,WAAWD;AAAmB,GAAG"}
1
+ {"version":3,"sources":["../../src/routes/project-settings.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {ProjectSettingsPage} from '#pages/project-settings-page.js';\n\nexport default defineRoute({\n staticData: {frame: 'content'},\n component: ProjectSettingsPage,\n});\n"],"names":["defineRoute","ProjectSettingsPage","staticData","frame","component"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,mBAAmB,QAAO,kCAAkC;AAEpE,eAAeD,YAAY;IACzBE,YAAY;QAACC,OAAO;IAAS;IAC7BC,WAAWH;AACb,GAAG"}