Merge pull request #10752 from opf/feature/non-working-days-frontend
Add weekdays to the datepicker instancespull/10779/head
commit
42da22b9f1
@ -0,0 +1,33 @@ |
||||
// -- copyright
|
||||
// OpenProject is an open source project management software.
|
||||
// Copyright (C) 2012-2022 the OpenProject GmbH
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License version 3.
|
||||
//
|
||||
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
// Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
// Copyright (C) 2010-2013 the ChiliProject Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// See COPYRIGHT and LICENSE files for more details.
|
||||
//++
|
||||
|
||||
import { ApiV3GettableResource } from 'core-app/core/apiv3/paths/apiv3-resource'; |
||||
import { IDay } from 'core-app/core/state/days/day.model'; |
||||
|
||||
export class ApiV3DayPaths extends ApiV3GettableResource<IDay> { |
||||
} |
@ -0,0 +1,50 @@ |
||||
// -- copyright
|
||||
// OpenProject is an open source project management software.
|
||||
// Copyright (C) 2012-2022 the OpenProject GmbH
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License version 3.
|
||||
//
|
||||
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
// Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
// Copyright (C) 2010-2013 the ChiliProject Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// See COPYRIGHT and LICENSE files for more details.
|
||||
//++
|
||||
|
||||
import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; |
||||
import { ApiV3DayPaths } from 'core-app/core/apiv3/endpoints/days/api-v3-day-paths'; |
||||
import { IDay } from 'core-app/core/state/days/day.model'; |
||||
import { |
||||
ApiV3GettableResource, |
||||
ApiV3ResourceCollection, |
||||
} from 'core-app/core/apiv3/paths/apiv3-resource'; |
||||
|
||||
export class ApiV3DaysPaths extends ApiV3ResourceCollection<IDay, ApiV3DayPaths> { |
||||
// Base path
|
||||
public readonly path:string; |
||||
|
||||
constructor(readonly apiRoot:ApiV3Service, |
||||
protected basePath:string) { |
||||
super(apiRoot, basePath, 'days', ApiV3DayPaths); |
||||
} |
||||
|
||||
// Static paths
|
||||
|
||||
// /api/v3/days/week
|
||||
public readonly week = new ApiV3GettableResource(this.apiRoot, this.path, 'week', this); |
||||
} |
@ -0,0 +1,9 @@ |
||||
import { IHalResourceLinks } from 'core-app/core/state/hal-resource'; |
||||
|
||||
export interface IDay { |
||||
id:string; |
||||
date:string; |
||||
name:string; |
||||
working:boolean; |
||||
_links:IHalResourceLinks; |
||||
} |
@ -0,0 +1,54 @@ |
||||
import { Injectable } from '@angular/core'; |
||||
import { |
||||
map, |
||||
tap, |
||||
} from 'rxjs/operators'; |
||||
import { Observable } from 'rxjs'; |
||||
import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; |
||||
import { IHALCollection } from 'core-app/core/apiv3/types/hal-collection.type'; |
||||
import { HttpClient } from '@angular/common/http'; |
||||
import { ApiV3ListParameters } from 'core-app/core/apiv3/paths/apiv3-list-resource.interface'; |
||||
import { |
||||
collectionKey, |
||||
extendCollectionElementsWithId, |
||||
insertCollectionIntoState, |
||||
} from 'core-app/core/state/collection-store'; |
||||
import { DayStore } from 'core-app/core/state/days/day.store'; |
||||
import { IDay } from 'core-app/core/state/days/day.model'; |
||||
import { |
||||
CollectionStore, |
||||
ResourceCollectionService, |
||||
} from 'core-app/core/state/resource-collection.service'; |
||||
|
||||
@Injectable() |
||||
export class DayResourceService extends ResourceCollectionService<IDay> { |
||||
private get daysPath():string { |
||||
return this |
||||
.apiV3Service |
||||
.days |
||||
.path; |
||||
} |
||||
|
||||
constructor( |
||||
private http:HttpClient, |
||||
private apiV3Service:ApiV3Service, |
||||
) { |
||||
super(); |
||||
} |
||||
|
||||
fetchDays(params:ApiV3ListParameters):Observable<IHALCollection<IDay>> { |
||||
const collectionURL = collectionKey(params); |
||||
|
||||
return this |
||||
.http |
||||
.get<IHALCollection<IDay>>(this.daysPath + collectionURL) |
||||
.pipe( |
||||
map((collection) => extendCollectionElementsWithId(collection)), |
||||
tap((collection) => insertCollectionIntoState(this.store, collection, collectionURL)), |
||||
); |
||||
} |
||||
|
||||
protected createStore():CollectionStore<IDay> { |
||||
return new DayStore(); |
||||
} |
||||
} |
@ -0,0 +1,19 @@ |
||||
import { |
||||
EntityStore, |
||||
StoreConfig, |
||||
} from '@datorama/akita'; |
||||
import { |
||||
CollectionState, |
||||
createInitialCollectionState, |
||||
} from 'core-app/core/state/collection-store'; |
||||
import { IDay } from 'core-app/core/state/days/day.model'; |
||||
|
||||
export interface DayState extends CollectionState<IDay> { |
||||
} |
||||
|
||||
@StoreConfig({ name: 'days' }) |
||||
export class DayStore extends EntityStore<DayState> { |
||||
constructor() { |
||||
super(createInitialCollectionState()); |
||||
} |
||||
} |
@ -0,0 +1,9 @@ |
||||
import { IHalResourceLinks } from 'core-app/core/state/hal-resource'; |
||||
|
||||
export interface IWeekday { |
||||
id:string; |
||||
day:1|2|3|4|5|6|7; |
||||
name:string; |
||||
working:boolean; |
||||
_links:IHalResourceLinks; |
||||
} |
@ -0,0 +1,67 @@ |
||||
import { Injectable } from '@angular/core'; |
||||
import { |
||||
map, |
||||
switchMap, |
||||
tap, |
||||
} from 'rxjs/operators'; |
||||
import { |
||||
EMPTY, |
||||
Observable, |
||||
} from 'rxjs'; |
||||
import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; |
||||
import { IHALCollection } from 'core-app/core/apiv3/types/hal-collection.type'; |
||||
import { HttpClient } from '@angular/common/http'; |
||||
import { |
||||
extendCollectionElementsWithId, |
||||
insertCollectionIntoState, |
||||
} from 'core-app/core/state/collection-store'; |
||||
import { WeekdayStore } from 'core-app/core/state/days/weekday.store'; |
||||
import { IWeekday } from 'core-app/core/state/days/weekday.model'; |
||||
import { |
||||
CollectionStore, |
||||
ResourceCollectionService, |
||||
} from 'core-app/core/state/resource-collection.service'; |
||||
|
||||
@Injectable() |
||||
export class WeekdayResourceService extends ResourceCollectionService<IWeekday> { |
||||
private get weekdaysPath():string { |
||||
return this |
||||
.apiV3Service |
||||
.days |
||||
.week |
||||
.path; |
||||
} |
||||
|
||||
constructor( |
||||
private http:HttpClient, |
||||
private apiV3Service:ApiV3Service, |
||||
) { |
||||
super(); |
||||
} |
||||
|
||||
require():Observable<IWeekday[]> { |
||||
return this |
||||
.query |
||||
.selectHasCache() |
||||
.pipe( |
||||
switchMap((hasCache) => (hasCache ? EMPTY : this.fetchWeekdays())), |
||||
switchMap(() => this.query.selectAll()), |
||||
); |
||||
} |
||||
|
||||
private fetchWeekdays():Observable<IHALCollection<IWeekday>> { |
||||
const collectionURL = 'all'; // We load all weekdays
|
||||
|
||||
return this |
||||
.http |
||||
.get<IHALCollection<IWeekday>>(this.weekdaysPath) |
||||
.pipe( |
||||
map((collection) => extendCollectionElementsWithId(collection)), |
||||
tap((collection) => insertCollectionIntoState(this.store, collection, collectionURL)), |
||||
); |
||||
} |
||||
|
||||
protected createStore():CollectionStore<IWeekday> { |
||||
return new WeekdayStore(); |
||||
} |
||||
} |
@ -0,0 +1,19 @@ |
||||
import { |
||||
EntityStore, |
||||
StoreConfig, |
||||
} from '@datorama/akita'; |
||||
import { |
||||
CollectionState, |
||||
createInitialCollectionState, |
||||
} from 'core-app/core/state/collection-store'; |
||||
import { IWeekday } from 'core-app/core/state/days/weekday.model'; |
||||
|
||||
export interface WeekdayState extends CollectionState<IWeekday> { |
||||
} |
||||
|
||||
@StoreConfig({ name: 'weekdays' }) |
||||
export class WeekdayStore extends EntityStore<WeekdayState> { |
||||
constructor() { |
||||
super(createInitialCollectionState()); |
||||
} |
||||
} |
@ -0,0 +1,91 @@ |
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2022 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See COPYRIGHT and LICENSE files for more details. |
||||
#++ |
||||
|
||||
require 'spec_helper' |
||||
require 'features/page_objects/notification' |
||||
require 'features/work_packages/details/inplace_editor/shared_examples' |
||||
require 'features/work_packages/shared_contexts' |
||||
require 'support/edit_fields/edit_field' |
||||
require 'features/work_packages/work_packages_page' |
||||
|
||||
describe 'Work packages datepicker workdays', |
||||
with_settings: { date_format: '%Y-%m-%d' }, |
||||
js: true do |
||||
shared_let(:project) { create :project_with_types, public: true } |
||||
shared_let(:work_package) { create :work_package, project:, start_date: Date.parse('2022-01-01') } |
||||
shared_let(:user) { create :admin } |
||||
shared_let(:work_packages_page) { Pages::FullWorkPackage.new(work_package, project) } |
||||
|
||||
let(:combined_date) { work_packages_page.edit_field(:combinedDate) } |
||||
|
||||
before do |
||||
login_as(user) |
||||
|
||||
work_packages_page.visit! |
||||
work_packages_page.ensure_page_loaded |
||||
|
||||
combined_date.activate! |
||||
combined_date.expect_active! |
||||
end |
||||
|
||||
context 'with default work days' do |
||||
let!(:week_days) { create :week_days } |
||||
|
||||
it 'shows them as disabled' do |
||||
expect(page).to have_selector('.dayContainer', count: 2) |
||||
|
||||
weekend_days = %w[1 2 8 9 15 16 22 23 29 30].map(&:to_i) |
||||
weekend_days.each do |weekend_day| |
||||
expect(page).to have_selector('.dayContainer:first-of-type .flatpickr-day.flatpickr-non-working-day', |
||||
text: weekend_day, |
||||
exact_text: true) |
||||
end |
||||
|
||||
((1..31).to_a - weekend_days).each do |workday| |
||||
expect(page).to have_selector('.dayContainer:first-of-type .flatpickr-day:not(.flatpickr-non-working-day)', |
||||
text: workday, |
||||
exact_text: true) |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'with all days marked as weekend' do |
||||
let!(:week_days) do |
||||
days = create(:week_days) |
||||
|
||||
WeekDay.update_all(working: false) |
||||
|
||||
days |
||||
end |
||||
|
||||
it 'shows them as disabled' do |
||||
expect(page).to have_selector('.dayContainer', count: 2) |
||||
expect(page).to have_selector('.dayContainer:first-of-type .flatpickr-day.flatpickr-non-working-day', count: 31) |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue