diff --git a/javascript/apps/taiga/src/app/modules/project/data-access/+state/actions/project.actions.ts b/javascript/apps/taiga/src/app/modules/project/data-access/+state/actions/project.actions.ts
index 3bc15d2ff..c9e7d1407 100644
--- a/javascript/apps/taiga/src/app/modules/project/data-access/+state/actions/project.actions.ts
+++ b/javascript/apps/taiga/src/app/modules/project/data-access/+state/actions/project.actions.ts
@@ -16,6 +16,7 @@ import {
StoryDetail,
User,
UserComment,
+ Workflow,
} from '@taiga/data';
import { DropCandidate } from '@taiga/ui/drag/drag.model';
@@ -67,6 +68,14 @@ export const updateStoryShowView = createAction(
}>()
);
+export const createWorkflow = createAction(
+ '[Project] Create Workflow',
+ props<{
+ project: Project;
+ name: Workflow['name'];
+ }>
+);
+
export const newProjectMembers = createAction(
'[Project][ws] New Project Members'
);
diff --git a/javascript/apps/taiga/src/app/modules/project/feature-kanban/data-access/+state/actions/kanban.actions.ts b/javascript/apps/taiga/src/app/modules/project/feature-kanban/data-access/+state/actions/kanban.actions.ts
index 41a3bbd46..e53685fc4 100644
--- a/javascript/apps/taiga/src/app/modules/project/feature-kanban/data-access/+state/actions/kanban.actions.ts
+++ b/javascript/apps/taiga/src/app/modules/project/feature-kanban/data-access/+state/actions/kanban.actions.ts
@@ -20,6 +20,7 @@ export const KanbanActions = createActionGroup({
source: 'Kanban',
events: {
'Init Kanban': props<{ workflow: Workflow['slug'] }>(),
+ 'Load Workflow kanban': props<{ workflow: Workflow['slug'] }>(),
'Open Create Story form': props<{ status: Status['id'] }>(),
'Close Create Story form': emptyProps(),
'Create Story': props<{
diff --git a/javascript/apps/taiga/src/app/modules/project/feature-kanban/data-access/+state/effects/kanban.effects.ts b/javascript/apps/taiga/src/app/modules/project/feature-kanban/data-access/+state/effects/kanban.effects.ts
index 760212afd..c43352087 100644
--- a/javascript/apps/taiga/src/app/modules/project/feature-kanban/data-access/+state/effects/kanban.effects.ts
+++ b/javascript/apps/taiga/src/app/modules/project/feature-kanban/data-access/+state/effects/kanban.effects.ts
@@ -44,7 +44,7 @@ import {
export class KanbanEffects {
public loadKanbanWorkflows$ = createEffect(() => {
return this.actions$.pipe(
- ofType(KanbanActions.initKanban),
+ ofType(KanbanActions.initKanban, KanbanActions.loadWorkflowKanban),
concatLatestFrom(() => [
this.store.select(selectCurrentProject).pipe(filterNil()),
]),
@@ -67,7 +67,7 @@ export class KanbanEffects {
public loadKanbanStories$ = createEffect(() => {
return this.actions$.pipe(
- ofType(KanbanActions.initKanban),
+ ofType(KanbanActions.initKanban, KanbanActions.loadWorkflowKanban),
concatLatestFrom(() => [
this.store.select(selectCurrentProject).pipe(filterNil()),
this.store.select(selectWorkflow),
diff --git a/javascript/apps/taiga/src/app/modules/project/feature-kanban/project-feature-kanban.component.ts b/javascript/apps/taiga/src/app/modules/project/feature-kanban/project-feature-kanban.component.ts
index 0c2d520f0..3ed2ceb0d 100644
--- a/javascript/apps/taiga/src/app/modules/project/feature-kanban/project-feature-kanban.component.ts
+++ b/javascript/apps/taiga/src/app/modules/project/feature-kanban/project-feature-kanban.component.ts
@@ -30,7 +30,16 @@ import {
WorkflowStatus,
} from '@taiga/data';
import { ModalComponent } from '@taiga/ui/modal/components';
-import { combineLatest, filter, map, merge, pairwise, take } from 'rxjs';
+import {
+ combineLatest,
+ distinctUntilChanged,
+ filter,
+ map,
+ merge,
+ pairwise,
+ skip,
+ take,
+} from 'rxjs';
import * as ProjectActions from '~/app/modules/project/data-access/+state/actions/project.actions';
import {
selectCurrentProject,
@@ -134,16 +143,22 @@ export class ProjectFeatureKanbanComponent {
return;
}
- this.route.paramMap.subscribe((params) => {
- const workflowSlug = params.get('workflow') ?? 'main';
- this.store.dispatch(KanbanActions.initKanban({ workflow: workflowSlug }));
- });
-
// Load on init kanban page. Not on every reload
- // const workflowSlug = this.route.snapshot.params['workflow'];
- // this.store.dispatch(
- // KanbanActions.initKanban({ workflow: workflowSlug })
- // );
+ const workflow: Workflow['slug'] =
+ (this.route.snapshot.params['workflow'] as Workflow['slug']) ?? 'main';
+ this.store.dispatch(KanbanActions.initKanban({ workflow }));
+
+ this.route.paramMap
+ .pipe(
+ map((params) => {
+ return params.get('workflow') ?? 'main';
+ }),
+ distinctUntilChanged(),
+ skip(1)
+ )
+ .subscribe((workflow: Workflow['slug']) => {
+ this.store.dispatch(KanbanActions.loadWorkflowKanban({ workflow }));
+ });
this.checkInviteModalStatus();
this.state.connect(
diff --git a/javascript/apps/taiga/src/app/modules/project/feature-navigation/components/project-navigation-menu/project-navigation-menu.component.html b/javascript/apps/taiga/src/app/modules/project/feature-navigation/components/project-navigation-menu/project-navigation-menu.component.html
index 3f366bcd5..a08f62a0e 100644
--- a/javascript/apps/taiga/src/app/modules/project/feature-navigation/components/project-navigation-menu/project-navigation-menu.component.html
+++ b/javascript/apps/taiga/src/app/modules/project/feature-navigation/components/project-navigation-menu/project-navigation-menu.component.html
@@ -394,7 +394,8 @@
-
+ icon="plus">
diff --git a/javascript/apps/taiga/src/app/modules/project/feature-navigation/components/project-navigation-menu/project-navigation-menu.component.ts b/javascript/apps/taiga/src/app/modules/project/feature-navigation/components/project-navigation-menu/project-navigation-menu.component.ts
index 6af7f0f5c..66b24fe5e 100644
--- a/javascript/apps/taiga/src/app/modules/project/feature-navigation/components/project-navigation-menu/project-navigation-menu.component.ts
+++ b/javascript/apps/taiga/src/app/modules/project/feature-navigation/components/project-navigation-menu/project-navigation-menu.component.ts
@@ -213,10 +213,6 @@ export class ProjectNavigationMenuComponent {
this.dialog.type = '';
}
- public createWorkflow() {
- console.log('create workflow');
- }
-
public trackById(_index: number, workflow: Workflow) {
return workflow.id;
}
diff --git a/javascript/apps/taiga/src/app/modules/project/feature-new-kanban/project-feature-new-kanban-routing.module.ts b/javascript/apps/taiga/src/app/modules/project/feature-new-kanban/project-feature-new-kanban-routing.module.ts
new file mode 100644
index 000000000..0c6c7e4d1
--- /dev/null
+++ b/javascript/apps/taiga/src/app/modules/project/feature-new-kanban/project-feature-new-kanban-routing.module.ts
@@ -0,0 +1,21 @@
+/**
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * Copyright (c) 2023-present Kaleidos INC
+ */
+
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+import { ProjectFeatureNewKanbanComponent } from './project-feature-new-kanban.component';
+
+const routes: Routes = [
+ { path: '', component: ProjectFeatureNewKanbanComponent },
+];
+
+@NgModule({
+ imports: [RouterModule.forChild(routes)],
+ exports: [RouterModule],
+})
+export class ProjectFeatureNewKanbanRoutingModule {}
diff --git a/javascript/apps/taiga/src/app/modules/project/feature-new-kanban/project-feature-new-kanban.component.css b/javascript/apps/taiga/src/app/modules/project/feature-new-kanban/project-feature-new-kanban.component.css
new file mode 100644
index 000000000..711742e35
--- /dev/null
+++ b/javascript/apps/taiga/src/app/modules/project/feature-new-kanban/project-feature-new-kanban.component.css
@@ -0,0 +1,7 @@
+/*
+This Source Code Form is subject to the terms of the Mozilla Public
+License, v. 2.0. If a copy of the MPL was not distributed with this
+file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+Copyright (c) 2023-present Kaleidos INC
+*/
diff --git a/javascript/apps/taiga/src/app/modules/project/feature-new-kanban/project-feature-new-kanban.component.html b/javascript/apps/taiga/src/app/modules/project/feature-new-kanban/project-feature-new-kanban.component.html
new file mode 100644
index 000000000..51250bb62
--- /dev/null
+++ b/javascript/apps/taiga/src/app/modules/project/feature-new-kanban/project-feature-new-kanban.component.html
@@ -0,0 +1,9 @@
+
+
+
New Kanban Works!
diff --git a/javascript/apps/taiga/src/app/modules/project/feature-new-kanban/project-feature-new-kanban.component.spec.ts b/javascript/apps/taiga/src/app/modules/project/feature-new-kanban/project-feature-new-kanban.component.spec.ts
new file mode 100644
index 000000000..3de00cc2f
--- /dev/null
+++ b/javascript/apps/taiga/src/app/modules/project/feature-new-kanban/project-feature-new-kanban.component.spec.ts
@@ -0,0 +1,63 @@
+/**
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * Copyright (c) 2023-present Kaleidos INC
+ */
+
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { Spectator, createComponentFactory } from '@ngneat/spectator/jest';
+import { getTranslocoModule } from '~/app/transloco/transloco-testing.module';
+import { provideMockStore, MockStore } from '@ngrx/store/testing';
+import { ProjectFeatureNewKanbanComponent } from './project-feature-new-kanban.component';
+
+import { selectorExample } from './+state/selectors/project-feature-new-kanban.selectors';
+
+describe('ProjectFeatureNewKanbanComponent', () => {
+ let spectator: Spectator;
+ let store: MockStore;
+
+ const createComponent = createComponentFactory({
+ component: ProjectFeatureNewKanbanComponent,
+ imports: [getTranslocoModule()],
+ schemas: [NO_ERRORS_SCHEMA],
+ mocks: [],
+ });
+
+ const initialState = { loggedIn: false };
+
+ beforeEach(() => {
+ spectator = createComponent({
+ // The component inputs
+ props: {
+ name: 'example',
+ },
+ // Override the component's providers
+ providers: [provideMockStore({ initialState })],
+ // Whether to run change detection (defaults to true)
+ detectChanges: false,
+ });
+
+ store = spectator.inject(MockStore);
+ });
+
+ it('example', () => {
+ // change ngrx state
+ store.setState({ loggedIn: true });
+
+ // mock selector
+ store.overrideSelector(selectorExample, {
+ id: 1,
+ name: 'test',
+ });
+
+ // trigger emission from all selectors
+ store.refreshState();
+
+ spectator.detectChanges();
+
+ // This test checks that the input attribute name becomes a class in the component structure
+ expect(spectator.query('div')).toHaveClass('example');
+ });
+});
diff --git a/javascript/apps/taiga/src/app/modules/project/feature-new-kanban/project-feature-new-kanban.component.ts b/javascript/apps/taiga/src/app/modules/project/feature-new-kanban/project-feature-new-kanban.component.ts
new file mode 100644
index 000000000..d66572e09
--- /dev/null
+++ b/javascript/apps/taiga/src/app/modules/project/feature-new-kanban/project-feature-new-kanban.component.ts
@@ -0,0 +1,22 @@
+/**
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * Copyright (c) 2023-present Kaleidos INC
+ */
+
+import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'tg-project-feature-new-kanban',
+ templateUrl: './project-feature-new-kanban.component.html',
+ styleUrls: ['./project-feature-new-kanban.component.css'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ standalone: true,
+})
+export class ProjectFeatureNewKanbanComponent implements OnInit {
+ public ngOnInit(): void {
+ console.log('ProjectFeatureNewKanbanComponent works!');
+ }
+}
diff --git a/javascript/apps/taiga/src/app/modules/project/feature-new-kanban/project-feature-new-kanban.module.ts b/javascript/apps/taiga/src/app/modules/project/feature-new-kanban/project-feature-new-kanban.module.ts
new file mode 100644
index 000000000..789e74603
--- /dev/null
+++ b/javascript/apps/taiga/src/app/modules/project/feature-new-kanban/project-feature-new-kanban.module.ts
@@ -0,0 +1,19 @@
+/**
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * Copyright (c) 2023-present Kaleidos INC
+ */
+
+import { CommonModule } from '@angular/common';
+import { NgModule } from '@angular/core';
+import { ProjectFeatureNewKanbanRoutingModule } from './project-feature-new-kanban-routing.module';
+
+@NgModule({
+ imports: [CommonModule, ProjectFeatureNewKanbanRoutingModule],
+ declarations: [],
+ providers: [],
+ exports: [],
+})
+export class ProjectFeatureNewKanbanModule {}
diff --git a/javascript/apps/taiga/src/app/modules/project/feature-shell/project-feature-shell-routing.module.ts b/javascript/apps/taiga/src/app/modules/project/feature-shell/project-feature-shell-routing.module.ts
index 170a8efc0..f9909a921 100644
--- a/javascript/apps/taiga/src/app/modules/project/feature-shell/project-feature-shell-routing.module.ts
+++ b/javascript/apps/taiga/src/app/modules/project/feature-shell/project-feature-shell-routing.module.ts
@@ -28,6 +28,26 @@ const routes: Routes = [
'~/app/modules/project/feature-overview/project-feature-overview.module'
).then((m) => m.ProjectFeatureOverviewModule),
},
+ {
+ path: 'overview',
+ loadChildren: () =>
+ import(
+ '~/app/modules/project/feature-overview/project-feature-overview.module'
+ ).then((m) => m.ProjectFeatureOverviewModule),
+ data: {
+ overview: true,
+ },
+ },
+ {
+ path: ':slug/overview',
+ loadChildren: () =>
+ import(
+ '~/app/modules/project/feature-overview/project-feature-overview.module'
+ ).then((m) => m.ProjectFeatureOverviewModule),
+ data: {
+ overview: true,
+ },
+ },
{
path: 'kanban',
loadChildren: () =>
@@ -48,6 +68,17 @@ const routes: Routes = [
redirectTo: ':slug/kanban/main',
pathMatch: 'full',
},
+ {
+ path: ':slug/new-kanban',
+ loadChildren: () =>
+ import(
+ '~/app/modules/project/feature-new-kanban/project-feature-new-kanban.module'
+ ).then((m) => m.ProjectFeatureNewKanbanModule),
+ canDeactivate: [CanDeactivateGuard],
+ data: {
+ newKanban: true,
+ },
+ },
{
path: ':slug/kanban/:workflow',
loadChildren: () =>
diff --git a/javascript/apps/taiga/src/app/modules/project/feature-shell/project-feature-shell.component.ts b/javascript/apps/taiga/src/app/modules/project/feature-shell/project-feature-shell.component.ts
index a54d0fd27..f64ce4620 100644
--- a/javascript/apps/taiga/src/app/modules/project/feature-shell/project-feature-shell.component.ts
+++ b/javascript/apps/taiga/src/app/modules/project/feature-shell/project-feature-shell.component.ts
@@ -15,15 +15,15 @@ import {
OnDestroy,
} from '@angular/core';
import {
- Router,
- RouterOutlet,
ActivatedRoute,
ActivatedRouteSnapshot,
+ Router,
+ RouterOutlet,
} from '@angular/router';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { Store } from '@ngrx/store';
import { RxState } from '@rx-angular/state';
-import { TuiNotification, TuiButtonModule } from '@taiga-ui/core';
+import { TuiButtonModule, TuiNotification } from '@taiga-ui/core';
import {
Attachment,
Membership,
@@ -54,9 +54,9 @@ import {
} from '../data-access/+state/actions/project.actions';
import { setNotificationClosed } from '../feature-overview/data-access/+state/actions/project-overview.actions';
-import { ProjectNavigationComponent } from '../feature-navigation/project-feature-navigation.component';
import { TranslocoDirective } from '@ngneat/transloco';
import { ContextNotificationComponent } from '@taiga/ui/context-notification/context-notification.component';
+import { ProjectNavigationComponent } from '../feature-navigation/project-feature-navigation.component';
@UntilDestroy()
@Component({
@@ -153,12 +153,18 @@ export class ProjectFeatureShellComponent implements OnDestroy, AfterViewInit {
active.routeConfig?.component?.name ===
'ProjectFeatureOverviewComponent';
const isSettings = !!active.data.settings;
+ const isNewKanban = !!active.data.newKanban;
if (isKanban) {
void this.router.navigate(
[`project/${project.id}/${project.slug}/kanban`],
{ replaceUrl: true }
);
+ } else if (isNewKanban) {
+ void this.router.navigate(
+ [`project/${project.id}/${project.slug}/new-kanban`],
+ { replaceUrl: true }
+ );
} else if (isOverview) {
void this.router.navigate(
[`project/${project.id}/${project.slug}/overview`],