mirror of
https://github.com/opf/openproject.git
synced 2026-06-14 03:30:14 +00:00
Merge branch 'dev' into epic/workflows-ux-quick-wins
This commit is contained in:
@@ -84,6 +84,8 @@ jobs:
|
||||
wielinde,
|
||||
yanzubrytskyi,
|
||||
ehassan01,
|
||||
JohannaStriebing,
|
||||
fereshtehnm,
|
||||
thykel
|
||||
# the followings are the optional inputs - If the optional inputs are not given, then default values will be taken
|
||||
remote-organization-name: opf
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send mail
|
||||
uses: dawidd6/action-send-mail@v15
|
||||
uses: dawidd6/action-send-mail@v16
|
||||
with:
|
||||
subject: ${{ inputs.subject }}
|
||||
body: ${{ inputs.body }}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
name: Package
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
branches:
|
||||
- packaging/*
|
||||
- release/*
|
||||
- stable/*
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 3 * * *' # Daily at 03:00
|
||||
@@ -42,6 +43,12 @@ jobs:
|
||||
run: |
|
||||
VERSION=$(ruby -r ./lib/open_project/version.rb -e "puts OpenProject::VERSION")
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
if [[ "${{ github.ref_type }}" == "tag" ]]; then
|
||||
MAJOR=$(ruby -r ./lib/open_project/version.rb -e "puts OpenProject::VERSION::MAJOR")
|
||||
echo "channel=stable/${MAJOR}" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "channel=${{ github.ref_name }}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Package
|
||||
uses: pkgr/action/package@main
|
||||
id: package
|
||||
@@ -58,5 +65,5 @@ jobs:
|
||||
target: ${{ matrix.target }}
|
||||
token: ${{ secrets.PACKAGER_PUBLISH_TOKEN }}
|
||||
repository: opf/openproject
|
||||
channel: ${{ github.ref_name }}
|
||||
channel: ${{ steps.setup.outputs.channel }}
|
||||
file: ${{ steps.package.outputs.package_path }}
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
token: ${{ secrets.OPENPROJECTCI_GH_FORK_TOKEN }}
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
@@ -47,3 +47,8 @@ jobs:
|
||||
git reset --hard "upstream/$branch"
|
||||
git push origin "$branch" --force
|
||||
done
|
||||
|
||||
- name: Sync tags
|
||||
run: |
|
||||
git fetch upstream --tags
|
||||
git push origin --tags --force
|
||||
|
||||
@@ -87,7 +87,7 @@ gem "htmldiff"
|
||||
gem "stringex", "~> 2.8.5"
|
||||
|
||||
# CommonMark markdown parser with GFM extension
|
||||
gem "commonmarker", "~> 2.6.0"
|
||||
gem "commonmarker", "~> 2.7.0"
|
||||
|
||||
# HTML pipeline for transformations on text formatter output
|
||||
# such as sanitization or additional features
|
||||
@@ -121,10 +121,10 @@ gem "csv", "~> 3.3"
|
||||
# provide compatible filesystem information for available storage
|
||||
gem "sys-filesystem", "~> 1.5.0", require: false
|
||||
|
||||
gem "bcrypt", "~> 3.1.6"
|
||||
gem "bcrypt", "~> 3.1.22"
|
||||
|
||||
gem "multi_json", "~> 1.19.0"
|
||||
gem "oj", "~> 3.16.12"
|
||||
gem "oj", "~> 3.16.16"
|
||||
|
||||
gem "daemons"
|
||||
gem "good_job", "~> 4.13.3" # update should be done manually in sync with saas-openproject version.
|
||||
@@ -163,7 +163,7 @@ gem "matrix", "~> 0.4.3"
|
||||
|
||||
gem "mcp", "~> 0.8.0"
|
||||
|
||||
gem "meta-tags", "~> 2.22.3"
|
||||
gem "meta-tags", "~> 2.23.0"
|
||||
|
||||
gem "paper_trail", "~> 17.0.0"
|
||||
|
||||
@@ -208,7 +208,7 @@ gem "aws-sdk-core", "~> 3.241"
|
||||
# File upload via fog + screenshots on travis
|
||||
gem "aws-sdk-s3", "~> 1.213"
|
||||
|
||||
gem "openproject-token", "~> 8.8.0"
|
||||
gem "openproject-token", "~> 8.8.2"
|
||||
|
||||
gem "plaintext", "~> 0.3.7"
|
||||
|
||||
@@ -236,8 +236,8 @@ gem "yabeda-puma-plugin"
|
||||
gem "yabeda-rails"
|
||||
|
||||
# opentelemetry
|
||||
gem "opentelemetry-exporter-otlp", "~> 0.31.0", require: false
|
||||
gem "opentelemetry-instrumentation-all", "~> 0.90.0", require: false
|
||||
gem "opentelemetry-exporter-otlp", "~> 0.32.0", require: false
|
||||
gem "opentelemetry-instrumentation-all", "~> 0.91.0", require: false
|
||||
gem "opentelemetry-sdk", "~> 1.10", require: false
|
||||
|
||||
gem "view_component", "~> 4.5.0"
|
||||
@@ -254,9 +254,7 @@ gem "factory_bot_rails", "~> 6.5.0", require: false
|
||||
gem "turbo_power", "~> 0.7.0"
|
||||
gem "turbo-rails", "~> 2.0.20"
|
||||
|
||||
# There is a problem with version 1.4.0. Do not update until you're sure there is no infinite hang
|
||||
# happenning in failing tests when WebMock or VCR stub cannot be found.
|
||||
gem "httpx", "~> 1.7.3"
|
||||
gem "httpx", "~> 1.7.4"
|
||||
|
||||
# Brings actual deep-freezing to most ruby objects
|
||||
gem "ice_nine"
|
||||
@@ -277,7 +275,7 @@ group :test do
|
||||
gem "rspec-rails", "~> 8.0.4", group: :development
|
||||
|
||||
# Retry failures within the same environment
|
||||
gem "retriable", "~> 3.2.1"
|
||||
gem "retriable", "~> 3.4.1"
|
||||
gem "rspec-retry", "~> 0.6.1"
|
||||
|
||||
# Accessibility tests
|
||||
@@ -434,4 +432,4 @@ end
|
||||
|
||||
gem "openproject-octicons", "~>19.32.0"
|
||||
gem "openproject-octicons_helper", "~>19.32.0"
|
||||
gem "openproject-primer_view_components", "~>0.82.0"
|
||||
gem "openproject-primer_view_components", "~>0.83.0"
|
||||
|
||||
+198
-188
@@ -213,6 +213,11 @@ PATH
|
||||
specs:
|
||||
openproject-webhooks (1.0.0)
|
||||
|
||||
PATH
|
||||
remote: modules/wikis
|
||||
specs:
|
||||
openproject-wikis (1.0.0)
|
||||
|
||||
PATH
|
||||
remote: modules/xls_export
|
||||
specs:
|
||||
@@ -225,29 +230,29 @@ GEM
|
||||
Ascii85 (2.0.1)
|
||||
action_text-trix (2.1.17)
|
||||
railties
|
||||
actioncable (8.1.2)
|
||||
actionpack (= 8.1.2)
|
||||
activesupport (= 8.1.2)
|
||||
actioncable (8.1.2.1)
|
||||
actionpack (= 8.1.2.1)
|
||||
activesupport (= 8.1.2.1)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
zeitwerk (~> 2.6)
|
||||
actionmailbox (8.1.2)
|
||||
actionpack (= 8.1.2)
|
||||
activejob (= 8.1.2)
|
||||
activerecord (= 8.1.2)
|
||||
activestorage (= 8.1.2)
|
||||
activesupport (= 8.1.2)
|
||||
actionmailbox (8.1.2.1)
|
||||
actionpack (= 8.1.2.1)
|
||||
activejob (= 8.1.2.1)
|
||||
activerecord (= 8.1.2.1)
|
||||
activestorage (= 8.1.2.1)
|
||||
activesupport (= 8.1.2.1)
|
||||
mail (>= 2.8.0)
|
||||
actionmailer (8.1.2)
|
||||
actionpack (= 8.1.2)
|
||||
actionview (= 8.1.2)
|
||||
activejob (= 8.1.2)
|
||||
activesupport (= 8.1.2)
|
||||
actionmailer (8.1.2.1)
|
||||
actionpack (= 8.1.2.1)
|
||||
actionview (= 8.1.2.1)
|
||||
activejob (= 8.1.2.1)
|
||||
activesupport (= 8.1.2.1)
|
||||
mail (>= 2.8.0)
|
||||
rails-dom-testing (~> 2.2)
|
||||
actionpack (8.1.2)
|
||||
actionview (= 8.1.2)
|
||||
activesupport (= 8.1.2)
|
||||
actionpack (8.1.2.1)
|
||||
actionview (= 8.1.2.1)
|
||||
activesupport (= 8.1.2.1)
|
||||
nokogiri (>= 1.8.5)
|
||||
rack (>= 2.2.4)
|
||||
rack-session (>= 1.0.1)
|
||||
@@ -258,34 +263,34 @@ GEM
|
||||
actionpack-xml_parser (2.0.1)
|
||||
actionpack (>= 5.0)
|
||||
railties (>= 5.0)
|
||||
actiontext (8.1.2)
|
||||
actiontext (8.1.2.1)
|
||||
action_text-trix (~> 2.1.15)
|
||||
actionpack (= 8.1.2)
|
||||
activerecord (= 8.1.2)
|
||||
activestorage (= 8.1.2)
|
||||
activesupport (= 8.1.2)
|
||||
actionpack (= 8.1.2.1)
|
||||
activerecord (= 8.1.2.1)
|
||||
activestorage (= 8.1.2.1)
|
||||
activesupport (= 8.1.2.1)
|
||||
globalid (>= 0.6.0)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (8.1.2)
|
||||
activesupport (= 8.1.2)
|
||||
actionview (8.1.2.1)
|
||||
activesupport (= 8.1.2.1)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.11)
|
||||
rails-dom-testing (~> 2.2)
|
||||
rails-html-sanitizer (~> 1.6)
|
||||
active_record_doctor (2.0.1)
|
||||
activerecord (>= 7.0.0)
|
||||
activejob (8.1.2)
|
||||
activesupport (= 8.1.2)
|
||||
activejob (8.1.2.1)
|
||||
activesupport (= 8.1.2.1)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (8.1.2)
|
||||
activesupport (= 8.1.2)
|
||||
activemodel (8.1.2.1)
|
||||
activesupport (= 8.1.2.1)
|
||||
activemodel-serializers-xml (1.0.3)
|
||||
activemodel (>= 5.0.0.a)
|
||||
activesupport (>= 5.0.0.a)
|
||||
builder (~> 3.1)
|
||||
activerecord (8.1.2)
|
||||
activemodel (= 8.1.2)
|
||||
activesupport (= 8.1.2)
|
||||
activerecord (8.1.2.1)
|
||||
activemodel (= 8.1.2.1)
|
||||
activesupport (= 8.1.2.1)
|
||||
timeout (>= 0.4.0)
|
||||
activerecord-import (2.2.0)
|
||||
activerecord (>= 4.2)
|
||||
@@ -297,13 +302,13 @@ GEM
|
||||
cgi (>= 0.3.6)
|
||||
rack (>= 2.0.8, < 4)
|
||||
railties (>= 7.0)
|
||||
activestorage (8.1.2)
|
||||
actionpack (= 8.1.2)
|
||||
activejob (= 8.1.2)
|
||||
activerecord (= 8.1.2)
|
||||
activesupport (= 8.1.2)
|
||||
activestorage (8.1.2.1)
|
||||
actionpack (= 8.1.2.1)
|
||||
activejob (= 8.1.2.1)
|
||||
activerecord (= 8.1.2.1)
|
||||
activesupport (= 8.1.2.1)
|
||||
marcel (~> 1.0)
|
||||
activesupport (8.1.2)
|
||||
activesupport (8.1.2.1)
|
||||
base64
|
||||
bigdecimal
|
||||
concurrent-ruby (~> 1.0, >= 1.3.1)
|
||||
@@ -342,8 +347,8 @@ GEM
|
||||
awesome_nested_set (3.9.0)
|
||||
activerecord (>= 4.0.0, < 8.2)
|
||||
aws-eventstream (1.4.0)
|
||||
aws-partitions (1.1221.0)
|
||||
aws-sdk-core (3.242.0)
|
||||
aws-partitions (1.1227.0)
|
||||
aws-sdk-core (3.243.0)
|
||||
aws-eventstream (~> 1, >= 1.3.0)
|
||||
aws-partitions (~> 1, >= 1.992.0)
|
||||
aws-sigv4 (~> 1.9)
|
||||
@@ -354,8 +359,8 @@ GEM
|
||||
aws-sdk-kms (1.122.0)
|
||||
aws-sdk-core (~> 3, >= 3.241.4)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sdk-s3 (1.214.0)
|
||||
aws-sdk-core (~> 3, >= 3.241.4)
|
||||
aws-sdk-s3 (1.216.0)
|
||||
aws-sdk-core (~> 3, >= 3.243.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sdk-sns (1.112.0)
|
||||
@@ -377,7 +382,7 @@ GEM
|
||||
ice_nine (~> 0.11.0)
|
||||
thread_safe (~> 0.3, >= 0.3.1)
|
||||
base64 (0.3.0)
|
||||
bcrypt (3.1.21)
|
||||
bcrypt (3.1.22)
|
||||
benchmark (0.5.0)
|
||||
better_html (2.2.0)
|
||||
actionview (>= 7.0)
|
||||
@@ -433,12 +438,13 @@ GEM
|
||||
descendants_tracker (~> 0.0.1)
|
||||
color_conversion (0.1.2)
|
||||
colored2 (4.0.3)
|
||||
commonmarker (2.6.3-aarch64-linux)
|
||||
commonmarker (2.6.3-arm-linux)
|
||||
commonmarker (2.6.3-arm64-darwin)
|
||||
commonmarker (2.6.3-x86_64-darwin)
|
||||
commonmarker (2.6.3-x86_64-linux)
|
||||
commonmarker (2.6.3-x86_64-linux-musl)
|
||||
commonmarker (2.7.0-aarch64-linux)
|
||||
commonmarker (2.7.0-aarch64-linux-musl)
|
||||
commonmarker (2.7.0-arm-linux)
|
||||
commonmarker (2.7.0-arm64-darwin)
|
||||
commonmarker (2.7.0-x86_64-darwin)
|
||||
commonmarker (2.7.0-x86_64-linux)
|
||||
commonmarker (2.7.0-x86_64-linux-musl)
|
||||
compare-xml (0.66)
|
||||
nokogiri (~> 1.8)
|
||||
concurrent-ruby (1.3.6)
|
||||
@@ -697,7 +703,7 @@ GEM
|
||||
htmlentities (4.3.4)
|
||||
http-2 (1.1.3)
|
||||
http_parser.rb (0.8.1)
|
||||
httpx (1.7.3)
|
||||
httpx (1.7.4)
|
||||
http-2 (>= 1.1.3)
|
||||
i18n (1.14.8)
|
||||
concurrent-ruby (~> 1.0)
|
||||
@@ -740,7 +746,7 @@ GEM
|
||||
jmespath (1.6.2)
|
||||
job-iteration (1.12.0)
|
||||
activejob (>= 6.1)
|
||||
json (2.19.1)
|
||||
json (2.19.2)
|
||||
json-jwt (1.17.0)
|
||||
activesupport (>= 4.2)
|
||||
aes_key_wrap
|
||||
@@ -767,7 +773,7 @@ GEM
|
||||
addressable (~> 2.8)
|
||||
childprocess (~> 5.0)
|
||||
logger (~> 1.6)
|
||||
lefthook (2.1.3)
|
||||
lefthook (2.1.4)
|
||||
letter_opener (1.10.0)
|
||||
launchy (>= 2.2, < 4)
|
||||
letter_opener_web (3.0.0)
|
||||
@@ -790,7 +796,7 @@ GEM
|
||||
activesupport (>= 4)
|
||||
railties (>= 4)
|
||||
request_store (~> 1.0)
|
||||
loofah (2.25.0)
|
||||
loofah (2.25.1)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.12.0)
|
||||
lookbook (2.3.14)
|
||||
@@ -818,13 +824,13 @@ GEM
|
||||
json-schema (>= 4.1)
|
||||
messagebird-rest (5.0.0)
|
||||
jwt (< 4)
|
||||
meta-tags (2.22.3)
|
||||
meta-tags (2.23.0)
|
||||
actionpack (>= 6.0.0)
|
||||
method_source (1.1.0)
|
||||
mime-types (3.7.0)
|
||||
logger
|
||||
mime-types-data (~> 3.2025, >= 3.2025.0507)
|
||||
mime-types-data (3.2026.0303)
|
||||
mime-types-data (3.2026.0317)
|
||||
mini_magick (5.3.1)
|
||||
logger
|
||||
mini_mime (1.1.5)
|
||||
@@ -868,7 +874,7 @@ GEM
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.19.1-x86_64-linux-musl)
|
||||
racc (~> 1.4)
|
||||
oj (3.16.15)
|
||||
oj (3.16.16)
|
||||
bigdecimal (>= 3.0)
|
||||
ostruct (>= 0.2)
|
||||
okcomputer (1.19.1)
|
||||
@@ -895,27 +901,28 @@ GEM
|
||||
actionview
|
||||
openproject-octicons (= 19.32.1)
|
||||
railties
|
||||
openproject-primer_view_components (0.82.0)
|
||||
openproject-primer_view_components (0.83.0)
|
||||
actionview (>= 7.2.0)
|
||||
activesupport (>= 7.2.0)
|
||||
openproject-octicons (>= 19.30.1)
|
||||
view_component (>= 3.1, < 5.0)
|
||||
openproject-token (8.8.0)
|
||||
openproject-token (8.8.2)
|
||||
activemodel
|
||||
openssl (4.0.1)
|
||||
openssl-signature_algorithm (1.3.0)
|
||||
openssl (> 2.0)
|
||||
opentelemetry-api (1.7.0)
|
||||
opentelemetry-api (1.8.0)
|
||||
logger
|
||||
opentelemetry-common (0.23.0)
|
||||
opentelemetry-api (~> 1.0)
|
||||
opentelemetry-exporter-otlp (0.31.1)
|
||||
opentelemetry-exporter-otlp (0.32.0)
|
||||
google-protobuf (>= 3.18)
|
||||
googleapis-common-protos-types (~> 1.3)
|
||||
opentelemetry-api (~> 1.1)
|
||||
opentelemetry-common (~> 0.20)
|
||||
opentelemetry-sdk (~> 1.10)
|
||||
opentelemetry-semantic_conventions
|
||||
opentelemetry-helpers-mysql (0.4.0)
|
||||
opentelemetry-helpers-mysql (0.5.0)
|
||||
opentelemetry-api (~> 1.7)
|
||||
opentelemetry-common (~> 0.21)
|
||||
opentelemetry-helpers-sql (0.3.0)
|
||||
@@ -925,7 +932,7 @@ GEM
|
||||
opentelemetry-common (~> 0.21)
|
||||
opentelemetry-instrumentation-action_mailer (0.6.1)
|
||||
opentelemetry-instrumentation-active_support (~> 0.10)
|
||||
opentelemetry-instrumentation-action_pack (0.15.1)
|
||||
opentelemetry-instrumentation-action_pack (0.16.0)
|
||||
opentelemetry-instrumentation-rack (~> 0.29)
|
||||
opentelemetry-instrumentation-action_view (0.11.2)
|
||||
opentelemetry-instrumentation-active_support (~> 0.10)
|
||||
@@ -939,45 +946,45 @@ GEM
|
||||
opentelemetry-instrumentation-active_support (~> 0.10)
|
||||
opentelemetry-instrumentation-active_support (0.10.1)
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
opentelemetry-instrumentation-all (0.90.1)
|
||||
opentelemetry-instrumentation-all (0.91.0)
|
||||
opentelemetry-instrumentation-active_model_serializers (~> 0.24.0)
|
||||
opentelemetry-instrumentation-anthropic (~> 0.3.0)
|
||||
opentelemetry-instrumentation-anthropic (~> 0.4.0)
|
||||
opentelemetry-instrumentation-aws_lambda (~> 0.6.0)
|
||||
opentelemetry-instrumentation-aws_sdk (~> 0.11.0)
|
||||
opentelemetry-instrumentation-bunny (~> 0.24.0)
|
||||
opentelemetry-instrumentation-concurrent_ruby (~> 0.24.0)
|
||||
opentelemetry-instrumentation-dalli (~> 0.29.0)
|
||||
opentelemetry-instrumentation-dalli (~> 0.29.2)
|
||||
opentelemetry-instrumentation-delayed_job (~> 0.25.1)
|
||||
opentelemetry-instrumentation-ethon (~> 0.27.0)
|
||||
opentelemetry-instrumentation-excon (~> 0.27.0)
|
||||
opentelemetry-instrumentation-faraday (~> 0.31.0)
|
||||
opentelemetry-instrumentation-grape (~> 0.5.0)
|
||||
opentelemetry-instrumentation-graphql (~> 0.31.1)
|
||||
opentelemetry-instrumentation-ethon (~> 0.28.0)
|
||||
opentelemetry-instrumentation-excon (~> 0.28.0)
|
||||
opentelemetry-instrumentation-faraday (~> 0.32.0)
|
||||
opentelemetry-instrumentation-grape (~> 0.6.0)
|
||||
opentelemetry-instrumentation-graphql (~> 0.31.2)
|
||||
opentelemetry-instrumentation-grpc (~> 0.4.1)
|
||||
opentelemetry-instrumentation-gruf (~> 0.5.0)
|
||||
opentelemetry-instrumentation-http (~> 0.28.0)
|
||||
opentelemetry-instrumentation-http_client (~> 0.27.0)
|
||||
opentelemetry-instrumentation-httpx (~> 0.6.0)
|
||||
opentelemetry-instrumentation-http (~> 0.29.0)
|
||||
opentelemetry-instrumentation-http_client (~> 0.28.0)
|
||||
opentelemetry-instrumentation-httpx (~> 0.7.0)
|
||||
opentelemetry-instrumentation-koala (~> 0.23.0)
|
||||
opentelemetry-instrumentation-lmdb (~> 0.25.0)
|
||||
opentelemetry-instrumentation-mongo (~> 0.25.0)
|
||||
opentelemetry-instrumentation-mysql2 (~> 0.33.0)
|
||||
opentelemetry-instrumentation-net_http (~> 0.27.0)
|
||||
opentelemetry-instrumentation-net_http (~> 0.28.0)
|
||||
opentelemetry-instrumentation-pg (~> 0.35.0)
|
||||
opentelemetry-instrumentation-que (~> 0.12.0)
|
||||
opentelemetry-instrumentation-racecar (~> 0.6.0)
|
||||
opentelemetry-instrumentation-rack (~> 0.29.0)
|
||||
opentelemetry-instrumentation-rails (~> 0.39.1)
|
||||
opentelemetry-instrumentation-racecar (~> 0.6.1)
|
||||
opentelemetry-instrumentation-rack (~> 0.30.0)
|
||||
opentelemetry-instrumentation-rails (~> 0.40.0)
|
||||
opentelemetry-instrumentation-rake (~> 0.5.0)
|
||||
opentelemetry-instrumentation-rdkafka (~> 0.9.0)
|
||||
opentelemetry-instrumentation-redis (~> 0.28.0)
|
||||
opentelemetry-instrumentation-resque (~> 0.8.0)
|
||||
opentelemetry-instrumentation-restclient (~> 0.26.0)
|
||||
opentelemetry-instrumentation-restclient (~> 0.27.0)
|
||||
opentelemetry-instrumentation-ruby_kafka (~> 0.24.0)
|
||||
opentelemetry-instrumentation-sidekiq (~> 0.28.1)
|
||||
opentelemetry-instrumentation-sinatra (~> 0.28.0)
|
||||
opentelemetry-instrumentation-trilogy (~> 0.66.0)
|
||||
opentelemetry-instrumentation-anthropic (0.3.0)
|
||||
opentelemetry-instrumentation-sinatra (~> 0.29.0)
|
||||
opentelemetry-instrumentation-trilogy (~> 0.67.0)
|
||||
opentelemetry-instrumentation-anthropic (0.4.0)
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
opentelemetry-instrumentation-aws_lambda (0.6.0)
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
@@ -995,13 +1002,13 @@ GEM
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
opentelemetry-instrumentation-delayed_job (0.25.1)
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
opentelemetry-instrumentation-ethon (0.27.0)
|
||||
opentelemetry-instrumentation-ethon (0.28.0)
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
opentelemetry-instrumentation-excon (0.27.0)
|
||||
opentelemetry-instrumentation-excon (0.28.0)
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
opentelemetry-instrumentation-faraday (0.31.0)
|
||||
opentelemetry-instrumentation-faraday (0.32.0)
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
opentelemetry-instrumentation-grape (0.5.1)
|
||||
opentelemetry-instrumentation-grape (0.6.0)
|
||||
opentelemetry-instrumentation-rack (~> 0.29)
|
||||
opentelemetry-instrumentation-graphql (0.31.2)
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
@@ -1009,11 +1016,11 @@ GEM
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
opentelemetry-instrumentation-gruf (0.5.0)
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
opentelemetry-instrumentation-http (0.28.0)
|
||||
opentelemetry-instrumentation-http (0.29.0)
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
opentelemetry-instrumentation-http_client (0.27.0)
|
||||
opentelemetry-instrumentation-http_client (0.28.0)
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
opentelemetry-instrumentation-httpx (0.6.1)
|
||||
opentelemetry-instrumentation-httpx (0.7.0)
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
opentelemetry-instrumentation-koala (0.23.0)
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
@@ -1026,7 +1033,7 @@ GEM
|
||||
opentelemetry-helpers-sql
|
||||
opentelemetry-helpers-sql-processor
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
opentelemetry-instrumentation-net_http (0.27.0)
|
||||
opentelemetry-instrumentation-net_http (0.28.0)
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
opentelemetry-instrumentation-pg (0.35.0)
|
||||
opentelemetry-helpers-sql
|
||||
@@ -1036,9 +1043,9 @@ GEM
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
opentelemetry-instrumentation-racecar (0.6.1)
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
opentelemetry-instrumentation-rack (0.29.0)
|
||||
opentelemetry-instrumentation-rack (0.30.0)
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
opentelemetry-instrumentation-rails (0.39.1)
|
||||
opentelemetry-instrumentation-rails (0.40.0)
|
||||
opentelemetry-instrumentation-action_mailer (~> 0.6)
|
||||
opentelemetry-instrumentation-action_pack (~> 0.15)
|
||||
opentelemetry-instrumentation-action_view (~> 0.11)
|
||||
@@ -1055,15 +1062,15 @@ GEM
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
opentelemetry-instrumentation-resque (0.8.0)
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
opentelemetry-instrumentation-restclient (0.26.0)
|
||||
opentelemetry-instrumentation-restclient (0.27.0)
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
opentelemetry-instrumentation-ruby_kafka (0.24.0)
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
opentelemetry-instrumentation-sidekiq (0.28.1)
|
||||
opentelemetry-instrumentation-base (~> 0.25)
|
||||
opentelemetry-instrumentation-sinatra (0.28.0)
|
||||
opentelemetry-instrumentation-sinatra (0.29.0)
|
||||
opentelemetry-instrumentation-rack (~> 0.29)
|
||||
opentelemetry-instrumentation-trilogy (0.66.0)
|
||||
opentelemetry-instrumentation-trilogy (0.67.0)
|
||||
opentelemetry-helpers-mysql
|
||||
opentelemetry-helpers-sql
|
||||
opentelemetry-helpers-sql-processor
|
||||
@@ -1083,7 +1090,7 @@ GEM
|
||||
ostruct (0.6.3)
|
||||
ox (2.14.23)
|
||||
bigdecimal (>= 3.0)
|
||||
pagy (43.3.2)
|
||||
pagy (43.4.0)
|
||||
json
|
||||
uri
|
||||
yaml
|
||||
@@ -1176,7 +1183,7 @@ GEM
|
||||
date
|
||||
stringio
|
||||
public_suffix (7.0.5)
|
||||
puffing-billy (4.0.3)
|
||||
puffing-billy (4.0.4)
|
||||
addressable (~> 2.5)
|
||||
cgi
|
||||
em-http-request (~> 1.1, >= 1.1.0)
|
||||
@@ -1219,20 +1226,20 @@ GEM
|
||||
rackup (1.0.1)
|
||||
rack (< 3)
|
||||
webrick
|
||||
rails (8.1.2)
|
||||
actioncable (= 8.1.2)
|
||||
actionmailbox (= 8.1.2)
|
||||
actionmailer (= 8.1.2)
|
||||
actionpack (= 8.1.2)
|
||||
actiontext (= 8.1.2)
|
||||
actionview (= 8.1.2)
|
||||
activejob (= 8.1.2)
|
||||
activemodel (= 8.1.2)
|
||||
activerecord (= 8.1.2)
|
||||
activestorage (= 8.1.2)
|
||||
activesupport (= 8.1.2)
|
||||
rails (8.1.2.1)
|
||||
actioncable (= 8.1.2.1)
|
||||
actionmailbox (= 8.1.2.1)
|
||||
actionmailer (= 8.1.2.1)
|
||||
actionpack (= 8.1.2.1)
|
||||
actiontext (= 8.1.2.1)
|
||||
actionview (= 8.1.2.1)
|
||||
activejob (= 8.1.2.1)
|
||||
activemodel (= 8.1.2.1)
|
||||
activerecord (= 8.1.2.1)
|
||||
activestorage (= 8.1.2.1)
|
||||
activesupport (= 8.1.2.1)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 8.1.2)
|
||||
railties (= 8.1.2.1)
|
||||
rails-controller-testing (1.0.5)
|
||||
actionpack (>= 5.0.1.rc1)
|
||||
actionview (>= 5.0.1.rc1)
|
||||
@@ -1247,9 +1254,9 @@ GEM
|
||||
rails-i18n (8.1.0)
|
||||
i18n (>= 0.7, < 2)
|
||||
railties (>= 8.0.0, < 9)
|
||||
railties (8.1.2)
|
||||
actionpack (= 8.1.2)
|
||||
activesupport (= 8.1.2)
|
||||
railties (8.1.2.1)
|
||||
actionpack (= 8.1.2.1)
|
||||
activesupport (= 8.1.2.1)
|
||||
irb (~> 1.13)
|
||||
rackup (>= 1.0.0)
|
||||
rake (>= 12.2)
|
||||
@@ -1277,7 +1284,7 @@ GEM
|
||||
redcarpet (3.6.1)
|
||||
redis (5.4.1)
|
||||
redis-client (>= 0.22.0)
|
||||
redis-client (0.26.4)
|
||||
redis-client (0.28.0)
|
||||
connection_pool
|
||||
regexp_parser (2.11.3)
|
||||
reline (0.6.3)
|
||||
@@ -1291,7 +1298,7 @@ GEM
|
||||
responders (3.2.0)
|
||||
actionpack (>= 7.0)
|
||||
railties (>= 7.0)
|
||||
retriable (3.2.1)
|
||||
retriable (3.4.1)
|
||||
rexml (3.4.4)
|
||||
rinku (2.0.6)
|
||||
roar (1.2.0)
|
||||
@@ -1335,7 +1342,7 @@ GEM
|
||||
rubocop-ast (>= 1.49.0, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 2.4.0, < 4.0)
|
||||
rubocop-ast (1.49.0)
|
||||
rubocop-ast (1.49.1)
|
||||
parser (>= 3.3.7.2)
|
||||
prism (~> 1.7)
|
||||
rubocop-capybara (2.22.1)
|
||||
@@ -1389,10 +1396,10 @@ GEM
|
||||
sanitize (7.0.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.16.8)
|
||||
scimitar (2.14.0)
|
||||
scimitar (2.15.0)
|
||||
rails (>= 7.0)
|
||||
securerandom (0.4.1)
|
||||
selenium-devtools (0.143.0)
|
||||
selenium-devtools (0.145.0)
|
||||
selenium-webdriver (~> 4.2)
|
||||
selenium-webdriver (4.41.0)
|
||||
base64 (~> 0.2)
|
||||
@@ -1507,7 +1514,7 @@ GEM
|
||||
activesupport
|
||||
faraday (~> 2.0)
|
||||
faraday-follow_redirects
|
||||
webmock (3.26.1)
|
||||
webmock (3.26.2)
|
||||
addressable (>= 2.8.0)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff (>= 0.4.0, < 2.0.0)
|
||||
@@ -1523,7 +1530,7 @@ GEM
|
||||
zeitwerk (>= 2.7)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
yabeda (0.15.0)
|
||||
yabeda (0.16.0)
|
||||
anyway_config (>= 1.0, < 3)
|
||||
concurrent-ruby
|
||||
dry-initializer
|
||||
@@ -1576,7 +1583,7 @@ DEPENDENCIES
|
||||
aws-sdk-core (~> 3.241)
|
||||
aws-sdk-s3 (~> 1.213)
|
||||
axe-core-rspec
|
||||
bcrypt (~> 3.1.6)
|
||||
bcrypt (~> 3.1.22)
|
||||
bootsnap (~> 1.23.0)
|
||||
brakeman (~> 8.0.1)
|
||||
browser (~> 6.2.0)
|
||||
@@ -1590,7 +1597,7 @@ DEPENDENCIES
|
||||
climate_control
|
||||
closure_tree (~> 9.6.1)
|
||||
colored2
|
||||
commonmarker (~> 2.6.0)
|
||||
commonmarker (~> 2.7.0)
|
||||
compare-xml (~> 0.66)
|
||||
connection_pool (~> 3.0.2)
|
||||
costs!
|
||||
@@ -1629,7 +1636,7 @@ DEPENDENCIES
|
||||
grids!
|
||||
html-pipeline (~> 2.14.0)
|
||||
htmldiff
|
||||
httpx (~> 1.7.3)
|
||||
httpx (~> 1.7.4)
|
||||
i18n-js (~> 4.2.4)
|
||||
i18n-tasks (~> 1.1.0)
|
||||
ice_cube (~> 0.17.0)
|
||||
@@ -1650,13 +1657,13 @@ DEPENDENCIES
|
||||
matrix (~> 0.4.3)
|
||||
mcp (~> 0.8.0)
|
||||
md_to_pdf!
|
||||
meta-tags (~> 2.22.3)
|
||||
meta-tags (~> 2.23.0)
|
||||
mini_magick (~> 5.3.0)
|
||||
multi_json (~> 1.19.0)
|
||||
my_page!
|
||||
net-ldap (~> 0.20.0)
|
||||
nokogiri (~> 1.19.1)
|
||||
oj (~> 3.16.12)
|
||||
oj (~> 3.16.16)
|
||||
okcomputer (~> 1.19.1)
|
||||
omniauth!
|
||||
omniauth-openid-connect!
|
||||
@@ -1679,17 +1686,18 @@ DEPENDENCIES
|
||||
openproject-octicons (~> 19.32.0)
|
||||
openproject-octicons_helper (~> 19.32.0)
|
||||
openproject-openid_connect!
|
||||
openproject-primer_view_components (~> 0.82.0)
|
||||
openproject-primer_view_components (~> 0.83.0)
|
||||
openproject-recaptcha!
|
||||
openproject-reporting!
|
||||
openproject-storages!
|
||||
openproject-team_planner!
|
||||
openproject-token (~> 8.8.0)
|
||||
openproject-token (~> 8.8.2)
|
||||
openproject-two_factor_authentication!
|
||||
openproject-webhooks!
|
||||
openproject-wikis!
|
||||
openproject-xls_export!
|
||||
opentelemetry-exporter-otlp (~> 0.31.0)
|
||||
opentelemetry-instrumentation-all (~> 0.90.0)
|
||||
opentelemetry-exporter-otlp (~> 0.32.0)
|
||||
opentelemetry-instrumentation-all (~> 0.91.0)
|
||||
opentelemetry-sdk (~> 1.10)
|
||||
overviews!
|
||||
ox
|
||||
@@ -1721,7 +1729,7 @@ DEPENDENCIES
|
||||
redis (~> 5.4.0)
|
||||
request_store (~> 1.7.0)
|
||||
responders (~> 3.2)
|
||||
retriable (~> 3.2.1)
|
||||
retriable (~> 3.4.1)
|
||||
rinku (~> 2.0.4)
|
||||
roar (~> 1.2.0)
|
||||
rouge (~> 4.7.0)
|
||||
@@ -1786,23 +1794,23 @@ DEPENDENCIES
|
||||
CHECKSUMS
|
||||
Ascii85 (2.0.1) sha256=15cb5d941808543cbb9e7e6aea3c8ec3877f154c3461e8b3673e97f7ecedbe5a
|
||||
action_text-trix (2.1.17) sha256=b44691639d77e67169dc054ceacd1edc04d44dc3e4c6a427aa155a2beb4cc951
|
||||
actioncable (8.1.2) sha256=dc31efc34cca9cdefc5c691ddb8b4b214c0ea5cd1372108cbc1377767fb91969
|
||||
actionmailbox (8.1.2) sha256=058b2fb1980e5d5a894f675475fcfa45c62631103d5a2596d9610ec81581889b
|
||||
actionmailer (8.1.2) sha256=f4c1d2060f653bfe908aa7fdc5a61c0e5279670de992146582f2e36f8b9175e9
|
||||
actionpack (8.1.2) sha256=ced74147a1f0daafaa4bab7f677513fd4d3add574c7839958f7b4f1de44f8423
|
||||
actioncable (8.1.2.1) sha256=a2f88cecce148b3fcb63d2e517d7694e119830a85baa7d6cf59e5453dcf32e8d
|
||||
actionmailbox (8.1.2.1) sha256=c2e45c0c1e5687e35e050838c94a8aed0d954c56a32ea411d54cd848c338c54e
|
||||
actionmailer (8.1.2.1) sha256=d7d62fbc2197f1a7006bb5af4c665edf999adf79ab6c10337c088d27e6622071
|
||||
actionpack (8.1.2.1) sha256=a6b69cd10ec4c8d978c8eee51206e34152b1c1be017e534236dbc89a3d00ffb8
|
||||
actionpack-xml_parser (2.0.1) sha256=40cb461ee99445314ab580a783fb7413580deb8b28113c9e70ecd7c1b334d5e6
|
||||
actiontext (8.1.2) sha256=0bf57da22a9c19d970779c3ce24a56be31b51c7640f2763ec64aa72e358d2d2d
|
||||
actionview (8.1.2) sha256=80455b2588911c9b72cec22d240edacb7c150e800ef2234821269b2b2c3e2e5b
|
||||
actiontext (8.1.2.1) sha256=1e503ce600a6ab2e12a46f999959a7d8e2fdaff910ca01dcf3b968934b55d957
|
||||
actionview (8.1.2.1) sha256=38daa7b87bca427e2967f139e5b7f0d1081271bdafd0e015d8ef97a006f570a6
|
||||
active_record_doctor (2.0.1) sha256=7af0ac02195385c8f2f67d0e4ebe72b1fc79d65eaaf329e0db07f4d12a84069a
|
||||
activejob (8.1.2) sha256=908dab3713b101859536375819f4156b07bdf4c232cc645e7538adb9e302f825
|
||||
activemodel (8.1.2) sha256=e21358c11ce68aed3f9838b7e464977bc007b4446c6e4059781e1d5c03bcf33e
|
||||
activejob (8.1.2.1) sha256=c89c311d07fd358b76c581ed8fee87c5b4351fb44994f3389385c014d22182fe
|
||||
activemodel (8.1.2.1) sha256=8f31a6f9c12fecb8e5a0fce8a8950cfd94f0d75829322935f99e8217a3e5f3c6
|
||||
activemodel-serializers-xml (1.0.3) sha256=fa1b16305e7254cc58a59c68833e3c0a593a59c8ab95d3be5aaea7cd9416c397
|
||||
activerecord (8.1.2) sha256=acfbe0cadfcc50fa208011fe6f4eb01cae682ebae0ef57145ba45380c74bcc44
|
||||
activerecord (8.1.2.1) sha256=3f79140318ff6d23376f5d9b1b5b5e2c7d3cc8979dd71367e9a8394378ca630a
|
||||
activerecord-import (2.2.0) sha256=f8ca99b196e50775723d1f1d192c379f656378dc9f5628240992a0d78807fa4b
|
||||
activerecord-nulldb-adapter (1.2.2) sha256=01e0b2e49af11ad56a92e274a3d8c9fb3c50a12a5460218c4c4b45355d9ef968
|
||||
activerecord-session_store (2.2.0) sha256=65918054573683bf4f87af89e765e1fece14c9d71cfac1f11abe4687c96e2743
|
||||
activestorage (8.1.2) sha256=8a63a48c3999caeee26a59441f813f94681fc35cc41aba7ce1f836add04fba76
|
||||
activesupport (8.1.2) sha256=88842578ccd0d40f658289b0e8c842acfe9af751afee2e0744a7873f50b6fdae
|
||||
activestorage (8.1.2.1) sha256=36794c9b8853ac9276b0386cb1f8973374d8e71e8a9666bb02e70f5b7c9c5391
|
||||
activesupport (8.1.2.1) sha256=beec20ced12ad569194554399449a6372fdab03061b8f48a9ed6ef9b7dc251b2
|
||||
acts_as_list (1.2.6) sha256=8345380900b7bee620c07ad00991ccee59af3d8c9e8574f426e321da2865fdc8
|
||||
acts_as_tree (2.9.1) sha256=b869eb10a8de38616b64ffcf9e882d3d99c8e06909c4057078a76c3b89a9a2f3
|
||||
addressable (2.8.9) sha256=cc154fcbe689711808a43601dee7b980238ce54368d23e127421753e46895485
|
||||
@@ -1818,17 +1826,17 @@ CHECKSUMS
|
||||
auto_strip_attributes (2.6.0) sha256=a7e2e0cf744de2bcd947fd68014220702bcc88c81274c1cd9ce6f7316aae39b0
|
||||
awesome_nested_set (3.9.0) sha256=3ce99e816550f97f4de118e621630070aacf24928b920fe4a68846578a8daaed
|
||||
aws-eventstream (1.4.0) sha256=116bf85c436200d1060811e6f5d2d40c88f65448f2125bc77ffce5121e6e183b
|
||||
aws-partitions (1.1221.0) sha256=f09304480191f5ff03f8994705067779bc8fe5b4731183ce45f092afb706e8eb
|
||||
aws-sdk-core (3.242.0) sha256=c17b3003acc78d80c1a8437b285a1cfc5e4d7749ce7821cf3071e847535a29a0
|
||||
aws-partitions (1.1227.0) sha256=122dd20fe108cb38d38cccbc1f2592408bc1b30ca6e0d05797a7af2501567e29
|
||||
aws-sdk-core (3.243.0) sha256=a014eef785124b71d28325783fa422a1512f8421ec9b6e3931c8b0ca3fbb0f1c
|
||||
aws-sdk-kms (1.122.0) sha256=47ce3f51b26bd7d76f1270cfdfca17b40073ecd3219c8c9400788712abfb4eb8
|
||||
aws-sdk-s3 (1.214.0) sha256=923135327634c873ecedbd8f396ea9939874d524b14fa65eced766660c8dd62e
|
||||
aws-sdk-s3 (1.216.0) sha256=a3bf6191e6f7a3dfb04b7cc73409f059394be559e4aff92d2a764341e4d90af4
|
||||
aws-sdk-sns (1.112.0) sha256=aff1b1b5bbcb4229599221c558a41790c1cd1a1fed47ac3d27d27512ad24b254
|
||||
aws-sigv4 (1.12.1) sha256=6973ff95cb0fd0dc58ba26e90e9510a2219525d07620c8babeb70ef831826c00
|
||||
axe-core-api (4.11.1) sha256=a6460506449a692030620a0574fee7afa6cd38cfbbf6620d20bf4d53d33a80cc
|
||||
axe-core-rspec (4.11.1) sha256=dc6c0e166405cd3a28c4a0937f6521ee5b511c12c0ca1627144a1ee7d5014aec
|
||||
axiom-types (0.1.1) sha256=c1ff113f3de516fa195b2db7e0a9a95fd1b08475a502ff660d04507a09980383
|
||||
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
|
||||
bcrypt (3.1.21) sha256=5964613d750a42c7ee5dc61f7b9336fb6caca429ba4ac9f2011609946e4a2dcf
|
||||
bcrypt (3.1.22) sha256=1f0072e88c2d705d94aff7f2c5cb02eb3f1ec4b8368671e19112527489f29032
|
||||
benchmark (0.5.0) sha256=465df122341aedcb81a2a24b4d3bd19b6c67c1530713fd533f3ff034e419236c
|
||||
better_html (2.2.0) sha256=e68ab66ab09696b708333bbf35e8aa3c107500ba7892f528e2111624bdd8cf76
|
||||
bigdecimal (3.3.1) sha256=eaa01e228be54c4f9f53bf3cc34fe3d5e845c31963e7fcc5bedb05a4e7d52218
|
||||
@@ -1853,12 +1861,13 @@ CHECKSUMS
|
||||
coercible (1.0.0) sha256=5081ad24352cc8435ce5472bc2faa30260c7ea7f2102cc6a9f167c4d9bffaadc
|
||||
color_conversion (0.1.2) sha256=99bea5fa412e1527a11389975aa6ad445ff8528ebae202c11d08c45ea2b94c96
|
||||
colored2 (4.0.3) sha256=63e1038183976287efc43034f5cca17fb180b4deef207da8ba78d051cbce2b37
|
||||
commonmarker (2.6.3-aarch64-linux) sha256=73795e80ab5ef1e4b5b83ada6f082bccb0ed7eae0b910232e13af1b2d71b14d6
|
||||
commonmarker (2.6.3-arm-linux) sha256=62b9f32d7d3f85d47988a4a98a2e66e60ca42b894687047db8332f1e80caff7b
|
||||
commonmarker (2.6.3-arm64-darwin) sha256=d6c1e4955619da3f68fed22de99dec49a24925611770c039bf870823846c8b21
|
||||
commonmarker (2.6.3-x86_64-darwin) sha256=cd8ab974bb24f675a250ea91a811b3ff70405be1c219f0052446995db6ca90c6
|
||||
commonmarker (2.6.3-x86_64-linux) sha256=e861ba1812721113725ebd8e46e4fee20dc732842f5555db2cfb8dcd74056583
|
||||
commonmarker (2.6.3-x86_64-linux-musl) sha256=2c62d2dc0d5c4efc6dde39bc5c5fac292169206601a3daf75e562d70b795d49e
|
||||
commonmarker (2.7.0-aarch64-linux) sha256=a15a47bb901f4cfcb3b4fe54d0daf91ec46e70b6e9704c67abec0ba30064b2dc
|
||||
commonmarker (2.7.0-aarch64-linux-musl) sha256=fa08eb19ffc3cf2f7132d86d74622338a0c35b480b23814a7ae63deaeafb56d3
|
||||
commonmarker (2.7.0-arm-linux) sha256=85274d47feca40bd574ac49c2959a015464936f7f830c1e2918ee147d5be1a13
|
||||
commonmarker (2.7.0-arm64-darwin) sha256=ef00f7efef4822e8e7f88be88920a319f71a251e894e02fbc82f6db5d4291db3
|
||||
commonmarker (2.7.0-x86_64-darwin) sha256=a8f5928f36138347b5e672d080b8f76dc430c6207566128b117cdd02f3e48c34
|
||||
commonmarker (2.7.0-x86_64-linux) sha256=53243eeb30e4ddb6a474672597ff7e59334e55a7653126bb87e65e6ab2629a4c
|
||||
commonmarker (2.7.0-x86_64-linux-musl) sha256=e4f5c06975f37a405bd157d276609e1c02502d2fbaf576f6e3616fba2e7b5662
|
||||
compare-xml (0.66) sha256=e21aa5c0f69ef1177eced997c688fd4df989084e74a1b612257af32e1dd05319
|
||||
concurrent-ruby (1.3.6) sha256=6b56837e1e7e5292f9864f34b69c5a2cbc75c0cf5338f1ce9903d10fa762d5ab
|
||||
connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
|
||||
@@ -1966,7 +1975,7 @@ CHECKSUMS
|
||||
htmlentities (4.3.4) sha256=125a73c6c9f2d1b62100b7c3c401e3624441b663762afa7fe428476435a673da
|
||||
http-2 (1.1.3) sha256=1b2f379d35a11dbae94f8a1a52c053d8c161eb4a0c98b5d1605ff1b2bf171c9c
|
||||
http_parser.rb (0.8.1) sha256=9ae8df145b39aa5398b2f90090d651c67bd8e2ebfe4507c966579f641e11097a
|
||||
httpx (1.7.3) sha256=126914109a58350e5ad0c13786092f35e2419857dad15745f31fc81c371f93a8
|
||||
httpx (1.7.4) sha256=91fb3e0f7325966a5da4d463a1dd7240e8550d8b0de79e346cc5dc1df1eacd2b
|
||||
i18n (1.14.8) sha256=285778639134865c5e0f6269e0b818256017e8cde89993fdfcbfb64d088824a5
|
||||
i18n-js (4.2.4) sha256=61390d372f8fa68c495c5907d577657e8cc3a7031f4945db1e91f935e1391355
|
||||
i18n-tasks (1.1.2) sha256=4dcfba49e52a623f30661cb316cb80d84fbba5cb8c6d88ef5e02545fffa3637a
|
||||
@@ -1981,7 +1990,7 @@ CHECKSUMS
|
||||
iso8601 (0.13.0) sha256=298c2b15b7be5fa95a1372813d36a2257656cd8e906dfbc1f5cb409851425aa2
|
||||
jmespath (1.6.2) sha256=238d774a58723d6c090494c8879b5e9918c19485f7e840f2c1c7532cf84ebcb1
|
||||
job-iteration (1.12.0) sha256=0164057417750f6e9c3ed548f029f1136b18eb53975fa438b09304a525d6c6c0
|
||||
json (2.19.1) sha256=dd94fdc59e48bff85913829a32350b3148156bc4fd2a95a2568a78b11344082d
|
||||
json (2.19.2) sha256=e7e1bd318b2c37c4ceee2444841c86539bc462e81f40d134cf97826cb14e83cf
|
||||
json-jwt (1.17.0) sha256=6ff99026b4c54281a9431179f76ceb81faa14772d710ef6169785199caadc4cc
|
||||
json-schema (4.3.1) sha256=d5e68dc32b94408d0b06ad04f9382ccbb6fe5a44910e066f8547f56c471a7825
|
||||
json_schemer (2.5.0) sha256=2f01fb4cce721a4e08dd068fc2030cffd0702a7f333f1ea2be6e8991f00ae396
|
||||
@@ -1990,7 +1999,7 @@ CHECKSUMS
|
||||
ladle (1.0.1) sha256=e8586964108c798d48bf57d2a65bd5602e8e5223a176b6602a0fb36c0bda90dc
|
||||
language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc
|
||||
launchy (3.1.1) sha256=72b847b5cc961589dde2c395af0108c86ff0119f42d4648d25b5440ebb10059e
|
||||
lefthook (2.1.3) sha256=399eae9411d5a65fbeff38230a685073fefac6ef9ae18165a81f5f7ffa2df7a7
|
||||
lefthook (2.1.4) sha256=b3c5bba86911e85b239fea3861ba8c74740fc084ba9ac79dba3fe79267572d6a
|
||||
letter_opener (1.10.0) sha256=2ff33f2e3b5c3c26d1959be54b395c086ca6d44826e8bf41a14ff96fdf1bdbb2
|
||||
letter_opener_web (3.0.0) sha256=3f391efe0e8b9b24becfab5537dfb17a5cf5eb532038f947daab58cb4b749860
|
||||
lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
|
||||
@@ -1998,7 +2007,7 @@ CHECKSUMS
|
||||
lobby_boy (0.1.3) sha256=9460bb3c052aef158eb3f137b8f7679ca756b8e2983d140dbdc0caa85c018172
|
||||
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
|
||||
lograge (0.14.0) sha256=42371a75823775f166f727639f5ddce73dd149452a55fc94b90c303213dc9ae1
|
||||
loofah (2.25.0) sha256=df5ed7ac3bac6a4ec802df3877ee5cc86d027299f8952e6243b3dac446b060e6
|
||||
loofah (2.25.1) sha256=d436c73dbd0c1147b16c4a41db097942d217303e1f7728704b37e4df9f6d2e04
|
||||
lookbook (2.3.14) sha256=c11a693bde9915b553c4463440ad5e750829f90bff08abdb6b8610373864cd7c
|
||||
mail (2.9.0) sha256=6fa6673ecd71c60c2d996260f9ee3dd387d4673b8169b502134659ece6d34941
|
||||
marcel (1.0.4) sha256=0d5649feb64b8f19f3d3468b96c680bae9746335d02194270287868a661516a4
|
||||
@@ -2007,10 +2016,10 @@ CHECKSUMS
|
||||
mcp (0.8.0) sha256=ae8bd146bb8e168852866fd26f805f52744f6326afb3211e073f78a95e0c34fb
|
||||
md_to_pdf (0.2.5)
|
||||
messagebird-rest (5.0.0) sha256=da4cc1efba3d5e4aa021fad07426c2cb6b326ce5670da5104bb8f6056a39d59c
|
||||
meta-tags (2.22.3) sha256=41ead5437140869717cbdd659cc6f1caa3e498b3e74b03ed63503b5b38ed504f
|
||||
meta-tags (2.23.0) sha256=ffe78b5bee398de4ff5ac3316f5a786049538a651643b8476def06c3acc762c1
|
||||
method_source (1.1.0) sha256=181301c9c45b731b4769bc81e8860e72f9161ad7d66dd99103c9ab84f560f5c5
|
||||
mime-types (3.7.0) sha256=dcebf61c246f08e15a4de34e386ebe8233791e868564a470c3fe77c00eed5e56
|
||||
mime-types-data (3.2026.0303) sha256=164af1de5824c5195d4b503b0a62062383b65c08671c792412450cd22d3bc224
|
||||
mime-types-data (3.2026.0317) sha256=77f078a4d8631d52b842ba77099734b06eddb7ad339d792e746d2272b67e511b
|
||||
mini_magick (5.3.1) sha256=29395dfd76badcabb6403ee5aff6f681e867074f8f28ce08d78661e9e4a351c4
|
||||
mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef
|
||||
minitest (6.0.2) sha256=db6e57956f6ecc6134683b4c87467d6dd792323c7f0eea7b93f66bd284adbc3d
|
||||
@@ -2034,7 +2043,7 @@ CHECKSUMS
|
||||
nokogiri (1.19.1-x86_64-darwin) sha256=7093896778cc03efb74b85f915a775862730e887f2e58d6921e3fa3d981e68bf
|
||||
nokogiri (1.19.1-x86_64-linux-gnu) sha256=1a4902842a186b4f901078e692d12257678e6133858d0566152fe29cdb98456a
|
||||
nokogiri (1.19.1-x86_64-linux-musl) sha256=4267f38ad4fc7e52a2e7ee28ed494e8f9d8eb4f4b3320901d55981c7b995fc23
|
||||
oj (3.16.15) sha256=4d3324cac3e8fef54c0fa250b2af26a16dadd9f9788a1d6b1b2098b793a1b2cd
|
||||
oj (3.16.16) sha256=3635b36128991796434f55da8decc0de236a323535adcb36fc04e6d0253c013d
|
||||
okcomputer (1.19.1) sha256=7df770e768434816d228407f0786563827cbf34cb379933578829720cb4f1e77
|
||||
omniauth (1.9.2)
|
||||
omniauth-openid-connect (0.5.0)
|
||||
@@ -2060,33 +2069,34 @@ CHECKSUMS
|
||||
openproject-octicons (19.32.1) sha256=32253f3256ad4e1aec36442558ce140623c01e5241d9b90f6eb6d317f462781e
|
||||
openproject-octicons_helper (19.32.1) sha256=7676059927ae940170fb13d62f88b885985a3f0d483e1bb246475afcffd90f8f
|
||||
openproject-openid_connect (1.0.0)
|
||||
openproject-primer_view_components (0.82.0) sha256=c3d61578d26e6fa6e4bfaf520345de01cc09b487c9841de78494017aadd65ae2
|
||||
openproject-primer_view_components (0.83.0) sha256=5e0b89f6467f5c69f017dd14723be28dcc34c842312999c954184ef5b82b83b9
|
||||
openproject-recaptcha (1.0.0)
|
||||
openproject-reporting (1.0.0)
|
||||
openproject-storages (1.0.0)
|
||||
openproject-team_planner (1.0.0)
|
||||
openproject-token (8.8.0) sha256=832a493e05dcce806134faf63ae8011cc5a48422fbed9ebb552f8028912954d4
|
||||
openproject-token (8.8.2) sha256=081cbff7269d92a82fa1d63e9e09c87b70d47d7aefadcbb80d1e7368bc2cf096
|
||||
openproject-two_factor_authentication (1.0.0)
|
||||
openproject-webhooks (1.0.0)
|
||||
openproject-wikis (1.0.0)
|
||||
openproject-xls_export (1.0.0)
|
||||
openssl (4.0.1) sha256=e27974136b7b02894a1bce46c5397ee889afafe704a839446b54dc81cb9c5f7d
|
||||
openssl-signature_algorithm (1.3.0) sha256=a3b40b5e8276162d4a6e50c7c97cdaf1446f9b2c3946a6fa2c14628e0c957e80
|
||||
opentelemetry-api (1.7.0) sha256=ccfd264ea6f2db5bf4185e3c07a1297977b44a944e2ce65457c4fe63a697214f
|
||||
opentelemetry-api (1.8.0) sha256=3af51183daf0f56a164bc1579782245be70a40678566b9a393cbe5af28ea87c6
|
||||
opentelemetry-common (0.23.0) sha256=da721190479d57bae0ad2207468f47f3e2c3b9a91024b5bc32c9d280183eb32c
|
||||
opentelemetry-exporter-otlp (0.31.1) sha256=5358be17d7849cbcc4f49e1fc24105edc780a6f96c8e57b64192ab9a8e47474a
|
||||
opentelemetry-helpers-mysql (0.4.0) sha256=d309c0b20825bdd14d4dbc75e0d3b381ffdad37d16424ceca3cb8453d9cb5a4f
|
||||
opentelemetry-exporter-otlp (0.32.0) sha256=fd4c77a07bb96919e8ff8bbd19ed96d07cac1e368d8e920af2bf2ab02bfb1ec3
|
||||
opentelemetry-helpers-mysql (0.5.0) sha256=8c2a5d5428aec271a7d2e25c158d06d4d8a914143b5004305964d1fcbc176eca
|
||||
opentelemetry-helpers-sql (0.3.0) sha256=4bb08017d6a16dd41c4d1c53c7fd30f9c5bb691195d8b458933724627b3f37f9
|
||||
opentelemetry-helpers-sql-processor (0.4.0) sha256=ec238d7a2887219bd247dc31d0eb8a1a03d414a899963b68e14bb9f4d18b23f4
|
||||
opentelemetry-instrumentation-action_mailer (0.6.1) sha256=8384866bdb066ae14b9a1fe686ffaf1f23468326a35af64390c0395fcd471057
|
||||
opentelemetry-instrumentation-action_pack (0.15.1) sha256=84fade740783caeebf260aaefcbf8f1a7a4c49f946944ff520a2fb1d6b07f273
|
||||
opentelemetry-instrumentation-action_pack (0.16.0) sha256=f4d54806b96dff89af31fb971fe5b1f79dd41fcc46489ed7c5340a47ee12a7f9
|
||||
opentelemetry-instrumentation-action_view (0.11.2) sha256=e6a099015d672dabc19993d6fca99ef1e7210361ef21549a6e2076a67719fafc
|
||||
opentelemetry-instrumentation-active_job (0.10.1) sha256=aea1311224c20d064a8f218a44299171152dc36eeb531b9eba84bed8b3942a89
|
||||
opentelemetry-instrumentation-active_model_serializers (0.24.0) sha256=8fe81e44167d17e45d9acfa588d20140c7640c323e58aca99e266de1bb3fce15
|
||||
opentelemetry-instrumentation-active_record (0.11.1) sha256=1b083f34eea0449f8d6f4370b3fb4b935757fac6e4e538e67bb98211809e7c92
|
||||
opentelemetry-instrumentation-active_storage (0.3.1) sha256=f89b0fef54921f17c0c4c38a6e0926d29afabd0ac98436fcdbb8bde85dfde89e
|
||||
opentelemetry-instrumentation-active_support (0.10.1) sha256=82ea98367158797e33c6de96581f10aa4fe8adf0ebec832dcff5fd04c59bc57d
|
||||
opentelemetry-instrumentation-all (0.90.1) sha256=7c6a1cb321fbf320618e644a2a0572881eccb7f26d27b61b7079f2dc64fc88e4
|
||||
opentelemetry-instrumentation-anthropic (0.3.0) sha256=09bd9b4ba6189389a6c0f7ba49f1d11f387d93b411ab585137a48b59925a48de
|
||||
opentelemetry-instrumentation-all (0.91.0) sha256=b077ce47da94e70e167157206034405f37ed0a4641d12ca8180a4b655c5727e2
|
||||
opentelemetry-instrumentation-anthropic (0.4.0) sha256=0040e0d97e9a66ef32cc35612ff28d7310d4ec1cd2f949805a2017f00f4d2de0
|
||||
opentelemetry-instrumentation-aws_lambda (0.6.0) sha256=1a3161393cfe9bc9eddd81a0668d076c38a0a2c3d5df40e95d02f5a8fcd3334c
|
||||
opentelemetry-instrumentation-aws_sdk (0.11.0) sha256=67a21e754ddf51e2bb8c3e46e116aa9158d8db800f34c2a9b1e0da5a6ca911e3
|
||||
opentelemetry-instrumentation-base (0.25.0) sha256=642a3a7f08354e6e969423327a4fa67ed2cca7ac6fe5ee09e55b17d1c576da27
|
||||
@@ -2094,35 +2104,35 @@ CHECKSUMS
|
||||
opentelemetry-instrumentation-concurrent_ruby (0.24.0) sha256=229bd8b72000c59de693609bb637b8a9114992f5e0ab03730d7fd7ef91f7d1d2
|
||||
opentelemetry-instrumentation-dalli (0.29.2) sha256=21b82772ced1529288c7f08285d44d5690de11f3d275e24558a062f39a270f4f
|
||||
opentelemetry-instrumentation-delayed_job (0.25.1) sha256=47f35b10d2bfd9ac7c2bbbe10dea095a2e25db2a84f5351860ead969d180c3ec
|
||||
opentelemetry-instrumentation-ethon (0.27.0) sha256=bfd2e34a5f34c7114727b0e0c9d441a1c6c7a4cceb8374d90ae9332f009f3968
|
||||
opentelemetry-instrumentation-excon (0.27.0) sha256=3d7e6e160f0328e1136646aabb23efefdb125854637d5bd57b849720f783b5bb
|
||||
opentelemetry-instrumentation-faraday (0.31.0) sha256=1c00dc96d4c18890a34a20eef27eae536bf6558965e03e254bb7b84a4f09840b
|
||||
opentelemetry-instrumentation-grape (0.5.1) sha256=a623608ef10e96c413f4d50b840082bf1ab9700126185d89ddbc8a29b49ec0ef
|
||||
opentelemetry-instrumentation-ethon (0.28.0) sha256=5ab5eb0733fec27300047f1f0906453171732c663d0484968ce0582026256b2d
|
||||
opentelemetry-instrumentation-excon (0.28.0) sha256=00bfd0bce489d5f924ab81c440098e99b6e4234f8968f942ce0753e2a326b99b
|
||||
opentelemetry-instrumentation-faraday (0.32.0) sha256=21f78858c4d8986a9b89a330bc1f6ef03007d6893d009865b4539269f686cdfd
|
||||
opentelemetry-instrumentation-grape (0.6.0) sha256=bc6f0ac3416b42bf096032ab79193326d6b50b12e8ccbcf028a78a4df492d057
|
||||
opentelemetry-instrumentation-graphql (0.31.2) sha256=a4455f225427f8f9058247c8c0b351b8932567913c35ef049f7958801d401b1f
|
||||
opentelemetry-instrumentation-grpc (0.4.1) sha256=5ffa2bb1d5ec69bcd1fe23e1d8c1a563a00351ce052fe9d76885cc43f21ebc87
|
||||
opentelemetry-instrumentation-gruf (0.5.0) sha256=ee21be36e312e71b847c9a87168225625890121140a364b68d3668e0df58dacd
|
||||
opentelemetry-instrumentation-http (0.28.0) sha256=0946a9593d64740780d16e041f9441945c4970b17566ce98e1a63bd3b276cacd
|
||||
opentelemetry-instrumentation-http_client (0.27.0) sha256=6a639a21296d3b6d3934b2d5465bee5d5f9180208d0329a5bbe34b374030690e
|
||||
opentelemetry-instrumentation-httpx (0.6.1) sha256=9050801826bb0f148f603b39b119c4f49591955ee53d3d0b2a8fb1eb8f558022
|
||||
opentelemetry-instrumentation-http (0.29.0) sha256=c2981f22dac791f1768595c08b5338d29ad57bd98e23e9a2c0df7a1dc54122f1
|
||||
opentelemetry-instrumentation-http_client (0.28.0) sha256=f6dadfed166d75d5632ae0b3521ed6a491080972923031489b85711e6d58fcb8
|
||||
opentelemetry-instrumentation-httpx (0.7.0) sha256=3928185b62066cf6d8fe3b011dc5587ba53b09a5c7b573e36481b8d713d6aa03
|
||||
opentelemetry-instrumentation-koala (0.23.0) sha256=8f324b50a2a64fd4994bb2b105a4cb0c80b64ec05cf5487d2daa906c650bc6f9
|
||||
opentelemetry-instrumentation-lmdb (0.25.0) sha256=1e4d66d583ea242d4f72051062971f5af1ea353484d224abbd0aabdd1ce5f5cb
|
||||
opentelemetry-instrumentation-mongo (0.25.0) sha256=d04585669f928ea82e7c469f996061d39d8ff184278d57cf4fc77a6d607f9c7a
|
||||
opentelemetry-instrumentation-mysql2 (0.33.0) sha256=b49b7957d5eef59e046e73be3ca370518965d61495745b4cb7ece3ef5470bcf9
|
||||
opentelemetry-instrumentation-net_http (0.27.0) sha256=f7ad7c5887646a09043b5f4e73717d9a897272f8032b883b0fadffbcc27f8990
|
||||
opentelemetry-instrumentation-net_http (0.28.0) sha256=63b00c1c8fcfba15cd293ece8383d19bbc35e9b5cc04056b3e95799be11026f5
|
||||
opentelemetry-instrumentation-pg (0.35.0) sha256=65a6e78bd45282b56021f1ee1b88b9fd318abf6812c32bd740465e6b9997aad4
|
||||
opentelemetry-instrumentation-que (0.12.0) sha256=3b7a84341f6af5a04f8c57860aeba4033f87c855d40c611a2fc40dde849944fb
|
||||
opentelemetry-instrumentation-racecar (0.6.1) sha256=833f6611906fb661f577e841d4ec52549474d32b4e8edea8048162348d35b845
|
||||
opentelemetry-instrumentation-rack (0.29.0) sha256=9e2cbb8336087064cbe33b502d917d85b174162bc717efda1cfdbd182342f377
|
||||
opentelemetry-instrumentation-rails (0.39.1) sha256=7959df7895543040fbb5cd3877c37bc9f95d79ff9d7749334314c50b871ac96f
|
||||
opentelemetry-instrumentation-rack (0.30.0) sha256=30a54f7b44d4b91839622a20eb0b25a7c47084b37c2b03cfc149bfc4ef62303c
|
||||
opentelemetry-instrumentation-rails (0.40.0) sha256=f794d477e8b48d9167ac1dbaf71dfc88e2a5647f76394cab7d1dfc6d5217b983
|
||||
opentelemetry-instrumentation-rake (0.5.0) sha256=fa6bd019078975ac8a67eaea06294e4fe6707e6770d8ced88d74dc573b0a01ef
|
||||
opentelemetry-instrumentation-rdkafka (0.9.0) sha256=f3beb56828c584d7d91a2c46f6e5a2ef82289b1d4445b1eb5bc13b80ab6aca89
|
||||
opentelemetry-instrumentation-redis (0.28.0) sha256=8721957d1c527dd22bd564d17f3a8db252081abb302be189511282d023693900
|
||||
opentelemetry-instrumentation-resque (0.8.0) sha256=559edde9d6273dd757ae5149ed36e26d147b63028d084121203f51c8cff805e5
|
||||
opentelemetry-instrumentation-restclient (0.26.0) sha256=5d4e9d93ef51564a1023c076e17b4ac3b42fe81003321d9fb66e44886538bdce
|
||||
opentelemetry-instrumentation-restclient (0.27.0) sha256=1abe208f5f43eff8648fa3ec3393c021bcbf30512f0fd69e4edbe8345ac3f899
|
||||
opentelemetry-instrumentation-ruby_kafka (0.24.0) sha256=257e891f4ce630ba3e0669408d497b44afcc493cd49aed09343d5a51fa8952c2
|
||||
opentelemetry-instrumentation-sidekiq (0.28.1) sha256=abc85d62996a5362e7a9fd7af9f6c709d01ce04795514d12fee5126335ae97ae
|
||||
opentelemetry-instrumentation-sinatra (0.28.0) sha256=9f11d68c580a421cadd633aca1f8f92707d6b6995d48fffa045a48c187347f26
|
||||
opentelemetry-instrumentation-trilogy (0.66.0) sha256=f09dead2fc09b84e5f84a9e39bdd351486549724f47471766230774831ecbe70
|
||||
opentelemetry-instrumentation-sinatra (0.29.0) sha256=08595fec08d198df581d96aceb4b27998b84431e44a679950af7d00ab6559bdb
|
||||
opentelemetry-instrumentation-trilogy (0.67.0) sha256=40394d3071d92aa418ef5aedab8e74f7683c0566c285a5418f75ca0586fd025f
|
||||
opentelemetry-registry (0.4.0) sha256=903fa6bfaa29eac1c1d73a4fdd29b850977b5353b84b8cdff11222c00ad2968f
|
||||
opentelemetry-sdk (1.10.0) sha256=43719949be8df24dcaeb86ebbf75636cda87d51a01af2729499b92a48b80521a
|
||||
opentelemetry-semantic_conventions (1.36.0) sha256=c1b1607dbc7853aac7f9e23f6e8b76969c45b07f2b812a4aa4383c19a3b0f617
|
||||
@@ -2131,7 +2141,7 @@ CHECKSUMS
|
||||
ostruct (0.6.3) sha256=95a2ed4a4bd1d190784e666b47b2d3f078e4a9efda2fccf18f84ddc6538ed912
|
||||
overviews (1.0.0)
|
||||
ox (2.14.23) sha256=4a9aedb4d6c78c5ebac1d7287dc7cc6808e14a8831d7adb727438f6a1b461b66
|
||||
pagy (43.3.2) sha256=105683fccb86c1011a96c76da610c35c64aaf4cf412165606aadcb39e733e830
|
||||
pagy (43.4.0) sha256=5962b476bab4b96aa6029be76d537dd9597aa00454508aecee001517d9744a9b
|
||||
paper_trail (17.0.0) sha256=1c2842061d3874ca7015908e821e2aa14f9b982af2acb2a7974713bf79021c85
|
||||
parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130
|
||||
parallel_tests (4.10.1) sha256=df05458c691462b210f7a41fc2651d4e4e8a881e8190e6d1e122c92c07735d70
|
||||
@@ -2165,7 +2175,7 @@ CHECKSUMS
|
||||
pry-rescue (1.6.0) sha256=985bfd506d9866b587fd86790cf8445266a41b7f92c627fc5b21ec7d92aba6db
|
||||
psych (5.3.1) sha256=eb7a57cef10c9d70173ff74e739d843ac3b2c019a003de48447b2963d81b1974
|
||||
public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623
|
||||
puffing-billy (4.0.3) sha256=376fe2e2cc3ff9d48814a15153db80970cf0539ba026ac5108c971c2e160883c
|
||||
puffing-billy (4.0.4) sha256=87015b0c41e0722b2171a0c5aa8130fd3f58aa1c016a1dc6dc569b2028aa846f
|
||||
puma (7.2.0) sha256=bf8ef4ab514a4e6d4554cb4326b2004eba5036ae05cf765cfe51aba9706a72a8
|
||||
puma-plugin-statsd (2.7.0) sha256=04f243a7233f4d06ec0e26f1a3522bce18a5910ae711763fabff22681bdad08b
|
||||
raabro (1.4.0) sha256=d4fa9ff5172391edb92b242eed8be802d1934b1464061ae5e70d80962c5da882
|
||||
@@ -2181,12 +2191,12 @@ CHECKSUMS
|
||||
rack-timeout (0.7.0) sha256=757337e9793cca999bb73a61fe2a7d4280aa9eefbaf787ce3b98d860749c87d9
|
||||
rack_session_access (0.2.0) sha256=03eb98f2027429ccbbeb18556006dfb6d928b0557ad3770783b8e2f368198d6b
|
||||
rackup (1.0.1) sha256=ba86604a28989fe1043bff20d819b360944ca08156406812dca6742b24b3c249
|
||||
rails (8.1.2) sha256=5069061b23dfa8706b9f0159ae8b9d35727359103178a26962b868a680ba7d95
|
||||
rails (8.1.2.1) sha256=93ebf1efc792c9bc47e9795259c920312d3920008dad3ae634b7a0457ffe0af8
|
||||
rails-controller-testing (1.0.5) sha256=741448db59366073e86fc965ba403f881c636b79a2c39a48d0486f2607182e94
|
||||
rails-dom-testing (2.3.0) sha256=8acc7953a7b911ca44588bf08737bc16719f431a1cc3091a292bca7317925c1d
|
||||
rails-html-sanitizer (1.7.0) sha256=28b145cceaf9cc214a9874feaa183c3acba036c9592b19886e0e45efc62b1e89
|
||||
rails-i18n (8.1.0) sha256=52d5fd6c0abef28d84223cc05647f6ae0fd552637a1ede92deee9545755b6cf3
|
||||
railties (8.1.2) sha256=1289ece76b4f7668fc46d07e55cc992b5b8751f2ad85548b7da351b8c59f8055
|
||||
railties (8.1.2.1) sha256=f4d902869541af4e5b5552d726062fa59ec0fd9078f7ab87720dbd93f22c43ee
|
||||
rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
|
||||
rake (13.3.1) sha256=8c9e89d09f66a26a01264e7e3480ec0607f0c497a861ef16063604b1b08eb19c
|
||||
rake-compiler-dock (1.11.0) sha256=eab51f2cd533eb35cea6b624a75281f047123e70a64c58b607471bb49428f8c2
|
||||
@@ -2199,13 +2209,13 @@ CHECKSUMS
|
||||
recaptcha (5.21.1) sha256=e003e9ceba9b993a9f0c6a828c192f2d46693cd2aa0b0beae94f936649507adb
|
||||
redcarpet (3.6.1) sha256=d444910e6aa55480c6bcdc0cdb057626e8a32c054c29e793fa642ba2f155f445
|
||||
redis (5.4.1) sha256=b5e675b57ad22b15c9bcc765d5ac26f60b675408af916d31527af9bd5a81faae
|
||||
redis-client (0.26.4) sha256=3ad70beff5da2653e02dfeae996e7d8d7147a558da12b16b2282ad345e4c7120
|
||||
redis-client (0.28.0) sha256=888892f9cd8787a41c0ece00bdf5f556dfff7770326ce40bb2bc11f1bfec824b
|
||||
regexp_parser (2.11.3) sha256=ca13f381a173b7a93450e53459075c9b76a10433caadcb2f1180f2c741fc55a4
|
||||
reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835
|
||||
representable (3.2.0) sha256=cc29bf7eebc31653586849371a43ffe36c60b54b0a6365b5f7d95ec34d1ebace
|
||||
request_store (1.7.0) sha256=e1b75d5346a315f452242a68c937ef8e48b215b9453a77a6c0acdca2934c88cb
|
||||
responders (3.2.0) sha256=89c2d6ac0ae16f6458a11524cae4a8efdceba1a3baea164d28ee9046bd3df55a
|
||||
retriable (3.2.1) sha256=26e87a33391fae4c382d4750f1e135e4dda7e5aa32b6b71f1992265981f9b991
|
||||
retriable (3.4.1) sha256=fb3f114b7d492121c158c01f3d5152b5a615c5b70d5877d0bc08c7ec3725c3bc
|
||||
rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
|
||||
rinku (2.0.6) sha256=8b60670e3143f3db2b37efa262971ce3619ec23092045498ef9f077d82828d7d
|
||||
roar (1.2.0) sha256=8db4d1ca79c57a5fb746c16c0d5661d7c3e0de3d9553dc016a88d2dba2929d08
|
||||
@@ -2220,7 +2230,7 @@ CHECKSUMS
|
||||
rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
|
||||
rspec-wait (1.0.2) sha256=865f921239325d3d26fc10ded4bdd485d8b58bcaaad1a28dd85ed15266b5a912
|
||||
rubocop (1.85.1) sha256=3dbcf9e961baa4c376eeeb2a03913dca5e3987033b04d38fa538aa1e7406cc77
|
||||
rubocop-ast (1.49.0) sha256=49c3676d3123a0923d333e20c6c2dbaaae2d2287b475273fddee0c61da9f71fd
|
||||
rubocop-ast (1.49.1) sha256=4412f3ee70f6fe4546cc489548e0f6fcf76cafcfa80fa03af67098ffed755035
|
||||
rubocop-capybara (2.22.1) sha256=ced88caef23efea53f46e098ff352f8fc1068c649606ca75cb74650970f51c0c
|
||||
rubocop-factory_bot (2.28.0) sha256=4b17fc02124444173317e131759d195b0d762844a71a29fe8139c1105d92f0cb
|
||||
rubocop-openproject (0.3.0) sha256=9554496e7ef0a2cf65dc2b32bee1bfa223b4f9ae058a5c603489d34e9001a828
|
||||
@@ -2241,9 +2251,9 @@ CHECKSUMS
|
||||
rubyzip (2.4.1) sha256=8577c88edc1fde8935eb91064c5cb1aef9ad5494b940cf19c775ee833e075615
|
||||
safety_net_attestation (0.5.0) sha256=c8cd01dd550dbe8553862918af6355a04672db11d218ec96104ce3955293f2aa
|
||||
sanitize (7.0.0) sha256=269d1b9d7326e69307723af5643ec032ff86ad616e72a3b36d301ac75a273984
|
||||
scimitar (2.14.0) sha256=93b29132ebd50d78e61d56c8a17406b1c01857448f2d075560fd6c7660ac9aa9
|
||||
scimitar (2.15.0) sha256=700901afab9303b705a8f37644cd733ee3c3819b168d24a14a48dec060b16d63
|
||||
securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
|
||||
selenium-devtools (0.143.0) sha256=23e8b364e1074a93a56ea0365ff739022a23a72e9033ad69832400c884417dc4
|
||||
selenium-devtools (0.145.0) sha256=629c4964ebeb140f0e08cd6e88e241b07a995416a0e45d8416ba25cf4a7f0513
|
||||
selenium-webdriver (4.41.0) sha256=cdc1173cd55cf186022cea83156cc2d0bec06d337e039b02ad25d94e41bedd22
|
||||
semantic (1.6.1) sha256=3cdbb48f59198ebb782a3fdfb87b559e0822a311610db153bae22777a7d0c163
|
||||
shoulda-context (2.0.0) sha256=7adf45342cd800f507d2a053658cb1cce2884b616b26004d39684b912ea32c34
|
||||
@@ -2299,7 +2309,7 @@ CHECKSUMS
|
||||
warden-basic_auth (0.2.1) sha256=bfc752e0109c0182c3e69e930284c5e1e81e7b4a354aeb2b5914ead1391f3c6e
|
||||
webauthn (3.4.3) sha256=9be6f5f838f3405b0226e560aa40b67cc8c15ec9154509b997caa7ec9a05e1fc
|
||||
webfinger (2.1.3) sha256=567a52bde77fb38ca6b67e55db755f988766ec4651c1d24916a65aa70540695c
|
||||
webmock (3.26.1) sha256=4f696fb57c90a827c20aadb2d4f9058bbff10f7f043bd0d4c3f58791143b1cd7
|
||||
webmock (3.26.2) sha256=774556f2ea6371846cca68c01769b2eac0d134492d21f6d0ab5dd643965a4c90
|
||||
webrick (1.9.2) sha256=beb4a15fc474defed24a3bda4ffd88a490d517c9e4e6118c3edce59e45864131
|
||||
websocket (1.2.11) sha256=b7e7a74e2410b5e85c25858b26b3322f29161e300935f70a0e0d3c35e0462737
|
||||
websocket-driver (0.8.0) sha256=ed0dba4b943c22f17f9a734817e808bc84cdce6a7e22045f5315aa57676d4962
|
||||
@@ -2307,7 +2317,7 @@ CHECKSUMS
|
||||
will_paginate (4.0.1) sha256=107b226ebe1d393d274575956a7c472e1eefdd97d8828e01b72d425d15a875b9
|
||||
with_advisory_lock (7.5.0) sha256=dadb2f1ed35a10ed7b9649a6769e6848bc64f735a85eb8a6e162a81d383a15bf
|
||||
xpath (3.2.0) sha256=6dfda79d91bb3b949b947ecc5919f042ef2f399b904013eb3ef6d20dd3a4082e
|
||||
yabeda (0.15.0) sha256=81ce61c6e89d42ac3c5855aa2dcddfc2347c33d1595372d59d79e7dda4e72238
|
||||
yabeda (0.16.0) sha256=7f6e51acd7d9a51d850ea8c3844f72a24882f1312b3fc3836052bcd63d384cba
|
||||
yabeda-activerecord (0.1.2) sha256=1dd281a64e5742445a6718aa05e799ea08a397e9ab9c0d254ece447635a3e0e2
|
||||
yabeda-prometheus-mmap (0.4.0) sha256=1a66120756d6f931f03a7784e08e79060d71681ff83a9f5287df2ff756e9e2c9
|
||||
yabeda-puma-plugin (0.9.0) sha256=b78673ecc7ee30bc50691ddc41b7022c1c1801843900d5101418f4a14b550bc8
|
||||
|
||||
@@ -40,6 +40,7 @@ group :opf_plugins do
|
||||
gem 'openproject-gantt', path: 'modules/gantt'
|
||||
gem 'openproject-calendar', path: 'modules/calendar'
|
||||
gem 'openproject-storages', path: 'modules/storages'
|
||||
gem 'openproject-wikis', path: 'modules/wikis'
|
||||
gem 'openproject-documents', path: 'modules/documents'
|
||||
|
||||
gem 'openproject-bim', path: 'modules/bim'
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
@import "step_wizard/footer_component"
|
||||
@import "step_wizard/page_layout_component"
|
||||
@import "users/hover_card_component"
|
||||
@import "users/non_working_times/calendar_component"
|
||||
@import "work_package_relations_tab/index_component"
|
||||
@import "work_package_relations_tab/relation_component"
|
||||
@import "work_package_types/pattern_input"
|
||||
|
||||
@@ -34,7 +34,10 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<% else %>
|
||||
<%= @event.event_title %>
|
||||
<% end %>
|
||||
<%= project_suffix %>
|
||||
<% suffix = project_suffix %>
|
||||
<% if suffix.present? %>
|
||||
(<%= suffix %>)
|
||||
<% end %>
|
||||
</div>
|
||||
<%=
|
||||
render(
|
||||
|
||||
@@ -45,8 +45,7 @@ class Activities::ItemComponent < ViewComponent::Base
|
||||
return if activity_is_from_current_project?
|
||||
|
||||
kind = activity_is_from_subproject? ? "subproject" : "project"
|
||||
suffix = I18n.t("events.title.#{kind}", name: link_to(@event.project.name, @event.project))
|
||||
"(#{suffix})".html_safe # rubocop:disable Rails/OutputSafety
|
||||
helpers.t("events.title.#{kind}_html", name: link_to(@event.project.name, @event.project))
|
||||
end
|
||||
|
||||
def display_user?
|
||||
|
||||
@@ -29,10 +29,11 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
|
||||
<div class="op-activity-list--item-subtitle">
|
||||
<%=
|
||||
# OG: html_safe usage has been double-checked and would otherwise require a lot of i18n key change
|
||||
I18n.t(
|
||||
i18n_key,
|
||||
user: user_html,
|
||||
datetime: datetime_html
|
||||
).html_safe
|
||||
user: h(user_html),
|
||||
datetime: h(datetime_html)
|
||||
).html_safe # rubocop:disable Rails/OutputSafety
|
||||
%>
|
||||
</div>
|
||||
|
||||
@@ -42,10 +42,12 @@ class Activities::ItemSubtitleComponent < ViewComponent::Base
|
||||
def user_html
|
||||
return unless @user
|
||||
|
||||
[
|
||||
parts = [
|
||||
helpers.avatar(@user, size: "mini"),
|
||||
helpers.content_tag("span", helpers.link_to_user(@user), class: %w[spot-caption spot-caption_bold])
|
||||
].join(" ")
|
||||
]
|
||||
|
||||
safe_join(parts, " ")
|
||||
end
|
||||
|
||||
def datetime_html
|
||||
|
||||
@@ -48,7 +48,7 @@ module EnterpriseEdition
|
||||
def description
|
||||
@description || begin
|
||||
if I18n.exists?(:description_html, scope: i18n_scope)
|
||||
I18n.t(:description_html, scope: i18n_scope).html_safe
|
||||
helpers.t(:description_html, scope: i18n_scope)
|
||||
else
|
||||
I18n.t(:description, scope: i18n_scope)
|
||||
end
|
||||
@@ -85,7 +85,7 @@ module EnterpriseEdition
|
||||
I18n.t("ee.upsell.plan_name", plan: plan.capitalize)
|
||||
end
|
||||
|
||||
I18n.t("ee.upsell.plan_text_html", plan_name:).html_safe
|
||||
helpers.t("ee.upsell.plan_text_html", plan_name:)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -65,7 +65,7 @@ module EnterpriseEdition
|
||||
end
|
||||
|
||||
def description
|
||||
I18n.t("ee.teaser.description", trial_plan: plan_name).html_safe
|
||||
helpers.t("ee.teaser.description_html", trial_plan: plan_name)
|
||||
end
|
||||
|
||||
def plan_name
|
||||
|
||||
@@ -31,7 +31,14 @@
|
||||
module Groups
|
||||
class RowComponent < OpPrimer::BorderBoxRowComponent
|
||||
def name
|
||||
render(Primer::Beta::Link.new(href: edit_group_path(model), font_weight: :bold)) { model.name }
|
||||
depth = model.hierarchy_depth || 0
|
||||
link = render(Primer::Beta::Link.new(href: edit_group_path(model), font_weight: :bold)) { model.name }
|
||||
|
||||
if depth > 0
|
||||
tag.span(style: "margin-left: #{depth * 20}px") { link }
|
||||
else
|
||||
link
|
||||
end
|
||||
end
|
||||
|
||||
def user_count
|
||||
|
||||
@@ -113,6 +113,12 @@ module Members
|
||||
end
|
||||
end
|
||||
|
||||
def filter_group(query, group_id)
|
||||
if group_id.present?
|
||||
query.where(:group_hierarchy, "=", group_id)
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def filter_shares(query, role_id)
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module My
|
||||
class WorkingTimesHeaderComponent < ApplicationComponent
|
||||
def call
|
||||
render(Primer::OpenProject::PageHeader.new) do |header|
|
||||
header.with_title { t(:label_schedule_and_availability) }
|
||||
header.with_breadcrumbs(
|
||||
[{ href: my_account_path, text: t(:label_my_account) },
|
||||
t(:label_schedule_and_availability)]
|
||||
)
|
||||
|
||||
helpers.render_tab_header_nav(header, tabs)
|
||||
end
|
||||
end
|
||||
|
||||
def tabs
|
||||
[
|
||||
{
|
||||
name: "working_hours",
|
||||
path: my_working_hours_path,
|
||||
label: t(:label_working_hours)
|
||||
},
|
||||
{
|
||||
name: "non_working_times",
|
||||
path: my_non_working_times_path(year: Date.current.year),
|
||||
label: t(:label_non_working_days)
|
||||
}
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -78,24 +78,20 @@ module OpenProject
|
||||
|
||||
private
|
||||
|
||||
def first_paragraph_content
|
||||
return unless first_paragraph_ast
|
||||
|
||||
first_paragraph_ast
|
||||
.inner_html
|
||||
.html_safe # rubocop:disable Rails/OutputSafety
|
||||
end
|
||||
|
||||
# rubocop:disable Rails/OutputSafety
|
||||
# OG: html_safe double-checked and expected here,
|
||||
# output is coming from format_text which we output elsewhere, too.
|
||||
def first_paragraph
|
||||
@first_paragraph ||= if body_children.any?
|
||||
body_children
|
||||
.first
|
||||
.inner_html
|
||||
.html_safe # rubocop:disable Rails/OutputSafety
|
||||
.html_safe
|
||||
else
|
||||
""
|
||||
end
|
||||
end
|
||||
# rubocop:enable Rails/OutputSafety
|
||||
|
||||
def first_paragraph_ast
|
||||
@first_paragraph_ast ||= text_ast
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
<%= component_wrapper(tag: :div, class: "op-inplace-edit", data: { test_selector: wrapper_test_selector }) do %>
|
||||
<% if display_field_component.present? && !enforce_edit_mode %>
|
||||
<%= component_wrapper(
|
||||
tag: :div,
|
||||
class: "op-inplace-edit",
|
||||
uniq_by: wrapper_uniq_by,
|
||||
data: {
|
||||
test_selector: wrapper_test_selector,
|
||||
turbo_stream_target: wrapper_id,
|
||||
inplace_edit_stable_key: wrapper_uniq_by,
|
||||
inplace_edit_system_arguments: @system_arguments.to_json
|
||||
}
|
||||
) do %>
|
||||
<% if display_field_component.present? && (!enforce_edit_mode || !writable?) %>
|
||||
<%= render display_field_component %>
|
||||
<% else %>
|
||||
<%= primer_form_with(
|
||||
model:,
|
||||
url: inplace_edit_field_update_path(model: model.class.name, id: model.id, attribute:),
|
||||
method: :patch,
|
||||
data: { turbo_stream: true }
|
||||
) do |form|
|
||||
render_field_component = ->(f) { render edit_field_component(f) } # The render_inline_form method looses context and thus does not know about the `field_component` method
|
||||
system_arguments = @system_arguments
|
||||
<%= primer_form_with(**form_options) do |form|
|
||||
render_field_component = ->(f) { render edit_field_component(f) } # The render_inline_form method looses context and thus does not know about the `field_component` method
|
||||
system_arguments = @system_arguments
|
||||
|
||||
render_inline_form(form) do |f|
|
||||
f.hidden name: "system_arguments_json", value: system_arguments.to_json
|
||||
render_field_component.call(f)
|
||||
end
|
||||
render_inline_form(form) do |f|
|
||||
f.hidden name: "system_arguments_json", value: system_arguments.to_json
|
||||
render_field_component.call(f)
|
||||
end
|
||||
end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -33,21 +33,38 @@ module OpenProject
|
||||
class InplaceEditFieldComponent < ViewComponent::Base
|
||||
include OpTurbo::Streamable
|
||||
|
||||
attr_reader :model, :attribute, :enforce_edit_mode
|
||||
attr_reader :model, :attribute, :enforce_edit_mode, :open_in_dialog, :show_action_buttons, :truncated
|
||||
|
||||
def initialize(model:, attribute:, enforce_edit_mode: false,
|
||||
update_registry: OpenProject::InplaceEdit::UpdateRegistry.default, **system_arguments)
|
||||
def initialize(model:,
|
||||
attribute:,
|
||||
enforce_edit_mode: false,
|
||||
open_in_dialog: false,
|
||||
show_action_buttons: true,
|
||||
truncated: false,
|
||||
update_registry: OpenProject::InplaceEdit::UpdateRegistry.default,
|
||||
**system_arguments)
|
||||
super()
|
||||
@model = model
|
||||
@attribute = attribute
|
||||
@enforce_edit_mode = enforce_edit_mode
|
||||
@open_in_dialog = open_in_dialog
|
||||
@show_action_buttons = show_action_buttons
|
||||
@truncated = truncated
|
||||
@update_registry = update_registry
|
||||
@system_arguments = system_arguments
|
||||
|
||||
@system_arguments[:id] = system_arguments[:id] || SecureRandom.uuid
|
||||
@system_arguments[:required] ||= required?
|
||||
@system_arguments[:label] ||= field_label
|
||||
@system_arguments[:truncated] = truncated
|
||||
end
|
||||
|
||||
def field_class
|
||||
OpenProject::InplaceEdit::FieldRegistry.fetch(attribute)
|
||||
if custom_field?
|
||||
OpenProject::InplaceEdit::FieldRegistry.fetch_for_custom_field_format(custom_field&.field_format)
|
||||
else
|
||||
OpenProject::InplaceEdit::FieldRegistry.fetch(attribute)
|
||||
end
|
||||
end
|
||||
|
||||
def edit_field_component(form)
|
||||
@@ -55,6 +72,7 @@ module OpenProject
|
||||
form:,
|
||||
attribute:,
|
||||
model:,
|
||||
show_action_buttons:,
|
||||
**@system_arguments
|
||||
)
|
||||
end
|
||||
@@ -70,20 +88,102 @@ module OpenProject
|
||||
def display_field_component
|
||||
return nil if display_field_class.nil?
|
||||
|
||||
display_field_class.new(model:, attribute:, writable: writable?, **@system_arguments)
|
||||
@display_field_component ||= build_display_field_component
|
||||
end
|
||||
|
||||
def wrapper_key
|
||||
model_class = @model.class.name.parameterize(separator: "_")
|
||||
"op-inplace-edit-field--#{model_class}-#{model.id}--#{attribute.name}--#{@system_arguments[:id]}"
|
||||
end
|
||||
|
||||
def wrapper_test_selector
|
||||
"op-inplace-edit-field"
|
||||
"op-inplace-edit-field--#{model_class}-#{model.id}--#{attribute.name}"
|
||||
end
|
||||
|
||||
def wrapper_uniq_by
|
||||
"#{model_class}_#{@model.id}_#{@attribute}"
|
||||
end
|
||||
|
||||
def form_id
|
||||
@system_arguments[:form_id]
|
||||
end
|
||||
|
||||
def wrapper_id
|
||||
@system_arguments[:wrapper_id]
|
||||
end
|
||||
|
||||
def form_options
|
||||
options = {
|
||||
model: @model,
|
||||
url: inplace_edit_field_update_path(
|
||||
model: @model.class.name,
|
||||
id: @model.id,
|
||||
attribute: @attribute
|
||||
),
|
||||
method: :patch,
|
||||
data: { turbo_stream: true,
|
||||
test_selector: "op-inplace-edit-field--form" }
|
||||
|
||||
}
|
||||
|
||||
options[:id] = form_id if form_id.present?
|
||||
options
|
||||
end
|
||||
|
||||
def open_in_dialog?
|
||||
@open_in_dialog || field_class.open_in_dialog? || (custom_field? && custom_field&.has_comment?)
|
||||
end
|
||||
|
||||
def dialog_edit_url
|
||||
return unless open_in_dialog?
|
||||
|
||||
inplace_edit_field_dialog_path(
|
||||
model: model.class.name,
|
||||
id: model.id,
|
||||
attribute:,
|
||||
system_arguments_json: @system_arguments
|
||||
.except(:id)
|
||||
.merge(page_component_id: @system_arguments[:id], writable: writable?)
|
||||
.to_json
|
||||
)
|
||||
end
|
||||
|
||||
def model_class
|
||||
@model_class ||= @model.class.name.parameterize(separator: "_")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_display_field_component
|
||||
has_comment = custom_field? && custom_field&.has_comment?
|
||||
additional_args = open_in_dialog? ? dialog_display_arguments : {}
|
||||
display_field_class.new(
|
||||
model:,
|
||||
attribute:,
|
||||
writable: writable?,
|
||||
truncated:,
|
||||
has_comment:,
|
||||
# Show comment as read-only text when a non-writable user opens the dialog.
|
||||
# enforce_edit_mode identifies the dialog context.
|
||||
show_comment: enforce_edit_mode && !writable? && has_comment,
|
||||
**@system_arguments.merge(additional_args)
|
||||
)
|
||||
end
|
||||
|
||||
def dialog_trigger_arguments
|
||||
{
|
||||
dialog_controller_name: "inplace-edit",
|
||||
dialog_url: dialog_edit_url
|
||||
}
|
||||
end
|
||||
|
||||
# When inside a dialog and the field is not writable, strip dialog trigger args
|
||||
# to prevent opening a nested dialog from the display component.
|
||||
def dialog_display_arguments
|
||||
return {} if enforce_edit_mode && !writable?
|
||||
|
||||
dialog_trigger_arguments
|
||||
end
|
||||
|
||||
def writable?
|
||||
return @writable if defined?(@writable)
|
||||
|
||||
@@ -95,6 +195,40 @@ module OpenProject
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def field_label
|
||||
# Check if this is a custom field attribute
|
||||
if custom_field? && custom_field
|
||||
return custom_field.name
|
||||
end
|
||||
|
||||
label = model.class.human_attribute_name(attribute)
|
||||
label = label.titleize if attribute.to_s.include?("_")
|
||||
label
|
||||
end
|
||||
|
||||
def required?
|
||||
return @required if instance_variable_defined?(:@required)
|
||||
|
||||
@required = if @system_arguments.key?(:required)
|
||||
@system_arguments[:required]
|
||||
elsif custom_field?
|
||||
# For custom fields, check the is_required attribute
|
||||
custom_field&.is_required || false
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def custom_field?
|
||||
attribute.to_s.start_with?("custom_field_")
|
||||
end
|
||||
|
||||
def custom_field
|
||||
return @custom_field if defined?(@custom_field)
|
||||
|
||||
@custom_field = CustomField.find_by(id: attribute.to_s.sub("custom_field_", "").to_i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<%=
|
||||
render(
|
||||
Primer::Alpha::Dialog.new(
|
||||
title: dialog_title,
|
||||
classes: "Overlay--size-large-portrait op-inplace-edit--dialog",
|
||||
size: :large,
|
||||
id: dialog_id
|
||||
)
|
||||
) do |d|
|
||||
d.with_header(variant: :large)
|
||||
d.with_body(classes: "Overlay-body_autocomplete_height",
|
||||
test_selector: "async-dialog-content") do
|
||||
render(edit_component)
|
||||
end
|
||||
d.with_footer do
|
||||
component_collection do |footer_collection|
|
||||
footer_collection.with_component(
|
||||
Primer::Beta::Button.new(data: { "close-dialog-id": dialog_id })
|
||||
) do
|
||||
writable? ? t("button_cancel") : t("button_close")
|
||||
end
|
||||
if writable?
|
||||
footer_collection.with_component(
|
||||
Primer::Beta::Button.new(scheme: :primary,
|
||||
type: :submit,
|
||||
form: form_id,
|
||||
data: {
|
||||
test_selector: "save-inplace-edit-field-button",
|
||||
turbo: true
|
||||
})
|
||||
) do
|
||||
t("button_save")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
%>
|
||||
@@ -0,0 +1,81 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module OpenProject
|
||||
module Common
|
||||
class InplaceEditFieldDialogComponent < ViewComponent::Base
|
||||
include OpTurbo::Streamable
|
||||
include OpPrimer::ComponentHelpers
|
||||
|
||||
def initialize(model:, attribute:, system_arguments: {})
|
||||
super()
|
||||
@model = model
|
||||
@attribute = attribute
|
||||
@system_arguments = system_arguments
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def writable?
|
||||
@system_arguments[:writable] == true
|
||||
end
|
||||
|
||||
def dialog_title
|
||||
@system_arguments[:label] || @model.class.human_attribute_name(@attribute)
|
||||
end
|
||||
|
||||
def dialog_id
|
||||
model_class = @model.class.name.parameterize(separator: "_")
|
||||
"inplace-edit-field-dialog--#{model_class}-#{@model.id}--#{@attribute}"
|
||||
end
|
||||
|
||||
def wrapper_id
|
||||
"##{dialog_id}"
|
||||
end
|
||||
|
||||
def form_id
|
||||
"inplace-edit-field-form-#{dialog_id}"
|
||||
end
|
||||
|
||||
def edit_component
|
||||
OpenProject::Common::InplaceEditFieldComponent.new(
|
||||
model: @model,
|
||||
attribute: @attribute,
|
||||
enforce_edit_mode: true,
|
||||
**@system_arguments.merge(
|
||||
wrapper_id:,
|
||||
form_id:,
|
||||
show_action_buttons: false
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,88 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module OpenProject
|
||||
module Common
|
||||
module InplaceEditFields
|
||||
class BaseFieldComponent < ViewComponent::Base
|
||||
include Primer::AttributesHelper
|
||||
|
||||
attr_reader :form, :attribute, :model, :show_action_buttons
|
||||
|
||||
def self.display_class
|
||||
DisplayFields::DisplayFieldComponent
|
||||
end
|
||||
|
||||
def self.open_in_dialog?
|
||||
false
|
||||
end
|
||||
|
||||
def initialize(form:, attribute:, model:, show_action_buttons: true, **system_arguments)
|
||||
super()
|
||||
@form = form
|
||||
@attribute = attribute
|
||||
@model = model
|
||||
@show_action_buttons = show_action_buttons
|
||||
@system_arguments = system_arguments
|
||||
end
|
||||
|
||||
def comment_field_if_enabled(form)
|
||||
return unless show_comment_field?
|
||||
|
||||
form.text_area(name: "#{model.class.model_name.param_key}[custom_comments][#{custom_field.id}]",
|
||||
scope_name_to_model: false,
|
||||
label: I18n.t("attributes.comment"),
|
||||
value: model.custom_comment_for(custom_field)&.text,
|
||||
rows: 5)
|
||||
end
|
||||
|
||||
def show_comment_field?
|
||||
custom_field? && custom_field&.has_comment?
|
||||
end
|
||||
|
||||
def custom_field?
|
||||
attribute.to_s.start_with?("custom_field_")
|
||||
end
|
||||
|
||||
def custom_field
|
||||
return @custom_field if defined?(@custom_field)
|
||||
|
||||
@custom_field = CustomField.find_by(id: attribute.to_s.sub("custom_field_", "").to_i)
|
||||
end
|
||||
|
||||
def test_selector
|
||||
if custom_field?
|
||||
"custom-field-#{custom_field.id}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,73 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module OpenProject
|
||||
module Common
|
||||
module InplaceEditFields
|
||||
class BooleanInputComponent < BaseFieldComponent
|
||||
def call
|
||||
@system_arguments[:data] = merge_data(
|
||||
@system_arguments,
|
||||
**additional_arguments
|
||||
)
|
||||
|
||||
form.check_box name: attribute,
|
||||
**@system_arguments
|
||||
|
||||
comment_field_if_enabled(form)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def reset_url
|
||||
inplace_edit_field_reset_path(
|
||||
model: model.class.name,
|
||||
id: model.id,
|
||||
attribute:,
|
||||
system_arguments_json: @system_arguments.to_json
|
||||
)
|
||||
end
|
||||
|
||||
def additional_arguments
|
||||
if show_action_buttons
|
||||
{
|
||||
data: { controller: "inplace-edit",
|
||||
inplace_edit_url_value: reset_url,
|
||||
action: "click->inplace-edit#submitForm keydown.esc->inplace-edit#request",
|
||||
test_selector: }
|
||||
}
|
||||
else
|
||||
{ data: { test_selector: } }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module OpenProject
|
||||
module Common
|
||||
module InplaceEditFields
|
||||
class CalculatedValueInputComponent < InplaceEditFields::TextInputComponent
|
||||
def self.display_class
|
||||
DisplayFields::CalculatedValueInputComponent
|
||||
end
|
||||
|
||||
def initialize(form:, attribute:, model:, **system_arguments)
|
||||
system_arguments ||= {}
|
||||
system_arguments[:readonly] = true
|
||||
|
||||
super(form:, attribute:, model:, show_action_buttons: false, **system_arguments)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,71 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module OpenProject
|
||||
module Common
|
||||
module InplaceEditFields
|
||||
class DateInputComponent < InplaceEditFields::TextInputComponent
|
||||
def initialize(form:, attribute:, model:, **system_arguments)
|
||||
system_arguments[:type] = :date
|
||||
super
|
||||
end
|
||||
|
||||
def call
|
||||
@system_arguments[:data] = merge_data(
|
||||
@system_arguments,
|
||||
**additional_arguments
|
||||
)
|
||||
|
||||
form.text_field name: attribute,
|
||||
**@system_arguments
|
||||
|
||||
comment_field_if_enabled(form)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def additional_arguments
|
||||
if show_action_buttons
|
||||
{
|
||||
data: { controller: "inplace-edit",
|
||||
inplace_edit_url_value: reset_url,
|
||||
action: "keydown.esc->inplace-edit#request " \
|
||||
"keydown.enter->inplace-edit#submitForm " \
|
||||
"change->inplace-edit#submitForm",
|
||||
test_selector: }
|
||||
}
|
||||
else
|
||||
{ data: { test_selector: } }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module OpenProject
|
||||
module Common
|
||||
module InplaceEditFields
|
||||
module DisplayFields
|
||||
class CalculatedValueInputComponent < DisplayFieldComponent
|
||||
include OpPrimer::ComponentHelpers
|
||||
include CalculatedValues::ErrorsHelper
|
||||
|
||||
attr_reader :model, :attribute
|
||||
|
||||
# If the writable attribute is not explicitly listed as an argument,
|
||||
# it will be interpreted as one of the system_arguments and thus overwrite the `writable: false`
|
||||
# rubocop:disable Lint/UnusedMethodArgument
|
||||
def initialize(model:, attribute:, writable: nil, truncated: false, has_comment: false, show_comment: false,
|
||||
**system_arguments)
|
||||
super(model:, attribute:, writable: false, truncated:, has_comment:, show_comment:, **system_arguments)
|
||||
end
|
||||
# rubocop:enable Lint/UnusedMethodArgument
|
||||
|
||||
def render_calculation_error
|
||||
error = custom_field&.first_calculation_error(model)
|
||||
return unless error
|
||||
|
||||
render(Primer::OpenProject::FlexLayout.new(
|
||||
align_items: :flex_start,
|
||||
data: { test_selector: "error--custom_field_#{custom_field.id}" }
|
||||
)) do |container|
|
||||
container.with_column do
|
||||
render Primer::Beta::Octicon.new(icon: :"alert-fill", color: :danger)
|
||||
end
|
||||
container.with_column(ml: 2) do
|
||||
render Primer::Beta::Text.new(color: :danger) do
|
||||
calculated_value_error_msg(error)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def render_tooltip
|
||||
render Primer::Alpha::Tooltip.new(
|
||||
for_id: @system_arguments[:id],
|
||||
type: :description,
|
||||
text: I18n.t("custom_fields.calculated_field_not_editable"),
|
||||
direction: :s
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
<%=
|
||||
flex_layout(
|
||||
align_items: :flex_start,
|
||||
justify_content: :space_between,
|
||||
test_selector: @system_arguments[:test_selector],
|
||||
classes: @system_arguments[:display_classes]
|
||||
) do |flex|
|
||||
flex.with_row(mb: 1) do
|
||||
render OpenProject::Common::AttributeLabelComponent.new(
|
||||
attribute:,
|
||||
model:,
|
||||
required: @system_arguments[:required],
|
||||
hidden: @system_arguments[:visually_hide_label]
|
||||
) do
|
||||
render(Primer::Beta::Text.new(font_weight: :bold)) { @system_arguments[:label] }
|
||||
end
|
||||
end
|
||||
|
||||
flex.with_row(w: :full) do
|
||||
input_specific_call
|
||||
end
|
||||
|
||||
flex.with_row(w: :full) do
|
||||
render OpenProject::Common::AttributeHelpTextCaptionComponent.new(
|
||||
help_text: helpers.help_text_for(
|
||||
model,
|
||||
attribute,
|
||||
current_user: helpers.current_user
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
if show_comment?
|
||||
flex.with_row(w: :full, mt: 2) do
|
||||
flex_layout do |inner_flex|
|
||||
inner_flex.with_row do
|
||||
render(Primer::Beta::Text.new(tag: :label, font_weight: :bold)) { t("attributes.comment") }
|
||||
end
|
||||
inner_flex.with_row(mt: 1) do
|
||||
render(Primer::Beta::Text.new) { comment_text }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (error_html = render_calculation_error).present?
|
||||
flex.with_row(w: :full) { error_html }
|
||||
end
|
||||
end
|
||||
%>
|
||||
|
||||
<%= render_tooltip %>
|
||||
+140
-12
@@ -33,47 +33,136 @@ module OpenProject
|
||||
module InplaceEditFields
|
||||
module DisplayFields
|
||||
class DisplayFieldComponent < ViewComponent::Base
|
||||
include OpenProject::TextFormatting
|
||||
include OpPrimer::ComponentHelpers
|
||||
|
||||
attr_reader :model, :attribute, :writable
|
||||
attr_reader :model, :attribute, :writable, :truncated
|
||||
|
||||
def initialize(model:, attribute:, writable:, **system_arguments)
|
||||
def initialize(model:, attribute:, writable:, truncated:, has_comment: false, show_comment: false, **system_arguments)
|
||||
super()
|
||||
@model = model
|
||||
@attribute = attribute
|
||||
@writable = writable
|
||||
@truncated = truncated
|
||||
@has_comment = has_comment
|
||||
@show_comment = show_comment
|
||||
@system_arguments = system_arguments
|
||||
end
|
||||
|
||||
def render_display_value
|
||||
value = model.public_send(attribute)
|
||||
|
||||
if value.present?
|
||||
format_text(value)
|
||||
if value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
||||
boolean_display_value(value)
|
||||
elsif value.is_a?(Date) || value.is_a?(Time)
|
||||
helpers.format_date(value)
|
||||
elsif value.present? && value != [nil]
|
||||
format_present_value(value)
|
||||
else
|
||||
"–"
|
||||
t("placeholders.default")
|
||||
end
|
||||
end
|
||||
|
||||
def display_field_arguments
|
||||
@display_field_arguments ||= {
|
||||
classes: "op-inplace-edit--display-field #{'op-inplace-edit--display-field_editable' if writable}",
|
||||
@display_field_arguments ||= if open_in_dialog?
|
||||
base_arguments.merge(dialog_field_arguments)
|
||||
else
|
||||
base_arguments.merge(inline_edit_field_arguments)
|
||||
end
|
||||
end
|
||||
|
||||
def open_in_dialog?
|
||||
@system_arguments[:dialog_controller_name].present?
|
||||
end
|
||||
|
||||
def base_arguments
|
||||
{
|
||||
classes: display_field_classes,
|
||||
id: @system_arguments[:id],
|
||||
role: "button",
|
||||
tabindex: 0
|
||||
}
|
||||
end
|
||||
|
||||
def dialog_field_arguments
|
||||
return {} unless writable? || @has_comment
|
||||
|
||||
{
|
||||
data: {
|
||||
controller: "inplace-edit",
|
||||
inplace_edit_url_value: edit_url,
|
||||
action: writable ? "click->inplace-edit#request" : ""
|
||||
controller: "inplace-edit async-dialog",
|
||||
inplace_edit_dialog_url_value: @system_arguments[:dialog_url],
|
||||
action: dialog_controller_actions,
|
||||
test_selector: "inplace-edit-dialog-button-#{model.id}"
|
||||
},
|
||||
aria: {
|
||||
label: [
|
||||
I18n.t(:label_edit_x, x: @system_arguments[:label]),
|
||||
I18n.t(:label_value_x, x: render_display_value)
|
||||
].join(", ")
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def call
|
||||
def inline_edit_field_arguments
|
||||
return {} unless writable?
|
||||
|
||||
{
|
||||
data: {
|
||||
controller: "inplace-edit",
|
||||
inplace_edit_url_value: edit_url,
|
||||
action: inline_controller_actions,
|
||||
test_selector: "inplace-edit-field-button-#{model.id}"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def render_calculation_error
|
||||
# no-op — subclasses may override to render a calculation error row
|
||||
end
|
||||
|
||||
def show_comment?
|
||||
@show_comment
|
||||
end
|
||||
|
||||
def input_specific_call
|
||||
render(Primer::BaseComponent.new(tag: :div, **display_field_arguments)) do
|
||||
render_display_value
|
||||
end
|
||||
end
|
||||
|
||||
def render_tooltip
|
||||
nil
|
||||
end
|
||||
|
||||
def custom_field?
|
||||
attribute.to_s.start_with?("custom_field_")
|
||||
end
|
||||
|
||||
def custom_field
|
||||
return @custom_field if defined?(@custom_field)
|
||||
|
||||
@custom_field = CustomField.find_by(id: attribute.to_s.sub("custom_field_", "").to_i)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def display_field_classes
|
||||
# The later check catches non-editable users which should still see the comment in a dialog
|
||||
clickable = writable? || open_in_dialog?
|
||||
"op-inplace-edit--display-field#{' op-inplace-edit--display-field_clickable' if clickable}"
|
||||
end
|
||||
|
||||
def format_present_value(value)
|
||||
if custom_field?
|
||||
helpers.format_value(value, custom_field)
|
||||
else
|
||||
value.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def comment_text
|
||||
model.custom_comment_for(custom_field)&.text.presence || t("placeholders.default")
|
||||
end
|
||||
|
||||
def edit_url
|
||||
inplace_edit_field_edit_path(
|
||||
model: model.class.name,
|
||||
@@ -82,6 +171,45 @@ module OpenProject
|
||||
system_arguments_json: @system_arguments.to_json
|
||||
)
|
||||
end
|
||||
|
||||
def boolean_display_value(value)
|
||||
I18n.t("general_text_#{value ? 'Yes' : 'No'}")
|
||||
end
|
||||
|
||||
def writable?
|
||||
writable && (@system_arguments[:readonly].nil? || @system_arguments[:readonly] == false)
|
||||
end
|
||||
|
||||
def custom_field_values
|
||||
CustomValue
|
||||
.includes(custom_field: :custom_options)
|
||||
.where(
|
||||
custom_field_id: custom_field&.id,
|
||||
customized_id: model.id
|
||||
)
|
||||
.to_a
|
||||
end
|
||||
|
||||
def dialog_controller_actions
|
||||
return "" unless writable? || @has_comment
|
||||
|
||||
[
|
||||
"click->inplace-edit#openDialog",
|
||||
"keydown.enter->inplace-edit#openDialog",
|
||||
"keydown.space->inplace-edit#openDialog",
|
||||
"inplace-edit:open-dialog->async-dialog#handleOpenDialog"
|
||||
].join(" ")
|
||||
end
|
||||
|
||||
def inline_controller_actions
|
||||
return "" unless writable?
|
||||
|
||||
[
|
||||
"click->inplace-edit#request",
|
||||
"keydown.enter->inplace-edit#request",
|
||||
"keydown.space->inplace-edit#request"
|
||||
].join(" ")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+17
-11
@@ -1,16 +1,22 @@
|
||||
.op-inplace-edit
|
||||
&--display-field
|
||||
&_editable
|
||||
margin-left: -9px !important // cancel out 8px padding + 1px border
|
||||
margin-right: -9px !important // cancel out 8px padding + 1px border
|
||||
padding: var(--base-size-8)
|
||||
width: calc(100% + 18px) !important
|
||||
border: 1px solid transparent
|
||||
border-radius: var(--borderRadius-medium)
|
||||
padding: var(--base-size-4) var(--base-size-8)
|
||||
border: 1px solid transparent
|
||||
border-radius: var(--borderRadius-medium)
|
||||
@include text-shortener(false)
|
||||
|
||||
&:hover, &:focus
|
||||
border-color: var(--borderColor-default)
|
||||
box-shadow: var(--shadow-inset)
|
||||
&:hover, &:focus
|
||||
border-color: var(--borderColor-default)
|
||||
box-shadow: var(--shadow-inset)
|
||||
|
||||
&:not(&_editable)
|
||||
&:not(&_clickable)
|
||||
cursor: not-allowed
|
||||
&:hover, &:focus
|
||||
background-color: var(--bgColor-muted)
|
||||
|
||||
&--dialog
|
||||
.op-inplace-edit--display-field
|
||||
&:not(&_clickable)
|
||||
&:hover, &:focus
|
||||
border-color: transparent
|
||||
box-shadow: none
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module OpenProject
|
||||
module Common
|
||||
module InplaceEditFields
|
||||
module DisplayFields
|
||||
class HierarchyListComponent < DisplayFieldComponent
|
||||
def render_display_value
|
||||
items = hierarchy_items
|
||||
|
||||
if items.empty?
|
||||
t("placeholders.default")
|
||||
elsif custom_field.multi_value?
|
||||
items.join(", ")
|
||||
else
|
||||
items.first.to_s
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def hierarchy_items
|
||||
custom_field_values.filter_map do |cv|
|
||||
CustomField::Hierarchy::Item.find_by(id: cv.value&.to_i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module OpenProject
|
||||
module Common
|
||||
module InplaceEditFields
|
||||
module DisplayFields
|
||||
class LinkInputComponent < DisplayFieldComponent
|
||||
include OpenProject::TextFormatting
|
||||
|
||||
attr_reader :model, :attribute, :writable
|
||||
|
||||
def render_display_value
|
||||
value = model.public_send(attribute)
|
||||
|
||||
if value.present?
|
||||
render_link(value)
|
||||
else
|
||||
t("placeholders.default")
|
||||
end
|
||||
end
|
||||
|
||||
def render_link(href)
|
||||
link = Addressable::URI.parse(href)
|
||||
return href unless link
|
||||
|
||||
render(Primer::Beta::Link.new(href:, rel: "noopener noreferrer")) do
|
||||
href
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+21
-2
@@ -33,16 +33,35 @@ module OpenProject
|
||||
module InplaceEditFields
|
||||
module DisplayFields
|
||||
class RichTextAreaComponent < DisplayFieldComponent
|
||||
include OpenProject::TextFormatting
|
||||
|
||||
attr_reader :model, :attribute, :writable
|
||||
|
||||
def call
|
||||
def input_specific_call
|
||||
render(Primer::BaseComponent.new(tag: :div, **display_field_arguments)) do
|
||||
render(Primer::BaseComponent.new(tag: :div,
|
||||
classes: "op-uc-container op-uc-container_reduced-headings -multiline")) do
|
||||
render_display_value
|
||||
if field_value.present?
|
||||
if truncated
|
||||
render OpenProject::Common::AttributeComponent.new("#{attribute}-truncated-display-field",
|
||||
attribute,
|
||||
field_value,
|
||||
lines: 3)
|
||||
else
|
||||
format_text(field_value)
|
||||
end
|
||||
else
|
||||
t("placeholders.default")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def field_value
|
||||
model.public_send(attribute)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module OpenProject
|
||||
module Common
|
||||
module InplaceEditFields
|
||||
module DisplayFields
|
||||
class SelectListComponent < DisplayFieldComponent
|
||||
include CustomFieldsHelper
|
||||
|
||||
attr_reader :model, :attribute, :writable
|
||||
|
||||
def render_display_value
|
||||
value = model.public_send(attribute)
|
||||
|
||||
if value.present? && value != [nil]
|
||||
render_value(value)
|
||||
else
|
||||
t("placeholders.default")
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def render_value(value)
|
||||
if custom_field?
|
||||
formatted_custom_field_values.presence || t("placeholders.default")
|
||||
else
|
||||
value.is_a?(Array) ? value.join(", ") : value.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def formatted_custom_field_values
|
||||
return @formatted_custom_field_values if defined?(@formatted_custom_field_values)
|
||||
|
||||
values = custom_field_values.map { |v| format_value(v.value, custom_field) }
|
||||
|
||||
@formatted_custom_field_values = custom_field&.multi_value? ? values.join(", ") : values.first
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module OpenProject
|
||||
module Common
|
||||
module InplaceEditFields
|
||||
module DisplayFields
|
||||
class UserSelectListComponent < SelectListComponent
|
||||
include CustomFieldsHelper
|
||||
|
||||
attr_reader :model, :attribute, :writable
|
||||
|
||||
def formatted_custom_field_values
|
||||
return @formatted_custom_field_values if defined?(@formatted_custom_field_values)
|
||||
|
||||
cf_values = custom_field_values
|
||||
|
||||
users = cf_values.filter_map(&:typed_value)
|
||||
|
||||
@formatted_custom_field_values = if custom_field.multi_value?
|
||||
flex_layout do |avatar_container|
|
||||
users.each do |user|
|
||||
avatar_container.with_row do
|
||||
render_avatar(user)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
render_avatar(users.first)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def render_avatar(user)
|
||||
return unless user
|
||||
|
||||
render(::Users::AvatarComponent.new(user:, size: :mini))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,43 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module OpenProject
|
||||
module Common
|
||||
module InplaceEditFields
|
||||
class FloatInputComponent < InplaceEditFields::TextInputComponent
|
||||
def initialize(form:, attribute:, model:, **system_arguments)
|
||||
system_arguments[:type] = :number
|
||||
system_arguments[:step] = "any"
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,97 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module OpenProject
|
||||
module Common
|
||||
module InplaceEditFields
|
||||
class HierarchyListComponent < BaseFieldComponent
|
||||
include CustomFieldHierarchyTreeViewHelper
|
||||
|
||||
def self.display_class
|
||||
DisplayFields::HierarchyListComponent
|
||||
end
|
||||
|
||||
def self.open_in_dialog?
|
||||
true
|
||||
end
|
||||
|
||||
def initialize(form:, attribute:, model:, show_action_buttons: false, **system_arguments)
|
||||
super
|
||||
end
|
||||
|
||||
def call
|
||||
form_field_name = "project[custom_field_values][]"
|
||||
|
||||
form.hidden(name: form_field_name, value: "", scope_name_to_model: false)
|
||||
filterable_tree_view(form)
|
||||
comment_field_if_enabled(form)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def filterable_tree_view(form)
|
||||
form.html_content do
|
||||
render(Primer::OpenProject::FilterableTreeView.new(
|
||||
form_arguments: { builder: rails_builder, name: "custom_field_values" },
|
||||
include_sub_items_check_box_arguments: { hidden: true },
|
||||
filter_mode_control_arguments: { hidden: true }
|
||||
)) do |tree_view|
|
||||
item_options = {
|
||||
expanded_fn: ->(*) { true },
|
||||
label_fn:,
|
||||
checked_fn:,
|
||||
select_variant: custom_field.multi_value? ? :multiple : :single
|
||||
}
|
||||
|
||||
populate_tree_view(tree_view, custom_field, item_options:)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Primer's FormObject stores the underlying ActionView/Primer form builder
|
||||
# as @builder. FilterableTreeView requires an ActionView::FormBuilder to
|
||||
# generate its hidden form inputs via hidden_field.
|
||||
def rails_builder
|
||||
form.instance_variable_get(:@builder)
|
||||
end
|
||||
|
||||
def checked_fn
|
||||
current_values = Array(model.custom_value_for(custom_field)).map(&:value)
|
||||
lambda { |item| current_values.include?(item.id.to_s) }
|
||||
end
|
||||
|
||||
def label_fn
|
||||
item_formatter = standard_tree_view_item_formatter
|
||||
lambda { |item| item_formatter.format(item:) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,42 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module OpenProject
|
||||
module Common
|
||||
module InplaceEditFields
|
||||
class IntegerInputComponent < InplaceEditFields::TextInputComponent
|
||||
def initialize(form:, attribute:, model:, **system_arguments)
|
||||
system_arguments[:type] = :number
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,46 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module OpenProject
|
||||
module Common
|
||||
module InplaceEditFields
|
||||
class LinkInputComponent < TextInputComponent
|
||||
def self.display_class
|
||||
DisplayFields::LinkInputComponent
|
||||
end
|
||||
|
||||
def initialize(form:, attribute:, model:, **system_arguments)
|
||||
system_arguments[:type] = :url
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+32
-15
@@ -31,38 +31,55 @@
|
||||
module OpenProject
|
||||
module Common
|
||||
module InplaceEditFields
|
||||
class RichTextAreaComponent < ViewComponent::Base
|
||||
attr_reader :form, :attribute, :model
|
||||
|
||||
class RichTextAreaComponent < BaseFieldComponent
|
||||
def self.display_class
|
||||
DisplayFields::RichTextAreaComponent
|
||||
end
|
||||
|
||||
def initialize(form:, attribute:, model:, **system_arguments)
|
||||
super()
|
||||
@form = form
|
||||
@attribute = attribute
|
||||
@model = model
|
||||
@system_arguments = system_arguments
|
||||
def initialize(form:, attribute:, model:, show_action_buttons: true, **system_arguments)
|
||||
super
|
||||
@system_arguments[:classes] = class_names(
|
||||
@system_arguments[:classes],
|
||||
"op-inplace-edit-field--text-area"
|
||||
)
|
||||
@system_arguments[:label] ||= model.class.human_attribute_name(attribute)
|
||||
|
||||
@system_arguments[:rich_text_options] ||= {}
|
||||
@system_arguments[:rich_text_options][:primerized] = true
|
||||
|
||||
@system_arguments[:data] = merge_data(
|
||||
@system_arguments,
|
||||
data: { test_selector: }
|
||||
)
|
||||
end
|
||||
|
||||
def call
|
||||
form.rich_text_area(name: attribute,
|
||||
wrapper_data_attributes: {
|
||||
controller: "ckeditor-focus",
|
||||
ckeditor_focus_target: "editor",
|
||||
ckeditor_focus_autofocus_value: true
|
||||
},
|
||||
wrapper_data_attributes: ckeditor_wrapper_data,
|
||||
**@system_arguments)
|
||||
|
||||
comment_field_if_enabled(form)
|
||||
render_action_buttons if show_action_buttons
|
||||
end
|
||||
|
||||
def test_selector
|
||||
if custom_field?
|
||||
"custom-field-#{custom_field.id}"
|
||||
else
|
||||
"augmented-text-area-#{attribute.to_s.parameterize(separator: '_')}"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ckeditor_wrapper_data
|
||||
{
|
||||
controller: "ckeditor-focus",
|
||||
ckeditor_focus_target: "editor",
|
||||
ckeditor_focus_autofocus_value: true
|
||||
}
|
||||
end
|
||||
|
||||
def render_action_buttons
|
||||
form.group(layout: :horizontal, justify_content: :flex_end) do |button_group|
|
||||
button_group.submit(name: :reset,
|
||||
type: :submit,
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module OpenProject
|
||||
module Common
|
||||
module InplaceEditFields
|
||||
class SelectListComponent < BaseFieldComponent
|
||||
def self.display_class
|
||||
DisplayFields::SelectListComponent
|
||||
end
|
||||
|
||||
def initialize(form:, attribute:, model:, show_action_buttons: true, **system_arguments)
|
||||
super
|
||||
|
||||
@system_arguments[:autocomplete_options] ||= {}
|
||||
set_autocomplete_defaults(model, attribute)
|
||||
end
|
||||
|
||||
def call
|
||||
if custom_field?
|
||||
render_custom_field_input
|
||||
else
|
||||
render_autocompleter
|
||||
end
|
||||
|
||||
comment_field_if_enabled(form)
|
||||
render_action_buttons if show_action_buttons
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_autocomplete_defaults(model, attribute)
|
||||
opts = @system_arguments[:autocomplete_options]
|
||||
opts[:model] ||= { id: model.id, name: model.name }
|
||||
opts[:inputName] ||= attribute
|
||||
opts[:wrapper_id] ||= @system_arguments[:wrapper_id]
|
||||
opts[:focusDirectly] = true if opts[:focusDirectly].nil?
|
||||
opts[:closeOnSelect] = false if opts[:closeOnSelect].nil?
|
||||
end
|
||||
|
||||
def render_action_buttons
|
||||
form.group(layout: :horizontal, justify_content: :flex_end) do |button_group|
|
||||
button_group.submit(name: :reset,
|
||||
type: :submit,
|
||||
label: I18n.t(:button_cancel),
|
||||
scheme: :default,
|
||||
formaction: inplace_edit_field_reset_path(model: model.class.name, id: model.id, attribute:),
|
||||
formmethod: :get)
|
||||
button_group.submit(name: :submit,
|
||||
label: I18n.t(:button_save),
|
||||
scheme: :primary)
|
||||
end
|
||||
end
|
||||
|
||||
def render_custom_field_input
|
||||
input_class = if custom_field.multi_value?
|
||||
CustomFields::Inputs::MultiSelectList
|
||||
else
|
||||
CustomFields::Inputs::SingleSelectList
|
||||
end
|
||||
|
||||
# Use fields_for to create the proper context for custom field inputs
|
||||
form.fields_for(:custom_field_values) do |builder|
|
||||
input_class.new(builder, custom_field:, object: model, **@system_arguments[:autocomplete_options])
|
||||
end
|
||||
end
|
||||
|
||||
def render_autocompleter
|
||||
form.autocompleter(name: attribute, **@system_arguments)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -31,29 +31,18 @@
|
||||
module OpenProject
|
||||
module Common
|
||||
module InplaceEditFields
|
||||
class TextInputComponent < ViewComponent::Base
|
||||
attr_reader :form, :attribute, :model
|
||||
|
||||
def self.display_class
|
||||
DisplayFields::DisplayFieldComponent
|
||||
end
|
||||
|
||||
def initialize(form:, attribute:, model:, **system_arguments)
|
||||
super()
|
||||
@form = form
|
||||
@attribute = attribute
|
||||
@model = model
|
||||
@system_arguments = system_arguments
|
||||
@system_arguments[:label] ||= model.class.human_attribute_name(attribute)
|
||||
end
|
||||
|
||||
class TextInputComponent < BaseFieldComponent
|
||||
def call
|
||||
@system_arguments[:data] = merge_data(
|
||||
@system_arguments,
|
||||
**additional_arguments
|
||||
)
|
||||
|
||||
form.text_field name: attribute,
|
||||
autofocus: true,
|
||||
data: { controller: "inplace-edit",
|
||||
inplace_edit_url_value: reset_url,
|
||||
action: "keydown.esc->inplace-edit#request" },
|
||||
**@system_arguments
|
||||
|
||||
comment_field_if_enabled(form)
|
||||
end
|
||||
|
||||
private
|
||||
@@ -66,6 +55,19 @@ module OpenProject
|
||||
system_arguments_json: @system_arguments.to_json
|
||||
)
|
||||
end
|
||||
|
||||
def additional_arguments
|
||||
if show_action_buttons
|
||||
{
|
||||
data: { controller: "inplace-edit",
|
||||
inplace_edit_url_value: reset_url,
|
||||
action: "keydown.esc->inplace-edit#request",
|
||||
test_selector: }
|
||||
}
|
||||
else
|
||||
{ data: { test_selector: } }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module OpenProject
|
||||
module Common
|
||||
module InplaceEditFields
|
||||
class UserSelectListComponent < SelectListComponent
|
||||
def self.display_class
|
||||
DisplayFields::UserSelectListComponent
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def render_custom_field_input
|
||||
input_class = if custom_field.multi_value?
|
||||
CustomFields::Inputs::MultiUserSelectList
|
||||
else
|
||||
CustomFields::Inputs::SingleUserSelectList
|
||||
end
|
||||
|
||||
# Use fields_for to create the proper context for custom field inputs
|
||||
form.fields_for(:custom_field_values) do |builder|
|
||||
input_class.new(builder, custom_field:, object: model, **@system_arguments[:autocomplete_options])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module OpenProject
|
||||
module Common
|
||||
module InplaceEditFields
|
||||
class VersionSelectListComponent < SelectListComponent
|
||||
def initialize(form:, attribute:, model:, **system_arguments)
|
||||
super
|
||||
|
||||
unless custom_field?
|
||||
assign_defaults!
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def render_custom_field_input
|
||||
input_class = if custom_field.multi_value?
|
||||
CustomFields::Inputs::MultiVersionSelectList
|
||||
else
|
||||
CustomFields::Inputs::SingleVersionSelectList
|
||||
end
|
||||
|
||||
# Use fields_for to create the proper context for custom field inputs
|
||||
form.fields_for(:custom_field_values) do |builder|
|
||||
input_class.new(builder, custom_field:, object: model, **@system_arguments[:autocomplete_options])
|
||||
end
|
||||
end
|
||||
|
||||
def render_autocompleter
|
||||
form.autocompleter(name: attribute, **@system_arguments) do |list|
|
||||
model.assignable_versions.each do |version|
|
||||
list.option(
|
||||
label: version.name,
|
||||
value: version.id,
|
||||
selected: version.id == model.version&.id
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def assign_defaults!
|
||||
version = model.version
|
||||
@system_arguments[:autocomplete_options][:inputValue] = version&.id
|
||||
@system_arguments[:autocomplete_options][:model] = version_model
|
||||
@system_arguments[:autocomplete_options][:decorated] = true
|
||||
@system_arguments[:autocomplete_options][:closeOnSelect] = true
|
||||
# Override inputName to use Rails form builder naming convention
|
||||
@system_arguments[:autocomplete_options][:inputName] = input_name
|
||||
end
|
||||
|
||||
def version_model
|
||||
version ? { id: version.id, name: version.name } : nil
|
||||
end
|
||||
|
||||
def input_name
|
||||
"#{model.class.model_name.param_key}[#{attribute}]"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -28,7 +28,11 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
++#%>
|
||||
<%=
|
||||
render(Primer::OpenProject::PageHeader.new) do |header|
|
||||
header.with_title { "#{avatar @placeholder_user} #{h(@placeholder_user.name)}".html_safe }
|
||||
header.with_title do
|
||||
concat avatar(@placeholder_user)
|
||||
concat @placeholder_user.name
|
||||
end
|
||||
|
||||
header.with_breadcrumbs(breadcrumb_items)
|
||||
|
||||
if @current_user.allowed_globally?(:manage_placeholder_user)
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
# ++
|
||||
|
||||
module Projects
|
||||
module Concerns
|
||||
module IdentifierSuggestion
|
||||
def identifier_suggestion_data
|
||||
suggestion_mode = Setting::WorkPackageIdentifier.alphanumeric? ? "semantic" : "legacy"
|
||||
|
||||
{
|
||||
controller: "projects--identifier-suggestion",
|
||||
"projects--identifier-suggestion-mode-value": suggestion_mode,
|
||||
"projects--identifier-suggestion-url-value": projects_identifier_suggestion_path,
|
||||
"projects--identifier-suggestion-set-name-first-value": I18n.t("js.projects.identifier_suggestion.set_name_first")
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -28,7 +28,7 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
++#%>
|
||||
|
||||
<%=
|
||||
component_wrapper do
|
||||
component_wrapper(data: identifier_suggestion_data) do
|
||||
settings_primer_form_with(model: target_project, url: copy_project_path(source_project), method: :post) do |f|
|
||||
flex_layout do |container|
|
||||
container.with_row(mb: 3) do
|
||||
@@ -41,6 +41,7 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
render(
|
||||
Primer::Forms::FormList.new(
|
||||
Projects::Settings::NameForm.new(f),
|
||||
Projects::Settings::EditableIdentifierForm.new(f),
|
||||
Projects::Settings::RelationsForm.new(f, invisible: params[:parent_id].present?),
|
||||
Projects::Settings::CustomFieldsForm.new(f)
|
||||
)
|
||||
|
||||
@@ -33,6 +33,7 @@ module Projects
|
||||
include ApplicationHelper
|
||||
include OpPrimer::ComponentHelpers
|
||||
include OpTurbo::Streamable
|
||||
include Projects::Concerns::IdentifierSuggestion
|
||||
|
||||
options :source_project, :target_project, :copy_options
|
||||
end
|
||||
|
||||
@@ -28,7 +28,7 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
++#%>
|
||||
|
||||
<%=
|
||||
component_wrapper(target: "_top") do
|
||||
component_wrapper(target: "_top", data: identifier_suggestion_data) do
|
||||
settings_primer_form_with(
|
||||
model: project,
|
||||
url: workspaces_path,
|
||||
@@ -39,6 +39,7 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
render(
|
||||
Primer::Forms::FormList.new(
|
||||
Projects::Settings::NameForm.new(f),
|
||||
Projects::Settings::EditableIdentifierForm.new(f),
|
||||
Projects::Settings::DescriptionForm.new(f),
|
||||
Projects::Settings::RelationsForm.new(f, invisible: params[:parent_id].present?),
|
||||
Projects::Settings::TypeForm.new(f)
|
||||
|
||||
@@ -33,6 +33,7 @@ module Projects
|
||||
include ApplicationHelper
|
||||
include OpPrimer::ComponentHelpers
|
||||
include OpTurbo::Streamable
|
||||
include Projects::Concerns::IdentifierSuggestion
|
||||
|
||||
options :project, :template, :step
|
||||
|
||||
|
||||
@@ -27,9 +27,10 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
|
||||
++#%>
|
||||
|
||||
<tr
|
||||
<%= "id=\"#{row_css_id}\"".html_safe if row_css_id %>
|
||||
<%= "class=\"#{row_css_class}\"".html_safe if row_css_class %>>
|
||||
<%= content_tag(:tr,
|
||||
id: row_css_id,
|
||||
class: row_css_class
|
||||
) do %>
|
||||
<% columns.each do |column| %>
|
||||
<td class="<%= column_css_class(column) %>">
|
||||
<%= column_value(column) %>
|
||||
@@ -40,4 +41,4 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<%= link %>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
|
||||
@@ -211,17 +211,15 @@ module Projects
|
||||
def project_status
|
||||
return nil unless user_can_view_project_attributes?
|
||||
|
||||
content = "".html_safe
|
||||
|
||||
status_code = project.status_code
|
||||
|
||||
if status_code
|
||||
classes = helpers.project_status_css_class(status_code)
|
||||
content << content_tag(:span, "", class: "project-status--bulb -inline #{classes}")
|
||||
content << content_tag(:span, helpers.project_status_name(status_code), class: "project-status--name #{classes}")
|
||||
end
|
||||
|
||||
content
|
||||
capture do
|
||||
concat content_tag(:span, "", class: "project-status--bulb -inline #{classes}")
|
||||
concat content_tag(:span, helpers.project_status_name(status_code), class: "project-status--name #{classes}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def status_explanation
|
||||
@@ -250,7 +248,7 @@ module Projects
|
||||
|
||||
def row_css_class
|
||||
classes = %w[basics context-menu--reveal op-project-row-component]
|
||||
classes << project_css_classes
|
||||
classes += project_css_classes
|
||||
classes << row_css_level_classes
|
||||
|
||||
classes.join(" ")
|
||||
@@ -269,13 +267,13 @@ module Projects
|
||||
end
|
||||
|
||||
def project_css_classes
|
||||
s = " project ".html_safe
|
||||
output = ["project"]
|
||||
|
||||
s << " root" if project.root?
|
||||
s << " child" if project.child?
|
||||
s << (project.leaf? ? " leaf" : " parent")
|
||||
output << "root" if project.root?
|
||||
output << "child" if project.child?
|
||||
output << (project.leaf? ? "leaf" : "parent")
|
||||
|
||||
s
|
||||
output
|
||||
end
|
||||
|
||||
def column_css_class(column)
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
<%#-- copyright
|
||||
OpenProject is an open source project management software.
|
||||
Copyright (C) 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.
|
||||
|
||||
++#%>
|
||||
|
||||
<%= render(Primer::Alpha::Dialog.new(
|
||||
id: "change-identifier-dialog",
|
||||
title: t("projects.settings.change_identifier_dialog_title")
|
||||
)) do |dialog| %>
|
||||
<% dialog.with_body do %>
|
||||
<%= render(Primer::Alpha::Banner.new(scheme: :warning, mb: 3)) do %>
|
||||
<%= t("projects.settings.change_identifier_warning") %>
|
||||
<% end %>
|
||||
|
||||
<%= settings_primer_form_with(model: project,
|
||||
url: project_identifier_path(project),
|
||||
id: "change-identifier-form",
|
||||
mt: 4) do |f| %>
|
||||
<%= render(Primer::Forms::FormList.new(Projects::Settings::EditableIdentifierForm.new(f))) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% dialog.with_footer do %>
|
||||
<%= render(Primer::Beta::Button.new(data: { "close-dialog-id": "change-identifier-dialog" })) do %>
|
||||
<%= t(:button_cancel) %>
|
||||
<% end %>
|
||||
<%= render(Primer::Beta::Button.new(scheme: :primary, type: :submit, form: "change-identifier-form")) do %>
|
||||
<%= t("projects.settings.change_identifier") %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
+7
-14
@@ -28,25 +28,18 @@
|
||||
# See COPYRIGHT and LICENSE files for more details.
|
||||
#++
|
||||
|
||||
module Overviews
|
||||
module ProjectCustomFields
|
||||
class EditComponent < ApplicationComponent
|
||||
include ApplicationHelper
|
||||
include OpTurbo::Streamable
|
||||
module Projects
|
||||
module Settings
|
||||
class ChangeIdentifierDialogComponent < ApplicationComponent
|
||||
include OpPrimer::ComponentHelpers
|
||||
include CustomFieldHierarchyTreeViewHelper
|
||||
include OpPrimer::FormHelpers
|
||||
include OpTurbo::Streamable
|
||||
|
||||
attr_reader :wrapper_id
|
||||
attr_reader :project
|
||||
|
||||
def initialize(project:, project_custom_field:, wrapper_id: nil)
|
||||
def initialize(project:)
|
||||
super
|
||||
@project = project
|
||||
@project_custom_field = project_custom_field
|
||||
@wrapper_id = wrapper_id
|
||||
end
|
||||
|
||||
def wrapper_uniq_by
|
||||
@project_custom_field.id
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -46,6 +46,29 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
%>
|
||||
<% end %>
|
||||
|
||||
<%= render(Primer::BaseComponent.new(tag: :section, mb: 4)) do %>
|
||||
<%=
|
||||
render(Primer::Beta::Subhead.new) do |component|
|
||||
component.with_heading(tag: :h3, size: :medium) { t(:label_identifier) }
|
||||
end
|
||||
%>
|
||||
<%=
|
||||
settings_primer_form_with(model: project, url: project_settings_general_path(project)) do |f|
|
||||
render(Primer::Forms::FormList.new(Projects::Settings::IdentifierForm.new(f)))
|
||||
end
|
||||
%>
|
||||
<%=
|
||||
render(
|
||||
Primer::Beta::Button.new(
|
||||
tag: :a,
|
||||
href: identifier_update_dialog_project_identifier_path(project_id: project),
|
||||
data: { turbo_stream: true },
|
||||
mt: 2
|
||||
)
|
||||
) { t("projects.settings.change_identifier") }
|
||||
%>
|
||||
<% end %>
|
||||
|
||||
<%= render(Primer::BaseComponent.new(tag: :section, mb: 4)) do %>
|
||||
<%=
|
||||
render(Primer::Beta::Subhead.new) do |component|
|
||||
|
||||
@@ -24,19 +24,6 @@
|
||||
end
|
||||
end
|
||||
|
||||
header.with_action_button(
|
||||
tag: :a,
|
||||
mobile_icon: :pencil,
|
||||
mobile_label: t("projects.settings.change_identifier"),
|
||||
size: :medium,
|
||||
href: project_identifier_path(@project),
|
||||
aria: { label: t("projects.settings.change_identifier") },
|
||||
title: t("projects.settings.change_identifier")
|
||||
) do |button|
|
||||
button.with_leading_visual_icon(icon: :pencil)
|
||||
t("projects.settings.change_identifier")
|
||||
end
|
||||
|
||||
header.with_action_menu(
|
||||
menu_arguments: {
|
||||
anchor_align: :end
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
end
|
||||
end
|
||||
|
||||
unless hide_help_text?
|
||||
unless hide_help_text_caption?
|
||||
container.with_row do
|
||||
render(
|
||||
OpenProject::Common::AttributeHelpTextCaptionComponent.new(
|
||||
|
||||
@@ -34,16 +34,18 @@ class Projects::StatusButtonComponent < ApplicationComponent
|
||||
include OpPrimer::ComponentHelpers
|
||||
include ProjectStatusHelper
|
||||
|
||||
attr_reader :project, :user, :hide_help_text
|
||||
attr_reader :project, :user, :hide_help_text, :hide_help_text_caption
|
||||
alias :hide_help_text? :hide_help_text
|
||||
alias :hide_help_text_caption? :hide_help_text_caption
|
||||
|
||||
def initialize(project:, user:, size: :medium, hide_help_text: false)
|
||||
def initialize(project:, user:, size: :medium, hide_help_text: false, hide_help_text_caption: false)
|
||||
super
|
||||
|
||||
@project = project
|
||||
@user = user
|
||||
@size = size
|
||||
@hide_help_text = hide_help_text
|
||||
@hide_help_text_caption = hide_help_text_caption
|
||||
|
||||
@status = find_status(project.status_code)
|
||||
end
|
||||
|
||||
@@ -31,7 +31,10 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<div class="generic-table--flex-container">
|
||||
<div class="generic-table--container <%= container_class %>">
|
||||
<div class="generic-table--results-container">
|
||||
<table class="generic-table" data-controller="table-highlighting" <%= table_id ? "id=\"#{table_id}\"".html_safe : "" %>>
|
||||
<%= content_tag :table,
|
||||
id: table_id,
|
||||
class: "generic-table",
|
||||
data: { controller: "table-highlighting" } do %>
|
||||
<colgroup>
|
||||
<% columns.each do |column| %>
|
||||
<col <%= "opHighlightCol" unless column.attribute == :hierarchy %>>
|
||||
@@ -84,7 +87,7 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<% end %>
|
||||
<%= render_collection rows %>
|
||||
</tbody>
|
||||
</table>
|
||||
<% end %>
|
||||
<% if inline_create_link && show_inline_create %>
|
||||
<div class="wp-inline-create-button">
|
||||
<%= inline_create_link %>
|
||||
|
||||
@@ -27,9 +27,10 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
|
||||
++#%>
|
||||
|
||||
<tr
|
||||
<%= "id=\"#{row_css_id}\"".html_safe if row_css_id %>
|
||||
<%= "class=\"#{row_css_class}\"".html_safe if row_css_class %>>
|
||||
<%= content_tag(:tr,
|
||||
id: row_css_id,
|
||||
class: row_css_class
|
||||
) do %>
|
||||
<% columns.each do |column| %>
|
||||
<td class="<%= column_css_class(column) %>">
|
||||
<%= column_value(column) %>
|
||||
@@ -40,4 +41,4 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<%= link %>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
|
||||
@@ -104,7 +104,7 @@ module Settings
|
||||
|
||||
private
|
||||
|
||||
def internal_comments_translation = t("ee.features.internal_comments").html_safe
|
||||
def internal_comments_translation = t("ee.features.internal_comments")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -39,11 +39,11 @@
|
||||
|
||||
user_limit_row.with_column do
|
||||
render(Primer::Beta::Text.new(color: :danger)) do
|
||||
I18n.t(
|
||||
helpers.link_translate(
|
||||
"sharing.warning_user_limit_reached#{'_admin' if User.current.admin?}",
|
||||
upgrade_url: OpenProject::Enterprise.upgrade_url,
|
||||
entity: entity.model_name.human
|
||||
).html_safe
|
||||
i18n_args: { entity: entity.model_name.human },
|
||||
links: { upgrade_url: OpenProject::Enterprise.upgrade_url }
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -90,9 +90,9 @@ class Users::HoverCardComponent < ApplicationComponent
|
||||
remaining_count_link = link_to(t("users.groups.more", count: remaining_count), user_path(@user))
|
||||
|
||||
if remaining_count > 0
|
||||
t("users.groups.summary_with_more", names: summary_links, count_link: remaining_count_link).html_safe
|
||||
t("users.groups.summary_with_more_html", names: summary_links, count_link: remaining_count_link)
|
||||
else
|
||||
t("users.groups.summary", names: summary_links).html_safe
|
||||
t("users.groups.summary_html", names: summary_links)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
data: {
|
||||
turbo_method: :post
|
||||
}
|
||||
)) do
|
||||
)
|
||||
) do
|
||||
I18n.t(:button_back)
|
||||
end
|
||||
end
|
||||
@@ -20,6 +21,7 @@
|
||||
buttons.with_component(
|
||||
Primer::Beta::Button.new(
|
||||
data: { "close-dialog-id": Users::Invitation::DialogComponent::DIALOG_ID },
|
||||
mr: 1
|
||||
)
|
||||
) do
|
||||
I18n.t(:button_cancel)
|
||||
|
||||
@@ -44,7 +44,8 @@ module Users::Invitation::ProjectStep
|
||||
component_collection do |modal_footer|
|
||||
modal_footer.with_component(
|
||||
Primer::Beta::Button.new(
|
||||
data: { "close-dialog-id": Users::Invitation::DialogComponent::DIALOG_ID }
|
||||
data: { "close-dialog-id": Users::Invitation::DialogComponent::DIALOG_ID },
|
||||
mr: 1
|
||||
)
|
||||
) do
|
||||
I18n.t(:button_cancel)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<%= flex_layout(data: wrapper_data, classes: "users-non-working-times-calendar-view") do |calendar_page| %>
|
||||
<% calendar_page.with_row(classes: "op-fc-wrapper", data: { "users--non-working-times-target" => "calendar" }) %>
|
||||
<% end %>
|
||||
@@ -0,0 +1,126 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module Users
|
||||
module NonWorkingTimes
|
||||
class CalendarComponent < ApplicationComponent
|
||||
include OpTurbo::Streamable
|
||||
include OpPrimer::ComponentHelpers
|
||||
|
||||
options non_working_times: [],
|
||||
year: Date.current.year,
|
||||
user: nil
|
||||
|
||||
private
|
||||
|
||||
def can_update?
|
||||
user.present? && UserNonWorkingTimes::UpdateContract.can_update?(user: User.current, target_user: user)
|
||||
end
|
||||
|
||||
def can_create?
|
||||
user.present? && UserNonWorkingTimes::CreateContract.can_create?(user: User.current, target_user: user)
|
||||
end
|
||||
|
||||
def wrapper_data
|
||||
data = {
|
||||
"controller" => "users--non-working-times",
|
||||
"users--non-working-times-events-value" => events_json,
|
||||
"users--non-working-times-year-value" => year,
|
||||
"users--non-working-times-locale-value" => I18n.locale,
|
||||
"users--non-working-times-start-of-week-value" => first_day_of_week,
|
||||
"users--non-working-times-working-days-value" => working_days.to_json
|
||||
}
|
||||
|
||||
if can_create?
|
||||
data["users--non-working-times-new-url-value"] = new_user_non_working_time_path(user)
|
||||
end
|
||||
|
||||
data
|
||||
end
|
||||
|
||||
def working_days
|
||||
# Setting.working_days is mo=1, tu=2, we=3, th=4, fr=5, sa=6, su=7
|
||||
# businessHours in fullcalendar is su=0, mo=1, tu=2, we=3, th=4, fr=5, sa=6
|
||||
Setting.working_days.map { |day| day % 7 }.sort
|
||||
end
|
||||
|
||||
def events_json
|
||||
(global_events + user_events).to_json
|
||||
end
|
||||
|
||||
def global_events
|
||||
non_working_times
|
||||
.grep(NonWorkingDay)
|
||||
.map do |day|
|
||||
{ date: day.date.iso8601, title: day.name, type: "global" }
|
||||
end
|
||||
end
|
||||
|
||||
def user_events
|
||||
system_dates = non_working_times.grep(NonWorkingDay).to_set(&:date)
|
||||
non_working_times
|
||||
.grep(UserNonWorkingTime)
|
||||
.map { |nwt| user_event_for(nwt, system_dates) }
|
||||
end
|
||||
|
||||
def user_event_for(nwt, system_dates)
|
||||
clipped = nwt.clip_to_year(year, system_non_working_dates: system_dates)
|
||||
{
|
||||
start: clipped.start_date.iso8601,
|
||||
end: (clipped.end_date + 1.day).iso8601,
|
||||
title: event_title(clipped),
|
||||
working_days: clipped.working_days_count,
|
||||
type: "user",
|
||||
edit_url: can_update? ? edit_user_non_working_time_path(user, nwt.id) : nil
|
||||
}.compact
|
||||
end
|
||||
|
||||
def event_title(clipped)
|
||||
base = I18n.t("label_x_working_days_time_off", count: clipped.working_days_count)
|
||||
|
||||
if clipped.continues_from_previous_year
|
||||
"#{base} (#{I18n.t('label_continued_from_previous_year')})"
|
||||
elsif clipped.continues_into_next_year
|
||||
"#{base} (#{I18n.t('label_continues_into_next_year')})"
|
||||
else
|
||||
base
|
||||
end
|
||||
end
|
||||
|
||||
# Maps Setting.start_of_week to FullCalendar's firstDay convention.
|
||||
# Setting: nil=locale default, 1=Monday, 6=Saturday, 7=Sunday
|
||||
# FullCalendar firstDay: 0=Sunday, 1=Monday, ..., 6=Saturday
|
||||
# Nil defaults to 1 (Monday) to match Rails/OpenProject convention.
|
||||
def first_day_of_week
|
||||
(Setting.start_of_week || 1) % 7
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,45 @@
|
||||
.users-non-working-times-year-overview
|
||||
// 100% - page header height - subheader height
|
||||
height: calc(100% - 180px)
|
||||
@media (max-width: $breakpoint-sm)
|
||||
height: 100%
|
||||
|
||||
// Set the display of calendar container
|
||||
#tab-content-non_working_times
|
||||
display: inline
|
||||
|
||||
.users-non-working-times-calendar-view
|
||||
height: 100%
|
||||
|
||||
.op-fc-wrapper
|
||||
flex-grow: 1
|
||||
.users-non-working-times-calendar-view
|
||||
.op-fc-wrapper
|
||||
// Rounded corners for the entire calendar
|
||||
.fc-multiMonthYear-view
|
||||
border-radius: var(--borderRadius-medium) !important
|
||||
|
||||
// Global non-working days rendered as FullCalendar background events
|
||||
.fc-bg-event.non-working-day--global
|
||||
background-color: var(--bgColor-done-muted) !important
|
||||
color: var(--fgColor-sponsors) !important
|
||||
opacity: 0.8 !important
|
||||
border: 1px solid var(--display-pink-scale-5) !important
|
||||
|
||||
// User non-working day ranges rendered as foreground all-day bars
|
||||
.fc-event.non-working-day--user
|
||||
background-color: var(--bgColor-accent-emphasis) !important
|
||||
border-color: var(--bgColor-accent-emphasis) !important
|
||||
color: var(--fgColor-onEmphasis) !important
|
||||
border-radius: var(--borderRadius-medium) !important
|
||||
cursor: pointer !important
|
||||
|
||||
.fc-multimonth-daygrid-table
|
||||
.fc-day
|
||||
background-color: var(--body-background) !important
|
||||
|
||||
&.fc-day-today
|
||||
background-color: var(--bgColor-accent-muted) !important
|
||||
color: var(--fgColor-accent) !important
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<%= component_wrapper do %>
|
||||
<%= render(Primer::Alpha::Dialog.new(id: DIALOG_ID, title:)) do |dialog| %>
|
||||
<% dialog.with_body do %>
|
||||
<%= render(Users::NonWorkingTimes::FormComponent.new(user:, non_working_time:)) %>
|
||||
<% end %>
|
||||
|
||||
<% dialog.with_footer do %>
|
||||
<% if non_working_time.persisted? && can_delete? %>
|
||||
<%= render(
|
||||
Primer::Beta::Button.new(
|
||||
scheme: :danger,
|
||||
tag: :a,
|
||||
href: destroy_url,
|
||||
mr: :auto,
|
||||
data: { turbo_method: :delete, turbo_confirm: t(:text_are_you_sure) }
|
||||
)
|
||||
) do %>
|
||||
<%= t(:button_delete) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<%= render(Primer::Beta::Button.new(data: { "close-dialog-id": DIALOG_ID })) { t(:button_cancel) } %>
|
||||
<%= render(Primer::Beta::Button.new(scheme: :primary, form: Users::NonWorkingTimes::FormComponent::FORM_ID, type: :submit)) { t(:button_confirm) } %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
+16
-27
@@ -28,43 +28,32 @@
|
||||
# See COPYRIGHT and LICENSE files for more details.
|
||||
#++
|
||||
|
||||
module Overviews
|
||||
module ProjectCustomFields
|
||||
module Users
|
||||
module NonWorkingTimes
|
||||
class DialogComponent < ApplicationComponent
|
||||
include ApplicationHelper
|
||||
include OpTurbo::Streamable
|
||||
include OpPrimer::ComponentHelpers
|
||||
|
||||
def initialize(project:, project_custom_field:)
|
||||
super
|
||||
@project = project
|
||||
@project_custom_field = project_custom_field
|
||||
DIALOG_ID = "non-working-time-dialog"
|
||||
|
||||
attr_reader :user, :non_working_time
|
||||
|
||||
def initialize(user:, non_working_time:, **)
|
||||
super(nil, **)
|
||||
@user = user
|
||||
@non_working_time = non_working_time
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def dialog_title
|
||||
@project_custom_field.project_custom_field_section.name
|
||||
def title
|
||||
non_working_time.persisted? ? t(:button_edit_non_working_time) : t(:button_add_non_working_time)
|
||||
end
|
||||
|
||||
def dialog_id
|
||||
"project-custom-field-dialog-#{@project_custom_field.id}"
|
||||
def can_delete?
|
||||
UserNonWorkingTimes::DeleteContract.can_delete?(user: User.current, target_user: user)
|
||||
end
|
||||
|
||||
def wrapper_id
|
||||
"##{dialog_id}"
|
||||
end
|
||||
|
||||
def body_component
|
||||
fail NoMethodError, "Must be overridden in subclass"
|
||||
end
|
||||
|
||||
def close_button_title
|
||||
fail NoMethodError, "Must be overridden in subclass"
|
||||
end
|
||||
|
||||
def footer_buttons(footer_collection)
|
||||
# noop
|
||||
def destroy_url
|
||||
user_non_working_time_path(user, non_working_time)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,51 @@
|
||||
<%= component_wrapper do %>
|
||||
<%= primer_form_with(
|
||||
model: non_working_time,
|
||||
url: form_url,
|
||||
method: form_method,
|
||||
id: FORM_ID,
|
||||
data: {
|
||||
controller: "users--non-working-times-form",
|
||||
"users--non-working-times-form-preview-url-value" => working_days_preview_url
|
||||
}
|
||||
) do |form|
|
||||
render_inline_form(form) do |f|
|
||||
f.group(layout: :horizontal) do |g|
|
||||
g.single_date_picker(
|
||||
name: :start_date,
|
||||
label: I18n.t(:label_start_date),
|
||||
required: true,
|
||||
value: model.start_date&.iso8601,
|
||||
datepicker_options: {
|
||||
inDialog: Users::NonWorkingTimes::DialogComponent::DIALOG_ID,
|
||||
data: {
|
||||
action: "change->users--non-working-times-form#previewWorkingDays"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
g.single_date_picker(
|
||||
name: :end_date,
|
||||
label: I18n.t(:label_end_date),
|
||||
required: true,
|
||||
value: model.end_date&.iso8601,
|
||||
datepicker_options: {
|
||||
inDialog: Users::NonWorkingTimes::DialogComponent::DIALOG_ID,
|
||||
data: {
|
||||
action: "change->users--non-working-times-form#previewWorkingDays"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
g.text_field(
|
||||
name: :working_days_display,
|
||||
label: I18n.t(:label_working_days),
|
||||
readonly: true,
|
||||
value: model.working_days_count,
|
||||
datepicker_options: { inDialog: Users::NonWorkingTimes::DialogComponent::DIALOG_ID },
|
||||
data: { "users--non-working-times-form-target": "workingDaysInput" }
|
||||
)
|
||||
end
|
||||
end
|
||||
end %>
|
||||
<% end %>
|
||||
+26
-27
@@ -28,38 +28,37 @@
|
||||
# See COPYRIGHT and LICENSE files for more details.
|
||||
#++
|
||||
|
||||
module Overviews
|
||||
module ProjectCustomFields
|
||||
class EditDialogComponent < DialogComponent
|
||||
private
|
||||
module Users
|
||||
module NonWorkingTimes
|
||||
class FormComponent < ApplicationComponent
|
||||
include OpTurbo::Streamable
|
||||
include OpPrimer::ComponentHelpers
|
||||
|
||||
def body_component
|
||||
Overviews::ProjectCustomFields::EditComponent.new(
|
||||
project_custom_field: @project_custom_field,
|
||||
project: @project,
|
||||
wrapper_id:
|
||||
)
|
||||
FORM_ID = "non-working-time-form"
|
||||
|
||||
attr_reader :user, :non_working_time
|
||||
|
||||
def initialize(user:, non_working_time:, **)
|
||||
super(nil, **)
|
||||
@user = user
|
||||
@non_working_time = non_working_time
|
||||
end
|
||||
|
||||
def close_button_title
|
||||
t("button_cancel")
|
||||
end
|
||||
|
||||
def footer_buttons(footer_collection)
|
||||
footer_collection.with_component(
|
||||
Primer::Beta::Button.new(
|
||||
scheme: :primary,
|
||||
type: :submit,
|
||||
form: "project-custom-field-edit-form",
|
||||
data: {
|
||||
test_selector: "save-project-attributes-button",
|
||||
turbo: true
|
||||
}
|
||||
)
|
||||
) do
|
||||
t("button_save")
|
||||
def form_url
|
||||
if non_working_time.persisted?
|
||||
user_non_working_time_path(user, non_working_time)
|
||||
else
|
||||
user_non_working_times_path(user)
|
||||
end
|
||||
end
|
||||
|
||||
def form_method
|
||||
non_working_time.persisted? ? :patch : :post
|
||||
end
|
||||
|
||||
def working_days_preview_url
|
||||
working_days_preview_user_non_working_times_path(user)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,40 @@
|
||||
<%= render(Primer::Box.new(border: true, border_radius: 3, p: 3, mb: 3)) do %>
|
||||
<%= render(Primer::Beta::Heading.new(tag: :h3, mb: 2)) do %>
|
||||
<%= t(:label_non_working_times_with_count, year:, count: total_user_days) %>
|
||||
<% end %>
|
||||
|
||||
<% user_non_working_times.each do |nwt| %>
|
||||
<% if can_update? %>
|
||||
<%= link_to edit_href(nwt),
|
||||
class: "color-bg-accent-emphasis color-fg-on-emphasis rounded-2 p-2 mb-1 d-block",
|
||||
data: { controller: "async-dialog" } do %>
|
||||
<%= range_label(nwt) %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= render(Primer::Box.new(bg: :accent_emphasis, color: :on_emphasis, border_radius: 2, p: 2, mb: 1)) do %>
|
||||
<%= range_label(nwt) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= render(Primer::Box.new(border: true, border_radius: 3, p: 3)) do %>
|
||||
<%= render(Primer::Beta::Heading.new(tag: :h3, mb: 2)) do %>
|
||||
<%= t(:label_non_working_times_summary, year:) %>
|
||||
<% end %>
|
||||
|
||||
<%= render(Primer::OpenProject::FlexLayout.new(justify_content: :space_between, mb: 1)) do |row| %>
|
||||
<% row.with_column { t(:label_total_user_non_working_times) } %>
|
||||
<% row.with_column { render(Primer::Beta::Text.new(font_weight: :bold, color: :accent)) { total_user_days.to_s } } %>
|
||||
<% end %>
|
||||
|
||||
<%= render(Primer::OpenProject::FlexLayout.new(justify_content: :space_between, mb: 2)) do |row| %>
|
||||
<% row.with_column { t(:label_total_global_non_working_days) } %>
|
||||
<% row.with_column { render(Primer::Beta::Text.new(font_weight: :bold, color: :sponsors)) { global_day_count.to_s } } %>
|
||||
<% end %>
|
||||
|
||||
<%= render(Primer::OpenProject::FlexLayout.new(justify_content: :space_between)) do |row| %>
|
||||
<% row.with_column { render(Primer::Beta::Text.new(font_weight: :bold)) { t(:label_total_days_off) } } %>
|
||||
<% row.with_column { render(Primer::Beta::Text.new(font_weight: :bold)) { total_days.to_s } } %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
@@ -0,0 +1,88 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module Users
|
||||
module NonWorkingTimes
|
||||
class SidebarComponent < ApplicationComponent
|
||||
include OpPrimer::ComponentHelpers
|
||||
|
||||
options non_working_times: [],
|
||||
year: Date.current.year,
|
||||
user: nil
|
||||
|
||||
private
|
||||
|
||||
def user_non_working_times
|
||||
system_dates = non_working_times.grep(NonWorkingDay).to_set(&:date)
|
||||
non_working_times
|
||||
.grep(UserNonWorkingTime)
|
||||
.sort_by(&:start_date)
|
||||
.map { |nwt| nwt.clip_to_year(year, system_non_working_dates: system_dates) }
|
||||
end
|
||||
|
||||
def global_day_count
|
||||
non_working_times.count { |d| d.is_a?(NonWorkingDay) }
|
||||
end
|
||||
|
||||
def total_user_days
|
||||
user_non_working_times.sum(&:working_days_count)
|
||||
end
|
||||
|
||||
def total_days
|
||||
total_user_days + global_day_count
|
||||
end
|
||||
|
||||
def can_update?
|
||||
user.present? && UserNonWorkingTimes::UpdateContract.can_update?(user: User.current, target_user: user)
|
||||
end
|
||||
|
||||
def can_delete?
|
||||
user.present? && UserNonWorkingTimes::DeleteContract.can_delete?(user: User.current, target_user: user)
|
||||
end
|
||||
|
||||
def edit_href(clipped)
|
||||
edit_user_non_working_time_path(user, clipped.id)
|
||||
end
|
||||
|
||||
def range_label(clipped)
|
||||
date_range = format_date_range(clipped.start_date, clipped.end_date)
|
||||
"#{date_range}: #{I18n.t('label_x_working_days', count: clipped.working_days_count)}"
|
||||
end
|
||||
|
||||
def format_date_range(first, last)
|
||||
if first.year == last.year
|
||||
"#{I18n.l(first, format: :short)} - #{I18n.l(last, format: :short)}, #{first.year}"
|
||||
else
|
||||
"#{I18n.l(first, format: :long)} - #{I18n.l(last, format: :long)}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,30 @@
|
||||
<%= render(Primer::OpenProject::SubHeader.new) do |component|
|
||||
component.with_text { year.to_s }
|
||||
|
||||
component.with_action_button_group do |group|
|
||||
group.with_button(icon: "arrow-left", tag: :a, **previous_year_attrs)
|
||||
group.with_button(icon: "arrow-right", tag: :a, **next_year_attrs)
|
||||
end
|
||||
|
||||
component.with_action_button(
|
||||
tag: :a,
|
||||
href: today_href,
|
||||
leading_icon: :"op-calendar-day",
|
||||
label: I18n.t(:label_today)
|
||||
) do
|
||||
I18n.t(:label_today_capitalized)
|
||||
end
|
||||
|
||||
if can_create?
|
||||
component.with_action_button(
|
||||
scheme: :primary,
|
||||
leading_icon: :plus,
|
||||
label: I18n.t(:button_add_non_working_time),
|
||||
data: { controller: "async-dialog" },
|
||||
tag: :a,
|
||||
href: new_non_working_time_href
|
||||
) do
|
||||
t(:button_add_non_working_time)
|
||||
end
|
||||
end
|
||||
end %>
|
||||
@@ -0,0 +1,69 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module Users
|
||||
module NonWorkingTimes
|
||||
class SubHeaderComponent < ApplicationComponent
|
||||
options :year, :user
|
||||
|
||||
def can_create?
|
||||
UserNonWorkingTimes::CreateContract.can_create?(user: User.current, target_user: user)
|
||||
end
|
||||
|
||||
def new_non_working_time_href
|
||||
new_user_non_working_time_path(user)
|
||||
end
|
||||
|
||||
def previous_year_attrs
|
||||
{
|
||||
href: path_for(year: year - 1),
|
||||
aria: { label: I18n.t(:label_previous_year) }
|
||||
}
|
||||
end
|
||||
|
||||
def next_year_attrs
|
||||
{
|
||||
href: path_for(year: year + 1),
|
||||
aria: { label: I18n.t(:label_next_year) }
|
||||
}
|
||||
end
|
||||
|
||||
def today_href
|
||||
path_for(year: Date.current.year)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def path_for(year:)
|
||||
url_for(controller: params[:controller], action: params[:action], user_id: params[:user_id], year:, tab: params[:tab])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,62 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module Users
|
||||
module NonWorkingTimes
|
||||
class YearOverviewComponent < ApplicationComponent
|
||||
attr_reader :non_working_times, :year, :user
|
||||
|
||||
def initialize(year:, non_working_times:, user:, **)
|
||||
super(**)
|
||||
@year = year
|
||||
@non_working_times = non_working_times
|
||||
@user = user
|
||||
end
|
||||
|
||||
def call
|
||||
render(Users::NonWorkingTimes::SubHeaderComponent.new(year:, user:)) +
|
||||
render(Primer::Alpha::Layout.new(classes: "users-non-working-times-year-overview")) do |layout|
|
||||
layout.with_main { render_calendar }
|
||||
layout.with_sidebar(col_placement: :end, width: :wide) { render_sidebar }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def render_calendar
|
||||
render(Users::NonWorkingTimes::CalendarComponent.new(non_working_times:, year:, user:))
|
||||
end
|
||||
|
||||
def render_sidebar
|
||||
render(Users::NonWorkingTimes::SidebarComponent.new(non_working_times:, year:, user:))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -29,7 +29,8 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<%=
|
||||
render(Primer::OpenProject::PageHeader.new) do |header|
|
||||
header.with_title do
|
||||
"#{avatar(@user, hover_card: { active: false })} #{h(@user.name)}".html_safe
|
||||
concat avatar(@user, hover_card: { active: false })
|
||||
concat @user.name
|
||||
end
|
||||
header.with_breadcrumbs(breadcrumb_items)
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
class Users::WorkingHours::AvailabilityFactorForm < ApplicationForm
|
||||
form do |form|
|
||||
form.html_content do
|
||||
render(Primer::Beta::Subhead.new(spacious: true)) do |component|
|
||||
component.with_heading(tag: :div) do
|
||||
I18n.t("users.working_hours.form.title_availability_factor")
|
||||
end
|
||||
|
||||
component.with_description do
|
||||
I18n.t("users.working_hours.form.availability_description")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
form.text_field name: :availability_factor,
|
||||
label: UserWorkingHours.human_attribute_name(:availability_factor),
|
||||
caption: I18n.t("users.working_hours.form.availability_factor_caption"),
|
||||
input_width: :large,
|
||||
inputmode: "numeric",
|
||||
value: model.availability_factor,
|
||||
data: {
|
||||
"users--working-hours-form-target": "availabilityFactorInput",
|
||||
action: "input->users--working-hours-form#availabilityChanged"
|
||||
},
|
||||
trailing_visual: { text: { text: "%" } }
|
||||
|
||||
form.text_field name: :total_factored_hours,
|
||||
label: I18n.t("users.working_hours.form.total_available_hours"),
|
||||
input_width: :large,
|
||||
readonly: true,
|
||||
data: { "users--working-hours-form-target": "totalAvailableHoursDisplay" },
|
||||
trailing_visual: { text: { text: I18n.t("users.working_hours.form.per_week") } }
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,81 @@
|
||||
<%#-- copyright
|
||||
OpenProject is an open source project management software.
|
||||
Copyright (C) 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.
|
||||
|
||||
++#%>
|
||||
|
||||
<%= render(Primer::Box.new(border: true, border_radius: 3, bg: :accent, p: 3)) do %>
|
||||
<%= render(Primer::Box.new(display: :flex, justify_content: :space_between, align_items: :center, mb: 3)) do %>
|
||||
<%= render(Primer::Beta::Heading.new(tag: :h3)) do %>
|
||||
<%= t("users.working_hours.current_schedule.title") %>
|
||||
<% end %>
|
||||
<% if editable? %>
|
||||
<%= render(
|
||||
Primer::Beta::IconButton.new(
|
||||
icon: :pencil,
|
||||
tag: :a,
|
||||
href: create_or_edit_path,
|
||||
"aria-label": t("button_edit"),
|
||||
data: { controller: "async-dialog" }
|
||||
)
|
||||
) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= render(Primer::Box.new(display: :flex, classes: "gap-3")) do %>
|
||||
<%= render(
|
||||
Users::WorkingHours::StatCardComponent.new(
|
||||
label: t("users.working_hours.current_schedule.work_days"),
|
||||
value: work_days_value,
|
||||
subtitle: work_days_subtitle
|
||||
)
|
||||
) %>
|
||||
|
||||
<%= render(
|
||||
Users::WorkingHours::StatCardComponent.new(
|
||||
label: t("users.working_hours.current_schedule.work_hours"),
|
||||
value: weekly_hours_value,
|
||||
subtitle: weekly_hours_subtitle
|
||||
)
|
||||
) %>
|
||||
|
||||
<%= render(
|
||||
Users::WorkingHours::StatCardComponent.new(
|
||||
label: t("users.working_hours.current_schedule.availability_factor"),
|
||||
value: availability_value,
|
||||
subtitle: t("users.working_hours.current_schedule.availability_subtitle")
|
||||
)
|
||||
) %>
|
||||
|
||||
<%= render(
|
||||
Users::WorkingHours::StatCardComponent.new(
|
||||
label: t("users.working_hours.current_schedule.effective_hours"),
|
||||
value: effective_hours_value,
|
||||
subtitle: t("users.working_hours.current_schedule.effective_subtitle")
|
||||
)
|
||||
) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
@@ -0,0 +1,105 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module Users
|
||||
module WorkingHours
|
||||
class CurrentScheduleComponent < ApplicationComponent
|
||||
attr_reader :working_hours, :user
|
||||
|
||||
def initialize(working_hours:, user:, **)
|
||||
super(working_hours, **)
|
||||
@working_hours = working_hours
|
||||
@user = user
|
||||
end
|
||||
|
||||
def editable?
|
||||
if working_hours && working_hours.valid_from == Date.current
|
||||
UserWorkingHours::UpdateContract.can_update?(user: User.current, working_hours:)
|
||||
else
|
||||
UserWorkingHours::CreateContract.can_create?(user: User.current, target_user: user)
|
||||
end
|
||||
end
|
||||
|
||||
def work_days_value
|
||||
return "–" unless working_hours
|
||||
|
||||
UserWorkingHours::DAYS.count { |day| working_hours.public_send(day) > 0 }.to_s
|
||||
end
|
||||
|
||||
def work_days_subtitle
|
||||
return t("users.working_hours.current_schedule.not_set") unless working_hours
|
||||
|
||||
working_hours.working_day_ranges
|
||||
end
|
||||
|
||||
def weekly_hours_value
|
||||
return "–" unless working_hours
|
||||
|
||||
format_hours(working_hours.weekly_working_hours)
|
||||
end
|
||||
|
||||
def weekly_hours_subtitle
|
||||
return t("users.working_hours.current_schedule.not_set") unless working_hours
|
||||
|
||||
working_hours.working_days_summary
|
||||
end
|
||||
|
||||
def availability_value
|
||||
return "–" unless working_hours
|
||||
|
||||
"#{working_hours.availability_factor}%"
|
||||
end
|
||||
|
||||
def effective_hours_value
|
||||
return "–" unless working_hours
|
||||
|
||||
format_hours(working_hours.effective_weekly_working_hours)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def format_hours(hours)
|
||||
formatted = helpers.number_with_precision(hours,
|
||||
precision: 2,
|
||||
strip_insignificant_zeros: true,
|
||||
separator: I18n.t("number.format.separator"))
|
||||
"#{formatted}h"
|
||||
end
|
||||
|
||||
def create_or_edit_path
|
||||
if working_hours && working_hours.valid_from == Date.current
|
||||
edit_user_working_hour_path(user, working_hours, current: true)
|
||||
else
|
||||
new_user_working_hour_path(user, current: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,161 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
class Users::WorkingHours::DaysAndHoursForm < ApplicationForm
|
||||
def initialize(is_first_form:)
|
||||
super()
|
||||
@is_first_form = is_first_form
|
||||
end
|
||||
|
||||
form do |form|
|
||||
form.html_content do
|
||||
render(Primer::Beta::Subhead.new(spacious: !@is_first_form)) do |component|
|
||||
component.with_heading(tag: :div, size: :medium) do
|
||||
I18n.t("users.working_hours.form.title_days_and_hours")
|
||||
end
|
||||
end
|
||||
|
||||
if model.errors[:days].present?
|
||||
render(Primer::Alpha::Banner.new(mb: 3, icon: :stop, scheme: :danger)) { model.errors[:days].join("\n") }
|
||||
end
|
||||
end
|
||||
|
||||
form.group(layout: :horizontal, mb: 2) do |group|
|
||||
ordered_days.each do |day|
|
||||
group.hidden name: "#{day}_hours", value: 0
|
||||
group.check_box name: "day_enabled_#{day}",
|
||||
data: {
|
||||
"users--working-hours-form-target": "dayCheckbox",
|
||||
day: day,
|
||||
action: "users--working-hours-form#dayToggled"
|
||||
},
|
||||
checked: day_enabled?(day),
|
||||
label: full_day_name(day),
|
||||
label_arguments: { mr: 3 }
|
||||
end
|
||||
end
|
||||
|
||||
form.radio_button_group(name: "hours_mode", label: I18n.t("users.working_hours.form.hours_mode_label"),
|
||||
mb: 2) do |group|
|
||||
group.radio_button(
|
||||
label: I18n.t("users.working_hours.form.same_hours_mode"),
|
||||
value: "same",
|
||||
checked: all_same_hours?,
|
||||
data: { action: "users--working-hours-form#hoursModeChanged" }
|
||||
)
|
||||
group.radio_button(
|
||||
label: I18n.t("users.working_hours.form.individual_hours_mode"),
|
||||
value: "individual",
|
||||
checked: !all_same_hours?,
|
||||
data: { action: "users--working-hours-form#hoursModeChanged" }
|
||||
)
|
||||
end
|
||||
|
||||
copy_day_errors_to_shared_hours
|
||||
|
||||
form.group(data: { "users--working-hours-form-target": "sameHoursSection" }) do |group|
|
||||
group.text_field name: :shared_hours,
|
||||
label: UserWorkingHours.human_attribute_name(:shared_hours),
|
||||
input_width: :large,
|
||||
value: shared_hours,
|
||||
data: {
|
||||
"users--working-hours-form-target": "sharedHoursInput",
|
||||
action: "input->users--working-hours-form#hoursChanged blur->users--working-hours-form#hoursFormatted"
|
||||
},
|
||||
trailing_visual: { text: { text: I18n.t("users.working_hours.form.per_day") } }
|
||||
end
|
||||
|
||||
form.group(data: { "users--working-hours-form-target": "individualSection" }) do |group|
|
||||
ordered_days.each do |day|
|
||||
group.text_field name: "#{day}_hours",
|
||||
label: UserWorkingHours.human_attribute_name("#{day}_hours"),
|
||||
value: day_hours(day),
|
||||
input_width: :large,
|
||||
data: {
|
||||
"users--working-hours-form-target": "dayHoursInput",
|
||||
day: day,
|
||||
action: "input->users--working-hours-form#hoursChanged blur->users--working-hours-form#hoursFormatted"
|
||||
},
|
||||
disabled: !day_enabled?(day)
|
||||
end
|
||||
end
|
||||
|
||||
form.text_field name: :total_work_hours,
|
||||
label: I18n.t("users.working_hours.form.total_work_hours"),
|
||||
input_width: :large,
|
||||
readonly: true,
|
||||
data: { "users--working-hours-form-target": "totalWorkHoursDisplay" },
|
||||
trailing_visual: { text: { text: I18n.t("users.working_hours.form.per_week") } }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ordered_days
|
||||
# DAYS = [monday(0), tuesday(1), ..., saturday(5), sunday(6)]
|
||||
# Setting.start_of_week: 1=Monday, 6=Saturday, 7=Sunday, nil=locale default (treat as Monday)
|
||||
start_index = case Setting.start_of_week
|
||||
when 6 then UserWorkingHours::DAYS.index(:saturday)
|
||||
when 7 then UserWorkingHours::DAYS.index(:sunday)
|
||||
else 0 # Monday
|
||||
end
|
||||
UserWorkingHours::DAYS.rotate(start_index)
|
||||
end
|
||||
|
||||
def day_enabled?(day)
|
||||
model.public_send(day) > 0
|
||||
end
|
||||
|
||||
def day_hours(day)
|
||||
"#{model.public_send("#{day}_hours").round(2)}h"
|
||||
end
|
||||
|
||||
def all_same_hours?
|
||||
enabled = UserWorkingHours::DAYS.select { |d| day_enabled?(d) }
|
||||
return true if enabled.empty?
|
||||
|
||||
enabled.map { |d| day_hours(d) }.uniq.one?
|
||||
end
|
||||
|
||||
def shared_hours
|
||||
first_enabled = UserWorkingHours::DAYS.find { |d| day_enabled?(d) }
|
||||
first_enabled ? day_hours(first_enabled) : "#{Setting.hours_per_day.round(2)}h"
|
||||
end
|
||||
|
||||
def copy_day_errors_to_shared_hours
|
||||
UserWorkingHours::DAYS
|
||||
.flat_map { |day| model.errors[:"#{day}_hours"] }
|
||||
.uniq
|
||||
.each { |message| model.errors.add(:shared_hours, message) }
|
||||
end
|
||||
|
||||
def full_day_name(day)
|
||||
I18n.t("date.day_names")[UserWorkingHours::DAY_ABBR_INDEX[day]]
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,45 @@
|
||||
<%#-- copyright
|
||||
OpenProject is an open source project management software.
|
||||
Copyright (C) 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.
|
||||
|
||||
++#%>
|
||||
|
||||
<%= render(Primer::Alpha::Dialog.new(id: DIALOG_ID, title:, position: :right, size: :xlarge)) do |dialog| %>
|
||||
<% dialog.with_header(variant: :large) %>
|
||||
<% dialog.with_body do %>
|
||||
<%= render(Users::WorkingHours::FormComponent.new(user:, working_hours:, show_valid_from:)) %>
|
||||
<% end %>
|
||||
<% dialog.with_footer do %>
|
||||
<%= component_collection do |footer| %>
|
||||
<% footer.with_component(Primer::Beta::Button.new(data: { "close-dialog-id": DIALOG_ID })) do %>
|
||||
<%= t(:button_cancel) %>
|
||||
<% end %>
|
||||
<% footer.with_component(Primer::Beta::Button.new(scheme: :primary, form: Users::WorkingHours::FormComponent::FORM_ID, type: :submit)) do %>
|
||||
<%= working_hours.persisted? ? t(:button_save) : t(:button_create) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
@@ -0,0 +1,57 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module Users
|
||||
module WorkingHours
|
||||
class DialogComponent < ApplicationComponent
|
||||
include OpTurbo::Streamable
|
||||
include OpPrimer::ComponentHelpers
|
||||
|
||||
DIALOG_ID = "working-hours-dialog"
|
||||
|
||||
attr_reader :user, :working_hours, :show_valid_from
|
||||
|
||||
def initialize(user:, working_hours:, show_valid_from: true, **)
|
||||
super(nil, **)
|
||||
@user = user
|
||||
@working_hours = working_hours
|
||||
@show_valid_from = show_valid_from
|
||||
end
|
||||
|
||||
def title
|
||||
if show_valid_from
|
||||
t("users.working_hours.form.title")
|
||||
else
|
||||
t("users.working_hours.form.title_current")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+9
-8
@@ -27,11 +27,12 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
|
||||
++#%>
|
||||
|
||||
<%= attachments.select { |a| a.readable? and a.visible? }
|
||||
.map do |a|
|
||||
{ content_type: CGI::escape_html(a.content_type),
|
||||
filename: CGI::escape_html(a.filename),
|
||||
description: CGI::escape_html(a.description),
|
||||
url: url_for(controller: "/attachments", action: "show", id: a, filename: a.filename),
|
||||
is_image: !!a.image? } # doing the !! as image? for whatever reason returns null or a number
|
||||
end.to_json.html_safe %>
|
||||
<%= component_wrapper do %>
|
||||
<%= primer_form_with(**form_options) do |f| %>
|
||||
<% if show_valid_from %>
|
||||
<%= render(Users::WorkingHours::ValidFromForm.new(f)) %>
|
||||
<% end %>
|
||||
<%= render(Users::WorkingHours::DaysAndHoursForm.new(f, is_first_form: !show_valid_from)) %>
|
||||
<%= render(Users::WorkingHours::AvailabilityFactorForm.new(f)) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
@@ -0,0 +1,79 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module Users
|
||||
module WorkingHours
|
||||
class FormComponent < ApplicationComponent
|
||||
include OpTurbo::Streamable
|
||||
include OpPrimer::ComponentHelpers
|
||||
|
||||
FORM_ID = "working-hours-form"
|
||||
|
||||
attr_reader :user, :working_hours, :show_valid_from
|
||||
|
||||
def initialize(user:, working_hours:, show_valid_from: true, **)
|
||||
super(nil, **)
|
||||
@user = user
|
||||
@working_hours = working_hours
|
||||
@show_valid_from = show_valid_from
|
||||
end
|
||||
|
||||
def form_url
|
||||
url_params = show_valid_from ? {} : { current: true }
|
||||
|
||||
if working_hours.persisted?
|
||||
user_working_hour_path(user, working_hours, **url_params)
|
||||
else
|
||||
user_working_hours_path(user, **url_params)
|
||||
end
|
||||
end
|
||||
|
||||
def form_options
|
||||
{
|
||||
model: working_hours,
|
||||
url: form_url,
|
||||
method: form_method,
|
||||
|
||||
html: {
|
||||
id: FORM_ID,
|
||||
data: {
|
||||
turbo: true,
|
||||
controller: "users--working-hours-form"
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def form_method
|
||||
working_hours.persisted? ? :patch : :post
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,110 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module Users
|
||||
module WorkingHours
|
||||
class ScheduleRowComponent < OpPrimer::BorderBoxRowComponent
|
||||
def working_hours
|
||||
model
|
||||
end
|
||||
|
||||
def start_date
|
||||
helpers.format_date(working_hours.valid_from)
|
||||
end
|
||||
|
||||
def work_days
|
||||
working_hours.working_days_summary
|
||||
end
|
||||
|
||||
def work_hours
|
||||
formatted = helpers.number_with_precision(working_hours.weekly_working_hours,
|
||||
precision: 2,
|
||||
strip_insignificant_zeros: true,
|
||||
separator: I18n.t("number.format.separator"))
|
||||
"#{formatted}h"
|
||||
end
|
||||
|
||||
def availability_factor
|
||||
"#{working_hours.availability_factor}%"
|
||||
end
|
||||
|
||||
def effective_work_hours
|
||||
"#{working_hours.effective_weekly_working_hours}h"
|
||||
end
|
||||
|
||||
def button_links
|
||||
[action_menu]
|
||||
end
|
||||
|
||||
def action_menu
|
||||
render(Primer::Alpha::ActionMenu.new) do |menu|
|
||||
menu.with_show_button(icon: "kebab-horizontal", "aria-label": t(:label_more), scheme: :invisible)
|
||||
add_edit_item(menu) if can_update?
|
||||
add_delete_item(menu) if can_delete?
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def can_update?
|
||||
UserWorkingHours::UpdateContract.can_update?(user: User.current, working_hours:)
|
||||
end
|
||||
|
||||
def can_delete?
|
||||
UserWorkingHours::DeleteContract.can_delete?(user: User.current, target_user: table.user)
|
||||
end
|
||||
|
||||
def add_edit_item(menu)
|
||||
menu.with_item(label: t(:button_edit),
|
||||
href: edit_user_working_hour_path(table.user, working_hours),
|
||||
tag: :a,
|
||||
content_arguments: { data: { controller: "async-dialog" } }) do |item|
|
||||
item.with_leading_visual_icon(icon: :pencil)
|
||||
end
|
||||
end
|
||||
|
||||
def add_delete_item(menu)
|
||||
menu.with_item(label: t(:button_delete),
|
||||
href: user_working_hour_path(table.user, working_hours),
|
||||
tag: :a,
|
||||
scheme: :danger,
|
||||
content_arguments: {
|
||||
data: {
|
||||
"turbo-method": :delete,
|
||||
"turbo-stream": true,
|
||||
"turbo-confirm": t("users.working_hours.destroy.confirm")
|
||||
}
|
||||
}) do |item|
|
||||
item.with_leading_visual_icon(icon: :trash)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,109 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module Users
|
||||
module WorkingHours
|
||||
class ScheduleTableComponent < OpPrimer::BorderBoxTableComponent
|
||||
columns :start_date, :work_days, :work_hours, :availability_factor, :effective_work_hours
|
||||
main_column :work_days
|
||||
|
||||
attr_reader :variant, :user
|
||||
|
||||
def initialize(rows:, variant:, user:, **)
|
||||
super(rows:, **)
|
||||
@variant = variant
|
||||
@user = user
|
||||
end
|
||||
|
||||
def has_actions?
|
||||
true
|
||||
end
|
||||
|
||||
def blank_icon
|
||||
:calendar
|
||||
end
|
||||
|
||||
def row_class
|
||||
Users::WorkingHours::ScheduleRowComponent
|
||||
end
|
||||
|
||||
def mobile_title
|
||||
I18n.t("users.working_hours.table.mobile_title")
|
||||
end
|
||||
|
||||
def headers
|
||||
[
|
||||
[:start_date, { caption: I18n.t("users.working_hours.table.start_date") }],
|
||||
[:work_days, { caption: I18n.t("users.working_hours.table.work_days") }],
|
||||
[:work_hours, { caption: I18n.t("users.working_hours.table.work_hours") }],
|
||||
[:availability_factor, { caption: I18n.t("users.working_hours.table.availability_factor") }],
|
||||
[:effective_work_hours, { caption: I18n.t("users.working_hours.table.effective_work_hours") }]
|
||||
]
|
||||
end
|
||||
|
||||
def action_row_header_content
|
||||
return unless variant == :future
|
||||
return unless UserWorkingHours::CreateContract.can_create?(user: User.current, target_user: user)
|
||||
|
||||
render(Primer::Beta::IconButton.new(
|
||||
icon: :plus,
|
||||
scheme: :invisible,
|
||||
tag: :a,
|
||||
href: new_user_working_hour_path(user),
|
||||
data: { turbo: true, controller: "async-dialog" },
|
||||
"aria-label": I18n.t("users.working_hours.future.add_button")
|
||||
))
|
||||
end
|
||||
|
||||
def blank_title
|
||||
I18n.t("users.working_hours.#{@variant}.blank_title")
|
||||
end
|
||||
|
||||
def blank_description
|
||||
I18n.t("users.working_hours.#{@variant}.blank_description")
|
||||
end
|
||||
|
||||
def render_blank_slate
|
||||
render(Primer::Beta::Blankslate.new(border: false)) do |component|
|
||||
component.with_visual_icon(icon: :calendar, size: :medium)
|
||||
component.with_heading(tag: :h2) { blank_title }
|
||||
component.with_description { blank_description }
|
||||
if variant == :future && UserWorkingHours::CreateContract.can_create?(user: User.current, target_user: user)
|
||||
component.with_primary_action(href: new_user_working_hour_path(user),
|
||||
data: { turbo: true,
|
||||
controller: "async-dialog" }) do
|
||||
I18n.t("users.working_hours.future.add_button")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,34 @@
|
||||
<%#-- copyright
|
||||
OpenProject is an open source project management software.
|
||||
Copyright (C) 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.
|
||||
|
||||
++#%>
|
||||
|
||||
<%= render(Primer::Box.new(border: true, border_radius: 3, bg: :default, p: 4, flex: 1)) do %>
|
||||
<%= render(Primer::Beta::Text.new(color: :subtle, font_size: 4, tag: :p)) { label } %>
|
||||
<%= render(Primer::Beta::Text.new(font_weight: :normal, font_size: 1, mb: 2)) { value } %>
|
||||
<%= render(Primer::Beta::Text.new(color: :subtle, font_size: :small, tag: :p)) { subtitle } %>
|
||||
<% end %>
|
||||
@@ -0,0 +1,44 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module Users
|
||||
module WorkingHours
|
||||
class StatCardComponent < ApplicationComponent
|
||||
attr_reader :label, :value, :subtitle
|
||||
|
||||
def initialize(label:, value:, subtitle:, **)
|
||||
super(nil, **)
|
||||
@label = label.to_s
|
||||
@value = value.to_s
|
||||
@subtitle = subtitle.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,50 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
class Users::WorkingHours::ValidFromForm < ApplicationForm
|
||||
form do |f|
|
||||
f.html_content do
|
||||
render(Primer::Beta::Subhead.new) do |component|
|
||||
component.with_heading(tag: :div, size: :medium) do
|
||||
I18n.t("users.working_hours.form.title_future_dates")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
f.single_date_picker name: :valid_from,
|
||||
type: "date",
|
||||
required: true,
|
||||
input_width: :large,
|
||||
datepicker_options: { inDialog: Users::WorkingHours::DialogComponent::DIALOG_ID },
|
||||
value: model.valid_from&.iso8601,
|
||||
caption: I18n.t("users.working_hours.form.start_date_caption"),
|
||||
label: I18n.t("users.working_hours.form.start_date")
|
||||
end
|
||||
end
|
||||
@@ -42,13 +42,13 @@
|
||||
target: "_blank"
|
||||
)
|
||||
) do
|
||||
I18n.t("js.label_committed_link", revision_identifier: short_revision)
|
||||
I18n.t(:label_committed_link, revision_identifier: short_revision)
|
||||
end
|
||||
I18n.t(
|
||||
"js.label_committed_at",
|
||||
committed_revision_link: committed_text.html_safe,
|
||||
:label_committed_at_html,
|
||||
committed_revision_link: committed_text,
|
||||
date: format_time(changeset.committed_on)
|
||||
).html_safe
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -69,13 +69,13 @@
|
||||
target: "_blank"
|
||||
)
|
||||
) do
|
||||
I18n.t("js.label_committed_link", revision_identifier: short_revision)
|
||||
I18n.t(:label_committed_link, revision_identifier: short_revision)
|
||||
end
|
||||
I18n.t(
|
||||
"js.label_committed_at",
|
||||
committed_revision_link: committed_text.html_safe,
|
||||
t(
|
||||
:label_committed_at_html,
|
||||
committed_revision_link: committed_text,
|
||||
date: format_time(changeset.committed_on)
|
||||
).html_safe
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
<%#
|
||||
-- copyright
|
||||
OpenProject is an open source project management software.
|
||||
Copyright (C) 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.
|
||||
|
||||
++#
|
||||
%>
|
||||
|
||||
<%=
|
||||
render(
|
||||
Primer::OpenProject::DangerDialog.new(
|
||||
id: "change-identifiers-dialog",
|
||||
title: I18n.t("admin.settings.work_packages_identifier.dialog.title"),
|
||||
confirm_button_text: I18n.t("admin.settings.work_packages_identifier.dialog.confirm_button"),
|
||||
cancel_button_text: I18n.t("button_close"),
|
||||
size: :large,
|
||||
form_arguments: {
|
||||
action: admin_settings_work_packages_identifier_path,
|
||||
method: :patch
|
||||
}
|
||||
)
|
||||
) do |dialog|
|
||||
dialog.with_confirmation_message do |message|
|
||||
message.with_heading(tag: :h2) do
|
||||
I18n.t("admin.settings.work_packages_identifier.dialog.heading")
|
||||
end
|
||||
|
||||
message.with_description_content(
|
||||
I18n.t("admin.settings.work_packages_identifier.dialog.description")
|
||||
)
|
||||
end
|
||||
|
||||
dialog.with_confirmation_check_box_content(
|
||||
I18n.t("admin.settings.work_packages_identifier.dialog.checkbox_label")
|
||||
)
|
||||
|
||||
dialog.with_additional_details(display: :none) do
|
||||
hidden_field_tag("settings[work_packages_identifier]", Setting::WorkPackageIdentifier::ALPHANUMERIC)
|
||||
end
|
||||
end
|
||||
%>
|
||||
@@ -0,0 +1,40 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module WorkPackages
|
||||
module Admin
|
||||
module Settings
|
||||
class ChangeIdentifiersDialogComponent < ApplicationComponent
|
||||
include OpPrimer::ComponentHelpers
|
||||
include OpTurbo::Streamable
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
<%#
|
||||
-- copyright
|
||||
OpenProject is an open source project management software.
|
||||
Copyright (C) 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.
|
||||
|
||||
++#
|
||||
%>
|
||||
|
||||
<%=
|
||||
render(Primer::Alpha::Banner.new(scheme: :warning, my: 3)) do
|
||||
I18n.t(
|
||||
"admin.settings.work_packages_identifier.banner.existing_identifiers_notice",
|
||||
project_count: total_count
|
||||
)
|
||||
end
|
||||
%>
|
||||
|
||||
<%=
|
||||
render(border_box_container(mb: 3)) do |component|
|
||||
component.with_header(font_weight: :bold) do
|
||||
flex_layout do |header|
|
||||
header.with_column(flex: 1) do
|
||||
render(Primer::Beta::Text.new(font_weight: :semibold)) do
|
||||
I18n.t("admin.settings.work_packages_identifier.box_header.label_project")
|
||||
end
|
||||
end
|
||||
header.with_column(flex: 1) do
|
||||
render(Primer::Beta::Text.new(font_weight: :semibold)) do
|
||||
I18n.t("admin.settings.work_packages_identifier.box_header.label_previous_identifier")
|
||||
end
|
||||
end
|
||||
header.with_column(flex: 1) do
|
||||
render(Primer::Beta::Text.new(font_weight: :semibold)) do
|
||||
I18n.t("admin.settings.work_packages_identifier.box_header.label_autofixed_suggestion")
|
||||
end
|
||||
end
|
||||
header.with_column(flex: 1) do
|
||||
render(Primer::Beta::Text.new(font_weight: :semibold)) do
|
||||
I18n.t("admin.settings.work_packages_identifier.box_header.label_example_work_package_id")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
displayed.each do |entry|
|
||||
component.with_row do
|
||||
flex_layout(align_items: :center) do |row|
|
||||
row.with_column(flex: 1) do
|
||||
render(Primer::Beta::Link.new(href: project_path(entry[:project]))) do
|
||||
entry[:project].name
|
||||
end
|
||||
end
|
||||
row.with_column(flex: 1) do
|
||||
flex_layout(direction: :column) do |col|
|
||||
col.with_row do
|
||||
render(Primer::Beta::Text.new) { entry[:current_identifier] }
|
||||
end
|
||||
col.with_row do
|
||||
render(Primer::Beta::Text.new(color: :danger, font_size: :small)) do
|
||||
error_label(entry[:error_reason])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
row.with_column(flex: 1) do
|
||||
render(Primer::Beta::Text.new) { entry[:suggested_identifier] }
|
||||
end
|
||||
row.with_column(flex: 1) do
|
||||
render(Primer::Beta::Text.new) { sample_wp_id(entry[:suggested_identifier]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if remaining_count.positive?
|
||||
component.with_row do
|
||||
render(Primer::Beta::Text.new(color: :muted)) do
|
||||
I18n.t(
|
||||
"admin.settings.work_packages_identifier.autofix_preview.remaining_projects",
|
||||
count: remaining_count
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
%>
|
||||
@@ -0,0 +1,65 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
|
||||
module WorkPackages
|
||||
module Admin
|
||||
module Settings
|
||||
class IdentifierAutofixSectionComponent < ApplicationComponent
|
||||
include OpPrimer::ComponentHelpers
|
||||
|
||||
DISPLAY_COUNT = WorkPackages::IdentifierAutofix::PreviewQuery::DISPLAY_COUNT
|
||||
|
||||
def initialize(projects_data:, total_count: projects_data.size)
|
||||
super()
|
||||
@total_count = total_count
|
||||
@displayed = projects_data.first(DISPLAY_COUNT)
|
||||
@remaining_count = [total_count - @displayed.size, 0].max
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :total_count, :displayed, :remaining_count
|
||||
|
||||
def error_label(error_reason)
|
||||
I18n.t("admin.settings.work_packages_identifier.autofix_preview.error_#{error_reason}",
|
||||
default: "")
|
||||
end
|
||||
|
||||
# Produces a realistic-looking example work package ID for the preview table.
|
||||
# The sequence number is derived deterministically from the identifier so it looks
|
||||
# varied across projects but is stable across renders. Range: 1–500.
|
||||
def sample_wp_id(identifier)
|
||||
n = (identifier.bytes.sum % 500) + 1
|
||||
"#{identifier}-#{n}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
<%#
|
||||
-- copyright
|
||||
OpenProject is an open source project management software.
|
||||
Copyright (C) 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.
|
||||
|
||||
++#
|
||||
%>
|
||||
|
||||
<%= component_wrapper(**wrapper_data_attrs) do %>
|
||||
<%=
|
||||
settings_primer_form_with(
|
||||
scope: :settings, action: :update, method: :patch,
|
||||
html: change_in_progress? ? {} : { id: form_id }
|
||||
) do |f|
|
||||
render_inline_settings_form(f) do |form|
|
||||
form.radio_button_group(
|
||||
name: :work_packages_identifier,
|
||||
label: I18n.t("settings.work_packages.work_package_identifier"),
|
||||
required: true,
|
||||
**radio_button_options
|
||||
)
|
||||
|
||||
form.html_content do
|
||||
if change_in_progress?
|
||||
render(Primer::Beta::Text.new(my: 3)) do
|
||||
render(Primer::Beta::Spinner.new(size: :small, mr: 2)).to_s +
|
||||
I18n.t("admin.settings.work_packages_identifier.in_progress.banner_message")
|
||||
end
|
||||
elsif completed?
|
||||
render(Primer::Alpha::Banner.new(scheme: :success, dismiss_scheme: :remove, mb: 3)) do
|
||||
I18n.t("admin.settings.work_packages_identifier.success_banner")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
%>
|
||||
|
||||
<% unless change_in_progress? %>
|
||||
<%= tag.div(
|
||||
hidden: !show_autofix_section?,
|
||||
data: { admin__work_packages_identifier_target: "autofixSection" }
|
||||
) do %>
|
||||
<%= render(
|
||||
WorkPackages::Admin::Settings::IdentifierAutofixSectionComponent.new(
|
||||
projects_data:,
|
||||
total_count:
|
||||
)
|
||||
) %>
|
||||
<% end %>
|
||||
<div class="op-admin-settings-form-wrapper">
|
||||
<%= render(
|
||||
Primer::Beta::Button.new(
|
||||
scheme: :primary,
|
||||
type: :submit,
|
||||
form: form_id,
|
||||
hidden: show_autofix_section?,
|
||||
data: { admin__work_packages_identifier_target: "saveButton" }
|
||||
)
|
||||
) { t("button_save") } %>
|
||||
|
||||
<%= render(
|
||||
Primer::Beta::Button.new(
|
||||
tag: :a,
|
||||
href: confirm_dialog_admin_settings_work_packages_identifier_path,
|
||||
scheme: :primary,
|
||||
hidden: !show_autofix_section?,
|
||||
data: {
|
||||
turbo_stream: true,
|
||||
admin__work_packages_identifier_target: "autofixButton"
|
||||
}
|
||||
)
|
||||
) { t("admin.settings.work_packages_identifier.button_autofix") } %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
@@ -0,0 +1,110 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) 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.
|
||||
#++
|
||||
#
|
||||
|
||||
module WorkPackages
|
||||
module Admin
|
||||
module Settings
|
||||
class IdentifierSettingsFormComponent < ApplicationComponent
|
||||
include OpPrimer::FormHelpers
|
||||
include OpTurbo::Streamable
|
||||
|
||||
STATES = %i[edit change_in_progress completed].freeze
|
||||
|
||||
attr_reader :projects_data, :total_count, :state
|
||||
|
||||
def initialize(state: :edit)
|
||||
raise ArgumentError, "Unknown state: #{state}" unless STATES.include?(state)
|
||||
|
||||
super()
|
||||
@state = state
|
||||
if state == :edit
|
||||
result = WorkPackages::IdentifierAutofix::PreviewQuery.new.call
|
||||
@projects_data = result.projects_data
|
||||
@total_count = result.total_count
|
||||
else
|
||||
@projects_data = []
|
||||
@total_count = 0
|
||||
end
|
||||
end
|
||||
|
||||
def has_problematic_projects?
|
||||
total_count > 0
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def form_id = "wp-identifier-settings-form"
|
||||
|
||||
def show_autofix_section?
|
||||
state == :edit && Setting::WorkPackageIdentifier.alphanumeric? && has_problematic_projects?
|
||||
end
|
||||
|
||||
def change_in_progress? = state == :change_in_progress
|
||||
def completed? = state == :completed
|
||||
|
||||
def wrapper_data_attrs
|
||||
if change_in_progress?
|
||||
poll_for_changes_controller_attrs
|
||||
else
|
||||
work_package_identifier_controller_attrs
|
||||
end
|
||||
end
|
||||
|
||||
def poll_for_changes_controller_attrs
|
||||
{
|
||||
data: {
|
||||
controller: "poll-for-changes",
|
||||
poll_for_changes_url_value: url_helpers.status_admin_settings_work_packages_identifier_path,
|
||||
poll_for_changes_interval_value: 5000
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def work_package_identifier_controller_attrs
|
||||
{
|
||||
data: {
|
||||
controller: "admin--work-packages-identifier",
|
||||
admin__work_packages_identifier_has_problematic_projects_value: has_problematic_projects?
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def radio_button_options
|
||||
if change_in_progress?
|
||||
{ button_options: { disabled: true } }
|
||||
else
|
||||
{ button_options: { data: { action: "change->admin--work-packages-identifier#handleChange" } } }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -96,15 +96,17 @@ module WorkPackages
|
||||
end
|
||||
|
||||
def mobile_description
|
||||
text = if @manually_scheduled
|
||||
I18n.t("work_packages.datepicker_modal.banner.description.manual_mobile")
|
||||
else
|
||||
I18n.t("work_packages.datepicker_modal.banner.description.automatic_mobile")
|
||||
end
|
||||
text =
|
||||
if @manually_scheduled
|
||||
I18n.t("work_packages.datepicker_modal.banner.description.manual_mobile")
|
||||
else
|
||||
I18n.t("work_packages.datepicker_modal.banner.description.automatic_mobile")
|
||||
end
|
||||
|
||||
"#{text} #{render(Primer::Beta::Link.new(tag: :a, href: link, target: '_blank', underline: true)) do
|
||||
I18n.t('work_packages.datepicker_modal.show_relations')
|
||||
end}".html_safe
|
||||
capture do
|
||||
concat text
|
||||
concat render(Primer::Beta::Link.new(tag: :a, href: link, target: "_blank", underline: true)) { I18n.t("work_packages.datepicker_modal.show_relations") }
|
||||
end
|
||||
end
|
||||
|
||||
def overlapping_predecessor?
|
||||
@@ -122,7 +124,7 @@ module WorkPackages
|
||||
|
||||
predecessor_work_packages.filter_map(&:due_date)
|
||||
.max
|
||||
&.before?(@work_package.start_date - 2)
|
||||
&.before?(@work_package.start_date - 2)
|
||||
end
|
||||
|
||||
def predecessor_relations
|
||||
@@ -131,8 +133,8 @@ module WorkPackages
|
||||
|
||||
def predecessor_work_packages
|
||||
@predecessor_work_packages ||= predecessor_relations
|
||||
.includes(:to)
|
||||
.map(&:to)
|
||||
.includes(:to)
|
||||
.map(&:to)
|
||||
end
|
||||
|
||||
def children
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<%= render(
|
||||
Primer::Alpha::Dialog.new(
|
||||
title: I18n.t("export.dialog.title"),
|
||||
size: :xlarge,
|
||||
size: :large,
|
||||
position: :right,
|
||||
position_narrow: :fullscreen,
|
||||
id: MODAL_ID,
|
||||
data: {
|
||||
controller: "work-packages--export--dialog"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user