@shipfox/client-projects 12.0.2 → 14.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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +64 -0
- package/dist/chrome.d.ts +5 -1
- package/dist/chrome.d.ts.map +1 -1
- package/dist/chrome.js +18 -26
- package/dist/chrome.js.map +1 -1
- package/dist/components/model-provider-reminder-banner.d.ts.map +1 -1
- package/dist/components/model-provider-reminder-banner.js +4 -2
- package/dist/components/model-provider-reminder-banner.js.map +1 -1
- package/dist/components/project-crumb.d.ts +3 -1
- package/dist/components/project-crumb.d.ts.map +1 -1
- package/dist/components/project-crumb.js +7 -5
- package/dist/components/project-crumb.js.map +1 -1
- package/dist/components/project-switcher.d.ts +2 -1
- package/dist/components/project-switcher.d.ts.map +1 -1
- package/dist/components/project-switcher.js +10 -8
- package/dist/components/project-switcher.js.map +1 -1
- package/dist/core/definition.d.ts +6 -0
- package/dist/core/definition.d.ts.map +1 -1
- package/dist/core/definition.js.map +1 -1
- package/dist/core/project.d.ts +7 -0
- package/dist/core/project.d.ts.map +1 -1
- package/dist/core/project.js.map +1 -1
- package/dist/feature.d.ts +29 -5
- package/dist/feature.d.ts.map +1 -1
- package/dist/feature.js +26 -5
- package/dist/feature.js.map +1 -1
- package/dist/hooks/api/mappers.d.ts.map +1 -1
- package/dist/hooks/api/mappers.js +9 -1
- package/dist/hooks/api/mappers.js.map +1 -1
- package/dist/hooks/api/projects.d.ts +21 -2
- package/dist/hooks/api/projects.d.ts.map +1 -1
- package/dist/hooks/api/projects.js +175 -2
- package/dist/hooks/api/projects.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/pages/create-project-page.d.ts.map +1 -1
- package/dist/pages/create-project-page.js +97 -21
- package/dist/pages/create-project-page.js.map +1 -1
- package/dist/pages/create-project-page.stories.js +5 -3
- package/dist/pages/create-project-page.stories.js.map +1 -1
- package/dist/pages/project-settings-form-errors.d.ts +11 -0
- package/dist/pages/project-settings-form-errors.d.ts.map +1 -0
- package/dist/pages/project-settings-form-errors.js +16 -0
- package/dist/pages/project-settings-form-errors.js.map +1 -0
- package/dist/pages/project-settings-page.d.ts +2 -0
- package/dist/pages/project-settings-page.d.ts.map +1 -0
- package/dist/pages/project-settings-page.js +225 -0
- package/dist/pages/project-settings-page.js.map +1 -0
- package/dist/pages/project-settings-page.stories.d.ts +23 -0
- package/dist/pages/project-settings-page.stories.d.ts.map +1 -0
- package/dist/pages/project-settings-page.stories.js +206 -0
- package/dist/pages/project-settings-page.stories.js.map +1 -0
- package/dist/pages/projects-hub-page.js +11 -11
- package/dist/pages/projects-hub-page.js.map +1 -1
- package/dist/project-error.d.ts +1 -0
- package/dist/project-error.d.ts.map +1 -1
- package/dist/project-error.js +7 -0
- package/dist/project-error.js.map +1 -1
- package/dist/routes/inputs.d.ts +2 -2
- package/dist/routes/inputs.d.ts.map +1 -1
- package/dist/routes/inputs.js +5 -5
- package/dist/routes/inputs.js.map +1 -1
- package/dist/routes/project-index.d.ts +2 -2
- package/dist/routes/project-index.d.ts.map +1 -1
- package/dist/routes/project-index.js +3 -3
- package/dist/routes/project-index.js.map +1 -1
- package/dist/routes/project-settings-index.d.ts +10 -0
- package/dist/routes/project-settings-index.d.ts.map +1 -0
- package/dist/routes/project-settings-index.js +12 -0
- package/dist/routes/project-settings-index.js.map +1 -0
- package/dist/routes/project-settings.d.ts +6 -0
- package/dist/routes/project-settings.d.ts.map +1 -0
- package/dist/routes/project-settings.js +7 -0
- package/dist/routes/project-settings.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +11 -10
- package/src/chrome.tsx +27 -19
- package/src/components/model-provider-reminder-banner.tsx +3 -1
- package/src/components/project-crumb.tsx +18 -5
- package/src/components/project-switcher.test.tsx +7 -4
- package/src/components/project-switcher.tsx +16 -5
- package/src/core/definition.ts +7 -0
- package/src/core/project.ts +8 -0
- package/src/feature.ts +26 -4
- package/src/hooks/api/mappers.test.ts +19 -0
- package/src/hooks/api/mappers.ts +6 -0
- package/src/hooks/api/projects.test.ts +247 -1
- package/src/hooks/api/projects.ts +227 -2
- package/src/index.ts +9 -1
- package/src/pages/create-project-page.stories.tsx +5 -4
- package/src/pages/create-project-page.test.tsx +191 -12
- package/src/pages/create-project-page.tsx +102 -15
- package/src/pages/home-router.test.tsx +2 -2
- package/src/pages/project-settings-form-errors.ts +17 -0
- package/src/pages/project-settings-page.stories.tsx +216 -0
- package/src/pages/project-settings-page.test.tsx +74 -0
- package/src/pages/project-settings-page.tsx +220 -0
- package/src/pages/projects-hub-page.test.tsx +17 -11
- package/src/pages/projects-hub-page.tsx +9 -9
- package/src/project-error.test.ts +1 -0
- package/src/project-error.ts +8 -0
- package/src/routes/inputs.test.ts +8 -6
- package/src/routes/inputs.ts +7 -6
- package/src/routes/project-index.tsx +3 -3
- package/src/routes/project-settings-index.tsx +11 -0
- package/src/routes/project-settings.tsx +4 -0
- package/test/pages.tsx +26 -22
- package/tsconfig.build.tsbuildinfo +1 -1
package/src/project-error.ts
CHANGED
|
@@ -4,6 +4,7 @@ export interface ProjectErrorCopy {
|
|
|
4
4
|
title: string;
|
|
5
5
|
message: string;
|
|
6
6
|
existingProjectId?: string | undefined;
|
|
7
|
+
slugConflict?: boolean | undefined;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
@@ -98,6 +99,13 @@ export function projectErrorCopy(error: unknown): ProjectErrorCopy {
|
|
|
98
99
|
existingProjectId: stringDetail(error, 'existing_project_id'),
|
|
99
100
|
};
|
|
100
101
|
}
|
|
102
|
+
if (error.code === 'slug-conflict') {
|
|
103
|
+
return {
|
|
104
|
+
title: 'Project slug already exists',
|
|
105
|
+
message: 'Choose another slug and try again.',
|
|
106
|
+
slugConflict: true,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
101
109
|
|
|
102
110
|
return {
|
|
103
111
|
title: 'Project request failed',
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import {projectRouteParams} from './inputs.js';
|
|
2
2
|
|
|
3
3
|
describe('project route inputs', () => {
|
|
4
|
-
it('requires both workspace and project
|
|
5
|
-
expect(projectRouteParams({
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
it('requires both workspace and project slugs', () => {
|
|
5
|
+
expect(projectRouteParams({workspaceSlug: 'workspace-1', projectSlug: 'project-1'})).toEqual({
|
|
6
|
+
workspaceSlug: 'workspace-1',
|
|
7
|
+
projectSlug: 'project-1',
|
|
8
8
|
});
|
|
9
|
-
expect(() => projectRouteParams({
|
|
9
|
+
expect(() => projectRouteParams({workspaceSlug: 'workspace-1'})).toThrow(
|
|
10
10
|
'Project route is missing required path parameters.',
|
|
11
11
|
);
|
|
12
|
-
expect(() =>
|
|
12
|
+
expect(() =>
|
|
13
|
+
projectRouteParams({workspaceSlug: ['workspace-1'], projectSlug: 'project-1'}),
|
|
14
|
+
).toThrow();
|
|
13
15
|
});
|
|
14
16
|
});
|
package/src/routes/inputs.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
export interface ProjectRouteParams {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
workspaceSlug: string;
|
|
3
|
+
projectSlug: string;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
export function projectRouteParams(input: Record<string, unknown>): ProjectRouteParams {
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
if (!
|
|
10
|
-
|
|
7
|
+
const workspaceSlug = stringParam(input.workspaceSlug);
|
|
8
|
+
const projectSlug = stringParam(input.projectSlug);
|
|
9
|
+
if (!workspaceSlug || !projectSlug)
|
|
10
|
+
throw new Error('Project route is missing required path parameters.');
|
|
11
|
+
return {workspaceSlug, projectSlug};
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
function stringParam(value: unknown): string | undefined {
|
|
@@ -2,10 +2,10 @@ import {defineRoute} from '@shipfox/client-shell/runtime';
|
|
|
2
2
|
import {redirect} from '@tanstack/react-router';
|
|
3
3
|
|
|
4
4
|
export default defineRoute({
|
|
5
|
-
beforeLoad: ({params}: {params: {
|
|
5
|
+
beforeLoad: ({params}: {params: {workspaceSlug: string; projectSlug: string}}) => {
|
|
6
6
|
throw redirect({
|
|
7
|
-
to: '/
|
|
8
|
-
params: {
|
|
7
|
+
to: '/w/$workspaceSlug/p/$projectSlug/runs',
|
|
8
|
+
params: {workspaceSlug: params.workspaceSlug, projectSlug: params.projectSlug},
|
|
9
9
|
});
|
|
10
10
|
},
|
|
11
11
|
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import {defineRoute} from '@shipfox/client-shell/runtime';
|
|
2
|
+
import {redirect} from '@tanstack/react-router';
|
|
3
|
+
|
|
4
|
+
export default defineRoute({
|
|
5
|
+
beforeLoad: ({params}: {params: {workspaceSlug: string; projectSlug: string}}) => {
|
|
6
|
+
throw redirect({
|
|
7
|
+
to: '/w/$workspaceSlug/p/$projectSlug/settings/general',
|
|
8
|
+
params,
|
|
9
|
+
});
|
|
10
|
+
},
|
|
11
|
+
});
|
package/test/pages.tsx
CHANGED
|
@@ -18,9 +18,9 @@ import {CreateProjectPage} from '#pages/create-project-page.js';
|
|
|
18
18
|
import {ProjectsHubPage} from '#pages/projects-hub-page.js';
|
|
19
19
|
|
|
20
20
|
// All test renders that exercise pages requiring `useActiveWorkspace()` mount
|
|
21
|
-
// under `/
|
|
22
|
-
// used in routes.
|
|
21
|
+
// under `/w/$workspaceSlug`. APIs still use the UUID below.
|
|
23
22
|
export const PROJECT_TEST_WID = '11111111-1111-4111-8111-111111111111';
|
|
23
|
+
export const PROJECT_TEST_WSLUG = 'acme';
|
|
24
24
|
|
|
25
25
|
const authState: AuthState = {
|
|
26
26
|
status: 'authenticated',
|
|
@@ -30,7 +30,7 @@ const authState: AuthState = {
|
|
|
30
30
|
email: 'user@example.com',
|
|
31
31
|
emailVerifiedAt: new Date().toISOString(),
|
|
32
32
|
},
|
|
33
|
-
workspaces: [{id: PROJECT_TEST_WID, name: 'Acme', membershipId: 'm-1'}],
|
|
33
|
+
workspaces: [{id: PROJECT_TEST_WID, name: 'Acme', slug: PROJECT_TEST_WSLUG, membershipId: 'm-1'}],
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
export function jsonResponse(body: unknown, init: ResponseInit = {}) {
|
|
@@ -51,36 +51,36 @@ function createTestRouter(path: string, element: ReactElement) {
|
|
|
51
51
|
});
|
|
52
52
|
const workspaceRoute = createRoute({
|
|
53
53
|
getParentRoute: () => rootRoute,
|
|
54
|
-
path: '/
|
|
55
|
-
component: () =>
|
|
56
|
-
initialPath === `/workspaces/${PROJECT_TEST_WID}` ? element : <ProjectsHubPage />,
|
|
54
|
+
path: '/w/$workspaceSlug',
|
|
55
|
+
component: () => (initialPath === `/w/${PROJECT_TEST_WSLUG}` ? element : <ProjectsHubPage />),
|
|
57
56
|
});
|
|
58
57
|
const workspaceNewProjectRoute = createRoute({
|
|
59
58
|
getParentRoute: () => rootRoute,
|
|
60
|
-
path: '/
|
|
59
|
+
path: '/w/$workspaceSlug/projects/new',
|
|
61
60
|
component: () =>
|
|
62
|
-
initialPath === `/
|
|
63
|
-
element
|
|
64
|
-
) : (
|
|
65
|
-
<CreateProjectPage />
|
|
66
|
-
),
|
|
61
|
+
initialPath === `/w/${PROJECT_TEST_WSLUG}/projects/new` ? element : <CreateProjectPage />,
|
|
67
62
|
});
|
|
68
63
|
const integrationsRoute = createRoute({
|
|
69
64
|
getParentRoute: () => rootRoute,
|
|
70
|
-
path: '/
|
|
65
|
+
path: '/w/$workspaceSlug/integrations',
|
|
71
66
|
component: () => <div>Integrations gallery placeholder</div>,
|
|
72
67
|
});
|
|
73
68
|
const modelProviderSettingsRoute = createRoute({
|
|
74
69
|
getParentRoute: () => rootRoute,
|
|
75
|
-
path: '/
|
|
70
|
+
path: '/w/$workspaceSlug/settings/agents',
|
|
76
71
|
component: () => <div>Agent settings placeholder</div>,
|
|
77
72
|
});
|
|
73
|
+
const projectSettingsGeneralRoute = createRoute({
|
|
74
|
+
getParentRoute: () => rootRoute,
|
|
75
|
+
path: '/w/$workspaceSlug/p/$projectSlug/settings/general',
|
|
76
|
+
component: () => element,
|
|
77
|
+
});
|
|
78
78
|
const projectDetailRoute = createRoute({
|
|
79
79
|
getParentRoute: () => rootRoute,
|
|
80
|
-
path: '/
|
|
80
|
+
path: '/w/$workspaceSlug/p/$projectSlug',
|
|
81
81
|
component: () => {
|
|
82
|
-
const params = useParams({strict: false}) as {
|
|
83
|
-
if (initialPath === `/
|
|
82
|
+
const params = useParams({strict: false}) as {projectSlug?: string};
|
|
83
|
+
if (initialPath === `/w/${PROJECT_TEST_WSLUG}/p/${params.projectSlug}`) {
|
|
84
84
|
return element;
|
|
85
85
|
}
|
|
86
86
|
|
|
@@ -92,10 +92,10 @@ function createTestRouter(path: string, element: ReactElement) {
|
|
|
92
92
|
});
|
|
93
93
|
const projectWorkflowsRoute = createRoute({
|
|
94
94
|
getParentRoute: () => rootRoute,
|
|
95
|
-
path: '/
|
|
95
|
+
path: '/w/$workspaceSlug/p/$projectSlug/workflows',
|
|
96
96
|
component: () => {
|
|
97
|
-
const params = useParams({strict: false}) as {
|
|
98
|
-
if (initialPath === `/
|
|
97
|
+
const params = useParams({strict: false}) as {projectSlug?: string};
|
|
98
|
+
if (initialPath === `/w/${PROJECT_TEST_WSLUG}/p/${params.projectSlug}/workflows`) {
|
|
99
99
|
return element;
|
|
100
100
|
}
|
|
101
101
|
|
|
@@ -111,14 +111,18 @@ function createTestRouter(path: string, element: ReactElement) {
|
|
|
111
111
|
workspaceNewProjectRoute,
|
|
112
112
|
integrationsRoute,
|
|
113
113
|
modelProviderSettingsRoute,
|
|
114
|
+
projectSettingsGeneralRoute,
|
|
114
115
|
projectDetailRoute,
|
|
115
116
|
projectWorkflowsRoute,
|
|
116
117
|
]),
|
|
117
118
|
});
|
|
118
119
|
}
|
|
119
120
|
|
|
120
|
-
export function renderProjectPage(
|
|
121
|
-
|
|
121
|
+
export function renderProjectPage(
|
|
122
|
+
path: string,
|
|
123
|
+
element: ReactElement,
|
|
124
|
+
queryClient = new QueryClient({defaultOptions: {queries: {retry: false}}}),
|
|
125
|
+
): RenderResult {
|
|
122
126
|
const router = createTestRouter(path, element);
|
|
123
127
|
const store = createStore();
|
|
124
128
|
store.set(authStateAtom, authState);
|