From b35f04d220806a32dfb01e0b28575176d790e60b Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Wed, 15 Jul 2026 00:12:32 +0500 Subject: [PATCH 01/41] Replace Unit configuration with Nginx + PHP-FPM setup in Docker - Remove outdated Unit-based Docker configuration and scripts. - Introduce Nginx + PHP-FPM stack with environment-based configurations. - Add custom Nginx setup for frontend-backend routing. - Update Dockerfiles, Docker Compose, and related scripts for new architecture. --- .env.example | 2 +- app/docker/docker-entrypoint_dev.sh | 112 -------------------- app/docker/docker-entrypoint_prod.sh | 122 ---------------------- app/docker/{ => php}/Dockerfile | 50 +++------ app/docker/php/conf/app-fpm.conf | 26 +++++ app/docker/php/start.sh | 59 +++++++++++ app/docker/start.sh | 21 ---- app/docker/unit-config.json | 69 ------------ app/docker/web/Dockerfile | 31 ++++++ app/docker/web/Dockerfile.dockerignore | 3 + app/docker/web/conf/default.conf.template | 44 ++++++++ app/docker/web/conf/fastcgi_params | 23 ++++ app/docker/web/start.sh | 35 +++++++ docker-compose-prod.yml | 33 ++++-- docker-compose.yml | 38 +++++-- 15 files changed, 293 insertions(+), 375 deletions(-) delete mode 100644 app/docker/docker-entrypoint_dev.sh delete mode 100644 app/docker/docker-entrypoint_prod.sh rename app/docker/{ => php}/Dockerfile (61%) create mode 100644 app/docker/php/conf/app-fpm.conf create mode 100755 app/docker/php/start.sh delete mode 100755 app/docker/start.sh delete mode 100644 app/docker/unit-config.json create mode 100644 app/docker/web/Dockerfile create mode 100644 app/docker/web/Dockerfile.dockerignore create mode 100644 app/docker/web/conf/default.conf.template create mode 100644 app/docker/web/conf/fastcgi_params create mode 100644 app/docker/web/start.sh diff --git a/.env.example b/.env.example index e8286b0..a0a680a 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,4 @@ -DOCKER_APP_PORT=8080 +DOCKER_WEB_PORT=8080 DOCKER_CAPTCHA_PORT=8081 DOCKER_CAPTCHA_WEBSOCKET_PORT=8082 DOCKER_DB_PORT=3306 diff --git a/app/docker/docker-entrypoint_dev.sh b/app/docker/docker-entrypoint_dev.sh deleted file mode 100644 index 9fc4564..0000000 --- a/app/docker/docker-entrypoint_dev.sh +++ /dev/null @@ -1,112 +0,0 @@ -#!/bin/sh - -set -euo pipefail - -WAITLOOPS=5 -SLEEPSEC=1 -unitd="unitd" -role=${CONTAINER_ROLE:-app} - -curl_put() -{ - RET=$(/usr/bin/curl -s -w '%{http_code}' -X PUT --data-binary @$1 --unix-socket /var/run/control.unit.sock http://localhost/$2) - RET_BODY=$(echo $RET | /bin/sed '$ s/...$//') - RET_STATUS=$(echo $RET | /usr/bin/tail -c 4) - if [ "$RET_STATUS" -ne "200" ]; then - echo "$0: Error: HTTP response status code is '$RET_STATUS'" - echo "$RET_BODY" - return 1 - else - echo "$0: OK: HTTP response status code is '$RET_STATUS'" - echo "$RET_BODY" - fi - return 0 -} - -if [ "$role" = "app" ]; then - echo "$0: Launching Unit daemon to perform initial configuration..." - /usr/sbin/$unitd --control unix:/var/run/control.unit.sock - for i in $(/usr/bin/seq $WAITLOOPS); do - if [ ! -S /var/run/control.unit.sock ]; then - echo "$0: Waiting for control socket to be created..." - /bin/sleep $SLEEPSEC - else - break - fi - done - - # even when the control socket exists, it does not mean unit has finished initialisation - # this curl call will get a reply once unit is fully launched - /usr/bin/curl -s -X GET --unix-socket /var/run/control.unit.sock http://localhost/ - - if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -print -quit 2>/dev/null | /bin/grep -q .; then - echo "$0: /docker-entrypoint.d/ is not empty, applying initial configuration..." - - if [[ -n "${UNIT_SOURCE:-}" ]]; then - config="/docker-entrypoint.d/config.json" - tmp="$(mktemp)" - jq --arg src "${UNIT_SOURCE}" ' - .listeners["*:9000"].forwarded.source = - ( $src - | split(",") - | map( gsub("^\\s+|\\s+$"; "") ) # trim пробелы - | map( select(. != "") ) # убрать пустые - ) - ' "$config" > "$tmp" - mv "$tmp" "$config" - fi - - echo "$0: Looking for certificate bundles in /docker-entrypoint.d/..." - for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -name "*.pem"); do - echo "$0: Uploading certificates bundle: $f" - curl_put $f "certificates/$(basename $f .pem)" - done - - echo "$0: Looking for JavaScript modules in /docker-entrypoint.d/..." - for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -name "*.js"); do - echo "$0: Uploading JavaScript module: $f" - curl_put $f "js_modules/$(basename $f .js)" - done - - echo "$0: Looking for configuration snippets in /docker-entrypoint.d/..." - for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -name "*.json"); do - echo "$0: Applying configuration $f"; - curl_put $f "config" - done - - echo "$0: Looking for shell scripts in /docker-entrypoint.d/..." - for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -name "*.sh"); do - echo "$0: Launching $f"; - "$f" - done - - # warn on filetypes we don't know what to do with - for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -not -name "*.sh" -not -name "*.json" -not -name "*.pem" -not -name "*.js"); do - echo "$0: Ignoring $f"; - done - fi - - echo "$0: Stopping Unit daemon after initial configuration..." - kill -TERM $(/bin/cat /var/run/unit.pid) - - for i in $(/usr/bin/seq $WAITLOOPS); do - if [ -S /var/run/control.unit.sock ]; then - echo "$0: Waiting for control socket to be removed..." - /bin/sleep $SLEEPSEC - else - break - fi - done - if [ -S /var/run/control.unit.sock ]; then - kill -KILL $(/bin/cat /var/run/unit.pid) - rm -f /var/run/control.unit.sock - fi - - echo - echo "$0: Unit initial configuration complete; ready for start up..." - echo -fi - -chmod -R 777 /var/www/html/storage /var/www/html/bootstrap/cache - -exec "$@" diff --git a/app/docker/docker-entrypoint_prod.sh b/app/docker/docker-entrypoint_prod.sh deleted file mode 100644 index 8093a90..0000000 --- a/app/docker/docker-entrypoint_prod.sh +++ /dev/null @@ -1,122 +0,0 @@ -#!/bin/sh - -set -euo pipefail - -WAITLOOPS=5 -SLEEPSEC=1 -unitd="unitd" -role=${CONTAINER_ROLE:-app} - -curl_put() -{ - RET=$(/usr/bin/curl -s -w '%{http_code}' -X PUT --data-binary @$1 --unix-socket /var/run/control.unit.sock http://localhost/$2) - RET_BODY=$(echo $RET | /bin/sed '$ s/...$//') - RET_STATUS=$(echo $RET | /usr/bin/tail -c 4) - if [ "$RET_STATUS" -ne "200" ]; then - echo "$0: Error: HTTP response status code is '$RET_STATUS'" - echo "$RET_BODY" - return 1 - else - echo "$0: OK: HTTP response status code is '$RET_STATUS'" - echo "$RET_BODY" - fi - return 0 -} - -if [ "$role" = "app" ]; then - echo "$0: Launching Unit daemon to perform initial configuration..." - /usr/sbin/$unitd --control unix:/var/run/control.unit.sock - for i in $(/usr/bin/seq $WAITLOOPS); do - if [ ! -S /var/run/control.unit.sock ]; then - echo "$0: Waiting for control socket to be created..." - /bin/sleep $SLEEPSEC - else - break - fi - done - - # even when the control socket exists, it does not mean unit has finished initialisation - # this curl call will get a reply once unit is fully launched - /usr/bin/curl -s -X GET --unix-socket /var/run/control.unit.sock http://localhost/ - - if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -print -quit 2>/dev/null | /bin/grep -q .; then - echo "$0: /docker-entrypoint.d/ is not empty, applying initial configuration..." - - if [[ -n "${UNIT_SOURCE:-}" ]]; then - config="/docker-entrypoint.d/config.json" - tmp="$(mktemp)" - jq --arg src "${UNIT_SOURCE}" ' - .listeners["*:9000"].forwarded.source = - ( $src - | split(",") - | map( gsub("^\\s+|\\s+$"; "") ) # trim пробелы - | map( select(. != "") ) # убрать пустые - ) - ' "$config" > "$tmp" - mv "$tmp" "$config" - fi - - echo "$0: Looking for certificate bundles in /docker-entrypoint.d/..." - for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -name "*.pem"); do - echo "$0: Uploading certificates bundle: $f" - curl_put $f "certificates/$(basename $f .pem)" - done - - echo "$0: Looking for JavaScript modules in /docker-entrypoint.d/..." - for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -name "*.js"); do - echo "$0: Uploading JavaScript module: $f" - curl_put $f "js_modules/$(basename $f .js)" - done - - echo "$0: Looking for configuration snippets in /docker-entrypoint.d/..." - for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -name "*.json"); do - echo "$0: Applying configuration $f"; - curl_put $f "config" - done - - echo "$0: Looking for shell scripts in /docker-entrypoint.d/..." - for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -name "*.sh"); do - echo "$0: Launching $f"; - "$f" - done - - # warn on filetypes we don't know what to do with - for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -not -name "*.sh" -not -name "*.json" -not -name "*.pem" -not -name "*.js"); do - echo "$0: Ignoring $f"; - done - fi - - echo "$0: Stopping Unit daemon after initial configuration..." - kill -TERM $(/bin/cat /var/run/unit.pid) - - for i in $(/usr/bin/seq $WAITLOOPS); do - if [ -S /var/run/control.unit.sock ]; then - echo "$0: Waiting for control socket to be removed..." - /bin/sleep $SLEEPSEC - else - break - fi - done - if [ -S /var/run/control.unit.sock ]; then - kill -KILL $(/bin/cat /var/run/unit.pid) - rm -f /var/run/control.unit.sock - fi - - echo - echo "$0: Unit initial configuration complete; ready for start up..." - echo -fi - -php artisan config:cache -php artisan event:cache -php artisan route:cache -php artisan view:cache -php artisan storage:link -if [ "$role" = "app" ]; then - php artisan migrate --force -fi -chown -R unit:unit /var/www/html -chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache -chmod -R 777 /var/www/html/storage /var/www/html/bootstrap/cache - -exec "$@" diff --git a/app/docker/Dockerfile b/app/docker/php/Dockerfile similarity index 61% rename from app/docker/Dockerfile rename to app/docker/php/Dockerfile index fd1d63f..61efbcd 100644 --- a/app/docker/Dockerfile +++ b/app/docker/php/Dockerfile @@ -1,23 +1,4 @@ -FROM docker.io/php:8.3-zts-alpine3.18 AS unit_builder - -ARG UNIT_VERSION=1.31.1 - -RUN apk --no-cache add pcre2-dev gcc git musl-dev make && \ - mkdir -p /usr/lib/unit/modules && \ - git clone https://github.com/nginx/unit.git && \ - cd unit && \ - git checkout $UNIT_VERSION && \ - ./configure --prefix=/var --statedir=/var/lib/unit --runstatedir=/var/run --control=unix:/run/unit/control.unit.sock --log=/var/log/unit.log --user=www-data --group=www-data --tmpdir=/tmp --modulesdir=/var/lib/unit/modules && \ - ./configure php && \ - make && \ - make install - -FROM docker.io/php:8.3-zts-alpine3.18 AS build - -COPY --from=unit_builder /var/sbin/unitd /usr/sbin/unitd -COPY --from=unit_builder /var/lib/unit/ /var/lib/unit/ - -COPY docker/unit-config.json /docker-entrypoint.d/config.json +FROM docker.io/php:8.5-fpm-alpine3.23 AS build RUN apk --no-cache add pcre2 libbz2 libpng libwebp libjpeg-turbo icu-libs freetype oniguruma libzip jq \ && apk add --no-cache --virtual .phpize-deps icu-dev libpng-dev bzip2-dev libwebp-dev libjpeg-turbo-dev freetype-dev oniguruma-dev libzip-dev pcre2-dev ${PHPIZE_DEPS} \ @@ -30,7 +11,7 @@ RUN apk --no-cache add pcre2 libbz2 libpng libwebp libjpeg-turbo icu-libs freety mysqli pdo_mysql \ intl mbstring \ zip pcntl \ - exif opcache bz2 \ + exif bz2 \ calendar \ && pear update-channels && pecl update-channels \ && pecl install redis && docker-php-ext-enable redis \ @@ -44,36 +25,35 @@ RUN apk --no-cache add pcre2 libbz2 libpng libwebp libjpeg-turbo icu-libs freety && mkdir -p /tmp/php/upload \ && mkdir -p /tmp/php/sys \ && mkdir -p /tmp/php/session \ - && chown -R www-data:www-data /tmp/php \ - && ln -sf /dev/stdout /var/log/unit.log \ - && addgroup -S unit && adduser -S unit -G unit + && chown -R www-data:www-data /tmp/php + +COPY docker/php/conf/app-fpm.conf /usr/local/etc/php-fpm.d/app-fpm.conf FROM build AS app_build_for_production WORKDIR /home/app COPY application /home/app -RUN apk --no-cache add git nodejs npm \ +RUN apk --no-cache add git \ && curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \ && composer install --optimize-autoloader --no-dev \ - && npm install && npm run build \ - && rm -rf /home/app/node_modules /home/app/.env + && rm -rf /home/app/.env FROM build AS production COPY --from=app_build_for_production /home/app /var/www/html -COPY docker/docker-entrypoint_prod.sh /home/unit/docker-entrypoint.sh -COPY docker/start.sh /usr/local/bin/start +COPY docker/php/start.sh /usr/local/bin/start WORKDIR /var/www/html -RUN chmod 755 /home/unit/docker-entrypoint.sh \ +RUN chown -R www-data:www-data /var/www/html \ && chmod 755 /usr/local/bin/start STOPSIGNAL SIGTERM -ENTRYPOINT ["/home/unit/docker-entrypoint.sh"] +USER www-data + EXPOSE 9000 CMD ["/usr/local/bin/start"] @@ -82,17 +62,15 @@ FROM build AS develop WORKDIR /var/www/html -COPY docker/docker-entrypoint_dev.sh /home/unit/docker-entrypoint.sh -COPY docker/start.sh /usr/local/bin/start +COPY docker/php/start.sh /usr/local/bin/start STOPSIGNAL SIGTERM -RUN chmod 755 /home/unit/docker-entrypoint.sh \ - && chmod 755 /usr/local/bin/start \ +RUN chmod 755 /usr/local/bin/start \ && apk --no-cache add git nodejs npm \ && curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer -ENTRYPOINT ["/home/unit/docker-entrypoint.sh"] +USER www-data EXPOSE 9000 CMD ["/usr/local/bin/start"] diff --git a/app/docker/php/conf/app-fpm.conf b/app/docker/php/conf/app-fpm.conf new file mode 100644 index 0000000..0c37c38 --- /dev/null +++ b/app/docker/php/conf/app-fpm.conf @@ -0,0 +1,26 @@ +[www] +php_admin_value[upload_tmp_dir] = "/tmp/php/upload" +php_admin_value[sys_temp_dir] = "/tmp/php/sys" +php_admin_value[session.save_path] = "/tmp/php/session" +php_admin_value[open_basedir] = "/var/www/html:/tmp/php:." +php_admin_value[expose_php] = 0 +php_admin_value[memory_limit] = ${PHP_FPM_MEMORY_LIMIT} +php_admin_value[upload_max_filesize] = ${PHP_FPM_UPLOAD_MAX_FILESIZE} +php_admin_value[post_max_size] = ${PHP_FPM_POST_MAX_SIZE} +php_admin_value[display_errors] = ${PHP_FPM_DISPLAY_ERRORS} +php_admin_value[opcache.enable] = ${PHP_FPM_OPCACHE_ENABLE} +php_admin_value[opcache.validate_timestamps] = ${PHP_FPM_OPCACHE_VALIDATE_TIMESTAMPS} + +pm = dynamic +pm.max_children = ${PHP_FPM_PM_MAX_CHILDREN} +pm.start_servers = ${PHP_FPM_PM_START_SERVERS} +pm.min_spare_servers = ${PHP_FPM_PM_MIN_SPARE_SERVERS} +pm.max_spare_servers = ${PHP_FPM_PM_MAX_SPARE_SERVERS} +pm.max_requests = ${PHP_FPM_PM_MAX_REQUESTS} + + +listen = 127.0.0.1:9000 +listen.mode = 0660 +listen.owner = www-data +listen.group = www-data +security.limit_extensions = .php diff --git a/app/docker/php/start.sh b/app/docker/php/start.sh new file mode 100755 index 0000000..08f6b0d --- /dev/null +++ b/app/docker/php/start.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env sh +set -e +role=${CONTAINER_ROLE:-app} + +if [ "$APP_ENV" = "production" ]; then + php artisan config:cache + php artisan event:cache + php artisan route:cache + php artisan view:cache + php artisan storage:link +fi + +if [ "$role" = "app" ]; then + + export PHP_FPM_MEMORY_LIMIT="${PHP_FPM_MEMORY_LIMIT:-256m}" + export PHP_FPM_UPLOAD_MAX_FILESIZE="${PHP_FPM_UPLOAD_MAX_FILESIZE:-20m}" + export PHP_FPM_POST_MAX_SIZE="${PHP_FPM_POST_MAX_SIZE:-20m}" + + export PHP_FPM_PM_MAX_CHILDREN="${PHP_FPM_PM_MAX_CHILDREN:-10}" + export PHP_FPM_PM_START_SERVERS="${PHP_FPM_PM_START_SERVERS:-5}" + export PHP_FPM_PM_MIN_SPARE_SERVERS="${PHP_FPM_PM_MIN_SPARE_SERVERS:-2}" + export PHP_FPM_PM_MAX_SPARE_SERVERS="${PHP_FPM_PM_MAX_SPARE_SERVERS:-5}" + export PHP_FPM_PM_MAX_REQUESTS="${PHP_FPM_PM_MAX_REQUESTS:-500}" + + if [ "$APP_ENV" = "production" ]; then + export PHP_FPM_DISPLAY_ERRORS="${PHP_FPM_DISPLAY_ERRORS:-0}" + export PHP_FPM_OPCACHE_ENABLE="${PHP_FPM_OPCACHE_ENABLE:-1}" + export PHP_FPM_OPCACHE_VALIDATE_TIMESTAMPS="${PHP_FPM_OPCACHE_VALIDATE_TIMESTAMPS:-0}" + + php artisan migrate --force + else + export PHP_FPM_DISPLAY_ERRORS="${PHP_FPM_DISPLAY_ERRORS:-1}" + export PHP_FPM_OPCACHE_ENABLE="${PHP_FPM_OPCACHE_ENABLE:-0}" + export PHP_FPM_OPCACHE_VALIDATE_TIMESTAMPS="${PHP_FPM_OPCACHE_VALIDATE_TIMESTAMPS:-1}" + fi + + if [ ! -w /var/www/html/storage ]; then + echo "ERROR: /var/www/html/storage must be writable." + echo "Fix permissions on host/volume for the www-data user." + exit 1 + fi + + exec php-fpm +elif [ "$role" = "queue" ]; then + echo "Running the queue..." + while [ true ] + do + php /var/www/html/artisan queue:work --verbose --sleep=5 --tries=100 --backoff=10 --max-time=3600 --queue=high,normal,low,default + done +elif [ "$role" = "scheduler" ]; then + while [ true ] + do + php /var/www/html/artisan schedule:run --verbose --no-interaction & + sleep 60 + done +else + echo "Could not match the container role \"$role\"" + exit 1 +fi diff --git a/app/docker/start.sh b/app/docker/start.sh deleted file mode 100755 index 691e23a..0000000 --- a/app/docker/start.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env sh -set -e -role=${CONTAINER_ROLE:-app} -if [ "$role" = "app" ]; then - exec unitd --no-daemon --control unix:/var/run/control.unit.sock --user unit --group unit -elif [ "$role" = "queue" ]; then - echo "Running the queue..." - while [ true ] - do - php /var/www/html/artisan queue:work --verbose --sleep=5 --tries=100 --backoff=10 --max-time=3600 --queue=high,normal,low,default - done -elif [ "$role" = "scheduler" ]; then - while [ true ] - do - php /var/www/html/artisan schedule:run --verbose --no-interaction & - sleep 60 - done -else - echo "Could not match the container role \"$role\"" - exit 1 -fi diff --git a/app/docker/unit-config.json b/app/docker/unit-config.json deleted file mode 100644 index 1b73dff..0000000 --- a/app/docker/unit-config.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "listeners": { - "*:9000": { - "pass": "routes", - "forwarded": { - "client_ip": "X-Forwarded-For", - "recursive": false, - "source": [ - - ] - } - } - }, - - "routes": [ - { - "match": { - "uri": [ - "/index.php/", - "~^/index\\.php/.*", - "~\\.php$" - ] - }, - "action": { - "return": 404 - } - }, - { - "action": { - "share": "/var/www/html/public$uri", - "fallback": { - "pass": "applications/laravel" - } - } - } - ], - - "applications": { - "laravel": { - "type": "php", - "root": "/var/www/html/public", - "working_directory": "/var/www/html", - "user": "www-data", - "group": "www-data", - "script": "index.php", - "processes": { - "max": 10, - "spare": 5, - "idle_timeout": 20 - }, - "options": { - "file": "/usr/local/etc/php/php.ini", - "admin": { - "upload_tmp_dir": "/tmp/php/upload", - "sys_temp_dir": "/tmp/php/sys", - "session.save_path": "/tmp/php/session", - "open_basedir": "/var/www/html:/tmp/php:.", - "memory_limit": "256M", - "upload_max_filesize": "20M", - "post_max_size": "20M", - "expose_php": "0" - }, - "user": { - "display_errors": "0" - } - } - } - } -} diff --git a/app/docker/web/Dockerfile b/app/docker/web/Dockerfile new file mode 100644 index 0000000..8c55a71 --- /dev/null +++ b/app/docker/web/Dockerfile @@ -0,0 +1,31 @@ +FROM docker.io/nginx:1.31-alpine AS build + +COPY docker/web/conf/default.conf.template /etc/nginx/templates/default.conf.template +COPY docker/web/conf/fastcgi_params /etc/nginx/fastcgi_params +COPY docker/web/start.sh /usr/local/bin/start + +RUN chmod 755 /usr/local/bin/start + +FROM build AS app_build_for_production + +WORKDIR /home/app +COPY application /home/app + +RUN apk --no-cache add git nodejs npm \ + && npm install && npm run build \ + && rm -rf /home/app/node_modules + +FROM build AS production + +COPY --from=app_build_for_production /home/app/public /usr/share/nginx/html + +WORKDIR /usr/share/nginx/html + +EXPOSE 80 +CMD ["/usr/local/bin/start"] + +FROM build AS develop +WORKDIR /usr/share/nginx/html + +EXPOSE 80 +CMD ["/usr/local/bin/start"] diff --git a/app/docker/web/Dockerfile.dockerignore b/app/docker/web/Dockerfile.dockerignore new file mode 100644 index 0000000..1e5f2e7 --- /dev/null +++ b/app/docker/web/Dockerfile.dockerignore @@ -0,0 +1,3 @@ +application/public/*.php +application/public/**/*.php +application/public/.htaccess \ No newline at end of file diff --git a/app/docker/web/conf/default.conf.template b/app/docker/web/conf/default.conf.template new file mode 100644 index 0000000..5942f95 --- /dev/null +++ b/app/docker/web/conf/default.conf.template @@ -0,0 +1,44 @@ +map $FRONTEND_PORT $host_with_port { + "" $host; + default "$host:$FRONTEND_PORT"; +} + +server { + listen 80 default_server; + listen [::]:80 default_server; + + client_max_body_size ${NGINX_CLIENT_MAX_BODY_SIZE}; + + disable_symlinks if_not_owner from=/usr/share/nginx/html; + + index index.php index.html; + root /usr/share/nginx/html; + + ${GZIP_BLOCK} + ${REAL_IP_BLOCK} + + location / { + location / { + try_files $uri @fallback; + } + location ~ \.php$ { + return 404; + } + location ~ /\. { + return 404; + } + location ~ /\.(ht|svn|git) { + return 404; + } + } + + location @fallback { + fastcgi_pass ${BACKEND_HOST}:${BACKEND_PORT}; + fastcgi_index index.php; + fastcgi_param REDIRECT_STATUS 200; + fastcgi_param SCRIPT_FILENAME /var/www/html/public/index.php; + fastcgi_param SCRIPT_NAME /index.php; + fastcgi_param HTTP_HOST $host_with_port; + include /etc/nginx/fastcgi_params; + } +} \ No newline at end of file diff --git a/app/docker/web/conf/fastcgi_params b/app/docker/web/conf/fastcgi_params new file mode 100644 index 0000000..a0dffef --- /dev/null +++ b/app/docker/web/conf/fastcgi_params @@ -0,0 +1,23 @@ +fastcgi_param QUERY_STRING $query_string; +fastcgi_param REQUEST_METHOD $request_method; +fastcgi_param CONTENT_TYPE $content_type; +fastcgi_param CONTENT_LENGTH $content_length; + +fastcgi_param SCRIPT_NAME $fastcgi_script_name; +fastcgi_param REQUEST_URI $request_uri; +fastcgi_param DOCUMENT_URI $document_uri; +fastcgi_param DOCUMENT_ROOT $document_root; +fastcgi_param SERVER_PROTOCOL $server_protocol; +fastcgi_param HTTPS $https if_not_empty; + +fastcgi_param GATEWAY_INTERFACE CGI/1.1; +fastcgi_param SERVER_SOFTWARE nginx; + +fastcgi_param REMOTE_ADDR $remote_addr; +fastcgi_param REMOTE_PORT $remote_port; +fastcgi_param SERVER_ADDR $server_addr; +fastcgi_param SERVER_PORT $server_port; +fastcgi_param SERVER_NAME $server_name; + +fastcgi_param HTTP_X_FORWARDED_FOR $proxy_add_x_forwarded_for; +fastcgi_param HTTP_X_REAL_IP $remote_addr; diff --git a/app/docker/web/start.sh b/app/docker/web/start.sh new file mode 100644 index 0000000..5d08752 --- /dev/null +++ b/app/docker/web/start.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env sh + +if [ "$IS_GZIP" = "1" ]; then + GZIP_BLOCK='gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_buffers 16 8k; + gzip_http_version 1.1; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; + gzip_min_length 1024;' +else + GZIP_BLOCK='' +fi +export GZIP_BLOCK + +if [ -n "$TRUSTED_PROXY_CIDR" ]; then + REAL_IP_BLOCK="set_real_ip_from $TRUSTED_PROXY_CIDR; + real_ip_header X-Forwarded-For; + real_ip_recursive on;" +else + REAL_IP_BLOCK="" +fi +export REAL_IP_BLOCK + +export BACKEND_HOST="${BACKEND_HOST:-app}" +export BACKEND_PORT="${BACKEND_PORT:-9000}" +export FRONTEND_PORT="${FRONTEND_PORT:-''}" +export NGINX_CLIENT_MAX_BODY_SIZE="${NGINX_CLIENT_MAX_BODY_SIZE:-'20m'}" + +envsubst '$BACKEND_HOST $BACKEND_PORT $GZIP_BLOCK $REAL_IP_BLOCK $FRONTEND_PORT $NGINX_CLIENT_MAX_BODY_SIZE' \ + < /etc/nginx/templates/default.conf.template \ + > /etc/nginx/conf.d/default.conf + +exec nginx -g 'daemon off;' diff --git a/docker-compose-prod.yml b/docker-compose-prod.yml index 575e94f..958f4df 100644 --- a/docker-compose-prod.yml +++ b/docker-compose-prod.yml @@ -1,30 +1,47 @@ #version: '3.7' services: + web: + build: + context: app + dockerfile: docker/web/Dockerfile + target: DEVELOP + depends_on: + - app + ports: + - ${DOCKER_WEB_PORT}:80 + environment: + IS_GZIP: 1 + # FRONTEND_PORT: ${DOCKER_WEB_PORT} + BACKEND_HOST: app + BACKEND_PORT: 9000 + TRUSTED_PROXY_CIDR: 172.16.0.0/12 + volumes: + - ./app/application/public:/usr/share/nginx/html + - ./app/application/storage/app/public:/usr/share/nginx/html/storage app: build: context: app - dockerfile: docker/Dockerfile + dockerfile: docker/php/Dockerfile target: PRODUCTION # restart: always depends_on: - db - app-redis - captcha-app - ports: - - ${DOCKER_APP_PORT}:9000 volumes: - ./app/application:/var/www/html environment: CONTAINER_ROLE: app - UNIT_SOURCE: "172.16.0.0/12" + APP_ENV: production queue: build: context: app - dockerfile: docker/Dockerfile + dockerfile: docker/php/Dockerfile target: PRODUCTION # restart: always depends_on: + - app - db - app-redis environment: @@ -35,10 +52,11 @@ services: scheduler: build: context: app - dockerfile: docker/Dockerfile + dockerfile: docker/php/Dockerfile target: PRODUCTION # restart: always depends_on: + - app - db - app-redis environment: @@ -75,6 +93,7 @@ services: image: korelf/service-captcha:0.8.2 # restart: always depends_on: + - captcha-app - db - captcha-redis environment: @@ -84,6 +103,7 @@ services: image: korelf/service-captcha:0.8.2 # restart: always depends_on: + - captcha-app - db - captcha-redis environment: @@ -95,6 +115,7 @@ services: image: korelf/service-captcha:0.8.2 # restart: always depends_on: + - captcha-app - db - captcha-redis environment: diff --git a/docker-compose.yml b/docker-compose.yml index 570c3f2..b756861 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,28 +1,46 @@ #version: '3.7' services: + web: + build: + context: app + dockerfile: docker/web/Dockerfile + target: DEVELOP + depends_on: + - app + ports: + - ${DOCKER_WEB_PORT}:80 + environment: + IS_GZIP: 0 + FRONTEND_PORT: ${DOCKER_WEB_PORT} + BACKEND_HOST: app + BACKEND_PORT: 9000 +# TRUSTED_PROXY_CIDR: 172.16.0.0/12 + volumes: + - ./app/application/public:/usr/share/nginx/html + - ./app/application/storage/app/public:/usr/share/nginx/html/storage app: build: context: app - dockerfile: docker/Dockerfile + dockerfile: docker/php/Dockerfile target: DEVELOP depends_on: - db - app-redis - captcha-app - ports: - - ${DOCKER_APP_PORT}:9000 volumes: - ./app/application:/var/www/html environment: CONTAINER_ROLE: app + APP_ENV: local queue: build: context: app - dockerfile: docker/Dockerfile + dockerfile: docker/php/Dockerfile target: DEVELOP depends_on: - db + - app - app-redis environment: CONTAINER_ROLE: queue @@ -32,10 +50,11 @@ services: scheduler: build: context: app - dockerfile: docker/Dockerfile + dockerfile: docker/php/Dockerfile target: DEVELOP depends_on: - db + - app - app-redis environment: CONTAINER_ROLE: scheduler @@ -69,6 +88,7 @@ services: image: korelf/service-captcha:0.8.2 # restart: always depends_on: + - captcha-app - db - captcha-redis environment: @@ -81,6 +101,7 @@ services: image: korelf/service-captcha:0.8.2 # restart: always depends_on: + - captcha-app - db - captcha-redis environment: @@ -95,6 +116,7 @@ services: image: korelf/service-captcha:0.8.2 # restart: always depends_on: + - captcha-app - db - captcha-redis environment: @@ -124,7 +146,7 @@ services: artisan: build: context: app - dockerfile: docker/Dockerfile + dockerfile: docker/php/Dockerfile target: ARTISAN user: "${UID}:${GID}" volumes: @@ -133,7 +155,7 @@ services: composer: build: context: app - dockerfile: docker/Dockerfile + dockerfile: docker/php/Dockerfile target: COMPOSER user: "${UID}:${GID}" volumes: @@ -142,7 +164,7 @@ services: npm: build: context: app - dockerfile: docker/Dockerfile + dockerfile: docker/php/Dockerfile target: NPM user: "${UID}:${GID}" volumes: From b5258f840e8f443ef5e6865f98b142d185c44c49 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Wed, 15 Jul 2026 22:35:55 +0500 Subject: [PATCH 02/41] Replace `$request->get()` with `$request->attributes->get()` throughout controllers and middleware for consistency and enhanced reliability. --- .../Site/DocumentationController.php | 22 +++++++++---------- .../Controllers/Site/FeedbackController.php | 8 +++---- .../Controllers/Site/ProjectsController.php | 4 ++-- .../Http/Middleware/DocumentationVersion.php | 2 +- .../app/Http/Middleware/IsProject.php | 2 +- .../Http/Middleware/IsWebsiteTranslations.php | 2 +- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/app/application/app/Http/Controllers/Site/DocumentationController.php b/app/application/app/Http/Controllers/Site/DocumentationController.php index feb935e..00c16b8 100644 --- a/app/application/app/Http/Controllers/Site/DocumentationController.php +++ b/app/application/app/Http/Controllers/Site/DocumentationController.php @@ -16,8 +16,8 @@ final class DocumentationController extends Controller public function defaultVersion(Request $request): RedirectResponse | View { - $project = $request->get('project'); - $websiteTranslations = $request->get('websiteTranslations'); + $project = $request->attributes->get('project'); + $websiteTranslations = $request->attributes->get('websiteTranslations'); $result = $this->documentationService->defaultVersion($project, $request->user()); if ($result->isError()) { @@ -40,9 +40,9 @@ final class DocumentationController extends Controller public function index(Request $request): View { $documentation = new Documentation( - project: $request->get('project'), - version: $request->get('version'), - websiteTranslations: $request->get('websiteTranslations'), + project: $request->attributes->get('project'), + version: $request->attributes->get('version'), + websiteTranslations: $request->attributes->get('websiteTranslations'), user: $request->user(), ); $result = $this->documentationService->index($documentation); @@ -57,9 +57,9 @@ final class DocumentationController extends Controller public function category(string $slug, Request $request): View { $documentation = new Documentation( - project: $request->get('project'), - version: $request->get('version'), - websiteTranslations: $request->get('websiteTranslations'), + project: $request->attributes->get('project'), + version: $request->attributes->get('version'), + websiteTranslations: $request->attributes->get('websiteTranslations'), user: $request->user(), ); $result = $this->documentationService->category($slug, $documentation); @@ -77,9 +77,9 @@ final class DocumentationController extends Controller public function view(string $slug, Request $request): View { $documentation = new Documentation( - project: $request->get('project'), - version: $request->get('version'), - websiteTranslations: $request->get('websiteTranslations'), + project: $request->attributes->get('project'), + version: $request->attributes->get('version'), + websiteTranslations: $request->attributes->get('websiteTranslations'), user: $request->user(), ); $result = $this->documentationService->view($slug, $documentation); diff --git a/app/application/app/Http/Controllers/Site/FeedbackController.php b/app/application/app/Http/Controllers/Site/FeedbackController.php index dec9747..7344e73 100644 --- a/app/application/app/Http/Controllers/Site/FeedbackController.php +++ b/app/application/app/Http/Controllers/Site/FeedbackController.php @@ -17,16 +17,16 @@ final class FeedbackController extends Controller public function index(Request $request): View { return view('site.feedback.index', [ - 'project' => $request->get('project'), - 'websiteTranslations' => $request->get('websiteTranslations'), + 'project' => $request->attributes->get('project'), + 'websiteTranslations' => $request->attributes->get('websiteTranslations'), 'captcha' => config('app.captcha', false), ]); } public function send(SendRequest $request): RedirectResponse { - $project = $request->get('project'); - $websiteTranslations = $request->get('websiteTranslations'); + $project = $request->attributes->get('project'); + $websiteTranslations = $request->attributes->get('websiteTranslations'); $user = $request->user(); $data = $request->getDto(); diff --git a/app/application/app/Http/Controllers/Site/ProjectsController.php b/app/application/app/Http/Controllers/Site/ProjectsController.php index 8db8737..1209029 100644 --- a/app/application/app/Http/Controllers/Site/ProjectsController.php +++ b/app/application/app/Http/Controllers/Site/ProjectsController.php @@ -15,7 +15,7 @@ final class ProjectsController extends Controller public function index(Request $request): View { $user = $request->user(); - $project = $request->get('project'); + $project = $request->attributes->get('project'); if (\is_null($project)) { $with = ['storage']; @@ -27,7 +27,7 @@ final class ProjectsController extends Controller return \view('site.projects.index', $result->getData()); } - $websiteTranslations = $request->get('websiteTranslations'); + $websiteTranslations = $request->attributes->get('websiteTranslations'); $result = $this->projectService->getAboutByProject($project, $websiteTranslations, $user); if ($result->isError()) { $this->errors($result); diff --git a/app/application/app/Http/Middleware/DocumentationVersion.php b/app/application/app/Http/Middleware/DocumentationVersion.php index 8f951dc..2c37a2a 100644 --- a/app/application/app/Http/Middleware/DocumentationVersion.php +++ b/app/application/app/Http/Middleware/DocumentationVersion.php @@ -10,7 +10,7 @@ final readonly class DocumentationVersion { public function handle(Request $request, \Closure $next): Response { - $project = $request->get('project'); + $project = $request->attributes->get('project'); $versionSlug = $request->route()?->parameter('version'); if ($versionSlug === null || $project === null) { \abort(Response::HTTP_NOT_FOUND); diff --git a/app/application/app/Http/Middleware/IsProject.php b/app/application/app/Http/Middleware/IsProject.php index 8b6ca5b..f745296 100644 --- a/app/application/app/Http/Middleware/IsProject.php +++ b/app/application/app/Http/Middleware/IsProject.php @@ -10,7 +10,7 @@ final class IsProject { public function handle(Request $request, \Closure $next): Response { - $project = $request->get('project'); + $project = $request->attributes->get('project'); if (\is_null($project)) { \abort(Response::HTTP_NOT_FOUND); } diff --git a/app/application/app/Http/Middleware/IsWebsiteTranslations.php b/app/application/app/Http/Middleware/IsWebsiteTranslations.php index 261af69..0dac065 100644 --- a/app/application/app/Http/Middleware/IsWebsiteTranslations.php +++ b/app/application/app/Http/Middleware/IsWebsiteTranslations.php @@ -10,7 +10,7 @@ final class IsWebsiteTranslations { public function handle(Request $request, \Closure $next): Response { - $websiteTranslations = $request->get('websiteTranslations'); + $websiteTranslations = $request->attributes->get('websiteTranslations'); if (\is_null($websiteTranslations)) { \abort(Response::HTTP_NOT_FOUND); } From 9356b06ef50415f7b4623b7cfcf708b144c42ca9 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Wed, 15 Jul 2026 23:00:43 +0500 Subject: [PATCH 03/41] Set default height of TinyMCE textarea to 600px in WYSIWYG component. --- .../views/components/volt/forms/textarea-wysiwyg.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/application/resources/views/components/volt/forms/textarea-wysiwyg.blade.php b/app/application/resources/views/components/volt/forms/textarea-wysiwyg.blade.php index b63bddf..6d83d70 100644 --- a/app/application/resources/views/components/volt/forms/textarea-wysiwyg.blade.php +++ b/app/application/resources/views/components/volt/forms/textarea-wysiwyg.blade.php @@ -12,7 +12,7 @@ @endphp
- + @foreach($images as $image) @continue( empty($image['file']) ) From 756cafdbff47a6f1d41ba4faa5496b59f395f121 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Wed, 15 Jul 2026 23:40:25 +0500 Subject: [PATCH 04/41] Remove `SCRIPT_NAME` from FastCGI params and update Nginx default index to prioritize `index.html`. --- app/docker/web/conf/default.conf.template | 2 +- app/docker/web/conf/fastcgi_params | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/docker/web/conf/default.conf.template b/app/docker/web/conf/default.conf.template index 5942f95..19660a4 100644 --- a/app/docker/web/conf/default.conf.template +++ b/app/docker/web/conf/default.conf.template @@ -11,7 +11,7 @@ server { disable_symlinks if_not_owner from=/usr/share/nginx/html; - index index.php index.html; + index index.html; root /usr/share/nginx/html; ${GZIP_BLOCK} diff --git a/app/docker/web/conf/fastcgi_params b/app/docker/web/conf/fastcgi_params index a0dffef..0baa7ca 100644 --- a/app/docker/web/conf/fastcgi_params +++ b/app/docker/web/conf/fastcgi_params @@ -3,7 +3,6 @@ fastcgi_param REQUEST_METHOD $request_method; fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length; -fastcgi_param SCRIPT_NAME $fastcgi_script_name; fastcgi_param REQUEST_URI $request_uri; fastcgi_param DOCUMENT_URI $document_uri; fastcgi_param DOCUMENT_ROOT $document_root; From d3a84bad04b941851d75d58d0f11e683198e2294 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Wed, 22 Jul 2026 01:01:53 +0500 Subject: [PATCH 05/41] Add category-specific documentation view and management - Introduce routes and controller actions for displaying and managing documentation by category. - Adjust views to incorporate category-specific information and actions. - Update services to handle category retrieval and validation. - Add localized strings for new functionality in English and Russian. --- .../DocumentationVersionController.php | 4 +- .../Project/DocumentationVersionService.php | 51 +++++- app/application/lang/en.json | 3 + app/application/lang/en/admin-sections.php | 2 + app/application/lang/ru.json | 3 + app/application/lang/ru/admin-sections.php | 2 + .../documentation-versions/_top.blade.php | 30 +++- .../documentation-versions/show.blade.php | 163 +++++++++++++++--- app/application/routes/web.php | 1 + 9 files changed, 226 insertions(+), 33 deletions(-) diff --git a/app/application/app/Http/Controllers/Admin/Projects/DocumentationVersionController.php b/app/application/app/Http/Controllers/Admin/Projects/DocumentationVersionController.php index 36f1374..492435c 100644 --- a/app/application/app/Http/Controllers/Admin/Projects/DocumentationVersionController.php +++ b/app/application/app/Http/Controllers/Admin/Projects/DocumentationVersionController.php @@ -35,10 +35,10 @@ final class DocumentationVersionController extends Controller return view('admin/projects/documentation-versions/index', $result->getData()); } - public function show(int $projectId, int $id, Request $request): View + public function show(int $projectId, int $id, Request $request, ?int $category = null): View { $user = $request->user(); - $result = $this->documentationVersionService->show($projectId, $id, $user); + $result = $this->documentationVersionService->show($projectId, $id, $category, $user); if ($result->isError()) { $this->errors($result); } diff --git a/app/application/app/Services/Admin/Project/DocumentationVersionService.php b/app/application/app/Services/Admin/Project/DocumentationVersionService.php index 768bbc8..b208461 100644 --- a/app/application/app/Services/Admin/Project/DocumentationVersionService.php +++ b/app/application/app/Services/Admin/Project/DocumentationVersionService.php @@ -2,12 +2,17 @@ namespace App\Services\Admin\Project; +use App\Dto\Builder\Documentation as DocumentationBuilderDto; +use App\Dto\Builder\DocumentationCategory as DocumentationCategoryBuilderDto; use App\Dto\QuerySettingsDto; use App\Dto\Builder\DocumentationVersion as DocumentationVersionBuilderDto; use App\Dto\Service\Admin\Project\DocumentationVersion\StoreUpdate; use App\Enums\DocumentationVersionStatus; use App\Models\DocumentationVersion; +use App\Models\ProjectLanguage; use App\Models\User; +use App\Repositories\DocumentationCategoryRepository; +use App\Repositories\DocumentationRepository; use App\Repositories\DocumentationVersionRepository; use App\Repositories\ProjectRepository; use App\ServiceResults\ServiceResultArray; @@ -17,6 +22,8 @@ use App\ServiceResults\StoreUpdateResult; use App\Services\ClearCacheCommandHandler; use App\Services\DocumentationVersion\DocumentationVersionCommandHandler; use App\Services\Service; +use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Support\Facades\DB; final class DocumentationVersionService extends Service @@ -26,6 +33,9 @@ final class DocumentationVersionService extends Service private readonly DocumentationVersionRepository $documentationVersionRepository, private readonly DocumentationVersionCommandHandler $documentationVersionCommandHandler, private readonly ClearCacheCommandHandler $clearCacheCommandHandler, + + private readonly DocumentationCategoryRepository $documentationCategoryRepository, + private readonly DocumentationRepository $documentationRepository ) { } public function index(int $projectId, DocumentationVersionBuilderDto $documentationVersionBuilderDto, QuerySettingsDto $querySettingsDto, User $user): ServiceResultError | ServiceResultArray @@ -54,7 +64,7 @@ final class DocumentationVersionService extends Service ]); } - public function show(int $projectId, int $versionId, User $user): ServiceResultError | ServiceResultArray + public function show(int $projectId, int $versionId, ?int $categoryId, User $user): ServiceResultError | ServiceResultArray { $project = $this->projectRepository->getProjectById($projectId); if (\is_null($project)) { @@ -70,9 +80,48 @@ final class DocumentationVersionService extends Service return $this->errFobidden(__('Access is denied')); } + $category = null; + if (!\is_null($categoryId)) { + $category = $this->documentationCategoryRepository->getCategoryById($categoryId); + if (\is_null($category) || $category->version_id !== $version->id) { + return $this->errNotFound(__('Not Found')); + } + if ($user->cannot('view', $category)) { + return $this->errFobidden(__('Access is denied')); + } + } + + $defaultLanguage = $project->languages()->where('is_default', 1)->first(); + $with = [ + 'content' => function (HasOne $hasOne) use ($defaultLanguage) { + /** @var ?ProjectLanguage $defaultLanguage */ + $hasOne->when($defaultLanguage, function (Builder $query, ProjectLanguage $defaultLanguage) { + $query->where('language_id', $defaultLanguage->id); + }); + } + ]; + + $categoryDto = new DocumentationCategoryBuilderDto\Category($category->id ?? null); + $categories = $this->documentationCategoryRepository + ->getCategories( + $version->id, + new DocumentationCategoryBuilderDto(parentId: $categoryDto), + $with + )->all(); + $documents = $this->documentationRepository + ->getDocumentations( + $version->id, + new DocumentationBuilderDto(categoryId: $categoryDto), + $with + )->all(); + return $this->result([ 'version' => $version, 'project' => $project, + + 'category' => $category, + 'categories' => $categories, + 'documentations' => $documents, ]); } diff --git a/app/application/lang/en.json b/app/application/lang/en.json index d976cca..8ee4b02 100644 --- a/app/application/lang/en.json +++ b/app/application/lang/en.json @@ -38,6 +38,9 @@ "Connection Timed Out": "Connection Timed Out", "Continue": "Continue", "Create": "Create", + "Create documentation": "Create documentation", + "Create category": "Create category", + "Create version documentation": "Create a version of the documentation", "Create :name": "Create :name", "Created": "Created", "Delete": "Delete", diff --git a/app/application/lang/en/admin-sections.php b/app/application/lang/en/admin-sections.php index 69871f4..7309220 100644 --- a/app/application/lang/en/admin-sections.php +++ b/app/application/lang/en/admin-sections.php @@ -17,4 +17,6 @@ return [ 'Documentation' => 'Documentation', 'Categories' => 'Categories', 'Setting up automatic translation' => 'Setting up automatic translation', + 'Documentation not found' => 'Documentation not found', + 'No categories found' => 'No categories found', ]; diff --git a/app/application/lang/ru.json b/app/application/lang/ru.json index 0d55c4b..36c2e67 100644 --- a/app/application/lang/ru.json +++ b/app/application/lang/ru.json @@ -38,6 +38,9 @@ "Connection Timed Out": "Соединение не отвечает", "Continue": "Продолжай", "Create": "Создать", + "Create documentation": "Создать документацию", + "Create category": "Создать категорию", + "Create version documentation": "Создать версию документации", "Create :name": "Создать :name", "Created": "Создано", "Delete": "Удалить", diff --git a/app/application/lang/ru/admin-sections.php b/app/application/lang/ru/admin-sections.php index 09c6504..454ccd4 100644 --- a/app/application/lang/ru/admin-sections.php +++ b/app/application/lang/ru/admin-sections.php @@ -17,4 +17,6 @@ return [ 'Documentation' => 'Документация', 'Categories' => 'Категории', 'Setting up automatic translation' => 'Настройка автоматического перевода', + 'Documentation not found' => 'Документация не найдена', + 'No categories found' => 'Категории не найдены', ]; diff --git a/app/application/resources/views/admin/projects/documentation-versions/_top.blade.php b/app/application/resources/views/admin/projects/documentation-versions/_top.blade.php index 6010f38..d9d038b 100644 --- a/app/application/resources/views/admin/projects/documentation-versions/_top.blade.php +++ b/app/application/resources/views/admin/projects/documentation-versions/_top.blade.php @@ -1,18 +1,34 @@ - -
+
@can('create', \App\Models\DocumentationVersion::class) - + - {{ __('Create') }} + {{ __('Create version documentation') }} @endcan @can('viewAny', \App\Models\DocumentationVersion::class) - + - {{ __('List') }} + {{ __('admin-sections.Documentation version') }} @endcan + @if(!empty($version) && $version->id) + @can('viewAny', \App\Models\Documentation::class) + + + {{ __('admin-sections.Documentation') }} + + @endcan + @can('viewAny', \App\Models\DocumentationCategory::class) + + + {{ __('admin-sections.Categories') }} + + @endcan + @endif
- diff --git a/app/application/resources/views/admin/projects/documentation-versions/show.blade.php b/app/application/resources/views/admin/projects/documentation-versions/show.blade.php index 2100a7d..6a1dc09 100644 --- a/app/application/resources/views/admin/projects/documentation-versions/show.blade.php +++ b/app/application/resources/views/admin/projects/documentation-versions/show.blade.php @@ -1,45 +1,162 @@ @section('meta_title', __('admin-sections.Documentation version')) @section('h1', __('admin-sections.Project') . ': ' . $project->name) +@php + $categoryId = null; + if (!empty($category) && $category->id) { + $categoryId = $category->id; + } +@endphp @include('admin.projects.documentation-versions._top')
-

{{ __('admin-sections.Documentation version') }}: {{ $version->title }}

+

+ {{ __('admin-sections.Documentation version') }}: {{ $version->title }} +

+ @if(!empty($category)) +

+ {{ __('admin-sections.Categories') }}: {{ $category->content?->title }} +

+ @endif + @can('create', \App\Models\Documentation::class) + + + {{ __('Create documentation') }} + + @endcan + @can('create', \App\Models\DocumentationCategory::class) + + + {{ __('Create category') }} + + @endcan
- + + + + + + + - @can('viewAny', \App\Models\Documentation::class) - - + - - @endcan - @can('viewAny', \App\Models\DocumentationCategory::class) - - + + + - - @endcan + @endcan + @can('delete', $category) + + @csrf + @method('DELETE') + + + @endcan + + + @empty + + + + @endforelse + + + + + + + + + + + + @forelse($documentations as $documentation) + + + + + + + @empty + + + + @endforelse
{{ __('admin-sections.Sections') }}
{{ __('admin-sections.Categories') }}
{{ __('validation.attributes.title') }}{{ __('validation.attributes.slug') }}{{ __('validation.attributes.is_public') }}
- - - {{ __('admin-sections.Documentation') }} + @forelse($categories as $category) +
+ @can('view', $category) + + {{ $category->content?->title }} -
- - + {{ $category->slug }} + + @if($category->is_public) + {{ __('Yes') }} + @else + {{ __('No') }} + @endif + + @can('update', $category) + + + - {{ __('admin-sections.Categories') }} -
+
+ {{ __('admin-sections.No categories found') }} +
+
{{ __('admin-sections.Documentation') }}
{{ __('validation.attributes.title') }}{{ __('validation.attributes.slug') }}{{ __('validation.attributes.is_public') }}
+ {{ $documentation->content?->title }} + + {{ $documentation->slug }} + + @if($documentation->is_public) + {{ __('Yes') }} + @else + {{ __('No') }} + @endif + + @can('update', $documentation) + + + + + + @endcan + @can('delete', $documentation) +
+ @csrf + @method('DELETE') + +
+ @endcan +
+
+ {{ __('admin-sections.Documentation not found') }} +
+
+ @push('scripts') + @include('admin._scripts._click-confirm', ['alert' => __('Do you want to delete?')]) + @endpush
diff --git a/app/application/routes/web.php b/app/application/routes/web.php index 90ffec4..a6be9f0 100644 --- a/app/application/routes/web.php +++ b/app/application/routes/web.php @@ -38,6 +38,7 @@ Route::middleware(['auth', 'verified', \App\Http\Middleware\UserLocale::class])- Route::resource('documentation-versions', \App\Http\Controllers\Admin\Projects\DocumentationVersionController::class)->where(['documentation_version' => '[0-9]+']); Route::prefix('documentation-versions/{version}')->as('documentation-versions.')->group(function () { + Route::get('category/{category}', [\App\Http\Controllers\Admin\Projects\DocumentationVersionController::class, 'show'])->where(['category' => '[0-9]+'])->name('category.show'); Route::resource('documentations', \App\Http\Controllers\Admin\Projects\DocumentationsController::class)->except(['show'])->where(['documentation' => '[0-9]+']); Route::resource('categories', \App\Http\Controllers\Admin\Projects\DocumentationCategoriesController::class)->except(['show'])->where(['category' => '[0-9]+']); })->where(['version' => '[0-9]+']); From 68315926613a362ffc8662d74852c6b26eb4d9d7 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Wed, 22 Jul 2026 01:03:25 +0500 Subject: [PATCH 06/41] Move documentation category templates under version-specific directories - Update paths to reflect the inclusion of documentation versions: `documentation-categories` views were moved into the `documentation-versions` directory. - Adjusted includes and routes in affected templates to align with new structure. - Minor styling updates (e.g., margins) for better spacing consistency. --- .../documentation-categories/_from.blade.php | 0 .../documentation-categories/_top.blade.php | 16 ++++++++++++---- .../documentation-categories/edit.blade.php | 7 ++++--- .../documentation-categories/index.blade.php | 2 +- 4 files changed, 17 insertions(+), 8 deletions(-) rename app/application/resources/views/admin/projects/{ => documentation-versions}/documentation-categories/_from.blade.php (100%) rename app/application/resources/views/admin/projects/{ => documentation-versions}/documentation-categories/_top.blade.php (68%) rename app/application/resources/views/admin/projects/{ => documentation-versions}/documentation-categories/edit.blade.php (58%) rename app/application/resources/views/admin/projects/{ => documentation-versions}/documentation-categories/index.blade.php (98%) diff --git a/app/application/resources/views/admin/projects/documentation-categories/_from.blade.php b/app/application/resources/views/admin/projects/documentation-versions/documentation-categories/_from.blade.php similarity index 100% rename from app/application/resources/views/admin/projects/documentation-categories/_from.blade.php rename to app/application/resources/views/admin/projects/documentation-versions/documentation-categories/_from.blade.php diff --git a/app/application/resources/views/admin/projects/documentation-categories/_top.blade.php b/app/application/resources/views/admin/projects/documentation-versions/documentation-categories/_top.blade.php similarity index 68% rename from app/application/resources/views/admin/projects/documentation-categories/_top.blade.php rename to app/application/resources/views/admin/projects/documentation-versions/documentation-categories/_top.blade.php index 068a81f..a85ff29 100644 --- a/app/application/resources/views/admin/projects/documentation-categories/_top.blade.php +++ b/app/application/resources/views/admin/projects/documentation-versions/documentation-categories/_top.blade.php @@ -1,12 +1,12 @@ -
+
@can('create', \App\Models\DocumentationCategory::class) - + {{ __('Create') }} @endcan @can('viewAny', \App\Models\DocumentationCategory::class) - + @@ -14,12 +14,20 @@ @endcan @can('viewAny', \App\Models\Documentation::class) - + {{ __('admin-sections.Documentation') }} @endcan + @can('viewAny', \App\Models\DocumentationVersion::class) + + + {{ __('admin-sections.Documentation version') }} + + @endcan
diff --git a/app/application/resources/views/admin/projects/documentation-categories/edit.blade.php b/app/application/resources/views/admin/projects/documentation-versions/documentation-categories/edit.blade.php similarity index 58% rename from app/application/resources/views/admin/projects/documentation-categories/edit.blade.php rename to app/application/resources/views/admin/projects/documentation-versions/documentation-categories/edit.blade.php index 925ee69..a28aa20 100644 --- a/app/application/resources/views/admin/projects/documentation-categories/edit.blade.php +++ b/app/application/resources/views/admin/projects/documentation-versions/documentation-categories/edit.blade.php @@ -3,15 +3,16 @@ {{ __('admin-sections.Project') . ': ' . $project->name . ' (' . $version->title . ')' }} @endsection - @include('admin.projects.documentation-categories._top') + @include('admin.projects.documentation-versions.documentation-categories._top')

{{ __('admin-sections.Categories') }}

-
+ @method('PUT') - @include('admin.projects.documentation-categories._from') + @include('admin.projects.documentation-versions.documentation-categories._from')
diff --git a/app/application/resources/views/admin/projects/documentation-categories/index.blade.php b/app/application/resources/views/admin/projects/documentation-versions/documentation-categories/index.blade.php similarity index 98% rename from app/application/resources/views/admin/projects/documentation-categories/index.blade.php rename to app/application/resources/views/admin/projects/documentation-versions/documentation-categories/index.blade.php index 564c747..3757257 100644 --- a/app/application/resources/views/admin/projects/documentation-categories/index.blade.php +++ b/app/application/resources/views/admin/projects/documentation-versions/documentation-categories/index.blade.php @@ -3,7 +3,7 @@ {{ __('admin-sections.Project') . ': ' . $project->name . ' (' . $version->title . ')' }} @endsection - @include('admin.projects.documentation-categories._top') + @include('admin.projects.documentation-versions.documentation-categories._top')

{{ __('admin-sections.Categories') }}

From 02bcac7889ce2b2a8352b66130809836c4f094dc Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Wed, 22 Jul 2026 01:04:01 +0500 Subject: [PATCH 07/41] Move documentation templates under version-specific directories - Relocate `documentations` views to the `documentation-versions` directory. - Update includes and routes across affected templates to align with the new directory structure. - Apply minor styling tweaks for improved spacing consistency. --- .../documentations/_from.blade.php | 0 .../documentations/_top.blade.php | 16 ++++++++++++---- .../documentations/edit.blade.php | 4 ++-- .../documentations/index.blade.php | 2 +- 4 files changed, 15 insertions(+), 7 deletions(-) rename app/application/resources/views/admin/projects/{ => documentation-versions}/documentations/_from.blade.php (100%) rename app/application/resources/views/admin/projects/{ => documentation-versions}/documentations/_top.blade.php (68%) rename app/application/resources/views/admin/projects/{ => documentation-versions}/documentations/edit.blade.php (83%) rename app/application/resources/views/admin/projects/{ => documentation-versions}/documentations/index.blade.php (98%) diff --git a/app/application/resources/views/admin/projects/documentations/_from.blade.php b/app/application/resources/views/admin/projects/documentation-versions/documentations/_from.blade.php similarity index 100% rename from app/application/resources/views/admin/projects/documentations/_from.blade.php rename to app/application/resources/views/admin/projects/documentation-versions/documentations/_from.blade.php diff --git a/app/application/resources/views/admin/projects/documentations/_top.blade.php b/app/application/resources/views/admin/projects/documentation-versions/documentations/_top.blade.php similarity index 68% rename from app/application/resources/views/admin/projects/documentations/_top.blade.php rename to app/application/resources/views/admin/projects/documentation-versions/documentations/_top.blade.php index 9ee8ee1..2856bc4 100644 --- a/app/application/resources/views/admin/projects/documentations/_top.blade.php +++ b/app/application/resources/views/admin/projects/documentation-versions/documentations/_top.blade.php @@ -1,12 +1,12 @@ -
+
@can('create', \App\Models\Documentation::class) - + {{ __('Create') }} @endcan @can('viewAny', \App\Models\Documentation::class) - + @@ -14,12 +14,20 @@ @endcan @can('viewAny', \App\Models\DocumentationCategory::class) - + {{ __('admin-sections.Categories') }} @endcan + @can('viewAny', \App\Models\DocumentationVersion::class) + + + {{ __('admin-sections.Documentation version') }} + + @endcan
diff --git a/app/application/resources/views/admin/projects/documentations/edit.blade.php b/app/application/resources/views/admin/projects/documentation-versions/documentations/edit.blade.php similarity index 83% rename from app/application/resources/views/admin/projects/documentations/edit.blade.php rename to app/application/resources/views/admin/projects/documentation-versions/documentations/edit.blade.php index b7c9be3..e715251 100644 --- a/app/application/resources/views/admin/projects/documentations/edit.blade.php +++ b/app/application/resources/views/admin/projects/documentation-versions/documentations/edit.blade.php @@ -3,7 +3,7 @@ {{ __('admin-sections.Project') . ': ' . $project->name . ' (' . $version->title . ')' }} @endsection - @include('admin.projects.documentations._top') + @include('admin.projects.documentation-versions.documentations._top')
@@ -11,7 +11,7 @@

{{ __('admin-sections.Documentation') }}

@method('PUT') - @include('admin.projects.documentations._from') + @include('admin.projects.documentation-versions.documentations._from')
diff --git a/app/application/resources/views/admin/projects/documentations/index.blade.php b/app/application/resources/views/admin/projects/documentation-versions/documentations/index.blade.php similarity index 98% rename from app/application/resources/views/admin/projects/documentations/index.blade.php rename to app/application/resources/views/admin/projects/documentation-versions/documentations/index.blade.php index 3bbeb85..61f2302 100644 --- a/app/application/resources/views/admin/projects/documentations/index.blade.php +++ b/app/application/resources/views/admin/projects/documentation-versions/documentations/index.blade.php @@ -3,7 +3,7 @@ {{ __('admin-sections.Project') . ': ' . $project->name . ' (' . $version->title . ')' }} @endsection - @include('admin.projects.documentations._top') + @include('admin.projects.documentation-versions.documentations._top')

{{ __('admin-sections.Documentation') }}

From 3a7c747ac425794a57e3b2c2f10f185528e3d532 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Wed, 22 Jul 2026 01:04:41 +0500 Subject: [PATCH 08/41] Add support for parent categories in documentation management - Update `DocumentationCategoryService::create` to accept an optional `parentId` parameter. - Introduce `CreateRequest` for validating parent category input. - Adjust controller actions to handle parent category information. - Restructure templates under `documentation-versions` for better organization. --- .../DocumentationCategoriesController.php | 16 +++++++++++----- .../DocumentationCategories/CreateRequest.php | 18 ++++++++++++++++++ .../Project/DocumentationCategoryService.php | 9 +++++++-- .../documentation-categories/create.blade.php | 4 ++-- 4 files changed, 38 insertions(+), 9 deletions(-) create mode 100644 app/application/app/Http/Requests/Admin/Projects/DocumentationCategories/CreateRequest.php rename app/application/resources/views/admin/projects/{ => documentation-versions}/documentation-categories/create.blade.php (79%) diff --git a/app/application/app/Http/Controllers/Admin/Projects/DocumentationCategoriesController.php b/app/application/app/Http/Controllers/Admin/Projects/DocumentationCategoriesController.php index 554984a..b4a9d6f 100644 --- a/app/application/app/Http/Controllers/Admin/Projects/DocumentationCategoriesController.php +++ b/app/application/app/Http/Controllers/Admin/Projects/DocumentationCategoriesController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Admin\Projects; use App\Dto\QuerySettingsDto; use App\Http\Controllers\Admin\Controller; +use App\Http\Requests\Admin\Projects\DocumentationCategories\CreateRequest; use App\Http\Requests\Admin\Projects\DocumentationCategories\IndexRequest; use App\Http\Requests\Admin\Projects\DocumentationCategories\StoreUpdateRequest; use App\Services\Admin\Project\DocumentationCategoryService; @@ -32,18 +33,23 @@ final class DocumentationCategoriesController extends Controller $this->errors($result); } - return view('admin/projects/documentation-categories/index', $result->getData()); + return view('admin/projects/documentation-versions/documentation-categories/index', $result->getData()); } - public function create(int $projectId, int $versionId, Request $request): View + public function create(int $projectId, int $versionId, CreateRequest $request): View { + $parentId = null; + if ($request->has('category')) { + $parentId = (int) $request->input('category'); + } + $user = $request->user(); - $result = $this->documentationCategoryService->create($projectId, $versionId, $user); + $result = $this->documentationCategoryService->create($projectId, $versionId, $user, $parentId); if ($result->isError()) { $this->errors($result); } - return view('admin/projects/documentation-categories/create', $result->getData()); + return view('admin/projects/documentation-versions/documentation-categories/create', $result->getData()); } public function edit(int $projectId, int $versionId, int $id, Request $request): View @@ -54,7 +60,7 @@ final class DocumentationCategoriesController extends Controller $this->errors($result); } - return view('admin/projects/documentation-categories/edit', $result->getData()); + return view('admin/projects/documentation-versions/documentation-categories/edit', $result->getData()); } public function store(int $projectId, int $versionId, StoreUpdateRequest $request): RedirectResponse diff --git a/app/application/app/Http/Requests/Admin/Projects/DocumentationCategories/CreateRequest.php b/app/application/app/Http/Requests/Admin/Projects/DocumentationCategories/CreateRequest.php new file mode 100644 index 0000000..1e89fb3 --- /dev/null +++ b/app/application/app/Http/Requests/Admin/Projects/DocumentationCategories/CreateRequest.php @@ -0,0 +1,18 @@ + ['nullable', 'integer', 'min:1'], + ]; + } +} diff --git a/app/application/app/Services/Admin/Project/DocumentationCategoryService.php b/app/application/app/Services/Admin/Project/DocumentationCategoryService.php index 7abce3d..6d2229e 100644 --- a/app/application/app/Services/Admin/Project/DocumentationCategoryService.php +++ b/app/application/app/Services/Admin/Project/DocumentationCategoryService.php @@ -75,7 +75,7 @@ final class DocumentationCategoryService extends Service ]); } - public function create(int $projectId, int $versionId, User $user): ServiceResultError | ServiceResultArray + public function create(int $projectId, int $versionId, User $user, ?int $parentId = null): ServiceResultError | ServiceResultArray { $version = $this->documentationVersionRepository->getVersionById($versionId); $project = $version?->project; @@ -88,10 +88,15 @@ final class DocumentationCategoryService extends Service } $defaultLanguage = $project->languages->where('is_default', 1)->first(); + $category = new DocumentationCategory(); + if ($parentId !== null) { + $category->parent_id = $parentId; + } + return $this->result([ 'version' => $version, 'project' => $project, - 'category' => new DocumentationCategory(), + 'category' => $category, 'categories' => $this->documentationCategoryRepository->getForSelect($version, $defaultLanguage), 'serviceTranslationEnable' => config('translation_service.enable', false), ]); diff --git a/app/application/resources/views/admin/projects/documentation-categories/create.blade.php b/app/application/resources/views/admin/projects/documentation-versions/documentation-categories/create.blade.php similarity index 79% rename from app/application/resources/views/admin/projects/documentation-categories/create.blade.php rename to app/application/resources/views/admin/projects/documentation-versions/documentation-categories/create.blade.php index 83a2b01..c7869f1 100644 --- a/app/application/resources/views/admin/projects/documentation-categories/create.blade.php +++ b/app/application/resources/views/admin/projects/documentation-versions/documentation-categories/create.blade.php @@ -3,14 +3,14 @@ {{ __('admin-sections.Project') . ': ' . $project->name . ' (' . $version->title . ')' }} @endsection - @include('admin.projects.documentation-categories._top') + @include('admin.projects.documentation-versions.documentation-categories._top')

{{ __('admin-sections.Categories') }}

- @include('admin.projects.documentation-categories._from') + @include('admin.projects.documentation-versions.documentation-categories._from')
From 820450c48c89f608af7b14cb9d439bb99cb4cb1c Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Wed, 22 Jul 2026 01:05:15 +0500 Subject: [PATCH 09/41] Add optional category selection when creating documentation - Update `DocumentationService::create` to support an optional `categoryId` parameter. - Introduce `CreateRequest` for input validation. - Adjust controller logic to handle `categoryId`. - Restructure create/edit views under the `documentation-versions` directory. --- .../Projects/DocumentationsController.php | 16 +++++++++++----- .../Projects/Documentations/CreateRequest.php | 18 ++++++++++++++++++ .../Admin/Project/DocumentationService.php | 9 +++++++-- .../documentations/create.blade.php | 4 ++-- 4 files changed, 38 insertions(+), 9 deletions(-) create mode 100644 app/application/app/Http/Requests/Admin/Projects/Documentations/CreateRequest.php rename app/application/resources/views/admin/projects/{ => documentation-versions}/documentations/create.blade.php (81%) diff --git a/app/application/app/Http/Controllers/Admin/Projects/DocumentationsController.php b/app/application/app/Http/Controllers/Admin/Projects/DocumentationsController.php index 3dee28c..ea2f3f7 100644 --- a/app/application/app/Http/Controllers/Admin/Projects/DocumentationsController.php +++ b/app/application/app/Http/Controllers/Admin/Projects/DocumentationsController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Admin\Projects; use App\Dto\QuerySettingsDto; use App\Http\Controllers\Admin\Controller; +use App\Http\Requests\Admin\Projects\Documentations\CreateRequest; use App\Http\Requests\Admin\Projects\Documentations\IndexRequest; use App\Http\Requests\Admin\Projects\Documentations\StoreUpdateRequest; use App\Services\Admin\Project\DocumentationService; @@ -32,18 +33,23 @@ final class DocumentationsController extends Controller $this->errors($result); } - return view('admin/projects/documentations/index', $result->getData()); + return view('admin/projects/documentation-versions/documentations/index', $result->getData()); } - public function create(int $projectId, int $versionId, Request $request): View + public function create(int $projectId, int $versionId, CreateRequest $request): View { + $categoryId = null; + if ($request->has('category')) { + $categoryId = (int) $request->input('category'); + } + $user = $request->user(); - $result = $this->documentationService->create($projectId, $versionId, $user); + $result = $this->documentationService->create($projectId, $versionId, $user, $categoryId); if ($result->isError()) { $this->errors($result); } - return view('admin/projects/documentations/create', $result->getData()); + return view('admin/projects/documentation-versions/documentations/create', $result->getData()); } public function edit(int $projectId, int $versionId, int $id, Request $request): View @@ -54,7 +60,7 @@ final class DocumentationsController extends Controller $this->errors($result); } - return view('admin/projects/documentations/edit', $result->getData()); + return view('admin/projects/documentation-versions/documentations/edit', $result->getData()); } public function store(int $projectId, int $versionId, StoreUpdateRequest $request): RedirectResponse diff --git a/app/application/app/Http/Requests/Admin/Projects/Documentations/CreateRequest.php b/app/application/app/Http/Requests/Admin/Projects/Documentations/CreateRequest.php new file mode 100644 index 0000000..f1b3831 --- /dev/null +++ b/app/application/app/Http/Requests/Admin/Projects/Documentations/CreateRequest.php @@ -0,0 +1,18 @@ + ['nullable', 'integer', 'min:1'], + ]; + } +} diff --git a/app/application/app/Services/Admin/Project/DocumentationService.php b/app/application/app/Services/Admin/Project/DocumentationService.php index 4a1313c..d8b7291 100644 --- a/app/application/app/Services/Admin/Project/DocumentationService.php +++ b/app/application/app/Services/Admin/Project/DocumentationService.php @@ -75,7 +75,7 @@ final class DocumentationService extends Service ]); } - public function create(int $projectId, int $versionId, User $user): ServiceResultError | ServiceResultArray + public function create(int $projectId, int $versionId, User $user, ?int $categoryId = null): ServiceResultError | ServiceResultArray { $version = $this->documentationVersionRepository->getVersionById($versionId); $project = $version?->project; @@ -87,11 +87,16 @@ final class DocumentationService extends Service return $this->errFobidden(__('Access is denied')); } + $documentation = new Documentation(); + if ($categoryId !== null) { + $documentation->category_id = $categoryId; + } + $defaultLanguage = $project->languages->where('is_default', 1)->first(); return $this->result([ 'version' => $version, 'project' => $project, - 'documentation' => new Documentation(), + 'documentation' => $documentation, 'categories' => $this->documentationCategoryRepository->getForSelect($version, $defaultLanguage), 'serviceTranslationEnable' => config('translation_service.enable', false), ]); diff --git a/app/application/resources/views/admin/projects/documentations/create.blade.php b/app/application/resources/views/admin/projects/documentation-versions/documentations/create.blade.php similarity index 81% rename from app/application/resources/views/admin/projects/documentations/create.blade.php rename to app/application/resources/views/admin/projects/documentation-versions/documentations/create.blade.php index b03d0d5..405c0ad 100644 --- a/app/application/resources/views/admin/projects/documentations/create.blade.php +++ b/app/application/resources/views/admin/projects/documentation-versions/documentations/create.blade.php @@ -3,14 +3,14 @@ {{ __('admin-sections.Project') . ': ' . $project->name . ' (' . $version->title . ')' }} @endsection - @include('admin.projects.documentations._top') + @include('admin.projects.documentation-versions.documentations._top')

{{ __('admin-sections.Documentation') }}

- @include('admin.projects.documentations._from') + @include('admin.projects.documentation-versions.documentations._from')
From 949ffc6221eb384f43b77cb7bf61f22c6c6111a0 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Wed, 22 Jul 2026 20:57:11 +0500 Subject: [PATCH 10/41] Update environment variables and dependencies in Docker Compose for production - Replace `UNIT_SOURCE` with `APP_ENV` variable. - Add `app` and `captcha-app` as dependencies in relevant services. --- docker-compose-prod_docker-hub.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docker-compose-prod_docker-hub.yml b/docker-compose-prod_docker-hub.yml index d86a622..d918466 100644 --- a/docker-compose-prod_docker-hub.yml +++ b/docker-compose-prod_docker-hub.yml @@ -13,7 +13,7 @@ services: env_file: app/.env environment: CONTAINER_ROLE: app - UNIT_SOURCE: "172.16.0.0/12" + APP_ENV: production volumes: - ./app/storage/app:/var/www/html/storage/app - ./app/storage/logs:/var/www/html/storage/logs @@ -24,6 +24,7 @@ services: image: docker.mdhub.kor-elf.net/kor-elf/my-projects-website:0.6.0 # MDHub # restart: always depends_on: + - app - db - app-redis environment: @@ -39,6 +40,7 @@ services: image: docker.mdhub.kor-elf.net/kor-elf/my-projects-website:0.6.0 # MDHub # restart: always depends_on: + - app - db - app-redis environment: @@ -78,6 +80,7 @@ services: image: korelf/service-captcha:0.8.2 # restart: always depends_on: + - captcha-app - db - captcha-redis environment: @@ -87,6 +90,7 @@ services: image: korelf/service-captcha:0.8.2 # restart: always depends_on: + - captcha-app - db - captcha-redis environment: @@ -98,6 +102,7 @@ services: image: korelf/service-captcha:0.8.2 # restart: always depends_on: + - captcha-app - db - captcha-redis environment: From 0a612e72c7b8ef6ec37e8956fe0df5a5d44022bd Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Wed, 22 Jul 2026 21:01:09 +0500 Subject: [PATCH 11/41] Add breadcrumb support using Laravel Breadcrumbs package - Install `diglactic/laravel-breadcrumbs` and update composer dependencies. - Configure breadcrumbs in `config/breadcrumbs.php`. - Define breadcrumbs for admin and site views. - Update base admin controller to include default breadcrumb view. --- .../Project/Translation/Translations.php | 1 + .../app/Http/Controllers/Admin/Controller.php | 6 +- .../Controllers/Admin/LanguagesController.php | 4 +- .../Admin/Projects/AboutController.php | 4 +- .../DocumentationCategoriesController.php | 4 +- .../DocumentationVersionController.php | 4 +- .../Projects/DocumentationsController.php | 4 +- .../Admin/Projects/FeedbacksController.php | 4 +- .../Admin/Projects/LinksController.php | 4 +- .../Projects/ServiceTranslateController.php | 4 +- .../Admin/Projects/TranslationsController.php | 6 +- .../Controllers/Admin/ProjectsController.php | 4 +- .../Controllers/Admin/RolesController.php | 4 +- .../Controllers/Admin/UsersController.php | 4 +- app/application/breadcrumbs/admin.php | 7 + app/application/breadcrumbs/site.php | 8 ++ app/application/composer.json | 1 + app/application/composer.lock | 127 +++++++++++++++++- app/application/config/breadcrumbs.php | 78 +++++++++++ 19 files changed, 262 insertions(+), 16 deletions(-) create mode 100644 app/application/breadcrumbs/admin.php create mode 100644 app/application/breadcrumbs/site.php create mode 100644 app/application/config/breadcrumbs.php diff --git a/app/application/app/Dto/Service/Admin/Project/Translation/Translations.php b/app/application/app/Dto/Service/Admin/Project/Translation/Translations.php index f7831bb..3b72245 100644 --- a/app/application/app/Dto/Service/Admin/Project/Translation/Translations.php +++ b/app/application/app/Dto/Service/Admin/Project/Translation/Translations.php @@ -44,6 +44,7 @@ final class Translations 'site.Choose version', 'site.alert-status-not-supported', 'site.alert-status-future', + 'Home', ]; foreach (DocumentationVersionStatus::cases() as $status) { diff --git a/app/application/app/Http/Controllers/Admin/Controller.php b/app/application/app/Http/Controllers/Admin/Controller.php index 35c90d7..02783f4 100644 --- a/app/application/app/Http/Controllers/Admin/Controller.php +++ b/app/application/app/Http/Controllers/Admin/Controller.php @@ -3,8 +3,12 @@ namespace App\Http\Controllers\Admin; use App\Http\Controllers\Controller as BaseController; +use Illuminate\Support\Facades\Config; abstract class Controller extends BaseController { - + public function __construct() + { + Config::set('breadcrumbs.view', 'breadcrumbs::bootstrap4'); + } } diff --git a/app/application/app/Http/Controllers/Admin/LanguagesController.php b/app/application/app/Http/Controllers/Admin/LanguagesController.php index 4726f7c..c57e32a 100644 --- a/app/application/app/Http/Controllers/Admin/LanguagesController.php +++ b/app/application/app/Http/Controllers/Admin/LanguagesController.php @@ -11,7 +11,9 @@ final class LanguagesController extends Controller { public function __construct( private readonly LanguageService $languageService, - ) { } + ) { + parent::__construct(); + } public function newLanguage(NewLanguageRequest $request): JsonResponse { diff --git a/app/application/app/Http/Controllers/Admin/Projects/AboutController.php b/app/application/app/Http/Controllers/Admin/Projects/AboutController.php index d315844..5a90704 100644 --- a/app/application/app/Http/Controllers/Admin/Projects/AboutController.php +++ b/app/application/app/Http/Controllers/Admin/Projects/AboutController.php @@ -13,7 +13,9 @@ final class AboutController extends Controller { public function __construct( private readonly AboutService $aboutService, - ) { } + ) { + parent::__construct(); + } public function languages(int $project, Request $request): View { diff --git a/app/application/app/Http/Controllers/Admin/Projects/DocumentationCategoriesController.php b/app/application/app/Http/Controllers/Admin/Projects/DocumentationCategoriesController.php index b4a9d6f..0bb6912 100644 --- a/app/application/app/Http/Controllers/Admin/Projects/DocumentationCategoriesController.php +++ b/app/application/app/Http/Controllers/Admin/Projects/DocumentationCategoriesController.php @@ -16,7 +16,9 @@ final class DocumentationCategoriesController extends Controller { public function __construct( private readonly DocumentationCategoryService $documentationCategoryService, - ) { } + ) { + parent::__construct(); + } public function index(int $projectId, int $versionId, IndexRequest $request): View { diff --git a/app/application/app/Http/Controllers/Admin/Projects/DocumentationVersionController.php b/app/application/app/Http/Controllers/Admin/Projects/DocumentationVersionController.php index 492435c..370ddfb 100644 --- a/app/application/app/Http/Controllers/Admin/Projects/DocumentationVersionController.php +++ b/app/application/app/Http/Controllers/Admin/Projects/DocumentationVersionController.php @@ -15,7 +15,9 @@ final class DocumentationVersionController extends Controller { public function __construct( private readonly DocumentationVersionService $documentationVersionService, - ) { } + ) { + parent::__construct(); + } public function index(int $projectId, IndexRequest $request): View { diff --git a/app/application/app/Http/Controllers/Admin/Projects/DocumentationsController.php b/app/application/app/Http/Controllers/Admin/Projects/DocumentationsController.php index ea2f3f7..4e042d9 100644 --- a/app/application/app/Http/Controllers/Admin/Projects/DocumentationsController.php +++ b/app/application/app/Http/Controllers/Admin/Projects/DocumentationsController.php @@ -16,7 +16,9 @@ final class DocumentationsController extends Controller { public function __construct( private readonly DocumentationService $documentationService, - ) { } + ) { + parent::__construct(); + } public function index(int $projectId, int $versionId, IndexRequest $request): View { diff --git a/app/application/app/Http/Controllers/Admin/Projects/FeedbacksController.php b/app/application/app/Http/Controllers/Admin/Projects/FeedbacksController.php index 370c3ac..9427ef4 100644 --- a/app/application/app/Http/Controllers/Admin/Projects/FeedbacksController.php +++ b/app/application/app/Http/Controllers/Admin/Projects/FeedbacksController.php @@ -12,7 +12,9 @@ final class FeedbacksController extends Controller { public function __construct( private readonly FeedbackService $feedbackService, - ) { } + ) { + parent::__construct(); + } public function index(IndexRequest $request): View { diff --git a/app/application/app/Http/Controllers/Admin/Projects/LinksController.php b/app/application/app/Http/Controllers/Admin/Projects/LinksController.php index c937267..039dd43 100644 --- a/app/application/app/Http/Controllers/Admin/Projects/LinksController.php +++ b/app/application/app/Http/Controllers/Admin/Projects/LinksController.php @@ -15,7 +15,9 @@ final class LinksController extends Controller { public function __construct( private readonly LinkService $linkService - ) { } + ) { + parent::__construct(); + } public function index(int $projectId, IndexRequest $request): View { diff --git a/app/application/app/Http/Controllers/Admin/Projects/ServiceTranslateController.php b/app/application/app/Http/Controllers/Admin/Projects/ServiceTranslateController.php index 5b33722..074f65c 100644 --- a/app/application/app/Http/Controllers/Admin/Projects/ServiceTranslateController.php +++ b/app/application/app/Http/Controllers/Admin/Projects/ServiceTranslateController.php @@ -13,7 +13,9 @@ final class ServiceTranslateController extends Controller { public function __construct( private readonly ServiceTranslateService $serviceTranslateService, - ) { } + ) { + parent::__construct(); + } public function view(int $projectId, Request $request): View { diff --git a/app/application/app/Http/Controllers/Admin/Projects/TranslationsController.php b/app/application/app/Http/Controllers/Admin/Projects/TranslationsController.php index 560c640..b50fd27 100644 --- a/app/application/app/Http/Controllers/Admin/Projects/TranslationsController.php +++ b/app/application/app/Http/Controllers/Admin/Projects/TranslationsController.php @@ -2,7 +2,7 @@ namespace App\Http\Controllers\Admin\Projects; -use App\Http\Controllers\Controller; +use App\Http\Controllers\Admin\Controller; use App\Http\Requests\Admin\Projects\Translations\UpdateRequest; use App\Services\Admin\Project\TranslationService; use Illuminate\Http\RedirectResponse; @@ -13,7 +13,9 @@ final class TranslationsController extends Controller { public function __construct( private readonly TranslationService $translationService, - ) { } + ) { + parent::__construct(); + } public function languages(int $projectId, Request $request): View { diff --git a/app/application/app/Http/Controllers/Admin/ProjectsController.php b/app/application/app/Http/Controllers/Admin/ProjectsController.php index 3ab7a46..8a315f3 100644 --- a/app/application/app/Http/Controllers/Admin/ProjectsController.php +++ b/app/application/app/Http/Controllers/Admin/ProjectsController.php @@ -14,7 +14,9 @@ final class ProjectsController extends Controller { public function __construct( private readonly ProjectService $projectService - ) { } + ) { + parent::__construct(); + } public function index(IndexRequest $request): View { diff --git a/app/application/app/Http/Controllers/Admin/RolesController.php b/app/application/app/Http/Controllers/Admin/RolesController.php index 9632ea5..c713068 100644 --- a/app/application/app/Http/Controllers/Admin/RolesController.php +++ b/app/application/app/Http/Controllers/Admin/RolesController.php @@ -14,7 +14,9 @@ final class RolesController extends Controller { public function __construct( private readonly RoleService $roleService - ) { } + ) { + parent::__construct(); + } public function index(IndexRequest $request): View { $user = $request->user(); diff --git a/app/application/app/Http/Controllers/Admin/UsersController.php b/app/application/app/Http/Controllers/Admin/UsersController.php index 4d13ec1..0577d3e 100644 --- a/app/application/app/Http/Controllers/Admin/UsersController.php +++ b/app/application/app/Http/Controllers/Admin/UsersController.php @@ -15,7 +15,9 @@ final class UsersController extends Controller { public function __construct( private readonly UserService $userService - ) { } + ) { + parent::__construct(); + } public function index(IndexRequest $request): View { diff --git a/app/application/breadcrumbs/admin.php b/app/application/breadcrumbs/admin.php new file mode 100644 index 0000000..9f97f5c --- /dev/null +++ b/app/application/breadcrumbs/admin.php @@ -0,0 +1,7 @@ +push(__('Home'), route('admin.home')); +}); diff --git a/app/application/breadcrumbs/site.php b/app/application/breadcrumbs/site.php new file mode 100644 index 0000000..0e442dc --- /dev/null +++ b/app/application/breadcrumbs/site.php @@ -0,0 +1,8 @@ +push($websiteTranslations->translate('Home'), route('home')); +}); diff --git a/app/application/composer.json b/app/application/composer.json index e4dc362..5cdc34f 100644 --- a/app/application/composer.json +++ b/app/application/composer.json @@ -7,6 +7,7 @@ "license": "MIT", "require": { "php": "^8.3", + "diglactic/laravel-breadcrumbs": "^10.1", "intervention/image-laravel": "^4.0", "kor-elf/captcha-rule-for-laravel": "^1.0", "kor-elf/translate-laravel": "^2.0", diff --git a/app/application/composer.lock b/app/application/composer.lock index b1003cd..39aff29 100644 --- a/app/application/composer.lock +++ b/app/application/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a920317eed14f3d09c7ed37a87c65ec1", + "content-hash": "497caf2eb305e80d19ee6e7fbeffd7b7", "packages": [ { "name": "brick/math", @@ -209,6 +209,76 @@ }, "time": "2024-07-08T12:26:09+00:00" }, + { + "name": "diglactic/laravel-breadcrumbs", + "version": "v10.1.0", + "source": { + "type": "git", + "url": "https://github.com/diglactic/laravel-breadcrumbs.git", + "reference": "64245821155ccfe721832898840408ee6136db31" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/diglactic/laravel-breadcrumbs/zipball/64245821155ccfe721832898840408ee6136db31", + "reference": "64245821155ccfe721832898840408ee6136db31", + "shasum": "" + }, + "require": { + "facade/ignition-contracts": "^1.0", + "laravel/framework": "^10.0 || ^11.0 || ^12.0 || ^13.0", + "php": "^8.1" + }, + "conflict": { + "davejamesmiller/laravel-breadcrumbs": "*" + }, + "require-dev": { + "orchestra/testbench": "^8.0 || ^9.0 || ^10.0 || ^11.0", + "phpunit/phpunit": "^10.5 || ^11.5 || ^12.5", + "spatie/phpunit-snapshot-assertions": "^5.1" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Breadcrumbs": "Diglactic\\Breadcrumbs\\Breadcrumbs" + }, + "providers": [ + "Diglactic\\Breadcrumbs\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Diglactic\\Breadcrumbs\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sheng Slogar", + "email": "sheng@diglactic.com", + "role": "Maintainer" + }, + { + "name": "Dave James Miller", + "email": "dave@davejamesmiller.com", + "role": "Original Creator" + } + ], + "description": "A simple Laravel-style way to create breadcrumbs.", + "homepage": "https://github.com/diglactic/laravel-breadcrumbs", + "keywords": [ + "laravel" + ], + "support": { + "issues": "https://github.com/diglactic/laravel-breadcrumbs/issues", + "source": "https://github.com/diglactic/laravel-breadcrumbs/tree/v10.1.0" + }, + "time": "2026-03-30T05:25:07+00:00" + }, { "name": "doctrine/inflector", "version": "2.1.0", @@ -507,6 +577,59 @@ ], "time": "2025-03-06T22:45:56+00:00" }, + { + "name": "facade/ignition-contracts", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/facade/ignition-contracts.git", + "reference": "3c921a1cdba35b68a7f0ccffc6dffc1995b18267" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/facade/ignition-contracts/zipball/3c921a1cdba35b68a7f0ccffc6dffc1995b18267", + "reference": "3c921a1cdba35b68a7f0ccffc6dffc1995b18267", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^v2.15.8", + "phpunit/phpunit": "^9.3.11", + "vimeo/psalm": "^3.17.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Facade\\IgnitionContracts\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://flareapp.io", + "role": "Developer" + } + ], + "description": "Solution contracts for Ignition", + "homepage": "https://github.com/facade/ignition-contracts", + "keywords": [ + "contracts", + "flare", + "ignition" + ], + "support": { + "issues": "https://github.com/facade/ignition-contracts/issues", + "source": "https://github.com/facade/ignition-contracts/tree/1.0.2" + }, + "time": "2020-10-16T08:27:54+00:00" + }, { "name": "fruitcake/php-cors", "version": "v1.4.0", @@ -9789,5 +9912,5 @@ "php": "^8.3" }, "platform-dev": {}, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/app/application/config/breadcrumbs.php b/app/application/config/breadcrumbs.php new file mode 100644 index 0000000..41dc41b --- /dev/null +++ b/app/application/config/breadcrumbs.php @@ -0,0 +1,78 @@ + 'breadcrumbs.site', + + /* + |-------------------------------------------------------------------------- + | Breadcrumbs File(s) + |-------------------------------------------------------------------------- + | + | The file(s) where breadcrumbs are defined. e.g. + | + | - base_path('routes/breadcrumbs.php') + | - glob(base_path('breadcrumbs/*.php')) + | + */ + + 'files' => [ + base_path('breadcrumbs/site.php'), + base_path('breadcrumbs/admin.php'), + ], + + /* + |-------------------------------------------------------------------------- + | Exceptions + |-------------------------------------------------------------------------- + | + | Determine when to throw an exception. + | + */ + + // When route-bound breadcrumbs are used but the current route doesn't have a name (UnnamedRouteException) + 'unnamed-route-exception' => true, + + // When route-bound breadcrumbs are used and the matching breadcrumb doesn't exist (InvalidBreadcrumbException) + 'missing-route-bound-breadcrumb-exception' => true, + + // When a named breadcrumb is used but doesn't exist (InvalidBreadcrumbException) + 'invalid-named-breadcrumb-exception' => true, + + /* + |-------------------------------------------------------------------------- + | Classes + |-------------------------------------------------------------------------- + | + | Subclass the default classes for more advanced customisations. + | + */ + + // Manager + 'manager-class' => Diglactic\Breadcrumbs\Manager::class, + + // Generator + 'generator-class' => Diglactic\Breadcrumbs\Generator::class, + +]; From ba9be40c786ba28f4c76cdeca92fdd1a93c9314b Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Thu, 23 Jul 2026 23:36:14 +0500 Subject: [PATCH 12/41] Add profile and settings links to mobile navigation menu --- .../views/admin/layout/_navigation.blade.php | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/app/application/resources/views/admin/layout/_navigation.blade.php b/app/application/resources/views/admin/layout/_navigation.blade.php index 2312c1b..18d79e9 100644 --- a/app/application/resources/views/admin/layout/_navigation.blade.php +++ b/app/application/resources/views/admin/layout/_navigation.blade.php @@ -69,3 +69,25 @@ @endcan + +
  • + + +
  • From e2f5d155ff07f8eb0ec638f437b50bfc2c547f42 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Thu, 23 Jul 2026 23:57:24 +0500 Subject: [PATCH 13/41] Add breadcrumb support and hide navbar links on smaller screens - Introduce optional `@yield('breadcrumbs')` for rendering breadcrumbs. - Adjust navbar navigation visibility with `d-none d-lg-flex` class. --- app/application/resources/views/layout/private.blade.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/application/resources/views/layout/private.blade.php b/app/application/resources/views/layout/private.blade.php index 995457e..580a1ac 100644 --- a/app/application/resources/views/layout/private.blade.php +++ b/app/application/resources/views/layout/private.blade.php @@ -69,11 +69,12 @@ +@endunless From 4a4d6d3bdfe1fac437c33ed10431f9f2889bfe73 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Fri, 24 Jul 2026 00:02:19 +0500 Subject: [PATCH 15/41] Add breadcrumbs for admin roles views - Implement breadcrumbs in roles index, create, and edit views. - Update admin breadcrumbs configuration with roles-specific paths. --- app/application/breadcrumbs/admin.php | 22 ++++++++++++++++++- .../views/admin/roles/create.blade.php | 1 + .../views/admin/roles/edit.blade.php | 1 + .../views/admin/roles/index.blade.php | 1 + 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/app/application/breadcrumbs/admin.php b/app/application/breadcrumbs/admin.php index 9f97f5c..6cfaf3b 100644 --- a/app/application/breadcrumbs/admin.php +++ b/app/application/breadcrumbs/admin.php @@ -3,5 +3,25 @@ use Diglactic\Breadcrumbs\Breadcrumbs; Breadcrumbs::for('admin.home', function ($trail) { - $trail->push(__('Home'), route('admin.home')); + $trail->push(__('Home'), route('admin.home'), ['icon' => '']); }); + +Breadcrumbs::for('admin.roles.index', function ($trail) { + $trail->parent('admin.home'); + + $trail->push(__('admin-sections.User group'), route('admin.roles.index')); +}); + +Breadcrumbs::for('admin.roles.create', function ($trail) { + $trail->parent('admin.roles.index'); + + $trail->push(__('Create'), route('admin.roles.create')); +}); + +Breadcrumbs::for('admin.roles.edit', function ($trail, \App\Models\Role $role) { + $trail->parent('admin.roles.index'); + + $trail->push(__('Edit'), route('admin.roles.edit', $role)); +}); + + diff --git a/app/application/resources/views/admin/roles/create.blade.php b/app/application/resources/views/admin/roles/create.blade.php index 6623ca9..ad97aed 100644 --- a/app/application/resources/views/admin/roles/create.blade.php +++ b/app/application/resources/views/admin/roles/create.blade.php @@ -1,5 +1,6 @@ @section('meta_title', __('admin-sections.User group')) @section('h1', __('admin-sections.User group')) +@section('breadcrumbs', Breadcrumbs::render('admin.roles.create')) @include('admin.roles._top')
    diff --git a/app/application/resources/views/admin/roles/edit.blade.php b/app/application/resources/views/admin/roles/edit.blade.php index 13b8ecd..12060d9 100644 --- a/app/application/resources/views/admin/roles/edit.blade.php +++ b/app/application/resources/views/admin/roles/edit.blade.php @@ -1,5 +1,6 @@ @section('meta_title', __('admin-sections.User group')) @section('h1', __('admin-sections.User group')) +@section('breadcrumbs', Breadcrumbs::render('admin.roles.edit', $role)) @include('admin.roles._top')
    diff --git a/app/application/resources/views/admin/roles/index.blade.php b/app/application/resources/views/admin/roles/index.blade.php index 27ca4c0..088a935 100644 --- a/app/application/resources/views/admin/roles/index.blade.php +++ b/app/application/resources/views/admin/roles/index.blade.php @@ -1,5 +1,6 @@ @section('meta_title', __('admin-sections.User group')) @section('h1', __('admin-sections.User group')) +@section('breadcrumbs', Breadcrumbs::render('admin.roles.index')) @include('admin.roles._top')
    From 6e8b7087497f44287c23fdb2771cf84010eebc60 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 25 Jul 2026 01:02:25 +0500 Subject: [PATCH 16/41] Add breadcrumbs for admin users, feedbacks, projects, and related views - Extend admin breadcrumbs configuration with paths for users, feedbacks, projects, translations, links, about, documentation versions, documentations, and categories. --- app/application/breadcrumbs/admin.php | 235 ++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) diff --git a/app/application/breadcrumbs/admin.php b/app/application/breadcrumbs/admin.php index 6cfaf3b..ccc05f8 100644 --- a/app/application/breadcrumbs/admin.php +++ b/app/application/breadcrumbs/admin.php @@ -6,6 +6,7 @@ Breadcrumbs::for('admin.home', function ($trail) { $trail->push(__('Home'), route('admin.home'), ['icon' => '']); }); +// Roles Breadcrumbs::for('admin.roles.index', function ($trail) { $trail->parent('admin.home'); @@ -23,5 +24,239 @@ Breadcrumbs::for('admin.roles.edit', function ($trail, \App\Models\Role $role) { $trail->push(__('Edit'), route('admin.roles.edit', $role)); }); +// End Roles +// Users +Breadcrumbs::for('admin.users.index', function ($trail) { + $trail->parent('admin.home'); + $trail->push(__('admin-sections.Users'), route('admin.users.index')); +}); + +Breadcrumbs::for('admin.users.create', function ($trail) { + $trail->parent('admin.users.index'); + + $trail->push(__('Create'), route('admin.users.create')); +}); + +Breadcrumbs::for('admin.users.edit', function ($trail, \App\Models\User $user) { + $trail->parent('admin.users.index'); + + $trail->push(__('Edit'), route('admin.users.edit', $user)); +}); +// End Users + +Breadcrumbs::for('admin.feedbacks.index', function ($trail) { + $trail->parent('admin.home'); + + $trail->push(__('admin-sections.Feedback'), route('admin.feedbacks.index')); +}); + +// Projects +Breadcrumbs::for('admin.projects.index', function ($trail) { + $trail->parent('admin.home'); + + $trail->push(__('admin-sections.Projects'), route('admin.projects.index')); +}); + +Breadcrumbs::for('admin.projects.create', function ($trail) { + $trail->parent('admin.projects.index'); + + $trail->push(__('Create'), route('admin.projects.create')); +}); + +Breadcrumbs::for('admin.projects.edit', function ($trail, \App\Models\Project $project) { + $trail->parent('admin.projects.index'); + + $trail->push(__('Edit'), route('admin.projects.edit', $project)); +}); + +Breadcrumbs::for('admin.projects.show', function ($trail, \App\Models\Project $project) { + $trail->parent('admin.projects.index'); + + $trail->push($project->name, route('admin.projects.show', $project)); +}); + +Breadcrumbs::for('admin.projects.feedbacks.index', function ($trail, \App\Models\Project $project) { + $trail->parent('admin.projects.show', $project); + + $trail->push(__('admin-sections.Feedback'), route('admin.projects.feedbacks.index', $project)); +}); +// End Projects + +// Projects translations +Breadcrumbs::for('admin.projects.translations.languages', function ($trail, \App\Models\Project $project) { + $trail->parent('admin.projects.show', $project); + + $trail->push(__('admin-sections.Translations'), route('admin.projects.translations.languages', $project)); +}); + +Breadcrumbs::for('admin.projects.translations.edit', function ($trail, \App\Models\Project $project, \App\Models\ProjectLanguage $language) { + $trail->parent('admin.projects.translations.languages', $project); + + $trail->push($language->title, route('admin.projects.translations.edit', ['project' => $project->id, 'language' => $language->id])); +}); + +Breadcrumbs::for('admin.projects.service-translate.view', function ($trail, \App\Models\Project $project) { + $trail->parent('admin.projects.show', $project); + + $trail->push(__('admin-sections.Setting up automatic translation'), route('admin.projects.service-translate.view', $project)); +}); +// End Projects translations + +// Projects links +Breadcrumbs::for('admin.projects.links.index', function ($trail, \App\Models\Project $project) { + $trail->parent('admin.projects.show', $project); + + $trail->push(__('admin-sections.Links project'), route('admin.projects.links.index', $project)); +}); + +Breadcrumbs::for('admin.projects.links.create', function ($trail, \App\Models\Project $project) { + $trail->parent('admin.projects.links.index', $project); + + $trail->push(__('Create'), route('admin.projects.links.create', $project)); +}); + +Breadcrumbs::for('admin.projects.links.edit', function ($trail, \App\Models\Project $project, \App\Models\ProjectLink $link) { + $trail->parent('admin.projects.links.index', $project); + + $trail->push(__('Edit'), route('admin.projects.links.edit', ['project' => $project->id, 'link' => $link->id])); +}); +// End Projects links + +// Projects about +Breadcrumbs::for('admin.projects.about.languages', function ($trail, \App\Models\Project $project) { + $trail->parent('admin.projects.show', $project); + + $trail->push(__('admin-sections.About project'), route('admin.projects.about.languages', $project)); +}); + +Breadcrumbs::for('admin.projects.about.edit', function ($trail, \App\Models\Project $project, \App\Models\ProjectLanguage $language) { + $trail->parent('admin.projects.about.languages', $project); + + $trail->push($language->title, route('admin.projects.about.edit', ['project' => $project->id, 'language' => $language->id])); +}); +// End Projects about + +// Projects documentation-versions +Breadcrumbs::for('admin.projects.documentation-versions.index', function ($trail, \App\Models\Project $project) { + $trail->parent('admin.projects.show', $project); + + $trail->push(__('admin-sections.Documentation version'), route('admin.projects.documentation-versions.index', $project)); +}); + +Breadcrumbs::for('admin.projects.documentation-versions.create', function ($trail, \App\Models\Project $project) { + $trail->parent('admin.projects.documentation-versions.index', $project); + + $trail->push(__('Create'), route('admin.projects.documentation-versions.create', $project)); +}); + +Breadcrumbs::for('admin.projects.documentation-versions.edit', function ($trail, \App\Models\Project $project, \App\Models\DocumentationVersion $version) { + $trail->parent('admin.projects.documentation-versions.index', $project); + + $trail->push(__('Edit'), route('admin.projects.documentation-versions.edit', ['project' => $project->id, 'documentation_version' => $version->id])); +}); + +Breadcrumbs::for('admin.projects.documentation-versions.show', function ($trail, \App\Models\Project $project, \App\Models\DocumentationVersion $version) { + $trail->parent('admin.projects.documentation-versions.index', $project); + + $trail->push($version->title, route('admin.projects.documentation-versions.show', ['project' => $project->id, 'documentation_version' => $version->id])); +}); + +Breadcrumbs::for('admin.projects.documentation-versions.category-path', function ($trail, \App\Models\Project $project, \App\Models\DocumentationVersion $version, \Illuminate\Support\Collection $categoryPath) { + $trail->parent('admin.projects.documentation-versions.show', $project, $version); + + foreach ($categoryPath as $pathItem) { + $trail->push( + $pathItem->content->title, + route('admin.projects.documentation-versions.category.show', [ + 'project' => $project->id, + 'version' => $version->id, + 'category' => $pathItem->id, + ]) + ); + } +}); + +Breadcrumbs::for('admin.projects.documentation-versions.show.category', function ($trail, \App\Models\Project $project, \App\Models\DocumentationVersion $version, \App\Models\DocumentationCategory $category, ?\Illuminate\Support\Collection $categoryPath) { + $path = $categoryPath ?? collect([$category]); + + $trail->parent('admin.projects.documentation-versions.category-path', $project, $version, $path); +}); +// End Projects documentation-versions + +// Projects documentation-versions documentations +Breadcrumbs::for('admin.projects.documentation-versions.documentations.index', function ($trail, \App\Models\Project $project, \App\Models\DocumentationVersion $version) { + $trail->parent('admin.projects.documentation-versions.show', $project, $version); + + $trail->push(__('admin-sections.Documentation'), route('admin.projects.documentation-versions.documentations.index', ['project' => $project->id, 'version' => $version->id])); +}); + +Breadcrumbs::for('admin.projects.documentation-versions.documentations.create', function ($trail, \App\Models\Project $project, \App\Models\DocumentationVersion $version) { + $trail->parent('admin.projects.documentation-versions.documentations.index', $project, $version); + + $trail->push(__('Create'), route('admin.projects.documentation-versions.documentations.create', ['project' => $project->id, 'version' => $version->id])); +}); + +Breadcrumbs::for('admin.projects.documentation-versions.category-path.documentations', function ($trail, \App\Models\Project $project, \App\Models\DocumentationVersion $version, \Illuminate\Support\Collection $categoryPath) { + $trail->parent('admin.projects.documentation-versions.category-path', $project, $version, $categoryPath); + + $trail->push(__('admin-sections.Documentation'), route('admin.projects.documentation-versions.documentations.index', ['project' => $project->id, 'version' => $version->id])); +}); + +Breadcrumbs::for('admin.projects.documentation-versions.category-path.documentations.create', function ($trail, \App\Models\Project $project, \App\Models\DocumentationVersion $version, \Illuminate\Support\Collection $categoryPath) { + $trail->parent('admin.projects.documentation-versions.category-path.documentations', $project, $version, $categoryPath); + + $trail->push(__('Create'), route('admin.projects.documentation-versions.documentations.create', ['project' => $project->id, 'version' => $version->id])); +}); + +Breadcrumbs::for('admin.projects.documentation-versions.documentations.edit', function ($trail, \App\Models\Project $project, \App\Models\DocumentationVersion $version, \App\Models\Documentation $documentation) { + $trail->parent('admin.projects.documentation-versions.documentations.index', $project, $version); + + $trail->push(__('Edit'), route('admin.projects.documentation-versions.documentations.edit', ['project' => $project->id, 'version' => $version->id, 'documentation' => $documentation->id])); +}); + +Breadcrumbs::for('admin.projects.documentation-versions.category-path.documentations.edit', function ($trail, \App\Models\Project $project, \App\Models\DocumentationVersion $version, \App\Models\Documentation $documentation, \Illuminate\Support\Collection $categoryPath) { + $trail->parent('admin.projects.documentation-versions.category-path.documentations', $project, $version, $categoryPath); + + $trail->push(__('Edit'), route('admin.projects.documentation-versions.documentations.edit', ['project' => $project->id, 'version' => $version->id, 'documentation' => $documentation->id])); +}); +// End Projects documentation-versions documentations + +// Projects documentation-versions categories +Breadcrumbs::for('admin.projects.documentation-versions.categories.index', function ($trail, \App\Models\Project $project, \App\Models\DocumentationVersion $version) { + $trail->parent('admin.projects.documentation-versions.show', $project, $version); + + $trail->push(__('admin-sections.Categories'), route('admin.projects.documentation-versions.categories.index', ['project' => $project->id, 'version' => $version->id])); +}); + +Breadcrumbs::for('admin.projects.documentation-versions.categories.create', function ($trail, \App\Models\Project $project, \App\Models\DocumentationVersion $version) { + $trail->parent('admin.projects.documentation-versions.categories.index', $project, $version); + + $trail->push(__('Create'), route('admin.projects.documentation-versions.categories.create', ['project' => $project->id, 'version' => $version->id])); +}); + +Breadcrumbs::for('admin.projects.documentation-versions.category-path.categories', function ($trail, \App\Models\Project $project, \App\Models\DocumentationVersion $version, \Illuminate\Support\Collection $categoryPath) { + $trail->parent('admin.projects.documentation-versions.category-path', $project, $version, $categoryPath); + + $trail->push(__('admin-sections.Categories'), route('admin.projects.documentation-versions.categories.index', ['project' => $project->id, 'version' => $version->id])); +}); + +Breadcrumbs::for('admin.projects.documentation-versions.category-path.categories.create', function ($trail, \App\Models\Project $project, \App\Models\DocumentationVersion $version, \Illuminate\Support\Collection $categoryPath) { + $trail->parent('admin.projects.documentation-versions.category-path.categories', $project, $version, $categoryPath); + + $trail->push(__('Create'), route('admin.projects.documentation-versions.categories.create', ['project' => $project->id, 'version' => $version->id])); +}); + +Breadcrumbs::for('admin.projects.documentation-versions.categories.edit', function ($trail, \App\Models\Project $project, \App\Models\DocumentationVersion $version, \App\Models\DocumentationCategory $category) { + $trail->parent('admin.projects.documentation-versions.categories.index', $project, $version); + + $trail->push(__('Edit'), route('admin.projects.documentation-versions.categories.edit', ['project' => $project->id, 'version' => $version->id, 'category' => $category->id])); +}); + +Breadcrumbs::for('admin.projects.documentation-versions.category-path.categories.edit', function ($trail, \App\Models\Project $project, \App\Models\DocumentationVersion $version, \App\Models\DocumentationCategory $category, \Illuminate\Support\Collection $categoryPath) { + $trail->parent('admin.projects.documentation-versions.category-path.categories', $project, $version, $categoryPath); + + $trail->push(__('Edit'), route('admin.projects.documentation-versions.categories.edit', ['project' => $project->id, 'version' => $version->id, 'category' => $category->id])); +}); +// End Projects documentation-versions categories From a70e09bddccdbe714c6ff751013c938f997b5af5 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 25 Jul 2026 01:17:55 +0500 Subject: [PATCH 17/41] Add recursive method to fetch category path in DocumentationCategoryRepository - Implement `getPathFromRoot` to retrieve the category path using recursive SQL query. - Cache results for improved performance. --- .../DocumentationCategoryRepository.php | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/app/application/app/Repositories/DocumentationCategoryRepository.php b/app/application/app/Repositories/DocumentationCategoryRepository.php index 1f013aa..1c70a1c 100644 --- a/app/application/app/Repositories/DocumentationCategoryRepository.php +++ b/app/application/app/Repositories/DocumentationCategoryRepository.php @@ -11,6 +11,9 @@ use App\Services\Search\CreateSearchInstanceCommand; use App\Dto\Builder\DocumentationCategory as DocumentationCategoryBuilderDto; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Relations\HasOne; +use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Cache; +use Illuminate\Support\Facades\DB; use Illuminate\Support\Str; final readonly class DocumentationCategoryRepository @@ -93,4 +96,52 @@ final readonly class DocumentationCategoryRepository ->pluck('title', 'id') ->toArray(); } + + public function getPathFromRoot(int $categoryId, ?ProjectLanguage $projectLanguage): Collection + { + $seconds = 3600; + return Cache::remember(self::class . $categoryId . '_' . $projectLanguage->id ?? 0, $seconds, function () use ($categoryId, $projectLanguage) { + $rows = DB::select( + <<pluck('id')->all(); + + $with = [ + 'content' => function (HasOne $hasOne) use ($projectLanguage) { + /** @var ?ProjectLanguage $projectLanguage */ + $hasOne->when($projectLanguage, function (Builder $query, ProjectLanguage $projectLanguage) { + $query->where('language_id', $projectLanguage->id); + }); + } + ]; + + $categories = DocumentationCategory::query() + ->whereIn('id', $idsInOrder) + ->with($with) + ->get() + ->keyBy('id'); + + return collect($idsInOrder) + ->map(fn (int $id) => $categories->get($id)) + ->filter() + ->values(); + }); + } } From 54a2d84a004b5a6439bace6660b4f1ff54c6a254 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 25 Jul 2026 01:18:29 +0500 Subject: [PATCH 18/41] Add category path resolution in DocumentationVersionService - Fetch category path using `getPathFromRoot` for the default project language. - Pass resolved `categoryPath` to the view. --- .../Services/Admin/Project/DocumentationVersionService.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/application/app/Services/Admin/Project/DocumentationVersionService.php b/app/application/app/Services/Admin/Project/DocumentationVersionService.php index b208461..7afb898 100644 --- a/app/application/app/Services/Admin/Project/DocumentationVersionService.php +++ b/app/application/app/Services/Admin/Project/DocumentationVersionService.php @@ -80,7 +80,10 @@ final class DocumentationVersionService extends Service return $this->errFobidden(__('Access is denied')); } + $defaultLanguage = $project->languages()->where('is_default', 1)->first(); + $category = null; + $categoryPath = null; if (!\is_null($categoryId)) { $category = $this->documentationCategoryRepository->getCategoryById($categoryId); if (\is_null($category) || $category->version_id !== $version->id) { @@ -89,9 +92,9 @@ final class DocumentationVersionService extends Service if ($user->cannot('view', $category)) { return $this->errFobidden(__('Access is denied')); } + $categoryPath = $this->documentationCategoryRepository->getPathFromRoot($category->id, $defaultLanguage); } - $defaultLanguage = $project->languages()->where('is_default', 1)->first(); $with = [ 'content' => function (HasOne $hasOne) use ($defaultLanguage) { /** @var ?ProjectLanguage $defaultLanguage */ @@ -120,6 +123,7 @@ final class DocumentationVersionService extends Service 'project' => $project, 'category' => $category, + 'categoryPath' => $categoryPath, 'categories' => $categories, 'documentations' => $documents, ]); From 0d08276d446643f5a0454cbfe93459f42f7dd090 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 25 Jul 2026 01:18:52 +0500 Subject: [PATCH 19/41] Add category path resolution in DocumentationService - Resolve `categoryPath` using `getPathFromRoot` based on the project's default language. - Include the resolved `categoryPath` in the response. --- .../Services/Admin/Project/DocumentationService.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/application/app/Services/Admin/Project/DocumentationService.php b/app/application/app/Services/Admin/Project/DocumentationService.php index d8b7291..0f7cd1d 100644 --- a/app/application/app/Services/Admin/Project/DocumentationService.php +++ b/app/application/app/Services/Admin/Project/DocumentationService.php @@ -87,18 +87,21 @@ final class DocumentationService extends Service return $this->errFobidden(__('Access is denied')); } + $defaultLanguage = $project->languages->where('is_default', 1)->first(); $documentation = new Documentation(); + $categoryPath = null; if ($categoryId !== null) { $documentation->category_id = $categoryId; + $categoryPath = $this->documentationCategoryRepository->getPathFromRoot($categoryId, $defaultLanguage); } - $defaultLanguage = $project->languages->where('is_default', 1)->first(); return $this->result([ 'version' => $version, 'project' => $project, 'documentation' => $documentation, 'categories' => $this->documentationCategoryRepository->getForSelect($version, $defaultLanguage), 'serviceTranslationEnable' => config('translation_service.enable', false), + 'categoryPath' => $categoryPath, ]); } @@ -119,17 +122,20 @@ final class DocumentationService extends Service return $this->errFobidden(__('Access is denied')); } + $defaultLanguage = $project->languages->where('is_default', 1)->first(); $withCategories = []; + $categoryPath = null; if ($documentation->category_id) { $withCategories[] = $documentation->category_id; + $categoryPath = $this->documentationCategoryRepository->getPathFromRoot($documentation->category_id, $defaultLanguage); } - $defaultLanguage = $project->languages->where('is_default', 1)->first(); return $this->result([ 'version' => $version, 'project' => $project, 'documentation' => $documentation, 'categories' => $this->documentationCategoryRepository->getForSelect($version, $defaultLanguage, null, $withCategories), 'serviceTranslationEnable' => config('translation_service.enable', false), + 'categoryPath' => $categoryPath, ]); } From d925928ea9e7ffac0d07cf3ddad1fde8b9565835 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 25 Jul 2026 01:19:09 +0500 Subject: [PATCH 20/41] Add cache clearing after key documentation category operations - Inject `ClearCacheCommandHandler` into `DocumentationCategoryService`. - Clear cache when creating, updating, or deleting a documentation category. - Extend responses to include `categoryPath` where applicable. --- .../Project/DocumentationCategoryService.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/app/application/app/Services/Admin/Project/DocumentationCategoryService.php b/app/application/app/Services/Admin/Project/DocumentationCategoryService.php index 6d2229e..ec994e7 100644 --- a/app/application/app/Services/Admin/Project/DocumentationCategoryService.php +++ b/app/application/app/Services/Admin/Project/DocumentationCategoryService.php @@ -20,6 +20,7 @@ use App\ServiceResults\ServiceResultArray; use App\ServiceResults\ServiceResultError; use App\ServiceResults\ServiceResultSuccess; use App\ServiceResults\StoreUpdateResult; +use App\Services\ClearCacheCommandHandler; use App\Services\DocumentationCategory\DocumentationCategoryCommandHandler; use App\Services\DocumentationCategoryContent\ModelSyncCommand; use App\Services\Service; @@ -34,6 +35,7 @@ final class DocumentationCategoryService extends Service private readonly DocumentationCategoryRepository $documentationCategoryRepository, private readonly DocumentationCategoryCommandHandler $categoryCommandHandler, private readonly ModelSyncCommand $contentSaveCommand, + private readonly ClearCacheCommandHandler $clearCacheCommandHandler, ) { } public function index(int $projectId, int $versionId, DocumentationCategoryBuilderDto $documentationCategoryBuilderDto, QuerySettingsDto $querySettingsDto, User $user): ServiceResultError | ServiceResultArray @@ -89,8 +91,10 @@ final class DocumentationCategoryService extends Service $defaultLanguage = $project->languages->where('is_default', 1)->first(); $category = new DocumentationCategory(); + $categoryPath = null; if ($parentId !== null) { $category->parent_id = $parentId; + $categoryPath = $this->documentationCategoryRepository->getPathFromRoot($parentId, $defaultLanguage); } return $this->result([ @@ -99,6 +103,7 @@ final class DocumentationCategoryService extends Service 'category' => $category, 'categories' => $this->documentationCategoryRepository->getForSelect($version, $defaultLanguage), 'serviceTranslationEnable' => config('translation_service.enable', false), + 'categoryPath' => $categoryPath, ]); } @@ -119,17 +124,20 @@ final class DocumentationCategoryService extends Service return $this->errFobidden(__('Access is denied')); } + $defaultLanguage = $project->languages->where('is_default', 1)->first(); $withCategories = []; + $categoryPath = null; if ($category->parent_id) { $withCategories[] = $category->parent_id; + $categoryPath = $this->documentationCategoryRepository->getPathFromRoot($category->parent_id, $defaultLanguage); } - $defaultLanguage = $project->languages->where('is_default', 1)->first(); return $this->result([ 'version' => $version, 'project' => $project, 'category' => $category, 'categories' => $this->documentationCategoryRepository->getForSelect($version, $defaultLanguage, $category, $withCategories), 'serviceTranslationEnable' => config('translation_service.enable', false), + 'categoryPath' => $categoryPath, ]); } @@ -165,6 +173,8 @@ final class DocumentationCategoryService extends Service if (\config('translation_service.enable', false)) { $this->translateContent($category, $data->getContents()); } + + $this->clearCacheCommandHandler->all(); } catch (ServiceException $e) { return $e->getServiceResultError(); } catch (\Throwable $e) { @@ -219,6 +229,8 @@ final class DocumentationCategoryService extends Service if (\config('translation_service.enable', false)) { $this->translateContent($category, $data->getContents()); } + + $this->clearCacheCommandHandler->all(); } catch (ServiceException $e) { return $e->getServiceResultError(); } catch (ParentException $e) { @@ -255,6 +267,8 @@ final class DocumentationCategoryService extends Service DB::transaction(function () use ($category) { $this->categoryCommandHandler->handleDestroy($category); }); + + $this->clearCacheCommandHandler->all(); } catch (\Throwable $e) { report($e); return $this->errService(__('Server Error')); From e11815b795b6c3b06df22de8b339cf0feb9fe421 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 25 Jul 2026 01:20:08 +0500 Subject: [PATCH 21/41] Add breadcrumbs to admin users views - Implement breadcrumbs for index, create, and edit views. --- app/application/resources/views/admin/users/create.blade.php | 1 + app/application/resources/views/admin/users/edit.blade.php | 1 + app/application/resources/views/admin/users/index.blade.php | 1 + 3 files changed, 3 insertions(+) diff --git a/app/application/resources/views/admin/users/create.blade.php b/app/application/resources/views/admin/users/create.blade.php index 85d3261..f4cf89c 100644 --- a/app/application/resources/views/admin/users/create.blade.php +++ b/app/application/resources/views/admin/users/create.blade.php @@ -1,5 +1,6 @@ @section('meta_title', __('admin-sections.Users')) @section('h1', __('admin-sections.Users')) +@section('breadcrumbs', Breadcrumbs::render('admin.users.create')) @include('admin.users._top')
    diff --git a/app/application/resources/views/admin/users/edit.blade.php b/app/application/resources/views/admin/users/edit.blade.php index 94bbefa..65adc5c 100644 --- a/app/application/resources/views/admin/users/edit.blade.php +++ b/app/application/resources/views/admin/users/edit.blade.php @@ -1,5 +1,6 @@ @section('meta_title', __('admin-sections.Users')) @section('h1', __('admin-sections.Users')) +@section('breadcrumbs', Breadcrumbs::render('admin.users.edit', $user)) @include('admin.users._top')
    diff --git a/app/application/resources/views/admin/users/index.blade.php b/app/application/resources/views/admin/users/index.blade.php index 85fed0d..92804e4 100644 --- a/app/application/resources/views/admin/users/index.blade.php +++ b/app/application/resources/views/admin/users/index.blade.php @@ -1,5 +1,6 @@ @section('meta_title', __('admin-sections.Users')) @section('h1', __('admin-sections.Users')) +@section('breadcrumbs', Breadcrumbs::render('admin.users.index')) @include('admin.users._top')
    From b8efe490a7d2be1968d209e494270ac660f5411b Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 25 Jul 2026 01:20:54 +0500 Subject: [PATCH 22/41] Add breadcrumbs to admin projects views - Implement breadcrumbs for index, create, edit, and show views. --- app/application/resources/views/admin/projects/create.blade.php | 1 + app/application/resources/views/admin/projects/edit.blade.php | 2 ++ app/application/resources/views/admin/projects/index.blade.php | 1 + app/application/resources/views/admin/projects/show.blade.php | 1 + 4 files changed, 5 insertions(+) diff --git a/app/application/resources/views/admin/projects/create.blade.php b/app/application/resources/views/admin/projects/create.blade.php index ce44758..265d57d 100644 --- a/app/application/resources/views/admin/projects/create.blade.php +++ b/app/application/resources/views/admin/projects/create.blade.php @@ -1,5 +1,6 @@ @section('meta_title', __('admin-sections.Projects')) @section('h1', __('admin-sections.Projects')) +@section('breadcrumbs', Breadcrumbs::render('admin.projects.create')) @include('admin.projects._top')
    diff --git a/app/application/resources/views/admin/projects/edit.blade.php b/app/application/resources/views/admin/projects/edit.blade.php index eee7180..08f82b3 100644 --- a/app/application/resources/views/admin/projects/edit.blade.php +++ b/app/application/resources/views/admin/projects/edit.blade.php @@ -1,11 +1,13 @@ @section('meta_title', __('admin-sections.Projects')) @section('h1', __('admin-sections.Projects')) +@section('breadcrumbs', Breadcrumbs::render('admin.projects.edit', $project)) @include('admin.projects._top')
    +

    {{ __('admin-sections.Project') . ': ' . $project->name }}

    @method('PUT') @include('admin.projects._from') diff --git a/app/application/resources/views/admin/projects/index.blade.php b/app/application/resources/views/admin/projects/index.blade.php index f903382..efc1ec0 100644 --- a/app/application/resources/views/admin/projects/index.blade.php +++ b/app/application/resources/views/admin/projects/index.blade.php @@ -1,5 +1,6 @@ @section('meta_title', __('admin-sections.Projects')) @section('h1', __('admin-sections.Projects')) +@section('breadcrumbs', Breadcrumbs::render('admin.projects.index')) @include('admin.projects._top')
    diff --git a/app/application/resources/views/admin/projects/show.blade.php b/app/application/resources/views/admin/projects/show.blade.php index 73a2608..3719017 100644 --- a/app/application/resources/views/admin/projects/show.blade.php +++ b/app/application/resources/views/admin/projects/show.blade.php @@ -1,5 +1,6 @@ @section('meta_title', __('admin-sections.Project') . ': ' . $project->name) @section('h1', __('admin-sections.Project') . ': ' . $project->name) +@section('breadcrumbs', Breadcrumbs::render('admin.projects.show', $project)) @include('admin.projects._top')
    From 506f403a00f703e72aca1aa081a6e06d5a5c9d43 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 25 Jul 2026 01:21:06 +0500 Subject: [PATCH 23/41] Add breadcrumbs to admin project feedback view --- .../resources/views/admin/projects/feedbacks/project.blade.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/application/resources/views/admin/projects/feedbacks/project.blade.php b/app/application/resources/views/admin/projects/feedbacks/project.blade.php index 871056e..e24cd3a 100644 --- a/app/application/resources/views/admin/projects/feedbacks/project.blade.php +++ b/app/application/resources/views/admin/projects/feedbacks/project.blade.php @@ -1,5 +1,6 @@ @section('meta_title', __('admin-sections.Project') . ': ' . $project->name . ' - ' . __('admin-sections.Feedback')) @section('h1', __('admin-sections.Project') . ': ' . $project->name) +@section('breadcrumbs', Breadcrumbs::render('admin.projects.feedbacks.index', $project))
    From 7fd3a0d45cda3299dc46589b858ad163bf59ab0b Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 25 Jul 2026 01:21:49 +0500 Subject: [PATCH 24/41] Add breadcrumbs to admin project languages and edit views --- .../resources/views/admin/projects/about/edit.blade.php | 1 + .../resources/views/admin/projects/about/languages.blade.php | 1 + 2 files changed, 2 insertions(+) diff --git a/app/application/resources/views/admin/projects/about/edit.blade.php b/app/application/resources/views/admin/projects/about/edit.blade.php index bfd057d..2386885 100644 --- a/app/application/resources/views/admin/projects/about/edit.blade.php +++ b/app/application/resources/views/admin/projects/about/edit.blade.php @@ -1,5 +1,6 @@ @section('meta_title', __('admin-sections.Project') . ': ' . $project->name) @section('h1', __('admin-sections.Project') . ': ' . $project->name) +@section('breadcrumbs', Breadcrumbs::render('admin.projects.about.edit', $project, $language))
    diff --git a/app/application/resources/views/admin/projects/about/languages.blade.php b/app/application/resources/views/admin/projects/about/languages.blade.php index 8ee9395..6fb1d3e 100644 --- a/app/application/resources/views/admin/projects/about/languages.blade.php +++ b/app/application/resources/views/admin/projects/about/languages.blade.php @@ -1,5 +1,6 @@ @section('meta_title', __('admin-sections.Project') . ': ' . $project->name) @section('h1', __('admin-sections.Project') . ': ' . $project->name) +@section('breadcrumbs', Breadcrumbs::render('admin.projects.about.languages', $project)) @php $contents = $project->contents; @endphp From aa03855bb35ca0920d4593753f11a0cc2d189f3d Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 25 Jul 2026 01:21:59 +0500 Subject: [PATCH 25/41] Add breadcrumbs to admin project feedbacks index view --- .../resources/views/admin/projects/feedbacks/index.blade.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/application/resources/views/admin/projects/feedbacks/index.blade.php b/app/application/resources/views/admin/projects/feedbacks/index.blade.php index 366fd74..617bc33 100644 --- a/app/application/resources/views/admin/projects/feedbacks/index.blade.php +++ b/app/application/resources/views/admin/projects/feedbacks/index.blade.php @@ -1,5 +1,6 @@ @section('meta_title', __('admin-sections.Feedback')) @section('h1', __('admin-sections.Feedback')) +@section('breadcrumbs', Breadcrumbs::render('admin.feedbacks.index'))
    From efab6d8eedd2f19aa7166bff3be6dfbecf6e1585 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 25 Jul 2026 01:22:13 +0500 Subject: [PATCH 26/41] Add breadcrumbs to admin project links views --- .../resources/views/admin/projects/links/create.blade.php | 1 + .../resources/views/admin/projects/links/edit.blade.php | 1 + .../resources/views/admin/projects/links/index.blade.php | 1 + 3 files changed, 3 insertions(+) diff --git a/app/application/resources/views/admin/projects/links/create.blade.php b/app/application/resources/views/admin/projects/links/create.blade.php index 196e3f5..bea1024 100644 --- a/app/application/resources/views/admin/projects/links/create.blade.php +++ b/app/application/resources/views/admin/projects/links/create.blade.php @@ -1,5 +1,6 @@ @section('meta_title', __('admin-sections.Links project')) @section('h1', __('admin-sections.Project') . ': ' . $project->name) +@section('breadcrumbs', Breadcrumbs::render('admin.projects.links.create', $project)) @include('admin.projects.links._top')
    diff --git a/app/application/resources/views/admin/projects/links/edit.blade.php b/app/application/resources/views/admin/projects/links/edit.blade.php index 189a52a..8434c6e 100644 --- a/app/application/resources/views/admin/projects/links/edit.blade.php +++ b/app/application/resources/views/admin/projects/links/edit.blade.php @@ -1,5 +1,6 @@ @section('meta_title', __('admin-sections.Links project')) @section('h1', __('admin-sections.Project') . ': ' . $project->name) +@section('breadcrumbs', Breadcrumbs::render('admin.projects.links.edit', $project, $link)) @include('admin.projects.links._top')
    diff --git a/app/application/resources/views/admin/projects/links/index.blade.php b/app/application/resources/views/admin/projects/links/index.blade.php index fb2419a..fea2018 100644 --- a/app/application/resources/views/admin/projects/links/index.blade.php +++ b/app/application/resources/views/admin/projects/links/index.blade.php @@ -1,5 +1,6 @@ @section('meta_title', __('admin-sections.Links project')) @section('h1', __('admin-sections.Project') . ': ' . $project->name) +@section('breadcrumbs', Breadcrumbs::render('admin.projects.links.index', $project)) @include('admin.projects.links._top')
    From 42d8728b1b8ecc1802202303816e49cf28a30b17 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 25 Jul 2026 01:22:26 +0500 Subject: [PATCH 27/41] Add breadcrumbs to admin project translations views - Add breadcrumbs to `languages` and `edit` views for improved navigation. --- .../resources/views/admin/projects/translations/edit.blade.php | 3 ++- .../views/admin/projects/translations/languages.blade.php | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/application/resources/views/admin/projects/translations/edit.blade.php b/app/application/resources/views/admin/projects/translations/edit.blade.php index 679b2ee..51c1d5b 100644 --- a/app/application/resources/views/admin/projects/translations/edit.blade.php +++ b/app/application/resources/views/admin/projects/translations/edit.blade.php @@ -1,11 +1,12 @@ @section('meta_title', __('admin-sections.Translations')) @section('h1', __('admin-sections.Project') . ': ' . $project->name) +@section('breadcrumbs', Breadcrumbs::render('admin.projects.translations.edit', $project, $language))
    -

    {{ __('admin-sections.Translations') }}

    +

    {{ __('admin-sections.Translations') . ': ' . $language->title }}

    @if($serviceTranslationEnable) diff --git a/app/application/resources/views/admin/projects/translations/languages.blade.php b/app/application/resources/views/admin/projects/translations/languages.blade.php index d14db7e..e9ded61 100644 --- a/app/application/resources/views/admin/projects/translations/languages.blade.php +++ b/app/application/resources/views/admin/projects/translations/languages.blade.php @@ -1,5 +1,6 @@ @section('meta_title', __('admin-sections.Translations')) @section('h1', __('admin-sections.Project') . ': ' . $project->name) +@section('breadcrumbs', Breadcrumbs::render('admin.projects.translations.languages', $project))
    From ace4c69bd8071341f0d8367b7d7f9f5355d3f7bc Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 25 Jul 2026 01:23:09 +0500 Subject: [PATCH 28/41] Add breadcrumbs to admin project service translate view - Introduce breadcrumbs for improved navigation on the service translate view. - Adjust heading structure and reorganize translation form layout. --- .../views/admin/projects/service-translate/view.blade.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/application/resources/views/admin/projects/service-translate/view.blade.php b/app/application/resources/views/admin/projects/service-translate/view.blade.php index 70c101a..7b41135 100644 --- a/app/application/resources/views/admin/projects/service-translate/view.blade.php +++ b/app/application/resources/views/admin/projects/service-translate/view.blade.php @@ -1,10 +1,12 @@ @section('meta_title', __('admin-sections.Setting up automatic translation') . '. ' . __('admin-sections.Project') . ': ' . $project->name) -@section('h1', __('admin-sections.Setting up automatic translation') . '. ' . __('admin-sections.Project') . ': ' . $project->name) +@section('h1', __('admin-sections.Project') . ': ' . $project->name) +@section('breadcrumbs', Breadcrumbs::render('admin.projects.service-translate.view', $project))
    +

    {{ __('admin-sections.Setting up automatic translation') }}

    @csrf
    From 754a67c5d01872b02875bc801d58a3f67840078a Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 25 Jul 2026 01:25:18 +0500 Subject: [PATCH 29/41] Add breadcrumbs to admin project documentation versions views - Introduce breadcrumbs for `index`, `create`, `edit`, and `show` views. - Adjust breadcrumb logic to handle optional categories in `show` view. --- .../admin/projects/documentation-versions/create.blade.php | 1 + .../admin/projects/documentation-versions/edit.blade.php | 1 + .../admin/projects/documentation-versions/index.blade.php | 3 ++- .../admin/projects/documentation-versions/show.blade.php | 5 +++++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/application/resources/views/admin/projects/documentation-versions/create.blade.php b/app/application/resources/views/admin/projects/documentation-versions/create.blade.php index bfd8739..0caa0e0 100644 --- a/app/application/resources/views/admin/projects/documentation-versions/create.blade.php +++ b/app/application/resources/views/admin/projects/documentation-versions/create.blade.php @@ -1,5 +1,6 @@ @section('meta_title', __('admin-sections.Documentation version')) @section('h1', __('admin-sections.Project') . ': ' . $project->name) +@section('breadcrumbs', Breadcrumbs::render('admin.projects.documentation-versions.create', $project)) @include('admin.projects.documentation-versions._top')
    diff --git a/app/application/resources/views/admin/projects/documentation-versions/edit.blade.php b/app/application/resources/views/admin/projects/documentation-versions/edit.blade.php index 00d86af..1c9576e 100644 --- a/app/application/resources/views/admin/projects/documentation-versions/edit.blade.php +++ b/app/application/resources/views/admin/projects/documentation-versions/edit.blade.php @@ -1,5 +1,6 @@ @section('meta_title', __('admin-sections.Documentation version')) @section('h1', __('admin-sections.Project') . ': ' . $project->name) +@section('breadcrumbs', Breadcrumbs::render('admin.projects.documentation-versions.edit', $project, $version)) @include('admin.projects.documentation-versions._top')
    diff --git a/app/application/resources/views/admin/projects/documentation-versions/index.blade.php b/app/application/resources/views/admin/projects/documentation-versions/index.blade.php index f442bee..f94a0f8 100644 --- a/app/application/resources/views/admin/projects/documentation-versions/index.blade.php +++ b/app/application/resources/views/admin/projects/documentation-versions/index.blade.php @@ -1,5 +1,6 @@ @section('meta_title', __('admin-sections.Documentation version')) @section('h1', __('admin-sections.Project') . ': ' . $project->name) +@section('breadcrumbs', Breadcrumbs::render('admin.projects.documentation-versions.index', $project)) @include('admin.projects.documentation-versions._top')
    @@ -41,7 +42,7 @@ @endif - + diff --git a/app/application/resources/views/admin/projects/documentation-versions/show.blade.php b/app/application/resources/views/admin/projects/documentation-versions/show.blade.php index 6a1dc09..e8c3824 100644 --- a/app/application/resources/views/admin/projects/documentation-versions/show.blade.php +++ b/app/application/resources/views/admin/projects/documentation-versions/show.blade.php @@ -1,5 +1,10 @@ @section('meta_title', __('admin-sections.Documentation version')) @section('h1', __('admin-sections.Project') . ': ' . $project->name) +@if(!empty($category)) + @section('breadcrumbs', Breadcrumbs::render('admin.projects.documentation-versions.show.category', $project, $version, $category, $categoryPath ?? null)) +@else + @section('breadcrumbs', Breadcrumbs::render('admin.projects.documentation-versions.show', $project, $version)) +@endif @php $categoryId = null; if (!empty($category) && $category->id) { From db0dce27b4444db25ec9deb4bec81ead65c2c621 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 25 Jul 2026 01:25:31 +0500 Subject: [PATCH 30/41] Add breadcrumbs to admin project documentation views - Implement breadcrumbs for `index`, `create`, and `edit` views. - Add conditional logic to handle optional category paths in `create` and `edit` views. --- .../documentation-versions/documentations/create.blade.php | 5 +++++ .../documentation-versions/documentations/edit.blade.php | 5 +++++ .../documentation-versions/documentations/index.blade.php | 1 + 3 files changed, 11 insertions(+) diff --git a/app/application/resources/views/admin/projects/documentation-versions/documentations/create.blade.php b/app/application/resources/views/admin/projects/documentation-versions/documentations/create.blade.php index 405c0ad..af2dc10 100644 --- a/app/application/resources/views/admin/projects/documentation-versions/documentations/create.blade.php +++ b/app/application/resources/views/admin/projects/documentation-versions/documentations/create.blade.php @@ -2,6 +2,11 @@ @section('h1') {{ __('admin-sections.Project') . ': ' . $project->name . ' (' . $version->title . ')' }} @endsection +@if(!empty($categoryPath)) + @section('breadcrumbs', Breadcrumbs::render('admin.projects.documentation-versions.category-path.documentations.create', $project, $version, $categoryPath)) +@else + @section('breadcrumbs', Breadcrumbs::render('admin.projects.documentation-versions.documentations.create', $project, $version)) +@endif @include('admin.projects.documentation-versions.documentations._top')
    diff --git a/app/application/resources/views/admin/projects/documentation-versions/documentations/edit.blade.php b/app/application/resources/views/admin/projects/documentation-versions/documentations/edit.blade.php index e715251..d07909c 100644 --- a/app/application/resources/views/admin/projects/documentation-versions/documentations/edit.blade.php +++ b/app/application/resources/views/admin/projects/documentation-versions/documentations/edit.blade.php @@ -2,6 +2,11 @@ @section('h1') {{ __('admin-sections.Project') . ': ' . $project->name . ' (' . $version->title . ')' }} @endsection +@if(!empty($categoryPath)) + @section('breadcrumbs', Breadcrumbs::render('admin.projects.documentation-versions.category-path.documentations.edit', $project, $version, $documentation, $categoryPath)) +@else + @section('breadcrumbs', Breadcrumbs::render('admin.projects.documentation-versions.documentations.edit', $project, $version, $documentation)) +@endif @include('admin.projects.documentation-versions.documentations._top')
    diff --git a/app/application/resources/views/admin/projects/documentation-versions/documentations/index.blade.php b/app/application/resources/views/admin/projects/documentation-versions/documentations/index.blade.php index 61f2302..2c56b39 100644 --- a/app/application/resources/views/admin/projects/documentation-versions/documentations/index.blade.php +++ b/app/application/resources/views/admin/projects/documentation-versions/documentations/index.blade.php @@ -2,6 +2,7 @@ @section('h1') {{ __('admin-sections.Project') . ': ' . $project->name . ' (' . $version->title . ')' }} @endsection +@section('breadcrumbs', Breadcrumbs::render('admin.projects.documentation-versions.documentations.index', $project, $version)) @include('admin.projects.documentation-versions.documentations._top')
    From 5f0958f42ee57c1f87b6ba3ee935dc18d64a09bd Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 25 Jul 2026 01:25:49 +0500 Subject: [PATCH 31/41] Add breadcrumbs to admin documentation categories views - Implement breadcrumbs for `index`, `create`, and `edit` views. - Add conditional logic for category paths in `create` and `edit` views. --- .../documentation-categories/create.blade.php | 5 +++++ .../documentation-categories/edit.blade.php | 5 +++++ .../documentation-categories/index.blade.php | 1 + 3 files changed, 11 insertions(+) diff --git a/app/application/resources/views/admin/projects/documentation-versions/documentation-categories/create.blade.php b/app/application/resources/views/admin/projects/documentation-versions/documentation-categories/create.blade.php index c7869f1..32915a5 100644 --- a/app/application/resources/views/admin/projects/documentation-versions/documentation-categories/create.blade.php +++ b/app/application/resources/views/admin/projects/documentation-versions/documentation-categories/create.blade.php @@ -2,6 +2,11 @@ @section('h1') {{ __('admin-sections.Project') . ': ' . $project->name . ' (' . $version->title . ')' }} @endsection +@if(!empty($categoryPath)) + @section('breadcrumbs', Breadcrumbs::render('admin.projects.documentation-versions.category-path.categories.create', $project, $version, $categoryPath)) +@else + @section('breadcrumbs', Breadcrumbs::render('admin.projects.documentation-versions.categories.create', $project, $version)) +@endif @include('admin.projects.documentation-versions.documentation-categories._top')
    diff --git a/app/application/resources/views/admin/projects/documentation-versions/documentation-categories/edit.blade.php b/app/application/resources/views/admin/projects/documentation-versions/documentation-categories/edit.blade.php index a28aa20..debe66b 100644 --- a/app/application/resources/views/admin/projects/documentation-versions/documentation-categories/edit.blade.php +++ b/app/application/resources/views/admin/projects/documentation-versions/documentation-categories/edit.blade.php @@ -2,6 +2,11 @@ @section('h1') {{ __('admin-sections.Project') . ': ' . $project->name . ' (' . $version->title . ')' }} @endsection +@if(!empty($categoryPath)) + @section('breadcrumbs', Breadcrumbs::render('admin.projects.documentation-versions.category-path.categories.edit', $project, $version, $category, $categoryPath)) +@else + @section('breadcrumbs', Breadcrumbs::render('admin.projects.documentation-versions.categories.edit', $project, $version, $category)) +@endif @include('admin.projects.documentation-versions.documentation-categories._top')
    diff --git a/app/application/resources/views/admin/projects/documentation-versions/documentation-categories/index.blade.php b/app/application/resources/views/admin/projects/documentation-versions/documentation-categories/index.blade.php index 3757257..ccd2458 100644 --- a/app/application/resources/views/admin/projects/documentation-versions/documentation-categories/index.blade.php +++ b/app/application/resources/views/admin/projects/documentation-versions/documentation-categories/index.blade.php @@ -2,6 +2,7 @@ @section('h1') {{ __('admin-sections.Project') . ': ' . $project->name . ' (' . $version->title . ')' }} @endsection +@section('breadcrumbs', Breadcrumbs::render('admin.projects.documentation-versions.categories.index', $project, $version)) @include('admin.projects.documentation-versions.documentation-categories._top')
    From 7a1b9ecda122b25b65f033dacd818fc43fa95f67 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 25 Jul 2026 02:09:15 +0500 Subject: [PATCH 32/41] Refactor table structure in admin documentation versions view - Consolidate category and documentation sections under a single table. - Adjust layout for clarity and consistency. --- .../documentation-versions/show.blade.php | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/app/application/resources/views/admin/projects/documentation-versions/show.blade.php b/app/application/resources/views/admin/projects/documentation-versions/show.blade.php index e8c3824..74b2548 100644 --- a/app/application/resources/views/admin/projects/documentation-versions/show.blade.php +++ b/app/application/resources/views/admin/projects/documentation-versions/show.blade.php @@ -39,9 +39,7 @@ - - - + @@ -51,6 +49,9 @@ @forelse($categories as $category) + @endforelse - - - - - - - - - - - @forelse($documentations as $documentation) + From 0922ee217fd20eebd8609d4f8d44bd98064207e1 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 25 Jul 2026 20:37:33 +0500 Subject: [PATCH 33/41] Adjust breadcrumb icon styling in SCSS --- .../resources/volt/scss/volt/components/_breadcrumb.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/application/resources/volt/scss/volt/components/_breadcrumb.scss b/app/application/resources/volt/scss/volt/components/_breadcrumb.scss index 38e72d6..e06d580 100755 --- a/app/application/resources/volt/scss/volt/components/_breadcrumb.scss +++ b/app/application/resources/volt/scss/volt/components/_breadcrumb.scss @@ -24,6 +24,10 @@ padding: 0; } +.breadcrumb .icon { + margin-top: -2px; +} + @each $color, $value in $theme-colors { .breadcrumb-#{$color} { background: $value; From c3b9fa56fb71084b739d9cf8d63af944ec9ed8e5 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 25 Jul 2026 22:33:43 +0500 Subject: [PATCH 34/41] Add breadcrumbs and dynamic head stack to site layout - Inject breadcrumbs into the main site layout for improved navigation. - Add `@stack('head')` for dynamic head content integration. --- app/application/resources/views/layout/site.blade.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/application/resources/views/layout/site.blade.php b/app/application/resources/views/layout/site.blade.php index 73c3f1a..d5767a3 100644 --- a/app/application/resources/views/layout/site.blade.php +++ b/app/application/resources/views/layout/site.blade.php @@ -9,6 +9,8 @@ @vite('resources/site/scss/app.scss') + + @stack('head')
    @@ -46,6 +48,7 @@
    + @yield('breadcrumbs', '') @if($attributes->has('documentationVersion')) @endif From 031aa196d1768c00c0960bd3d2c9a835bb094c2a Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 25 Jul 2026 22:34:21 +0500 Subject: [PATCH 35/41] Add breadcrumbs to site feedback page - Extend breadcrumb definitions for `home` and `feedbacks.index`. - Integrate breadcrumbs into the feedback page layout. - Add support for dynamic JSON-LD breadcrumb rendering. --- app/application/breadcrumbs/site.php | 12 ++++++++++-- .../resources/views/site/feedback/index.blade.php | 5 ++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/app/application/breadcrumbs/site.php b/app/application/breadcrumbs/site.php index 0e442dc..c875f79 100644 --- a/app/application/breadcrumbs/site.php +++ b/app/application/breadcrumbs/site.php @@ -1,8 +1,16 @@ push($websiteTranslations->translate('Home'), route('home')); +Breadcrumbs::for('home', function ($trail, Project $project, WebsiteTranslations $websiteTranslations) { + $trail->push($websiteTranslations->translate('Home'), ProjectSection::Home->url($project, $websiteTranslations->getLanguage())); +}); + +Breadcrumbs::for('feedbacks.index', function ($trail, Project $project, WebsiteTranslations $websiteTranslations) { + $trail->parent('home', $project, $websiteTranslations); + + $trail->push($websiteTranslations->translate('site.Feedback'), ProjectSection::Feedback->url($project, $websiteTranslations->getLanguage())); }); diff --git a/app/application/resources/views/site/feedback/index.blade.php b/app/application/resources/views/site/feedback/index.blade.php index 8bc14f0..0db8239 100644 --- a/app/application/resources/views/site/feedback/index.blade.php +++ b/app/application/resources/views/site/feedback/index.blade.php @@ -1,6 +1,9 @@ @section('meta_title', $websiteTranslations->translate('site.Feedback')) @section('h1', $websiteTranslations->translate('site.Feedback')) - +@section('breadcrumbs', Breadcrumbs::render('feedbacks.index', $project, $websiteTranslations)) +@push('head') + {{ Breadcrumbs::view('breadcrumbs::json-ld', 'feedbacks.index', $project, $websiteTranslations) }} +@endpush @if(Session::has('success') !== true) From 2a26008e8704eb2c9b51d952d117220d3334b2c5 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 25 Jul 2026 23:09:49 +0500 Subject: [PATCH 36/41] Add breadcrumbs to site views - Introduce breadcrumb SCSS styles and integrate `breadcrumbs/site.blade.php`. - Update SCSS imports to include new breadcrumb styles. --- app/application/resources/site/scss/app.scss | 1 + .../resources/site/scss/breadcrumb.scss | 45 +++++++++++++++++++ .../views/breadcrumbs/site.blade.php | 13 ++++++ 3 files changed, 59 insertions(+) create mode 100644 app/application/resources/site/scss/breadcrumb.scss create mode 100644 app/application/resources/views/breadcrumbs/site.blade.php diff --git a/app/application/resources/site/scss/app.scss b/app/application/resources/site/scss/app.scss index c1e24ad..dc85f25 100644 --- a/app/application/resources/site/scss/app.scss +++ b/app/application/resources/site/scss/app.scss @@ -1,6 +1,7 @@ @import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,700;1,400;1,700&display=swap'); @import "reset"; @import "forms"; +@import "breadcrumb"; html, body { diff --git a/app/application/resources/site/scss/breadcrumb.scss b/app/application/resources/site/scss/breadcrumb.scss new file mode 100644 index 0000000..20aeaf6 --- /dev/null +++ b/app/application/resources/site/scss/breadcrumb.scss @@ -0,0 +1,45 @@ +.breadcrumb-container { + display: flex; + flex-wrap: wrap; + padding: .5rem 1rem; + margin-bottom: 1rem; + list-style: none; + background-color: #eee; + border-radius: .5rem; +} + +.breadcrumb-item+.breadcrumb-item { + padding-left: .5rem +} + +.breadcrumb-item+.breadcrumb-item:before { + float: left; + padding-right: .5rem; + color: #4b5563; + content: "/"; +} + +.breadcrumb-item.active { + color: #4b5563; + font-weight: 400; +} + +.breadcrumb-item { + font-size: .75rem +} + +.breadcrumb-item,.breadcrumb-item a { + color: #374151; + font-weight: 400; + font-size: .75rem; +} +.breadcrumb-item a:hover { + color: #00040b; + text-decoration: none; +} + +@media (min-width: 576px) { + .breadcrumb-item,.breadcrumb-item a { + font-size:.875rem + } +} diff --git a/app/application/resources/views/breadcrumbs/site.blade.php b/app/application/resources/views/breadcrumbs/site.blade.php new file mode 100644 index 0000000..72f74c1 --- /dev/null +++ b/app/application/resources/views/breadcrumbs/site.blade.php @@ -0,0 +1,13 @@ +@unless ($breadcrumbs->isEmpty()) + +@endunless From 1ee453dcfe0efdb9e41a7bcf53fa7a64c2376ed3 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 25 Jul 2026 23:10:18 +0500 Subject: [PATCH 37/41] Add category path support to site documentation views - Introduce `categoryPath` for `DocumentationService` methods. - Resolve category paths dynamically based on parent or category ID. --- .../app/Services/Site/DocumentationService.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/application/app/Services/Site/DocumentationService.php b/app/application/app/Services/Site/DocumentationService.php index 5cbe96f..fa89288 100644 --- a/app/application/app/Services/Site/DocumentationService.php +++ b/app/application/app/Services/Site/DocumentationService.php @@ -77,10 +77,16 @@ final class DocumentationService extends Service return $this->errFobidden(__('Access is denied')); } + $categoryPath = null; + if ($category->parent_id) { + $categoryPath = $this->documentationCategoryRepository->getPathFromRoot($category->parent_id, $documentation->getWebsiteTranslations()->getLanguage()); + } + $data = array_merge($documentation->toArray(), [ 'category' => $category, 'categories' => $this->getCategories($documentation, $category->id), 'documentations' => $this->getDocumentations($documentation, $category->id), + 'categoryPath' => $categoryPath, ]); return $this->resultSitePage($documentation->getProject(), $documentation->getWebsiteTranslations(), $data, \is_null($category->content?->title)); } @@ -98,8 +104,14 @@ final class DocumentationService extends Service return $this->errFobidden(__('Access is denied')); } + $categoryPath = null; + if ($document->category_id) { + $categoryPath = $this->documentationCategoryRepository->getPathFromRoot($document->category_id, $documentation->getWebsiteTranslations()->getLanguage()); + } + $data = array_merge($documentation->toArray(), [ 'documentation' => $document, + 'categoryPath' => $categoryPath, ]); return $this->resultSitePage($documentation->getProject(), $documentation->getWebsiteTranslations(), $data, \is_null($document->content?->title)); } From 136451dc5acedc686379e546b094ce28942e68fe Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sat, 25 Jul 2026 23:10:37 +0500 Subject: [PATCH 38/41] Add breadcrumbs to site documentation views - Integrate breadcrumb rendering across documentation views (`index`, `category`, `view`, and `no-default-version`). - Add support for dynamic JSON-LD breadcrumb generation. --- app/application/breadcrumbs/site.php | 44 +++++++++++++++++++ .../projects/documentation/category.blade.php | 5 ++- .../projects/documentation/index.blade.php | 5 ++- .../no-default-version.blade.php | 5 ++- .../projects/documentation/view.blade.php | 5 ++- 5 files changed, 60 insertions(+), 4 deletions(-) diff --git a/app/application/breadcrumbs/site.php b/app/application/breadcrumbs/site.php index c875f79..0648a3c 100644 --- a/app/application/breadcrumbs/site.php +++ b/app/application/breadcrumbs/site.php @@ -1,9 +1,13 @@ push($websiteTranslations->translate('Home'), ProjectSection::Home->url($project, $websiteTranslations->getLanguage())); @@ -14,3 +18,43 @@ Breadcrumbs::for('feedbacks.index', function ($trail, Project $project, WebsiteT $trail->push($websiteTranslations->translate('site.Feedback'), ProjectSection::Feedback->url($project, $websiteTranslations->getLanguage())); }); + +// Documentation +Breadcrumbs::for('documentation.no-default-version', function ($trail, Project $project, WebsiteTranslations $websiteTranslations) { + $trail->parent('home', $project, $websiteTranslations); + + $trail->push($websiteTranslations->translate('site.Documentation'), ProjectSection::Documentation->url($project, $websiteTranslations->getLanguage())); +}); + +Breadcrumbs::for('documentation.index', function ($trail, Project $project, WebsiteTranslations $websiteTranslations, DocumentationVersion $version) { + $trail->parent('home', $project, $websiteTranslations); + + $trail->push($websiteTranslations->translate('site.Documentation'), ProjectSection::DocumentationVersion->url($project, $websiteTranslations->getLanguage(), ['version' => $version->slug])); +}); + +Breadcrumbs::for('documentation.category-path', function ($trail, Project $project, WebsiteTranslations $websiteTranslations, DocumentationVersion $version, Collection $categoryPath) { + $trail->parent('documentation.index', $project, $websiteTranslations, $version); + foreach ($categoryPath as $pathItem) { + $trail->push( + $pathItem->content->title, + ProjectSection::DocumentationCategory->url($project, $websiteTranslations->getLanguage(), ['version' => $version->slug, 'slug' => $pathItem->slug]) + ); + } +}); + +Breadcrumbs::for('documentation.category', function ($trail, Project $project, WebsiteTranslations $websiteTranslations, DocumentationVersion $version, DocumentationCategory $category, ?Collection $categoryPath) { + $trail->parent('documentation.category-path', $project, $websiteTranslations, $version, $categoryPath ?? collect()); + $trail->push( + $category->content->title, + ProjectSection::DocumentationCategory->url($project, $websiteTranslations->getLanguage(), ['version' => $version->slug, 'slug' => $category->slug]) + ); +}); + +Breadcrumbs::for('documentation.view', function ($trail, Project $project, WebsiteTranslations $websiteTranslations, DocumentationVersion $version, Documentation $documentation, ?Collection $categoryPath) { + $trail->parent('documentation.category-path', $project, $websiteTranslations, $version, $categoryPath ?? collect()); + $trail->push( + $documentation->content->title, + ProjectSection::Documentation->url($project, $websiteTranslations->getLanguage(), ['version' => $version->slug, 'slug' => $documentation->slug]) + ); +}); +// End Documentation diff --git a/app/application/resources/views/site/projects/documentation/category.blade.php b/app/application/resources/views/site/projects/documentation/category.blade.php index 06e4931..bedb165 100644 --- a/app/application/resources/views/site/projects/documentation/category.blade.php +++ b/app/application/resources/views/site/projects/documentation/category.blade.php @@ -1,6 +1,9 @@ @section('meta_title', $category->content?->title . ' - ' . $project->name . ' ' . $version->title) @section('h1', $category->content?->title) - +@section('breadcrumbs', Breadcrumbs::render('documentation.category', $project, $websiteTranslations, $version, $category, $categoryPath)) +@push('head') + {{ Breadcrumbs::view('breadcrumbs::json-ld', 'documentation.category', $project, $websiteTranslations, $version, $category, $categoryPath) }} +@endpush @foreach($categories as $category) @continue(! $category->content?->title) diff --git a/app/application/resources/views/site/projects/documentation/index.blade.php b/app/application/resources/views/site/projects/documentation/index.blade.php index 9391de0..d134a1f 100644 --- a/app/application/resources/views/site/projects/documentation/index.blade.php +++ b/app/application/resources/views/site/projects/documentation/index.blade.php @@ -1,6 +1,9 @@ @section('meta_title', $websiteTranslations->translate('site.Documentation') . ' - ' . $project->name . ' ' . $version->title) @section('h1', $websiteTranslations->translate('site.Documentation')) - +@section('breadcrumbs', Breadcrumbs::render('documentation.index', $project, $websiteTranslations, $version)) +@push('head') + {{ Breadcrumbs::view('breadcrumbs::json-ld', 'documentation.index', $project, $websiteTranslations, $version) }} +@endpush @foreach($categories as $category) @continue(! $category->content?->title) diff --git a/app/application/resources/views/site/projects/documentation/no-default-version.blade.php b/app/application/resources/views/site/projects/documentation/no-default-version.blade.php index 464dbae..81a3211 100644 --- a/app/application/resources/views/site/projects/documentation/no-default-version.blade.php +++ b/app/application/resources/views/site/projects/documentation/no-default-version.blade.php @@ -1,6 +1,9 @@ @section('meta_title', $websiteTranslations->translate('site.Documentation not created')) @section('h1', $websiteTranslations->translate('site.Documentation not created')) - +@section('breadcrumbs', Breadcrumbs::render('documentation.no-default-version', $project, $websiteTranslations)) +@push('head') + {{ Breadcrumbs::view('breadcrumbs::json-ld', 'documentation.no-default-version', $project, $websiteTranslations) }} +@endpush diff --git a/app/application/resources/views/site/projects/documentation/view.blade.php b/app/application/resources/views/site/projects/documentation/view.blade.php index ddf6c76..0274aec 100644 --- a/app/application/resources/views/site/projects/documentation/view.blade.php +++ b/app/application/resources/views/site/projects/documentation/view.blade.php @@ -1,6 +1,9 @@ @section('meta_title', $documentation->content?->title . ' - ' . $project->name . ' ' . $version->title) @section('h1', $documentation->content?->title) - +@section('breadcrumbs', Breadcrumbs::render('documentation.view', $project, $websiteTranslations, $version, $documentation, $categoryPath)) +@push('head') + {{ Breadcrumbs::view('breadcrumbs::json-ld', 'documentation.view', $project, $websiteTranslations, $version, $documentation, $categoryPath) }} +@endpush
    {!! $documentation->content->content !!}
    @push('scripts') From 059d2b9d6624ac02b4ccea2226ce339e4bb91e90 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sun, 26 Jul 2026 02:19:29 +0500 Subject: [PATCH 39/41] Consolidate PHP and Nginx Dockerfiles - Merge separate PHP and Nginx Dockerfiles into a unified structure under `app/docker/Dockerfile`. - Simplify build stages and streamline multi-service configuration for production, development, and utility containers. - Remove outdated individual Dockerfiles (`php/Dockerfile` and `web/Dockerfile`). --- app/docker/{php => }/Dockerfile | 58 ++++++++++++++++++++++++--------- app/docker/web/Dockerfile | 31 ------------------ 2 files changed, 43 insertions(+), 46 deletions(-) rename app/docker/{php => }/Dockerfile (69%) delete mode 100644 app/docker/web/Dockerfile diff --git a/app/docker/php/Dockerfile b/app/docker/Dockerfile similarity index 69% rename from app/docker/php/Dockerfile rename to app/docker/Dockerfile index 61efbcd..1c771fa 100644 --- a/app/docker/php/Dockerfile +++ b/app/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/php:8.5-fpm-alpine3.23 AS build +FROM docker.io/php:8.5-fpm-alpine3.23 AS php RUN apk --no-cache add pcre2 libbz2 libpng libwebp libjpeg-turbo icu-libs freetype oniguruma libzip jq \ && apk add --no-cache --virtual .phpize-deps icu-dev libpng-dev bzip2-dev libwebp-dev libjpeg-turbo-dev freetype-dev oniguruma-dev libzip-dev pcre2-dev ${PHPIZE_DEPS} \ @@ -29,20 +29,37 @@ RUN apk --no-cache add pcre2 libbz2 libpng libwebp libjpeg-turbo icu-libs freety COPY docker/php/conf/app-fpm.conf /usr/local/etc/php-fpm.d/app-fpm.conf -FROM build AS app_build_for_production +FROM docker.io/nginx:1.31-alpine AS nginx + +COPY docker/web/conf/default.conf.template /etc/nginx/templates/default.conf.template +COPY docker/web/conf/fastcgi_params /etc/nginx/fastcgi_params +COPY docker/web/start.sh /usr/local/bin/start + +RUN chmod 755 /usr/local/bin/start + +FROM nginx AS web_build_for_production + +WORKDIR /home/app +COPY application /home/app + +RUN apk --no-cache add git nodejs npm \ + && npm install && npm run build \ + && rm -rf /home/app/node_modules + +FROM php AS app_build_for_production WORKDIR /home/app COPY application /home/app -RUN apk --no-cache add git \ +RUN apk --no-cache add git \ && curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \ && composer install --optimize-autoloader --no-dev \ - && rm -rf /home/app/.env + && rm -f /home/app/.env - -FROM build AS production +FROM php AS app_production COPY --from=app_build_for_production /home/app /var/www/html +COPY --from=web_build_for_production /home/app/public/build/ /var/www/html/public/build COPY docker/php/start.sh /usr/local/bin/start WORKDIR /var/www/html @@ -57,8 +74,18 @@ USER www-data EXPOSE 9000 CMD ["/usr/local/bin/start"] +FROM nginx AS web_production -FROM build AS develop +COPY --from=web_build_for_production /home/app/public /usr/share/nginx/html +RUN rm -f /usr/share/nginx/html/50x.html /usr/share/nginx/html/index.php /usr/share/nginx/html/.htaccess + +WORKDIR /usr/share/nginx/html + +EXPOSE 80 +CMD ["/usr/local/bin/start"] + +# develop +FROM php AS app_develop WORKDIR /var/www/html @@ -75,15 +102,12 @@ USER www-data EXPOSE 9000 CMD ["/usr/local/bin/start"] - - -FROM build AS artisan +FROM php AS artisan WORKDIR /var/www/html STOPSIGNAL SIGTERM ENTRYPOINT ["php", "/var/www/html/artisan"] - -FROM build AS composer +FROM php AS composer WORKDIR /var/www/html STOPSIGNAL SIGTERM RUN apk --no-cache add git \ @@ -91,11 +115,15 @@ RUN apk --no-cache add git \ RUN mkdir "/.composer" && chmod -R 0777 "/.composer" ENTRYPOINT ["composer"] - - -FROM build AS npm +FROM php AS npm RUN mkdir "/.npm" && chmod -R 0777 "/.npm" WORKDIR /var/www/html STOPSIGNAL SIGTERM RUN apk --no-cache add nodejs npm ENTRYPOINT ["npm"] + +FROM nginx AS web_develop +WORKDIR /usr/share/nginx/html + +EXPOSE 80 +CMD ["/usr/local/bin/start"] diff --git a/app/docker/web/Dockerfile b/app/docker/web/Dockerfile deleted file mode 100644 index 8c55a71..0000000 --- a/app/docker/web/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -FROM docker.io/nginx:1.31-alpine AS build - -COPY docker/web/conf/default.conf.template /etc/nginx/templates/default.conf.template -COPY docker/web/conf/fastcgi_params /etc/nginx/fastcgi_params -COPY docker/web/start.sh /usr/local/bin/start - -RUN chmod 755 /usr/local/bin/start - -FROM build AS app_build_for_production - -WORKDIR /home/app -COPY application /home/app - -RUN apk --no-cache add git nodejs npm \ - && npm install && npm run build \ - && rm -rf /home/app/node_modules - -FROM build AS production - -COPY --from=app_build_for_production /home/app/public /usr/share/nginx/html - -WORKDIR /usr/share/nginx/html - -EXPOSE 80 -CMD ["/usr/local/bin/start"] - -FROM build AS develop -WORKDIR /usr/share/nginx/html - -EXPOSE 80 -CMD ["/usr/local/bin/start"] From 0fc181bf12682a342952b59c5dced961fbf31d14 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sun, 26 Jul 2026 02:20:13 +0500 Subject: [PATCH 40/41] Update Docker Compose files to use consolidated Dockerfile - Replace references to separate `php/Dockerfile` and `web/Dockerfile` with the unified `docker/Dockerfile`. - Adjust target stages across services for development and production environments. --- docker-compose-prod.yml | 17 +++++++++-------- docker-compose.yml | 22 +++++++++++----------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/docker-compose-prod.yml b/docker-compose-prod.yml index 958f4df..d2c714d 100644 --- a/docker-compose-prod.yml +++ b/docker-compose-prod.yml @@ -3,8 +3,9 @@ services: web: build: context: app - dockerfile: docker/web/Dockerfile - target: DEVELOP + dockerfile: docker/Dockerfile + target: WEB_PRODUCTION + # restart: always depends_on: - app ports: @@ -21,8 +22,8 @@ services: app: build: context: app - dockerfile: docker/php/Dockerfile - target: PRODUCTION + dockerfile: docker/Dockerfile + target: APP_PRODUCTION # restart: always depends_on: - db @@ -37,8 +38,8 @@ services: queue: build: context: app - dockerfile: docker/php/Dockerfile - target: PRODUCTION + dockerfile: docker/Dockerfile + target: APP_PRODUCTION # restart: always depends_on: - app @@ -52,8 +53,8 @@ services: scheduler: build: context: app - dockerfile: docker/php/Dockerfile - target: PRODUCTION + dockerfile: docker/Dockerfile + target: APP_PRODUCTION # restart: always depends_on: - app diff --git a/docker-compose.yml b/docker-compose.yml index b756861..448140d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,8 +3,8 @@ services: web: build: context: app - dockerfile: docker/web/Dockerfile - target: DEVELOP + dockerfile: docker/Dockerfile + target: WEB_DEVELOP depends_on: - app ports: @@ -21,8 +21,8 @@ services: app: build: context: app - dockerfile: docker/php/Dockerfile - target: DEVELOP + dockerfile: docker/Dockerfile + target: APP_DEVELOP depends_on: - db - app-redis @@ -36,8 +36,8 @@ services: queue: build: context: app - dockerfile: docker/php/Dockerfile - target: DEVELOP + dockerfile: docker/Dockerfile + target: APP_DEVELOP depends_on: - db - app @@ -50,8 +50,8 @@ services: scheduler: build: context: app - dockerfile: docker/php/Dockerfile - target: DEVELOP + dockerfile: docker/Dockerfile + target: APP_DEVELOP depends_on: - db - app @@ -146,7 +146,7 @@ services: artisan: build: context: app - dockerfile: docker/php/Dockerfile + dockerfile: docker/Dockerfile target: ARTISAN user: "${UID}:${GID}" volumes: @@ -155,7 +155,7 @@ services: composer: build: context: app - dockerfile: docker/php/Dockerfile + dockerfile: docker/Dockerfile target: COMPOSER user: "${UID}:${GID}" volumes: @@ -164,7 +164,7 @@ services: npm: build: context: app - dockerfile: docker/php/Dockerfile + dockerfile: docker/Dockerfile target: NPM user: "${UID}:${GID}" volumes: From 4cdf11be3d5380710dcc3a03affd7093bc7a9dc1 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sun, 26 Jul 2026 02:20:36 +0500 Subject: [PATCH 41/41] Update production Docker Compose to switch images to MDHub registry - Replace Docker Hub image references with MDHub equivalents across all services. - Update environment variables and volume bindings for web and app services. - Add production-specific environment variables (`APP_ENV`) for queue and scheduler. --- docker-compose-prod_docker-hub.yml | 32 ++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/docker-compose-prod_docker-hub.yml b/docker-compose-prod_docker-hub.yml index d918466..20daa69 100644 --- a/docker-compose-prod_docker-hub.yml +++ b/docker-compose-prod_docker-hub.yml @@ -1,15 +1,29 @@ #version: '3.7' services: + web: + # image: korelf/my-projects-website-web:0.7.0 # docker hub + image: docker.mdhub.kor-elf.net/kor-elf/my-projects-website-web:0.7.0 # MDHub + # restart: always + depends_on: + - app + ports: + - ${DOCKER_WEB_PORT}:80 + environment: + IS_GZIP: 0 +# FRONTEND_PORT: ${DOCKER_WEB_PORT} + BACKEND_HOST: app + BACKEND_PORT: 9000 + TRUSTED_PROXY_CIDR: 172.16.0.0/12 + volumes: + - ./app/storage/app/public:/usr/share/nginx/html/storage app: - # image: korelf/my-projects-website:0.6.0 # docker hub - image: docker.mdhub.kor-elf.net/kor-elf/my-projects-website:0.6.0 # MDHub + # image: korelf/my-projects-website-app:0.7.0 # docker hub + image: docker.mdhub.kor-elf.net/kor-elf/my-projects-website-app:0.7.0 # MDHub # restart: always depends_on: - db - app-redis - captcha-app - ports: - - ${DOCKER_APP_PORT}:9000 env_file: app/.env environment: CONTAINER_ROLE: app @@ -20,8 +34,8 @@ services: # - ./app/translate/authorized_key.json:/var/www/html/storage/translation_service/authorized_key.json queue: - # image: korelf/my-projects-website:0.6.0 # docker hub - image: docker.mdhub.kor-elf.net/kor-elf/my-projects-website:0.6.0 # MDHub + # image: korelf/my-projects-website-app:0.7.0 # docker hub + image: docker.mdhub.kor-elf.net/kor-elf/my-projects-website-app:0.7.0 # MDHub # restart: always depends_on: - app @@ -29,6 +43,7 @@ services: - app-redis environment: CONTAINER_ROLE: queue + APP_ENV: production env_file: app/.env volumes: - ./app/storage/app:/var/www/html/storage/app @@ -36,8 +51,8 @@ services: # - ./app/translate/authorized_key.json:/var/www/html/storage/translation_service/authorized_key.json scheduler: - # image: korelf/my-projects-website:0.6.0 # docker hub - image: docker.mdhub.kor-elf.net/kor-elf/my-projects-website:0.6.0 # MDHub + # image: korelf/my-projects-website-app:0.7.0 # docker hub + image: docker.mdhub.kor-elf.net/kor-elf/my-projects-website-app:0.7.0 # MDHub # restart: always depends_on: - app @@ -45,6 +60,7 @@ services: - app-redis environment: CONTAINER_ROLE: scheduler + APP_ENV: production env_file: app/.env volumes: - ./app/storage/app:/var/www/html/storage/app
    {{ __('admin-sections.Categories') }}
    {{ __('validation.attributes.type') }} {{ __('validation.attributes.title') }} {{ __('validation.attributes.slug') }} {{ __('validation.attributes.is_public') }}
    + {{ __('admin-sections.Categories') }} + @can('view', $category) @@ -100,19 +101,11 @@
    {{ __('admin-sections.Documentation') }}
    {{ __('validation.attributes.title') }}{{ __('validation.attributes.slug') }}{{ __('validation.attributes.is_public') }}
    + {{ __('admin-sections.Documentation') }} + {{ $documentation->content?->title }}