From 23667314ee9ad34330391ae3bfd83ceee1b609d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96ZT=C3=9CRK?= <76204082+iamsedatozturk@users.noreply.github.com> Date: Tue, 20 Oct 2026 13:53:21 +0300 Subject: [PATCH] =?UTF-8?q?changeLog=20sayfas=C4=B1na=20y=C3=B6nlendirme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +++- configs/deployment/README.md | 1 + configs/deployment/scripts/deploy/app.sh | 9 +++++++++ ui/README.md | 3 ++- ui/src/components/route/ProtectedRoute.tsx | 11 +++++++++++ ui/src/components/shared/PageModeBadge.tsx | 18 ++++++++++++++++++ ui/src/store/locale.model.ts | 7 ++++--- ui/src/utils/hooks/useAuth.ts | 16 +++------------- .../admin/listForm/edit/FormTabCommands.tsx | 4 ++-- .../listForm/edit/FormTabExtraFilters.tsx | 4 ++-- .../JsonRowOpDialogAnnotation.tsx | 4 ++-- .../JsonRowOpDialogSeries.tsx | 4 ++-- ui/src/views/form/shared/FormPageShell.tsx | 9 +++++---- ui/src/views/list/List.tsx | 5 +++-- ui/src/views/version/ChangeLog.tsx | 2 +- 15 files changed, 68 insertions(+), 33 deletions(-) create mode 100644 ui/src/components/shared/PageModeBadge.tsx diff --git a/README.md b/README.md index afd9de69..25691a54 100644 --- a/README.md +++ b/README.md @@ -2009,7 +2009,9 @@ konfigürasyonu ile üretilir. `ChangeLogStartupService` uygulama açılışında tabloyu bu dosyayla eşitler. Dosya imaja **gömülmez**: api build context'i içindeyken her yeni tag Dockerfile'daki `COPY . .` katmanını geçersiz kılıp imajı gereksiz yere yeniden derletiyordu; artık kod değişmediyse - imaj cache'ten gelir. Yeni sürüm için tag atıp deploy etmek yeterlidir. Notlar + imaj cache'ten gelir. Yeni sürüm için tag atıp deploy etmek yeterlidir: dosya değiştiyse + `app.sh` API'yi yeniden başlatır, çünkü imajı değişmeyen konteynere `up -d` dokunmaz ve + açılışta okunan dosya aksi hâlde tabloya hiç yansımazdı. Notlar **annotated** tag mesajından okunur (`git tag -a 1.2.0 -m "..."`), her satır bir madde. - **Tablo git tag listesinin aynasıdır:** yeni tag eklenir, mesajı düzeltilen tag güncellenir, git'ten silinen tag tablodan da (hard delete ile) kaldırılır. Bu yüzden `Sas_H_ChangeLog` diff --git a/configs/deployment/README.md b/configs/deployment/README.md index 6c2f8fff..d1f53b7a 100644 --- a/configs/deployment/README.md +++ b/configs/deployment/README.md @@ -224,6 +224,7 @@ Dev / Prod Ortam Makinalarında (imajları çeker, api ve ui konteynerlerini yer `~/sozsoft-platform/configs/deployment/scripts/9-dev-deploy.sh` (dev) `~/sozsoft-platform/configs/deployment/scripts/5-production-deploy.sh` (production) İkisi de `scripts/deploy/app.sh {env}` çağırır: konteynerler önceden durdurulmaz, `pull` → `up -d`; +`change-logs.json` değiştiyse (yeni tag) API ayrıca `restart` edilir, yoksa sürüm notu tabloya yansımaz; UI'nin eski sürüm chunk'ları `ui-assets` volume'unda kalır (bkz. `ui/README.md` › Konteyner). ### Migrator diff --git a/configs/deployment/scripts/deploy/app.sh b/configs/deployment/scripts/deploy/app.sh index 7ea51ab4..590de6be 100644 --- a/configs/deployment/scripts/deploy/app.sh +++ b/configs/deployment/scripts/deploy/app.sh @@ -12,6 +12,7 @@ git fetch --prune --prune-tags --tags --force && git pull # gerekmez). `bash ...` ile çağrılır: depo Windows'tan yönetildiği için # çalıştırma biti kaybolunca "Permission denied" verip notları sessizce düşürüyordu. RELEASE_NOTES=~/sozsoft-platform/configs/change-logs/change-logs.json +RELEASE_NOTES_BEFORE=$(md5sum "$RELEASE_NOTES" 2>/dev/null | cut -d' ' -f1) bash ~/sozsoft-platform/configs/change-logs/generate-change-logs.sh # Dosya yoksa API açılışta "change-logs.json bulunamadı" uyarısı verir ve @@ -30,3 +31,11 @@ COMPOSE="docker compose -f docker-compose-app.yml -f docker-compose-app.${ENV}.y $COMPOSE --profile api --profile ui pull $COMPOSE --profile api up -d --remove-orphans $COMPOSE --profile ui up -d --remove-orphans + +# API sürüm notlarını yalnızca açılışta okur. Yalnızca tag eklenen deploy'da imaj +# değişmediği için `up -d` konteynere dokunmaz ve yeni sürüm veritabanına hiç +# yazılmazdı; dosya değiştiyse API yeniden başlatılır. +if [ "$RELEASE_NOTES_BEFORE" != "$(md5sum "$RELEASE_NOTES" | cut -d' ' -f1)" ]; then + echo "change-logs.json değişti, API yeniden başlatılıyor." + $COMPOSE --profile api restart api +fi diff --git a/ui/README.md b/ui/README.md index 43807a52..514973f1 100644 --- a/ui/README.md +++ b/ui/README.md @@ -62,7 +62,8 @@ kökünde mount edilir), `installPrompt.ts` (PWA kurulum teklifi). bitene kadar kapanmaz; ilk kurulumda (controller yok) kapatılabilir. 4. **Sürüm numarası ve notlar backend'dedir** (`extraProperties.changeLogs`, `/api/app/change-log`); UI'da sürüm dosyası yoktur. Son görülen sürüm store'un `locale` - diliminde tutulur, giriş sonrası changelog yönlendirmesi oradan okunur. + diliminde tutulur; sürüm değişince yazılan bayrağı `ProtectedRoute` okur ve oturum açık + kullanıcıyı (login şartı olmadan) `/admin/changeLog`'a yönlendirir, ekran bayrağı tüketir. 5. **Runtime cache.** Hash'li dosyalar cache-first (`sozsoft-assets-v1`, aktivasyonda `warmup.json`'da olmayan eski chunk'lar silinir), DevExtreme temaları stale-while-revalidate, font/görsel cache-first. `/api`, `/connect`, `/swagger`, `/signalr`, `/hangfire` worker'a diff --git a/ui/src/components/route/ProtectedRoute.tsx b/ui/src/components/route/ProtectedRoute.tsx index 2f6fc786..1b8254a2 100644 --- a/ui/src/components/route/ProtectedRoute.tsx +++ b/ui/src/components/route/ProtectedRoute.tsx @@ -2,11 +2,14 @@ import appConfig from '@/proxy/configs/app.config' import { REDIRECT_URL_KEY } from '@/constants/app.constant' import { Navigate, Outlet, useLocation } from 'react-router-dom' import useAuth from '@/utils/hooks/useAuth' +import { useStoreState } from '@/store' +import { ROUTES_ENUM } from '@/routes/route.constant' const { unAuthenticatedEntryPath } = appConfig const ProtectedRoute = () => { const { authenticated } = useAuth() + const pendingChangelogVersion = useStoreState((state) => state.locale.pendingChangelogVersion) const location = useLocation() @@ -19,6 +22,14 @@ const ProtectedRoute = () => { ) } + // Yeni sürüm görüldüyse kullanıcı önce değişiklikleri okur. Kapı girişte değil + // burada: oturumu açık olan kullanıcı güncellemeyi yenileme ile alır ve bir daha + // login olmaz; yalnızca girişe bağlı bir yönlendirme onu hiç yakalamıyordu. + // Bayrak ChangeLog ekranında tüketilir. + if (pendingChangelogVersion && location.pathname !== ROUTES_ENUM.protected.admin.changeLog) { + return + } + return } diff --git a/ui/src/components/shared/PageModeBadge.tsx b/ui/src/components/shared/PageModeBadge.tsx new file mode 100644 index 00000000..a006c8c8 --- /dev/null +++ b/ui/src/components/shared/PageModeBadge.tsx @@ -0,0 +1,18 @@ +import { useConfig } from '@/components/ui/ConfigProvider' + +// Sayfa başlığındaki görünüm/mod rozeti. Badge'in .badge sınıfı katmansız CSS'te sabit kırmızıdır +// (bildirim sayacı) ve utility sınıfları ezemez; renkler tema renginden gelir, sınıflar +// tailwind/index.css'teki @source inline listesinde üretilir. +const PageModeBadge = ({ content }: { content: string }) => { + const { themeColor } = useConfig() + + return ( + + {content} + + ) +} + +export default PageModeBadge diff --git a/ui/src/store/locale.model.ts b/ui/src/store/locale.model.ts index 4a4b443b..7b2593e7 100644 --- a/ui/src/store/locale.model.ts +++ b/ui/src/store/locale.model.ts @@ -7,7 +7,7 @@ export interface LocaleStoreModel { currentLang: string currentUiVersion: string | undefined currentTenantName?: string - /** Dolu ise kullanıcı bir sonraki girişinde ChangeLog ekranına yönlendirilir. */ + /** Dolu ise oturum açık kullanıcı yönetim ekranlarına girerken ChangeLog'a yönlendirilir (`ProtectedRoute`). */ pendingChangelogVersion?: string } @@ -44,8 +44,9 @@ export const localeModel: LocaleModel = { * Backend'den gelen çalışan sürümü işler (application-configuration → * `extraProperties.changeLogs.uiVersion`). * - * Sürüm son görülenden farklıysa changelog bayrağı yazılır; kullanıcı bir - * sonraki girişinde ChangeLog ekranına yönlendirilir. Tetikleyici bilerek + * Sürüm son görülenden farklıysa changelog bayrağı yazılır; oturum açık kullanıcı + * yönetim ekranlarına ilk girişinde (`ProtectedRoute`) ChangeLog'a yönlendirilir — + * login şartı yoktur, güncellemeyi yenilemeyle alan açık oturum da yakalanır. Tetikleyici bilerek * bundle değil sürümdür: yalnızca yeni bir git tag'i eklendiğinde de kullanıcı * değişiklikleri görür. * diff --git a/ui/src/utils/hooks/useAuth.ts b/ui/src/utils/hooks/useAuth.ts index e015fbc2..1fd37e89 100644 --- a/ui/src/utils/hooks/useAuth.ts +++ b/ui/src/utils/hooks/useAuth.ts @@ -1,7 +1,7 @@ import { jwtDecode } from '@/utils/jwt' import { useNavigate } from 'react-router-dom' import useQuery from './useQuery' -import { store, useStoreActions, useStoreState } from '../../store/store' +import { useStoreActions, useStoreState } from '../../store/store' import { FailedSignInResponse, SignInCredential, @@ -13,7 +13,6 @@ import { isLoginSuccess } from '../../proxy/account/models' import { REDIRECT_URL_KEY } from '../../constants/app.constant' import appConfig from '../../proxy/configs/app.config' import { register } from '../../services/account.service' -import { ROUTES_ENUM } from '@/routes/route.constant' type Status = 'success' | 'failed' | 'error' @@ -68,17 +67,8 @@ function useAuth() { } = resp.data handleToken({ token, refreshToken, expiresIn }) - const redirectUrl = query.get(REDIRECT_URL_KEY) - // Uygulama yeni bir sürümle/deploy ile açıldıysa kullanıcı önce - // değişiklikleri görsün. Bayrak ChangeLog ekranında tüketilir. - // - // Değer hook state'inden değil doğrudan store'dan okunur: bayrak giriş - // sayfasındaki config yüklemesiyle yazılıyor, render kapanışı bayat - // kalabilir. - const target = store.getState().locale.pendingChangelogVersion - ? ROUTES_ENUM.protected.admin.changeLog - : (redirectUrl ?? appConfig.authenticatedEntryPath) - navigate(target) + // Bekleyen changelog yönlendirmesi ProtectedRoute'tadır; giriş hedefi değişmez. + navigate(query.get(REDIRECT_URL_KEY) ?? appConfig.authenticatedEntryPath) return { status: 'success', diff --git a/ui/src/views/admin/listForm/edit/FormTabCommands.tsx b/ui/src/views/admin/listForm/edit/FormTabCommands.tsx index 3aa2a92c..d49e08d1 100644 --- a/ui/src/views/admin/listForm/edit/FormTabCommands.tsx +++ b/ui/src/views/admin/listForm/edit/FormTabCommands.tsx @@ -5,7 +5,7 @@ import Td from '@/components/ui/Table/Td' import Th from '@/components/ui/Table/Th' import Tr from '@/components/ui/Table/Tr' import { ListFormEditTabs } from '@/proxy/admin/list-form/options' -import { useStoreState } from '@/store' +import { useListFormValues } from './listFormDraftContext' import { useLocalization } from '@/utils/hooks/useLocalization' import { useState } from 'react' import { FaEdit, FaFileMedical, FaTrash } from 'react-icons/fa' @@ -19,7 +19,7 @@ function FormTabCommands() { const [jsonRowOpModalData, setJsonRowOpModalData] = useState() const { translate } = useLocalization() - const initialValues = useStoreState((s) => s.admin.lists.values) + const initialValues = useListFormValues() if (!initialValues) { return null } diff --git a/ui/src/views/admin/listForm/edit/FormTabExtraFilters.tsx b/ui/src/views/admin/listForm/edit/FormTabExtraFilters.tsx index 78148c54..84140a66 100644 --- a/ui/src/views/admin/listForm/edit/FormTabExtraFilters.tsx +++ b/ui/src/views/admin/listForm/edit/FormTabExtraFilters.tsx @@ -6,7 +6,7 @@ import Td from '@/components/ui/Table/Td' import Th from '@/components/ui/Table/Th' import Tr from '@/components/ui/Table/Tr' import { ListFormEditTabs } from '@/proxy/admin/list-form/options' -import { useStoreState } from '@/store' +import { useListFormValues } from './listFormDraftContext' import { useLocalization } from '@/utils/hooks/useLocalization' import { useState } from 'react' import { FaEdit, FaFileMedical, FaTrash } from 'react-icons/fa' @@ -18,7 +18,7 @@ function FormTabExtraFilters(props: { listFormCode: string }) { const [jsonRowOpModalData, setJsonRowOpModalData] = useState() const { translate } = useLocalization() - const initialValues = useStoreState((s) => s.admin.lists.values) + const initialValues = useListFormValues() if (!initialValues) { return null } diff --git a/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogAnnotation.tsx b/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogAnnotation.tsx index 547c5623..5182ec91 100644 --- a/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogAnnotation.tsx +++ b/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogAnnotation.tsx @@ -1,7 +1,7 @@ import { Button, Checkbox, Dialog, FormItem, Input, Select, Tabs } from '@/components/ui' import { ListFormJsonRowDto } from '@/proxy/admin/list-form/models' import { SelectBoxOption } from '@/types/shared' -import { useStoreState } from '@/store' +import { useListFormValues } from '../listFormDraftContext' import { useLocalization } from '@/utils/hooks/useLocalization' import { Field, FieldProps, Form, Formik } from 'formik' import { Dispatch, SetStateAction } from 'react' @@ -39,7 +39,7 @@ function JsonRowOpDialogAnnotation({ const { translate } = useLocalization() const rows = useJsonRowOps() - const listFormValues = useStoreState((s) => s.admin.lists.values) + const listFormValues = useListFormValues() if (!listFormValues) { return null } diff --git a/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogSeries.tsx b/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogSeries.tsx index 37a1c60c..6f311b25 100644 --- a/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogSeries.tsx +++ b/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogSeries.tsx @@ -1,7 +1,7 @@ import { Button, Card, Checkbox, Dialog, FormItem, Input, Select } from '@/components/ui' import { ListFormJsonRowDto } from '@/proxy/admin/list-form/models' import { SelectBoxOption } from '@/types/shared' -import { useStoreState } from '@/store' +import { useListFormValues } from '../listFormDraftContext' import { useLocalization } from '@/utils/hooks/useLocalization' import { Field, FieldProps, Form, Formik } from 'formik' import { Dispatch, SetStateAction } from 'react' @@ -85,7 +85,7 @@ function JsonRowOpDialogSeries({ setIsOpen(false) } - const listFormValues = useStoreState((s) => s.admin.lists.values) + const listFormValues = useListFormValues() if (!listFormValues) { return null } diff --git a/ui/src/views/form/shared/FormPageShell.tsx b/ui/src/views/form/shared/FormPageShell.tsx index 980f2c40..776e1976 100644 --- a/ui/src/views/form/shared/FormPageShell.tsx +++ b/ui/src/views/form/shared/FormPageShell.tsx @@ -8,7 +8,8 @@ import type { ReactNode, RefObject } from 'react' import { useState } from 'react' import { FaInfoCircle } from 'react-icons/fa' import { Container, Loading } from '@/components/shared' -import { Badge, Dialog } from '@/components/ui' +import { Dialog } from '@/components/ui' +import PageModeBadge from '@/components/shared/PageModeBadge' import RichText from '@/components/shared/RichText' import type { GridDto } from '@/proxy/form/models' import { useCurrentMenuIcon } from '@/utils/hooks/useCurrentMenuIcon' @@ -121,14 +122,14 @@ export const FormPageShell = ({ type="button" onClick={() => setDescriptionOpen(true)} > -

{title}

+

{title}

) : ( -

{title}

+

{title}

)} ● - + )} diff --git a/ui/src/views/list/List.tsx b/ui/src/views/list/List.tsx index c166a834..b2ab41fd 100644 --- a/ui/src/views/list/List.tsx +++ b/ui/src/views/list/List.tsx @@ -12,7 +12,8 @@ import { useParams, useSearchParams } from 'react-router-dom' import classNames from 'classnames' import Container from '@/components/shared/Container' -import { Badge, Button, Dialog } from '@/components/ui' +import { Button, Dialog } from '@/components/ui' +import PageModeBadge from '@/components/shared/PageModeBadge' import RichText from '@/components/shared/RichText' import { useLocalization } from '@/utils/hooks/useLocalization' import { useCurrentMenuIcon } from '@/utils/hooks/useCurrentMenuIcon' @@ -245,7 +246,7 @@ const List: React.FC = ({ ) : (

{translate('::' + gridDto.gridOptions.title)}

)} - + {/* ======================= VIEW BUTTONS diff --git a/ui/src/views/version/ChangeLog.tsx b/ui/src/views/version/ChangeLog.tsx index c046e00d..ead6fc3b 100644 --- a/ui/src/views/version/ChangeLog.tsx +++ b/ui/src/views/version/ChangeLog.tsx @@ -89,7 +89,7 @@ const Changelog = () => { useEffect(() => { // Bekleyen changelog bayrağı burada tüketilir; kullanıcı değişiklikleri - // gördüğü için sonraki girişlerde tekrar yönlendirilmez. + // gördüğü için ProtectedRoute bir daha buraya yönlendirmez. clearPendingChangelog() void changeLogService