diff --git a/api/src/Kurs.Platform.DbMigrator/Seeds/SeederData.json b/api/src/Kurs.Platform.DbMigrator/Seeds/SeederData.json index 0f6ce311..26979817 100644 --- a/api/src/Kurs.Platform.DbMigrator/Seeds/SeederData.json +++ b/api/src/Kurs.Platform.DbMigrator/Seeds/SeederData.json @@ -7528,7 +7528,7 @@ { "key": "admin.changeLog", "path": "/admin/changeLog", - "componentPath": "@/views/docs/ChangeLog", + "componentPath": "@/views/version/ChangeLog", "routeType": "protected", "authority": [] }, diff --git a/api/src/Kurs.Platform.HttpApi.Host/appsettings.json b/api/src/Kurs.Platform.HttpApi.Host/appsettings.json index 730d26dc..d9c43bff 100644 --- a/api/src/Kurs.Platform.HttpApi.Host/appsettings.json +++ b/api/src/Kurs.Platform.HttpApi.Host/appsettings.json @@ -7,7 +7,7 @@ "AttachmentsPath": "C:\\Private\\Projects\\sozsoft\\configs\\mail-queue\\attachments", "CdnPath": "C:\\Private\\Projects\\sozsoft\\configs\\docker\\data\\cdn", "ImportPath": "C:\\Private\\Projects\\sozsoft\\configs\\docker\\data\\import", - "Version": "1.0.4" + "Version": "1.0.1" }, "ConnectionStrings": { "SqlServer": "Server=localhost;Database=KURS;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;TrustServerCertificate=True;", diff --git a/ui/.gitignore b/ui/.gitignore index a470a897..a33db1d1 100644 --- a/ui/.gitignore +++ b/ui/.gitignore @@ -9,6 +9,7 @@ lerna-debug.log* node_modules dist +dev-dist dist-ssr build *.local diff --git a/ui/dev-dist/sw.js b/ui/dev-dist/sw.js index 6bd1fe1f..9ebb4717 100644 --- a/ui/dev-dist/sw.js +++ b/ui/dev-dist/sw.js @@ -82,7 +82,7 @@ define(['./workbox-a959eb95'], (function (workbox) { 'use strict'; "revision": "3ca0b8505b4bec776b69afdba2768812" }, { "url": "/index.html", - "revision": "0.agtbclbpej8" + "revision": "0.jq60tbu0hgg" }], {}); workbox.cleanupOutdatedCaches(); workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("/index.html"), { diff --git a/ui/package.json b/ui/package.json index 8e272c87..eebdb31a 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,12 +1,12 @@ { "name": "kurs-platform-ui", "private": true, - "version": "1.0.4", + "version": "1.0.1", "elstarVersion": "2.1.6", "type": "module", "scripts": { "start": "vite", - "build": "vite build", + "build": "vite build && node scripts/write-version.js", "preview": "vite preview", "lint": "eslint . --ext .js,.jsx,.ts,.tsx,.json", "lint:fix": "npm run lint -- --fix", diff --git a/ui/public/version.json b/ui/public/version.json new file mode 100644 index 00000000..5fde58dd --- /dev/null +++ b/ui/public/version.json @@ -0,0 +1,35 @@ +{ + "commit": "6766d11", + "releases": [ + { + "version": "1.0.5", + "buildDate": "2025-09-19", + "changeLog": [ + "Form ekranındaki Butonlar güncellemeleri yapıldı", + "Edit Form ekranındaki Info butonu eklendi.", + "New Form ekranındaki Geri butonu eklendi." + ] + }, + { + "version": "1.0.4", + "buildDate": "2025-09-19", + "changeLog": [ + "Subformlar üzerinde extra filters ve Widget çalışmaları yapıldı." + ] + }, + { + "version": "1.0.3", + "buildDate": "2025-09-19", + "changeLog": [ + "Manage Grid üzerinde Extra filtre tanımlaması yapıldı." + ] + }, + { + "version": "1.0.2", + "buildDate": "2025-09-16", + "changeLog": [ + "Genel Static olan Url bilgileri kaldırıldı." + ] + } + ] +} \ No newline at end of file diff --git a/ui/scripts/write-version.js b/ui/scripts/write-version.js new file mode 100644 index 00000000..bd247a51 --- /dev/null +++ b/ui/scripts/write-version.js @@ -0,0 +1,46 @@ +import fs from "fs" +import { execSync } from "child_process" + +function safeExec(cmd) { + try { + return execSync(cmd, { stdio: ["pipe", "pipe", "ignore"] }).toString().trim() + } catch { + return null + } +} + +// Tüm tag isimlerini al +const rawTags = safeExec("git tag --list --sort=creatordate") + +if (!rawTags) { + console.log("> No git tags found, skipping version.json") + process.exit(0) +} + +const tags = rawTags + .split("\n") + .filter(Boolean) + .map((tag) => { + const date = safeExec(`git log -1 --format=%ad --date=short ${tag}`) + const messageRaw = safeExec(`git tag -l --format="%(contents)" ${tag}`) + + const changeLog = messageRaw + ? messageRaw.split("\n").map((s) => s.trim()).filter(Boolean) + : [] + + return { + version: tag.replace(/^v/, ""), // v1.0.5 → 1.0.5 + buildDate: date, + changeLog + } + }) + +const commit = safeExec("git rev-parse --short HEAD") + +const versionInfo = { + commit, + releases: tags.reverse() +} + +fs.writeFileSync("public/version.json", JSON.stringify(versionInfo, null, 2)) +console.log("> Version file written to public/version.json:", versionInfo) diff --git a/ui/src/components/UpdateNotifier.tsx b/ui/src/components/UpdateNotifier.tsx deleted file mode 100644 index 219087c0..00000000 --- a/ui/src/components/UpdateNotifier.tsx +++ /dev/null @@ -1,57 +0,0 @@ -// src/components/UpdateNotifier.jsx -import { useState, useEffect } from 'react' - -const UpdateNotifier = () => { - const [updateAvailable, setUpdateAvailable] = useState(false) - - useEffect(() => { - if ('serviceWorker' in navigator) { - const checkUpdate = () => { - navigator.serviceWorker.getRegistration().then(registration => { - if (registration) { - registration.addEventListener('updatefound', () => { - setUpdateAvailable(true) - }) - } - }) - } - - checkUpdate() - const interval = setInterval(checkUpdate, 30000) // 30 saniyede bir kontrol - return () => clearInterval(interval) - } - }, []) - - const handleUpdate = () => { - window.location.reload() - } - - if (!updateAvailable) return null - - return ( -
-

Yeni güncelleme mevcut!

- -
- ) -} - -export default UpdateNotifier \ No newline at end of file diff --git a/ui/src/components/layouts/Layouts.tsx b/ui/src/components/layouts/Layouts.tsx index 199de8e6..278c74dd 100644 --- a/ui/src/components/layouts/Layouts.tsx +++ b/ui/src/components/layouts/Layouts.tsx @@ -15,7 +15,7 @@ import useLocale from '@/utils/hooks/useLocale' import { useDynamicRoutes } from '@/routes/dynamicRoutesContext' import { useLocation } from 'react-router-dom' import { hasSubdomain } from '@/utils/subdomain' -import UpdateNotifier from '../UpdateNotifier' +import UpdateNotifier from '../../views/version/UpdateNotifier' export type LayoutType = | typeof LAYOUT_TYPE_CLASSIC diff --git a/ui/src/components/template/Footer.tsx b/ui/src/components/template/Footer.tsx index d93908f7..dbe6e955 100644 --- a/ui/src/components/template/Footer.tsx +++ b/ui/src/components/template/Footer.tsx @@ -1,13 +1,14 @@ -import classNames from 'classnames' -import Container from '@/components/shared/Container' -import { APP_NAME } from '@/constants/app.constant' -import { PAGE_CONTAINER_GUTTER_X } from '@/constants/theme.constant' -import { useStoreActions, useStoreState } from '@/store' -import { Link, useNavigate } from 'react-router-dom' -import { ROUTES_ENUM } from '@/routes/route.constant' -import UiDialog from '@/views/shared/UiDialog' +import { useEffect, useState } from "react" +import classNames from "classnames" +import Container from "@/components/shared/Container" +import { APP_NAME } from "@/constants/app.constant" +import { PAGE_CONTAINER_GUTTER_X } from "@/constants/theme.constant" +import { useStoreActions, useStoreState } from "@/store" +import { Link, useNavigate } from "react-router-dom" +import { ROUTES_ENUM } from "@/routes/route.constant" +import UiDialog from "@/views/shared/UiDialog" -export type FooterPageContainerType = 'gutterless' | 'contained' +export type FooterPageContainerType = "gutterless" | "contained" type FooterProps = { pageContainerType: FooterPageContainerType @@ -21,7 +22,20 @@ const FooterContent = () => { const apiConfig = useStoreState((state) => state.abpConfig.config?.extraProperties) const uiMode = import.meta.env.MODE - const reactAppVersion = import.meta.env.VITE_REACT_APP_VERSION + + const [latestVersion, setLatestVersion] = useState(null) + + // version.json'dan en güncel UI versiyonunu al + useEffect(() => { + fetch("/version.json?ts=" + Date.now()) + .then((res) => res.json()) + .then((data) => { + if (data?.releases?.length > 0) { + setLatestVersion(data.releases[0].version) // en güncel hep en üstte + } + }) + .catch(() => setLatestVersion(null)) + }, []) return ( <> @@ -40,24 +54,25 @@ const FooterContent = () => { {apiConfig && ( API: - {apiConfig['environment'].toString()}:{apiConfig['version'].toString()} + {apiConfig["environment"].toString()}:{apiConfig["version"].toString()} )} - {reactAppVersion != currentUiVersion && ( + + {latestVersion && latestVersion !== currentUiVersion && ( { - setUiVersion(reactAppVersion ) + setUiVersion(latestVersion) navigate(ROUTES_ENUM.protected.admin.changeLog) }} title="🎉 Yeni Güncelleme" > - Sözsoft Kurs Platform Sistemi güncellendi. + Sözsoft Kurs Platform Sistemi v{latestVersion} sürümüne güncellendi.

Detayları, "Güncelleme Günlüğü" ekranında görebilirsiniz.

)} @@ -65,12 +80,14 @@ const FooterContent = () => { ) } -export default function Footer({ pageContainerType = 'contained' }: FooterProps) { +export default function Footer({ pageContainerType = "contained" }: FooterProps) { return (